UVC 批量传输技术探讨

简介: UVC 批量传输技术探讨

1.描述符布局


image.png


如图为 bulk 传输描述符布局,相对于同步传输,批量传输只有一个可选择的配置,没有备用配置。


  • VideoControl :无变化


  • VideoStream:只有一个 bAlternateSetting(删除alt=1描述符)。同时支持bulk in 端点。


需要修改的地方:


static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
 .bLength  = USB_DT_INTERFACE_SIZE,
 .bDescriptorType = USB_DT_INTERFACE,
 .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
 .bAlternateSetting = 0,
 .bNumEndpoints  = 1,            /* alt0 挂一个bulk 端点*/
 .bInterfaceClass = USB_CLASS_VIDEO,
 .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING,
 .bInterfaceProtocol = 0x00,
 .iInterface  = 0,
};


端点描述符:


static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
 .bLength  = USB_DT_ENDPOINT_SIZE,
 .bDescriptorType = USB_DT_ENDPOINT,
 .bEndpointAddress = USB_DIR_IN,
 .bmAttributes  = USB_ENDPOINT_XFER_BULK,
    .wMaxPacketSize = 512,
    .bInterval = 0,
};


2. 控制流程


根据USB规范可知,同步传输方式是只要带中带有同步端点的接口,系统会定时从设备中读取数据,无论设备中是否有数据。而如要停止数据的传输,只需要选中不带有同步端点的接口即可。


USB同步传输这种灵活的数据传输方式是依靠视频流接口的转换接口即我们常说的备份接口实现的。在默认情况下数据不传输时,视频数据流接口和备份接口ID为0,其它的备份接口是可根据视频数据传输的大小可按需选择。


我们知道,批量传输只有一个可选择的altsetting ,那么如何知道设备控制设备的开流和关流动作呢?


2.1 stream on


使用视频流接口的VS_COMMIT_CONTROL 提交给设备,让其以指定的数据格式进行数据采样。


image.png


2.2 stream off


关流操作,通过发送一个clear_halt 请求,来中断流的操作。


image.png


2.3 代码分析


基于 linux 4.14.281 内核版本:分析host 端uvc 开关流流程


  • drivers/media/usb/uvc/uvc_queue.c


开流操作:uvc_start_streaming


static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
{
 struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
 struct uvc_streaming *stream = uvc_queue_to_stream(queue);
 unsigned long flags;
 int ret;
 queue->buf_used = 0;
 ret = uvc_video_enable(stream, 1);
 if (ret == 0)
  return 0;
 spin_lock_irqsave(&queue->irqlock, flags);
 uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
 spin_unlock_irqrestore(&queue->irqlock, flags);
 return ret;
}


关流操作:uvc_stop_streaming


static void uvc_stop_streaming(struct vb2_queue *vq)
{
 struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
 struct uvc_streaming *stream = uvc_queue_to_stream(queue);
 unsigned long flags;
 uvc_video_enable(stream, 0);
 spin_lock_irqsave(&queue->irqlock, flags);
 uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
 spin_unlock_irqrestore(&queue->irqlock, flags);
}


重点关注:uvc_video_enable


/*
 * Enable or disable the video stream.
 */
int uvc_video_enable(struct uvc_streaming *stream, int enable)
{
 int ret;
 if (!enable) {
  uvc_uninit_video(stream, 1);
  if (stream->intf->num_altsetting > 1) {
   usb_set_interface(stream->dev->udev,
       stream->intfnum, 0);
  } else {
   /* UVC doesn't specify how to inform a bulk-based device
    * when the video stream is stopped. Windows sends a
    * CLEAR_FEATURE(HALT) request to the video streaming
    * bulk endpoint, mimic the same behaviour.
    */
   unsigned int epnum = stream->header.bEndpointAddress
        & USB_ENDPOINT_NUMBER_MASK;
   unsigned int dir = stream->header.bEndpointAddress
      & USB_ENDPOINT_DIR_MASK;
   unsigned int pipe;
   pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
   usb_clear_halt(stream->dev->udev, pipe);
  }
  uvc_video_clock_cleanup(stream);
  return 0;
 }
 ret = uvc_video_clock_init(stream);
 if (ret < 0)
  return ret;
 /* Commit the streaming parameters. */
 ret = uvc_commit_video(stream, &stream->ctrl);
 if (ret < 0)
  goto error_commit;
 ret = uvc_init_video(stream, GFP_KERNEL);
 if (ret < 0)
  goto error_video;
 return 0;
error_video:
 usb_set_interface(stream->dev->udev, stream->intfnum, 0);
error_commit:
 uvc_video_clock_cleanup(stream);
 return ret;
}


分析代码可知:


  • 首先判断是否关流操作;


  • 如果是,判断接口的可选配置是否大于1,如果大于1,发送usb_set_interface(intfnum,0) 关流,否则发送usb_clear_halt 请求;


  • 如果是开流操作,发送commit 请求


  • 然后初始化 video


