Bootstrap

uvc摄像头代码解析7

13.u vc 视频初始化
13.1 uvc数据流控制
[cpp 
struct uvc_streaming_control {  
    __u16 bmHint;  
    __u8  bFormatIndex; //视频格式索引  
    __u8  bFrameIndex;  //视频帧索引  
    __u32 dwFrameInterval;  //视频帧间隔  
    __u16 wKeyFrameRate;    //  
    __u16 wPFrameRate;  
    __u16 wCompQuality;  
    __u16 wCompWindowSize;  
    __u16 wDelay;   //延时  
    __u32 dwMaxVideoFrameSize;  //最大视频帧大小  
    __u32 dwMaxPayloadTransferSize;  
    __u32 dwClockFrequency; //时钟频率  
    __u8  bmFramingInfo;  
    __u8  bPreferedVersion;  
    __u8  bMinVersion;  //版本  
    __u8  bMaxVersion;  //版本  
} __attribute__((__packed__));  
13.2 uvc_video_init
[cpp]  
int uvc_video_init(struct uvc_streaming *stream)  
{  
    struct uvc_streaming_control *probe = &stream->ctrl; //获取uvc数据流的uvs数据流控制对象  
    struct uvc_format *format = NULL;  
    struct uvc_frame *frame = NULL;  
    unsigned int i;  
    int ret;  
    if (stream->nformats == 0) {  
        uvc_printk(KERN_INFO, "No supported video formats found.\n");  
        return -EINVAL;  
    }  
    atomic_set(&stream->active, 0);  
    uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); //初始化视频缓冲区队列  
    usb_set_interface(stream->dev->udev, stream->intfnum, 0);  //选择Alt.Setting 0  
    if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) //VS_PROBE_CONTROL(GET_DEF)  
        uvc_set_video_ctrl(stream, probe, 1);                   //VS_PROBE_CONTROL(SET_DEF)  
    ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);    //VS_PROBE_CONTROL(GET_CUR)  
    if (ret < 0)  
        return ret;  
    for (i = stream->nformats; i > 0; --i) {  //获取对应的uvc格式  
        format = &stream->format[i-1];     
        if (format->index == probe->bFormatIndex) //匹配uvc格式索引值  
            break;  
    }  
    if (format->nframes == 0) {  
        uvc_printk(KERN_INFO, "No frame descriptor found for the default format.\n");  
        return -EINVAL;  
    }  
    for (i = format->nframes; i > 0; --i) {  
        frame = &format->frame[i-1]; //获取对应的uvc帧  
        if (frame->bFrameIndex == probe->bFrameIndex) //匹配uvc帧索引值  
            break;  
    }  
    probe->bFormatIndex = format->index;      //设置uvc视频流控制的格式索引为uvc格式的索引  
    probe->bFrameIndex = frame->bFrameIndex;  //设置uvc视频流控制的帧索引为uvc帧的索引  
    stream->cur_format = format;             //设置uvc格式为uvc数据流的cur_format成员  
    stream->cur_frame = frame;                   //设置uvc帧未uvc数据流的cur_frame成员  
    /* Select the video decoding function 选择视频解码函数*/  
    if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {   //视频采集  
        if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)  
            stream->decode = uvc_video_decode_isight;  
        else if (stream->intf->num_altsetting > 1)  
            stream->decode = uvc_video_decode_isoc;  //同步方式  
        else  
            stream->decode = uvc_video_decode_bulk;  //bluk方式  
    }   
    else {  //视频播放  
        if (stream->intf->num_altsetting == 1)  
            stream->decode = uvc_video_encode_bulk;  
        else {  
            uvc_printk(KERN_INFO, "Isochronous endpoints are not supported for video output devices.\n");  
            return -EINVAL;  
        }  
    }  
    return 0;  
}  
13.2.1 初始化uvc队列
[cpp]  
void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,int drop_corrupted)  
{  
    mutex_init(&queue->mutex);  
    spin_lock_init(&queue->irqlock);  
    INIT_LIST_HEAD(&queue->mainqueue);   //初始化uvc视频队列mainqueue链表  
    INIT_LIST_HEAD(&queue->irqqueue);    //初始化uvc视频队列irqqueue链表  
    queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;  
    queue->type = type;  
}  
 
