我的任务是创建一个应用程序,该应用程序接受音频/视频捕获输入(使用 Elgato Cam Link)并将其输出回用户。根据 Microsoft 的Audio/Video Capture in Media Foundation文档,它建议“如果要将音频捕获与视频捕获结合起来,请使用聚合媒体源。 ”
我从 topoedit 的源代码中提取了我需要的大部分代码,但是 topoedit 没有在任何地方使用聚合源,所以在正确使用它们时我有点盲目。
使用聚合媒体源时,我遇到了两个不同的问题。
帧速率
当我使用聚合媒体源时,我有一个捕获设备 (Elgato Cam Link) 可以成功播放音频和视频,但由于某种原因,帧速率比我只运行两个完全独立的拓扑(一个用于视频,一个用于音频)。调用 IMFTopoloader::Load() 时出现错误 MF_E_TOPO_CODEC_NOT_FOUND (0xC00D5212)
当我尝试在两台不同的笔记本电脑上使用聚合媒体源(使用任何内置的网络摄像头/麦克风组合)时,我遇到了这个错误。这个错误让我感到困惑,因为如果我使用两个完全独立的拓扑(一个用于视频,一个用于音频),它运行得很好。
主要设置:
- 操作系统:Windows 10
- 采集设备:Elgato Cam Link(带有 HDMI 输入的 USB 记忆棒)
- 语言:C++
只是为了提前排除一些建议......
只需使用 DirectShow!
我希望我能。我负责这个任务的唯一原因是因为我们的 DirectShow 解决方案在 Elgato Cam Link 设备上存在严重的帧速率问题。我们无法弄清楚原因,所以媒体基金会似乎是我们最好的选择。使用两个独立的拓扑!
这就是我目前的工作方式。只是感觉不对劲,你知道吗?文档说要使用聚合源,所以我想做我的尽职调查。最后,如果我没有得到这个问题的答案,我至少可以高枕无忧,因为我知道我有一些功能,但我主要是问这个问题,以防我能做得更好。Skype必须做这样的事情吗?(再想一想,Skype 不会将您的音频输出回您自己的扬声器,所以也许不是)肯定有人知道如何使用聚合源。
鉴于我的程序在 Elgato 设备上成功运行,我怀疑我没有做任何可怕的错误。如果有问题,可能是我没有的代码,而不是我能展示的任何东西。
话虽如此,这就是我创建聚合源的地方(为简洁起见,不包括错误检查)。
HRESULT GenerateAggregateSource(IMFMediaSource*& pAggSource, IMFMediaSource* pSource1, IMFMediaSource* pSource2)
{
pAggSource = NULL;
HRESULT hr = S_OK;
IMFCollection* pSourceCollection = NULL;
hr = ::MFCreateCollection(&pSourceCollection);
hr = pSourceCollection->AddElement(pSource1);
hr = pSourceCollection->AddElement(pSource2);
hr = ::MFCreateAggregateSource(pSourceCollection, &pAggSource);
pSourceCollection->Release(); // Done with this
return hr;
}
稍后我将 EVR 连接到流描述符 0,将 SAR 连接到流描述符 1。
如果您更热衷于解决问题 2(再次,为简洁起见,不包括错误检查),我将这里称为 IMFTopoloader::Load()。
HRESULT ResolveTopology(IMFTopology*& pTopology, IMFMediaSession* pMediaSession)
{
HRESULT hr = S_OK;
IMFTopoLoader* pTopoLoader = NULL;
hr = ::MFCreateTopoLoader(&pTopoLoader);
IMFTopology* pFullTopology = NULL;
hr = pTopoLoader->Load(pTopology, &pFullTopology, NULL);
// Laptop webcams seem to encounter this error here
//MF_E_TOPO_CODEC_NOT_FOUND
hr = pMediaSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pFullTopology);
// Swap the topology we're holding
pTopology->Release();
pTopology = pFullTopology;
// Done with this
pTopoLoader->Release();
return hr;
}
使用请求的拓扑更新:
这是我的部分拓扑的粗略示例。这可能是您所期望的,因为我不想做任何花哨的事情。
⇗ EVR
Agg. Source (A/V)
⇘ SAR
一旦我解决了拓扑,注入了哪些额外的东西?我不确定如何走链并查询当时拓扑中的所有内容。如果您相信 topoedit 分别对音频和视频源所做的事情,那么它看起来像这样。
⇗ {CF862982-23B0-4E3D-8C76-D03FEF084AF8} ⇒ EVR
Agg. Source (A/V)
⇘ SAR
是什么{CF862982-23B0-4E3D-8C76-D03FEF084AF8}
?我不确定,也不知道如何找到。
当我再次访问 Elgato 时,我将更新上述拓扑。我目前没有拥有它。
更新 2写这篇文章的客户似乎并不关心向我提供任何反馈(好或坏),所以我不太关心跟进这个奇怪的问题。