LINUX&UVC驱动开启视频流VIDIOC_STREAMON
2024-04-02
125
0
实现不想看那个视频流队列相关的东西,但有时又很好奇。就看了一个一知半解,只是梳理了一下代码流程,至于细节,鬼才去看。
最终,其实想分析的是视频打开,只为只有这里有与UVC规范相关的请求。
使用VIDIOC_STREAMON请求来打开视频流。
// 开始数据流
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
perror("Failed to start streaming");
close(fd);
return EXIT_FAILURE;
}
VIDIOC_STREAMON对应的UVC回调为:
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_streamon = uvc_ioctl_streamon,
}
这里其实调用的是队列相关的streamon,这也是为什么要大概分析一下队列相关的代码。
static int uvc_ioctl_streamon(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
if (!uvc_has_privileges(handle))
return -EBUSY;
mutex_lock(&stream->mutex);
ret = uvc_queue_streamon(&stream->queue, type);
mutex_unlock(&stream->mutex);
return ret;
}
int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_streamon(&queue->queue, type);
mutex_unlock(&queue->mutex);
return ret;
}
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
return vb2_core_streamon(q, type);
}
进入VB2模块的vb2_start_streaming函数,里面需要回调视频流节点注册的回调uvc_queue_qops的start_streaming回调。
关于start_streaming详见:https://www.usbzh.com/article/detail-1350.html
int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
{
int ret;
if (type != q->type) {
dprintk(1, "invalid stream type\n");
return -EINVAL;
}
if (q->streaming) {
dprintk(3, "already streaming\n");
return 0;
}
if (!q->num_buffers) {
dprintk(1, "no buffers have been allocated\n");
return -EINVAL;
}
if (q->num_buffers < q->min_buffers_needed) {
dprintk(1, "need at least %u allocated buffers\n",
q->min_buffers_needed);
return -EINVAL;
}
/*
* Tell driver to start streaming provided sufficient buffers
* are available.
*/
if (q->queued_count >= q->min_buffers_needed) {
ret = v4l_vb2q_enable_media_source(q);
if (ret)
return ret;
ret = vb2_start_streaming(q);
if (ret)
return ret;
}
q->streaming = 1;
dprintk(3, "successful\n");
return 0;
}
`
vb2_start_streaming
static int vb2_start_streaming(struct vb2_queue *q)
{
struct vb2_buffer *vb;
int ret;
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
/* Tell the driver to start streaming */
q->start_streaming_called = 1;
//调用start_streaming
ret = call_qop(q, start_streaming, q,
atomic_read(&q->owned_by_drv_count));
if (!ret)
return 0;
q->start_streaming_called = 0;
dprintk(1, "driver refused to start streaming\n");
/*
* If you see this warning, then the driver isn't cleaning up properly
* after a failed start_streaming(). See the start_streaming()
* documentation in videobuf2-core.h for more information how buffers
* should be returned to vb2 in start_streaming().
*/
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
unsigned i;
/*
* Forcefully reclaim buffers if the driver did not
* correctly return them to vb2.
*/
for (i = 0; i < q->num_buffers; ++i) {
vb = q->bufs[i];
if (vb->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
}
/* Must be zero now */
WARN_ON(atomic_read(&q->owned_by_drv_count));
}
/*
* If done_list is not empty, then start_streaming() didn't call
* vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED) but STATE_ERROR or
* STATE_DONE.
*/
WARN_ON(!list_empty(&q->done_list));
return ret;
}
所以最终落到start_streaming的函数指针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);
int ret;
lockdep_assert_irqs_enabled();
queue->buf_used = 0;
ret = uvc_video_start_streaming(stream);
if (ret == 0)
return 0;
spin_lock_irq(&queue->irqlock);
uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
spin_unlock_irq(&queue->irqlock);
return ret;
}
uvc_video_start_streaming。
int uvc_video_start_streaming(struct uvc_streaming *stream)
{
int ret;
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_video_start_transfer(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;
}
uvc_commit_video调用VS_COMMIT_CONTROLL请求
static int uvc_commit_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe)
{
return uvc_set_video_ctrl(stream, probe, 0);
}
关于uvc_set_video_ctrl详见https://www.usbzh.com/article/detail-1349.html
至于uvc_video_start_transfer则见:https://www.usbzh.com/article/detail-186.html