DirectShow访问USB摄像头Demo

目录

1 DirectShow 简介

2 DirectShow系统设备的枚举步骤

3 DirectShow获取摄像头视频流

4 演示Demo

4.1 开发环境

4.2 功能介绍

4.3 下载地址


1 DirectShow 简介

        DirectShow(简称 DShow) 是一个 Windows 平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能。它支持多种多样的媒体文件格式,包括 ASF、MPEG、AVI、MP3和WAV 文件,同时支持使用 WDM 驱动或早期的 VFW 驱动来进行多媒体流的采集。

  • DirectShow 大大简化了媒体回放、格式转换和采集工作。但与此同时,它也为用户自定义的解决方案提供了底层流控制框架,从而使用户可以自行创建支持新的文件格式或其他用户的 DirectShow 组件。

  • DirectShow 专为 C ++ 而设计。 Microsoft 不提供用于 DirectShow 的托管 API。

  • DirectShow 是基于组件对象模型(COM)的,因此当你编写 DirectShow 应用程序时,你必须具备 COM 客户端程序编写的知识。对于大部分的应用程序,你不需要实现自己的 COM 对象,DirectShow 提供了大部分你需要的 DirectShow 组件,但是假如你需要编写自己的 DirectShow 组件来进行扩充,那么你必须编写实现 COM 对象。

  • 使用 DirectShow 编写的典型应用程序包括:DVD 播放器、视频编辑程序、AVI 到 ASF 转换器、 MP3 播放器和数字视频采集应用

2 DirectShow系统设备的枚举步骤

  1. 使用CoCreateInstance函数创建系统枚举器组件对象,并获得ICreateDevEnum接口;

  2. 使用接口方法ICreateDevEnum::CreateClassEnumerator为指定的Filter注册类型目录创建一个枚举器,并获得

  3. IEnumMoniker接口;

  4. 使用IEnumMoniker接口方法枚举指定类型目录下所有设备标识

  5. 调用IMoniker::BindToStorage之后,可以访问设备标识的属性集,比如得到Display Name,Friendly Name等;

  6. 调用IMoniker::BindToOject可以将设备标识生成一个DirectShow Filter,随后调用IFilterGraph::AddFilter,并将之加入到FilterGraph中就可以参与工作了.

/***********************************************************************
    功能:    获取摄像头个数
    参数:
    返回:    摄像头个数
************************************************************************/
int CCameraDS::CameraCount()
{
    int count = 0;
    CoInitialize(NULL);

   // enumerate all video capture devices
    CComPtr<ICreateDevEnum> pCreateDevEnum;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
    CComPtr<IEnumMoniker> pEm;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
    if (hr != NOERROR) 
    {
        return count;
    }
    pEm->Reset();
    ULONG cFetched;
    IMoniker *pM;
    while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
    {
        count++;
    }
    return count;
}

/*******************************************************************
    功能:    PID和VID获取设备序号
    参数:    vid - 输入,vid,全小写,比如046d
         pid - 输入,pid,全小写,比如08ff
    返回:    当前PID和VID的摄像头个数
*******************************************************************/
int CCameraDS::EnumCamIndex(CString vid, CString pid,unsigned char CamIndex[32])
{
        int count = 0;
        CoInitialize(NULL);

        CString  strVid="";
        CString  strPid="";
        int id = 0;    //记录实际摄像头个数
        int camnum=0;  //记录当前符合PID VID的设备数
        // enumerate all video capture devices
        CComPtr<ICreateDevEnum> pCreateDevEnum;
        HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
        if (hr != NOERROR)
                return -1;

        CComPtr<IEnumMoniker> pEm;
        hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
        if (hr != NOERROR)
                return -1;
        pEm->Reset();

        ULONG cFetched;
        IMoniker *pM;
        while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK) 
        {
                IPropertyBag *pBag;
                hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
                if(SUCCEEDED(hr)) 
                {      
                        VARIANT varname; 
                        VARIANT varpath;
                        CString str="";
                        varname.vt = VT_BSTR;
                        varpath.vt = VT_BSTR;
                        //读取摄像头名称
                        hr = pBag->Read(L"FriendlyName", &varname, NULL);
                        if (hr != NOERROR) 
                        {
                                continue;
                        }
                        //读取摄像头PID,VID
                        hr = pBag->Read(L"DevicePath", &varpath, NULL);
                        if (hr == NOERROR) 
                        {    
                                int Count =0;
                                while(varpath.bstrVal[Count] != 0x00 )
                                {
                                        str += varpath.bstrVal[Count];
                                        Count++;
                                }
                                strVid = str.Mid(str.Find("vid_")+4, 4);
                                strPid = str.Mid(str.Find("pid_")+4, 4);
                                if( 0 == strcmp(strVid, vid) && 0 == strcmp(strPid, pid))
                                {
                                        if (camnum<32)
                                        {
                                                CamIndex[camnum]=id;
                                                camnum++;
                                        }        
                                }
                                id++;
                        }            
                        pBag->Release();
                }
                pM->Release();
        }
        return camnum;
}

