1

我正在研究 DirectShow 过滤器图以IMediaSample从视频文件中检索。不久前,我们从合同开发人员那里收到了最初的实现,我一直在努力想弄清楚为什么这段代码可以在我的开发机器上运行,但不能在我拥有的另外两台测试服务器上运行。

尽我所能告诉过滤器图永远不会在“损坏”的机器上完成。我总是接到E_ABORT电话IMediaEvent->WaitForCompletion()。然而,在“工作”机器上,这个调用通常S_OK在大约两个循环后返回。

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

使用GraphStudio,我能够从连接到我们的CTransInPlaceFilter. 在我的机器上它是MEDIASUBTYPE_YV12,但在“坏”机器上它是MEDIASUBTYPE_IYUV. 在我们的CheckInputType方法中,CTransInPlaceFilter我们只接受MEDIASUBTYPE_RGB24这让我相信有一个或多个“魔法过滤器”被插入到图中。

更新:感谢 Roman R。我能够让DirectShow Spy工作。至少在“坏掉”的机器上。在“工作”的机器上,我遇到了访问冲突,但过滤器图运行很快并且被拆除,因此很难连接到它。

我还发现我们有一个能够处理MEDIASUBTYPE_IYUV输入MEDIASUBTYPE_RGB24输出的色彩空间转换器。我将其添加到图表中,现在应该是正确的。

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从损坏的机器上的过滤器图中删除了我们,并且该图成功完成。连接后,CTransInPlaceFilterCPU 使用率降至零。所以现在我不确定为什么下面的代码适用于某些机器而不适用于其他机器。我将开始添加一些调试日志以CTransInPlaceFilter尝试找出发生了什么(或没有发生)。


解决方案:正如 Roman R. 所建议的那样(我觉得我在重复自己:P)问题最终陷入僵局。损坏的机器都有一个 CPU/核心,而工作机器有多个 CPU/核心。该应用程序由每个源视频的线程、合并线程和目标线程组成。

源线程运行一个过滤器图(我假设过滤器图也在它自己的线程中运行)以从 an 检索数据IMediaSample并将其放入CQueue<BYTE*>.

合并线程遍历源,从源检索样本数据CQueue<BYTE*>,将帧合并为单个图像,并将它们发送到CQueue<BYTE*>目标线程消耗。

目标线程运行另一个过滤器图来编码视频/音频。

Put 上的CQueue<BYTE*>块,直到有可用空间。通常这很好,因为合并线程正在删除项目。然而,在单个 CPU/核心机器上,合并线程被源线程阻塞。

长话短说,Sleep(0);这里和那里允许源线程屈服于合并线程,问题似乎得到了解决。

4

2 回答 2

2

播放完成在内部包括从流源发送流结束通知,这些通知由下游过滤器中继,在渲染器上收集,然后组合起来报告给应用程序。因此,成功完成取决于过滤图的所有参与者都正确行事。

您发现了图形的拓扑,并且需要比较不同机器上的拓扑。如果您在那里看到任何差异,他们可能会建议哪个过滤器可能会丢失完成通知。

但是,即使拓扑准确匹配,某些过滤器也可能由于其他原因而采取不同的行动。特别是,在图表上拥有自己的自定义过滤器很可能会丢失通知并且图表永远不会完成。它停止处理数据并从那里闲置(这是您要检查的另一件事 - 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
于 2013-08-16T19:05:32.050 回答
2

不看整个事情很难说。当我开发 DS 过滤器时,我大量使用了 GraphStudio 和FilterGraph Spy

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

我的猜测是您的部署服务器上不存在这些“魔术过滤器”中的一个或多个。您可以尝试的方法是直接在服务器上使用 GraphStudio,并像以编程方式执行的所有操作一样连接所有内容,然后查看它是如何以及为什么从那里失败的。

于 2013-08-15T17:48:20.100 回答