/*
 * 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 {
  /* Bulk endpoint, proceed to URB initialization. */
  ep = uvc_find_endpoint(&intf->altsetting[0],
    stream->header.bEndpointAddress);
  if (ep == NULL)
   return -EIO;
  /* Reject broken descriptors. */
  if (usb_endpoint_maxp(&ep->desc) == 0)
   return -EIO;
  ret = uvc_init_video_bulk(stream, ep, gfp_flags);
 }
 if (ret < 0)
  return ret;
 /* Submit the URBs. */
 for (i = 0; i < UVC_URBS; ++i) {
  ret = usb_submit_urb(stream->urb[i], gfp_flags);
  if (ret < 0) {
   uvc_printk(KERN_ERR, "Failed to submit URB %u "
     "(%d).\n", i, ret);
   uvc_uninit_video(stream, 1);
   return ret;
  }
 }
 /* The Logitech C920 temporarily forgets that it should not be adjusting
  * Exposure Absolute during init so restore controls to stored values.
  */
 if (stream->dev->quirks & UVC_QUIRK_RESTORE_CTRLS_ON_INIT)
  uvc_ctrl_restore_values(stream->dev);
 return 0;
}


从这段代码可以看出,如果altsetting 大于1 走同步传输,发送usb_set_interface(intfnum, altsetting) ,选择合适带宽配置。然后初始化同步传输管道。


否则,初始化 同步传输管道,提交传输。


3. 其他注意点


对比同步传输和批量传输我们可以发现,对于uvc 批量传输, 由于没有同步传输类似的多个可选配置,所以没法灵活控制开流关流操作。特别是在linux 平台下,要切换不同的格式和分辨率的时候没有同步传输方便。


故,笔者觉得同步传输适合传固定数据,或者对usb camera 做中转使用比较合适。


对于批量传输如果能充分发送usb 吞吐量,(USB2.0)一个微帧传输13个packet,理论带宽将近50MB/s, 笔者实际测试能达到47MB/s,对于YUYV图像能够极大提高帧率。

相关文章
|
开发工具 Android开发
Android平台GB28181设备接入端语音广播技术探究和填坑指南
GB/T28181-2016官方规范和交互流程,我们不再赘述。
109 0
|
3月前
|
监控 Java Linux
Jetson 学习笔记(十二):CSI摄像头实现rtsp流的传输并对动态获取多路流进行探索
本文是关于如何在Jetson设备上使用CSI摄像头实现RTSP流传输的详细教程,包括安装依赖、编译gst-rtsp-server、测试、源代码介绍以及如何动态获取多路流的RTSP服务器。
234 2
Jetson 学习笔记(十二):CSI摄像头实现rtsp流的传输并对动态获取多路流进行探索
|
7月前
|
编解码
FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作
《FFmpeg开发实战》书中3.4.3节讲解如何将H.264流封装成MP4。H.264流通常以SPS→PPS→IDR帧开始,这一说法通过雷霄骅的H264分析器得到验证。分析器能解析H.264文件但不支持MP4。ZLMediaKit服务器在遇到I帧时会自动插入SPS和PPS配置帧,确保流符合标准格式。若缺少这些帧,客户端拉流时会报错。FFmpeg开发实战:从零基础到短视频上线》书中提供了更多FFmpeg开发细节。
173 0
FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作
|
6月前
|
Python
在广播系统工程中,这通常涉及到音频信号的生成、处理、传输和播放等多个环节。
在广播系统工程中,这通常涉及到音频信号的生成、处理、传输和播放等多个环节。
|
开发工具 Android开发
Android平台GB28181设备接入端预置位查询(PresetQuery)探讨和技术实现
之前blog介绍了GB28181云台控制(PTZCmd)相关,本文主要是介绍下GB28181预置位查询。
190 0
|
编解码 监控 网络协议
Android平台GB28181设备接入侧如何实现按需打开视音频采集传输
Android平台GB28181设备接入侧如何实现按需打开视音频采集传输
166 2
|
Android开发 开发者
Android平台GB28181设备接入端语音广播如何实现实时音量调节
Android平台GB28181设备接入,语音广播功能非常重要,本文要介绍的,不是语音广播的流程,语音广播流程,之前的blog也有非常详细的分享,感兴趣的可以参考官方规范书的交互流程:
|
Windows
USB 设备:自定义批量传输及实现
USB 设备:自定义批量传输及实现
1174 0
振弦采集模块的通讯速率和软件握手( UART)
模块开始一次测量时,从 UART 接口主动发送 XOFF 信号( 0x13), 表示模块开始忙于测量数据, 当测量完成时主动发送 XON 信号( 0x11), 表示模块本次测量完成,正处于空闲状态。在开启模块的软件握手功能后, 若需要向模块发送指令,建议 UART 的通讯流程为: 首先等待模块返回 XON 信号( 0x11),当收到 XON 信号或等待超时后立即向模块发送指令。
振弦采集模块的通讯速率和软件握手( UART)
|
传感器 算法 测试技术
ESP32-C3 应用 篇(实例一、通过MQTT协议连接ONENET上报传感器数据,云平台下发灯光调色)
ESP32-C3学到现在,我们已经掌握了从基本外设到网络服务端的相关知识, 这篇文章就是做一个简单的应用,使用开发板连接ONENET云平台, 使用MQTT协议,上报温湿度和光照数据,平台下发命令控制全彩灯颜色切换。
1044 0
ESP32-C3 应用 篇(实例一、通过MQTT协议连接ONENET上报传感器数据,云平台下发灯光调色)

热门文章

最新文章