3 DirectShow获取摄像头视频流

/************************************************************************************
    功能:    打开USB摄像头
    参数:    index   - 输入,摄像头序号
              bDisplayProperties - 输入,指示是否自动弹出摄像头属性页
              nWidth  - 输入,宽度,输入不为0,并且在bDisplayProperties为false时有效
              nHeight - 输入,高度,输入不为0,并且在bDisplayProperties为false时有效
    返回:    true-成功,false-失败
*************************************************************************************/
bool CCameraDS::OpenCamera(int nCamID, bool bDisplayProperties,int nWidth,int nHeight)
{
        
        HRESULT hr = S_OK;

        CoInitialize(NULL);
        // Create the Filter Graph Manager.
        hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
                                                        IID_IGraphBuilder, (void **)&m_pGraph);

        hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, 
                                                        IID_IBaseFilter, (LPVOID *)&m_pSampleGrabberFilter);

        hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &m_pMediaControl);
        hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void **) &m_pMediaEvent);

        hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
                                                        IID_IBaseFilter, (LPVOID*) &m_pNullFilter);


        hr = m_pGraph->AddFilter(m_pNullFilter, L"NullRenderer");

        hr = m_pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&m_pSampleGrabber);

        AM_MEDIA_TYPE   mt;
        ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
        mt.majortype = MEDIATYPE_Video;
        mt.subtype = MEDIASUBTYPE_RGB24;
        mt.formattype = FORMAT_VideoInfo; 
        hr = m_pSampleGrabber->SetMediaType(&mt);
        MYFREEMEDIATYPE(mt);

        m_pGraph->AddFilter(m_pSampleGrabberFilter, L"Grabber");
 
        // Bind Device Filter.  We know the device because the id was passed in
        BindFilter(nCamID, &m_pDeviceFilter);
        m_pGraph->AddFilter(m_pDeviceFilter, NULL);

        CComPtr<IEnumPins> pEnum;
        m_pDeviceFilter->EnumPins(&pEnum);
 
        hr = pEnum->Reset();
        hr = pEnum->Next(1, &m_pCameraOutput, NULL); 

        pEnum = NULL; 
        m_pSampleGrabberFilter->EnumPins(&pEnum);
        pEnum->Reset();
        hr = pEnum->Next(1, &m_pGrabberInput, NULL); 

        pEnum = NULL;
        m_pSampleGrabberFilter->EnumPins(&pEnum);
        pEnum->Reset();
        pEnum->Skip(1);
        hr = pEnum->Next(1, &m_pGrabberOutput, NULL); 

        pEnum = NULL;
        m_pNullFilter->EnumPins(&pEnum);
        pEnum->Reset();
        hr = pEnum->Next(1, &m_pNullInputPin, NULL);

        //SetCrossBar();
        //add by chengs 20160617
        if (nWidth>0 && nHeight>0)
        {
                SetCaps(nWidth,nHeight);
        }

        if (bDisplayProperties) 
        {
                CComPtr<ISpecifyPropertyPages> pPages;

                HRESULT hr = m_pCameraOutput->QueryInterface(IID_ISpecifyPropertyPages, (void**)&pPages);
                if (SUCCEEDED(hr))
                {
                        PIN_INFO PinInfo;
                        m_pCameraOutput->QueryPinInfo(&PinInfo);

                        CAUUID caGUID;
                        pPages->GetPages(&caGUID);

                        OleCreatePropertyFrame(NULL, 0, 0,
                                                L"Property Sheet", 1,
                                                (IUnknown **)&(m_pCameraOutput.p),
                                                caGUID.cElems,
                                                caGUID.pElems,
                                                0, 0, NULL);
                        CoTaskMemFree(caGUID.pElems);
                        PinInfo.pFilter->Release();
                }
        }

        hr = m_pGraph->Connect(m_pCameraOutput, m_pGrabberInput);
        hr = m_pGraph->Connect(m_pGrabberOutput, m_pNullInputPin);

        if (FAILED(hr))
        {
                switch(hr)
                {
                        case VFW_S_NOPREVIEWPIN :
                                break;
                        case E_FAIL :
                                break;
                        case E_INVALIDARG :
                                break;
                        case E_POINTER :
                                break;
                }
        }

        m_pSampleGrabber->SetBufferSamples(TRUE);
        m_pSampleGrabber->SetOneShot(TRUE);
    
        hr = m_pSampleGrabber->GetConnectedMediaType(&mt);
        if(FAILED(hr))
                return false;

        VIDEOINFOHEADER *videoHeader;
        videoHeader = reinterpret_cast<VIDEOINFOHEADER*>(mt.pbFormat);
        m_nWidth = videoHeader->bmiHeader.biWidth;
        m_nHeight = videoHeader->bmiHeader.biHeight;
        m_bConnected = true;

        return true;
}

