windows - 摄像头采集

1、DriectShow 采集

1.1 DirectShow 基础

DirectShow 其主要设计目标是通过将应用程序与数据传输的复杂性、硬件差异和同步隔离,简化在 Windows 平台上创建数字媒体应用程序的任务 ,DirectShow简化媒体播放、格式转换和捕获任务

下图显示了应用程序、DirectShow组件以及应用程序支持的一些硬件和软件DirectShow之间的关系。
在这里插入图片描述
在这里插入图片描述
Filter是DirectShow技术体系中最基本的概念。如上图所示,DirectShow中的Filter分成三大类:Source Filter、Transform Filter、Render Filter。Source Filter就是提供数据源的Filter,所有的数据都是从Source Filter流出去的。不管是多媒体文件还是多媒体设备,Source Filter都进行了封装统一了接口,在使用方式上保持了一致。Transform Filter则是对数据进行操作处理的Filter,所有的图像操作都应该在这里进行。而Render Filter则是用来渲染图像的Filter,不管是保存到文件还是输出到其他地方,都由这个Render Filter来实现。Windows系统本身提供了非常多的Filter,我们在开发的时候可以直接使用。

下面是UI 界面配置directshow 软件(graphstudionext)的示意图,通过拖动就可以配置,filter 的概念。而整个图就 Filter Graph。如果需要就向 Filter Graph 添加filter , 并且链接 pin(引脚)
在这里插入图片描述

1.2 DriectShow 采集应用场景

现在市场上大多数的摄像头和采集卡在Windows系统上的驱动基于WDM架构,微软定义了采集卡设备与上层程序间的通信驱动接口,这已经成为一种标准,因此,控制摄像头和采集图像基本都通过Directshow框架来实现。

2、DirecShow 代码和原理介绍

2.1 ICaptureGraphBuilder2

因为graph manager 的接口的管理是比较复杂的,为了简化 IGraphBuilder 的使用, DirectShow 提供了一个名为 Capture Graph Builder 的辅助对象,ICaptureGraphBuilder2 接口对IGraphBuilder 进行封装。ICaptureGraphBuilder2 包含用于构建和控制捕获图的方法。

使用捕获图构建器

在这里插入图片描述

类图

在这里插入图片描述

初始化捕获图构建器

首先调用 CoCreateInstance 创建 Capture Graph Builder(ICaptureGraphBuilder2 ) 和 Filter Graph Manager (IGraphBuilder )的新实例。然后通过使用指向过滤器图管理器的IGraphBuilder接口的指针调用ICaptureGraphBuilder2::SetFiltergraph 来初始化捕获图生成器。下图说明了这个过程。

在这里插入图片描述

代码

HRESULT InitCaptureGraphBuilder(
  IGraphBuilder **ppGraph,  // Receives the pointer.
  ICaptureGraphBuilder2 **ppBuild  // Receives the pointer.
)
{
    if (!ppGraph || !ppBuild)
    {
        return E_POINTER;
    }
    IGraphBuilder *pGraph = NULL;
    ICaptureGraphBuilder2 *pBuild = NULL;

    // Create the Capture Graph Builder.
    HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, 
        CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuild );
    if (SUCCEEDED(hr))
    {
        // Create the Filter Graph Manager.
        hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
            IID_IGraphBuilder, (void**)&pGraph);
        if (SUCCEEDED(hr))
        {
            // 此处就是说明2者的关系。
            pBuild->SetFiltergraph(pGraph);

            // Return both interface pointers to the caller.
            *ppBuild = pBuild;
            *ppGraph = pGraph; // The caller must release both interfaces.
            return S_OK;
        }
        else
        {
            pBuild->Release();
        }
    }
    return hr; // Failed
}

2.2 DirectShow Video Capture Filters

pin 类别

video capture file 有2类的pin

  • caputre pin : 带时间戳,会有一定的延迟,一般是编码和存储使用
  • preview pin:不带时间错,为了实时显示,会丢帧,只是为了渲染使用

在详细的内容,大家可以自己看下微软官网DirectShow Video Capture Filters

2.3 获取设备信息

枚举设备

1、调用 CoCreateInstance 创建 System Device Enumerator 一个实例 ICreateDevEnum
2、 ICreateDevEnum::CreateClassEnumerator 并将设备类别指定为 GUID。对于捕获设备,以下类别是相关的。
在这里插入图片描述
3、CreateClassEnumerator方法返回一个指针IEnumMoniker接口。要枚举名字,请调用IEnumMoniker::Next。(Moniker 是 绰号的意思)
在这里插入图片描述

#include <windows.h>
#include <dshow.h>

#pragma comment(lib, "strmiids")

HRESULT EnumerateDevices(REFGUID category, IEnumMoniker **ppEnum)
{
    // Create the System Device Enumerator.
    ICreateDevEnum *pDevEnum;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,  
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));

    if (SUCCEEDED(hr))
    {
        // Create an enumerator for the category.
        hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
        if (hr == S_FALSE)
        {
            hr = VFW_E_NOT_FOUND;  // The category is empty. Treat as an error.
        }
        pDevEnum->Release();
    }
    return hr;
}

获取属性

设备含义如下:
在这里插入图片描述

“FriendlyName”:属性可用于每个设备。它包含设备的可读名称。
“Description”:属性仅适用于 DV 和 D-VHS/MPEG 摄像机设备。有关详细信息,请参阅MSDV 驱动程序和MSTape 驱动程序。如果可用,它包含比“FriendlyName”属性更具体的设备描述。通常它包括供应商名称。
“DevicePath”:属性不是人类可读的字符串,但保证对于系统上的每个视频捕获设备都是唯一的。您可以使用此属性来区分同一型号设备的两个或多个实例。
如果存在“WaveInID”属性,则表示 DirectShow 捕获过滤器在内部使用波形音频API 与设备进行通信。“WaveInID”:属性的值对应于waveIn*函数使用的标识符,例如waveInOpen。

