FFmpeg concat 视频和音频不同步

Posted

技术标签:

【中文标题】FFmpeg concat 视频和音频不同步【英文标题】:FFmpeg concat video and audio out of sync 【发布时间】:2016-05-26 18:16:09 【问题描述】:

使用 ffmpeg concat 连接多个文件似乎会导致音频的时间戳或偏移量不匹配。我尝试了几个视频,发现 h.264 / MP4 存在同样的问题。

使用concat 和编码视频似乎工作正常。当 ffmpeg 进行完整的转换计算时,音频保持同步,并且似乎一切正常。

但是,简单地连接视频而不进行任何转换或编码会导致同步问题缓慢增加。显然,编码视频而不是简单地加入它们会导致信息/质量损失,所以我宁愿找到解决这个问题的方法。

我已经tried several flags 解决了这个似乎基于时间戳的问题。不过,这些似乎都不能解决问题。

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 不是问题。原始剪辑集的时间戳不匹配。不知何故 concat + encoding 解决了这个问题,但我不想每次都重新编码视频并降低质量。

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

从那以后,我尝试在不同的地方使用-tom 和-t 以及-af apad -c:v copy,但我仍然未能使持续时间相同。

Here is the full ffprobe output

Here is the original (red) vs the segment (green)

详细的示例文件

我录制了一个示例视频,添加了将其切碎的命令,然后将其连接起来。 http://davidpennington.me/share/audio_sync_test_video.zip

【问题讨论】:

音频可能需要重新编码,但不是video。您可以使用-video_track_timescale 更改 MOV/MP4 的视频时基,而无需重新编码。如果您粘贴输入文件的详细信息,那将很有帮助。 我觉得可能和这个ffmpeg ticket for mp4/aac有关 This comment about keyframes 可能是音频同步问题的一部分。 sboisse 建议使用ffprobe to find the nearest keyframes, then clip at that point。 【参考方案1】:

如果输入的视频具有相同的视频格式、音频格式、尺寸等,您可以使用 mkvtoolnix 中的mkvmerge 将视频拼接起来,无需重新编码:

mkvmerge -o output.mkv file1.mkv + file2.mkv + file3.mkv

mkvmerge 也接受带有 MP4 容器的输入文件,但即使您尝试将输出文件的文件扩展名指定为 .mp4,输出文件也会有一个 MKV 容器。您可以使用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%mp4mkv;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/^/+ /'`

【讨论】:

OP 询问 ffmpeg。提供其他工具有什么意义?就像你问一些关于 C# 的问题,而有人回答了关于 qBasic 的问题...... 在花了几个小时尝试每个 FFmpeg 建议/修复后,我可以很好地放弃并厌倦了这个。它第一次起作用。非常感谢,我希望我先累了。【参考方案2】:

您可以使用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

【讨论】:

您的命令使用过滤器,因此将重新编码,但 Xeoncross 希望避免这种情况。【参考方案3】:

我遇到了类似的问题,并找到了一个有效的解决方案,至少对我来说是这样。就我而言,我也在连接文件,发现 iO 的音频/视频同步问题,但 Windows 没有(例如,VLC 媒体播放器使用相同的 mp4 文件没有显示同步问题)。 ios 播放这种连接的 mp4 的症状最初是良好的同步,然后随着电影的播放而失去同步,音频比视频快。有趣的是,可以通过将电影进度滑块推进到电影中的任何点来临时恢复同步,但是随着电影继续在 iO 中播放,同步将再次丢失。通过在 iOs 和 Windows VLC 中同时播放同一部电影,并尽可能地相互同步,通过观察它们之间“回声”的演变,我得出结论,iOs 音频过快(假设Windows 播放器是正确的)。

对我来说,解决方案是将音频过滤选项-af aresample=async=1000 添加到 ffmpeg 命令中,我在 ffmpeg 在线文档中找到了该命令作为示例并逐字使用。我不知道此设置是否最佳,但结果是在 iO 和 VLC 播放时音频和视频保持同步的 mp4。这个 ffmpeg 选项在连接期间和之后重新编码已经连接的文件时产生了正确的 iO 同步。

【讨论】:

这是唯一对我有用的解决方案。它需要重新编码音频(错误: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 我将此参数与 concat 过滤器一起使用,它可以工作。我就像但是为什么呢?【参考方案4】:

这两个步骤应该可以工作

第 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 包含填充文件的名称。

【讨论】:

“填充音频”有什么作用?它如何修复时间戳? 部分或全部片段中的音频不等于视频长度。因此音频关节与视频关节不同时,因此是异步的。第一步填充音频,即在每个片段的末尾添加无限期的静音,但最短的步骤会在视频流结束时停止操作,从而将音频和视频渲染为相同的长度(尽可能)。跨度> 我尝试了-af apad -c:v copy &lt;audio encoding params&gt; -shortest,但仍然无法修复视频时长。 我不知道文件中流的编解码器,但通常持续时间不匹配,因为两个流都是量化的,即对于 25 fps 视频,持续时间将是 0.04s 的倍数,并且对于 48 kHz 的 AAC 音频,0.0213 秒的倍数。我怀疑这是这里的问题。为整个输入和您制作的片段之一发布 ffprobe 读数(在我的 apad 建议之前) 您的片段具有负 PTS,因为 ffmpeg 正在分割点之前的关键帧处切割片段,但将 PTS 0 分配给您的分割点,因此之前的帧具有负 PTS。所以我编辑的命令可以解决这个问题。但是,有一个问题。分割点之前的音频量不等于之前的视频量,所以关节处仍然会有一些静音。 sboisse 的方法可能是最安全的。【参考方案5】:

我也为此苦苦挣扎了一段时间。特别是在使用 Panasonic 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

您可以查找 windows 和 linux 二进制连接的文档。

如果原始格式适合您,这种与转码相对的连接方法是可行的方法。这种方法实际上不使用 CPU 处理并保留原始质量。处理高质量大容量媒体时的双赢。

【讨论】:

我认为这对于将元数据作为文件的前 X 个字节的格式会失败。也许没有内置在媒体处理中的所有保护措施(比如无论流数据说什么都读取到文件末尾?) 这是一个有效的问题,在二进制级别连接文件时应该考虑到这一点。 TS 是一个流式容器。这不适用于 MP4 或 MOVs..etc

以上是关于FFmpeg concat 视频和音频不同步的主要内容,如果未能解决你的问题,请参考以下文章

FFmpeg学习6:视音频同步

使用 ffmpeg 剪切电影导致音频/视频不同步

音视频同步

FFmpeg C API - 同步视频和音频

ffmpeg音频和视频同步错误

与 ffmpeg 的音视频同步