14.uvc V4L2设备
14.1 V4L2操作函数集
[cpp] 
const struct v4l2_file_operations uvc_fops = {  
    .owner      = THIS_MODULE,  
    .open       = uvc_v4l2_open,    //打开方法  
    .release             = uvc_v4l2_release,    //释放方法  
    .unlocked_ioctl = uvc_v4l2_ioctl,   //控制方法  
    .read       = uvc_v4l2_read,    //读方法  
    .mmap       = uvc_v4l2_mmap,    //映射方法  
    .poll       = uvc_v4l2_poll,    //轮询方法  
};  
14.2 打开方法
14.2.1 相关结构体
[cpp]  
struct uvc_fh {//uvc句柄  
    struct uvc_video_chain *chain;  //uvc视频链  
    struct uvc_streaming *stream;   //uvc视频流  
    enum uvc_handle_state state;  
};  
14.2.2 open
[cpp]  
static int uvc_v4l2_open(struct file *file)  
{  
    struct uvc_streaming *stream;  
    struct uvc_fh *handle;  
    int ret = 0;  
  
    uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");  
    stream = video_drvdata(file);   //获取uvc视频流  
    if (stream->dev->state & UVC_DEV_DISCONNECTED)    //设备没连接  
        return -ENODEV;  
    ret = usb_autopm_get_interface(stream->dev->intf);    //唤醒设备  
    if (ret < 0)  
        return ret;  
    /* Create the device handle. */  
    handle = kzalloc(sizeof *handle, GFP_KERNEL);   //创建uvc句柄  
    if (handle == NULL) {  
        usb_autopm_put_interface(stream->dev->intf);  
        return -ENOMEM;  
    }  
    if (atomic_inc_return(&stream->dev->users) == 1) {  
        ret = uvc_status_start(stream->dev); //uvc状态开始  
        if (ret < 0) {  
            usb_autopm_put_interface(stream->dev->intf);  
            atomic_dec(&stream->dev->users);  
            kfree(handle);  
            return ret;  
        }  
    }  
    handle->chain = stream->chain;    //捆绑uvc句柄和uvc视频链  
    handle->stream = stream; //捆绑uvc句柄和uvc视频流  
    handle->state = UVC_HANDLE_PASSIVE;  //设置uvc状态为未激活  
    file->private_data = handle; //将uvc句柄作为文件的私有数据  
    return 0;  
}  
14.2.2.1 uvc_status_start启动状态
[cpp]  
int uvc_status_start(struct uvc_device *dev)  
{  
    if (dev->int_urb == NULL)  
        return 0;  
    return usb_submit_urb(dev->int_urb, GFP_KERNEL); //提交urb  
}  
参看 12.uvc状态初始化
14.3 控制方法
14.3.1 V4L2的控制方式可以参考下面的资料
linux媒体接口API
 
