【发布时间】:2019-03-07 23:35:18
【问题描述】:
我正在尝试编写一个简单的 Windows Media Foundation 命令行工具来使用 IMFSourceReader 和 IMFSyncWriter 加载视频,将视频和音频作为未压缩流读取并将它们重新编码为 H.246/具有一些特定硬编码设置的 AAC。
The simple program Gist is here
(注意:我一直在测试的视频都是立体声,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开始,然后是213333和426666 -
^^ 用于羊驼测试视频(需要 1 1024 块静音才能同步)
-
如果偏移问题是特定于文件的,可能是媒体基金会或用于比较输出的软件中 MP4 文件中的编辑列表原子支持不佳/缺失/错误。也就是说,也许您应该查看原始文件中的 edts/elst 原子,并检查它们是否与您需要添加的偏移量相关。
标签: c++ windows video-encoding ms-media-foundation audio-video-sync