UVC摄像头百科
+ -

UVC摄像头扩展单元功能的开发步骤是什么?

2021-05-23 3317 1

写在扩展单元的题外话

本人在开发支持UVC扩展单元的directShow应用时,必现并不需要注册接口,只需要在其源过滤器参照后续的代码枚举相关的接口如IKsControl,IKsTopologyInfo定位到指定的扩展单元接口后,直接使用其对应的IKsControl::KsProperty即可实现扩展单元的特定类请求

使用本站的UVC扩展单元调试工具进行调试:http://www.usbzh.com/article/detail-516.html

UVC扩展单元的固件支持

要进行UVC摄像头扩展单元的开发,首先第一是需要摄像头的描述符中含有UVC扩展单元描述符.

一个扩展单元描述符的示例:

BYTE  Length:            0x1a
BYTE  DescriptorType:    0x24
BYTE  DescriptorSubtype: 0x06
BYTE  bUnitID:           0x05
GUID  guidExtensionCode: xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx
BYTE  bNumControls:      0x03
BYTE  bNrInPins:         0x01
BYTE  baSourceID[0]:     0x01

说明:

  • bUnitID:不能和别的ID冲突。
  • guidExtensionCode:需要通过guidgen.exe生成一个惟一的ID标识。

其它要求:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/stream/device-requirements-for-usb-video-class-extension-units

扩展单元的注册

扩展单元的注册可以使用INF文件进行注册,也可以吏用注册表进行注册,也可以使用DLL注册。

使用INF文件进行扩展单元注册

INF 文件可用于提供设备特定的名称或注册扩展插件单元。
详情可叁考:
https://docs.microsoft.com/zh-cn/windows-hardware/drivers/stream/providing-a-uvc-inf-file

使用注册表进行扩展单元进行注册:

HKLM
{
    NoRemove SYSTEM
    {
        NoRemove CurrentControlSet
        {
            NoRemove Control
            {
                NoRemove NodeInterfaces
                {
                    ForceRemove {xxxxxxxx-xxxx-xxxx-xxxx-
                       xxxxxxxxxxxx} = s 'Extension Unit
                       Property Set'
                    {
                        val IID = b 'yyyyyyyyyyyyyyyyyyy
                           yyyyyyyyyyyyy'
                        val CLSID = b 'zzzzzzzzzzzzzzzzz
                           zzzzzzzzzzzzzzz'
                    }
                }
            }
        }
    }
}

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/stream/sample-registry-entry-for-uvc-extension-units
https://docs.microsoft.com/zh-cn/windows-hardware/drivers/stream/sample-extension-unit-plug-in-dll

使用插件DLL注册

使用DLL注册,需要先添加注册表项:

HKCR
{
    NoRemove CLSID
    {
         ForceRemove {zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz} = s 'CompanyName Extension Unit Interface'
        {
            InprocServer32 = s '%MODULE%'
                                                {
                                val ThreadingModel = s 'Both'
                                                }
        }

    }
}

示例详见:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/stream/sample-extension-unit-plug-in-dll

UVC 扩展单元的示例应用程序

pUnkOuter这个指针是UVC-CAMERA源,可通过下列函数指针ppSrcFilter返回。DeviceName是UVC设备名称。


