FFmpeg拼接视频和音频不同步

44

使用ffmpeg的concat命令将多个文件合并时,音频的时间戳或偏移会出现不匹配的情况。我已经尝试了几个视频,并注意到在h.264/MP4上都有同样的问题。

使用concat并对视频进行编码似乎可以正常工作。由于ffmpeg进行了完整的转换计算,并且似乎一切都正确,因此音频保持同步。

然而,仅仅将视频拼接起来而没有任何转换或编码会导致同步问题逐渐增加。显然,与其仅仅将视频连接起来而不是编码它们,这样做会导致信息/质量的损失,因此我更愿意找到解决这个问题的方法。

我已经尝试过几个标志来解决这个基于时间戳的问题。然而,这些都似乎无法解决这个问题。

ffmpeg -f concat -fflags +genpts -async 1 -i segments.txt test.mov
ffmpeg -auto_convert 1 -f concat -fflags +genpts -async 1 -i segments.txt -c copy test2.mov
ffmpeg -f concat -i segments.txt -c copy -fflags +genpts test3.mp4
ffmpeg -f concat -fflags +genpts -async 1 -i segments.txt -copyts test4.mov
ffmpeg -f concat -i segments.txt -copyts test5.mov
ffmpeg -f concat -i segments.txt -copyts -c copy test6.mov
ffmpeg -f concat -fflags +genpts -i segments.txt -copyts -c copy test7.mov

注意:我在SO上找到的所有其他问题似乎都是通过重新编码视频来“解决”问题。这不是一个好的解决方案。
更新
我意识到连接(concat)并不是问题所在。原始片段集的时间戳不匹配。不知何故,连接+编码修复了问题,但我不想每次都重新编码视频并失去质量。
ffmpeg -y -ss 00:00:02.750 -i input.MOV -c copy -t 00:00:05.880 output.MOV

这导致了以下数据。
ffprobe -v quiet -show_entries stream=start_time,duration output.MOV

start_time=-0.247500
duration=6.131125
start_time=-0.257333
duration=6.155333

从那时起,我尝试在不同位置使用-to-t-af apad -c:v copy,但我仍然无法使持续时间相同。

这里是完整的ffprobe输出

这里是原始(红色)与段落(绿色)的比较

详细示例文件

我录制了一个示例视频,添加了分割和连接命令。http://davidpennington.me/share/audio_sync_test_video.zip


1
音频可能需要重新编码,但视频不需要。您可以使用-video_track_timescale改变MOV/MP4的视频时间基准而无需重新编码。如果您粘贴输入文件的详细信息,那会很有帮助。 - Gyan
我认为这可能与ffmpeg有关的mp4 / aac问题有关。 - Xeoncross
这篇关于关键帧的评论可能是导致音频同步问题的一部分。 - Xeoncross
5个回答

29

这个两步骤的过程应该可行。

步骤1:填充每个片段中的音频。

ffmpeg -i segment1.mov -af apad -c:v copy <audio encoding params> -shortest -avoid_negative_ts make_zero -fflags +genpts padded1.mov

或者

使用同步流生成分段

ffmpeg -y -ss 00:00:02.750 -i input.MOV -c copy -t 00:00:05.880 -avoid_negative_ts make_zero -fflags +genpts segment.MOV

步骤2 合并

ffmpeg -f concat -i segments.txt -c copy test.mov

其中segments.txt包含填充文件的名称。


1
一些或所有片段中的音频长度与视频长度不相等。因此,音频连接点与视频连接点不在同一时间,导致异步。第一步是填充音频,即在每个片段的末尾添加无限期的静默,但最短的停止操作时,当视频流结束时,从而使音频和视频尽可能地具有相同的长度。 - Gyan
1
我不知道文件中流的编解码器,但通常持续时间不会匹配,因为两个流都是量化的,即对于25 fps视频,持续时间将是0.04秒的倍数,对于48 kHz的AAC音频,持续时间将是0.0213秒的倍数。我怀疑这不是问题所在。在我的“apad”建议之前,请发布整个输入和一个您制作的片段的ffprobe读出。 - Gyan
2
你的片段具有负PTS,因为ffmpeg在分割点之前的关键帧处切割片段,但将PTS 0分配给了分割点,因此之前的帧具有负PTS。因此,我的编辑命令可以解决这个问题。然而,有一个问题。分割点之前的音频量与视频不相等,因此连接处仍会有一些静默。sboisse的方法可能是最安全的。 - Gyan
很遗憾,音频垫仍然无法同步。我甚至解析了ffprobe以获取最接近分段时的关键帧,然后添加了垫层,但仍然不同步。我真的很想知道还能做什么。只是将几个剪辑合并成一个拼接视频,视频和音频之间的断开已经达到了1/6秒。 - Xeoncross
你能分享足够的文件让我重现这个问题吗? - Gyan
显示剩余21条评论

