Windows平臺使用DirectShow獲取UVC攝像頭上按鍵后的抓拍圖

什么是UVC攝像頭颈嚼,UVC是什么意思笆载?

UVC是一種標準的USB視頻設(shè)備協(xié)議饲常,就是傳說中的免驅(qū)攝像頭。UVC是Usb Video Class的簡稱搁料。
Windows平臺上UVC驅(qū)動文檔

DirectShow簡介

DirectShow是微軟公司在ActiveMovie和Video for Windows的基礎(chǔ)上推出的新一代基于COM(Component Object Model)的流媒體處理的開發(fā)包或详,與DirectX開發(fā)包一起發(fā)布系羞。DirectShow使用一種叫Filter Graph的模型來管理整個數(shù)據(jù)流的處理過程,運用DirectShow霸琴,我們可以很方便地從支持WDM驅(qū)動模型的采集卡上捕獲數(shù)據(jù)椒振,并且進行相應的后期處理乃至存儲到文件中。這樣使在多媒體數(shù)據(jù)庫管理系統(tǒng)(MDBMS)中多媒體數(shù)據(jù)的存取變得更加方便梧乘。它廣泛地支持各種媒體格式澎迎,包括Asf、Mpeg选调、Avi夹供、Dv、Mp3仁堪、Wave等哮洽,為多媒體流的捕捉和回放提供了強有力的支持。

調(diào)用DirectShow獲取攝像頭按鍵后的抓拍圖片

MSDN文檔:Video Capture

Filter

DirectShow使用模塊化體系結(jié)構(gòu)弦聂,其中每個處理階段都由稱為filter的COM對象完成鸟辅。 DirectShow為應用程序提供了一組標準filter,開發(fā)人員可以編寫自己的自定義過濾器來擴展DirectShow的功能莺葫。

Graph

有多個Filter連接匪凉,構(gòu)成Graph。Filter Graph Manager用來構(gòu)建和控制Graph中的Filter

代碼實戰(zhàn)

初始化各個Com接口

HRESULT GetInterfaces(void)
{
    HRESULT hr;

    //創(chuàng)建Filter Graph Manager.
    hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
        IID_IGraphBuilder, (void **) &pGraphBuilder);
    if (FAILED(hr))
        return hr;
    //創(chuàng)建Capture Graph Builder.
    hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
        IID_ICaptureGraphBuilder2, (void **) &pCaptureGraphBuilder2);
    if (FAILED(hr))
        return hr;

    // IMediaControl接口捺檬,用來控制流媒體在Filter Graph中的流動再层,例如流媒體的啟動和停止;
    hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *) &pMediaControl);
    if (FAILED(hr))
        return hr;

    // IVideoWindow,用來顯示預覽視頻
    hr = pGraphBuilder->QueryInterface(IID_IVideoWindow, (LPVOID *) &pVideoWindow);
    if (FAILED(hr))
        return hr;

    // IMediaEvent接口堡纬,該接口在Filter Graph發(fā)生一些事件時用來創(chuàng)建事件的標志信息并傳送給應用程序
    hr = pGraphBuilder->QueryInterface(IID_IMediaEvent,(LPVOID *) &pMediaEvent);
    if (FAILED(hr))
        return hr;

    //創(chuàng)建用于預覽的Sample Grabber Filter.
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
        IID_IBaseFilter, (void**)&pGrabberF);
    if (FAILED(hr))
        return hr;

    //獲取ISampleGrabber接口聂受,用于設(shè)置回調(diào)等相關(guān)信息
    hr = pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);
    if (FAILED(hr)) 
        return hr;

    //創(chuàng)建用于抓拍的Sample Grabber Filter.
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)&pGrabberStill);
    if (FAILED(hr))
        return hr;

    //獲取ISampleGrabber接口,用于設(shè)置回調(diào)等相關(guān)信息
    hr = pGrabberStill->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabberStill);
    if (FAILED(hr)) 
        return hr;

    //創(chuàng)建Null Filter
    hr = CoCreateInstance(CLSID_NullRenderer,NULL,CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**)&pNull);
    if (FAILED(hr))
        return hr;
    return hr;
}

檢測設(shè)備

