(DirectX系列05)DirectShow 视频采集

    前几小节陆续介绍了Directsound、Directshow音频处理方面的技术,还没有介绍视频方面的技术,从这节开始介绍视频采集方面的技术。今日刚好就介绍Directshow视频采集技术,其实DirectShow视频采集并不复杂,复杂的部分微软已经帮做好了,只需按照顺序将API连接起来即可,如下所叙述;

系统设备枚举

    系统设备枚举器为我们按类型枚举已注册在系统中的Fitler提供了统一的方法。而且它能够区分不同的硬件设备,即便是同一个Filter支持它们。这对那些使用Windows驱动模型和KSProxy Filter的设备来说是非常有用的。系统设备枚举器对它们按不同的设备实例进行对待。当我们利用系统设备枚举器查询设备的时候,系统设备枚举器为特定类型的设备(如,音频捕获和视频压缩)生成了一张枚举表(Enumerator)。类型枚举器(Category enumerator)为每个这种类型的设备返回一个Moniker,类型枚举器自动把每一种即插即用的设备包含在内。
按如下的步骤使用系统设备枚举器:
1. 调用方法CoCreateInstance生成系统设备枚举器。类标识(CLSID)为CLSID_SystemDeviceEnum。
2. 调用ICreateDevEnum::CreateClassEnumerator方法生成类型枚举器,参数为你想要得到的类型的CLSID,该方法返回一个IEnumMoniker接口指针,如果指定的类型(是空的)或不存在,函数ICreateDevEnum::CreateClassEnumerator将返回S_FALSE而不是错误代码,同时IEnumMoniker指针(译注:通过参数返回)也是空的,这就要求我们在调用CreateClassEnumerator的时候明确用S_OK进行比较而不是使用宏SUCCEEDED。
3. 使用IEnumMoniker::Next方法依次得到IEnumMoniker指针中的每个moniker。该方法返回一个IMoniker接口指针。当Next到达枚举的底部,它的返回值仍然是S_FALSE,这里我们仍需要用S_OK来进行检验。
4. 想要得到该设备较为友好的名称(例如想要在用户界面中进行显示),调用IMoniker::BindToStorage方法。
5. 如果想要生成并初始化管理该设备的Filter调用3返回指针的IMonitor::BindToObject方法,接下来调用IFilterGraph::AddFilter把该Filter添加到视图中。

构建Capture Graph Builder

    通过CoCreateInstance函数一一实例化IGraphBuilder和ICaptureGraphBuilder2接口,并将IGraphBuilder初始化到滤波器链表管理器中,方便控制滤波器链表,如下代码;

// 创建IGraphBuilder接口
hr = CoCreateInstance(CLSID_FilterGraph, NULL,
                    CLSCTX_INPROC_SERVER,
                    IID_IGraphBuilder, (void **)&m_pGB);

// 创建ICaptureGraphBuilder2接口
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2 , NULL,
                    CLSCTX_INPROC,
                    IID_ICaptureGraphBuilder2, (void **)&m_pCapture);

// 初始化滤波器链表管理器IGraphBuilder
m_pCapture->SetFiltergraph(m_pGB);

// 查询媒体控制接口
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
// 查询视频窗口接口
hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);

设备与滤波器捆绑

    通过上面的构建,接下来就要将选择的设备和滤波器进行绑定,绑定的步骤与枚举有点相似,其中还需要一个枚举的过程,其中通过index角标来进行指定设备的绑定,代码如下;

    ICreateDevEnum *pCreateDevEnum;

    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
                    CLSCTX_INPROC_SERVER,
                    IID_ICreateDevEnum,
                    (void**)&pCreateDevEnum);
    if (hr != NOERROR) return false;
    IEnumMoniker *pEm;

    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
    if (hr != NOERROR) return false;
    pEm->Reset();
    ULONG cFetched;
    IMoniker *pM;
    int index = 0;
    while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index     {
        IPropertyBag *pBag;
        hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
        if(SUCCEEDED(hr))
        {
            VARIANT var;
            var.vt = VT_BSTR;
            hr = pBag->Read(L"FriendlyName", &var, NULL);
            if (hr == NOERROR)
            {
                if (index == deviceId)
                {
     
pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
                }
                SysFreeString(var.bstrVal);
            }           
            pBag->Release();
        }
        pM->Release();
        index++;
    }

视频预览、视频保存

在Directshow中视频预览需要先渲染媒体,把链表中滤波器连接起来,后再设置显示窗口,如下代码;

    // 渲染媒体,把链表中滤波器连接起来
    hr = m_pCapture->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pBF, NULL, NULL );

    /* 设置视频显示窗口的特性 */ 
    HRESULT hr;
    //ljz
    hr = m_pVW->put_Visible(OAFALSE);
    hr = m_pVW->put_Owner((OAHWND)m_hWnd);
    if (FAILED(hr)) return hr;

    hr = m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
    if (FAILED(hr)) return hr;

    ResizeVideoWindow();

    经过前面的介绍,视频保存更加简单,直接调用SetOutputFileName函数和RenderStream函数即可,如下代码;
    // 先停止视频
    m_pMC->Stop();
    // 设置文件名,注意该函数的第二个参数的类型
    hr = m_pCapture->SetOutputFileName(&MEDIASUBTYPE_Avi, inFileName.AllocSysString(), &pMux, NULL );
    // 渲染媒体,连接所有滤波器
    hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pBF, NULL, pMux );
    pMux->Release();
    // 回复视频
    m_pMC->Run();

经过上面的几步,一个基于DirectShow视频采集程序就可以完成了。

上一篇:ML之LiR:基于编程实现简单线性回归案例


下一篇:融合Thread网路协议 ZigBee新版规范Q3出笼