常用的命令
[cpp]  
VIDIOC_REQBUFS:分配内存   
VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址   
VIDIOC_QUERYCAP:查询驱动功能   
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式   
VIDIOC_S_FMT:设置当前驱动的频捕获格式   
VIDIOC_G_FMT:读取当前驱动的频捕获格式   
VIDIOC_TRY_FMT:验证当前驱动的显示格式   
VIDIOC_CROPCAP:查询驱动的修剪能力   
VIDIOC_S_CROP:设置视频信号的边框   
VIDIOC_G_CROP:读取视频信号的边框   
VIDIOC_QBUF:把数据从缓存中读取出来   
VIDIOC_DQBUF:把数据放回缓存队列   
VIDIOC_STREAMON:开始视频显示函数   
VIDIOC_STREAMOFF:结束视频显示函数   
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。   
14.3.2 uvc设备V4L2控制方法uvc_v4l2_do_ioctl
[cpp]  
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)  
{  
    struct video_device *vdev = video_devdata(file);//获取V4L2设备  
    struct uvc_fh *handle = file->private_data;//获取uvc句柄  
    struct uvc_video_chain *chain = handle->chain;//获取uvc视频链  
    struct uvc_streaming *stream = handle->stream;//获取uvc视频流  
    long ret = 0;  
  
    switch (cmd) {  
    ...  
    case ...:  
    {  
        ...  
        break;  
    }  
    return ret;  
}  
a.VIDIOC_STREAMON 开始视频显示函数
[cpp]  
<pre class="cpp" name="code">   case VIDIOC_STREAMON:  
    {  
        int *type = arg;  
        if (*type != stream->type)  
            return -EINVAL;  
        if (!uvc_has_privileges(handle))  
            return -EBUSY;  
        mutex_lock(&stream->mutex);  
        ret = uvc_video_enable(stream, 1);  //uvc视频流使能  
        mutex_unlock(&stream->mutex);  
        if (ret < 0)  
            return ret;  
        break;  
    }</pre>a.1 uvc视频流使能<br>  
<pre class="cpp" name="code">int uvc_video_enable(struct uvc_streaming *stream, int enable)  
{  
    int ret;  
    if (!enable) {  
        uvc_uninit_video(stream, 1);//逆初始化视频  
        usb_set_interface(stream->dev->udev, stream->intfnum, 0);  
        uvc_queue_enable(&stream->queue, 0);//uvc禁用队列  
        return 0;  
    }  
    ret = uvc_queue_enable(&stream->queue, 1);   //uvc使能队列  
    if (ret < 0)  
        return ret;  
    /* Commit the streaming parameters. */  
    ret = uvc_commit_video(stream, &stream->ctrl);   //uvc提交视频参数  
    if (ret < 0)  
        return ret;  
    return uvc_init_video(stream, GFP_KERNEL);  //uvc初始化视频  
}</pre>a.1.1 uvc使能队列  
<pre></pre>  
<pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)  
{  
    unsigned int i;  
    int ret = 0;  
    mutex_lock(&queue->mutex);  
    if (enable) {   //使能uvc队列  
        if (uvc_queue_streaming(queue)) {   //判断队列标志是否为UVC_QUEUE_STREAMING  
            ret = -EBUSY;  
            goto done;  
        }  
        queue->sequence = 0;  
        queue->flags |= UVC_QUEUE_STREAMING; //设置队列标志  
        queue->buf_used = 0; //设置缓冲区使用标志  
    }   
    else {  
        uvc_queue_cancel(queue, 0); //取消uvc队列  
        INIT_LIST_HEAD(&queue->mainqueue);   //重新初始化uvc队列mainqueue队列头  
        for (i = 0; i < queue->count; ++i)  
            queue->buffer[i].state = UVC_BUF_STATE_IDLE; //设置缓冲区状态为闲置态  
        queue->flags &= ~UVC_QUEUE_STREAMING;    //设置队列标志  
    }  
done:  
    mutex_unlock(&queue->mutex);  
    return ret;  
}</pre>a.1.2 uvc提交视频参数  
<pre></pre>  
<pre class="cpp" name="code"><pre class="cpp" name="code">int uvc_commit_video(struct uvc_streaming *stream,struct uvc_streaming_control *probe)  
{  
    return uvc_set_video_ctrl(stream, probe, 0);    //uvc设置视频控制  
}</pre><span style="font-family:Arial,Helvetica,sans-serif">a.1.3 uvc初始化视频</span>  
<pre></pre>  
<pre class="cpp" name="code"><pre class="cpp" name="code">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;  
    if (intf->num_altsetting > 1) {   //同步方式  
        struct usb_host_endpoint *best_ep = NULL;  
        unsigned int best_psize = 3 * 1024;  
        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 = le16_to_cpu(ep->desc.wMaxPacketSize);  
            psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));  
            if (psize >= bandwidth && psize <= best_psize) {  
                altsetting = i;  
                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);  //uvc初始化视频(同步方法)  
    }   
    else {  //Bulk方式  
        /* Bulk endpoint, proceed to URB initialization. */  
        ep = uvc_find_endpoint(&intf->altsetting[0],stream->header.bEndpointAddress);  
        if (ep == NULL)  
            return -EIO;  
        ret = uvc_init_video_bulk(stream, ep, gfp_flags);   //uvc初始化视频(bulk方法)  
    }  
    if (ret < 0)  
        return ret;  
    /* Submit the URBs. */  
    for (i = 0; i < UVC_URBS; ++i) {  
        ret = usb_submit_urb(stream->urb[i], gfp_flags); //提交urb  
        if (ret < 0) {  
            uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n", i, ret);  
            uvc_uninit_video(stream, 1);  
            return ret;  
        }  
    }  
  
    return 0;  
}</pre>a.1.3.1 同步方式  
<pre></pre>  
<pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_init_video_isoc(struct uvc_streaming *stream,struct usb_host_endpoint *ep, gfp_t gfp_flags)  
{  
    struct urb *urb;  
    unsigned int npackets, i, j;  
    u16 psize;  
    u32 size;  
    psize = le16_to_cpu(ep->desc.wMaxPacketSize);  
    psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));  
    size = stream->ctrl.dwMaxVideoFrameSize;  
    npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);   //分配urb缓冲区  
    if (npackets == 0)  
        return -ENOMEM;  
    size = npackets * psize;  
    for (i = 0; i < UVC_URBS; ++i) {  
        urb = usb_alloc_urb(npackets, gfp_flags);   //分配urb  
        if (urb == NULL) {  
            uvc_uninit_video(stream, 1);  
            return -ENOMEM;  
        }  
        urb->dev = stream->dev->udev;  //设置urb  
        urb->context = stream;  
        urb->pipe = usb_rcvisocpipe(stream->dev->udev,ep->desc.bEndpointAddress);  
        urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;  
        urb->interval = ep->desc.bInterval;  
        urb->transfer_buffer = stream->urb_buffer[i];  
        urb->transfer_dma = stream->urb_dma[i];  
        urb->complete = uvc_video_complete;  
        urb->number_of_packets = npackets;  
        urb->transfer_buffer_length = size;  
        for (j = 0; j < npackets; ++j) {  
            urb->iso_frame_desc[j].offset = j * psize;  
            urb->iso_frame_desc[j].length = psize;  
        }  
        stream->urb[i] = urb;  
    }  
    return 0;  
}</pre><span style="font-family:Arial,Helvetica,sans-serif">a.1.3.2 Bluk方式</span>  
<pre></pre>  
<pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_init_video_bulk(struct uvc_streaming *stream,struct usb_host_endpoint *ep, gfp_t gfp_flags)  
{  
    struct urb *urb;  
    unsigned int npackets, pipe, i;  
    u16 psize;  
    u32 size;  
    psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;  
    size = stream->ctrl.dwMaxPayloadTransferSize;  
    stream->bulk.max_payload_size = size;  
    npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);   //分配urb缓冲区  
    if (npackets == 0)  
        return -ENOMEM;  
    size = npackets * psize;  
    if (usb_endpoint_dir_in(&ep->desc))  
        pipe = usb_rcvbulkpipe(stream->dev->udev,ep->desc.bEndpointAddress);  
    else  
        pipe = usb_sndbulkpipe(stream->dev->udev,ep->desc.bEndpointAddress);  
  
    if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)  
        size = 0;  
    for (i = 0; i < UVC_URBS; ++i) {  
        urb = usb_alloc_urb(0, gfp_flags);  //分配urb  
        if (urb == NULL) {  
            uvc_uninit_video(stream, 1);  
            return -ENOMEM;  
        }  
        usb_fill_bulk_urb(urb, stream->dev->udev, pipe,stream->urb_buffer[i], size, uvc_video_complete,stream);    //设置urb  
        urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  
        urb->transfer_dma = stream->urb_dma[i];  
        stream->urb[i] = urb;  
    }  
    return 0;  
}</pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"></span><pre class="cpp" name="code">a.1.3.1 同步方式和<span style="font-family:Arial, Helvetica, sans-serif;">a.1.3.2 Bluk方式 两种方式初始化uvc视频主要是分配设置urb,然后在</span><span style="font-family:Arial, Helvetica, sans-serif;">uvc_init_video函数中又通过</span><span style="font-family:Arial, Helvetica, sans-serif;">usb_submit_urb提交了urb,</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;">两种方法的urb回调函数都是uvc_video_complete</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;">a.2 urb回调函数uvc_video_complete</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"></span><pre class="cpp" name="code">static void uvc_video_complete(struct urb *urb)  
{  
    struct uvc_streaming *stream = urb->context;  
    struct uvc_video_queue *queue = &stream->queue;  
    struct uvc_buffer *buf = NULL;  
    unsigned long flags;  
    int ret;  
    switch (urb->status) {  
    case 0:  
        break;  
    default:  
        uvc_printk(KERN_WARNING, "Non-zero status (%d) in video completion handler.\n", urb->status);  
    case -ENOENT:       /* usb_kill_urb() called. */  
        if (stream->frozen)  
            return;  
    case -ECONNRESET:   /* usb_unlink_urb() called. */  
    case -ESHUTDOWN:    /* The endpoint is being disabled. */  
        uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);  
        return;  
    }  
    spin_lock_irqsave(&queue->irqlock, flags);  
    if (!list_empty(&queue->irqqueue))  
        buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,queue);  
    spin_unlock_irqrestore(&queue->irqlock, flags);  
    stream->decode(urb, stream, buf);    //调用uvc视频流的decode方法  
    if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {   //再次提交urb  
        uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",ret);  
    }  
}</pre>对于同步和bilk方式的decode方法分别是  
<pre></pre>  
<pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"><span style="WHITE-SPACE: pre">    </span>stream->decode = uvc_video_decode_isoc  
<span style="WHITE-SPACE: pre"> </span>stream->decode = uvc_video_encode_bulk;  
这个在前面uvc_video_init函数中设置了</span></pre>  
<pre></pre>  
ok后面就开始解码了  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
<pre></pre>  
</pre></pre></pre></pre></pre></pre></pre>  
 
 
;