我试图编写一个简单的Windows Media Foundation命令行工具,以使用IMFSourceReader
和IMFSyncWriter
加载视频,将视频和音频作为未压缩的流读取,并使用一些特定的硬编码将它们重新编码为H.246 / AAC设置。
The simple program Gist is here
sample video 1
sample video 2
sample video 3
(注意:我一直在测试的视频均为立体声,采样率为48000k)
该程序可以运行,但是在某些情况下,在编辑程序中将新输出的视频与原始视频进行比较时,我看到复制的视频流匹配,但是复制的音频流以一定程度的静默和音频为前缀抵消了,这在我的情况下是 Not Acceptable 。
audio samples:
original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy - |[silence] [silence] [silence] [audio1] [audio2] [audio3] ... etc
在这种情况下,进入的第一个视频帧具有非零时间戳,但第一个音频帧确实具有0时间戳。
我希望能够从视频和音频流中产生第一帧的复制视频,所以我首先尝试从所有后续视频帧中减去该初始时间戳(
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个或更多填充,而有些则根本不需要额外的填充!
我的问题是:
编辑:
对于上面的示例视频:
更新:
我尝试过的其他方法:
最佳答案
我编写了程序的另一个版本以正确处理NV12格式(您无法使用):
EncodeWithSourceReaderSinkWriter
我将Blender用作视频编辑工具。这是我与Tuning_against_a_window.mov的结果:
从底部到顶部:
就像Roman R.所说的那样,MediaFoundation mp4源不使用“edts / elst”原子。但是Blender和您的视频编辑工具可以。 mp4源也会忽略“tmcd”音轨。
“edts / elst”:
Edits Atom ( 'edts' )
MPEG-4 File Source
所以实际上,编码是好的。与真实的音频/视频数据相比,我认为没有音频流同步偏移。例如,您可以将“edts / elst”添加到编码文件中,以获得相同的结果。
PS:在编码文件上,我为音频/视频轨道添加了“edts / elst”。我还增加了trak原子和moov原子的大小。我确认,Blender对于原始文件和编码文件都显示相同的波形。
编辑
在3个视频样本中,我试图了解mvhd / tkhd / mdhd / elst原子之间的关系。 (是的,我知道,我应该阅读规格。但是我很懒...)
您可以使用mp4资源管理器工具获取atom的值,也可以使用H264Dxva2Decoder项目中的mp4解析器:
H264Dxva2Decoder
Tuning_against_a_window.mov
tkhd视频中的
GREEN_SCREEN_ANIMALS__ALPACA.mp4
tkhd视频中的
GOPR6239_1.mov
tkhd视频中的
如您所见,使用GOPR6239_1.mov,最远的媒体时间为0。这就是该文件不存在视频/音频同步问题的原因。
对于Tuning_against_a_window.mov和GREEN_SCREEN_ANIMALS__ALPACA.mp4,我尝试计算视频/音频偏移。
我修改了项目以考虑到这一点:
EncodeWithSourceReaderSinkWriter
目前,我没有找到所有文件的通用计算。
我只是找到正确编码两个文件所需的视频/音频偏移量。
对于Tuning_against_a_window.mov,我在(电影时间-视频/音频mdhd时间)之后开始编码。
对于GREEN_SCREEN_ANIMALS__ALPACA.mp4,我在视频/音频主要媒体时间之后开始编码。
可以,但是我需要为所有文件找到正确的唯一计算。
因此,您有2个选择:
这取决于您的需求:
如果您选择第一个选项,我将解释如何添加第一个原子。
PS:我对此问题很感兴趣,因为在我的H264Dxva2Decoder项目中,edts / elst原子在我的待办事项列表中。
我解析它,但我不使用它...
PS2:这个链接听起来很有趣:
Audio Priming - Handling Encoder Delay in AAC
关于c++ - Media Foundation视频重新编码,产生音频流同步偏移,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55054531/