HRESULT  CCamera::FindCaptureDevice(const std::wstring& DeviceName,IBaseFilter** ppSrcFilter)
{
    HRESULT hr;
    IBaseFilter* pSrc = NULL;
    IMoniker* pMoniker = NULL;
    ULONG cFetched;

    if (!ppSrcFilter)
        return E_POINTER;

    // Create the system device enumerator
    CComPtr <ICreateDevEnum> pDevEnum = NULL;

    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
        IID_ICreateDevEnum, (void**)&pDevEnum);
    if (FAILED(hr))
    {
        printf("Couldn't create system enumerator!  hr=0x%x", hr);
        return hr;
    }

    // Create an enumerator for the video capture devices
    CComPtr <IEnumMoniker> pClassEnum = NULL;

    hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
    if (FAILED(hr))
    {
        printf("Couldn't create class enumerator!  hr=0x%x", hr);
        return hr;
    }

    // If there are no enumerators for the requested type, then 
    // CreateClassEnumerator will succeed, but pClassEnum will be NULL.
    if (pClassEnum == NULL)
    {
        MessageBox(NULL, TEXT("No video capture device was detected.\r\n\r\n")
            TEXT("This sample requires a video capture device, such as a USB WebCam,\r\n")
            TEXT("to be installed and working properly.  The sample will now close."),
            TEXT("No Video Capture Hardware"), MB_OK | MB_ICONINFORMATION);
        return E_FAIL;
    }

    // Use the first video capture device on the device list.
    // Note that if the Next() call succeeds but there are no monikers,
    // it will return S_FALSE (which is not a failure).  Therefore, we
    // check that the return code is S_OK instead of using SUCCEEDED() macro.

    while (S_OK == (pClassEnum->Next(1, &pMoniker, &cFetched)))
    {
        IPropertyBag* pPropBag;
        hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
        if (SUCCEEDED(hr))
        {
            //获得Filter的Friendly Name
            VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(L"FriendlyName", &varName, 0);
            if (SUCCEEDED(hr))
            {
               // TCHAR* pName = (TCHAR*)varName.bstrVal;
                std::wstring str(varName.bstrVal);
                if (str == DeviceName)
                {
                    hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc);
                    if (FAILED(hr))
                    {
                        printf("Couldn't bind moniker to filter object!  hr=0x%x", hr);
                        return hr;
                    }
                    break;
                }
            }
            VariantClear(&varName);
            ////创建Filter实例
            //IBaseFilter* pFilater;
            //hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
            //pPropBag->Release();
        }
        pPropBag->Release();
        pMoniker->Release();
    }

    * ppSrcFilter = pSrc;

    return hr;
}

应用程序通过使用 IKsTopologyInfo::CreateNodeInstance ,然后调用节点对象上的 QueryInterface 以获取所需的 COM API 来访问接口。

// pUnkOuter is the unknown associated with the base filter
  hr = pUnkOuter->QueryInterface(__uuidof(IKsTopologyInfo),
                               (void **) &pKsTopologyInfo);
  if (!SUCCEEDED(hr))
  {
        printf("Unable to obtain IKsTopologyInfo %x\n", hr);
 goto errExit;
  }

  hr = FindExtensionNode(pKsTopologyInfo,
     GUID_EXTENSION_UNIT_DESCRIPTOR,
     &dwExtensionNode);
  if (FAILED(hr))
  {
        printf("Unable to find extension node : %x\n", hr);
 goto errExit;
  }

  hr = pKsTopologyInfo->CreateNodeInstance(
        dwExtensionNode,
   __uuidof(IExtensionUnit),
 (void **) &pExtensionUnit);
 if (FAILED(hr))
  {
        printf("Unable to create extension node instance : %x\n", hr);
 goto errExit;
  }

  hr = pExtensionUnit->get_PropertySize(1, &ulSize);
  if (FAILED(hr))
  {
        printf("Unable to find property size : %x\n", hr);
 goto errExit;
  }

  pbPropertyValue = new BYTE[ulSize];
  if (!pbPropertyValue)
  {
      printf("Unable to allocate memory for property value\n");
      goto errExit;
  }

  hr = pExtensionUnit->get_Property(1,ulSize, pbPropertyValue);
  if (FAILED(hr))
  {
      printf("Unable to get property value\n");
      goto errExit;
  }

  // assume the property value is an integer
  ASSERT(ulSize == 4);
  printf("The value of property 1 = %d\n", *((int *)
     pbPropertyValue));

编写 FindExtensionNode 函数的代码以查找必需的扩展单元节点并在 dwExtensionNode 中返回其 ID。 此 ID 在此示例代码中对 IKsTopologyInfo:: CreateNodeInstance 方法的后续调用中使用。

支持扩展单元的自动更新事件

hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hEvent)
{
    printf("CreateEvent failed\n");
    goto errExit;
}
Event.Set = KSEVENTSETID_VIDCAPNotify;
Event.Id = KSEVENT_VIDCAP_AUTO_UPDATE;
Event.Flags = KSEVENT_TYPE_ENABLE;

EventData.NotificationType = KSEVENTF_EVENT_HANDLE;
EventData.EventHandle.Event = hEvent;
EventData.EventHandle.Reserved[0] = 0;
EventData.EventHandle.Reserved[1] = 0;

// register for autoupdate events
hr = m_pKsControl->KsEvent(
    &Event, 
 sizeof(KSEVENT), 
    &EventData, 
 sizeof(KSEVENTDATA), 
    &ulBytesReturned);
if (FAILED(hr))
{
    printf("Failed to register for auto-update event : %x\n", hr);
 goto errExit;
}

// Wait for event for 5 seconds 
dwError = WaitForSingleObject(hEvent, 5000);