11
我曾经遇到一个类似的问题,并找到了一种解决方案,至少对我有效。在我的情况下,我也在合并文件,并发现在iOs上有音视频同步问题,但在Windows上没有(比如,VLC媒体播放器使用相同的mp4文件没有显示出同步问题)。在iOs播放这个连接起来的mp4时,初始的同步表现良好,随着电影的播放,同步逐渐丢失,音频比视频快。有趣的是,通过将电影进度滑块推进到电影的任何一点,可以暂时恢复同步,但随着在iOs中继续播放电影,同步又会丢失。通过同时在iOs和Windows VLC中播放相同的电影,并尽可能观察它们之间的"回声"演变,我得出结论:假设Windows播放器是正确的,iOs音频过快。

对于我来说,解决方案是在ffmpeg命令中添加音频过滤器选项 -af aresample=async=1000,我在ffmpeg在线文档中找到了这个示例,并直接使用了。我不知道这个设置是否最优,但结果是mp4的音频和视频在iOs和VLC播放时保持同步。这个ffmpeg选项在连接之后重新编码已经连接的文件时,都可以得到正确的iOs同步。

1
这是唯一对我有效的解决方案。但它需要重新编码音频(错误:Filtergraph 'aresample=async=1000' was defined for audio output stream 0:1 but codec copy was selected. Filtering and streamcopy cannot be used together),因此我不得不将标志 -c copy 更改为 -c:v copy - GG.
我使用concat筛选器与此参数,它可以工作。当时我想:“但是为什么?” - ipid
这确实解决了异步的问题,此外,在音频之上同步视频的方式是什么?可能需要重新编码视频轨道,但对我来说没关系。 - Radical Edward

3
您可以使用filter_complex一次性连接不同的选项
ffmpeg -i input1.mp4 -i input2.webm \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0] concat=n=2:v=1:a=1 [v] [a]" \
-map "[v]" -map "[a]" <encoding options> output.mkv

3
您的命令使用了过滤器,因此会重新编码,但Xeoncross希望避免这种情况。 - llogan

3

我也曾经为此苦苦挣扎,特别是在处理松下AVCHD生成的MTS文件时。我目前的解决方案是在操作系统级别上进行连接,而不是使用ffmpeg。我在Windows上这样做,大致是这样的:

COPY /b input_1.mts + input_2.mts + input_3.mts output.mts

在Linux上应该是这样的:
$ cat input_1.mts input_2.mts input_3.mts > output.mts

你可以查找有关 WindowsLinux 二进制串联的文档。
与转码相比,这种串联方法是一种更好的选择,如果原始格式适合您的话。这种方法几乎不使用 CPU 处理,并保留原始质量。处理高质量大量媒体时,双赢。

我认为这可能会在文件格式的前X个字节包含元数据的情况下失败。也许不是所有媒体处理中内置的保护措施(例如无论流数据如何都读取到文件结尾?)都会失败。 - Xeoncross
这是一个有效的关注点,在二进制级别连接文件时应该考虑到。 - salmore
2
TS是一种流式容器。它不能与MP4或MOV等格式兼容。 - Gyan

1
如果输入的视频具有相同的视频格式、音频格式、尺寸等,您可以使用mkvtoolnix中的mkvmerge连接视频而无需重新编码:mkvmerge
mkvmerge -o output.mkv file1.mkv + file2.mkv + file3.mkv

mkvmerge 还可以接受 MP4 容器的输入文件,但是输出文件的容器将会是 MKV,即使你试图将输出文件的文件名扩展名指定为 .mp4。你可以使用 ffmpeg 更改容器:

mkvmerge -o output.mkv file1.mp4 + file2.mp4 + file3.mp4
ffmpeg -i output.mkv -c copy output.mp4

我需要将来自不同源且采用不同设置编码的视频连接起来,所以我首先使用了以下命令调整大小并重新编码输入视频:

for f in *.mp4;do w=1280;h=720;ffmpeg -i $f -filter:v "scale=iw*min($w/iw\,$h/ih):ih*min($w/iw\,$h/ih),pad=$w:$h:($w-iw*min($w/iw\,$h/ih))/2:($h-ih*min($w/iw\,$h/ih))/2" -c:v libx264 -crf 22 -preset slow -pix_fmt yuv420p -c:a aac -q:a 1 -ac 2 -ar 44100 ${f%mp4}mkv;done

我的一些输入视频没有音频通道,因此我使用了以下命令为视频添加一个静音音频通道:

for f in *.mkv;do ffprobe $f|&grep -q 1:\ Audio||{ ffmpeg -i $f -f lavfi -i anullsrc -c:a aac -shortest -c:v copy temp-$f;mv temp-$f $f;};done

我使用mkvmerge将视频拼接起来:
mkvmerge -o output.mkv `printf %s\\n *.mkv|sed '1!s/^/+ /'`

1
OP 询问有关 ffmpeg 的问题。提供其他工具有什么意义呢?就像你问关于 C# 的问题,而有人回答 qBasic 的问题一样... - Alex Sham
2
在尝试了所有我能找到的FFmpeg建议/修复措施数小时后,我放弃了并尝试了这个。它第一次就成功了。非常感谢,我希望我早点尝试这个。 - Grimeire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接