v4l2视频采集资料总结
基于V4L2的视频采集系统的设计与实现
基于V4L2的视频采集系统的设计与实现本文主要介绍基于V4L2的视频采集系统的设计与实现。
V4L2是视频采集设备驱动程序接口,使用V4L2接口可以实现QT、GStreamer等框架的视频采集功能。
本系统采用了Linux操作系统,系统设计包括硬件和软件两个方面。
硬件部分主要包括相机和处理器,软件部分主要包括驱动程序和应用程序。
系统的设计首先要选用合适的相机,本系统选用了USB相机。
USB相机可以与电脑直接连接,无需额外的采集卡,且USB接口是Linux支持的标准接口。
处理器部分选用了ARM Cortex-A9,该处理器性能强劲,适合处理视频数据。
软件部分主要包括驱动程序和应用程序。
驱动程序是连接硬件和软件的桥梁,本系统采用了V4L2驱动程序,它能够支持从视频采集设备中采集视频数据,并把采集到的数据发送给应用程序。
应用程序是本系统的核心,主要功能是对采集到的数据进行处理和显示。
本系统采用了OpenCV库,它可以对图像进行处理和显示。
系统的实现主要分为硬件和软件两个方面。
硬件实现包括相机和处理器的连接;软件实现包括驱动程序、应用程序的编写以及数据采集和处理等。
本系统采用了Qt框架进行应用程序的设计。
应用程序的界面包括视频显示区域和控制区域。
视频显示区域可以显示采集到的实时视频数据,控制区域包括开始/停止采集、保存视频等功能。
在实现过程中,需要注意以下几点:首先,硬件的选型要合理,要考虑到系统的整体性能和兼容性;其次,驱动程序的编写要符合V4L2框架,以保证兼容性和稳定性;最后,应用程序的设计要符合用户使用习惯,简单易用,功能齐全。
总之,基于V4L2的视频采集系统的设计和实现是一项重要的工作。
通过合理的硬件选型、稳定的驱动程序和易用的应用程序,可以实现高质量的视频采集和处理。
V4L2采集YUV视频数据并通过X264实现数据压缩
V4L2采集YUV视频数据并通过X264实现数据压缩一、V4L2采集YUYV视频数据a)打开V4L2设备并创建接收yuyv数据的文件open_v4l2_device(const char *const devname)video_obj.v4l2_fd=open(devname,O_RDWR)//打卡v4l2设备fopen(name,"wb+")//创建yuyv数据接收文件b)设置视频格式,分辨率set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height) video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.fmt.fmt.pix.pixelformat = format;video_obj.fmt.fmt.pix.width = width;video_obj.fmt.fmt.pix.height = height;ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt)c)获取当前的格式和分辨率,查看设置是否生效struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt)d)设置帧率set_v4l2_param(unsigned int num,unsigned int deno)struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;param.parm.capture.timeperframe.numerator = num;param.parm.capture.timeperframe.denominator =deno;ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m)e)获取帧率,查看设置是否生效get_v4l2_param(void)struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m)f)申请V4L2帧缓存request_v4l2_buffer(unsigned int count)video_obj.buffers = calloc(count, sizeof(VideoBuffer));memset(&video_obj.req, 0, sizeof(video_obj.req));video_obj.req.count = count;video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.req.memory = V4L2_MEMORY_MMAP;ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req)g)内存映射摄像头的缓存for (numBufs = 0; numBufs < video_obj.req.count; numBufs++){memset(&video_obj.buf, 0, sizeof(video_obj.buf));//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = numBufs;//使配置生效video_obj.buffers[numBufs].length = video_obj.buf.length;video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;//使用mmap函数将申请的缓存地址转换应用程序的绝对地址video_obj.buffers[numBufs].start =mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE, MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);//放入缓存队列ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf)}h)开始采集数据i.获取一帧缓存数据start_v4l2_capture(void)type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type)pull_v4l2_frame_buffer(unsigned int index,unsigned char **start,unsigned int *len) video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = index;ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf)*start = video_obj.buffers[index].start;*len = video_obj.buffers[index].length;ii.将yuyv数据写入到文件(同时通过SDL2显示当前帧的数据)fwrite(photo,1,len,fd);fflush(fd);iii.将该帧缓存放入到缓存池中push_v4l2_frame_buffer(unsigned int index)video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = index;ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf)i)清理动态申请的数据release_v4l2_resource(void)for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);free(video_obj.buffers);close(video_obj.v4l2_fd);二、通过SDL2显示采集到的yuyv原始数据a)初始化sdl2需要用到的功能sdl2_init(int w,int h)SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)b)创建新的窗口sdl2_init(int w,int h)win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_ WINDOW_RESIZABLE);c)创建渲染器sdl2_init(int w,int h)renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);d)设置渲染器的纹理sdl2_init(int w,int h)SDL_TEXTUREACCESS_STREAMING,w,h);e)创建SDL2的事件处理线程pthread_create(&pid, NULL,event_loop,NULL)SDL_PollEvent(&event)f)循环显示步骤1:更新渲染器纹理sdl2_refresh(void *pixels,int pitch)SDL_UpdateT exture(texture,NULL,pixels,pitch)g)循环显示步骤2:清空渲染器sdl2_refresh(void *pixels,int pitch)SDL_UpdateT exture(texture,NULL,pixels,pitch)h)循环显示步骤3:将纹理数据拷贝到渲染器sdl2_refresh(void *pixels,int pitch)SDL_RenderCopy(renderer,texture,NULL,NULL)i)循环显示步骤4:显示视频sdl2_refresh(void *pixels,int pitch)SDL_RenderPresent(renderer);三、通过libX264压缩视频数据到H264a)创建相关结构体,打开yuyv文件和将要保存h264数据的文件x264_nal_t* pNals = NULL;x264_t* pHandle = NULL;x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));FILE* fp_src = fopen("x.yuv", "rb");FILE* fp_dst = fopen("x.h264", "wb");b)给结构体x264_param_t赋予一些默认的参数,修改参数中的宽高和数据格式(因为录制的时候是采用V4L2_PIX_FMT_YUYV格式,宽高为640x480)x264_param_default(pParam);pParam->i_width = width;pParam->i_height = height;pParam->i_csp = csp;c)设置profilex264_param_apply_profile(pParam, x264_profile_names[4]);d)打开编码器pHandle = x264_encoder_open(pParam);e)初始化帧数据的输入输出结构体x264_picture_init(pPic_out);x264_picture_alloc(pPic_in, csp, pParam->i_width,pParam->i_height);f)根据视频数据计算出视频帧数fseek(fp_src,0,SEEK_END);switch(csp){case X264_CSP_I444:frame_num=ftell(fp_src)/(y_size*3);break;case X264_CSP_I420:frame_num=ftell(fp_src)/(y_size*3/2);break;case X264_CSP_I422:break;}fseek(fp_src,0,SEEK_SET);g)循环编码步骤1:分离yuv分量h)循环编码步骤2:编码一帧i)循环编码步骤3:将编码后的数据写入到h264文件中上面三步骤包含的内容:for( i=0;i<frame_num;i++){< p="">switch(csp){case X264_CSP_I444:{fread(pPic_in->img.plane[0],y_size,1,fp_src);//Yfread(pPic_in->img.plane[1],y_size,1,fp_src);//Ufread(pPic_in->img.plane[2],y_size,1,fp_src);//Vbreak;}case X264_CSP_I420:{fread(pPic_in->img.plane[0],y_size,1,fp_src);//Yfread(pPic_in->img.plane[1],y_size/4,1,fp_src);//Ufread(pPic_in->img.plane[2],y_size/4,1,fp_src);//Vbreak;}case X264_CSP_I422:{/*Yuyv格式数据的存放方式为:(4X4像素)Y U Y V Y U Y VY U Y V Y U Y VY U Y V Y U Y VY U Y V Y U Y VY的个数为像素点的个数,实际上像素点的个数为y个数的两倍*/int index = 0;int y_i = 0 , u_i = 0 , v_i = 0;for(index = 0 ; index < y_size*2 ;){fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Yindex++;fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//Uindex++;fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Yindex++;fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//Vindex++;}break;}}pPic_in->i_pts = i;x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);for ( j = 0; j < iNal; ++j)fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);}while(1){ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);if(ret == 0)Break;for(j = 0;j < iNal; ++j)fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);}k)清理动态申请的资源x264_picture_clean(pPic_in);x264_encoder_close(pHandle);pHandle = NULL;free(pPic_in);free(pPic_out);free(pParam);fclose(fp_src);fclose(fp_dst);下面是两个项目的源码:说明:1、V4L2采集数据和SDL2显示是在同一个项目中2、YUV数据文件通过libx264压缩为H264格式的文件为一个项目3、实验环境是vmware的ubuntu系统,默认安装了SDL1.2,卸载了原来安装的1.2版本,重新编译安装SDL2.04、需要安装Libx264库5、项目中的源码大部分是参考网上各论坛的博客:/doc/0c9043386.html,/leixiaohua1020/artic le/details/42078645/doc/0c9043386.html,/yuanhubilie/article/ details/37930429文件v4l2lib.c(数据采集和显示项目)//编译命令gcc v4l2lib.c -L/usr/local/SDL/lib/ -I/usr/local/SDL/include/SDL2 -lSDL2 -lpthread -o v4l2 //SDL的库安装路径为/usr/local/SDL,根据自己安装路径修改#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include "SDL.h"#include#include//--------macro definition------#define MAX_DEV_NAME 32#define VIDEO_REC#define WIDTH_PIX 640#define HEIGHT_PIX 480//--------structions defined here-------typedef struct VideoBuffer{unsigned char *start;size_t offset;size_t length;}VideoBuffer;typedef struct v4l2_param{char v4l2_devname[MAX_DEV_NAME];//设备名int v4l2_fd;//描述符号VideoBuffer *buffers;struct v4l2_requestbuffers req;struct v4l2_capability cap;struct v4l2_input input;struct v4l2_format fmt;struct v4l2_buffer buf;}VIDEO_T;//--------variable defined here-------static VIDEO_T video_obj;static pthread_t pid;static unsigned char state = 0;//--------SDL2----------static unsigned char inited = 0;static SDL_Window * win = NULL;static SDL_Renderer * renderer = NULL;static SDL_Texture * texture = NULL;static SDL_CommonEvent comm;static SDL_Event event;static int open_v4l2_device(const char *const devname) { //打开设备if(strlen(devname) >= MAX_DEV_NAME){printf("device name fail:%s\n",devname);return -1;}elsememset(&video_obj,0,sizeof(video_obj));video_obj.v4l2_fd = open(devname,O_RDWR);if(video_obj.v4l2_fd <= 0){perror("open fail");return -1;elseprintf("%s success\n",__func__);memcpy(video_obj.v4l2_devname,devname,strlen(devname) );return 0;}static int set_v4l2_param(unsigned int num,unsigned int deno){struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;param.parm.capture.timeperframe.numerator = num;param.parm.capture.timeperframe.denominator =deno;if(ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m) < 0){printf("%s fail\n",__func__);return -1;}else{printf("%s ok\n",__func__);return 0;}}static int get_v4l2_param(void){struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m) < 0){perror("get param failed");return -1;}else{printf("%s:%d/%d\n",__func__,param.parm.capture.timeperframe.numerator,param.parm.c apture.timeperframe.denominator);return 0;}}static int set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height){//设置视频格式memset(&video_obj.fmt,0,sizeof(video_obj.fmt));video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频源的格式为JPEG或YUN4:2:2或RGB V4L2_PIX_FMT_RGB565 V4L2_PIX_FMT_YUV565 video_obj.fmt.fmt.pix.pixelformat = format;//video_obj.fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//设置视频宽度video_obj.fmt.fmt.pix.width = width;//设置视频高度video_obj.fmt.fmt.pix.height = height;if (ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt) < 0)//使配置生效{perror("set format failed");return -1;}elseprintf("%s success[format:%X w:%d h:%d]\n",__func__,format,width,height);struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt) < 0){perror("set format failed");return -1;}elseprintf("%sget success[format:%X w:%d h:%d]\n",__func__,fmt.fmt.pix.pixelformat,fmt.fmt.pix.width,fmt.fmt.pi x.height);return 0;}static int request_v4l2_buffer(unsigned int count){//申请帧缓冲video_obj.buffers = calloc(count, sizeof(VideoBuffer));memset(&video_obj.req, 0, sizeof(video_obj.req));//缓存数量,即可保存的图片数量video_obj.req.count = count;//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTRvideo_obj.req.memory = V4L2_MEMORY_MMAP;//使配置生效if (ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req) == -1){perror("request buffer error \n");return -1;}elseprintf("%s success[request %d buffers]\n",__func__,count);}static int mmap_v4l2_buffer(void){//将VIDIOC_REQBUFS获取内存转为物理空间int numBufs;for (numBufs = 0; numBufs < video_obj.req.count; numBufs++){memset(&video_obj.buf, 0, sizeof(video_obj.buf));//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = numBufs;//使配置生效if (ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf) < 0){perror("VIDIOC_QUERYBUF");return -1;}//printf("request buf %d success\n",numBufs);video_obj.buffers[numBufs].length = video_obj.buf.length;video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;//使用mmap函数将申请的缓存地址转换应用程序的绝对地址video_obj.buffers[numBufs].start = mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_obj.v4l2_fd,vi deo_obj.buf.m.offset);if (video_obj.buffers[numBufs].start == MAP_FAILED){perror("buffers error");return -1;}//printf("mmap buf 0x%p lenght:%d success\n",video_obj.buffers[numBufs].start,video_obj.buf.length );//放入缓存队列if (ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf) < 0) {printf("VIDIOC_QBUF");return -1;}}printf("%s success\n",__func__);return 0;}static int start_v4l2_capture(void){//开始视频显示enum v4l2_buf_type type;//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREif (ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type) < 0){perror("VIDIOC_STREAMON");return -1;}printf("%s stream on success\n",__func__);return 0;}static int pull_v4l2_frame_buffer(unsigned int index , unsigned char **start , unsigned int *len){video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR (用户指针)if(video_obj.req.count <= index)return -1;video_obj.buf.index = index; //读取缓存中的第几帧if (ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf) < 0){perror("VIDIOC_DQBUF");return -1;}*start = video_obj.buffers[index].start;*len = video_obj.buffers[index].length;return 0;}static int push_v4l2_frame_buffer(unsigned int index){video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR (用户指针)if(video_obj.req.count <= index)return -1;video_obj.buf.index = index; //第几帧放入缓存//获取下一帧视频数据if (ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf) < 0){perror("VIDIOC_QBUF");return -1;}return 0;}static void release_v4l2_resource(void){int numBufs;for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)free(video_obj.buffers);close(video_obj.v4l2_fd);printf("%s\n",__func__);}static int sdl2_init(int w,int h){if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1){printf("SDL_Init fail!");return -1;}elseprintf("SDL_Init success\n");/*title :窗口标题x :窗口位置x坐标。
V4L2采集YUV视频数据并通过X264实现数据压缩
一、V4L2采集YUYV视频数据a)打开V4L2设备并创建接收yuyv数据的文件open_v4l2_device(const char *const devname)video_obj.v4l2_fd=open(devname,O_RDWR)//打卡v4l2设备fopen(name,"wb+")//创建yuyv数据接收文件b)设置视频格式,分辨率set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height) video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.fmt.fmt.pix.pixelformat = format;video_obj.fmt.fmt.pix.width = width;video_obj.fmt.fmt.pix.height = height;ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt)c)获取当前的格式和分辨率,查看设置是否生效struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt)d)设置帧率set_v4l2_param(unsigned int num,unsigned int deno)struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;param.parm.capture.timeperframe.numerator = num;param.parm.capture.timeperframe.denominator =deno;ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m)e)获取帧率,查看设置是否生效get_v4l2_param(void)struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m)f)申请V4L2帧缓存request_v4l2_buffer(unsigned int count)video_obj.buffers = calloc(count, sizeof(VideoBuffer));memset(&video_obj.req, 0, sizeof(video_obj.req));video_obj.req.count = count;video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.req.memory = V4L2_MEMORY_MMAP;ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req)g)内存映射摄像头的缓存for (numBufs = 0; numBufs < video_obj.req.count; numBufs++){memset(&video_obj.buf, 0, sizeof(video_obj.buf));//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = numBufs;//使配置生效ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf)video_obj.buffers[numBufs].length = video_obj.buf.length;video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;//使用mmap函数将申请的缓存地址转换应用程序的绝对地址video_obj.buffers[numBufs].start =mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);//放入缓存队列ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf)}h)开始采集数据i.获取一帧缓存数据start_v4l2_capture(void)type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type)pull_v4l2_frame_buffer(unsigned int index,unsigned char **start,unsigned int *len) video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = index;ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf)*start = video_obj.buffers[index].start;*len = video_obj.buffers[index].length;ii.将yuyv数据写入到文件(同时通过SDL2显示当前帧的数据)fwrite(photo,1,len,fd);fflush(fd);iii.将该帧缓存放入到缓存池中push_v4l2_frame_buffer(unsigned int index)video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = index;ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf)i)清理动态申请的数据release_v4l2_resource(void)for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);free(video_obj.buffers);close(video_obj.v4l2_fd);二、通过SDL2显示采集到的yuyv原始数据a)初始化sdl2需要用到的功能sdl2_init(int w,int h)SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)b)创建新的窗口sdl2_init(int w,int h)win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);c)创建渲染器sdl2_init(int w,int h)renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);d)设置渲染器的纹理sdl2_init(int w,int h)Texture=SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YUY2,SDL_TEXTUREACCESS_STREAMING,w,h);e)创建SDL2的事件处理线程pthread_create(&pid, NULL,event_loop,NULL)SDL_PollEvent(&event)f)循环显示步骤1:更新渲染器纹理sdl2_refresh(void *pixels,int pitch)SDL_UpdateTexture(texture,NULL,pixels,pitch)g)循环显示步骤2:清空渲染器sdl2_refresh(void *pixels,int pitch)SDL_UpdateTexture(texture,NULL,pixels,pitch)h)循环显示步骤3:将纹理数据拷贝到渲染器sdl2_refresh(void *pixels,int pitch)SDL_RenderCopy(renderer,texture,NULL,NULL)i)循环显示步骤4:显示视频sdl2_refresh(void *pixels,int pitch)SDL_RenderPresent(renderer);三、通过libX264压缩视频数据到H264a)创建相关结构体,打开yuyv文件和将要保存h264数据的文件x264_nal_t* pNals = NULL;x264_t* pHandle = NULL;x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));FILE* fp_src = fopen("x.yuv", "rb");FILE* fp_dst = fopen("x.h264", "wb");b)给结构体x264_param_t赋予一些默认的参数,修改参数中的宽高和数据格式(因为录制的时候是采用V4L2_PIX_FMT_YUYV格式,宽高为640x480)x264_param_default(pParam);pParam->i_width = width;pParam->i_height = height;pParam->i_csp = csp;c)设置profilex264_param_apply_profile(pParam, x264_profile_names[4]);d)打开编码器pHandle = x264_encoder_open(pParam);e)初始化帧数据的输入输出结构体x264_picture_init(pPic_out);x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);f)根据视频数据计算出视频帧数fseek(fp_src,0,SEEK_END);switch(csp){case X264_CSP_I444:frame_num=ftell(fp_src)/(y_size*3);break;case X264_CSP_I420:frame_num=ftell(fp_src)/(y_size*3/2);break;case X264_CSP_I422:frame_num=ftell(fp_src)/(y_size*2);break;}fseek(fp_src,0,SEEK_SET);g)循环编码步骤1:分离yuv分量h)循环编码步骤2:编码一帧i)循环编码步骤3:将编码后的数据写入到h264文件中上面三步骤包含的内容:for( i=0;i<frame_num;i++){switch(csp){case X264_CSP_I444:{fread(pPic_in->img.plane[0],y_size,1,fp_src);//Yfread(pPic_in->img.plane[1],y_size,1,fp_src);//Ufread(pPic_in->img.plane[2],y_size,1,fp_src);//Vbreak;}case X264_CSP_I420:{fread(pPic_in->img.plane[0],y_size,1,fp_src);//Yfread(pPic_in->img.plane[1],y_size/4,1,fp_src);//Ufread(pPic_in->img.plane[2],y_size/4,1,fp_src);//Vbreak;}case X264_CSP_I422:{/*Yuyv格式数据的存放方式为:(4X4像素)Y U Y V Y U Y VY U Y V Y U Y VY U Y V Y U Y VY U Y V Y U Y VY的个数为像素点的个数,实际上像素点的个数为y个数的两倍*/int index = 0;int y_i = 0 , u_i = 0 , v_i = 0;for(index = 0 ; index < y_size*2 ;){fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Yindex++;fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//Uindex++;fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Yindex++;fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//Vindex++;}break;}}pPic_in->i_pts = i;x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);for ( j = 0; j < iNal; ++j)fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);}j)编码步骤4:将还残留在编码器中的数据flush out,并写入到文件while(1){ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);if(ret == 0)Break;for(j = 0;j < iNal; ++j)fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);}k)清理动态申请的资源x264_picture_clean(pPic_in);x264_encoder_close(pHandle);pHandle = NULL;free(pPic_in);free(pPic_out);free(pParam);fclose(fp_src);fclose(fp_dst);下面是两个项目的源码:说明:1、V4L2采集数据和SDL2显示是在同一个项目中2、YUV数据文件通过libx264压缩为H264格式的文件为一个项目3、实验环境是vmware的ubuntu系统,默认安装了SDL1.2,卸载了原来安装的1.2版本,重新编译安装SDL2.04、需要安装Libx264库5、项目中的源码大部分是参考网上各论坛的博客:/leixiaohua1020/article/details/42078645/yuanhubilie/article/details/37930429文件v4l2lib.c(数据采集和显示项目)//编译命令gcc v4l2lib.c -L/usr/local/SDL/lib/ -I/usr/local/SDL/include/SDL2 -lSDL2 -lpthread -o v4l2//SDL的库安装路径为/usr/local/SDL,根据自己安装路径修改#include <linux/videodev2.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/select.h>#include <sys/time.h>#include <pthread.h>#include "SDL.h"#include <stdio.h>#include <time.h>//--------macro definition------#define MAX_DEV_NAME 32#define MAX_BUF 5#define VIDEO_REC#define WIDTH_PIX 640#define HEIGHT_PIX 480//--------structions defined here-------typedef struct VideoBuffer{unsigned char *start;size_t offset;size_t length;}VideoBuffer;typedef struct v4l2_param{char v4l2_devname[MAX_DEV_NAME];//设备名int v4l2_fd;//描述符号VideoBuffer *buffers;struct v4l2_requestbuffers req;struct v4l2_capability cap;struct v4l2_input input;struct v4l2_format fmt;struct v4l2_buffer buf;}VIDEO_T;//--------variable defined here-------static VIDEO_T video_obj;static pthread_t pid;static unsigned char state = 0;//--------SDL2----------static unsigned char inited = 0;static SDL_Window * win = NULL;static SDL_Renderer * renderer = NULL;static SDL_Texture * texture = NULL;static SDL_CommonEvent comm;static SDL_Event event;static int open_v4l2_device(const char *const devname) {//打开设备if(strlen(devname) >= MAX_DEV_NAME){printf("device name fail:%s\n",devname);return -1;}elsememset(&video_obj,0,sizeof(video_obj));video_obj.v4l2_fd = open(devname,O_RDWR);if(video_obj.v4l2_fd <= 0){perror("open fail");return -1;}elseprintf("%s success\n",__func__);memcpy(video_obj.v4l2_devname,devname,strlen(devname));return 0;}static int set_v4l2_param(unsigned int num,unsigned int deno){struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;param.parm.capture.timeperframe.numerator = num;param.parm.capture.timeperframe.denominator =deno;if(ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m) < 0){printf("%s fail\n",__func__);return -1;}else{printf("%s ok\n",__func__);return 0;}}static int get_v4l2_param(void){struct v4l2_streamparm param;memset(¶m, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m) < 0){perror("get param failed");return -1;}else{printf("%s:%d/%d\n",__func__,param.parm.capture.timeperframe.numerator,param.parm.capture.timeperframe.denominator);return 0;}}static int set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height){//设置视频格式memset(&video_obj.fmt,0,sizeof(video_obj.fmt));//视频数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频源的格式为JPEG或YUN4:2:2或RGB V4L2_PIX_FMT_RGB565 V4L2_PIX_FMT_YUV565 video_obj.fmt.fmt.pix.pixelformat = format;//video_obj.fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//设置视频宽度video_obj.fmt.fmt.pix.width = width;//设置视频高度video_obj.fmt.fmt.pix.height = height;if (ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt) < 0)//使配置生效{perror("set format failed");return -1;}elseprintf("%s success[format:%X w:%d h:%d]\n",__func__,format,width,height);struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt) < 0){perror("set format failed");return -1;}elseprintf("%sget success[format:%X w:%d h:%d]\n",__func__,fmt.fmt.pix.pixelformat,fmt.fmt.pix.width,fmt.fmt.pix.height);return 0;}static int request_v4l2_buffer(unsigned int count){//申请帧缓冲video_obj.buffers = calloc(count, sizeof(VideoBuffer));memset(&video_obj.req, 0, sizeof(video_obj.req));//缓存数量,即可保存的图片数量video_obj.req.count = count;//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTRvideo_obj.req.memory = V4L2_MEMORY_MMAP;//使配置生效if (ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req) == -1){perror("request buffer error \n");return -1;}elseprintf("%s success[request %d buffers]\n",__func__,count);return 0;}static int mmap_v4l2_buffer(void){//将VIDIOC_REQBUFS获取内存转为物理空间int numBufs;for (numBufs = 0; numBufs < video_obj.req.count; numBufs++){memset(&video_obj.buf, 0, sizeof(video_obj.buf));//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREvideo_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)video_obj.buf.memory = V4L2_MEMORY_MMAP;video_obj.buf.index = numBufs;//使配置生效if (ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf) < 0){perror("VIDIOC_QUERYBUF");return -1;}//printf("request buf %d success\n",numBufs);video_obj.buffers[numBufs].length = video_obj.buf.length;video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;//使用mmap函数将申请的缓存地址转换应用程序的绝对地址video_obj.buffers[numBufs].start = mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);if (video_obj.buffers[numBufs].start == MAP_FAILED){perror("buffers error");return -1;}//printf("mmap buf 0x%p lenght:%d success\n",video_obj.buffers[numBufs].start,video_obj.buf.length);//放入缓存队列if (ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf) < 0){printf("VIDIOC_QBUF");return -1;}}printf("%s success\n",__func__);return 0;}static int start_v4l2_capture(void){//开始视频显示enum v4l2_buf_type type;//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTUREtype = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type) < 0){perror("VIDIOC_STREAMON");return -1;}printf("%s stream on success\n",__func__);return 0;}static int pull_v4l2_frame_buffer(unsigned int index , unsigned char **start , unsigned int *len){video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)if(video_obj.req.count <= index)return -1;video_obj.buf.index = index; //读取缓存中的第几帧if (ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf) < 0){perror("VIDIOC_DQBUF");return -1;}*start = video_obj.buffers[index].start;*len = video_obj.buffers[index].length;return 0;}static int push_v4l2_frame_buffer(unsigned int index){video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)if(video_obj.req.count <= index)return -1;video_obj.buf.index = index; //第几帧放入缓存//获取下一帧视频数据if (ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf) < 0){perror("VIDIOC_QBUF");return -1;}return 0;}static void release_v4l2_resource(void){int numBufs;for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);free(video_obj.buffers);close(video_obj.v4l2_fd);printf("%s\n",__func__);}static int sdl2_init(int w,int h){if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1){printf("SDL_Init fail!");return -1;}elseprintf("SDL_Init success\n");/*title :窗口标题x :窗口位置x坐标。
Linuxv4l2架构之v4l2-ctl抓取、设置图像
Linuxv4l2架构之v4l2-ctl抓取、设置图像一、本开发、测试基于RV1126-1109的SDK上进行。
一个mipi 的摄像头,接到rv1126上看看能不能抓到图。
不需要配置寄存器。
例如这个摄像头参数:raw8,4lanes,512*192,30fps二、v4l2-ctl工具则是针对/dev/video0,/dev/video1等video 设备,它在video设备上进行set_fmt、reqbuf、qbuf、dqbuf、stream_on、stream_off 等一系列操作。
复制一份索尼imx291的代码直接修改,改完之后测试。
测试方法如下:v4l2-ctl -d /dev/video0 --set-fmt-video=width=512,height=192,pixelformat=BG10 --stream-mmap=3 --stream-to=/tmp/bg10.bin --stream-count=1 --stream-poll三、测试方法和步骤如下:执行发现,没有抓到数据也没有timeout,直接退出了。
dmesg 发现出现了如下一条信息rkcif_mipi_lvds: crop size is bigger than input这部分代码如下:rkcif_start_streaming() -> rkcif_sanity_check_fmt(stream, NULL)static int rkcif_sanity_check_fmt(struct rkcif_stream *stream, const struct v4l2_rect *s_crop){struct rkcif_device *dev = stream->cifdev;struct v4l2_device *v4l2_dev = &dev->v4l2_dev;struct v4l2_rect input, *crop;stream->cif_fmt_in = get_input_fmt(dev->active_sensor->sd, &input, stream->id + 1);if (!stream->cif_fmt_in) {v4l2_err(v4l2_dev, "Input fmt is invalid\n");return -EINVAL;}if (s_crop)crop = (struct v4l2_rect *)s_crop;elsecrop = &stream->crop[CROP_SRC_ACT];if (crop->width + crop->left > input.width ||crop->height + crop->top > input.height) {v4l2_err(v4l2_dev, "crop size is bigger than input\n");return -EINVAL;}...}查看代码可以知道input.width及heigth小于crop的width或者heigth。
v4l2_buffer结构
v4l2_buffer结构
V4L2(Video4Linux2)是Linux中用于视频捕获的API。
其中的
`v4l2_buffer`结构体是用来描述视频捕获或输出缓冲区的。
当使用流I/O时,帧以`v4l2_buffer`的格式在应用和驱动之间传输。
一个缓冲区可以有三种基本状态:
1. 在驱动的传入队列中:如果驱动不用它做任何有用的事,应用就可以把缓冲区放在这个队列里。
对于视频捕获设备,传入队列中的缓冲区是空的,等待驱动向其中填入视频数据。
对于输出设备,这些缓冲区是要设备发送的帧数据。
2. 在驱动的传出队列中:这些缓冲区已由驱动处理过,正等待应用来认领。
对于捕获设备,传出缓冲区内是新的帧数据;对于输出设备,这些缓冲区是空的。
3. 不在上述两个队列里:在这种状态时,缓冲区由用户空间拥有,驱动无法访问。
这是应用可对缓冲区进行操作的唯一时间。
具体结构体定义可能因版本和平台而异,建议查阅相关文档或源码以获取更详细和准确的信息。
基于linux下的音视频采集与传输综述
摘要:在LINUX下实现对音频和视频的采集,并编写Socket程序将采集到的音频文件在两台主机的进程之间进行传输。
关键字:LINUX 音频、视频采集Socket传输项目简介本项目将分成三部分来分别实现,分别为Linux下视频的采集、Linux下音频的采集、Linux下Socket传输的实现。
下面将分别介绍各部分具体的实现过程。
Linux 下视频采集1、背景介绍V4L,其全称是Video4Linux(Video for Linux),是在linux内核中关于视频设备的API接口,涉及开关视频设备、采集并处理视频图像信息。
V4L从2.1.x 版本的内核中开始出现。
现在出现Video4Linux2 (Video for Linux Two),简称V4L2。
很显然,他是V4L的改进版,修复了第一代的部分设计bug。
从2.5.x开始,V4L2就被集成到内核里面去了。
尽管如此,还是有一部分设备的驱动不支持新版本的V4L2,所以,有时候我们会看到V4L跟V4L2同时出现在代码里面。
Linux系统中,视频设备被当作一个设备文件来看待,设备文件存放在 /dev 目录下,完整路径的设备文件名为: /dev/video0 .2、视频采集基本步骤视频采集基本步骤流程:打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲区管理)-> 循环获取数据-> 关闭设备。
其中打开视频设备非常简单,在V4L2中,视频设备被看做一个文件。
使用open函数打开这个设备,打开这个设备有两种模式即阻塞模式和非阻塞模式。
主要实现代码为:○1用非阻塞模式打开摄像头设备代码为int cameraFd;cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);○2用阻塞模式打开摄像头设备,上述代码变为:cameraFd = open("/dev/video0", O_RDWR);应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。
基于v4l2视频采集
V4L2 采集具体流程
检查当前设备支持的标准(可忽略)
在亚洲,一般使用PAL(720X576)制式的摄像头,而欧洲一般 使用NTSC(720X480)。
使用VIDIOC_QUERYSTD来检测:
v4l2_std_id std; do{
ret= ioctl(fd, VIDIOC_QUERYSTD, &std); } while(ret== -1 && errno== EAGAIN);
V4L2 采集具体流程
将申请到的帧缓冲映射到用户空间,并加入到采集队列中
for(i = 0; i< count; i++) { gst_videobuf.index = i; // 要获取内核视频缓冲区的信息编号 gst_videobuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 数据流类型 gst_videobuf.memory = V4L2_MEMORY_MMAP; //把内核空间缓冲区映射到用户空间缓冲区 ret = ioctl(g_videofd , VIDIOC_QUERYBUF, &gst_videobuf); if(ret < 0) { printf("VIDIOC_QUERYBUF (%d) failed (%d)\n", i, ret); return ret; } frame[i].length = gst_videobuf.length; // 图像的大小 frame[i].start = (char *) mmap(0, gst_videobuf.length, PROT_READ|PROT_WRITE, MAP_SHARED, g_videofd, gst_videobuf.m.offset); if (frame[i].start == MAP_FAILED) { printf("mmap (%d) failed: %s\n", i, strerror(errno)); return -1; } ret = ioctl(g_videofd , VIDIOC_QBUF, &gst_videobuf);//把刚刚映射的那帧内存加入到采集列 if (ret < 0){ printf ("VIDIOC_QBUF (%d) failed (%d)\n", i, ret); return -1; } }
v4l2-ctl 常用参数
v4l2-ctl 常用参数摄像头是现代电子设备中的重要组成部分,广泛应用于视频通信、图像采集等领域。
在Linux系统中,v4l2-ctl是一个常用的命令行工具,用于控制和配置视频 4 Linux 2(V4L2)设备的参数。
本文将介绍v4l2-ctl的常用参数,并详细说明它们的用途和配置方法。
1. --list-devices:列出系统中的视频设备列表该参数用于列出系统中所有可用的视频设备,包括摄像头和视频采集卡等。
通过执行命令`v4l2-ctl --list-devices`,可以查看系统中所有视频设备的名称和路径。
这对于多个摄像头的选择和配置非常有用。
2. --list-formats:列出设备支持的视频格式该参数用于列出指定视频设备所支持的视频格式。
通过执行命令`v4l2-ctl --list-formats -d /dev/video0`,可以查看摄像头支持的视频格式和对应的分辨率。
这对于选择合适的视频格式和配置摄像头的分辨率非常重要。
3. --set-fmt-video:设置视频格式和分辨率该参数用于设置视频设备的视频格式和分辨率。
例如,执行命令`v4l2-ctl --set-fmt-video=width=1280,height=720,pixelformat=YUYV -d /dev/video0`可以将摄像头的视频格式设置为YUYV,并将分辨率设置为1280x720。
通过调整视频格式和分辨率,可以满足不同应用场景的需求。
4. --set-ctrl:设置设备的控制参数该参数用于设置视频设备的各种控制参数,如对比度、亮度、饱和度等。
通过执行命令`v4l2-ctl --set-ctrl=brightness=100 -d /dev/video0`,可以将摄像头的亮度设置为100。
通过调整控制参数,可以改善图像质量和适应不同的环境条件。
5. --get-ctrl:获取设备的控制参数该参数用于获取视频设备的各种控制参数的当前值。
Linux V4L2 摄像头视频采集
Linux V4L2 摄像头视频采集2011-01-05 17:34一,什么是 video4linuxVideo4linux(简称V4L),是linux中关于视频设备的内核驱动,现在已有Video4linux2,还未加入linux内核,使用需自己下载补丁。
在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/videoN下,N可能为0,1,2,3... 一般0.另,推荐一个用于播放从摄像头采集到的raw数据的播放器RawPlayer,只需要把采集的数据保存到文件***.yuv就OK了。
二,V4L2采集视频流程1. 打开设备文件。
int fd=open(”/dev/video0″,O_RDWR);2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入,一个视频设备可以有多个视频输入。
VIDIOC_S_INPUT,struct v4l2_input4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format5. 向驱动申请帧缓冲,一般不超过5个。
struct v4l2_requestbuffers6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。
mmap7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer8. 开始视频的采集。
VIDIOC_STREAMON9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。
VIDIOC_DQBUF10. 将缓冲重新入队列尾,这样可以循环采集。
VIDIOC_QBUF11. 停止视频的采集。
用v4l2和framebuffer实现usb摄像头视频采集并显示
用v4l2和framebuffer实现usb摄像头视频采集并显示用v4l2和framebuffer实现usb摄像头图像采集并显示前言很多天了,看了数不尽的论坛和各网站的帖子后,我把我遇到的问题都解决的差不多了,感觉应该是把摄像头跑起来了,但还是遇到了一些问题,似乎是图像处理方面的,我好像解决不了这个问题了,我好想有个人帮帮我。
写这篇文章估计得花3~4小时,我真心希望哪位朋友能明白我的想法,能顺手帮帮我。
正文一,我的思路我想用一幅图来说明我所做的事情,如图1所示(图中uvcdriver 标错了,应该是uvcvideo)。
图1图1左侧是图像采集,右侧是图像显示。
采集的帧速是30 帧/秒。
二,v4l2(video for linux two)驱动摄像头的是uvcdriver,该驱动设计时采用了v4l系列的标准(该标准好像是linuxTV制定的,linuxTV的官网是),我的CentOS6.3(内核是linux-2.6.32.60)采用的是v4l2标准。
一开始我编写应用程序的时候什么都不懂,见论坛上帖子怎么讲,我就怎么写,当中很多是参照v4l标准,我当时不知道,直接照抄,出了问题,改用v4l2标准后才解决了问题。
v4l2 API的在/usr/include/linux/videodev2.h头文件中,很容易就找到了。
采集图像的实例程序网上很多,但最经典的还是linuxTV官网推出的capture.c,这里给出地址downloads/legacy/video4linux/API/V4L2_API/v4l2spec/capture.c这个程序写的很好,很值得研究。
我就是借鉴的这个程序,然后得到我的摄像头设备的信息,如图2(那个pixel_format应该用十六进制表示的,我没注意,十六进制的话应该是0x56595559,即YUYV的意思)所示。
图2一插上摄像头,uvcvideo就直接把我的摄像头识别了。
v4l2我不想再多写了,基本上只要看了那个经典的capture.c就够了。
V4L2简介
V4L2简介V4L2 简介2011-03-15 16:41:55| 分类: android相关 |字号订阅1.什么是video4linuxVideo4linux(简称V4L),是linux中关于视频设备的内核驱动,现在已有Video4linux2,还未加入linux内核,使用需自己下载补丁。
在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
2.Video4linux下视频编程的流程(1)打开视频设备:(2)读取设备信息(3)更改设备当前设置(没必要的话可以不做)(4)进行视频采集,两种方法:a.内存映射b.直接从设备读取(5)对采集的视频进行处理(6)关闭视频设备。
为程序定义的数据结构typedef struct v4l_struct{int fd;struct video_capability capability;struct video_channel channel[4];struct video_picture picture;struct video_window window;struct video_capture capture;struct video_buffer buffer;struct video_mmap mmap;struct video_mbuf mbuf;unsigned char *map;int frame;int framestat[2];}vd;3.Video4linux支持的数据结构及其用途(1) video_capability 包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等),包含的分量:name[32] //设备名称maxwidth ,maxheight,minwidth,minheightChannels //信号源个数type //是否能capture,彩色还是黑白,是否能裁剪等等。
Linuxv4l2编程(摄像头信息采集)
Linuxv4l2编程(摄像头信息采集)基于Linux3.4.2,⾃⼰做⼀点⼉视频信息采集及⽹络传输的⼩实验,边做边学,⼀些基础知识同步整理。
1. 定义V4L2(Video For Linux Two) 是内核提供给应⽤程序访问⾳、视频驱动的统⼀接⼝。
V4L2 的相关定义包含在头⽂件<linux/videodev2.h> 中.2. ⼯作流程:打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置⼀种输⼊输出⽅法(缓冲区管理)-> 循环获取数据-> 关闭设备。
3.设备打开和关闭//打开#include <fcntl.h>int open(const char *device_name, int flags);//关闭#include <unistd.h>int close(int fd);实验使⽤UVC摄像头,插⼊后⾃动⽣成设备“/dev/vedio0”,打开关闭实例如下int fd = open("/dev/video0",O_RDWR);close(fd);4. 查询设备属性: VIDIOC_QUERYCAP函数使⽤:int ioctl(int fd, int request, struct v4l2_capability *argp);v4l2相关结构体定义:struct v4l2_capability{u8 driver[16]; // 驱动名字u8 card[32]; // 设备名字u8 bus_info[32]; // 设备在系统中的位置u32 version;// 驱动版本号u32 capabilities;// 设备⽀持的操作u32 reserved[4]; // 保留字段};capabilities 常⽤值:V4L2_CAP_VIDEO_CAPTURE // 是否⽀持图像获取例:显⽰设备信息struct v4l2_capability cap;ioctl(fd,VIDIOC_QUERYCAP,&cap);printf(“Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n”,cap.driver,cap.card,cap.bus_info,cap.capabilities);5. 设置视频的制式和帧格式制式包括PAL,NTSC,帧的格式个包括宽度和⾼度等。
基于V4L2的视频采集系统的设计与实现
5结 语
本 文介绍 了一种基于L i n u x 系统接 H V 4 L 2 的视频采集系统 , 将 B 摄像 头采集到的视频数据通过Q t 界面程序在L C D 进行显示 , 实 帧, 该 帧必须 经过处理 后变为R G B 格式之后 , 在本地L C D 显示器展 US 时地获取采集到的图像。 系统运行平稳 , 结构简单 , 具有广 阔的应用 示。
应用 软件
嵌入 式L i n u x 操 作系统
软件
L mu x 仫 US B 摄像 头驱动
B o o t l o a d e r 引导程序
图 3 软件 系统 架构 图
程序开 始
f 驱 动程 序l L i n u x 内 核4 根 文件 系统l
B o o t l o a d e r  ̄ J f 导程序
( 4 ) 将 帧放入 缓 冲队 列并开始 视 频采 集 。 首先 调用VI D I OC — 录下; 最后开 发板上 电 , 在P C 调试 窗 口中 , 直接执 行 . / q t . O— q ws
Q B UF 将空缓存加入 到等 待缓存 队列 , 加入 队列之后调用V I D I OC — 命令 , 即可在开发板看到实时捕获图像 , 该 图像 较清 晰, 如 图5 所示 : S TR E AMO N命令开始视频采集 , 此时该缓存 中就有视频数据可供
I I 孛 技 术
f 五
设计开发
基于 v 4 L 2 的视频采集系统的设计与实现
毛 业 进 孙 文 华
( 1 . 重庆信科设计有限公 司 重庆 4 0 1 1 2 1 ; 2 . 重庆邮电大学, 通信新技术应 用研究所 重庆 4 0 0 0 6 5 )
V4L2的视频开发
编写基于V4L2视频驱动主要涉及到以下几个知识点:● 摄像头方面的知识要了解选用的摄像头的特性,包括访问控制方法、各种参数的配置方法、信号输出类型等。
● Camera解码器、控制器如果摄像头是模拟量输出的,要熟悉解码器的配置。
最后数字视频信号进入camera控制器后,还要熟悉camera控制器的操作。
● V4L2的API和数据结构编写驱动前要熟悉应用程序访问V4L2的方法及设计到的数据结构。
● V4L2的驱动架构最后编写出符合V4L2规范的视频驱动。
本文介绍基于S3C2440硬件平台的V4L2视频驱动开发。
摄像头采用OmniVision公司的OV9650和OV9655。
主要包含以下几个方面的内容:视频驱动的整体驱动框架● 3C2440 camera控制器+ov9650(ov9655)● V4L2 API及数据结构● V4L2驱动框架● ov9650(ov9655)+s3c2440+V4L2实例一、视频驱动的整体框架视频驱动的整体框架见下图:二、S3C2440 camera控制器+ov9650(ov9655)(1)S3C2440 camera控制器介绍S3C2440支持ITU-R BT601/656格式的数字图像输入,支持的2个通道的DMA,Preview通道和Codec通道,参见下图。
Preview通道可以将YCbCr4:2:2格式的图像转换为RGB(16bit或24bit)格式的数据,并存放于为Preview DMA分配的内存中,最大分辨率为640*480。
主要用于本地液晶屏显示。
如果将Preview DMA的内存和Framebuffer内存重叠的话,就可以实现采集直接输出到液晶屏上了。
Codec通道可以输出YCbCr4:2:0或YCbCr4:2:2格式到为Codec DMA分配的内存中。
最大分辨率为4096*4096。
主要用于图像的编解码处理。
上图中的window cut功能是指在图像可以先做一个裁剪。
V4L2视频采集实现例程
V4L2视频采集实现例程V4L2视频采集实现例程Video4linux 简介Video4Linux是为市场现在常见的电视捕获卡和并⼝及USB⼝的摄像头提供统⼀的编程接⼝。
同时也提供⽆线电通信和⽂字电视⼴播解码和垂直消隐的数据接⼝。
本⽂主要针对USB摄像头设备⽂件/dev/video0,进⾏视频图像采集⽅⾯的程序设计。
Video4linux 编程指南1.视频编程的流程(1)打开视频设备:(2)读取设备信息(3)更改设备当前设置(可以不做)(4)进⾏视频采集,两种⽅法:a.内存映射b.直接从设备读取(5)对采集的视频进⾏处理( 本程序没做,下次再show给⼤家)(6)关闭视频设备//下⾯我对这些操作做了个简单的函数封装#ifndef _V4L_H#define _V4L_H#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <error.h>#include <assert.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/mman.h>#include <linux/videodev.h>#include <sys/types.h>#include <string.h>/*采集的图像的最⼤长和宽*/#define MAX_WIDTH 400#define MAX_HEIGHT 300/*设备⽂件*/#define DEFAULT_DEVICE “/dev/video0″/*⾃定义数据结构,包含v4l 中⽤到的数据结构*/typedef struct v4l_struct{int fd;/*设备号*/struct video_capability capability; //包含设备的基本信息(设备名称、⽀持的最⼤最⼩分辨率、信号源信息等)struct video_channel channel[8];//信号源个数struct video_picture picture;//设备采集的图象的各种属性struct video_mmap mmap;//⽤于mmapstruct video_mbuf mbuf;//利⽤mmap进⾏映射的帧的信息unsigned char *buffer ;/*图像数据存放区*/unsigned char *map;/*mmap⽅式获取数据时,数据的⾸地址*/int frame_current;int frame_using[2]; /*这个帧的状态0 表⽰可⽤,1表⽰不可⽤*/}v4l_device;/*************************************************************** 函数名:v4l_open* 功 能: 打开设备* 输 ⼊: dev,vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功**************************************************************/int v4l_open( char *dev, v4l_device *vd ){if( !dev ){dev=DEFAULT_DEVICE ;}if( ( vd->fd = open( dev, O_RDWR ) ) < 0 ){perror( “v4l_open error” );return -1;}return 0;}/*************************************************************** 函数名: v4l_get_capability* 功 能: 获取设备属性* 输 ⼊: vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功**************************************************************/int v4l_get_capability( v4l_device *vd ){if( ioctl( vd->fd, VIDIOCGCAP, &( vd->capability ) ) <0 ) {perror( “v4l_get_capability” );return -1 ;}return 0;}* 功 能:获取图⽚属性* 输 ⼊: vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功***************************************************************/int v4l_get_picture( v4l_device *vd ){if( ioctl( vd->fd,VIDIOCGPICT,&( vd->picture ) ) < 0 ){return -1;}return 0;}/*************************************************************** 函数名: v4l_set_picture* 功 能: 设置图⽚属性* 输 ⼊: vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功**************************************************************/int v4l_set_picture( v4l_device *vd ){if( ioctl( vd->fd, VIDIOCSPICT, &( vd->picture ) ) < 0 ){return -1;}return 0;}/************************************************************** 函数名:v4l_get_channels* 功 能:获取通道信息* 输 ⼊: vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功*************************************************************/int v4l_get_channels( v4l_device *vd ){int i;for( i=0;i < vd->capability.channels ; i++ ){vd->channel[i].channel = i; //确定通道if( ioctl( vd->fd , VIDIOCGCHAN, &( vd->channel[i] ) ) <0 ) {perror( “v4l_get_channel” );return -1;}}return 0;}* 功 能: 获取内存映射信息* 输 ⼊: vd* 输 出: ⽆* 返 回: -1—-失败 0—-成功**************************************************************/int v4l_get_mbuf( v4l_device *vd ){if( ioctl ( vd->fd,VIDIOCGMBUF,&( vd->mbuf ) ) <0 ){perror( “get_mbuf:” );return -1;}if ( ( vd->map = ( unsigned char * )mmap( 0, vd->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd, 0 ) ) < 0 ) {perror(“v4l_mmap_init:mmap”);return -1;}return 0 ;}/************************************************************** 函数名: v4l_init_mbuff* 功 能: 初始化内存映射信息* 输 ⼊: vd* 输 出: ⽆* 返 回: 0—-成功**************************************************************/int v4l_init_mbuf(v4l_device *vd){//vd->mmap.frame = 10 ; //不懂双帧是怎样设置的这个frame 该是当前帧的可mbuf 以⼜没有设置怎么确定是双帧不是单帧还是更多vd->mmap.width = MAX_WIDTH;vd->mmap.height = MAX_HEIGHT;vd->mmap.format = vd->picture.palette;vd->frame_current = 0;vd->frame_using[0] = 0;vd->frame_using[1] = 0;return 0;}/*************************************************************** 函数名: v4l_get_address* 功 能: 获取数据在图像的地址***************************************************************/unsigned char *v4l_get_address(v4l_device *vd){return (vd->map + vd->mbuf.offsets[vd->frame_current]);}* 功 能: 捕获帧**************************************************************/int v4l_grab_frame(v4l_device *vd, int frame){if (vd->frame_using[frame]){fprintf(stderr, “v4l_grab_frame: frame %d is already used.\n”, frame); return -1;}vd->mmap.frame = frame;if ( ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap ) ) < 0 ){perror( “v4l_grab_frame” );return -1;}vd->frame_using[frame] = 1;vd->frame_current = frame;return 0;}/*************************************************************** 函数名: v4l_grab_sync* 功 能:与内存映射捕获⼀致**************************************************************/int v4l_grab_sync(v4l_device *vd){if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0){perror(“v4l_grab_sync”);}vd->frame_using[vd->frame_current] = 0;return 0;}/**************************************************************** 函数名: v4l_munmap* 功 能:停⽌内存映射***************************************************************/int v4l_munmap( v4l_device *vd ){if ( munmap( vd->map, vd->mbuf.size ) < 0 ){perror( “v4lmunmap:munmap” );return -1;}return 0;}* 函数名: v4l_close* 功 能:关闭设备***************************************************************/int v4l_close(v4l_device *vd){close(vd->fd);return 0;}#endif//简单的封装了关于SDL的相关操作”ScreenSurface.h”#ifndef SCREEN_SURFACE_H#define SCREEN_SURFACE_H#include <stdio.h>#include <stdlib.h>#include <SDL/SDL.h>class ScreenSurface{public:ScreenSurface();bool screen_init(int w, int h, int b = 0, Uint32 f = 0);//初始化 ~ScreenSurface();SDL_Surface* point() const;int screen_lock();void screen_unlock();void screen_quit();void screen_set_caption( const char *str );//设置标题bool flip( unsigned char * src) ;//显⽰int startTV();//开始采集private:static int screenNum;int width;int height;int bpp;Uint32 flags;SDL_Surface* pScreen;};#endif//ScreenSurface.cpp#include “ScreenSurface.h”#include “qt_v4l.h”v4l_device v4l_dev;/*************************************************************** 函数名: v4l_grab_movie* 功 能:捕获连续图像**************************************************************/void v4l_grab_movie(){v4l_grab_frame(&v4l_dev, v4l_dev.frame_current);/*获取下⼀ 帧*/v4l_grab_sync(&v4l_dev);/*等待传完⼀ 帧*/v4l_dev.buffer = v4l_get_address(&v4l_dev);/*得到这⼀帧的地址*/v4l_dev.frame_current = (v4l_dev.frame_current+1)%2; /* 下⼀帧的frame*/ }//构造函数。
v4l2 framebuffer 按键中断 视频采集
触发方式
Байду номын сангаас
设备名
dev_id
ev_press=0时休眠
休眠之后,被唤醒从 此处继续往下执行
主要 用在 释放 中断 时确 定释 放哪 一个
设备驱动程序的接口
关闭设备时用到这个函数
copy_to_user(void __user *to, const void *from, unsigned long n) To 目标地址,这个地址是用户空间的地址 From 源地址,这个地址是内核空间的地址 由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内 核空间的复制,函数copy_from_user()完成内核空间到用户空间的复制 如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数
在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键 值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键 值传递到用户态;当没有按 键按下时,系统并不会轮询按键状态,以节省时钟资源
引脚结构体
中断服务程序
系统函数 可以读出引脚的值
注册中断处理函数
中断号
中断处理函数
class_device_create
class_create()
ioremap()
将YUV格式的视频数据转换成RGB32格式
select( nfds, readfds, writefds, exceptfds,timeout); nfds:是一个整数值,是指集合中所有文件描述符 的范围,即所有文件描述符的最大值加1 readfds:(可选)指针,指向一组等待可读性检查 的套接口 writefds:(可选)指针,指向一组等待可写性检查 的套接口 exceptfds:(可选)指针,指向一组等待错误检查 的套接口 timeout:select()最多等待时间,对阻塞操作则为 NULL
Video for Linux Two(V4L2)
Video for Linux Two(V4L2) ——驱动编写指南原始翻译稿:Video4Linux2 (usr 技术社区 译文组)原文地址:The Video4Linux2整理:Tekkaman Ninja2012-8-17声明:本文是基于/网站上的经典系列文章《Video4Linux2》的翻译整理。
原始翻译来自usr 技术社区 译文组和雷宏亮的博文。
本文因个人学习需要顺手做整理、修正,发布此文档仅为方便广大Linux 爱好者。
T e kk a m an N in j a目录一、 API 介绍 ......................................................................................................................3 二、注册和open() ................................................................................................................4 1. 视频设备注册 ...............................................................................................................5 2. open() 和 release() .........................................................................................................5 三、 基本ioctl()处理 .............................................................................................................7 四、输入和输出 ..................................................................................................................9 1. 视频标准 .....................................................................................................................9 2. 输入 ..........................................................................................................................10 3. 输出 ..........................................................................................................................11 五、颜色与格式 ................................................................................................................12 1. 色域 ..........................................................................................................................12 2. 密集存储和平面存储 ....................................................................................................12 3. 四字符码 ...................................................................................................................13 4. RGB 格式 ..................................................................................................................13 5. YUV 格式 ..................................................................................................................14 6. 其他格式 ...................................................................................................................15 7. 格式描述 ...................................................................................................................15 六、 格式协商 ...................................................................................................................16 七、基本的帧I/O ..............................................................................................................19 1. read() 和 write() ..........................................................................................................19 2. 流参数 .......................................................................................................................19 八、流I/O ........................................................................................................................22 1. v4l2_buffer 结构体 .......................................................................................................22 2. 缓冲区设定 ................................................................................................................24 3. 将缓冲区映射到用户空间 ..............................................................................................25 4. 流I/O ........................................................................................................................26 九、 控制 .. (28)T e kk a m an N in j a一、 API 介绍笔者最近有机会写了一个用于“One Laptop Per C hild”项目中的摄像头驱动。
初识V4l2(二)-------浅析video_register_device
初识V4l2(⼆)-------浅析video_register_device在V4l2初识(⼀)中,我们已经知道当插上⼀个摄像头的时候,在uvc_driver.c中最终会调⽤函数video_register_device函数。
接下来我们就简要分析这个函数做了哪些事情,揭开其神秘⾯纱。
/* Register video devices. Note that if video_register_device fails,the release() callback of the video_device structure is *not* called, sothe caller is responsible for freeing any data. Usually that means thatyou call video_device_release() on failure. *///该函数的作⽤就是注册video devices,有⼀点注意,video_device_release函数需要开发者⼿动调⽤static inline int video_register_device(struct video_device *vdev,int type, int nr){return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);}就video_register_device函数中的形参进⾏说明:参数⼀:video_devide :即我们想要注册的video_device结构体参数⼆:type :要注册的device类型,其中包括:define VFL_TYPE_GRABBER 0图像采集设备,包括摄像头、调谐器define VFL_TYPE_VBI1 1从视频消隐的时间段取得信息的设备(1)#define VFL_TYPE_RADIO 2 ⽆线电设备#define VFL_TYPE_SUBDEV 3 视频传播设备#define VFL_TYPE_MAX 4参数三:nr:device node number0 == /dev/video0 1 == /dev/video1 ........ -1 == first free__video_register_device深⼊分析过程如下:/*** __video_register_device - register video4linux devices* @vdev: video device structure we want to register* @type: type of device to register* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...* -1 == first free)* @warn_if_nr_in_use: warn if the desired device node number* was already in use and another number was chosen instead.* @owner: module that owns the video device node** The registration code assigns minor numbers and device node numbers* based on the requested type and registers the new device node with* the kernel.** This function assumes that struct video_device was zeroed when it* was allocated and does not contain any stale date.** An error is returned if no free minor or device node number could be* found, or if the registration of the device node failed.** Zero is returned on success.** Valid types are** %VFL_TYPE_GRABBER - A frame grabber** %VFL_TYPE_VBI - Vertical blank data (undecoded)** %VFL_TYPE_RADIO - A radio card** %VFL_TYPE_SUBDEV - A subdevice*/int __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner){int i = 0;int ret;int minor_offset = 0;//次设备号的偏移量,对于不同类型的设备,次设备号的基数是不同的,最终minor = i + minor_offsetint minor_cnt = VIDEO_NUM_DEVICES;const char *name_base; //设备的名称,会根据传⼊的type来选择/* A minor value of -1 marks this video device as neverhaving been registered */vdev->minor = -1; //次设备号为-1,表明这个设备没有被注册/* the release callback MUST be present */if (WARN_ON(!vdev->release)) //如果video_device结构体中没有提供release函数,就会返回出错。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一般操作流程(视频设备):1. 打开设备文件。
intfd=open("/dev/video0",O_RDWR);2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入,一个视频设备可以有多个视频输入。
VIDIOC_S_INPUT,struct v4l2_input4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format5. 向驱动申请帧缓冲,一般不超过5个。
struct v4l2_requestbuffers6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。
mmap7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer8. 开始视频的采集。
VIDIOC_STREAMON9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。
VIDIOC_DQBUF10. 将缓冲重新入队列尾,这样可以循环采集。
VIDIOC_QBUF11. 停止视频的采集。
VIDIOC_STREAMOFF12. 关闭视频设备。
close(fd);常用的结构体(参见/usr/include/linux/videodev2.h):struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备struct v4l2_input input; //视频输入struct v4l2_standard std;//视频的制式,比如PAL,NTSCstruct v4l2_format fmt;//帧的格式,比如宽度,高度等struct v4l2_buffer buf;//代表驱动中的一帧v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_Bstruct v4l2_queryctrl query;//某一类型的控制struct v4l2_control control;//具体控制的值打开视频设备,设置视频设备属性及采集方式、视频数据处理,关闭视频设备,如下图所示:一、打开视频设备打开视频设备非常简单,在V4L2中,视频设备被看做一个文件。
使用open函数打开这个设备:1. 用非阻塞模式打开摄像头设备intcameraFd;cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);2. 如果用阻塞模式打开摄像头设备,上述代码变为:cameraFd = open("/dev/video0", O_RDWR);关于阻塞模式和非阻塞模式应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。
二、Linux视频设备驱动常用控制命令使用说明设置视频设备属性通过ioctl来进行设置,ioctl有三个参数,分别是fd, cmd,和parameter,表示设备描述符,控制命令和控制命令参数。
Linux 视频设备驱动接口V4L2支持的常用控制命令如下:1.控制命令VIDIOC_ENUM_FMT功能:获取当前视频设备支持的视频格式。
参数说明:参数类型为V4L2的视频格式描述符类型struct v4l2_fmtdesc返回值说明:执行成功时,函数返回值为0;struct v4l2_fmtdesc 结构体中的 .pixelformat和 .description 成员返回当前视频设备所支持的视频格式;使用举例:-------------------------------------------------------------------------------------------------struct v4l2_fmtdesc fmt;memset(&fmt, 0, sizeof(fmt));fmt.index = 0;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;while ((ret = ioctl(dev, VIDIOC_ENUM_FMT, &fmt)) == 0) {fmt.index++;printf("{ pixelformat = ''%c%c%c%c'', description = ''%s'' }/n",fmt.pixelformat& 0xFF, (fmt.pixelformat>> 8) & 0xFF,(fmt.pixelformat>> 16) & 0xFF, (fmt.pixelformat>> 24) & 0xFF,fmt.description);}---------------------------------------------------------------------------------------------------------------2.控制命令VIDIOC_QUERYCAP功能:查询视频设备的功能;参数说明:参数类型为V4L2的能力描述类型struct v4l2_capability ;返回值说明:执行成功时,函数返回值为0;函数执行成功后,struct v4l2_capability 结构体变量中的返回当前视频设备所支持的功能;例如支持视频捕获功能V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING等。
使用举例:-----------------------------------------------------------------------------------------------------------struct v4l2_capability cap;iret = ioctl(fd_usbcam, VIDIOC_QUERYCAP, &cap);if(iret< 0){printf("get vidieo capability error,error code: %d /n", errno);return ;}----------------------------------------------------------------------------------------------------------执行完VIDIOC_QUERYCAP命令后,cap变量中包含了该视频设备的能力信息,程序中通过检查cap中的设备能力信息来判断设备是否支持某项功能。
3.控制命令VIDIOC_S_FMT功能:设置视频设备的视频数据格式,例如设置视频图像数据的长、宽,图像格式(JPEG、YUYV格式);参数说明:参数类型为V4L2的视频数据格式类型struct v4l2_format ;返回值说明:执行成功时,函数返回值为0;使用举例:----------------------------------------------------------------------------------------------------------struct v4l2_format tv4l2_format;tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;tv4l2_format.fmt.pix.width = img_width;tv4l2_format.fmt.pix.height = img_height;tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED;iret = ioctl(fd_usbcam, VIDIOC_S_FMT, &tv4l2_format);-----------------------------------------------------------------------------------------------------------注意:如果该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的图像格式,所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取struct v4l2_format结构体变量。
4.控制命令VIDIOC_REQBUFS功能:请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存),V4L2是视频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBUFS控制命令字申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。
参数说明:参数类型为V4L2的申请缓冲区数据结构体类型struct v4l2_requestbuffers ;返回值说明:执行成功时,函数返回值为0;V4L2驱动层分配好了视频缓冲区;使用举例:-----------------------------------------------------------------------------------------------------------struct v4l2_requestbuffers tV4L2_reqbuf;memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));tV4L2_reqbuf.count = 1; //申请缓冲区的个数tV4L2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;tV4L2_reqbuf.memory = V4L2_MEMORY_MMAP;iret = ioctl(fd_usbcam, VIDIOC_REQBUFS, &tV4L2_reqbuf);--------------------------------------------------------------------------------------------------------------注意:VIDIOC_REQBUFS会修改tV4L2_reqbuf的count值,tV4L2_reqbuf的count值返回实际申请成功的视频缓冲区数目;5.控制命令VIDIOC_QUERYBUF功能:查询已经分配的V4L2的视频缓冲区的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等。