uvc camera在usb带宽不足的情况下,如何正常出图?

    在某些平台设备,如果同时使用多个uvc camera进行数据采集或者是同一个USB,既用作OTG功能,又用作USB host功能,会因为USB 带宽的的限制,导致camera无法正常使用,使用失败的现象如下:

uvcvideo: Failed to submit URB 0 (-28).
No space left on device

    在通过VIDIOC_STREAMON开启流数据传输时,将会产生以上错误,这个是由与USB的带宽不足导致的,出现这样的问题,可以尝试将分辨率调低,或者换为编码格式输出,如果还是不行,可以通过手动限制uvc camera的输出带宽。

    uvc驱动会在注册uvc camera的时候,在注册video节点之前,查询uvc camera实际硬件的设备情况,并保存好,然后当使用的时候,设置完输出分辨率、格式等参数之后,在VIDIOC_STREAMON的时候,会根据设置的分辨率以及格式、注册video节点时查询得到的信息计算所需带宽,然后将该带宽与uvc camera实际情况比较反馈得到一个合适的带宽设置到uvc camera中。

 

    那么如何查看uvc camera支持的带宽信息呢?先通过lsusb命令查看得到uvc camera的厂家信息,比如得到下面的信息:

Bus 001 Device 041: ID 045e:0779 Microsoft Corp. LifeCam HD-3000

    得知厂家id之后,比如从上面的信息可得知usb id为0x045e,接着通过lsusb -v -d 045e:   >info.txt命令,将查询uvc camera的详细信息输出到info.txt。在info.txt中,通过搜索bAlternateSetting可以看到类似以下的信息:

    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       3
      bNumEndpoints           1
      bInterfaceClass        14 Video
      bInterfaceSubClass      2 Video Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            5
          Transfer Type            Isochronous
          Synch Type               Asynchronous
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       4
      bNumEndpoints           1
      bInterfaceClass        14 Video
      bInterfaceSubClass      2 Video Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            5
          Transfer Type            Isochronous
          Synch Type               Asynchronous
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               1

    其中的wMaxPacketSize变量说明的就是该uvc camera支持的带宽,它会支持多种带宽,以适应不同的USB传输速率。

    在Linux内核中的uvc驱动就有usb带宽的匹配设置过程,代码位于drivers/media/usb/uvc下的uvc_video.c中,在uvc_init_video()函数中,就会根据所设置的分辨率以及格式等参数,计算得到的带宽与uvc camera硬件支持的带宽进行一个适配,从而设置有效参数。

/*
 * Initialize isochronous/bulk URBs and allocate transfer buffers.
 */
static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
{
	struct usb_interface *intf = stream->intf;
	struct usb_host_endpoint *ep;
	unsigned int i;
	int ret;

	stream->sequence = -1;
	stream->last_fid = -1;
	stream->bulk.header_size = 0;
	stream->bulk.skip_payload = 0;
	stream->bulk.payload_size = 0;

	uvc_video_stats_start(stream);

	if (intf->num_altsetting > 1) {
		struct usb_host_endpoint *best_ep = NULL;
		unsigned int best_psize = UINT_MAX;
		unsigned int bandwidth;
		unsigned int uninitialized_var(altsetting);
		int intfnum = stream->intfnum;

		/* Isochronous endpoint, select the alternate setting. */
		bandwidth = stream->ctrl.dwMaxPayloadTransferSize;

		if (bandwidth == 0) {
			uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
				"bandwidth, defaulting to lowest.\n");
			bandwidth = 1;
		} else {
			uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
				"B/frame bandwidth.\n", bandwidth);
		}

		for (i = 0; i < intf->num_altsetting; ++i) {
			struct usb_host_interface *alts;
			unsigned int psize;

			alts = &intf->altsetting[i];
			ep = uvc_find_endpoint(alts,
				stream->header.bEndpointAddress);
			if (ep == NULL)
				continue;

			/* Check if the bandwidth is high enough. */
			psize = uvc_endpoint_max_bpi(stream->dev->udev, ep);
			if (psize >= bandwidth && psize <= best_psize) {
				altsetting = alts->desc.bAlternateSetting;
				best_psize = psize;
				best_ep = ep;
			}
		}

		if (best_ep == NULL) {
			uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
				"for requested bandwidth.\n");
			return -EIO;
		}

		uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
			"(%u B/frame bandwidth).\n", altsetting, best_psize);

		ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
		if (ret < 0)
			return ret;

		ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
	} else {
		...
	}

        ...

	return 0;
}

        在上述函数中,有以下这个操作:

		/* Isochronous endpoint, select the alternate setting. */
		bandwidth = stream->ctrl.dwMaxPayloadTransferSize;

       bandWidth就是根据分辨率以及格式得到的所需带宽,我们可以手动的修改这个值,将它调小,降低usb带宽,从而在带宽有限的情况下可以有效的使用uvc camera,但是需要注意,并不是可以随便的修改该值,因为最终还需要通过该值与uvc camera的实际支持的带宽进行匹配,即上面通过lsusb得到的info.txt中的wMaxPacketSize值进行适配。wMaxPacketSize代表着usb传输数据时,每个包的数据量大小,当改小时,降低带宽,相应的分辨率以及帧率也都会降低,这个可以尝试修改,得到一个最合适的带宽。

    为了方便调试,看到相应的带宽设置参数,可通过修改drivers/media/usb/uvc中的uvc_driver.c的uvc_trace_param值,将其修改为如下

unsigned int uvc_trace_param = UVC_TRACE_VIDEO;

    这样就可以将与带宽相关的信息打印出来。

    在嵌入式中,如果出现这样的问题,如果是uvc camera本身是支持高分辨率的,一般都是嵌入式的usb驱动没有做好导致的。建议检查usb驱动,是不是标准的hci驱动,同时,有没有调试过相应驱动的同步传输。

 

转载请注明出处!

你可能感兴趣的:(camera)