媒体基础视频重新编码产生音频流同步偏移

Posted

技术标签:

【中文标题】媒体基础视频重新编码产生音频流同步偏移【英文标题】:Media Foundation video re-encoding producing audio stream sync offset 【发布时间】:2019-03-07 23:35:18 【问题描述】:

我正在尝试编写一个简单的 Windows Media Foundation 命令行工具来使用 IMFSourceReaderIMFSyncWriter 加载视频,将视频和音频作为未压缩流读取并将它们重新编码为 H.246/具有一些特定硬编码设置的 AAC。

The simple program Gist is here

sample video 1

sample video 2

sample video 3

(注意:我一直在测试的视频都是立体声,48000k 采样率)

该程序可以运行,但是在某些情况下,在编辑程序中将新输出的视频与原始视频进行比较时,我看到复制的视频流匹配,但复制的音频流预先固定了一些静音并且音频是偏移的,这在我的情况下是不可接受的。

audio samples:
original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[silence] [silence] [silence] [audio1] [audio2] [audio3] ... etc

在这种情况下,进入的第一个视频帧具有非零时间戳,但第一个音频帧确实具有 0 时间戳。

我希望能够生成一个复制的视频,该视频和音频流中的第一帧为 0,因此我首先尝试从生成视频 i 的所有后续视频帧中减去该初始时间戳 (videoOffset)想要,但导致音频出现这种情况:

original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[audio4] [audio5] [audio6] [audio7] [audio8] ... etc

音轨现在向另一个方向移动了一小部分,但仍然没有对齐。有时,当视频流的起始时间戳为 0 但 WMF 仍会在开头截断一些音频样本时(参见示例视频 3),有时也会发生这种情况!

通过在将音频样本数据传递到IMFSinkWriter 时插入以下代码,我已经能够修复此同步对齐并将视频流偏移到从 0 开始:

//inside read sample while loop
...

// LONGLONG llDuration has the currently read sample duration
// DWORD audioOffset has the global audio offset, starts as 0
// LONGLONG audioFrameTimestamp has the currently read sample timestamp

//add some random amount of silence in intervals of 1024 samples
static bool runOnce false ;
if (!runOnce)

    size_t numberOfSilenceBlocks = 1; //how to derive how many I need!?  It's aribrary
    size_t samples = 1024 * numberOfSilenceBlocks; 
    audioOffset = samples * 10000000 / audiosamplesPerSecond;
    std::vector<uint8_t> silence(samples * audioChannels * bytesPerSample, 0);
    WriteAudioBuffer(silence.data(), silence.size(), audioFrameTimeStamp, audioOffset);

    runOnce= true;


LONGLONG audioTime = audioFrameTimeStamp + audioOffset;
WriteAudioBuffer(dataPtr, dataSize, audioTime, llDuration);

奇怪的是,这会创建一个与原始视频文件匹配的输出视频文件。

original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc

解决方案是在音频流的开头插入 额外的静音,块大小为 1024。 IMFSourceReader 提供的音频块大小无关紧要,填充是 1024 的倍数。

我的问题是静音偏移似乎没有可检测的原因。为什么我需要它?我怎么知道我需要多少?经过几天的努力,我偶然发现了 1024 样本静音块解决方案。

有些视频似乎只需要 1 个填充块,有些需要 2 个或更多,有些则根本不需要额外的填充!

我的问题是:

有人知道为什么会这样吗?

在这种情况下我是否错误地使用了 Media Foundation 导致了这种情况?

如果我是正确的,我如何使用视频元数据来确定是否需要填充音频流以及填充中需要多少 1024 块静音?

编辑:

对于上面的示例视频:

sample video 1 :视频流从 0 开始,不需要额外的块,原始数据的透传工作正常。

sample video 2:视频流从 834166 (hns) 开始,需要 1 1024 块静默才能同步

sample video 3 : 视频流从 0 开始,需要 2 1024 个静音块才能同步。

更新:

我尝试过的其他事情:

增加第一个视频帧的持续时间以解决偏移:不产生任何效果。

【问题讨论】:

源文件和结果文件中前两个音频和视频样本的时间戳是什么? ^^ 忽略你的人工静音样本 @AndriyTylychko 在这种情况下,第一个视频时间戳是834166,然后是1251249,然后是1668332。音频时间戳从0 开始,然后是213333426666 ^^ 用于羊驼测试视频(需要 1 1024 块静音才能同步) 如果偏移问题是特定于文件的,可能是媒体基金会或用于比较输出的软件中 MP4 文件中的编辑列表原子支持不佳/缺失/错误。也就是说,也许您应该查看原始文件中的 edts/elst 原子,并检查它们是否与您需要添加的偏移量相关。 【参考方案1】:

