我想创建一个类,负责使用 Media Foundation 的 SourceReader 从 MP4 文件中读取压缩视频数据。我编写了一个读取单个样本的方法,它运行时没有任何错误,但缓冲区中的数据始终为空。我按照以下教程寻求指导:
我已经检查了文件验证,一切看起来都很好。此外,我尝试使用本机 MediaType 和手动配置的 MediaType 来配置 SourceReader。这是我的代码:
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;
}
}
我根据与读取音频流并将其写入文件相关的Microsoft 示例修改了代码。修改后,代码如下:
HRESULT MfVideoReader::Initialize(const std::string& filePath) {
try {
std::wstring wsFilePath = StringHelper::str2wstr(filePath);
throw_if_fail(MFCreateSourceReaderFromURL(wsFilePath.c_str(), NULL, &pSourceReader));
CComPtr<IMFMediaType> pUncompressedVideoType;
CComPtr<IMFMediaType> pPartialType;
throw_if_fail(MFCreateMediaType(&pPartialType));
throw_if_fail(pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
throw_if_fail(pPartialType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
throw_if_fail(pSourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pPartialType));
throw_if_fail(pSourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pUncompressedVideoType));
throw_if_fail(pSourceReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE));
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 streamFlags = 0;
throw_if_fail(pSourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &streamFlags, pSampleTime, &pSample));
CComPtr<IMFMediaBuffer> pMediaBuffer;
BYTE* pData = NULL;
DWORD cbBuffer = 0;
throw_if_fail(pSample->ConvertToContiguousBuffer(&pMediaBuffer));
throw_if_fail(pMediaBuffer->Lock(&pData, NULL, &cbBuffer));
memcpy(frameData, pData, cbBuffer);
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;
}
}
执行测试后,我注意到这种情况下并非支持所有视频格式。以下是支持的视频格式列表:
对于H264格式,没有设置标志,数据为空。对于 I420、IYUV、NV12、YUY2 和 YV12 格式,设置 MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED 标志并正确返回数据。