//遍歷設(shè)備隐轩,找到指定vidpid設(shè)備后打開設(shè)備
HRESULT InitMonikers()
{
    USES_CONVERSION;
    HRESULT hr;
    ULONG cFetched;

    //https://docs.microsoft.com/zh-cn/windows/desktop/DirectShow/selecting-a-capture-device
    //遍歷設(shè)備
    ICreateDevEnum *pCreateDevEnum;
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);
    if (FAILED(hr))
    {
        printf("Failed to enumerate all video and audio capture devices!  hr=0x%x\n", hr);
        return hr;
    }

    IEnumMoniker *pEnumMoniker;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);
    if (FAILED(hr) || !pEnumMoniker)
    {
        printf("Failed to create ClassEnumerator!  hr=0x%x\n", hr);
        return -1;
    }

    while (hr = pEnumMoniker->Next(1, &pMonikerVideo, &cFetched), hr == S_OK)
    {
        IPropertyBag *pPropBag;
        //BindToStorage之后就可以訪問設(shè)備標識的屬性集了。
        HRESULT hr = pMonikerVideo->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
        if (FAILED(hr))
        {
            pMonikerVideo->Release();
            continue;
        }

        VARIANT var;
        VariantInit(&var);
        //DevicePath中包含vidpid
        hr = pPropBag->Read(L"DevicePath", &var, 0);
        if (FAILED(hr))
        {
            VariantClear(&var);
            pMonikerVideo->Release();
            continue;
        }

        //比較是否是要使用的設(shè)備
        //TRACE("Device path: %S\n", var.bstrVal);
        std::string devpath = std::string(W2A(var.bstrVal));
        if (devpath.find("vid_06f8&pid_3015") == -1)
        {
            VariantClear(&var);
            pMonikerVideo->Release();
            continue;
        }
        VariantClear(&var);

        // BindToObject將某個設(shè)備標識綁定到一個DirectShow Filter渤早,
        //     然后調(diào)用IFilterGraph::AddFilter加入到Filter Graph中职车,這個設(shè)備就可以參與工作了
        // 調(diào)用IMoniker::BindToObject建立一個和選擇的device聯(lián)合的filter,
        //      并且裝載filter的屬性(CLSID,FriendlyName, and DevicePath)鹊杖。
        hr = pMonikerVideo->BindToObject(0, 0, IID_IBaseFilter, (void**)&pVideoCaptureFilter);
        if (FAILED(hr))
        {
            printf("Couldn't bind moniker to filter object!  hr=0x%x\n", hr);
            return hr;
        }

        pPropBag->Release();
        pMonikerVideo->Release();
    }

    pEnumMoniker->Release();
    return hr;
}

構(gòu)建預覽Graph

//開始構(gòu)建預覽graph
    //加入設(shè)備抓拍filter
    hr = pGraphBuilder->AddFilter(pVideoCaptureFilter, L"Video Capture");
    if (FAILED(hr))
    {
        printf("Couldn't add video capture filter to graph!  hr=0x%x\n", hr);
        pVideoCaptureFilter->Release();
        return hr;
    }

    //加入DirectShow中自帶的SampleGrabber Filter
    hr = pGraphBuilder->AddFilter(pGrabberF, L"Sample Grabber");
    if (FAILED(hr))
    {
        printf("Couldn't add sample grabber to graph!  hr=0x%x\n", hr);
        // Return an error.
    }

    //使用Capture Graph Builder構(gòu)建預覽 Graph
    hr = pCaptureGraphBuilder2->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoCaptureFilter, pGrabberF, 0 );
    if (FAILED(hr))
    {
        printf("Couldn't render video capture stream. The device may already be in use.  hr=0x%x\n", hr);
        pVideoCaptureFilter->Release();
        return hr;
    }

