【发布时间】:2013-08-15 17:11:54
【问题描述】:
我正在制作 DirectShow 过滤器图以从视频文件中检索 IMediaSample。不久前,我们从合同开发人员那里收到了最初的实现,我一直在拼命想弄清楚为什么这段代码在我的开发机器上运行,但在我拥有的另外两台测试服务器上却不行。
尽我所能告诉过滤器图永远不会在“坏”机器上完成。我总是从IMediaEvent->WaitForCompletion() 电话中收到E_ABORT。然而,在“工作”机器上,此调用通常在大约两个循环后返回 S_OK。
更新: DirectShow Spy 似乎对我不起作用。也许那是因为我们有一个自定义的、未注册的(
CTransInPlaceFilter 来收集链中的IMediaSample?没有错误,但 GraphEdit 和 GraphStudio 在尝试连接到远程图形时都会挂起。
使用GraphStudio,我能够从连接到CTransInPlaceFilter 的MPEG-4 解码器中获取媒体子类型。在我的机器上是MEDIASUBTYPE_YV12,但在“损坏”的机器上是MEDIASUBTYPE_IYUV。在我们的CTransInPlaceFilter 的CheckInputType 方法中,我们只接受MEDIASUBTYPE_RGB24,这让我相信图中插入了一个或多个“魔法过滤器”。
更新:感谢 Roman R。我能够让 DirectShow Spy 正常工作。至少在“坏掉”的机器上。在“工作”的机器上,我遇到了访问冲突,但过滤器图运行很快并且被拆除,因此很难连接到它。
我还发现我们有一个色彩空间转换器,它能够处理MEDIASUBTYPE_IYUV in MEDIASUBTYPE_RGB24 out。我将它添加到图表中,现在应该是正确的。
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); 在这里和那里允许源线程让步给合并线程,问题似乎已经解决。
【问题讨论】:
-
代码 sn-p 不太可能与问题相关。您需要确保首先成功创建过滤器图。然后,在运行图之前,您需要检查它的有效拓扑以查看里面的内容。在此步骤中,您可能会看到系统之间的差异。然后您将看到是什么导致流式传输不完整。
-
如果你看到你的图表,那么 spy 确实有效。但是,如果您在流式传输过程中不泵送窗口消息,则间谍和图形远程处理工具将冻结 - 所以这更多的是您的代码问题(当然不是致命的,但无论如何消息泵送是个好主意)。
标签: c++ visual-c++ directshow