我正在处理 DirectShow 过滤器图以检索 IMediaSample
来自视频文件。不久前,我们从契约(Contract)开发人员那里收到了原始实现,我一直在用头撞墙,试图弄清楚为什么这段代码在我的开发机器上运行,但在我拥有的另外两台测试服务器上运行不正常。
据我所知,过滤器图永远不会在“损坏”的机器上完成。我总是收到 E_ABORT
来自 IMediaEvent->WaitForCompletion()
称呼。然而在“工作”机器上,这个调用通常返回 S_OK
在大约两个循环之后。
更新: DirectShow Spy似乎不适合我。也许那是因为我们有一个未注册的自定义 (<-- 正如建议的消息泵解决了这个问题)CTransInPlaceFilter
收集 IMediaSample
在链中?没有错误,但 GraphEdit 和 GraphStudio 只是在尝试连接到远程图形时挂起。
使用 GraphStudio我能够从连接到我们的 CTransInPlaceFilter
的 MPEG-4 解码器获取媒体子类型。 .在我的机器上它是 MEDIASUBTYPE_YV12
但在“坏”机器上它是MEDIASUBTYPE_IYUV
.在CheckInputType
我们的方法CTransInPlaceFilter
我们只接受MEDIASUBTYPE_RGB24
这让我相信图中插入了一个或多个“魔法过滤器”。
更新:感谢 Roman R。我能够获得 DirectShow Spy在职的。至少在“坏”机器上。在“正常工作”的机器上,我遇到了访问冲突,但过滤器图运行很快并且被拆除,因此很难连接到它。
我还发现我们有一个能够处理 MEDIASUBTYPE_IYUV
的颜色空间转换器在MEDIASUBTYPE_RGB24
出去。我将其添加到图表中,现在应该是正确的。
DirectShow Spy将其显示为过滤图(对我来说看起来很完整):
File Source -> MPEG Demux -> MPEG4 Decoder -> Color Space Converter -> CTransInPlaceFilter -> Null Render
However the IMediaEvent->WaitForCompletion()
call never returns S_OK
and the filter graph just runs forever. So now I'm stumped as to what is going on. Is there anything else I should be checking for an error state or something?
Update: I modified the loop to enumerate the filters in the graph and query their state:
char debugString[512];
int count = 0;
long EvCode;
mediaFilter->SetSyncSource(NULL);
hr = mediaControl->Run();
sprintf(debugString, "mediaControl->Run() %d", hr);
DebugLog(debugString);
while (!m_ThreadKill)
{
hr = mediaEvent->WaitForCompletion(200, &EvCode);
sprintf(debugString, "mediaEvent->WaitForCompletion() %d, %d", hr, count);
DebugLog(debugString);
count++;
IEnumFilters *pEnum = NULL;
IBaseFilter *pFilter;
ULONG cFetched;
graphBuilder->EnumFilters(&pEnum);
while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
{
FILTER_INFO FilterInfo;
FILTER_STATE FilterState;
char szName[256];
pFilter->GetState(200, &FilterState);
pFilter->QueryFilterInfo(&FilterInfo);
WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName, -1, szName, 256, 0, 0);
sprintf(debugString, "Filter: %s, %d", szName, FilterState);
DebugLog(debugString);
SAFE_RELEASE(FilterInfo.pGraph);
SAFE_RELEASE(pFilter);
}
SAFE_RELEASE(pEnum);
if (hr == S_OK)
{
break;
}
}
sprintf(debugString, "mediaControl->Stop()");
DebugLog(debugString);
mediaControl->Stop();
都处于“Running”状态。因此,如果过滤器连接正确并且所有过滤器都在运行,为什么图形在“损坏”的机器上永远不会完成?
更新:按照 Roman R 的建议,我删除了我们的 CTransInPlaceFilter
从损坏的机器上的过滤器图和图成功完成。随着CTransInPlaceFilter
连接后 CPU 使用率降为零。所以现在我不确定为什么下面的代码在某些机器上工作而不是其他机器。我将开始向 CTransInPlaceFilter
添加一些调试日志记录尝试找出正在发生(或未发生)的事情。
解决方案: 正如 Roman R. 所建议的(我觉得我在重复自己 :P),问题最终变成了僵局。损坏的机器都有一个 CPU/内核,而工作机器有多个 CPU/内核。该应用程序由每个源视频一个线程、一个合并线程和一个目标线程组成。
源线程运行一个过滤图(我假设过滤图也在它自己的线程中运行)以从 IMediaSample
中检索数据并将其放入 CQueue<BYTE*>
.
合并线程遍历源,从源中检索样本数据 CQueue<BYTE*>
,将帧合并为单个图像,并将它们发送到 CQueue<BYTE*>
目标线程消耗。
目标线程运行另一个过滤图来对视频/音频进行编码。
CQueue<BYTE*>
在 Put 上阻塞,直到有可用空间。通常这很好,因为合并线程正在删除项目。然而,在单 CPU/核心机器上,合并线程被源线程阻塞。
长话短说 Sleep(0);
在这里和那里允许源线程屈服于合并线程,问题似乎已解决。
最佳答案
播放完成在内部包括从流源发送流结束通知,这些通知由下游过滤器中继,在渲染器上收集,然后合并,报告给应用程序。因此,成功完成取决于过滤图的所有参与者是否正确行事。
您发现了图形的拓扑结构,并且需要比较不同机器上的拓扑结构。如果您在那里看到任何差异,他们可能会建议哪个过滤器可能会丢失完成通知。
但是,即使拓扑准确匹配,某些过滤器也可能由于其他原因而采取不同的行为。特别是,在图表上使用您自己的自定义过滤器很可能会丢失通知并且图表永远不会完成。它停止处理数据并从那里闲置(这是您要检查的另一件事 - CPU 消耗是否降低到零或某些处理仍在进行,在这种情况下您可以将问题重新限定为死锁)。
您可以或多或少地轻松解决此问题的方法是开始从图表中删除过滤器,以确定哪个过滤器确实带来了问题。尝试这些图表可能会发现违规者:
File Source -> MPEG Demux -> MPEG4 Decoder -> Color Space Converter -> Null Render
File Source -> MPEG Demux -> MPEG4 Decoder -> Null Render
File Source -> MPEG Demux -> Null Render
关于c++ - DirectShow 过滤器图在某些机器上永远不会完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18257910/