vivi.c框架
- 内核文档: V4L2-framework.txt
- UVC:usb video controll
- UVC驱动框架:
- system call: open read write
- -------------------------------------------------
- 核心层:
- source: v4l2_dev.c
- struct: v4l2_device
- operation: .分配 file_operations (v4l2_file_operations)
- .设置 file_operations (v4l2_file_operations)
- .注册 .cdev_alloc
- .cdev->ops = v4l2_file_operations
- .cdev_add
- ----------------------------------------------------
- 硬件层:
- source: uvc_driver.c
- struct: video_device
- operation: .初始化 v4l2_driver_register
- .分配 video_device_alloc
- .设置 video_device
- .注册 video_register_device
- ----------------------------------------------------
- 如何写一个v4l2驱动:
- .分配,设置,注册 v4l2_device
- .分配 video_device
- .设置
- . 将自己的video_device与v4l2核心层连接起来
- video_device->v4l2_dev = v4l2_device
- . 设置自己的video_device的fops操作函数
- video_device->fops = xxx_fops
- video_device->ioctl_ops = xxx_ioctl
- . 设置自己的video_device的v4l2_ctrl_handler,并提交给核心层
- .v4l2_ctrl_handler_init
- .v4l2_ctrl_new_std
- v4l2_ctrl_new_custom
- .v4l2_device->ctr_handler = v4l2_ctrl_handler
- vivi.c 驱动框架分析:
- ---------------------------------------------------------------
- 核心层:
- source: v4l2_dev.c
- struct: v4l2_device
- v4l2_ops(file_operations)
- ----------------------------------------------------------------
- 设备层:
- source: uvc_driver.c
- struct: video_device
- -----------------------------------------------------------------
- 虚拟设备层:
- source: vivi.c
- struct: vivi_dev
- vivi_ops(v4l2_file_operations)
- vivi_ioctl_ops(v4l2_ioctl_ops)
- ----------------------------------------------------------------
- 应用程序开启摄像头,驱动ioctl被调用流程:
- //可以省略的ioctl
- . ioctl(VIDIOC_G_FMT)
- . for() ioctl(VIDIOC_ENUM_FMT)
- . ioctl(VIDIOC_QUERYCAP) // 列举性能
- . ioctl(VIDIOC_G_INPUT) // 获得当前使用输入源
- . ioctl(VIDIOC_ENUMINPUT) // 列举输入源
- . ioctl(VIDIOC_QUERYCTRL) // 查询属性,比如亮度、对比度
- . ioctl(VIDIOC_QUERYCAP)
- . ioctl(VIDIOC_ENUMINPUT)
- // 打开设备节点
- . fd = open
- . ioctl(fd,VIDIOC_QUERYCAP)
- // 获得设备容量
- . for() ioctl(VIDIOC_ENUMINPUT) // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
- . for() ioctl(VIDIOC_ENUMSTD) // 列举标准(制式), 不是必需的
- . for() ioctl(VIDIOC_ENUM_FMT) // 列举格式
- . ioctl(VIDIOC_G_PARM)
- . for() ioctl(VIDIOC_QUERYCTRL) // 查询属性(比如说亮度值最小值、最大值、默认值)
- // 读取属性
- . ioctl(VIDIOC_G_STD) // 获得当前使用的标准(制式), 不是必需的
- . ioctl(VIDIOC_G_INPUT)
- . ioctl(VIDIOC_G_CTRL ) // 获得当前属性, 比如亮度是多少
- . ioctl(VIDIOC_TRY_FMT) // 试试能否支持某种格式
- . ioctl(VIDIOC_S_FMT) // 设置摄像头使用某种格式
- // 启动摄像头 streamon
- . ioctl(VIDIOC_REQBUFS ) // 请求系统分配缓冲区
- . for() ioctl(VIDIOC_QUERYBUF) // 查询所分配的缓冲区
- mmap()
- . for() ioctl(VIDIOC_QBUF) // 把缓冲区放入队列
- . ioctl(VIDIOC_STREAMON) // 启动摄像头
- // 设置属性
- . for () ioctl(VIDIOC_S_CTRL) // 设置属性
- ioctl(VIDIOC_S_INPUT ) // 设置输入源
- ioctl(VIDIOC_S_STD) // 设置标准(制式), 不是必需的
- // 不断地读取缓冲区数据 v4l2_nextframe > v4l2_waiton
- . v4l2_queue_all
- v4l2_waiton
- for ()
- {
- select(, [], NULL, NULL, {, }) = (in [], left {, })
- ioctl(, VIDIOC_DQBUF // de-queue, 把缓冲区从队列中取出
- // 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据
- ioctl(, VIDIOC_QBUF // 把缓冲区放入队列
- }
- 应用程序读取缓冲区数据流程:
- .请求分配缓冲区 ==>vidioc_reqbufs
- .查询映射缓冲区 ==>vidioc_querybuf (mmap)
- .把缓冲区加入队列 ==>vidioc_qbuf
- .启动摄像头 ==>vidioc_streamon
- .select查村是否有数据 ==>select
- .队列取出缓冲区数据 ==>vidioc_dqbu
- 摄像头数据的读取过程:
- . 请求分配缓冲区:
- ioctl(VIDIOC_REQBUFS) // 请求系统分配缓冲区
- videobuf_reqbufs(buf_queue, v4l2_requestbuffers) // buf_queue在open函数用videobuf_queue_vmalloc_init初始化
- // 注意:这个ioctl只是分配缓冲区的头部信息,真正的缓存还没有分配呢
- . 查询映射缓冲区:
- ioctl(VIDIOC_QUERYBUF) // 查询所分配的缓冲区
- videobuf_querybuf // 获得缓冲区的数据格式、大小、每一行长度、高度
- mmap(参数里有"大小") // 在这里才分配缓存
- v4l2_mmap
- vivi_mmap
- videobuf_mmap_mapper
- videobuf-vmalloc.c里的__videobuf_mmap_mapper
- mem->vmalloc = vmalloc_user(pages); // 在这里才给缓冲区分配空间
- . 把缓冲区放入队列:
- ioctl(VIDIOC_QBUF) // 把缓冲区放入队列
- videobuf_qbuf
- q->ops->buf_prepare(q, buf, field); // 调用驱动程序提供的函数做些预处理
- list_add_tail(&buf->stream, &q->stream);// 把缓冲区放入队列的尾部
- q->ops->buf_queue(q, buf); // 调用驱动程序提供的"入队列函数"
- . 启动摄像头
- ioctl(VIDIOC_STREAMON)
- videobuf_streamon
- q->streaming = ;
- . 用select查询是否有数据
- // 驱动程序里必定有: 产生数据、唤醒进程
- v4l2_poll
- vdev->fops->poll
- vivi_poll
- videobuf_poll_stream
- // 从队列的头部获得缓冲区
- buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
- // 如果没有数据则休眠
- poll_wait(file, &buf->done, wait);
- 谁来产生数据、谁来唤醒它?
- 内核线程vivi_thread每30MS执行一次,它调用
- vivi_thread_tick
- vivi_fillbuff(fh, buf); // 构造数据
- wake_up(&buf->vb.done); // 唤醒进程
- . 有数据后从队列里取出缓冲区
- // 有那么多缓冲区,APP如何知道哪一个缓冲区有数据?调用VIDIOC_DQBUF
- ioctl(VIDIOC_DQBUF)
- vidioc_dqbuf
- // 在队列里获得有数据的缓冲区
- retval = stream_next_buffer(buf_queue, &buf, nonblocking);
- // 把它从队列中删掉
- list_del(&buf->stream);
- // 把这个缓冲区的状态返回给APP
- videobuf_status(buf_queue, b, buf, buf_queue->type);
- . 应用程序根据VIDIOC_DQBUF所得到缓冲区状态,知道是哪一个缓冲区有数据
- 就去读对应的地址(该地址来自前面的mmap)
- 大概流程:
- . VIDIOC_REQBUFS:
- 设置缓冲区头部 buf_a_head ---> buf_b_head
- . VIDIOC_QUERYBUF:
- mmap分配空间:buf_a_head+buffer---> buf_b_head+buffer
- . VIDIOC_QBUF:
- 把缓冲区放入队列:queue_head: buf_a_head+buffer ---> queue_last:buf_b_head+buffer
- . streaming_on:
- . slect:
- 查询队列头部的缓冲区
- .VIDIOC_DQBUF:
- 返回队列头部的buf_a_head+buffer,从队列中删除头部buf_a_head+buffer,
- 此时buf_b_head+buffer位队列头部,处理完把buf_a_head+buffer添加到队列尾部
- 再进行第三部VIDIOC_QBUF
vivi.c源码:
- #include <linux/module.h>
- #include <linux/delay.h>
- #include <linux/errno.h>
- #include <linux/fs.h>
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/ioport.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/pci.h>
- #include <linux/random.h>
- #include <linux/version.h>
- #include <linux/mutex.h>
- #include <linux/videodev2.h>
- #include <linux/dma-mapping.h>
- #include <linux/interrupt.h>
- #include <linux/kthread.h>
- #include <linux/highmem.h>
- #include <linux/freezer.h>
- #include <media/videobuf-vmalloc.h>
- #include <media/v4l2-device.h>
- #include <media/v4l2-ioctl.h>
- /* 定义一个video_device */
- static struct video_device *my_video_device;
- /* 摄像头数据格式结构体 */
- static struct v4l2_format my_v4l2_format;
- /* videobuf-core.c维护的缓存队列 */
- static struct videobuf_queue my_videobuf_queue;
- /* 队列自旋锁 */
- static spinlock_t my_queue_slock;
- /* 数据上报定时器*/
- static struct timer_list my_timer;
- /* 本地维护的缓存队列*/
- static struct list_head my_local_queue;
- /*包含摄像头数据构造函数*/
- #include"my_fill_buf.c"
- /**************************************************videobuf_queue_ops start********************************************************/
- static int my_buf_setup(struct videobuf_queue *q,unsigned int *count, unsigned int *size)
- {
- /* 重新计算buff size并调整,不要浪费空间 */
- *size = my_v4l2_format.fmt.pix.sizeimage;
- /* 一个队列最多支持32缓存块 */
- if(*count == ) *count=;
- return ;
- }
- static int my_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field)
- {
- /*设置video_buf的大小,宽度*/
- vb->size = my_v4l2_format.fmt.pix.sizeimage;
- vb->bytesperline = my_v4l2_format.fmt.pix.bytesperline;
- vb->width = my_v4l2_format.fmt.pix.width;
- vb->height = my_v4l2_format.fmt.pix.height;
- vb->field = field;
- /*摄像头数据初始化*/
- my_precalculate_bars();
- /*设置buf状态*/
- vb->state = VIDEOBUF_PREPARED;
- return ;
- }
- static void my_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
- {
- /* 应用程序查询缓冲区时把本地队列的buf 放入 videobuf-core.c维护的队列 */
- list_add_tail(&vb->queue, &my_local_queue);
- /* 设置buf状态 */
- vb->state = VIDEOBUF_QUEUED;
- }
- static void my_buf_release(struct videobuf_queue *q,struct videobuf_buffer *vb)
- {
- /*释放buf*/
- videobuf_vmalloc_free(vb);
- /*设置buf状态*/
- vb->state = VIDEOBUF_NEEDS_INIT;
- }
- /*
- *
- ops->buf_setup - calculates the size of the video buffers and avoid they to waste more than some maximum limit of RAM;
- ops->buf_prepare - fills the video buffer structs and calls videobuf_iolock() to alloc and prepare mmaped memory;
- ops->buf_queue - advices the driver that another buffer were requested (by read() or by QBUF);
- ops->buf_release - frees any buffer that were allocated.
- *
- */
- static struct videobuf_queue_ops my_videobuf_queue_ops =
- {
- .buf_setup = my_buf_setup, /* app调用ioctl(REQBUF)时,此函数被调用 */
- .buf_prepare = my_buf_prepare, /* app调用ioctl(QUERYBUF)时,此函数被调用 */
- .buf_queue = my_buf_queue, /* app调用ioctl(QBUF)时,此函数被调用 */
- .buf_release = my_buf_release, /* app调用ioctl(DQBUF)时,此函数被调用 */
- };
- /**************************************************videobuf_queue_ops end********************************************************/
- /***************************************************fops start************************************************************/
- static int my_vivi_open(struct file *file)
- {
- /* 初始化videobuf_queue结构体
- * videobuf_queue->bufs = videobuf_buffer
- * videobuf_queue->ops = videobuf_queue_ops
- * videobuf_queue->irqlock = my_queue_slock
- */
- videobuf_queue_vmalloc_init(&my_videobuf_queue, // 缓冲区队列
- &my_videobuf_queue_ops, // 缓冲区队列的操作函数
- NULL, &my_queue_slock, // 缓冲区操作函数要用的自旋锁
- V4L2_BUF_TYPE_VIDEO_CAPTURE, // 单个缓冲区的类型
- V4L2_FIELD_INTERLACED, // 奇偶帧
- sizeof(struct videobuf_buffer), // 缓冲头部大小
- NULL); // 私有数据
- /*设置超调时间*/
- my_timer.expires = jiffies + ;
- /*将定时器加入内核链表*/
- add_timer(&my_timer);
- return ;
- }
- static unsigned int my_vivi_poll(struct file *file, struct poll_table_struct *wait)
- {
/* 调用内核的poll函数查询数据,如果没有数据,休眠在videobuf->done上
* poll_wait(file, &buf->done, wait);
*
*/
return videobuf_poll_stream(file,&my_videobuf_queue , wait);
- }
- static int my_vivi_mmap(struct file *file, struct vm_area_struct *vma)
- {
- /*映射缓冲区*/
- return videobuf_mmap_mapper(&my_videobuf_queue , vma);
- }
- static int my_vivi_release(struct file *file)
- {
- /*内核链表删除定时器*/
- del_timer(&my_timer);
- /*停止队列*/
- videobuf_stop(&my_videobuf_queue);
- /*取消缓存区映射*/
- videobuf_mmap_free(&my_videobuf_queue );
- return ;
- }
- /***************************************************fops end**************************************************************/
- /**************************************************ioctl start************************************************************/
- /*判断是否是摄像头设备*/
- static int my_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
- {
- /* 驱动名称 */
- strcpy(cap->driver,"zsy_vivi");
- strcpy(cap->card,"zsy_vivi");
- /* 摄像头版本号 */
- cap->version = 0x0011;
- /* 视频捕捉设备,streaming表明是app通过ioctl 来进行读写数据*/
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- return ;
- }
- /* 枚举当前设备支持的格式 */
- static int my_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
- {
- /* 支持几种格式 */
- if(f->index >= ) return -EINVAL;
- /* 设置当前格式为YUYV*/
- strcpy(f->description,"4:2:2,packed,YUYV");
- f->pixelformat = V4L2_PIX_FMT_YUYV;
- return ;
- }
- static int my_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
- {
- /* 返回当前的格式 */
- memcpy(f,&my_v4l2_format,sizeof(my_v4l2_format));
- return ;
- }
- static int my_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
- {
- unsigned int maxw,maxh;
- enum v4l2_field field;
- /* 测试是否支持这个格式 */
- if(f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) return -EINVAL;
- field = f->fmt.pix.field;
- if (field == V4L2_FIELD_ANY) field = V4L2_FIELD_INTERLACED;
- else if (V4L2_FIELD_INTERLACED != field) return -EINVAL;
- /* 设置最大宽度和高度 */
- maxw = ;
- maxh = ;
- v4l_bound_align_image(&f->fmt.pix.width, , maxw, , &f->fmt.pix.height, , maxh, , );
- /* 调整宽度 */
- f->fmt.pix.bytesperline =(f->fmt.pix.width * ) >> ;
- /* 调整图片大小 */
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- return ;
- }
- static int my_s_fmt_vid_cap(struct file *file, void *fh,struct v4l2_format *f)
- {
- int ret = my_try_fmt_vid_cap(file,NULL,f);
- if(ret < ) return ret;
- /* 设置上个函数测试好了的格式 */
- memcpy(&my_v4l2_format,f,sizeof(my_v4l2_format));
- return ret;
- }
- static int my_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
- {
- /* 请求一个video缓存 */
- return (videobuf_reqbufs(&my_videobuf_queue, b));
- }
- static int my_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
- {
- /* 查询缓存 */
- return (videobuf_querybuf(&my_videobuf_queue,b));
- }
- static int my_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
- {
- /*把缓存挂到队列*/
- return (videobuf_qbuf(&my_videobuf_queue,b));
- }
- static int my_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
- {
- /* 把缓存拿出队列 */
- return (videobuf_dqbuf(&my_videobuf_queue, b, file->f_flags & O_NONBLOCK));
- }
- static int my_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
- {
- /* 打开摄像头 */
- return videobuf_streamon(&my_videobuf_queue);
- }
- static int my_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
- {
- /* 关闭摄像头*/
- videobuf_streamoff(&my_videobuf_queue);
- return ;
- }
- /*************************************************ioctl end*************************************************************/
- /* 定义一个v4l2_file_operations */
- static struct v4l2_file_operations my_fops =
- {
- .owner = THIS_MODULE,
- .open = my_vivi_open,
- .release = my_vivi_release,
- .mmap = my_vivi_mmap,
- .poll = my_vivi_poll,
- .ioctl = video_ioctl2,
- };
- /* 定义一个v4l2_ioctl_ops */
- static struct v4l2_ioctl_ops my_ioctl_ops =
- {
- /* 摄像头容量 */
- .vidioc_querycap = my_querycap,
- /* 数据格式 */
- .vidioc_enum_fmt_vid_cap = my_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = my_g_fmt_vid_cap ,
- .vidioc_try_fmt_vid_cap = my_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = my_s_fmt_vid_cap,
- /* 缓冲区 */
- .vidioc_reqbufs = my_reqbufs,
- .vidioc_querybuf = my_querybuf,
- .vidioc_qbuf = my_qbuf,
- .vidioc_dqbuf = my_dqbuf,
- /* 启动/停止摄像头 */
- .vidioc_streamon = my_streamon,
- .vidioc_streamoff = my_streamoff,
- };
- /*定时器超时函数,填充数据*/
- void my_timer_fun(unsigned long data )
- {
- struct videobuf_buffer *vb;
- void *vbuf;
- struct timeval ts;
- /* 1.1如果队列本地队列没有数据 ,结束 */
- if(list_empty(&my_local_queue))
- {
- mod_timer(&my_timer, jiffies + HZ/);
- return ;
- }
- /* 1.2从本地队列里面取出第一个buf*/
- vb = list_entry(my_local_queue.next, struct videobuf_buffer, queue);
- /* 1.3如果应用程序没有等待数据 ,结束 */
- if(!waitqueue_active(&vb->done))
- {
- mod_timer(&my_timer, jiffies + HZ/);
- return ;
- }
- /* 2.填充假数据*/
- vbuf = videobuf_to_vmalloc(vb);
- my_fill_buff(vb);
- vb->field_count++;
- do_gettimeofday(&ts);
- vb->ts = ts;
- vb->state = VIDEOBUF_DONE;
- /* 3.把填充了数据的buf从队列里面删除*/
- list_del(&vb->queue);
/* 4.唤醒videobuf_buffer->done进程,
* 当进程使用poll机制查询缓冲区时暂时没有数据时
* 进程休眠在 videobuf_buffer->done上面
*/
wake_up(&vb->done);
- /* 5.修改超时时间,30毫秒产生一帧数据 */
- mod_timer(&my_timer, jiffies + HZ/);
- }
- /* 释放函数 */
- void my_video_device_release(struct video_device *vdev)
- {}
- int my_vivi_init(void)
- {
- /* 1.分配一个video_device */
- my_video_device = video_device_alloc();
- /* 2.设置video_device */
- my_video_device->release = my_video_device_release;
- my_video_device->fops = &my_fops;
- my_video_device->ioctl_ops = &my_ioctl_ops;
- /* 3.注册video_device*/
- video_register_device(my_video_device,VFL_TYPE_GRABBER, -);
- /* 初始化定时器*/
- init_timer(&my_timer);
- my_timer.function = my_timer_fun;
- /* 初始化自旋锁 */
- spin_lock_init(&my_queue_slock);
- /*初始化本地队列*/
- INIT_LIST_HEAD(&my_local_queue);
- return ;
- }
- void my_vivi_exit(void)
- {
- video_unregister_device(my_video_device);
- video_device_release(my_video_device);
- }
- module_init(my_vivi_init);
- module_exit(my_vivi_exit);
- MODULE_LICENSE("GPL");
fill_buffer.c源码:
- /* Bars and Colors should match positions */
- enum colors {
- WHITE,
- AMBAR,
- CYAN,
- GREEN,
- MAGENTA,
- RED,
- BLUE,
- BLACK,
- };
- /* R G B */
- #define COLOR_WHITE {204, 204, 204}
- #define COLOR_AMBAR {208, 208, 0}
- #define COLOR_CIAN { 0, 206, 206}
- #define COLOR_GREEN { 0, 239, 0}
- #define COLOR_MAGENTA {239, 0, 239}
- #define COLOR_RED {205, 0, 0}
- #define COLOR_BLUE { 0, 0, 255}
- #define COLOR_BLACK { 0, 0, 0}
- struct bar_std {
- u8 bar[][];
- };
- /* Maximum number of bars are 10 - otherwise, the input print code
- should be modified */
- static struct bar_std bars[] = {
- { /* Standard ITU-R color bar sequence */
- {
- COLOR_WHITE,
- COLOR_AMBAR,
- COLOR_CIAN,
- COLOR_GREEN,
- COLOR_MAGENTA,
- COLOR_RED,
- COLOR_BLUE,
- COLOR_BLACK,
- }
- }, {
- {
- COLOR_WHITE,
- COLOR_AMBAR,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_AMBAR,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_AMBAR,
- }
- }, {
- {
- COLOR_WHITE,
- COLOR_CIAN,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_CIAN,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_CIAN,
- }
- }, {
- {
- COLOR_WHITE,
- COLOR_GREEN,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_GREEN,
- COLOR_BLACK,
- COLOR_WHITE,
- COLOR_GREEN,
- }
- },
- };
- #define NUM_INPUTS ARRAY_SIZE(bars)
- #define TO_Y(r, g, b) \
- ((( * r + * g + * b + ) >> ) + )
- /* RGB to V(Cr) Color transform */
- #define TO_V(r, g, b) \
- ((( * r - * g - * b + ) >> ) + )
- /* RGB to U(Cb) Color transform */
- #define TO_U(r, g, b) \
- (((- * r - * g + * b + ) >> ) + )
- static unsigned char myvivi_cur_bars[][];
- /* precalculate color bar values to speed up rendering */
- static void my_precalculate_bars(int input)
- {
- unsigned char r, g, b;
- int k, is_yuv;
- for (k = ; k < ; k++) {
- r = bars[input].bar[k][];
- g = bars[input].bar[k][];
- b = bars[input].bar[k][];
- is_yuv = ;
- switch (my_v4l2_format.fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- is_yuv = ;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- r >>= ;
- g >>= ;
- b >>= ;
- break;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB555X:
- r >>= ;
- g >>= ;
- b >>= ;
- break;
- }
- if (is_yuv) {
- myvivi_cur_bars[k][] = TO_Y(r, g, b); /* Luma */
- myvivi_cur_bars[k][] = TO_U(r, g, b); /* Cb */
- myvivi_cur_bars[k][] = TO_V(r, g, b); /* Cr */
- } else {
- myvivi_cur_bars[k][] = r;
- myvivi_cur_bars[k][] = g;
- myvivi_cur_bars[k][] = b;
- }
- }
- }
- static void myvivi_gen_twopix(unsigned char *buf, int colorpos)
- {
- unsigned char r_y, g_u, b_v;
- unsigned char *p;
- int color;
- r_y = myvivi_cur_bars[colorpos][]; /* R or precalculated Y */
- g_u = myvivi_cur_bars[colorpos][]; /* G or precalculated U */
- b_v = myvivi_cur_bars[colorpos][]; /* B or precalculated V */
- for (color = ; color < ; color++) {
- p = buf + color;
- switch (my_v4l2_format.fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUYV:
- switch (color) {
- case :
- case :
- *p = r_y;
- break;
- case :
- *p = g_u;
- break;
- case :
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_UYVY:
- switch (color) {
- case :
- case :
- *p = r_y;
- break;
- case :
- *p = g_u;
- break;
- case :
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565:
- switch (color) {
- case :
- case :
- *p = (g_u << ) | b_v;
- break;
- case :
- case :
- *p = (r_y << ) | (g_u >> );
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565X:
- switch (color) {
- case :
- case :
- *p = (r_y << ) | (g_u >> );
- break;
- case :
- case :
- *p = (g_u << ) | b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555:
- switch (color) {
- case :
- case :
- *p = (g_u << ) | b_v;
- break;
- case :
- case :
- *p = (r_y << ) | (g_u >> );
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555X:
- switch (color) {
- case :
- case :
- *p = (r_y << ) | (g_u >> );
- break;
- case :
- case :
- *p = (g_u << ) | b_v;
- break;
- }
- break;
- }
- }
- }
- static void myvivi_gen_line(char *basep, int inipos, int wmax,
- int hmax, int line, int count)
- {
- int w;
- int pos = inipos;
- /* We will just duplicate the second pixel at the packet */
- wmax /= ;
- /* Generate a standard color bar pattern */
- for (w = ; w < wmax; w++) {
- int colorpos = ((w + count) * /(wmax + )) % ;
- myvivi_gen_twopix(basep + pos, colorpos);
- pos += ; /* only 16 bpp supported for now */
- }
- return;
- }
- static void my_fill_buff(struct videobuf_buffer *vb)
- {
- int h , pos = ;
- int hmax = vb->height;
- int wmax = vb->width;
- char *tmpbuf;
- void *vbuf = videobuf_to_vmalloc(vb);
- static int mv_count = ;
- if (!vbuf)
- return;
- tmpbuf = kmalloc(wmax * , GFP_ATOMIC);
- if (!tmpbuf)
- return;
- for (h = ; h < hmax; h++) {
- myvivi_gen_line(tmpbuf, , wmax, hmax, h, mv_count);
- memcpy(vbuf + pos, tmpbuf, wmax * );
- pos += wmax*;
- }
- mv_count++;
- kfree(tmpbuf);
- }
vivi.c框架的更多相关文章
- V4L2学习(五)VIVI虚拟摄像头驱动
概述 前面简单分析了内核中虚拟摄像头驱动 vivi 的框架与实现,本文参考 vivi 来写一个虚拟摄像头驱动,查询.设置视频格式相对简单,难点在于 vb2_buf 的处理过程. 数据采集流程分析 在我 ...
- V4L2应用程序框架-二【转】
本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11371439 V4L2驱动框架 主设备号: 81 次设备号: 0-63 64 ...
- Linux摄像头驱动学习之:(一)V4L2_框架分析
这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux ...
- (七) UVC框架分析
title: UVC框架分析 date: 2019/4/23 19:50:00 toc: true --- UVC框架分析 源码的位置在drivers\media\video\uvc,查看下Makef ...
- v4l2框架
参考:https://www.cnblogs.com/tuotuteng/p/4648387.html http://blog.sina.com.cn/s/blog_c91863e60102w65w. ...
- 2.5 USB摄像头驱动程序框架
学习目标:根据vivi驱动架构和linux-2.6.31/linux-2.6.31.14/drivers/media/video/uvc/Uvc_driver.c驱动源码,分析usb摄像头驱动程序框架 ...
- 2.1 摄像头V4L2驱动框架分析
学习目标:学习V4L2(V4L2:vidio for linux version 2)摄像头驱动框架,分析vivi.c(虚拟视频硬件相关)驱动源码程序,总结V4L2硬件相关的驱动的步骤: 一.V4L ...
- V4L2(二)虚拟摄像头驱动vivi深入分析【转】
转自:http://www.cnblogs.com/tureno/articles/6694463.html 转载于: http://blog.csdn.net/lizuobin2/article/d ...
- V4L2学习(四)VIVI分析
vivi 相对于后面要分析的 usb 摄像头驱动程序,它没有真正的硬件相关层的操作,也就是说抛开了复杂的 usb 层的相关知识,便于理解 V4L2 驱动框架,侧重于驱动和应用的交互. 前面我们提到,V ...
随机推荐
- 我的IntelliJ IDEA 设置
1.关闭代码折叠 2.设置代码格式 3.函数参数提醒
- Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk
查下是不是有在环境变量中定义NDK_PROJECT_PATH 这个变量,如果有,删除试试
- python多线程应用——DB2数据库备份
前言:DB2一个实例下,可以存在多个数据库,之前使用shell备份脚本,但是同一时刻只能备份一个数据库,对于几百G的备份文件,这个速度显然太慢,今天学习了Python多线程,刚好应用一下. 分析:1. ...
- csv.writer写入文件有多余的空行
在用csv.writer写入文件的时候发现中间有多余的空行. 最早打开方式只是‘w’,会出现多余的空行,网上建议使用binary形式‘wb’打开可以解决问题: with open('egg2.csv' ...
- linux php --ini
$ php --ini
- 此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站。若要允许 GET 请求,请将 JsonRequestBehavior 设置为 AllowGet。
1.问题描述 mvc从一个路径获取所有的图片信息,ajax方法如下: function getimages(day) { var year = $("#selYear").val( ...
- (转)The C10K problem翻译
The C10K problem 如今的web服务器需要同时处理一万个以上的客户端了,难道不是吗?毕竟如今的网络是个big place了. 现在的计算机也很强大了,你只需要花大概$1200就可以买一个 ...
- Houdini技术体系 基础管线(三) :UE4 Landscape Component的多选支持 下篇
背景 上篇中,我们介绍了如何修改Houdini Enigne来设置单个Landscape Compnent的Height和Layer的数据,但原生Houdini Engine并不支持多选Compone ...
- swiper4自动轮播切换手动触碰后停止踩坑——属性disableOnInteraction
swiper4轮播设置autoplay自动切换后,即默认设置: <script> var mySwiper = new Swiper('.swiper-container', { auto ...
- 安卓sdk webview获取淘宝个人信息100项,源码。
1.贴出主要代码.这个不是python,python只涉及了服务端对信息提取结果的接受.主体是java + android + js.由于淘宝各模块都是二级子域名,不能只在一个页面完成所有请求,aj ...