如何使用 Media Foundation 生成“moov before mdat”MP4 视频文件

Posted

技术标签:

【中文标题】如何使用 Media Foundation 生成“moov before mdat”MP4 视频文件【英文标题】:How to generate "moov before mdat" MP4 video files with Media Foundation 【发布时间】:2014-06-06 15:19:58 【问题描述】:

我发现 Microsoft Media Foundation 会生成 MPEG4 文件,其中 MDAT 原子位于 MOOV 原子之前。流式传输需要 MDAT 之前的 MOOV。我认为我的问题的解决方案是在创建接收器时使用 MF_MPEG4SINK_MOOV_BEFORE_MDAT 属性,但我似乎无法让它产生效果。我的代码与 http://blogs.msdn.com/b/eternalcoding/archive/2013/03/06/developing-a-winrt-component-to-create-a-video-file-using-media-foundation.aspx 基本相同。在该示例中,我在设置 MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS 之前将属性设置为 UINT32 TRUE。

【问题讨论】:

只是检查一下:您知道该功能从 Windows 8/Windows Server 2012 开始,不是吗? 是的,我在 Windows 8.1 上运行它 【参考方案1】:

如果你不能让编码器输出一个开头带有 moov atom 的文件,你总是可以在之后更正文件。这两个实用程序都应该在 Windows 上运行,并且可以做到这一点。 (尽管名称中包含“qt”,但它们都可以毫无问题地处理 .mp4 文件)

https://github.com/danielgtaylor/qtfaststart

http://ffmpeg.zeranoe.com/blog/?p=59

【讨论】:

其实我在encoder之后已经用过qtfaststart了,希望能从MF那里找到更好的解决方案【参考方案2】:

虽然距离这篇文章已经有很长时间了,但在使用 MediaFoundation 编码的 MDAT 之前,我也遇到了 MOOV 的问题。 Microsoft 提供的关于该主题的文档很少。该设置需要在创建对象时应用到 MFT Video Sink 或 SinkWriter 中。

我设法启用了我想要的功能,但录制中的视频是空帧,即使文件大小相同并且 mp4box 信息表明支持渐进式下载、视频长度等。音频很好。 GOP 也没有出现在信息中,因此仍然存在错误配置。

然后我对 H264 和 MP4 结构进行了更多阅读,

对我来说,关键是 MPEG 容器需要是一个分段的 MP4 容器,因此只需将容器类型设置为 FMPEG4 而不是 MPEG4 就可以了。下面是 SinkWriter 的初始化,它可以很好地使用此功能。

这是我对 SinkWriter 的完整初始化。

ComPtr<ID3D11Device> device;
ComPtr<ID3D11Device> dx3Device; // multithread configuration.
ComPtr<ID3D10Multithread> dx3MultiThread;
ComPtr<IMFDXGIDeviceManager> manager;

unsigned videoQuality = 50;
unsigned videoBitrate = FPS * width * height; //  DEFAULT_BITRATE;
videoBitrate = DEFAULT_BITRATE;
// Audio Input
const UINT SamplesPerSecond = BaseSampleRate;
const UINT AverageBytesPerSecond = SamplesPerSecond / sizeof(FLOAT);
const UINT ChannelCount = 2;     // Converted
const UINT BitsPerSample = 16;   // Converted

MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);
_Clock = new Clock();
// Create a random access stream in memory
// CHK(MFCreateMFByteStreamOnStreamEx((IUnknown*)videoStream, &m_SpByteStream));

// Create a temporary working MP4.
IMFByteStreamEx::CreateInstance((IUnknown*)videoStream, IID_IMFByteStream, &m_SpByteStream);

// Create the Sink Writer
ComPtr<IMFAttributes> spAttr;

ComPtr<IMFMediaType> audioOutputType;
ComPtr<IMFMediaType> spVideoTypeIn;
ComPtr<IMFMediaType> spVideoTypeOut;

CHK(MFCreateAttributes(&spAttr, 10));
CHK(spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE));
CHK(spAttr->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, FALSE));
CHK(spAttr->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, TRUE));
CHK(spAttr->SetUINT32(MF_LOW_LATENCY, TRUE));
CHK(spAttr->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_FMPEG4));
CHK(spAttr->SetUINT32(MF_MPEG4SINK_MOOV_BEFORE_MDAT, TRUE))