/************************************************************************
    功能:    获取一帧视频流数据
    参数:
    返回:    一帧视频流数据
*************************************************************************/
unsigned char* CCameraDS::QueryFrame()
{
        long evCode;
        long size = 0;

        m_pMediaControl->Run();

        m_pMediaEvent->WaitForCompletion(5000, &evCode); //INFINITE

        m_pSampleGrabber->GetCurrentBuffer(&size, NULL);
        if (size == 0)
        {
                return NULL;
        }
        //if the buffer size changed
        if (size != m_nBufferSize)
        {
                if (m_pFrame)
                        free(m_pFrame);

                m_nBufferSize = size;
                m_pFrame = (unsigned char*)malloc(m_nWidth*m_nHeight*3);
        }
        if (m_pFrame == NULL)
        {
                return NULL;
        }
        m_pSampleGrabber->GetCurrentBuffer(&m_nBufferSize, (long*)m_pFrame);
        ImrotateVertical_RGB(m_pFrame, m_nWidth, m_nHeight);
        return m_pFrame;
}

 

4 演示Demo

4.1 开发环境

  • Visual Studio 2013

  • DirectShow

  • Windows 10 Pro x64

4.2 功能介绍

        演示VS2013 封装DirectShow,用于打开摄像头、获取摄像头视频流数据等操作。

4.3 下载地址

        开发环境:

  • Visual Studio 2013

  • DirectShow

  • Windows 10 Pro x64

        下载地址:DirectShow访问USB摄像头Demo

 

 