構(gòu)建抓拍Graph

    //加入DirectShow中自帶的SampleGrabber Filter
    hr = pGraphBuilder->AddFilter(pGrabberStill, L"Still Sample Grabber");
    if (FAILED(hr))
    {
        printf("Couldn't add sample grabber to graph!  hr=0x%x\n", hr);
        // Return an error.
    }

    //加入DirectShow中自帶的NullRender Filter
    hr = pGraphBuilder->AddFilter(pNull, L"NullRender");
    if (FAILED(hr))
    {
        printf("Couldn't add null to graph!  hr=0x%x\n", hr);
        return hr;
    }

    //使用Capture Graph Builder構(gòu)建抓拍 Graph
    hr = pCaptureGraphBuilder2->RenderStream(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, pVideoCaptureFilter, pGrabberStill, pNull);
    if (FAILED(hr))
    {
        printf("Couldn't render video capture stream. The device may already be in use.  hr=0x%x\n", hr);
        pVideoCaptureFilter->Release();
        return hr;
    }

    //configure the Sample Grabber so that it buffers samples :
    hr = pSampleGrabberStill->SetOneShot(FALSE);
    hr = pSampleGrabberStill->SetBufferSamples(TRUE);

    //獲取設(shè)備輸出格式信息
    AM_MEDIA_TYPE mt;
    hr = pSampleGrabber->GetConnectedMediaType(&mt);
    if (FAILED(hr))
    {
        return -1;
    }
    VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*)mt.pbFormat;
    CSampleGrabberCB *CB = new CSampleGrabberCB();
    if (!FAILED(hr))
    {
        CB->Width = vih->bmiHeader.biWidth;
        CB->Height = vih->bmiHeader.biHeight;
    }

    //設(shè)置觸發(fā)抓拍后悴灵,調(diào)用的回調(diào)函數(shù) 0-調(diào)用SampleCB 1-BufferCB
    hr = pSampleGrabberStill->SetCallback(CB, 1);
    if (FAILED(hr))
    {
        printf("set still trigger call back failed\n");
    }

啟動Graph

    //設(shè)置預覽窗口大小位置
    SetupVideoWindow();

    //啟動設(shè)備
    hr = pMediaControl->Run();
    if (FAILED(hr))
    {
        printf("Couldn't run the graph!  hr=0x%x\n", hr);
        return hr;
    }
    else 
        psCurrent = Running;

使用GraphEdt進行調(diào)試

GraphEdt.exe是windows sdk中的graph調(diào)試工具,使用方法簡介如下:

  1. 查看設(shè)備
    在Graph菜單中點擊Insert Filters骂蓖,找到Video Capture Sources


    微信圖片_20181018093403.png

    可以看到設(shè)備信息积瞒,點擊Insert Filter可以將設(shè)備Filter加入到Graph中。

  2. 構(gòu)建Graph
    如果不清楚設(shè)備FIlter后邊應該假如什么Filter茫孔,可以在Filter的Pin上右鍵叮喳,選擇Render Filter,會自動連接一個Video Render缰贝。
    微信圖片_20181018093830.png

    此時點擊播放按鈕剩晴,可以看到視頻

源碼

github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绽左,隨后出現(xiàn)的幾起案子妇菱,更是在濱河造成了極大的恐慌房交,老刑警劉巖候味,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異笼裳,居然都是意外死亡卵沉,警方通過查閱死者的電腦和手機木柬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門姥宝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人箩兽,你說我怎么就攤上這事汗贫》技ǎ” “怎么了掀亥?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵撞反,是天一觀的道長妥色。 經(jīng)常有香客問我,道長遏片,這世上最難降的妖魔是什么嘹害? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任撮竿,我火速辦了婚禮,結(jié)果婚禮上笔呀,老公的妹妹穿的比我還像新娘幢踏。我一直安慰自己,他們只是感情好许师,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布房蝉。 她就那樣靜靜地躺著,像睡著了一般微渠。 火紅的嫁衣襯著肌膚如雪搭幻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天逞盆,我揣著相機與錄音檀蹋,去河邊找鬼。 笑死云芦,一個胖子當著我的面吹牛俯逾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舅逸,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桌肴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堡赔?” 一聲冷哼從身側(cè)響起识脆,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎善已,沒想到半個月后灼捂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡换团,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年悉稠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艘包。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡的猛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出想虎,到底是詐尸還是另有隱情卦尊,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布舌厨,位于F島的核電站岂却,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躏哩,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一署浩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扫尺,春花似錦筋栋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姑曙,卻和暖如春肴颊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渣磷。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工婿着, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人醋界。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓竟宋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親形纺。 傳聞我的和親對象是個殘疾皇子丘侠,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容