You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Media Foundation SourceReader读取MP4压缩视频返回空数据问题

基于Media Foundation的SourceReader读取MP4压缩视频数据时缓冲区为空的问题

我需要实现一个基于Media Foundation的SourceReader类,用来读取MP4文件中的压缩视频数据。已经编写了读取单帧样本的方法,运行没有报错,但缓冲区数据始终为空。已经验证过文件是有效的,也尝试过使用原生MediaType和手动配置MediaType,相关代码如下:

HRESULT MfVideoReader::Initialize(std::string filePath) {
    try {
        std::wstring wsFilePath = StringHelper::str2wstr(filePath);
        throw_if_fail(MFCreateSourceReaderFromURL(wsFilePath.c_str(), NULL, &pSourceReader));
        throw_if_fail(pSourceReader->GetNativeMediaType(0, 0, &pOutputMediaType));
        throw_if_fail(pSourceReader->SetCurrentMediaType(0, NULL, pOutputMediaType));
        return S_OK;
    }
    catch (const std::exception& e) {
        Logger::log_error("Fail to initialize the video source reader", e.what());
        return E_FAIL;
    }
}

HRESULT MfVideoReader::ReadFrame(BYTE* frameData, LONGLONG* pSampleTime) {
    try {
        CComPtr<IMFSample> pSample;
        DWORD stream = 0;
        DWORD streamFlags = 0;
        throw_if_fail(pSourceReader->ReadSample(0, 0, &stream, &streamFlags, pSampleTime, &pSample));
        
        CComPtr<IMFMediaBuffer> pMediaBuffer;
        throw_if_fail(pSample->GetBufferByIndex(0, &pMediaBuffer));

        BYTE* pData = nullptr;
        DWORD maxLength = 0;
        DWORD dataSize = 0;
        throw_if_fail(pMediaBuffer->Lock(&pData, &maxLength, &dataSize));
        memcpy(frameData, pData, dataSize);
        throw_if_fail(pMediaBuffer->Unlock());

        return S_OK;
    }
    catch (const std::exception& e) {
        Logger::log_error("Fail to read a sample", e.what());
        return E_FAIL;
    }
}

问题排查与解决方法

1. 必须检查ReadSample返回的流标志

ReadSample返回S_OK并不代表一定拿到了有效样本,需要先检查streamFlags:

  • 如果包含MF_SOURCE_READERF_ENDOFSTREAM,说明已到文件末尾,pSample会为空;
  • 如果包含MF_SOURCE_READERF_NEWSTREAM或MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED,需要重新处理媒体类型;
  • 若pSample为空但无错误标志,说明正在缓冲,需要循环读取直到拿到有效样本。

修改ReadFrame中的读取逻辑:

do
{
    throw_if_fail(pSourceReader->ReadSample(videoStreamIndex, 0, &stream, &streamFlags, pSampleTime, &pSample));
    if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
    {
        // 到达文件末尾,返回对应错误
        return E_FAIL;
    }
} while (pSample == nullptr);

2. 确认正确的视频流索引

MP4文件的流索引0不一定是视频流(可能是音频流),需要遍历所有流找到视频流:

HRESULT FindVideoStreamIndex(IMFSourceReader* pReader, DWORD* pIndex)
{
    DWORD streamCount = 0;
    HRESULT hr = pReader->GetStreamCount(&streamCount);
    if (FAILED(hr)) return hr;

    for (DWORD i = 0; i < streamCount; i++)
    {
        CComPtr<IMFMediaType> pType;
        hr = pReader->GetNativeMediaType(i, 0, &pType);
        if (FAILED(hr)) continue;

        GUID majorType;
        hr = pType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
        if (SUCCEEDED(hr) && majorType == MFMediaType_Video)
        {
            *pIndex = i;
            return S_OK;
        }
    }
    return E_FAIL;
}

在Initialize函数中调用该函数获取正确的视频流索引,替换硬编码的0。

3. 验证媒体类型为压缩视频格式

确保选择的原生MediaType是压缩视频类型(如H.264的MFVideoFormat_H264),可以在设置MediaType前检查:

GUID subType;
throw_if_fail(pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &subType));
// 可根据需求扩展支持的压缩格式
if (subType != MFVideoFormat_H264 && subType != MFVideoFormat_MPEG4)
{
    pOutputMediaType.Release();
    // 尝试枚举下一个原生类型
    throw_if_fail(pSourceReader->GetNativeMediaType(videoStreamIndex, 1, &pOutputMediaType));
}

4. 确保缓冲区有足够空间

调用ReadFrame前,需要保证frameData的大小不小于样本的dataSize。可以修改ReadFrame,先返回数据大小,或者传入缓冲区大小参数做校验:

HRESULT MfVideoReader::GetFrameSize(DWORD* pDataSize)
{
    // 读取一个样本获取大小后释放,或者从媒体类型推导
    // 示例逻辑:
    CComPtr<IMFSample> pSample;
    DWORD stream = 0;
    DWORD streamFlags = 0;
    LONGLONG sampleTime = 0;
    throw_if_fail(pSourceReader->ReadSample(videoStreamIndex, 0, &stream, &streamFlags, &sampleTime, &pSample));
    if (!pSample) return E_FAIL;

    CComPtr<IMFMediaBuffer> pMediaBuffer;
    throw_if_fail(pSample->GetBufferByIndex(0, &pMediaBuffer));
    return pMediaBuffer->GetCurrentLength(pDataSize);
}

内容的提问来源于stack exchange,提问作者m3tler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 05:17:02