媒体基础获取拓扑节点的 IMFMedia类型
Media Foundation get IMFMediaType of a topology node
>我写了一个基于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
,事件将发布以进行异步检索,并且样本抓取器将继续处理,因此从技术上讲,您可以在获取会话事件之前使用新分辨率的帧进行抓取器回调。
如果在实际应用程序中使用流接收器而不是媒体接收器激活(如上面的示例(创建输出节点,则无需使用拓扑节点检索媒体类型句柄 - 可以直接拉取它。
- 反向给定链表中的K节点
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- Boost Graph Library,修复节点大小
- C++A*算法并不总是在路径中具有目标节点
- 如何找到2个单链表的公共节点
- 计算每个节点的树高,帮助我解释这个代码解决方案
- 为什么我的删除节点函数实际上没有删除节点?
- 我们可以删除链表中静态内存中的节点吗
- 如何在pugixml中获取节点的内部XML
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- C++RapidXml-使用first_node()遍历以修改XML文件中节点的值
- 为什么C++对链表中的下一个节点使用指针,而像 C# 或 Java 这样的语言只使用类 Node 的名称?
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- 节点 *temp; 和节点 *tmp = 新节点之间的差异
- 引擎节点:未定义的符号:_ZTV6Config
- 为什么"delete"关键字不删除节点?
- 如何使用发送数据包所花费的时间计算两个节点之间的距离?
- 如何按数字顺序插入链表节点?
- 为什么我的双向链表删除函数会删除多个节点?
- 媒体基础获取拓扑节点的 IMFMedia类型