1.UVC摄像i头从插入到加载驱动,看我前面的文章分析过程
2.UVC摄像头驱动加载时在kernel\drivers\media\usb\uvc\uvc_driver.c uvc_probe->函数中完成的
整个流程如下
uvc_register_chains->
uvc_register_terms->
uvc_register_video->
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
vdev->ioctl_ops = &uvc_ioctl_ops;
video_register_device->
__video_register_device-> H:\RK3399\kernel\drivers\media\v4l2-core\v4l2-dev.c
case VFL_TYPE_GRABBER:
name_base = "video";
//这个字符串就是我们在/dev/下面看到的vido0,video1等设备,按摄像头
3.应用层app打开camera,最后必然调用open("/dev/video0") 打开设备
uvc_v4l2_open-> kernel\drivers\media\usb\uvc\uvc_v4l2.c
4.摄像头打开的同时就开始操作摄像头,进行抓取视频流的初始化工作
从uvc_start_streaming-> drivers/media/usb/uvc/uvc_queue.c 开始
uvc_init_video_isoc->urb->complete = uvc_video_complete;
重要的就是注册了一个uvc_video_complete一个urb完成函数,这个就是我们视频数据解析的开始
完成的时候注册了一个视频解析函数uvc_video_decode_isoc
uvc_video_decode_isoc 分析
static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf)
{
u8 *mem;
int ret, i;
for (i = 0; i < urb->number_of_packets; ++i) {
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
/* Mark the buffer as faulty. */
if (buf != NULL)
buf->error = 1;
continue;
}
/* Decode the payload header. */
mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; //解析数据头
do {
ret = uvc_video_decode_start(stream, buf, mem, //从数据队列里寻找一帧数据的开头,找到了退出
urb->iso_frame_desc[i].actual_length);
if (ret == -EAGAIN) {
uvc_video_validate_buffer(stream, buf);
buf = uvc_queue_next_buffer(&stream->queue,
buf);
}
} while (ret == -EAGAIN);
if (ret < 0)
continue;
/* Decode the payload data. */
uvc_video_decode_data(stream, buf, mem + ret, //开始解析一帧数据
urb->iso_frame_desc[i].actual_length - ret);
/* Process the header again. */
uvc_video_decode_end(stream, buf, mem, //判断数据是否结束,数据结束
urb->iso_frame_desc[i].actual_length);
if (buf->state == UVC_BUF_STATE_READY) { //确认数据结束标志
uvc_video_validate_buffer(stream, buf); //判断数据是否有效
buf = uvc_queue_next_buffer(&stream->queue, buf); //找到下一个空buf并把当前buf数据送到用户空间
}
}
}
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
unsigned long flags;
if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
buf->bytesused = 0;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
return buf;
}
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, //查找下一个空buf来存储下一帧数据
queue);
else
nextbuf = NULL;
spin_unlock_irqrestore(&queue->irqlock, flags);
buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); //数据完成,把当前帧返回用户空间,唤醒等待数据的进程
return nextbuf;
}
从urb数据不断的完成,不断的中断通知系统,系统调用 uvc_video_complete进而调用 uvc_video_decode_isoc来解析视频数据
在 uvc_video_complete被回调的时候 usb_submit_urb会再次被提交,从而保证整个数据采集中断不断的进行,这些数据一帧一帧的被解析出来,我们再把每帧数据送到用户空间,用户通过app把这些画帧按一定的速度显示出来,从而就形成了我们看到的视频。