代码示例

  • 调用IMoniker::BindToStorage 获得IPropertyBag 的接口
  • 调用IPropertyBag::Read 获取属性
void DisplayDeviceInformation(IEnumMoniker *pEnum)
{
    IMoniker *pMoniker = NULL;

    while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
    {
        IPropertyBag *pPropBag;
        HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
        if (FAILED(hr))
        {
            pMoniker->Release();
            continue;  
        } 

        VARIANT var;
        VariantInit(&var);

        // Get description or friendly name.
        hr = pPropBag->Read(L"Description", &var, 0);
        if (FAILED(hr))
        {
            hr = pPropBag->Read(L"FriendlyName", &var, 0);
        }
        if (SUCCEEDED(hr))
        {
            printf("%S\n", var.bstrVal);
            VariantClear(&var); 
        }

        hr = pPropBag->Write(L"FriendlyName", &var);

        // WaveInID applies only to audio capture devices.
        hr = pPropBag->Read(L"WaveInID", &var, 0);
        if (SUCCEEDED(hr))
        {
            printf("WaveIn ID: %d\n", var.lVal);
            VariantClear(&var); 
        }

        hr = pPropBag->Read(L"DevicePath", &var, 0);
        if (SUCCEEDED(hr))
        {
            // The device path is not intended for display.
            printf("Device path: %S\n", var.bstrVal);
            VariantClear(&var); 
        }

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

void main()
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        IEnumMoniker *pEnum;

        hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
        if (SUCCEEDED(hr))
        {
            DisplayDeviceInformation(pEnum);
            pEnum->Release();
        }
        hr = EnumerateDevices(CLSID_AudioInputDeviceCategory, &pEnum);
        if (SUCCEEDED(hr))
        {
            DisplayDeviceInformation(pEnum);
            pEnum->Release();
        }
        CoUninitialize();
    }
}

如果要为一个设备创建一个Filter,设备IMoniker 指针,调用IMoniker::BindToObject 获得一个IBaseFilter 指针,然后调用IFilterGraph::AddFilter 加入到 Filter Graph中

IBaseFilter *pCap = NULL;
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
    hr = m_pGraph->AddFilter(pCap, L"Capture Filter");
}

3、预览视频

ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
// Initialize pBuild (not shown).

IBaseFilter *pCap; // Video capture filter.

/* Initialize pCap and add it to the filter graph (not shown). */

hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, 
    pCap, NULL, NULL);

ICaptureGraphBuilder2::RenderStream方法的
第一个参数指定引脚类别;对于预览图,请使用PIN_CATEGORY_PREVIEW。

第二个参数指定媒体类型,作为主要类型 GUID。对于视频,请使用MEDIATYPE_Video。DV 设备提供交错的音频和视频,其媒体类型为MEDIATYPE_Interleaved。(有关 DV 捕获的更多信息,请参阅DirectShow 中的数字视频。)

第三个参数是指向捕获过滤器的IBaseFilter接口的指针。本示例中不需要接下来的两个参数。它们用于指定渲染流可能需要的其他过滤器。将最后一个参数设置为NULL会导致 Capture Graph Builder 根据媒体类型为流选择默认渲染器。对于视频,Capture Graph Builder 始终使用Video Renderer过滤器作为默认渲染器。

虽然 pin 类别被指定为PIN_CATEGORY_PREVIEW,但过滤器是否真的有预览 pin 并不重要;它可以有一个视频端口引脚或只是一个捕获引脚。无论哪种情况,Capture Graph Builder 都会自动构建正确的图形
在这里插入图片描述

4、捕获到文件

下图显示了将视频捕获到 AVI 文件的最简单的图形。
在这里插入图片描述
该AVI多路复用器过滤从捕捉引脚获取视频流并将其打包成AVI流。音频流也可以连接到 AVI Mux 过滤器,在这种情况下,mux 将交错两个流。该文件写入过滤器的AVI流写入到磁盘中。

要构建图形,首先调用ICaptureGraphBuilder2::SetOutputFileName方法,如下所示:

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(
    &MEDIASUBTYPE_Avi,  // Specifies AVI for the target file.
    L"C:\\Example.avi", // File name.
    &pMux,              // Receives a pointer to the mux.
    NULL);              // (Optional) Receives a pointer to the file sink.

接下来,调用ICaptureGraphBuilder2::RenderStream方法将捕获过滤器连接到 AVI Mux,如下所示:

hr = pBuild->RenderStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video,      // Media type.
    pCap,                  // Capture filter.
    NULL,                  // Intermediate filter (optional).
    pMux);                 // Mux or file sink filter.

// Release the mux filter.
pMux->Release();

5、Control Capture Graph

过滤器图形管理器的IMediaControl接口具有运行、停止和暂停整个图形的方法。但是,如果过滤器图具有捕获和预览流,您可能希望独立控制这两个流。例如,您可能想要预览视频而不捕获它。您可以通过ICaptureGraphBuilder2::ControlStream方法执行此操作。

以下代码将视频捕获流设置为运行四秒,在图形运行后一秒开始:

// Control the video capture stream. 
REFERENCE_TIME rtStart = 10000000, rtStop = 50000000;
const WORD wStartCookie = 1, wStopCookie = 2;  // Arbitrary values.
hr = pBuild->ControlStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video,      // Media type.
    pCap,                 // Capture filter.
    &rtStart, &rtStop,     // Start and stop times.
    wStartCookie, wStopCookie  // Values for the start and stop events.
);
pControl->Run();
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值