zoukankan      html  css  js  c++  java
  • vivi.c框架

    内核文档: V4L2-framework.txt
    
    UVC:usb video controll
    UVC驱动框架:
    
    system call: open   read   write
    
    -------------------------------------------------
    
    核心层:
            source: v4l2_dev.c
            struct: v4l2_device 
            operation:  1.分配  file_operations (v4l2_file_operations)
                        2.设置  file_operations (v4l2_file_operations)
                        2.注册  1.cdev_alloc
                                2.cdev->ops = v4l2_file_operations
                                3.cdev_add
    ----------------------------------------------------
    
    硬件层:
            source: uvc_driver.c
            struct: video_device
            operation:  1.初始化 v4l2_driver_register
                        2.分配      video_device_alloc
                        3.设置   video_device
                        4.注册   video_register_device
    
    ----------------------------------------------------
    
    如何写一个v4l2驱动:
        1.分配,设置,注册 v4l2_device
        2.分配 video_device
        3.设置 
                1. 将自己的video_device与v4l2核心层连接起来
                    video_device->v4l2_dev = v4l2_device 
                2. 设置自己的video_device的fops操作函数
                    video_device->fops       = xxx_fops
                    video_device->ioctl_ops = xxx_ioctl 
                3. 设置自己的video_device的v4l2_ctrl_handler,并提交给核心层
                    1.v4l2_ctrl_handler_init
                    2.v4l2_ctrl_new_std 
                      v4l2_ctrl_new_custom
                    3.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
    3.  ioctl(VIDIOC_G_FMT)
    4.  for() ioctl(VIDIOC_ENUM_FMT)
    5.  ioctl(VIDIOC_QUERYCAP)    // 列举性能
    6.  ioctl(VIDIOC_G_INPUT)     // 获得当前使用输入源
    7.  ioctl(VIDIOC_ENUMINPUT)   // 列举输入源
    8.  ioctl(VIDIOC_QUERYCTRL)   // 查询属性,比如亮度、对比度
    9.  ioctl(VIDIOC_QUERYCAP)
    10. ioctl(VIDIOC_ENUMINPUT)
    
    
    // 打开设备节点
    1. fd = open
    2. ioctl(fd,VIDIOC_QUERYCAP) 
      
    // 获得设备容量
    3. for() ioctl(VIDIOC_ENUMINPUT)     // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
    4. for() ioctl(VIDIOC_ENUMSTD)       // 列举标准(制式), 不是必需的
    5. for() ioctl(VIDIOC_ENUM_FMT)      // 列举格式
    6. ioctl(VIDIOC_G_PARM)
    7. for() ioctl(VIDIOC_QUERYCTRL)     // 查询属性(比如说亮度值最小值、最大值、默认值)
    
    // 读取属性   
    8.  ioctl(VIDIOC_G_STD)             // 获得当前使用的标准(制式), 不是必需的
    9.  ioctl(VIDIOC_G_INPUT) 
    10. ioctl(VIDIOC_G_CTRL )           // 获得当前属性, 比如亮度是多少
    11. ioctl(VIDIOC_TRY_FMT)           // 试试能否支持某种格式
    12. ioctl(VIDIOC_S_FMT)             // 设置摄像头使用某种格式
    
    // 启动摄像头 streamon
    13. ioctl(VIDIOC_REQBUFS )          // 请求系统分配缓冲区
    14. for() ioctl(VIDIOC_QUERYBUF)    // 查询所分配的缓冲区   
              mmap()       
    15. for() ioctl(VIDIOC_QBUF)        // 把缓冲区放入队列        
    16. ioctl(VIDIOC_STREAMON)         // 启动摄像头
    
    // 设置属性
    17. for ()  ioctl(VIDIOC_S_CTRL)           // 设置属性
                ioctl(VIDIOC_S_INPUT )         // 设置输入源
                ioctl(VIDIOC_S_STD)            // 设置标准(制式), 不是必需的
    
    // 不断地读取缓冲区数据 v4l2_nextframe > v4l2_waiton    
    18. v4l2_queue_all
        v4l2_waiton    
            for ()
            {
                select(5, [4], NULL, NULL, {5, 0})      = 1 (in [4], left {4, 985979})
                ioctl(4, VIDIOC_DQBUF           // de-queue, 把缓冲区从队列中取出
                                                // 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据        
                ioctl(4, VIDIOC_QBUF            // 把缓冲区放入队列
            }
    
            
    应用程序读取缓冲区数据流程:
    
    1.请求分配缓冲区       ==>vidioc_reqbufs  
    2.查询映射缓冲区       ==>vidioc_querybuf (mmap) 
    3.把缓冲区加入队列     ==>vidioc_qbuf
    4.启动摄像头           ==>vidioc_streamon
    5.select查村是否有数据 ==>select
    6.队列取出缓冲区数据   ==>vidioc_dqbu
    
    
    摄像头数据的读取过程:
    
    1. 请求分配缓冲区: 
    ioctl(VIDIOC_REQBUFS)         // 请求系统分配缓冲区
            videobuf_reqbufs(buf_queue, v4l2_requestbuffers) // buf_queue在open函数用videobuf_queue_vmalloc_init初始化
                                 // 注意:这个ioctl只是分配缓冲区的头部信息,真正的缓存还没有分配呢
    
    2. 查询映射缓冲区:
    ioctl(VIDIOC_QUERYBUF)          // 查询所分配的缓冲区
            videobuf_querybuf       // 获得缓冲区的数据格式、大小、每一行长度、高度            
    mmap(参数里有"大小")            // 在这里才分配缓存
            v4l2_mmap
                vivi_mmap
                    videobuf_mmap_mapper
                        videobuf-vmalloc.c里的__videobuf_mmap_mapper
                                mem->vmalloc = vmalloc_user(pages);   // 在这里才给缓冲区分配空间
    
    3. 把缓冲区放入队列:
    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);              // 调用驱动程序提供的"入队列函数"
            
    
    4. 启动摄像头
    ioctl(VIDIOC_STREAMON) 
        videobuf_streamon
            q->streaming = 1;
            
    
    5. 用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);  // 唤醒进程
              
    6. 有数据后从队列里取出缓冲区
    // 有那么多缓冲区,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);
            
    7. 应用程序根据VIDIOC_DQBUF所得到缓冲区状态,知道是哪一个缓冲区有数据
       就去读对应的地址(该地址来自前面的mmap)
    
    
    大概流程:
    1. VIDIOC_REQBUFS:
                    设置缓冲区头部 buf_a_head ---> buf_b_head
    
    2. VIDIOC_QUERYBUF:
                    mmap分配空间:buf_a_head+buffer---> buf_b_head+buffer
                                    
    3. VIDIOC_QBUF: 
                    把缓冲区放入队列:queue_head:  buf_a_head+buffer   ---> queue_last:buf_b_head+buffer              
                                        
    4. streaming_on:
    
    5. slect:
                    查询队列头部的缓冲区
    
    6.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 == 0) *count=32;
       return 0;
    }
    
    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(0);
    
       /*设置buf状态*/
       vb->state = VIDEOBUF_PREPARED;
       return 0;
    }
    
    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 + 1;
    
        /*将定时器加入内核链表*/
        add_timer(&my_timer);
        return 0;
    }
    
    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 0;
    }
    /***************************************************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 0;
    }
    
    /* 枚举当前设备支持的格式 */
    static int my_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
    {
       /* 支持几种格式 */
       if(f->index >= 1) return -EINVAL;
    
       /* 设置当前格式为YUYV*/
       strcpy(f->description,"4:2:2,packed,YUYV");
       f->pixelformat = V4L2_PIX_FMT_YUYV;
       return 0;
    }
    
    
    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 0;
    }
    
    
    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  = 1024;
        maxh  = 768;
    
        v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0);
        /* 调整宽度 */
        f->fmt.pix.bytesperline =(f->fmt.pix.width * 16) >> 3;
        /* 调整图片大小 */
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
    
        return 0;
    }
    
    
    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 < 0) 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 0;
    }
    /*************************************************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/30);
             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/30);
             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/30);  
        
    }
    
    /* 释放函数 */
    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, -1);
    
       /* 初始化定时器*/
       init_timer(&my_timer);
       my_timer.function = my_timer_fun;
    
       /* 初始化自旋锁 */
       spin_lock_init(&my_queue_slock);
    
       /*初始化本地队列*/
       INIT_LIST_HEAD(&my_local_queue);
     
       return 0;
    }
    
    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[8][3];
    };
    
    /* 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) 
        (((16829 * r + 33039 * g + 6416 * b  + 32768) >> 16) + 16)
    /* RGB to  V(Cr) Color transform */
    #define TO_V(r, g, b) 
        (((28784 * r - 24103 * g - 4681 * b  + 32768) >> 16) + 128)
    /* RGB to  U(Cb) Color transform */
    #define TO_U(r, g, b) 
        (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
    
    
    
    static unsigned char myvivi_cur_bars[8][3];
    
    
    
    /* 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 = 0; k < 8; k++) {
            r = bars[input].bar[k][0];
            g = bars[input].bar[k][1];
            b = bars[input].bar[k][2];
            is_yuv = 0;
    
            switch (my_v4l2_format.fmt.pix.pixelformat) {
            case V4L2_PIX_FMT_YUYV:
            case V4L2_PIX_FMT_UYVY:
                is_yuv = 1;
                break;
            case V4L2_PIX_FMT_RGB565:
            case V4L2_PIX_FMT_RGB565X:
                r >>= 3;
                g >>= 2;
                b >>= 3;
                break;
            case V4L2_PIX_FMT_RGB555:
            case V4L2_PIX_FMT_RGB555X:
                r >>= 3;
                g >>= 3;
                b >>= 3;
                break;
            }
    
            if (is_yuv) {
                myvivi_cur_bars[k][0] = TO_Y(r, g, b);    /* Luma */
                myvivi_cur_bars[k][1] = TO_U(r, g, b);    /* Cb */
                myvivi_cur_bars[k][2] = TO_V(r, g, b);    /* Cr */
            } else {
                myvivi_cur_bars[k][0] = r;
                myvivi_cur_bars[k][1] = g;
                myvivi_cur_bars[k][2] = 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][0]; /* R or precalculated Y */
        g_u = myvivi_cur_bars[colorpos][1]; /* G or precalculated U */
        b_v = myvivi_cur_bars[colorpos][2]; /* B or precalculated V */
    
        for (color = 0; color < 4; color++) {
            p = buf + color;
    
            switch (my_v4l2_format.fmt.pix.pixelformat) {
            case V4L2_PIX_FMT_YUYV:
                switch (color) {
                case 0:
                case 2:
                    *p = r_y;
                    break;
                case 1:
                    *p = g_u;
                    break;
                case 3:
                    *p = b_v;
                    break;
                }
                break;
            case V4L2_PIX_FMT_UYVY:
                switch (color) {
                case 1:
                case 3:
                    *p = r_y;
                    break;
                case 0:
                    *p = g_u;
                    break;
                case 2:
                    *p = b_v;
                    break;
                }
                break;
            case V4L2_PIX_FMT_RGB565:
                switch (color) {
                case 0:
                case 2:
                    *p = (g_u << 5) | b_v;
                    break;
                case 1:
                case 3:
                    *p = (r_y << 3) | (g_u >> 3);
                    break;
                }
                break;
            case V4L2_PIX_FMT_RGB565X:
                switch (color) {
                case 0:
                case 2:
                    *p = (r_y << 3) | (g_u >> 3);
                    break;
                case 1:
                case 3:
                    *p = (g_u << 5) | b_v;
                    break;
                }
                break;
            case V4L2_PIX_FMT_RGB555:
                switch (color) {
                case 0:
                case 2:
                    *p = (g_u << 5) | b_v;
                    break;
                case 1:
                case 3:
                    *p = (r_y << 2) | (g_u >> 3);
                    break;
                }
                break;
            case V4L2_PIX_FMT_RGB555X:
                switch (color) {
                case 0:
                case 2:
                    *p = (r_y << 2) | (g_u >> 3);
                    break;
                case 1:
                case 3:
                    *p = (g_u << 5) | 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 /= 2;
    
        /* Generate a standard color bar pattern */
        for (w = 0; w < wmax; w++) {
            int colorpos = ((w + count) * 8/(wmax + 1)) % 8;
    
            myvivi_gen_twopix(basep + pos, colorpos);
            pos += 4; /* only 16 bpp supported for now */
        }
        return;
    }
    
    static void my_fill_buff(struct videobuf_buffer *vb)
    {
        int h , pos = 0;
        int hmax  = vb->height;
        int wmax  = vb->width;
        char *tmpbuf;
        void *vbuf = videobuf_to_vmalloc(vb);
        static int mv_count = 0;
    
        if (!vbuf)
            return;
    
        tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC);
        if (!tmpbuf)
            return;
    
        for (h = 0; h < hmax; h++) {
            myvivi_gen_line(tmpbuf, 0, wmax, hmax, h, mv_count);
            memcpy(vbuf + pos, tmpbuf, wmax * 2);
            pos += wmax*2;
        }
    
        mv_count++;
    
        kfree(tmpbuf);
    }
  • 相关阅读:
    第二阶段~JS中的各种循环语句
    项目一~达人美食图册详情
    项目一~达人行程
    项目一~美食达人图册
    项目一~机票2
    项目一~达人首页
    项目一~Hotel5
    pythonday02基础与运算符
    pythonday01计算机初步认识
    第六章 百度Apollo ROS介绍(2)
  • 原文地址:https://www.cnblogs.com/zsy12138/p/10548841.html
Copyright © 2011-2022 走看看