我编写了另一个版本的程序来正确处理 NV12 格式(你的不工作):

EncodeWithSourceReaderSinkWriter

我使用 Blender 作为视频编辑工具。这是我使用 Tuning_against_a_window.mov 的结果:

从下往上:

原始文件 编码文件 我通过将“elst”原子设置为数字条目的值为 0 来更改原始文件(我使用 Visual Studio hexa 编辑器)

就像 Roman R. 所说,MediaFoundation mp4 源不使用“edts/elst”原子。但是 Blender 和你的视频编辑工具可以。 mp4 源也会忽略“tmcd”音轨。

“edts/elst”:

Edits Atom ( 'edts' )

编辑列表可用于提示轨道...

MPEG-4 File Source

MPEG-4 文件源默默地忽略提示音轨。

所以其实编码是好的。与真实的音频/视频数据相比,我认为没有音频流同步偏移。例如,您可以将“edts/elst”添加到编码文件中,以获得相同的结果。

PS:在编码文件中,我为两个音频/视频轨道添加了“edts/elst”。我还增加了 trak 原子和 moov 原子的大小。我确认,Blender 对原始文件和编码文件显示相同的波形。

编辑

我试图了解 3 个视频样本中 mvhd/tkhd/mdhd/elst 原子之间的关系。 (是的,我知道,我应该阅读规范。但我很懒……)

您可以使用 mp4 浏览器工具来获取 atom 的值,或者使用我的 H264Dxva2Decoder 项目中的 mp4 解析器:

H264Dxva2Decoder

Tuning_against_a_window.mov

tkhd 视频中的 elst(媒体时间):20689 来自 tkhd 音频的 elst(媒体时间):1483

GREEN_SCREEN_ANIMALS__ALPACA.mp4

elst(媒体时间)来自 tkhd 视频:2002 年 来自 tkhd 音频的 elst(媒体时间):1024

GOPR6239_1.mov

tkhd 视频的 elst(媒体时间):0 来自 tkhd 音频的 elst(媒体时间):0

如您所见,对于 GOPR6239_1.mov,从 elst 开始的媒体时间为 0。这就是该文件不存在视频/音频同步问题的原因。

对于 Tuning_against_a_window.mov 和 GREEN_SCREEN_ANIMALS__ALPACA.mp4,我尝试计算视频/音频偏移量。 我修改了我的项目以考虑到这一点:

EncodeWithSourceReaderSinkWriter

目前,我没有找到适用于所有文件的通用计算。

我只是找到了正确编码两个文件所需的视频/音频偏移量。

对于 Tuning_against_a_window.mov,我在(电影时间 - 视频/音频 mdhd 时间)之后开始编码。 对于 GREEN_SCREEN_ANIMALS__ALPACA.mp4,我在视频/音频 elst 媒体时间之后开始编码。

没关系,但我需要为所有文件找到正确的唯一计算。

所以你有两个选择:

对文件进行编码并添加 elst atom 使用右偏移计算对文件进行编码

这取决于您的需求:

第一个选项允许您保留原始文件。但您必须添加第一个原子 使用第二个选项,您必须在编码之前从文件中读取 atom,编码后的文件会丢失一些原始帧

如果您选择第一个选项,我将解释如何添加第一个原子。

PS:我对这个问题很感兴趣,因为在我的 H264Dxva2Decoder 项目中,edts/elst atom 在我的待办事项列表中。 我解析它,但我不使用它......

PS2:这个链接听起来很有趣: Audio Priming - Handling Encoder Delay in AAC

【讨论】:

感谢您对这个问题的详细检查。这确实看起来像正在发生的事情。我正在尝试重新创建您的解决方案,但被困在复制视频的 edts/elst 原子中的内容。当您在 PS 中说您为两个音频/视频轨道添加了原子时,您能否扩展您添加回这些轨道上的 elst 字段的值的确切位置?谢谢!

以上是关于媒体基础视频重新编码产生音频流同步偏移的主要内容,如果未能解决你的问题,请参考以下文章

基于块流协议保证音频优先发送

FFmpeg concat 视频和音频不同步

实时音频编解码之十一Opus编码

媒体基金会音频/视频捕获到 MPEG4FileSink 产生不正确的持续时间

多媒体通信基础

多媒体通信基础