媒体基础获取拓扑节点的 IMFMedia类型

Media Foundation get IMFMediaType of a topology node

本文关键字:节点 IMFMedia 类型 获取 媒体      更新时间:2023-10-16

>我写了一个基于SampleGrabberSink示例的应用程序。我的应用程序实际上可以按我想要的方式工作,但我需要执行的一个功能是在.mp4源在文件中部分更改时获得视频分辨率。我最终想出了如何做到这一点,但它似乎很冗长,我怀疑一定有更简单的方法。

在下面的示例中,有没有办法缩短处理MESessionStreamSinkFormatChanged情况的代码块?似乎需要近 40 行代码(计算初始化和清理(应该需要 1 或 2 行。

HRESULT RunSession(IMFMediaSession *pSession, IMFTopology *pTopology, OnVideoResolutionChangedFunc onVideoResolutionChanged)
{
IMFMediaEvent *pEvent = NULL;
IMFTopologyNode *pNode = nullptr;
IMFStreamSink *pStreamSink = nullptr;
IUnknown *pNodeObject = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = nullptr;
IMFMediaType *pMediaType = nullptr;
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = S_OK;
CHECK_HR(hr = pSession->SetTopology(0, pTopology));
CHECK_HR(hr = pSession->Start(&GUID_NULL, &var));
while(1)
{
HRESULT hrStatus = S_OK;
MediaEventType met;
CHECK_HR(hr = pSession->GetEvent(0, &pEvent));
CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
CHECK_HR(hr = pEvent->GetType(&met));
if(FAILED(hrStatus))
{
printf("Session error: 0x%x (event id: %d)n", hrStatus, met);
hr = hrStatus;
goto done;
}
else
{
//printf("Session event: event id: %dn",  met);
switch(met)
{
case MESessionStreamSinkFormatChanged:
//std::cout << "MESessionStreamSinkFormatChanged." << std::endl;
{
MF_TOPOLOGY_TYPE nodeType;
UINT64 outputNode{0};
GUID majorMediaType;
UINT64 videoResolution{0};
UINT32 stride{0};
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!
CHECK_HR_ERROR(pEvent->GetUINT64(MF_EVENT_OUTPUT_NODE, &outputNode), "Failed to get ouput node from media changed event.");
CHECK_HR_ERROR(pTopology->GetNodeByID(outputNode, &pNode), "Failed to get topology node for output ID.");
CHECK_HR_ERROR(pNode->GetObject(&pNodeObject), "Failed to get the node's object pointer.");
CHECK_HR_ERROR(pNodeObject->QueryInterface(IID_PPV_ARGS(&pStreamSink)), "Failed to get media stream sink from activation object.");
CHECK_HR_ERROR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler from stream sink.");
CHECK_HR_ERROR(pMediaTypeHandler->GetCurrentMediaType(&pMediaType), "Failed to get current media type.");
CHECK_HR_ERROR(pMediaType->GetMajorType(&majorMediaType), "Failed to get major media type.");
if(majorMediaType == MFMediaType_Video)
{
CHECK_HR_ERROR(pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &videoResolution), "Failed to get new video resolution.");
CHECK_HR_ERROR(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride), "Failed to get the new stride.");
std::cout << "Media session video resolution changed to width " << std::to_string(HI32(videoResolution)) 
<< " and height " << std::to_string(LO32(videoResolution)) 
<< " and stride " << stride << "." << std::endl;
if(onVideoResolutionChanged != nullptr) {
onVideoResolutionChanged(HI32(videoResolution), LO32(videoResolution), stride);
}
}
break;
}
default:
break;
}
}
if(met == MESessionEnded)
{
break;
}
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
}
done:
SafeRelease(&pEvent);
SafeRelease(&pNode);
SafeRelease(&pStreamSink);
SafeRelease(&pNodeObject);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pMediaType);
return hr;
}
// This seems a ridiculously convoluted way to extract the change to the video resolution. There may
// be a simpler way but then again this is the Media Foundation and COM!

代码看起来不错。无需对分辨率更改执行所有操作 - 只需检索一次媒体类型处理程序,并在需要时保留指针。

关于上面的有趣评论,我会说以下内容。就像在DirectShow的情况下一样,样品抓取器是一种努力偷工减料并做一些反对管道设计的方法。几乎每个人都喜欢DirectShow样本抓取器,所以媒体基础样本抓取器的未来可能是如果有足够多的人首先为媒体基金会开发。

分辨率更改通常是基元的业务,即源-转换、转换-转换和转换-接收器连接。即使在这种情况下,您也会收到带外分辨率更改的通知(这对您来说是异步通知(,您很幸运 媒体基金会及其样本抓取器非常灵活,您可以首先处理这个问题。

为了可靠地实现这一点,您通常需要一个自定义媒体接收器,但示例抓取器即使在此时也可以让您切入角落。

使用自定义接收器实现,可以保证在首先同意新分辨率之前不会收到具有新分辨率的媒体示例(当然,您可以拒绝它(。但是,有了MESessionStreamSinkFormatChanged,事件将发布以进行异步检索,并且样本抓取器将继续处理,因此从技术上讲,您可以在获取会话事件之前使用新分辨率的帧进行抓取器回调。

如果在实际应用程序中使用流接收器而不是媒体接收器激活(如上面的示例(创建输出节点,则无需使用拓扑节点检索媒体类型句柄 - 可以直接拉取它。