Media Foundation视频重新编码产生音频流同步偏移

问题描述 投票:2回答:1

我正在尝试编写一个简单的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,所以我首先尝试从产生我想要的视频的所有后续视频帧中减去该初始时间戳(videoOffset),但结果是这种情况与音频:

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

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

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

//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会导致这种情况吗?
  • 如果我是正确的,我如何使用视频元数据来确定我是否需要填充音频流以及需要在pad中有多少1024块静音?

编辑:

对于上面的示例视频:

  • sample video 1:视频流从0开始,不需要额外的块,原始数据的直通工作正常。
  • sample video 2:视频流从834166(hns)开始,需要1 1024块静音才能同步
  • sample video 3:视频流从0开始,需要2 1024个静音块才能同步。

更新:

我试过的其他事情:

  • 增加第一个视频帧的持续时间以考虑偏移:不产生任何效果。
c++ windows video-encoding ms-media-foundation audio-video-sync
1个回答
0
投票

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

EncodeWithSourceReaderSinkWriter

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

enter image description here

从下到上:

  • 原始文件
  • 编码文件
  • 我通过设置“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 atoms之间的关系。 (是的,我知道,我应该阅读规范。但我很懒...)

您可以使用mp4资源管理器工具获取atom的值,或使用我的H264Dxva2Decoder项目中的mp4解析器:

H264Dxva2Decoder

tuning_against_啊_window.mov

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

GREEN_SCREEN_ANIMALS__ALPACA.mp4

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

go PR6239_1.mov

  • 来自tkhd视频的elst(媒体时间):0
  • 来自tkhd audio的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原子
  • 使用正确的偏移计算编码文件

这取决于你的需求:

  • 第一个选项允许您保留原始文件。但是您必须添加elst原子
  • 使用第二个选项,您必须在编码之前从文件中读取atom,并且编码文件将丢失少量原始帧

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

PS:我正在讨论这个问题,因为在我的H264Dxva2Decoder项目中,edts / elst原子在我的待办事项列表中。我解析它,但我不使用它...

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

© www.soinside.com 2019 - 2024. All rights reserved.