MSDN学习DirectShow——第四章 使用DirectShow 第一节 用graphedit模拟创建Graph

时间:2023-02-02 07:52:08

第四章 使用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文档。