基于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采集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(&param, 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,&param)e)获取帧率,查看设置是否生效get_v4l2_param(void)struct v4l2_streamparm param;memset(&param, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, &param)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(&param, 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,&param) < 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(&param, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, &param) < 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采集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(&param, 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,&param)e)获取帧率,查看设置是否生效get_v4l2_param(void)struct v4l2_streamparm param;memset(&param, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, &param)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(&param, 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,&param) < 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(&param, 0, sizeof(struct v4l2_streamparm));param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, &param) < 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坐标。

基于V4L2的视频采集模块设计

基于V4L2的视频采集模块设计

【 关键词】 : V 4 L 2 ; 视频采集 ; A P I
1引言
像数 据 复制 到预 先 设定 好 的数据 缓 冲 区 中 , 就可 以实
V i d e o f o r L i n u x t w 0 f V i d e o 4 L i n u x 2 1 简称 V 4 L 2 , 是 现对 每 帧 图像数 据 的读 取 。用 户指 针 方 式 , 首 先 由应
程之 间通 过 映射 同一 文件 实 现共 享 内存 , 各个 进 程可 视频 采 集 、 关 闭视 频 设备 , 具 体 操 作通 过 i o c t l 等 函 数 以像 访 问普通 内存一 样对 文 件进 行访 问 , 访 问时 只需 来实 现 。操 作 流程 如下 : 要使用 指 针而 不用 调用文 件操 作 函数 。直接 读取 方式 ( r e a d ) , 通过 调 用 r e a d具有 阻塞 功能 的 函数 , 将 输 出 图
集 图片 、 视 频 和 音频 数 据 的 A P I 接 口, 配 合 适 当 的视 式 的视频采 集 。
频采 集 设备 和相 应 的驱动 程 序 , 可 以实 现 图片 、 视频、
应用程序通过 V 4 L 2接 口采 集 视 频 数 据 分 为 五 ( 1 ) 打 开视 频 设 备 文 件 , 进 行 视 频采 集 的 参 数初
( 4 ) 驱动 开 始 视 频 数 据 的采 集 , 应 用程 序 从 视 频 采 集 输 出 队列 取 出帧 缓冲 区 , 处 理 完后 , 将 帧 缓 冲 区
重新 放入 视频 采 集输 入 队列 , 循 环 往 复采 集连 续 的视 ( 5 ) 停 止视 频采 集 。
3用 V 4 L 2采 集视频 的程序流 程 和相关 AP I

V4L2视频采集程序框架

V4L2视频采集程序框架

V4L2应用程序框架V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移。

更好地了解V4L2先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。

本文先就V4L2在视频捕捉或camera方面的应用框架。

V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioct l函数来实现。

1.打开视频设备在V4L2中,视频设备被看做一个文件。

使用open函数打开这个设备:// 用非阻塞模式打开摄像头设备int cameraFd;cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);// 如果用阻塞模式打开摄像头设备,上述代码变为://cameraFd = open("/dev/video0", O_RDWR, 0);应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。

2. 设定属性及采集方式打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。

这一步是可选的。

在Linux编程中,一般使用ioctl 函数来对设备的I/O通道进行管理:int ioctl (int __fd, unsigned long int __request, .../*args*/) ;在进行V4L2开发中,常用的命令标志符如下(some are optional):∙VIDIOC_REQBUF S:分配内存∙VIDIOC_QUERYBUF:把VIDIOC_REQBUF S中分配的数据缓存转换成物理地址∙VIDIOC_QUERYCAP:查询驱动功能∙VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式∙VIDIOC_S_FMT:设置当前驱动的频捕获格式∙VIDIOC_G_FMT:读取当前驱动的频捕获格式∙VIDIOC_TRY_FMT:验证当前驱动的显示格式∙VIDIOC_CROPCAP:查询驱动的修剪能力∙VIDIOC_S_CROP:设置视频信号的边框∙VIDIOC_G_CROP:读取视频信号的边框∙VIDIOC_QBUF:把数据从缓存中读取出来∙VIDIOC_DQBUF:把数据放回缓存队列∙VIDIOC_STREAMON:开始视频显示函数∙VIDIOC_STREAMOFF:结束视频显示函数∙VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

[方案]基于Linux视频驱动接口V4L2视频采集编程

[方案]基于Linux视频驱动接口V4L2视频采集编程

[方案]基于Linux视频驱动接口V4L2视频采集编程Linux系统中,视频设备被当作一个设备文件来看待,设备文件存放在 /dev 目录下,完整路径的设备文件名为: /dev/video0 .视频采集基本步骤流程如下: 打开视频设备,设置视频设备属性及采集方式、视频数据处理,关闭视频设备,如下图所示:一、打开视频设备打开视频设备非常简单,在V4L2中,视频设备被看做一个文件。

使用open函数打开这个设备:1.用非阻塞模式打开摄像头设备int cameraFd;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;函数执行成功后,structv4l2_capability 结构体变量中的返回当前视频设备所支持的功能;例如支持视频捕获功能V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING等。

基于v4l2视频采集

基于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; } }

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就够了。

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 )
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档