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
相关产品推荐
相关产品推荐