//指定视频采集设备的友好名字,为它创建一个Filter IBaseFilter * CTestPreviewDlg::CreateVideoDevice(const char * inFriendlyName) { return CreateHardwareFilter(CLSID_VideoInputDeviceCategory,inFriendlyName); } //根据设备的友好名字,创建一个代表该设备的Filter IBaseFilter * CTestPreviewDlg::CreateHardwareFilter(GUID inCategory,const char * inFriendlyName) { //创建一个系统枚举组件对象 ICreateDevEnum * enumHardware = NULL; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_ALL, IID_ICreateDevEnum,(void**)&enumHardware); if(FAILED(hr)) { return NULL; } IBaseFilter * hardwareFilter = NULL; IEnumMoniker * enumMoniker = NULL; //为指定的目录创建枚举器 hr = enumHardware->CreateClassEnumerator(inCategory,&enumMoniker,0); if(enumMoniker) { enumMoniker->Reset(); ULONG fetched = 0; IMoniker * moniker = NULL; char friendlyName[256]; //枚举得到该目录下所有的设备,逐个进行名字匹配 while(!hardwareFilter && SUCCEEDED(enumMoniker->Next(1,&moniker, &fetched)) && fetched) { if(moniker) { IPropertyBag * propertyBag = NULL; VARIANT name; friendlyName[0] = 0; hr = moniker->BindToStorage(0,0,IID_IPropertyBag,(void**)&propertyBag); //读取设备的友好名字 if(SUCCEEDED(hr)) { name.vt = VT_BSTR; hr = propertyBag->Read(L"Friendlyname",&name,NULL); } if(SUCCEEDED(hr)) { WideCharToMultiByte(CP_ACP,0,name.bstrVal,-1, friendlyName,256,NULL,NULL); //如果当前设备的友好名字与用户指定的设备名字相同, //则将当前设备标识绑定为Filter形式 if(strcmp(friendlyName,inFriendlyName) == 0) { moniker->BindToObject(0,0,IID_IBaseFilter, (void**)&hardwareFilter); } } //释放使用过的接口 if(propertyBag) { propertyBag->Release(); propertyBag = NULL; } moniker->Release(); } } enumMoniker->Release(); } enumHardware->Release(); return hardwareFilter; }
DirectShow是一种基于COM的Microsoft多媒体框架,它可以在Windows上用于捕获,处理和渲染音频和视频数据。使用DirectShow,可以轻松地从摄像机或其他视频设备中捕获视频流并将其发送到计算机。下面是一些关于如何使用DirectShow打开摄像头源代码的详细信息。 首先,要使用DirectShow,需要导入相应的库和头文件。在Visual Studio中,可以使用以下代码来导入必要的库和头文件: #include <dshow.h> #pragma comment(lib, "strmiids.lib") 接下来,要打开摄像头,需要使用以下步骤: 1.创建一个DirectShow图形,这可以在以下代码中完成: IGraphBuilder *pGraph = NULL; CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); 这将创建一个名为pGraph的IGraphBuilder对象。 2.添加捕获过滤器到图形中: ICaptureGraphBuilder2 *pBuilder = NULL; CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&pBuilder); pBuilder->SetFiltergraph(pGraph); IBaseFilter *pCaptureFilter = NULL; pBuilder->FindInterface(NULL, &MEDIATYPE_Video, CLSID_VideoInputDeviceCategory, IID_IBaseFilter, (void **)&pCaptureFilter); pGraph->AddFilter(pCaptureFilter, L"Video Capture"); 在这里,我们创建了一个名为pBuilder的ICaptureGraphBuilder2对象,并使用它来添加捕获过滤器到图形中。FindInterface方法用于查找与摄像头相关联的过滤器。这将创建一个名为pCaptureFilter的IBaseFilter对象,并将其添加到图形中。 3.创建渲染过滤器: IBaseFilter *pRenderFilter = NULL; CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter,(void**)&pRenderFilter); pGraph->AddFilter(pRenderFilter, L"Video Renderer"); 该代码将创建一个名为pRenderFilter的IBaseFilter对象,并将其添加到图形中。 4.连接过滤器: pGraph->ConnectDirect(GetPin(pCaptureFilter, PINDIR_OUTPUT), GetPin(pRenderFilter, PINDIR_INPUT), NULL); 这将使用ConnectDirect方法连接过滤器,从捕获过滤器输出到渲染过滤器输入。 5.运行图形: IMediaControl *pControl = NULL; pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl); pControl->Run(); 此代码段将创建名为pControl的IMediaControl对象,并使用Run方法运行DirectShow图形,此时摄像头应该已可以正常工作。 这就是使用DirectShow打开摄像头源代码的基本步骤。但需要注意的是,有时候会出现不同的设备和SDK,可能会需要进行不同的步骤才能在DirectShow中打开摄像头
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值