You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中使用mp4Parser拼接.mov文件遇音视频不同步问题求解

解决mp4Parser视频拼接音视频不同步问题及替代方案

针对mp4Parser的修复方案

你遇到的音视频不同步问题,核心原因是第二个视频的音轨/视频轨起始时间未和第一个视频的结束时间正确对齐,或是两个视频的时间基准不一致。可以通过手动调整轨道时间偏移来修复,修改后的代码如下:

public static void combineVideos(String videoPath1, String videoPath2, String output, boolean last) throws IOException {
    Movie movie1 = MovieCreator.build(videoPath1);
    Movie movie2 = MovieCreator.build(videoPath2);

    // 计算第一个视频的总时长(取所有轨道的最大时长,确保覆盖完整内容)
    long firstVideoDuration = 0;
    for (Track track : movie1.getTracks()) {
        long trackDuration = track.getDuration();
        if (trackDuration > firstVideoDuration) {
            firstVideoDuration = trackDuration;
        }
    }

    // 给第二个视频的所有轨道添加时间偏移,确保从第一个视频结束位置开始
    List<Track> adjustedTracks = new ArrayList<>();
    for (Track track : movie2.getTracks()) {
        Track adjustedTrack = new TrackModifier(track).shiftTime(firstVideoDuration).modify();
        adjustedTracks.add(adjustedTrack);
    }

    // 分别收集并合并视频轨、音频轨
    List<Track> allVideoTracks = new ArrayList<>();
    allVideoTracks.addAll(movie1.getTracks().stream().filter(t -> t.getHandler().equals("vide")).toList());
    allVideoTracks.addAll(adjustedTracks.stream().filter(t -> t.getHandler().equals("vide")).toList());

    List<Track> allAudioTracks = new ArrayList<>();
    allAudioTracks.addAll(movie1.getTracks().stream().filter(t -> t.getHandler().equals("soun")).toList());
    allAudioTracks.addAll(adjustedTracks.stream().filter(t -> t.getHandler().equals("soun")).toList());

    // 生成输出视频
    Movie movieOutput = new Movie();
    movieOutput.addTrack(new AppendTrack(allVideoTracks.toArray(new Track[0])));
    movieOutput.addTrack(new AppendTrack(allAudioTracks.toArray(new Track[0])));

    DefaultMp4Builder mp4Builder = new DefaultMp4Builder();
    Container c = mp4Builder.build(movieOutput);

    try (FileOutputStream fos = new FileOutputStream("C:\\Users\\willj\\Downloads\\" + output + ".mp4")) {
        c.writeContainer(fos.getChannel());
    }
}

关键修改点:

  • 计算第一个视频的最大轨道时长,作为第二个视频轨道的时间偏移量
  • 使用TrackModifier给第二个视频的所有轨道添加偏移,确保播放起始位置对齐
  • 分开合并视频轨和音频轨,避免直接AppendTrack导致的时间基准不匹配

更稳定的替代方案:JavaCV(基于FFmpeg)

如果mp4Parser的方案仍无法彻底解决同步问题,推荐使用JavaCV——它基于FFmpeg开发,对音视频同步的处理更成熟,支持mov等多种格式,稳定性远超mp4Parser。

步骤1:添加Maven依赖

<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.9</version>
</dependency>

步骤2:拼接mov文件的示例代码

import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;

import java.util.ArrayList;
import java.util.List;

public class VideoCombiner {
    public static void combineMovVideos(List<String> inputPaths, String outputPath) throws Exception {
        // 初始化第一个抓取器,获取输出参数基准
        FFmpegFrameGrabber firstGrabber = new FFmpegFrameGrabber(inputPaths.get(0));
        firstGrabber.start();

        // 初始化录制器,匹配原视频的编码、分辨率等参数
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, firstGrabber.getImageWidth(), firstGrabber.getImageHeight());
        recorder.setFormat("mov");
        recorder.setFrameRate(firstGrabber.getFrameRate());
        recorder.setVideoCodec(firstGrabber.getVideoCodec());
        recorder.setAudioCodec(firstGrabber.getAudioCodec());
        recorder.setAudioChannels(firstGrabber.getAudioChannels());
        recorder.setSampleRate(firstGrabber.getSampleRate());
        recorder.start();

        // 遍历所有输入视频,逐帧录制到输出文件
        for (String path : inputPaths) {
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(path);
            grabber.start();

            Frame frame;
            while ((frame = grabber.grab()) != null) {
                recorder.record(frame);
            }

            grabber.stop();
            grabber.release();
        }

        recorder.stop();
        recorder.release();
        firstGrabber.stop();
        firstGrabber.release();
    }

    // 调用示例
    public static void main(String[] args) throws Exception {
        List<String> inputVideos = new ArrayList<>();
        inputVideos.add("path/to/first.mov");
        inputVideos.add("path/to/second.mov");
        combineMovVideos(inputVideos, "path/to/output.mov");
    }
}

内容的提问来源于stack exchange,提问作者wbradshaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:37:23