// cancel further notifications
hr = m_pKsControl->KsEvent(
    NULL, 
    0, 
    &EventData, 
 sizeof(KSEVENTDATA), 
    &ulBytesReturned);
if (FAILED(hr))  printf("Cancel event returns : %x\n", hr);

if ((dwError == WAIT_FAILED) || 
   (dwError == WAIT_ABANDONED) ||
   (dwError == WAIT_TIMEOUT))
{
    printf("Wait failed : %d\n", dwError);
 goto errExit;
} 
printf("Wait returned : %d\n", dwError);

// handle the autoupdate event..

FindExtensionNode


HRESULT FindExtensionNode(IKsTopologyInfo *pKsTopologyInfo, GUID guid, DWORD *node)
{
    HRESULT hr = E_FAIL;
    DWORD dwNumNodes = 0;
    GUID guidNodeType;
    IKsControl *pKsControl = NULL;
    ULONG ulBytesReturned = 0;
    KSP_NODE ExtensionProp;

    if(!pKsTopologyInfo || !node)
        return E_POINTER; 

    // Retrieve the number of nodes in the filter
    hr = pKsTopologyInfo->get_NumNodes(&dwNumNodes);
    if(!SUCCEEDED(hr))
        return hr;
    if(dwNumNodes == 0)
        return E_FAIL;


    // Find the extension unit node that corresponds to the given GUID
    for(unsigned int i = 0; i < dwNumNodes; i++)
    {
        hr = E_FAIL;
        pKsTopologyInfo->get_NodeType(i, &guidNodeType);
        if(IsEqualGUID(guidNodeType, KSNODETYPE_DEV_SPECIFIC))
        {
            printf("found one xu node\n");
            IExtensionUnit*   pExtensionUnit = NULL;
          hr = pKsTopologyInfo->CreateNodeInstance(i, __uuidof(IExtensionUnit), (void **)&pExtensionUnit);
            if(SUCCEEDED(hr))
            {
                ExtensionProp.Property.Set = guid;
                ExtensionProp.Property.Id = 0;
                ExtensionProp.Property.Flags = KSPROPERTY_TYPE_SETSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
                ExtensionProp.NodeId = i;
                ExtensionProp.Reserved = 0;

                *node = i;
                return hr;

                /*
                hr = pKsControl->KsProperty((PKSPROPERTY)&ExtensionProp, sizeof(ExtensionProp), NULL, 0, &ulBytesReturned);
                if(SUCCEEDED(hr))
                {
                    *node = i;
                    break;
                }
                */
            }
            else
            {
                printf("CreateNodeInstance failed - 0x%x\n", hr);
            }
        }
    }

    return hr;
}

参考资料:
https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/extension-unit-plug-in-architecture

HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936   

0 篇笔记 写笔记

