DirectShow一次实作笔记

转载请注明出处:http://blog.csdn.net/horkychen  一篇早期写的资料。
Day 1:
我是个DirectShow新手,我基于CCaptureVideo类,写了一个简单的测试程序,界面如下图,代码在这个Group的SkyDriver/Codes目录里,使用DirectX SDK9 2004 Summer Update编译通过。
DirectShow一次实作笔记_第1张图片
 
DirectShow一次实作笔记_第2张图片
 
今天主要做了两件事:
一. 加了Video Capture时间长短控制:
   a.在CCaptureVideo中定义了一个m_MaxTime,和一个成员函数SetTimeLimitation来接受UI的设定,以秒为单位,然后在IMediaControl呼叫Run之前设定时间:
if(m_MaxTime>0)
  {
   // Control the video capture stream. 
   REFERENCE_TIME rtStop = 10000000 * m_MaxTime;
   const WORD wStartCookie = 1, wStopCookie = 2;  // Arbitrary values.
   hr = m_pCapture->ControlStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video,      // Media type.
    m_pBF,                 // Capture filter.
    NULL, &rtStop,     // Start and stop times.
    wStartCookie, wStopCookie  // Values for the start and stop events.
    );    if(FAILED(hr))
   {
    AfxMessageBox(_T("Couldn’t control the graph!"));
   }   }
 
经过在PC上实验,如果设定n秒,则录制的Video时间为n-1秒,大家可以再试试看!
 
二.加了时间限制后,当录制结束,文件写入已经停止,但UI并没有回复正常,所以还要监测结束事件。于是有了BindMessageWindow函数,从Init函数传入两个句柄:
 HRESULT  Init(int iDeviceID, HWND hPreviewWnd, HWND hBaseWnd)
   hPreviewWnd代表预览窗口
   hBaseWnd代表主消息窗口
然后使用IMediaEventEx将消息绑定到主窗口之上(WM_GRAPHNOTIFY),这些都是DirectShow MSDN上所说明的方法:
HRESULT CCaptureVideo::BindMessageWindow()
{
  HRESULT hr = S_OK;
  if(m_hBaseWnd)
  {
 hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pEvent);
 if (FAILED(hr))return hr;
 m_pEvent->SetNotifyWindow((OAHWND)m_hBaseWnd,WM_GRAPHNOTIFY,0);
  }
  return S_OK;
}
然后在主窗口上,使用其WndProc函数来侦听,得到WM_GRAPHNOTIFY后使用IMediaEventEx的GetEvent方法(我把它包在long CCaptureVideo::GetCurrentEvent()函数中了),如果发现事件是EC_STREAM_CONTROL_STOPPED,表示已经结束录制了,就呼叫STOP的Click事件处理程序,来结束录制。

LRESULT CCameraCaptureDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
    if( WM_GRAPHNOTIFY == message)
 {
  HandleGraphEvent();
 }
 return CDialog::WindowProc(message, wParam, lParam);
}

void CCameraCaptureDlg::HandleGraphEvent()
{
 long eventCode = m_cap.GetCurrentEvent();
 TCHAR str[256] = {0};
 wsprintf(str,_T("Get Event:%d"),eventCode);
 m_ListLog.AddString(str);
 switch(eventCode)
 {
   case EC_STREAM_CONTROL_STOPPED:
    if(GetDlgItem(IDC_STOP)->IsWindowEnabled())
   OnCommand(IDC_STOP,NULL);
  break;
   default:
    break;
 }
}
 
这些就是今天DirectShow的进展!
 
Day 2:
今天原本要写个File Writer测试,后来才发现一直使用AVI进行测试,并不能代表录制ASF时有相同的问题,并且录制ASF时使用的是WM ASF Writer,所以实现了ASF录制功能。遇到如下几个问题:
  1.需要安装Windows Media SDK. (我用了Windows Media SDK 9),主要是CLSID_WMAsfWriter之类的定义。当然也要引用WMVCORE.lib
  2.在PC下,ASF的录制必须安装Audio Capture Filter,否则会出现"Unspecified Error! 0x80004005"!
    Audio Filter的安装包括两步: a. Bind the filter  b.Call RenderStream for Audio Filter.
    详见代码中的BindFilter和Init函数的修改!
   因为ASF文件包含很多的profile,可以使用DirectShow带的Profile Emulator来查看。其中也有(No Audio)的profile,可以不使用Audio Capture Filter,而单独录制视频! 所以它的行为和指定的Profile有很大的关系!
  3.在PC下,ASF的录制是会自动增长的。所以并没有实质解决G2为什么没有自动增长的问题! 我在PC上也遇到一个问题,我必须选中输出的文件,才能看到文件大小的改变。G2或许有一样的问题!

完成后的Graph应当有如下的Filters,其中的Renderer是用来作为Preview的。

完整的代码,也已经传到本组的SkyDrive的Codes目录下,也包括展开后的Windows Media SDK.

