zoukankan      html  css  js  c++  java
  • [C++]DirectShow检测音视频输入设备及其采集参数

    官方文档:https://docs.microsoft.com/zh-cn/windows/win32/directshow/directshow

    创建CLR类库项目(CSharpDirectShow),编写托管的DirectShow类库,右键项目属性-->链接器--> 输入-->附加依赖项;添加静态库文件Strmiids.lib和Quartz.lib;

     定义头文件CSharpDirectShow.h,包含头文件dshow.h,定义如下方法:

    #pragma once
    
    #include <dshow.h>
    
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    public ref class DirectShow
    {
    public:
        /// <summary>
        /// 在当前线程上初始化COM库,并将并发模型标识为单线程单元(STA)
        /// </summary>
        /// <returns></returns>
        static Boolean ComInit();
        /// <summary>
        /// 关闭当前线程上的COM库,卸载该线程加载的所有DLL,释放该线程维护的所有其他资源,并强制关闭该线程上的所有RPC连接
        /// </summary>
        static void ComUinit();
        /// <summary>
        /// 获取视频输入设备
        /// </summary>
        /// <param name="devices"></param>
        /// <returns></returns>
        static Int32 GetVideoInputDevices([Out]array<VideoInputDsDevice^>^% devices);
        /// <summary>
        /// 获取音频输入设备
        /// </summary>
        /// <param name="device"></param>
        /// <returns></returns>
        static Int32 GetAudioInputDevices([Out]array<AudioInputDsDevice^>^% device);
    };

    其中,初始化COM库只能在STA线程调用,控制台程序直接调用会返回失败,在Winform和WPF等应用程序应该在第一次使用时初始化COM库;
    当前线程初始化COM库和关闭COM库:

    Boolean DirectShow::ComInit()
    {
        HRESULT hr = CoInitialize(NULL);
        return hr == S_OK || hr == S_FALSE;
    }
    
    void DirectShow::ComUinit()
    {
        CoUninitialize();
    }

    一、枚举视频输入设备,参考:https://docs.microsoft.com/zh-cn/windows/win32/directshow/selecting-a-capture-device

    1、创建视频输入设备的枚举器IEnumMoniker

    HRESULT DirectShow::EnumerateDevices(REFGUID category, IEnumMoniker** ppEnum)
    {
        // 创建系统设备枚举器
        ICreateDevEnum* pDevEnum;
        HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
    
        if (SUCCEEDED(hr))
        {
            // 为类别创建枚举数.
            hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
    
            if (hr == S_FALSE)
                hr = VFW_E_NOT_FOUND;  // 类别是空的,视为错误
    
            pDevEnum->Release();
        }
        return hr;
    }
    
    Int32 DirectShow::GetVideoInputDevices([Out]array<VideoInputDsDevice^>^% devices)
    {
        devices = nullptr;
    
        IEnumMoniker* pEnum;
        HRESULT hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);        // 创建视频输入设备枚举器
    
        if (FAILED(hr))
            return hr;
    
        hr = EnumerateVideoInputDevices(pEnum, devices);    // 枚举视频输入设备
        pEnum->Release();
        return hr;
    }

    2、调用IEnumMoniker::Next()方法枚举设备,调用IPropertyBag::Read方法读取设备属性,以获取设备的友好名称和设备标识字符串:

    HRESULT DirectShow::EnumerateVideoInputDevices(IEnumMoniker* pEnum, [Out]array<VideoInputDsDevice^>^% devices)
    {
        HRESULT hr = S_FALSE;
        devices = nullptr;
        List<VideoInputDsDevice^>^ list = gcnew List<VideoInputDsDevice^>();
    
        IMoniker* pMoniker;
        while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
        {
            IPropertyBag* pPropBag;
            hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
            if (FAILED(hr))
            {
                pMoniker->Release();
                continue;
            }
    
            VideoInputDsDevice^ device = gcnew VideoInputDsDevice();
    
            VARIANT var;
            VariantInit(&var);
    
            // 获取设备友好名
            hr = pPropBag->Read(L"FriendlyName", &var, 0);
    
            if (FAILED(hr))
                hr = pPropBag->Read(L"Description", &var, 0);
    
            if (SUCCEEDED(hr))
            {
                device->FriendlyName = System::String(var.bstrVal).ToString();
                VariantClear(&var);
            }
    
            // 获取设备Moniker名
            LPOLESTR pOleDisplayName = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc(MAX_MONIKER_NAME_LENGTH * 2));
            hr = pMoniker->GetDisplayName(NULL, NULL, &pOleDisplayName);
    
            if (SUCCEEDED(hr))
            {
                device->MonikerName = System::String(pOleDisplayName).ToString();
                array<VideoParams^>^ params;
                hr = EnumerateVideoParams(pMoniker, params);    // 枚举设备的采集参数
                if (SUCCEEDED(hr))
                    device->Params = params;
            }
    
            CoTaskMemFree(pOleDisplayName);
    
            list->Add(device);
            pPropBag->Release();
            pMoniker->Release();
        }
    
        devices = list->ToArray();
        return S_OK;
    }

    3、枚举视频输入设备的采集参数:

    HRESULT DirectShow::EnumerateVideoParams(IMoniker* pMoniker, [Out]array<VideoParams^>^% params)
    {
        params = nullptr;
        IBaseFilter* pFilter;
        HRESULT    hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
    
        if (FAILED(hr))
            return hr;
    
        IEnumPins* pinEnum;
        hr = pFilter->EnumPins(&pinEnum);
    
        if (FAILED(hr)) {
            pFilter->Release();
            return hr;
        }
    
        List<VideoParams^>^ list = gcnew List<VideoParams^>();
        IPin* pPins;
        while (pinEnum->Next(1, &pPins, NULL) == S_OK)
        {
            PIN_INFO pinInfo;
            hr = pPins->QueryPinInfo(&pinInfo);
    
            if (FAILED(hr) || pinInfo.dir != PINDIR_OUTPUT)
            {
                pPins->Release();
                continue;
            }
    
            IEnumMediaTypes* mtEnum;
            hr = pPins->EnumMediaTypes(&mtEnum);
    
            if (FAILED(hr))
            {
                pPins->Release();
                continue;
            }
    
            AM_MEDIA_TYPE* mt;
            while (mtEnum->Next(1, &mt, NULL) == S_OK)
            {
                VideoParams^ param = nullptr;
                if (mt->formattype == FORMAT_VideoInfo)
                {
                    VIDEOINFOHEADER* pVih = reinterpret_cast<VIDEOINFOHEADER*>(mt->pbFormat);
                    param = gcnew VideoParams();
                    param->FrameWidth = pVih->bmiHeader.biWidth;
                    param->FrameHeight = pVih->bmiHeader.biHeight;
                    param->AverageFrameRate = pVih->AvgTimePerFrame == 0 ? 0 : 10000000 / pVih->AvgTimePerFrame;
                }
                else if (mt->formattype == FORMAT_VideoInfo2) {
                    VIDEOINFOHEADER2* pVih = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat);
                    param = gcnew VideoParams();
                    param->FrameWidth = pVih->bmiHeader.biWidth;
                    param->FrameHeight = pVih->bmiHeader.biHeight;
                    param->AverageFrameRate = pVih->AvgTimePerFrame == 0 ? 0 : 10000000 / pVih->AvgTimePerFrame;
                }
    
                if (param && param->AverageFrameRate > 1)
                {
                    Boolean isExit = false;
                    for each (VideoParams ^ item in list)
                    {
                        if (item->FrameWidth == param->FrameWidth && item->FrameHeight == param->FrameHeight && item->AverageFrameRate == param->AverageFrameRate)
                        {
                            isExit = true;
                            break;
                        }
                    }
                    if (!isExit)
                        list->Add(param);
                }
            }
            pPins->Release();
        }
    
        pFilter->Release();
    
        params = list->ToArray();
        return S_OK;
    }

    调用代码及结果:

    var ret = DirectShow.GetVideoInputDevices (out VideoInputDsDevice[] videoInputDevices);
    
    if (ret == 0) {
        Console.WriteLine ("视频输入设备:");
    
        foreach (var videoInputDevice in videoInputDevices) {
            Console.WriteLine ($"{videoInputDevice.FriendlyName}	{videoInputDevice.MonikerName}");
    
            if (videoInputDevice.Params.Length > 0) {
                Console.WriteLine ("像素宽度	像素高度	1秒平均帧数");
    
                foreach (var param in videoInputDevice.Params) {
                    Console.WriteLine ($"{param.FrameWidth}	{param.FrameHeight}	{param.AverageFrameRate}");
                }
            }
    
            Console.WriteLine ();
        }
    }

    二、枚举音频输入设备,参考https://docs.microsoft.com/zh-cn/windows/win32/directshow/selecting-a-capture-device,过程和视频一样:

    ret = DirectShow.GetAudioInputDevices (out AudioInputDsDevice[] audioInputDevices);
    
    if (ret == 0) {
        Console.WriteLine ("音频输入设备:");
    
        foreach (var audioInputDevice in audioInputDevices) {
            Console.WriteLine ($"{audioInputDevice.FriendlyName}	{audioInputDevice.MonikerName}");
    
            if (audioInputDevice.Params.Length > 0) {
                Console.WriteLine ("音频格式	通道数	采样速率	块对齐	位数");
    
                foreach (var param in audioInputDevice.Params) {
                    Console.WriteLine ($"{param.Format}	{param.Channels}	{param.SampleRate}	{param.BlockAlign}	{param.BitsPerSample}");
                }
            }
    
            Console.WriteLine ();
        }
    }

     代码已上传至Github:https://github.com/LowPlayer/CameraCapture

  • 相关阅读:
    某些输入文件使用或覆盖了已过时的 API
    laravel 重写以及500错误
    Ubuntu镜像使用帮助
    E: Sub-process /usr/bin/dpkg returned an error code (1) 解决方案
    python请求java Selenium Webdriver
    Selenium Grid 简易安装
    selenium + python 添加等待时间
    selenium帮助手册以及 webdriver的各种driver
    thinkphp结合layui上传图片
    thinkphp----替换写标签的方法
  • 原文地址:https://www.cnblogs.com/pumbaa/p/14255725.html
Copyright © 2011-2022 走看看