UVC 扩展单元描述符
扩展单元描述符允许硬件设计者定义任意一组控制接口,使类驱动程序可以在设备与供应商提供的主机软件之间进行通讯控制。扩展单元描述符ID由bUnitID字段中的值唯一标识描述符。同一视频功能内的任何其他单元或终端不得具有相同的功能ID。guidExtensionCode字段包含特定于供应商的代码,该代......
UVC摄像头扩展单元功能的开发步骤是什么?
写在扩展单元的题外话本人在开发支持UVC扩展单元的directShow应用时,必现并不需要注册接口,只需要在其源过滤器参照后续的代码枚举相关的接口如IKsControl,IKsTopologyInfo定位到指定的扩展单元接口后,直接使用其对应的IKsControl::KsProperty即可实现扩展......
UVC 扩展单元控制请求
扩展单元控制请求设置或读取扩展单元内的视频控件.bmRequestTypebRequestwValue(2)wIndexwLengthData00100001SET_CUR选择子扩展单元ID参数长度参数块bmRequestTypebRequestwValue(2)wIndexwLengthData1......
Windows 发送UVC XU Set命令卡死的一种处理办法
Windows系统应用层可以通过获取UVC设备的IKsControl对象,然后调用KsProperty来GET/SET XU命令。经过测试发现,如果调用SET命令的时候设备拔出了,有一定的概率会出现卡死现象,KsProperty这个方法卡住了。复现方法很简单,获取IKsControl对象,然后Sle......
V4L2访问摄像头扩展单元命令
我们可以通过IOCTL访问扩展单元,调用方法如下:ioctl(fd, UVCIOC_CTRL_QUERY, struct uvc_xu_control_query *);访问不同的扩展命令只需要修改uvc_xu_control_query 结构体里面内容即可。uvc_xu_control_qu......
XU打造UVC命令行调试工具
在给客户交付UVC固件之后,有时客户会反馈问题,需要客户查看固件的打印信息或者执行一些命令辅助定位问题。如果有一个工具可以通过USB线连接到设备,直接在电脑上查看打印信息,执行命令就好了。有想法就要行动起来,行动才能解决问题。目前该功能已经实现了,说一下实现的方法吧。上位机OS Windows设......
UVC摄像头扩展单元调试工具-临江仙版
UVC摄像头的开发者为了实现一些私有定制协议的通讯,通常使用UVC自带的扩展单元来实现。通常是自定义一个扩展单元,再定义一些相应的选择子,然后再通过特定类请求实现与固件的通讯。 本站现提供一个Windows环境下,使用DirectShow开发的UVC扩展单元调试工具.UVC摄像头扩展单元工具来自......
UVC PTZ和扩展单元XU的过滤与分发调试笔记
在写这篇文章之前,我得首先感谢微软,感谢你的不严格,让我可以有空子可钻,不至于太过狼狈…记得看过一句话,开局一张图,内容全告编。我多么希望自己在UVC摄像头的调试过程中也可以瞎编乱造,这样不至于身心疲惫。至少保持着愉快的心情,吹吹牛,也是一件很开心的事。今日有一个需求,对UVC摄像头的请求进行......
UVC摄像头驱动装调试总结
前几天发表了一篇文章 UVC PTZ和扩展单元的过滤与分发调试笔记,原文见: http://www.usbzh.com/article/detail-517.html ,本来想着应该没啥问题了,可没想到上周五在实际测试过程中,还是出现了问题:经过长时间的UVC摄像头工作,突然驱动显示了大量数据包错误......
windows上面通过dshow接口访问uvc摄像头扩展单元,只能访问0x01-0x1F命令问题
最近在给客户做一个修改摄像头数据的工具,在通过dshow接口发送扩展命令的时候发现0x01-0x1F命令都可以发送成功,0x20及以上定义的命令都发送失败,并且返回错误码0x80070490。谷歌百度查了很久终于在windows官网文档中找到了答案,原来是USB 视频类驱动程序对UVC扩展单元实现......
UVC规范USB摄像头扩展单元XU的初始化分析
USB摄像头的UVC的扩展单元用于UVC摄像头的自定义功能扩展,支持UVC扩展单元的摄像头在其UVC控制接口中都会有一个扩展单元描述符。该扩展单元描述符的字段描述符了其支持的选择子,GUID,扩展单元等相关信息。我们知道,UVC摄像头在接入PC机后,和其它USB设备一样,第一步是肯定是USB设备信......
USBXU摄像头UVC扩展单元调试工具使用图解
USB中文网技术交流群临江仙大佬推出的USB摄像头UVC扩展单元调试工具,大大提高高了大家进行USB摄像头扩展单元的调试的工作效率,不过对于UVCB扩展单元不太熟悉的同学来说,有时在使用上也有一定的困难。今天本人将做一个详细的介绍它的使用方法。USB摄像头UVC扩展单元调试工具的准备该软件的版权归......
记一次USB摄像头UVC扩展单元功能的BUG追踪笔记
毫不自负的说,本人觉地自己对USB摄像头的UVC扩展单元这一块的技术点应该来说还是很熟悉的,至少截止到现在,本人对于此处的技术知识点都是成竹在胸了。无论项目中提出的各处功能,本人都比较好地完成了工作并完满地交付了。但有时,有些事,架不过时间的考验和相关配套程序条件限制。今天,本人就翻车了。从中午1......
Windows XP UVC XU传输数据限制
UVC是从Windows XP sp2开始支持的,经过测试发现在xp sp3系统上收发XU数据长度有限制。当len >= 60的时候收发数据会反馈失败。搞了个测试固件,len(cs) = 58 + cs && maxlen = 64, 当cs=2的时候就没法获取cs数据长度了......
UVCXU摄像头扩展单元调试工具UVCXU-USB中文网官方版
使用说明最新版本:为了便于调试,自己只好手动编写一个UVC扩展单元调试工具。第一版,只是实现了基本的功能,界面比较粗糙,界面如下:这里介绍一下界面的功能:UVC设备:打开软件,自动枚举系统中所有的UVC。XU_GUID:需要手动填写扩展单元的GUID,格式如界面所示。如果执行GET_CUR或......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!