UVC等时传输中的dwMaxVideoFrameSize和dwMaxPayloadTransferSize关系
在其整个视频流控制接口参数偏移地址18处的字段为一4字节的dwMaxVideoFrameSize,代表的是如果选择当前数据帧格式,其一帧图像的最大数据量大小,以字节为单位。
关于dwMaxVideoFrameSize的计算可以参考 YUV2摄像头相关数据大小计算。
这里我们主要说一下dwMaxVideoFrameSize和dwMaxPayloadTransferSize关系。
通过上节UVC等时传输中的dwMaxPayloadTransferSize可知道,在一次URB的传输过程中,可能包含多次的dwMaxPayloadTransferSize,那么这个多次具体是多少了,而且这个多次直接影响的是我们使用抓包工具如bushound进行抓包的总在大小。
在上一节初的Linux代码中,根据dwMaxPayloadTransferSize选择了合适的转换接口后,然后调用uvc_init_video_isoc(stream, best_ep, gfp_flags);来进行初始化同步/等时传输。在这个函数里就是说明了dwMaxVideoFrameSize和dwMaxPayloadTransferSize关系。
uvc_init_video_isoc源代码如下:
static int uvc_init_video_isoc(struct uvc_streaming *stream,
struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
struct urb *urb;
struct uvc_urb *uvc_urb;
unsigned int npackets, i;
u16 psize;
u32 size;
psize = uvc_endpoint_max_bpi(stream->dev->udev, ep);
size = stream->ctrl.dwMaxVideoFrameSize;
npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;
size = npackets * psize;
for_each_uvc_urb(uvc_urb, stream) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
uvc_video_stop_transfer(stream, 1);
return -ENOMEM;
}
urb->dev = stream->dev->udev;
urb->context = uvc_urb;
urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = uvc_urb->dma;
#else
urb->transfer_flags = URB_ISO_ASAP;
#endif
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = uvc_urb->buffer;
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
for (i = 0; i < npackets; ++i) {
urb->iso_frame_desc[i].offset = i * psize;
urb->iso_frame_desc[i].length = psize;
}
uvc_urb->urb = urb;
}
return 0;
}
通过uvc_init_video_isoc的源代码可知,其分配URB的buffer内存空间是通过uvc_alloc_urb_buffers来计算的,其源代码如下:
static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
unsigned int size, unsigned int psize, gfp_t gfp_flags)
{
unsigned int npackets;
unsigned int i;
/* Buffers are already allocated, bail out. */
if (stream->urb_size)
return stream->urb_size / psize;
/* Compute the number of packets. Bulk endpoints might transfer UVC
* payloads across multiple URBs.
*/
npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_PACKETS)
npackets = UVC_MAX_PACKETS;
/* Retry allocations until one succeed. */
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
stream->urb_size = psize * npackets;
#ifndef CONFIG_DMA_NONCOHERENT
uvc_urb->buffer = usb_alloc_coherent(
stream->dev->udev, stream->urb_size,
gfp_flags | __GFP_NOWARN, &uvc_urb->dma);
#else
uvc_urb->buffer =
kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
#endif
if (!uvc_urb->buffer) {
uvc_free_urb_buffers(stream);
break;
}
uvc_urb->stream = stream;
}
if (i == UVC_URBS) {
uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
"of %ux%u bytes each.\n", UVC_URBS, npackets,
psize);
return npackets;
}
}
uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
"per packet).\n", psize);
return 0;
}
从上面的代码可知,如果定义了urb_size,则直接使用urb_size/psize (等时/同步传输中dwMaxPayloadTransferSize=psize),如果不存在,则使用size即dwMaxVideoFrameSize直接除以pssize,计算包的个数。这里可以看到,在一次URB传输过程中,效率最高的时候就是一个URB可以直接传输一帧图像的数据,使用多次即多个类似windows中的USBD_ISO_PACKET_DESCRIPTOR。
#define UVC_MAX_PACKETS 32
如果包大大,则最大不能超过32个,这样表示一个URB中最大可以传输32个ISO同步传输包,这样还可能存在一帧数据需要多个URB来传输完成。这样一帧图像可能包需要多个URB来传输,每个URB中最多不超过32个ISO传输。但实际在分配内存的时候,可能由于dwMaxPayloadTransferSize太大,导致一个URB直接分配32个dwMaxPayloadTransferSize的连续的整片内存分配失败,Linux做了拆半分配算法处理。
uvc_init_video_isoc函数的后半段可以看到,这里是分配urb空间,并对各个urb内部的iso_frame进行初始化,并设置相关该iso_frame的大小和偏移。