Linux下v4l2采集编程笔记

来源:互联网 发布:读圣经软件 编辑:程序博客网 时间:2024/05/17 04:08

Linuxv4l2采集编程笔记

采集图片

1打开视频设备文件

STATUS open_dev()

{

         int fd;

         fd = open("/dev/video0",O_RDWR);

         if(fd<0)

         {

                   perror("open video device faild:");

                   exit(-1);

         }

         else

         printf("open :sucess!\n");

         return fd;

}

2获取设备相关信息:

在结构体:v4l2_capability

         type定义:

定义一个结构体变量cap

通过ioctl()函数:

         SYNOPSIS

       #include <sys/ioctl.h>

 

       int ioctl(int d, int request, ...);

第二个参数从上面填写;比如我们要获取的是设备的信息,结构体是v4l2_cappability那么应该是 VIDIOC_QUERYCAP

         代码:

/*

 *    获取设备相关属性*

 *

 */

void get_dev_info(int fd)

{

         struct v4l2_capability cap;

         int ret;

         ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);

         if(-1 == ret )

         {

                   perror("v4l2_capability ioctl faild:");

                   exit(-1);

         }

         /*成功绑定,获取到信息*/

         printf("---------------------------------\n");

         printf("采集摄像头相关信息:\n");

         printf("   :%s\n",cap.driver);

         printf("   :%s\n",cap.card);

         printf("接口信息:%s\n",cap.bus_info);

         printf("   :%x\n",cap.version);

         printf("capabilities: 0x%08X\n",cap.capabilities);

 

        

         //printf("reserved:%d\n",cap.reserved);

        

         printf("---------------------------------\n");

        

}

3设置视频的制式

Viodeodev.h 629-729行:

/*

 *      A N A L O G   V I D E O   S T A N D A R D模拟视频标准

 */

 

typedef __u64 v4l2_std_id;

 

/* one bit for each */

#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)

#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)

#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)

#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)

#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)

#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)

#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)

#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)

 

#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)

#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)

#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)

#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)

 

#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)

#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000)

#define V4L2_STD_NTSC_443       ((v4l2_std_id)0x00004000)

#define V4L2_STD_NTSC_M_KR      ((v4l2_std_id)0x00008000)

 

#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)

#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)

#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)

#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)

#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)

#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)

#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)

#define V4L2_STD_SECAM_LC       ((v4l2_std_id)0x00800000)

 

/* ATSC/HDTV */

#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)

#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)

 

/* FIXME:

   Although std_id is 64 bits, there is an issue on PPC32 architecture that

   makes switch(__u64) to break. So, there's a hack on v4l2-common.c rounding

   this value to 32 bits.

   As, currently, the max value is for V4L2_STD_ATSC_16_VSB (30 bits wide),

   it should work fine. However, if needed to add more than two standards,

   v4l2-common.c should be fixed.

 */

 

/* some merged standards */

#define V4L2_STD_MN  (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)

#define V4L2_STD_B       (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)

#define V4L2_STD_GH   (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)

#define V4L2_STD_DK    (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)

 

/*  一些常见的需要的东西 */

#define V4L2_STD_PAL_BG             (V4L2_STD_PAL_B            |\

                                      V4L2_STD_PAL_B1        |\

                                      V4L2_STD_PAL_G)

#define V4L2_STD_PAL_DK             (V4L2_STD_PAL_D           |\

                                      V4L2_STD_PAL_D1        |\

                                      V4L2_STD_PAL_K)

#define V4L2_STD_PAL          (V4L2_STD_PAL_BG         |\

                                     V4L2_STD_PAL_DK        |\

                                     V4L2_STD_PAL_H          |\

                                     V4L2_STD_PAL_I)

#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M         |\

                                      V4L2_STD_NTSC_M_JP     |\

                                      V4L2_STD_NTSC_M_KR)

#define V4L2_STD_SECAM_DK      (V4L2_STD_SECAM_D    |\

                                      V4L2_STD_SECAM_K    |\

                                      V4L2_STD_SECAM_K1)

#define V4L2_STD_SECAM              (V4L2_STD_SECAM_B     |\

                                      V4L2_STD_SECAM_G   |\

                                      V4L2_STD_SECAM_H   |\

                                      V4L2_STD_SECAM_DK |\

                                      V4L2_STD_SECAM_L       |\

                                      V4L2_STD_SECAM_LC)

 

#define V4L2_STD_525_60              (V4L2_STD_PAL_M          |\

                                      V4L2_STD_PAL_60         |\

                                      V4L2_STD_NTSC             |\

                                      V4L2_STD_NTSC_443)

#define V4L2_STD_625_50              (V4L2_STD_PAL                 |\

                                      V4L2_STD_PAL_N          |\

                                      V4L2_STD_PAL_Nc        |\

                                      V4L2_STD_SECAM)

#define V4L2_STD_ATSC           (V4L2_STD_ATSC_8_VSB    |\

                                      V4L2_STD_ATSC_16_VSB)

 

#define V4L2_STD_UNKNOWN        0

#define V4L2_STD_ALL            (V4L2_STD_525_60   |\

                                      V4L2_STD_625_50)

 

struct v4l2_standard {

         __u32                     index;

         v4l2_std_id          id;

         __u8                       name[24];

         struct v4l2_fract    frameperiod; /* Frames, not fields */

         __u32                     framelines;

         __u32                     reserved[4];

};

根据上图选择模拟视频标准制式;我们这里选择V4L2_STD_PAL

通过自定义类型定义一个变量 stdid= V4L2_STD_PAL

Ioctl();设置制式;

 

 

 

4设置帧格式

/*

v4l2_format 结构如下:

struct v4l2_format

{

enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

union

{

struct v4l2_pix_format pix;

struct v4l2_window win;

struct v4l2_vbi_format vbi;

__u8 raw_data[200];

} fmt;

};

struct v4l2_pix_format

{

__u32 width; // 宽,必须是16 的倍数

__u32 height; // 高,必须是16 的倍数

__u32 pixelformat; // 视频数据存储类型,例如是YUV 4 :2 :2 还是RGB

enum v4l2_field field;

__u32 bytesperline;

__u32 sizeimage;

enum v4l2_colorspace colorspace;

__u32 priv;

};

*/

/*

 *设置视频帧格式 帧的格式个包括宽度和高度等。

 *

 */

void set_frame_format(int fd)

{

         int ret;

         struct v4l2_fmtdesc fmtdesc;

         memset(&fmtdesc,0,sizeof(fmtdesc));

         fmtdesc.index = 0;

         fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

         while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)) == 0)

         {

                   fmtdesc.index ++ ;

 

 

         /*     printf("{pixelformat = %c%c%c%c},description = '%s'\n",

                                     fmtdesc.pixelformat & 0xff,(fmtdesc.pixelformat >> 8)&0xff,

                                     (fmtdesc.pixelformat >> 16) & 0xff,(fmtdesc.pixelformat >> 24)&0xff,

                                     fmtdesc.description);

                                     */

         }

 

         struct v4l2_format fmt;

         //以下初始化结构体变量fmt

         memset(&fmt , 0 , sizeof(fmt));//0

         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

         fmt.fmt.pix.width = 640;

         fmt.fmt.pix.height = 480;

         fmt.fmt.pix.pixelformat =V4L2_PIX_FMT_MJPEG;

         fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

        

         if((ioctl(fd,VIDIOC_S_FMT,&fmt))<0)

         {

                   perror("设置帧格式:");

                   //return -1;

         }

         else

         printf("设置帧格式成功(JPEG)......\n");

        

        

}

 

5申请帧缓冲

 

01551 :#define VIDIOC_REQBUFS            _IOWR('V',  8, struct v4l2_requestbuffers)

 

/*

 *向驱动申请帧缓冲

 *

 */

void get_frame_format(int fd)

{

         int ret;

         struct v4l2_requestbuffers req;

         memset(&req , 0 ,sizeof(req));

         req.count = 10;

         req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

         req.memory = V4L2_MEMORY_MMAP;

        

         ret = ioctl(fd,VIDIOC_REQBUFS,&req);

         if(ret<0)

         {

                   printf("get video sys faild\n");

                   //return -1;

         }

        

         n_buffer = req.count;//传给全局变量,申请内存的时候需要用到呢

        

         usr_buf = calloc(req.count,sizeof(*usr_buf));

        

         printf("n_buf:%d\n",n_buffer);

}

6申请物理内存并映射到申请的设备内存中

         那么应用程序直接操作物理内存就等译操作申请到的设备内存

01552#define VIDIOC_QUERYBUF               _IOWR('V',  9, struct v4l2_buffer)

 

         映射内存

  void *mmap(void *addr, size_t length, int prot, int flags,

                  int fd, off_t offset);

        

/*

 *申请内存(物理)

 *

 *///

 

 

STATUS mmap_memory(int fd)

{

         int ret;

         int i;

         memset(&buf , 0 ,sizeof(buf));

         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

         buf.memory = V4L2_MEMORY_MMAP;

        

         for(i = 0; i < n_buffer ; i++)

         {

        

                   buf.index = i; //下标

        

                  

                   ret = ioctl(fd , VIDIOC_QUERYBUF ,&buf);

                   if(-1 == ret)

                   {

                            printf("set video sys faild\n");

                            return FALSE;

                   }

                  

                   //内存映射:

                   usr_buf[i].length = buf.length;

                   usr_buf[i].start = mmap(NULL , buf.length , PROT_READ|PROT_WRITE, MAP_SHARED , fd , buf.m.offset);

                           

                   if (usr_buf[i].start == MAP_FAILED)

                   {

                            perror("buffers error\n");

                            return FALSE;

                   }

                  

                   if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)

                   {

                            printf("VIDIOC_QBUF error\n");

                            return FALSE;

                   }

        

        

         }//end of for

        

         return TRUE;

}

 

7开始采集视频:

 

01558#define VIDIOC_STREAMON              _IOW('V', 18, int)

//代码:

void mmap_memory(int fd)

{

         int ret;

         int i;

         memset(&buf , 0 ,sizeof(buf));

         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

         buf.memory = V4L2_MEMORY_MMAP;

        

         for(i = 0; i < n_buffer ; i++)

         {

        

                   buf.index = i; //下标

        

                  

                   ret = ioctl(fd , VIDIOC_QUERYBUF ,&buf);

                   if(-1 == ret)

                   {

                            printf("set video sys faild\n");

                            //return -1;

                   }

                  

                   //内存映射:

                   usr_buf[i].length = buf.length;

                   usr_buf[i].start = mmap(NULL , buf.length , PROT_READ|PROT_WRITE, MAP_SHARED , fd , buf.m.offset);

                           

                   if (usr_buf[i].start == MAP_FAILED)

                   {

                            perror("buffers error\n");

                            //return -1;

                   }

                   //映射内存后要进行一次入列的操作

                   if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)

                   {

                   printf("VIDIOC_QBUF error\n");

                   //return -1;

                   }

        

        

         }//end of for

        

         printf("1111index = %d\n",buf.index);

}

 

 

 

 

8保存图像:

int save_image(void *addr,int length)

{

         FILE *fp;

         int num=0;

         char picture_name[20];

         sprintf(picture_name,"picture%d.jpg",pic_num++);

                  

         fp = fopen(picture_name,"w");

         if(NULL == fp)

         {

                   perror("Open picture_name faild!");

                   exit(-1);

         }

         fwrite(addr,length,1,fp);

         usleep(100);

 

         fclose(fp);        

         return 0;

}

放进队列

/*

 *入列

 */

STATUS into_query(int fd)

{

         if(-1 == (ioctl(fd,VIDIOC_QBUF,&buf)))

         {

                   perror("Fail to ioctl 'VIDIOC_QBUF'");

                   return FALSE;

         }

取出队列

/*

 *

 *出队列取得已采集数据的帧缓冲,取得原始采集数据

 *

 */

STATUS get_query(int fd)

{

         int ret;

         if(-1 == (ioctl(fd,VIDIOC_DQBUF,&buf)))

         {

                   perror("Fail to ioctl 'VIDIOC_DQBUF'");

                   return FALSE;

         }

         return TRUE;

}

注意:保存的时候要通过出列,保存,入列的过程;这里通过封装一个函数实现

过程:先出列  保存  然后再如入列  

映射内存的完成后已经进行了入列的处理一次了!!!!

 

//读取一帧图片保存:

void read_framt(int fd)

 {

        // 出列

        printf("初始下标:index= %d\n",buf.index);//4

        if(-1 == ioctl(fd,VIDIOC_DQBUF,&buf))

        {

                  perror("Fail to ioctl 'VIDIOC_DQBUF'");

                  exit(EXIT_FAILURE);

        }

 

         //assert(buf.index < n_buffer);

        //保存

        save_image(usr_buf[buf.index].start,usr_buf[buf.index].length);

        //入列

        printf("保存下标为 :index= %d\n",buf.index);

        if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))

        {

                  perror("Fail to ioctl 'VIDIOC_QBUF'");

                  exit(EXIT_FAILURE);

        }

 

}

 

 

 

9关闭流:

void set_off_stream(int fd)

{

         int ret;

         enum v4l2_buf_type type;

         type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;

        

         ret = ioctl(fd, VIDIOC_STREAMOFF ,&type);

         if (ret == -1)

         {

                            perror("streamoff error\n");

                            //return -1;

         }

 

}

 

10关闭映射释放申请的内存

void uninit_camer_device()

{

         unsigned int i;

 

 

         for(i = 0;i < n_buffer;i ++)

         {

                   if(-1 == munmap(usr_buf[i].start,usr_buf[i].length))

                   {

                            exit(EXIT_FAILURE);

                   }

         }

        

         free(usr_buf);

}

11关闭设备:

close_dev(dev_fd);

 

 

 

捕获图片简单过程图

 

 

 

有关保存的时候出列的操作分析:

我们假设我们的申请的设备缓冲区count5

那么也就是说我们的换冲队列的大小也是5帧大小

 

在映射的时候已经进行了一次的入列操作,那么我们的下标默认开始是顶部了也就是4

 

 

分析过程流程图:

 

 

采集视频流格式:

压缩:YUYV-àjpeg

1设置帧格式的参数:

改为fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

 

2安装jpeg

·准备:需要安装第三方库jpeg;在开发板上运行的话需要交叉编译jpeg库;

·测试:pc   安装jpeg   移植jpeg库到板子

 

3添加头文件:#include <jpeglib.h>

 

4修改makefile,因为使用了第三方库

 

 

5压缩函数如下:(在读取帧的函数中可以读取  一样的顺序出列---处理压缩(保存)---入列)

/*

 *压缩   YUYV JPEG

 */

//绿色的为测试保存在当前目录的文件,实际上我们需要通过第二个参数的buf指针指向的申请的缓冲区保存;可以是连续帧得以形成录像

int compress_yuyv_to_jpeg(BUFTYPE *frame_buf, unsigned char *buffer,  int size)

//frame_buf是指采集的流,buffer是指需要保存的位置;size是大小;

{

         unsigned char *line_buffer ,*yuyv ,*row_pointer[1];

         FILE *fp;//这里测试保存在当前目录的文件流指针

 

         /*1申请并初始化jpeg压缩对象,同时要指定错误处理器*/

         struct jpeg_compress_struct cinfo;         //定义一个压缩信息结构体

         struct jpeg_error_mgr jerr;                       //声明错误处理器,并赋值给 cinfo.err

 

         cinfo.err = jpeg_std_error(&jerr);

         //创建压缩对象,并初始化

         jpeg_create_compress(&cinfo);

 

         /*2指定输出数据的传输位置,传输数据大小 */

        //jpeg_mem_dest(&cinfo,&buffer,&size);// buffer 保存处理后的数据 size大小

         /*2 指定压缩后的图像所存放的目标文件,注意,目标文件应以二进制模式打开 */

        fp = fopen("test.jpeg","wb");

        if(NULL ==fp)

                  {

                           perror(" 2" );

                           return 0;

                  }

        jpeg_stdio_dest(&cinfo,fp);

 

         /*设置压缩参数*/

 

         cinfo.image_height = PIX_HEIGHT;

         cinfo.image_width = PIX_WIDTH;

         cinfo.input_components = 3; //3//3表示3原色   1表示灰色

         cinfo.in_color_space =  JCS_RGB; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像

 

        

         jpeg_set_defaults(&cinfo);               //将设置的参数加入到默认参数中;

         jpeg_set_quality(&cinfo,80,TRUE);//设置压缩质量,压缩比在0-100间;true表示压缩 false表示不压缩

 

         /*4 开始压缩*/

         jpeg_start_compress(&cinfo,TRUE);

 

         //JSAMPARRAY row_pointer[1];//一行位图

 

         int z;

         z = 0;

         //对每一行进行压缩

         line_buffer = calloc(PIX_WIDTH*3,1);//每一行字节数

         yuyv = frame_buf->start;

        

         while(cinfo.next_scanline < cinfo.image_height)

                   {

                            int x;

                            unsigned char *ptr = line_buffer;//yuyv数据赋值给ptr指针

                            printf("compress start ......\n");

                            /*YUYVRGB格一行一行处理*/

                            for(x = 0 ;x < PIX_WIDTH ; x++)

                                     {

                                               int r,g,b;

                                               int y,u,v;

                                               if ( !z )

                                                y = yuyv[0] << 8;

                                               else

                                                y = yuyv[2] <<8;

                                               u = yuyv[1] - 128;

                                               v = yuyv[3] - 128;

 

 

                                               r = (y + (359 * v)) >>8;

                                               g = (y - (88 * u) -(183 * v)) >>8; 

                                               b = (y+(454* u )) >> 8;

 

                                               *(ptr ++) = (r > 255)  ? 255 : ((r<0) ? 0 : r);

                                               *(ptr ++) = (g > 255) ? 255 : ((g<0) ? 0 : g);

                                               *(ptr ++) = (b > 255) ? 255 : ((b<0) ? 0 : b);

 

                                               if (z ++)

                                                        {

                                                                 z = 0;

                                                                 yuyv +=4;

                                                        }

                                     }

 

                            row_pointer[0] = line_buffer;

                            jpeg_write_scanlines(&cinfo , row_pointer , 1);

                           

                   }

        

         jpeg_finish_compress(&cinfo);

         jpeg_destroy_compress(&cinfo);

        

         free(line_buffer);    

 

         return 0;

}

原创粉丝点击