最近我使用 ffmpeg 进行了重新编码,并在过程中遇到了听不见但波形可见的音频不同步问题。
MKV文件和音频流的源信息:
Mediainfo 显示6毫秒音频相对于视频的延迟:
Audio
ID : 2
Format : E-AC-3
Format/Info : Enhanced Audio Coding 3
Format settings, Endianness : Big
Codec ID : A_EAC3
Duration : 1 h 0 min
Bit rate mode : Constant
Bit rate : 640 kb/s
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Sampling rate : 48.0 kHz
Frame rate : 187.500 FPS (256 SPF)
Compression mode : Lossy
Delay relative to video : 6 ms
Stream size : 276 MiB (7%)
Language : English
Service kind : Complete Main
Default : Yes
Forced : No
通过查看 pts 和流开始时间也可以看到这一点:
ffprobe -of compact -select_streams a:0 -show_frames -show_entries frame=pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp_time,pkt_duration,nb_samples %sourcemkv%
frame|pkt_pts=6|pkt_pts_time=0.006000|pkt_dts=6|pkt_dts_time=0.006000|best_effort_timestamp_time=0.006000|pkt_duration=32|nb_samples=1536
frame|pkt_pts=38|pkt_pts_time=0.038000|pkt_dts=38|pkt_dts_time=0.038000|best_effort_timestamp_time=0.038000|pkt_duration=32|nb_samples=1536
frame|pkt_pts=70|pkt_pts_time=0.070000|pkt_dts=70|pkt_dts_time=0.070000|best_effort_timestamp_time=0.070000|pkt_duration=32|nb_samples=1536
此外,还可以通过流检查来检测延迟start_time
:
ffprobe -show_entries stream=codec_type,duration,start_time -of compact %sourcemkv%
stream|codec_type=video|start_time=0.000000|duration=N/A
stream|codec_type=audio|start_time=0.006000|duration=N/A
stream|codec_type=subtitle|start_time=0.000000|duration=3617.638000
重新编码过程
我选择将 EAC3 转码为 AAC 并混音为立体声,仅使用这些标志(无时间戳操作):-acodec aac -b:a 160k -ac 2
我获得延迟结果的一种方法是使用此命令重新编码:ffmpeg -i %sourcemkv% -vcodec libx264 -profile:v high -crf 23 -tune film -level 4.1 -acodec aac -b:a 160k -ac 2 re-encoded-with-aac.mkv
检测延迟
我通过检查视频帧对应的波形发现了延迟。延迟本身听不出来,因为我发现编码版本比原始版本晚了 15.333 毫秒,但通过 AvsPmod 中的以下 AviSynth+ 脚本检查发现延迟是存在的:
FFmpegSource2("re-encoded-with-aac.mkv", vtrack=-1, atrack=-1, cache=True, cachefile="", fpsnum=-1, fpsden=1, threads=-1, timecodes="", seekmode=1, overwrite=False, width=-1, height=-1, resizer="BICUBIC", colorspace="", rffmode=0, adjustdelay=-1, utf8=False, varprefix="")
ConvertToMono()
# Test if DelayAudio will sync it!
#AmplifydB(50.0)
#DelayAudio(-0.015333)
waveform( window=1, height=0.6)
我将相同的脚本应用到原始脚本中,并比较了某些视频帧的波形图:帧 139 的源 mkv 图表对比AAC 重新编码视频的第 139 帧的波形视图
可以看出,除非DelayAudio(-0.015333)
对重新编码的 AAC 音频流应用 a,否则它们不会同步。幸运的是,这种延迟在整个视频中都是恒定的!
重新编码的其他信息
重新混合的音频流具有start_time=0.000000
并且第一个音频帧的 pts 是0.000000
。有点奇怪的是,虽然报告的原始音频相对于视频的已知延迟为 6ms,但原始视频和使用 aac 流重新编码的视频之间的最终延迟为15.333ms
。我不知道这可能来自哪里;也许它与输出音频帧的长度有关,每个音频帧的持续时间为21.333ms
?!
问题
- 这是从 EAC3 转码为 AAC 时 ffmpeg 的一个错误吗?
- 我是否误用了 ffmpeg 或者对它能正确地重新编码抱有太高的期望?
- 是否需要指定一些额外的 ffmpeg 参数以实现同步?
- 除了目视检查帧的波形以确定可能发生的任意延迟之外,处理这种情况的最佳解决方案是什么?
- 原始音频流的 6ms 延迟是否会导致重新编码出现所有这些问题?
其他努力
我还尝试了以下实验:
实验 1
- 在 gMKVExtractGUI 中打开
%sourcemkv%
。检测到 EAC3 音频流延迟 6ms。提取此音频流。 - 将 mkvtoolnix-gui 中重新编码的视频流与原始 EAC3 音频重新混合,并在“延迟”字段中明确指定 6ms 延迟。
结果:音频同步按照上述检查方法判断,与原件无异。
实验 2
- 通过 FFmpeg 将原始音频转换为 2 声道 AAC
- 在 mkvtoolnix-gui 中将生成的 AAC 音频流与视频重新混合,并指定 6ms 的延迟 - 就像原始的一样。
结果:原始音频与结果音频不同步
答案1
虽然听起来违反直觉,但这不是错误
关键是,许多音频编解码器无法从 PTS 零开始 - AAC 就是其中之一。开始时有一个“稳定阶段”(我不知道确切的英文单词,在我的母语德语中是“Einschwingphase”),音频输出还不能承载有效载荷(而是呈现静音)。
所有正确的编码工具都会将音频轨道的启动时间提前到相应的视频轨道之前,这样第一幅图像的 PTS 就会与第一幅图像的 PTS 相重合。有效载荷音频数据包。这意味着,先前的音频数据包被渲染为静音。
当您从一个稳定阶段为 X 毫秒的编解码器编码为另一个稳定阶段为 Y 毫秒的编解码器时,事情开始变得更加混乱 - 在这种情况下,当旧编解码器比新编解码器需要更长的时间来稳定时,甚至可能会出现正延迟。
顺便说一下,这就是为什么总是在非稳定编解码器(通常是 PCM 系列的成员)中进行精确编辑的原因。
答案2
根据 Eugen 的回答,我搜索并找到了更多信息此 VideoHelp 论坛主题关于编解码器相关的静音,即编码开始时产生的延迟。然后我偶然发现了 Apple 的 QAAC 编码器及其允许修剪编码器特定延迟的选项之一,也在关于延迟的视频帮助主题:
--no-delay Compensate encoder delay by prepending 960 samples
of scilence, then trimming 3 AAC frames from
the beginning (and also tweak iTunSMPB).
This option is mainly intended for resolving
A/V sync issue of video.
通过使用上述选项,我成功实现了保持音频与原始音频同步的目标,我将在下面进行描述。我决定将此作为答案发布,因为主要问题是是否有一种方法可以缩混到 AAC,同时保持音频与源完美同步,而我已经找到了一种方法来做到这一点。
脚步:
AAC 下混提取:
ffmpeg -i %sourcemkv% -vn -ac 2 -acodec pcm_f32le -f wav - | qaac -v 160 --delay 0.006 --no-delay - -o outw6ms.m4a
该命令执行以下操作:
- 读取原始 mkv 的 EAC3 音频流,并使用将其混音为立体声
-ac 2
- 它将其解码为浮点 PCM(
pcm_f32le
) - 这是必要的,以便下混保留原始流的响度 - 将输出传送到 qaac 编码器
- qaac 编码器具有
--no-delay
可减少编码器特定延迟的标志 - 我还添加了
-delay 0.006
嵌入 6ms 静音的选项,因为原始文件相对于视频有这么大的延迟(我可以省略这个选项,而是在 mkvmerge 中重新混合结果,并在那里明确指定音频流需要 6ms 延迟 - 我测试过了,它也可以工作)
然后,我直接在 ffmpeg 中将生成的音频与转码后的视频重新混合:
ffmpeg -i encvideo.mkv -i outw6ms.m4a -codec copy -map 0:v -map 1:a result.mkv
我尝试了 AvsPMod 中的结果waveform()
,音频与源完美同步 - 从开始到结束。
所以我终于找到了一种保持原始音频与视频同步的方法。我会再等一段时间,直到接受这个答案,因为我想知道 ffmpeg 中是否有任何音频编码器允许这种qaac
具有此--no-delay
选项的行为。这似乎非常有用,并且是专门为这类事情而设计的。