// Setup the output video media type   

HRESULT hr = 0;
D3D_FEATURE_LEVEL levels[] =  D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 ;
CHK(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
    levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, &device, nullptr, nullptr));
UINT token;
CHK(MFCreateDXGIDeviceManager(&token, &manager));
HANDLE deviceHandle;

CHK(manager->ResetDevice(reinterpret_cast<IUnknown*>(device.Get()), token));
if (SUCCEEDED(manager->OpenDeviceHandle(&deviceHandle))) 
    // https://docs.microsoft.com/en-au/windows/desktop/medfound/supporting-direct3d-11-video-decoding-in-media-foundation
    // make sure we are using the same device
    hr = manager->GetVideoService(deviceHandle, IID_PPV_ARGS(&dx3Device));
    hr = dx3Device->QueryInterface(IID_PPV_ARGS(&dx3MultiThread));
    dx3MultiThread->SetMultithreadProtected(TRUE);

CHK(spAttr->SetUnknown(MF_SINK_WRITER_D3D_MANAGER, manager.Get()));
CHK(MFCreateSinkWriterFromURL(L".mp4v", m_SpByteStream.Get(), spAttr.Get(), &m_SpSinkWriter));

//// Video In Format
CHK(MFCreateMediaType(&spVideoTypeIn));
CHK(spVideoTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CHK(spVideoTypeIn->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32));

CHK(spVideoTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
CHK(MFSetAttributeSize(spVideoTypeIn.Get(), MF_MT_FRAME_SIZE, m_Width, m_Height));
CHK(MFSetAttributeRatio(spVideoTypeIn.Get(), MF_MT_FRAME_RATE, m_FramesPerSecond, 1));
CHK(MFSetAttributeRatio(spVideoTypeIn.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
CHK(spVideoTypeIn->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE));
CHK(spVideoTypeIn->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE));
// Video Out format

CHK(MFCreateMediaType(&spVideoTypeOut));
CHK(spVideoTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CHK(spVideoTypeOut->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
CHK(spVideoTypeOut->SetUINT32(MF_MT_COMPRESSED, FALSE));
CHK(spVideoTypeOut->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE));
CHK(spVideoTypeOut->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, FALSE));
CHK(spVideoTypeOut->SetUINT32(MF_MT_AVG_BITRATE, videoBitrate ));
CHK(spVideoTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
CHK(spVideoTypeOut->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_High));
CHK(MFSetAttributeSize(spVideoTypeOut.Get(), MF_MT_FRAME_SIZE, m_Width, m_Height));
CHK(MFSetAttributeRatio(spVideoTypeOut.Get(), MF_MT_FRAME_RATE, m_FramesPerSecond , 1));
CHK(MFSetAttributeRatio(spVideoTypeOut.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
spVideoTypeOut->SetUINT32(MF_MT_SAMPLE_SIZE, 1);
MFSetAttributeSize(spVideoTypeOut.Get(), MF_MT_FRAME_RATE_RANGE_MAX, m_FramesPerSecond, 1);
MFSetAttributeSize(spVideoTypeOut.Get(), MF_MT_FRAME_RATE_RANGE_MIN, m_FramesPerSecond / 2, 1);

// Audio In Format
ComPtr<IMFMediaType> spAudioTypeIn;
CHK(MFCreateMediaType(&spAudioTypeIn));
CHK(spAudioTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
CHK(spAudioTypeIn->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
CHK(spAudioTypeIn->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, BitsPerSample));
CHK(spAudioTypeIn->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, BaseSampleRate));
CHK(spAudioTypeIn->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, ChannelCount));
CHK(spAudioTypeIn->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, AverageBytesPerSec)); // 32bit converted to 16
CHK(spAudioTypeIn->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4));
CHK(spAudioTypeIn->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE));
CHK(spAudioTypeIn->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE));

CHK(MFCreateMediaType(&audioOutputType));
CHK(audioOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
CHK(audioOutputType->SetUINT32(MF_MT_AVG_BITRATE, 16000));
CHK(audioOutputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, SamplesPerSecond));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, BitsPerSample / ((BitsPerSample > 16) ? 2 : 1)));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, ChannelCount));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 12000)); // AverageBytesPerSecond));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1));
CHK(audioOutputType->SetUINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0x29));
CHK(audioOutputType->SetUINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1));
CHK(audioOutputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, FALSE));
CHK(audioOutputType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE));
// Add Video out stream

