第四章 使用DirectShow
(本人英语也不算好,第一次翻译MSDN,希望对初学者有帮助,更快的学习。有不恰当的地方,请多多指出,我会尽早修改。)
4.1 用graphedit模拟创建Graph
(Simulating Graph Building with GraphEdit)
DirectShow提供了一种叫做GraphEdit的调试公共程序,你可以用它来创建和测试filter graphs。
4.1.1 graphedit概述
GraphEdit是一个可视化工具,可以创建filter graphs。通过GraphEdit,你可以在写程序代码前实验filter graph。你也可以加载一个你的程序创建的filter graph,来核实你的程序是否创建了正确的Graph。如果你开发一个定制的filter,GraphEdit提供了一种快速的方式测试它:简单的加载一个带有你定制的filter的graph,并且试着运行它。如果你是一个directShow的新手,GraphEdit是一个好的方式,熟悉filter graph和DirectShow架构。
收下的插图显示了用GraphEdit怎么表示一个简单的filter graph。
每一个filter用一个矩形来表示。在filter边缘的小的正方形代表pin。输入pin在filter的左边,输出pin 在右边。箭头代表pin之间的联结。
使用GraphEdit,你可以:
l 你可以使用一个可视的可拖放的界面来创建和修改filter graph。
l 模拟编程调用来创建一个graph,例如IGraphBuilder::RenderFile。
l 运行,暂停,停止 和搜索一个graph。
l 看哪些filter已经在你的电脑上注册了,查看每一个filter的注册信息。
l 观察filter的页面属性。
l 观察pin连接的媒体类型。
4.1.2 使用graphedit
这一章包含了你可以使用graphedit 工具的做的一些事的简单介绍。要了解关于这些特征更详细的信息,请参照Graphedit程序的帮助文档。
创建一个文件播放graph
Graphedit可以创建一个文件播放 的filter graph。这个特点是与在一个程序中调用IGraphBuilder::RenderFile的方法有同样的作用。在【File】菜单中,点击【Render Media File】。Graphedit显示了Open File对话框。选择一个多媒体文件并点击【Open】。GraphEdit创建了一个filter graph来播放这个你选择的文件。
你也可以render一个位于url上的媒体文件。从【file】菜单上,点击【Render URL】,Graphedit显示了一个对话框,可以输入URL。
创建一个定制的Filter Graph
GraphEdit 可以创建一个定制的filter graph,使用您的系统中已经注册的一些fiters。从【Graph】菜单中,点击【Insert Filter】。一个根据filter类型组织的你的系统中的filter的列表的对话框,显示出来。GraphEdit创建了这个列表根据你的注册表信息。下图显示了此对话框。
添加一个filter到graph中,选择filter的名字,并点击【Insert Filters】按钮,或者双击filter的名字。你添加了filter以后,你可以连接两个filter,通过从一个filter的output pin 拖放鼠标到另一个filter的 input pin。如果pin接受了连接,Graphedit画一个箭头来连接他们。
运行graph
一旦你已经在graphedit中 创建了一个filter graph,你可以运行这个graph,看到它是否可以像你期望的那样工作。Graph菜单包含了播放,暂停和停止命令的菜单。这些命令分别调用了IMediaControl的Run,Pause,Stop方法。GraphEdit的工具栏有这些命令的按钮如下:
注意:GrhphEdit 停止命令(Stop)停止了graph,并且把时间定位为0(假定graph是可定位的)。对于文件回放,这个动作重置了视频窗口到第一帧。然后调用 IMediaControl::Stop。
如果graph是可定位的,你可以拖动滑动条来定位。拖动滑动条调用了IMediaSeeking::SetPositions方法。
查看属性页
一些filter支持定制属性页,它提供了设置属性的用户界面。在graphEdit中查看一个filter的属性页,右键点击filter,并在弹出窗口中选择【Properties】。Graphedit显示一个属性页包含此filter定义的属性表。此外,GraphEdit 包含了所有在filter上pin的属性表。Pin属性表被graphedit定义,不是被filter定义。如果pin被连接了,pin的属性表显示连接的媒体类型。另外,它列出了pin的首选媒体类型。
4.1.3 从外部过程加载一个graph
Graphedit可以载入一个由外部程序创建的filter graph 。利用这个特点,你可以精确的看到你的程序创建的filter graph,只有少量的额外的代码在你的程序中。
注意:这个特点需要windows 2000,windows XP,或者更新的版本。
注意:从Windows Vista开始,你必须注册proppage.dll才能使这个功能能用。
程序必须在运行对象表(Running Object Table(ROT))中注册filter graph实例。ROT是一个全局可查找的表保持运行跟踪。对象在ROT中用别名注册。为了连结graph,GraphEdit查找了ROT中显示名称与特有格式匹配的别名:
!FilterGraph X pid Y
X是十六进制的fiter graph manager的地址, Y是过程ID,也是十六进制的。
当你的程序第一次创建fiter graph,调用下面的函数。
HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) { IMoniker * pMoniker = NULL; IRunningObjectTable *pROT = NULL; if (FAILED(GetRunningObjectTable(0, &pROT))) { return E_FAIL; } const size_t STRING_LENGTH = 256; WCHAR wsz[STRING_LENGTH]; StringCchPrintfW(wsz, STRING_LENGTH, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, pMoniker, pdwRegister); pMoniker->Release(); } pROT->Release(); return hr; }
|
这个函数创建了一个别名和一个新的ROT入门。第一个参数是一个指向filter graph的指针。第二参数接收一个识别新ROT入口的值。在程序释放filter graph前,调用以下的函数移除ROT入口。pdwRegister 这个参数是AddToRot函数返回的标识。
void RemoveFromRot(DWORD pdwRegister) { IRunningObjectTable *pROT; if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { pROT->Revoke(pdwRegister); pROT->Release(); } }
|
以下的代码示例显示了怎么调用这些函数。在这个例子中,添加和删除ROT入口的代码是有条件编译的,因此只包含了调试版。
IGraphBuilder *pGraph; DWORD dwRegister; // Create the filter graph manager. CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); #ifdef _DEBUG hr = AddToRot(pGraph, &dwRegister); #endif // Rest of the application (not shown). #ifdef _DEBUG RemoveFromRot(dwRegister); #endif pGraph->Release();
|
在GraphEdit中查看filter graph,同时运行你的程序和graphedit。在GraphEdit【File】菜单中,点击【Connect to Remote Graph…】。在【Connect To Graph】对话框中,选择你程序的PID并点击【OK】。GraphEdit加载filter graph并显示。不要用其他的GraphEdit功能在这个graph中,可能会引用 不可预料的结果。例如,不要添加和删除filter,或者停止和开始graph。在退出程序前关闭graphEdit。
注意:你的程序可能引起中断当它存在时。你可以忽略它们。
下图显示了【Connect To Graph】对话框。
GraphEdit加载一个graph,它在目标程序的环境中执行。因此,GraphEdit限制因为它在等待一个线程。例如,这在调试你的代码时可能发生。
这个功能应该只用在调试版的程序中,不是发布版中,因为它可能使别程序查看或者控制filter graph。
从命令行连接到一个远程graph
GraphEdit支持命令行选项,加载一个远程Graph自动启动。语法是:
GraphEdt -a moniker
其中的moniker是一个用AddToRot函数先前创建和描述的。
4.1.4 保存一个filter graph到graphedit文件中
下面的代码例子显示了怎么保存一个filter graph 成.grf文件。这可能对你调试你的程序有用。
HRESULT SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath) { const WCHAR wszStreamName[] = L"ActiveMovieGraph"; HRESULT hr; IStorage *pStorage = NULL; hr = StgCreateDocfile( wszPath, STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pStorage); if(FAILED(hr)) { return hr; } IStream *pStream; hr = pStorage->CreateStream( wszStreamName, STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream); if (FAILED(hr)) { pStorage->Release(); return hr; } IPersistStream *pPersist = NULL; pGraph->QueryInterface(IID_IPersistStream, (void**)&pPersist); hr = pPersist->Save(pStream, TRUE); pStream->Release(); pPersist->Release(); if (SUCCEEDED(hr)) { hr = pStorage->Commit(STGC_DEFAULT); } pStorage->Release(); return hr; }
|
例如,下面的代码创建了一个回放的graph和保存它成MyGraph.grf。
void __cdecl main(void) { HRESULT hr; IGraphBuilder *pGraph; CoInitialize(NULL); // Create the Filter Graph Manager and render a file. CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, reinterpret_cast<void**>(&pGraph)); hr = pGraph->RenderFile(L"C:\\Video.avi", NULL); if (SUCCEEDED(hr)) { hr = SaveGraphFile(pGraph, L"C:\\MyGraph.grf"); } pGraph->Release(); CoUninitialize(); }
|
关于StgCreateDocfile函数的更多信息,请查看SDK文档。
4.1.5 加载一个可编程graphedit文件
(Loading a GraphEdit File Programmatically )
程序可以使用IPersistStream接口来加载一个grf文件,使用如下 的代码:
HRESULT LoadGraphFile(IGraphBuilder *pGraph, const WCHAR* wszName) { IStorage *pStorage = 0; if (S_OK != StgIsStorageFile(wszName)) { return E_FAIL; } HRESULT hr = StgOpenStorage(wszName, 0, STGM_TRANSACTED | STGM_READ | STGM_SHARE_DENY_WRITE, 0, 0, &pStorage); if (FAILED(hr)) { return hr; } IPersistStream *pPersistStream = 0; hr = pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersistStream)); if (SUCCEEDED(hr)) { IStream *pStream = 0; hr = pStorage->OpenStream(L"ActiveMovieGraph", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream); if(SUCCEEDED(hr)) { hr = pPersistStream->Load(pStream); pStream->Release(); } pPersistStream->Release(); } pStorage->Release(); return hr; }
|
注意: 在先前代码的IPersistStream::Load 函数能返回一个DirectShow错误或者成功的代码。想查看一系列返回值,去看Error 和 Success codes。
GraphEdit文件仅被用作调试和调试。我们不打算用它来终端用户程序。
更多StgIsStorageFile和StgOpenStorage函数的信息,请参照SDK文档。