[DirectShow]put_Owner返回E_NOINTERFACE的思考

转载地址 http://blog.csdn.net/bwmwm/article/details/4562709

MSDN一开始介绍DirectShwo的时候就给出了一个列子“How To Play a File“,代码如下:

  1. #include <dshow.h>  
  2. void main(void)  
  3. {  
  4.     IGraphBuilder *pGraph = NULL;  
  5.     IMediaControl *pControl = NULL;  
  6.     IMediaEvent   *pEvent = NULL;  
  7.   
  8.     // Initialize the COM library.  
  9.     HRESULT hr = CoInitialize(NULL);  
  10.     if (FAILED(hr))  
  11.     {  
  12.         printf("ERROR - Could not initialize COM library");  
  13.         return;  
  14.     }  
  15.   
  16.     // Create the filter graph manager and query for interfaces.  
  17.     hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,   
  18.                         IID_IGraphBuilder, (void **)&pGraph);  
  19.     if (FAILED(hr))  
  20.     {  
  21.         printf("ERROR - Could not create the Filter Graph Manager.");  
  22.         return;  
  23.     }  
  24.   
  25.     hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);  
  26.     hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);  
  27.   
  28.     // Build the graph. IMPORTANT: Change this string to a file on your system.  
  29.     hr = pGraph->RenderFile(L"C://Example.avi", NULL);  
  30.     if (SUCCEEDED(hr))  
  31.     {  
  32.         // Run the graph.  
  33.         hr = pControl->Run();  
  34.         if (SUCCEEDED(hr))  
  35.         {  
  36.             // Wait for completion.  
  37.             long evCode;  
  38.             pEvent->WaitForCompletion(INFINITE, &evCode);  
  39.   
  40.             // Note: Do not use INFINITE in a real application, because it  
  41.             // can block indefinitely.  
  42.         }  
  43.     }  
  44.     pControl->Release();  
  45.     pEvent->Release();  
  46.     pGraph->Release();  
  47.     CoUninitialize();  
  48. }  

这是一个控制台程序,Render会创建一个自己的窗口来播放视频。如果在MFC的对话框中执行这段代码,就会出现两个窗口,一个是Render创建的播放窗口,一个是程序的主窗口。MSDN告诉我们,可以把Render创建的窗口设置为主窗口的子窗口。这样,播放窗口就会贴附在主窗口上。使用IVideoWindow接口实现,代码如下:

  1. void RenderFile()  
  2. {  
  3.   IGraphBuilder *pGraph = NULL;  
  4.     IMediaControl *pControl = NULL;  
  5.     IMediaEvent   *pEvent = NULL;  
  6.   
  7.     // Initialize the COM library.  
  8.     HRESULT hr = CoInitialize(NULL);  
  9.     if (FAILED(hr))  
  10.     {  
  11.         printf("ERROR - Could not initialize COM library");  
  12.         return;  
  13.     }  
  14.   
  15.     // Create the filter graph manager and query for interfaces.  
  16.     hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,   
  17.                         IID_IGraphBuilder, (void **)&pGraph);  
  18.     if (FAILED(hr))  
  19.     {  
  20.         printf("ERROR - Could not create the Filter Graph Manager.");  
  21.         return;  
  22.     }  
  23.   
  24.     hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);  
  25.     hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);  
  26.       
  27.     IVideoWindow* pWnd = NULL;  
  28.   hr = pGraph->QueryInterface(IID_IVideoWindow,(void**)&pWnd);  
  29.   hr = pWnd->put_Owner((OAHWND)m_hWnd);  
  30.   pWnd->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);  
  31.     
  32.   CRect rc;  
  33.   GetClientRect(&rc);  
  34.   pWnd->SetWindowPosition(rc.left,rc.top,rc.Width(),rc.Height());  
  35.   
  36.     // Build the graph. IMPORTANT: Change this string to a file on your system.  
  37.     hr = pGraph->RenderFile(L"C://Example.avi", NULL);  
  38.     if (SUCCEEDED(hr))  
  39.     {  
  40.         // Run the graph.  
  41.         hr = pControl->Run();  
  42.         if (SUCCEEDED(hr))  
  43.         {  
  44.             // Wait for completion.  
  45.             long evCode;  
  46.             pEvent->WaitForCompletion(INFINITE, &evCode);  
  47.   
  48.             // Note: Do not use INFINITE in a real application, because it  
  49.             // can block indefinitely.  
  50.         }  
  51.     }  
  52.     pControl->Release();  
  53.     pEvent->Release();  
  54.     pGraph->Release();  
  55.     CoUninitialize();  
  56. }  

 

 

运行程序,执行RenderFile()函数,发现效果没变,还是两个窗口,怎么回事呢?单步调试跟踪发现,pWnd->put_Owner没有返回S_OK,而是返回E_NOINTERFACE,无效的接口指针,很奇怪,pWnd是一个有效的值,但是随后的几个函数执行都是返回E_NOINTERFACE。很明显,窗口设置不成功,没辙,只好世界各地寻找不成功的原因。
对比了DXSDK/Samples/C++/DirectShow/Editing/StillCap中的例子发现,pGraph->RenderFile()函数与pWnd->put_Owner()的执行顺序反了。于是把hr = pGraph->RenderFile(L"C://Example.avi", NULL)放到hr = pGraph->QueryInterface(IID_IVideoWindow,(void**)&pWnd)之前执行,OK,达到了效果,所有执行都返回S_OK。

虽然找到了错误的地方,也达到了效果,不过,使用一项技术,总还是要弄明白原理。于是我把MSDN中相关的内容又好好的看了一遍,发现原来如此:

在这个例子中,看不到添加任何filter的影子,既没有Source Filter ,又没有Render Filter,仅仅一句RenderFile()。是的,RenderFile()干了所有的事情。
首先IGraphBuilder::RenderFile()调用IGraphBuilder::AddSourceFilter()向Graph中添加一个Source Filter。IGraphBuilder::AddSourceFilter()会根据文件扩展名或者是文件头等信息智能选取一个匹配文件的Source Filter。
然后IGraphBuilder::RenderFile()调用IGraphBuilder::Render()完成剩下的Graph的生成。IGraphBuilder::Render()会从Source Filter的output pin开始寻找每一个匹配此pin的Filter加入到链路中来,直到找到一个Render Filter为止。当整个链路完成,也就代表Graph生成,可以调用pControl->Run()运行Graph了。
虽然IVideoWindow是由Filter Graph Manager暴露,但是IVideoWindow设置属性却是对Render Filter。在没有执行RenderFile之前,Graph中没有连接RenderFile,因此IVideoWindow的设置无效。

参考资料来自MSDN:
ms-help://MS.MSDNQTR.v90.en/directshow/htm/intelligentconnect.htm
ms-help://MS.MSDNQTR.v90.en/directshow/htm/igraphbuilderaddsourcefilter.htm
ms-help://MS.MSDNQTR.v90.en/directshow/htm/igraphbuilderrenderfile.htm
ms-help://MS.MSDNQTR.v90.en/directshow/htm/usingwindowedmode.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值