ComPtr<IMFAttributes> encoderAttributes; 
if (TRUE)                               // Experimental
    CHK(MFCreateAttributes(&encoderAttributes, 12));
    if (TRUE) 
        unsigned force_keyframe_every_nframes = 11;
        unsigned force_bframe_every_nframes = 2;
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncMPVGOPSize, force_keyframe_every_nframes));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncMPVDefaultBPictureCount, force_bframe_every_nframes));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncNumWorkerThreads, 6));
    
    if (TRUE) 
        // constant quality for screen captures
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncCommonRealTime, 1));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncAdaptiveMode, eAVEncAdaptiveMode_Resolution));
        CHK(encoderAttributes->SetGUID(CODECAPI_AVEncCodecType, CODECAPI_GUID_AVEncH264Video));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncCommonMultipassMode, 2));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncCommonRateControlMode, eAVEncCommonRateControlMode_PeakConstrainedVBR));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncCommonMeanBitRate, DEFAULT_BITRATE));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncCommonStreamEndHandling, eAVEncCommonStreamEndHandling_EnsureComplete));
        CHK(encoderAttributes->SetUINT32(CODECAPI_AVEncVideoContentType, eAVEncVideoContentType_FixedCameraAngle));
    

CHK(m_SpSinkWriter->AddStream(spVideoTypeOut.Get(), &m_VideoStreamIndex));
CHK(m_SpSinkWriter->SetInputMediaType(m_VideoStreamIndex, spVideoTypeIn.Get(), encoderAttributes.Get()));
CHK(m_SpSinkWriter->AddStream(audioOutputType.Get(), &m_AudiostreamIndex));
CHK(m_SpSinkWriter->SetInputMediaType(m_AudioStreamIndex, spAudioTypeIn.Get(), nullptr));
_Clock->Start();
m_ClockStart = clock();
CHK(m_SpSinkWriter->BeginWriting());

【讨论】:

分段 MP4 和“mdat 之前的 moov”无关。 Moov/mdat 挑战仅适用于传统 MP4,FMP4 不存在原子顺序问题。 我知道关于原子,只是好奇,如果我将容器类型设置为 MPEG4,如果没问题,我不会得到图像文件大小,moov 在​​ mdat 和渐进式下载支持之前。当我更改为碎片时,我会得到图片。 Microsoft 的 SinkWriter 必须在后端执行某些操作才能导致这种情况发生。【参考方案3】:

您是否阅读了 http://msdn.microsoft.com/en-us/library/windows/desktop/hh870256%28v=vs.85%29.aspx?

"为了让mpeg4 sink使用这个属性,传入的字节流一定不能slow seek或者remote"

请检查您的 IMFByteStream 的功能?

MFBYTESTREAM_IS_REMOTE 和 MFBYTESTREAM_HAS_SLOW_SEEK 应该被清除。

如果您的 IMFByteStream 不符合条件 - 则首先创建一个文件 MDAT->MOOV,然后重新混合到一个新文件 MOOV->MDAT。

【讨论】:

是的,MFBYTESTREAM_IS_REMOTE 和 MFBYTESTREAM_HAS_SLOW_SEEK 未在 IMFByteStream 中设置。 “如果您的 IMFByteStream 不符合条件,则首先创建一个文件 MDAT->MOOV,然后再重新组合到一个新文件 MOOV->MDAT。”是什么意思。 如果您的流是远程的 (MFBYTESTREAM_IS_REMOTE),则不能使用 MF_MPEG4SINK_MOOV_BEFORE_MDAT。 MFBYTESTREAM_IS_REMOTE 是 FALSE,因为我是从本地文件加载的

以上是关于如何使用 Media Foundation 生成“moov before mdat”MP4 视频文件的主要内容,如果未能解决你的问题,请参考以下文章

如何使基于Media Foundation的播放打开并快速查找moov结尾的MP4 HTTP文件?

无法使用 Media Foundation h264 编码器设置自定义 SEI

如何使用 Media Foundation 从 IMFSourceReader 播放音频缓冲区

如何使用 Media Foundation 从 MP4 视频中丢弃任意帧?

如何在没有拓扑的情况下将 Windows Media Foundation 与 UWP 结合使用

如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?