内核文档: 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框架的更多相关文章

  1. V4L2学习(五)VIVI虚拟摄像头驱动

    概述 前面简单分析了内核中虚拟摄像头驱动 vivi 的框架与实现,本文参考 vivi 来写一个虚拟摄像头驱动,查询.设置视频格式相对简单,难点在于 vb2_buf 的处理过程. 数据采集流程分析 在我 ...

  2. V4L2应用程序框架-二【转】

    本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11371439 V4L2驱动框架 主设备号: 81 次设备号:    0-63    64 ...

  3. Linux摄像头驱动学习之:(一)V4L2_框架分析

    这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux ...

  4. (七) UVC框架分析

    title: UVC框架分析 date: 2019/4/23 19:50:00 toc: true --- UVC框架分析 源码的位置在drivers\media\video\uvc,查看下Makef ...

  5. v4l2框架

    参考:https://www.cnblogs.com/tuotuteng/p/4648387.html http://blog.sina.com.cn/s/blog_c91863e60102w65w. ...

  6. 2.5 USB摄像头驱动程序框架

    学习目标:根据vivi驱动架构和linux-2.6.31/linux-2.6.31.14/drivers/media/video/uvc/Uvc_driver.c驱动源码,分析usb摄像头驱动程序框架 ...

  7. 2.1 摄像头V4L2驱动框架分析

    学习目标:学习V4L2(V4L2:vidio for linux version 2)摄像头驱动框架,分析vivi.c(虚拟视频硬件相关)驱动源码程序,总结V4L2硬件相关的驱动的步骤:  一.V4L ...

  8. V4L2(二)虚拟摄像头驱动vivi深入分析【转】

    转自:http://www.cnblogs.com/tureno/articles/6694463.html 转载于: http://blog.csdn.net/lizuobin2/article/d ...

  9. V4L2学习(四)VIVI分析

    vivi 相对于后面要分析的 usb 摄像头驱动程序,它没有真正的硬件相关层的操作,也就是说抛开了复杂的 usb 层的相关知识,便于理解 V4L2 驱动框架,侧重于驱动和应用的交互. 前面我们提到,V ...

随机推荐

  1. blender split mesh

    https://www.youtube.com/watch?v=yFpxQxEWNc4

  2. 基于Spring aop写的一个简单的耗时监控

    前言:毕业后应该有一两年没有好好的更新博客了,回头看看自己这一年,似乎少了太多的沉淀了.让自己做一个爱分享的人,好的知识点拿出来和大家一起分享,一起学习. 背景: 在做项目的时候,大家肯定都遇到对一些 ...

  3. python class和class(object)用法区别

    # -*- coding: utf-8 -*- # 经典类或者旧试类 class A: pass a = A() # 新式类 class B(object): pass b = B() # pytho ...

  4. 飞鹅云打印 API_C#

    飞鹅云打印: 提交订单支付成功后台自动打印,实现无人销售,自动打印,后台统计打印记录功能:   有自带WIFY:连接到wify就可以打印小票: 有自动SIM卡:第1年免流量费,第2年30一年     ...

  5. Mac 终端Terminal光标移动快捷键

    声明: 转载自: http://blog.csdn.net/lgm252008/article/details/8253519 在Mac系统中并没有Home.End等键,所以在使用时并不是特别的顺手, ...

  6. iOS之HTTP和HTTPS的基本知识和应用

    HTTPS的基本使用 1.https简单说明HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道 ...

  7. Unity图集打包

    http://www.xuanyusong.com/archives/3304 http://www.xuanyusong.com/archives/3315 http://www.xuanyuson ...

  8. Window 产品密钥

    2019.4.2 测试可用 window2003         DF74D-TWR86-D3F4V-M8D8J-WTT7M

  9. Code::Blocks设置支持C++ 11

    进入codeblocks,点击Settings --> Compiler..,进入如下页面 勾选“Have g++ follow the C++11 ISO language standard ...

  10. Visual Studio 2015编译wxWidgets

    宫指导说,换帅如换刀 程序员的编译器一换,基本套路必须都重练几次 使用wxWidgets并不难,但不能使用现有的库和工程配置文件,细节就必须理清楚 获取wxWidgets 官方的下载页面,下7z或zi ...