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:获取设备的控制参数该参数用于获取视频设备的各种控制参数的当前值。
- 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的视频缓冲区的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等。