下面附上一段,没有使用到代码,用来选择相应的profile,但对于其第一个参数从何而来,还不清楚!
HRESULT CCaptureVideo::MapProfileIdToProfile(int iProfile, IWMProfile **ppProfile)
{
    DWORD cProfiles;

    if (!ppProfile)
        return E_POINTER;
        
    *ppProfile = 0;
    
    CComPtr <IWMProfileManager> pIWMProfileManager;
    HRESULT hr = WMCreateProfileManager( &pIWMProfileManager );
    if(FAILED(hr)) 
    {
        printf("MapProfile: Failed to create profile manager!  hr=0x%x\n", hr);
        return hr;
    }

    // We only use 7_0 profiles
    CComQIPtr<IWMProfileManager2, &IID_IWMProfileManager2> pIPM2(pIWMProfileManager);
    if(!pIPM2) 
    {
        printf("MapProfile: Failed to QI IWMProfileManager2!\n");
        return E_UNEXPECTED;
    }

    hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 );
    if(FAILED(hr))
    {
        printf("MapProfile: Failed to set system profile version!  hr=0x%x\n", hr);
        return hr;
    }

    hr = pIWMProfileManager->GetSystemProfileCount( &cProfiles );
    if(FAILED(hr))
    {
        printf("MapProfile: Failed to get system profile count!  hr=0x%x\n", hr);
        return hr;
    }

    // Invalid profile requested?
    if( (DWORD)iProfile >= cProfiles ) 
    {
        printf("Invalid profile: %d\n", iProfile);
        return E_INVALIDARG;
    }

    return (pIWMProfileManager->LoadSystemProfile( iProfile, ppProfile ));
}
 
Day 3:
在写入ASF文件时,有两种创建Graph的方式:
1.手动创建File Writer和FileSink
    hr = CoCreateInstance(CLSID_WMAsfWriter, NULL,  CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)&m_pMux);
    if(FAILED(hr))
     return hr;
    hr  =  m_pGB->AddFilter(m_pMux,  L"WM ASF Writer");
    m_pMux->QueryInterface(IID_IFileSinkFilter,(void**) &m_pSink);
    hr=m_pSink->SetFileName(L"C:\\test.wmv",NULL);
    SAFE_RELEASE(m_pSink);

2.调用ICaptureGraphBuilder的SetOutputFileName方法
    hr = m_pCapture->SetOutputFileName(
     &MEDIASUBTYPE_Asf, // File type.
     _T("c:\\test.wmv"),     // File name, as a wide-character string.
     &m_pMux,             // Receives a pointer to the multiplexer.
     &m_pSink);             // Receives a pointer to the file writer.
 
两者生成的Graph是不同的。前者会使用WM ASF Writer写入文件,而后则使用MUX混合音频和视频后,使用File Writer来写入文件。
 
另外在使用<<DirectShow实务指南>>第2章的AVCap时,发现在公司的机器上,视频被分成5段,在家里则是正常,分析了一下,发现是启用DMR产生的问题,如下:
      VMR(Video Mixing Renderer)是DirectShow的新一代Video Renderer。VMR有两个版本:VMR-7和VMR-9。前者采用了DirectDraw 7技术,仅仅在Windows XP操作系统下可以获得,并且是XP上默认的用于视频显示的Renderer(代替传统的Video Renderer);后者采用了Direct3D 9的技术,是随DirectX9.0一起发布的,但任何时候都不是默认的Renderer。
       安装了DirectX9.0以后,就有4个Video Renderer可供选择使用:传统的Video Renderer、Overlay Mixer、VMR-7和CMR-9。
       VMR主要利用了显卡专有的图形处理能力(VMR做视频的合成和显示时并不占用系统的CPU资源),它能够表现的性能对于硬件的依赖性很高。

在MSDN有更为详细的解析,MSDN: http://msdn.microsoft.com/en-us/library/dd407295(VS.85).aspx
 
这和咱们的最终目的关系不大,不再深入。但它引出一个问题,我们的Camera的输出是什么格式的数据? 我在程序中加入了枚举输出格式的检测(CCaptureVideo::EnumVideoMediaType函数,参考CAnalogInputFilter的相应函数)。
 
发现在公司的摄像头仅支持YUY2 (通用驱动),而家里则支持RGB24和AYUV(厂商提供的驱动),这些格式对于后面写Encoder十分重要,应当需要了解:
 权威网站: http://www.fourcc.org/yuv.php
      AYUV   Combined YUV and alpha
      YUY2   YUV 4:2:2 as for UYVY but with different component ordering within the u_int32 macropixel.
  有了YUV数据,再转成MPEG,似乎是一套比较成熟的技术, ffmpeg是支持的。
*今天的代码还加入了对Video Caputer Filter的Property Page的显示! 还得了一篇有关如何写Filter的文章,我也放到SkyDrive里分享。希望对大家有用,我在这里抛砖,可能抛不了多久了,欢迎多多讨论!

你可能感兴趣的:(windows,filter,video,File,Graph,audio)