DirectShow筛选器图形在某些计算机上永远不会完成

DirectShow filter graph never completes on some machines

本文关键字:永远 计算机 筛选 图形 DirectShow      更新时间:2023-10-16

我正在使用DirectShow过滤器图从视频文件中检索IMediaSample。不久前,我们从一位合同开发人员那里收到了最初的实现,我一直在绞尽脑汁,试图弄清楚为什么这些代码在我的开发机器上有效,而在我的另外两个测试服务器上却无效。

我所能告诉你的是,在"坏"的机器上,过滤图永远不会完成。我总是从IMediaEvent->WaitForCompletion()呼叫接收E_ABORT。然而,在"工作"机器上,此调用通常在大约两个循环后返回S_OK

更新: DirectShow Spy似乎不适合我。也许这是因为我们有一个自定义的、未注册的CTransInPlaceFilter来收集链中的IMediaSample?没有错误,但GraphEdit和GraphStudio在尝试连接到远程图形时都挂起了(<--正如建议的消息泵送解决了这个问题)

使用GraphStudio,我能够从连接到我们的CTransInPlaceFilter的MPEG-4解码器中获得媒体子类型。在我的机器上是MEDIASUBTYPE_YV12,但在"坏"的机器上却是MEDIASUBTYPE_IYUV。在CTransInPlaceFilterCheckInputType方法中,我们只接受MEDIASUBTYPE_RGB24,这让我相信在图中插入了一个或多个"魔术过滤器"。

更新:多亏了Roman R.,我才能让DirectShow Spy正常工作。至少在"坏了"的机器上。在"工作"的机器上,我遇到了访问违规,但过滤图运行得很快,而且被破坏了,所以很难连接到它。

我还发现我们有一个颜色空间转换器,它能够在MEDIASUBTYPE_RGB24out中处理MEDIASUBTYPE_IYUV。我把它添加到图表中,现在应该是正确的。

DirectShow Spy将此显示为筛选器图(在我看来是完整的):

文件源->MPEG Demux->MPEG4解码器->颜色空间转换器->CTransInPlaceFilter->空渲染

然而,IMediaEvent->WaitForCompletion()调用从不返回S_OK,并且过滤器图只是永远运行。所以现在我很困惑到底发生了什么。还有什么我应该检查的错误状态吗?

更新:我修改了循环,以枚举图中的过滤器并查询它们的状态:

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();

他们都处于"跑步"状态。那么,如果过滤器连接正确,并且所有过滤器都在运行,为什么图形在"坏"的机器上永远不会完成?

更新:根据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

如果不看整件事,很难说。当我开发DS过滤器时,我经常使用GraphStudio和FilterGraph Spy。

一个常见的错误是使用目标机器上可能不可用的"自动过滤器"。假设您的视频是h264,并且您尝试从中读取原始RGB,DS将自动为您提供解码器过滤器和颜色空间变换。许多中间过滤器将在您没有从代码中注意到的情况下生成。这就是为什么在可视化工具中转储图形并检查所有内容的连接方式非常重要。

我的猜测是,部署服务器上不存在一个或多个这样的"神奇过滤器"。您可以尝试直接在服务器上使用GraphStudio,并像编程一样连接所有内容,然后查看它是如何以及为什么失败的。

相关文章: