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
相关产品推荐
相关产品推荐

