V4L2下 mmap 多帧采集

V4L2下 mmap 多帧采集 

one_capture_v4l2.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
 
#include <getopt.h>            

#include <fcntl.h>             
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
 
#include <asm/types.h>         
#include <linux/videodev2.h>
 
#define CLEAR(x) memset (&(x), 0, sizeof (x))
 
struct buffer {
         void *                  start;
         size_t                  length;
 };
 
static char *           dev_name        = "/dev/video12";
static int              fd              = -1;
struct buffer *         buffers         = NULL;
 
FILE *file_fd;
static unsigned long file_length;
//static unsigned char *file_name;
 
int main (int argc,char ** argv)
 {
struct v4l2_capability cap; 
struct v4l2_format fmt;
	char fourcc[5] = {0, 0, 0, 0, 0};
 
file_fd = fopen("test.yuv", "wb");
 
fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
 
    int ff = ioctl (fd, VIDIOC_QUERYCAP, &cap);

    if(ff < 0)
        printf("failture VIDIOC_QUERYCAP\n");
		
    printf("\n****Capability informations****\n");  
    printf("driver:   %s\n", cap.driver);  
      
    if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)  
       printf("Capture capability is supported\n");  
  
    if (cap.capabilities & V4L2_CAP_STREAMING)   
        printf("Streaming capability is supported\n");
 
CLEAR (fmt);
fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width       = 720; 
fmt.fmt.pix.height      = 288;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;  //视频数据存储类型//V4L2_PIX_FMT_JPEG;//V4L2_PIX_FMT_YVU420;//V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

    ff = ioctl (fd, VIDIOC_S_FMT, &fmt);       //设置图像格式
    if (ff < 0)
        printf("failture VIDIOC_S_FMT\n");
		
	memmove(fourcc, &fmt.fmt.pix.pixelformat, 4);
	
	printf("Goint to capture frames %d x %d format: %4s framesize: %d\n",
				 fmt.fmt.pix.width,
				 fmt.fmt.pix.height,
				 fourcc,
				 fmt.fmt.pix.sizeimage);
 
file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;  //计算图片大小
 
buffers = calloc (1, sizeof (*buffers));
	if (!buffers) {
		perror("Can't allocate buffer!\n");
		return 1;
	}
 
buffers[0].length = file_length;
buffers[0].start = malloc (file_length);
 
for (;;)    //这一段涉及到异步IO
{
    fd_set fds;
    struct timeval tv; //timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待
    int r;
 
    FD_ZERO (&fds);  //将指定的文件描述符集清空
    FD_SET (fd, &fds);    //在文件描述符集合中增加一个新的文件描述符
 
   /* Timeout. */
    tv.tv_sec = 2;
    tv.tv_usec = 0;
	
	/*readset writeset exceptset指定我们要让内核测试读、写和异常条件的描述字。
	如果对某一个的条件不感兴趣,就可以把它设为NULL。
	如果三个指针都为NULL,我们就有了一个比sleep()函数更为精确的定时器
	(sleep()以秒为最小单位,这个以微妙为单位)。
	int select (int maxfdp1, fd_set *readset, fd_set *writeset, 
					fd_set *exceptset,
                	const struct timeval * timeout); */
	
   r = select (fd + 1, &fds, NULL, NULL, &tv); //判断是否可读(即摄像头是否准备好),tv是定时
 
   if (-1 == r) {
		if (EINTR == errno)
			continue;
			printf ("select");
    }
 /*
   if (0 == r) {
		fprintf (stderr, "select timeout\n");
		exit (EXIT_FAILURE);
    }
 */
   if (read (fd, buffers[0].start, buffers[0].length))//如果可读,执行read()函数,并跳出循环
    break;
}
 
	fwrite(buffers[0].start, buffers[0].length, 1, file_fd);
 
	free (buffers[0].start);
	close (fd);
	fclose (file_fd);
	exit (EXIT_SUCCESS);
	return 0;
 }


 

 

mainloop.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <getopt.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <asm/types.h>

#include <linux/videodev2.h>

#define CLEAR(x) memset(&(x),0,sizeof(x))

//定义一个缓存结构体
struct buffer
{
  void* start;     //空指针
  size_t length;   //长度大小
};

#define CAPTURE_FILE "frame.yuv"
#define COMB_FILE "frame1.yuv"
#define f_count 	10		//连续采集帧数
#define fwidth		720
#define fheight		288

static char* dev_name = "/dev/video12";   //设备名
static int fd = -1;              //文件序列
static struct buffer* buffers = NULL;    //缓存对象
static unsigned int n_buffers = 0;   //缓存数
 
static void errno_exit(const char* s)   //错误输出   
{
   fprintf(stderr,"%s error %d, %s\n",s,errno,strerror(errno));
      exit(EXIT_FAILURE);
}

static void process_image(void)   //处理获取图像
{
   // fputc('!',stdout);
 //fflush(stdout);
	struct v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;

		if(-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
			errno_exit("VIDIOC_QUERYBUF");

	size_t	flength = buf.length;   //设置映射方式为mmap
 printf("flength %d\n", flength);
 
	int f ;
	FILE *fp = fopen(CAPTURE_FILE, "r");	
	if (fp < 0) {
        printf("open frame data file failed\n");
        exit(EXIT_FAILURE);
    }
	FILE *fp1 = fopen(COMB_FILE, "w");
	if (fp1 < 0) {
        printf("open frame1 data file failed\n");
        exit(EXIT_FAILURE);
    }
	
	void *tmp = calloc(1, flength);
	fseek(fp, flength, SEEK_CUR);
	fread(tmp, flength, 1, fp);
	fwrite(tmp, flength, 1, fp1);
	/*
	for(f = 0; f < fheight; f++) {
	
		fread(tmp, fwidth, 1, fp);
		fwrite(tmp, fwidth, 1, fp1);
		fseek(fp, flength - fwidth, SEEK_CUR);
		
		fread(tmp, fwidth, 1, fp);
		fwrite(tmp, fwidth, 1, fp1);
		fseek(fp, -flength, SEEK_CUR);
		
	}
	for(f = 0; f < fheight * 2; f++) {
	
		fread(tmp, fwidth/2, 1, fp);
		fwrite(tmp, fwidth/2, 1, fp1);
		fseek(fp, (flength - fwidth)/2, SEEK_CUR);
		
		fread(tmp, fwidth/2, 1, fp);
		fwrite(tmp, fwidth/2, 1, fp1);
		fseek(fp, -flength, SEEK_CUR);
		
	}
	*/
	fclose(fp);
	fclose(fp1);
	
	//fwrite(p,length, 1, fp);
	//perror("fwrite error");

	//fclose(fp);
    printf("Combination one frame saved in %s\n", COMB_FILE);
}

static int read_frame(FILE *fp)   //读取数据帧,用的都是mmap
{
	struct v4l2_buffer buf;
	unsigned int i;
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;

	if(-1 == ioctl(fd, VIDIOC_DQBUF, &buf))   //读取
        errno_exit("VIDIOC_DQBUF");
 
    //fwrite(buffers[buf.index].start, buf.length, 1, fp);
	printf("buf->length %d\n", buf.length);
	fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, fp);

	if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))    //放回缓存
		errno_exit("VIDIOC_QBUF");
	return 1;
}

static void mainloop(void)
{
    unsigned int count;
	count = f_count;
	FILE *fp = fopen(CAPTURE_FILE, "w");

    if (fp < 0) {
        printf("open frame data file failed\n");
        exit(EXIT_FAILURE);
    }
	while(count-- > 0) {
		/*for (;;) { //这一段涉及到异步IO
            fd_set fds;
            struct timeval tv;
            int r;
            FD_ZERO (&fds);//将指定的?件描述符集清空
            FD_SET (fd, &fds);//在文件描述符集合中增鍔????个新的文件描述符
           // Timeout. 
            tv.tv_sec = 2;
            tv.tv_usec = 0;
            r = select (fd + 1, &fds, NULL, NULL, &tv);//判断是否可读(即摄像头是否准备好),tv是定时

            if (-1 == r) {
				if (EINTR == errno)
                     continue;
                printf ("select err\n");
            }
			if (0 == r) {
                fprintf (stderr, "select timeout\n");
                exit (EXIT_FAILURE);
            }
			if (read_frame (fp)) //如果可读,执行read_frame ()函数,并跳出循环
               break;
		}*/

		read_frame(fp);
		//printf("Capture count %s\n", count);
	}
	fclose(fp);
    printf("Capture all frames saved in %s\n", CAPTURE_FILE);
}

static void stop_capturing(void)  //停止捕获帧
{
	enum v4l2_buf_type type;
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	if(-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))  //关闭视频流
		errno_exit("VIDIOC_STREAMOFF");
	printf("\nStreamOff success!\n");
}

static void start_capturing(void)   //开始捕获帧
{
	unsigned int i;
	enum v4l2_buf_type type;
	/*将申请到的帧缓冲全部入队列,以便存放采集到的数据*/
	for(i = 0; i < n_buffers; i++) {
		struct v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = i;

		if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))   //放入缓存
			errno_exit("VIDIOC_QBUF");
	}

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	if(-1 == ioctl(fd, VIDIOC_STREAMON, &type))   //打开视频流
		errno_exit("VIDIOC_STREAMON");
	else printf("StreamOn success!\n");
}

static void uninit_device(void)   //释放存储空间
{
   unsigned int i;

   for(i = 0; i < n_buffers; i++) {
      if(-1 == munmap(buffers[i].start, buffers[i].length))
			errno_exit("munmap");
	}

   free(buffers);
}


static void init_mmap(void)   //初始化读取方式
{
	struct v4l2_requestbuffers req;
	CLEAR(req);

	req.count = 4;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;

	if(-1 == ioctl(fd, VIDIOC_REQBUFS, &req)) { //分配内存
		if(EINVAL == errno) {
			fprintf(stderr,"%s does not support memory mapping\n",dev_name);
			exit(EXIT_FAILURE);
		} else
			errno_exit("VIDIOC_REQBUFS");
	}

	buffers = calloc(req.count, sizeof(*buffers));   //分配缓存
	if(!buffers) {
		fprintf(stderr,"Out of memory\n");
		exit(EXIT_FAILURE);
	}

	for(n_buffers = 0; n_buffers < req.count; n_buffers++) {
		struct v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buffers;

		if(-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
			errno_exit("VIDIOC_QUERYBUF");

		buffers[n_buffers].length = buf.length;   //设置映射方式为mmap
	printf("buf.length %d", buffers[n_buffers].length);
		buffers[n_buffers].start = mmap(NULL,
										buf.length,
										PROT_READ | PROT_WRITE,
										MAP_SHARED,
										fd,
										buf.m.offset);

		if(MAP_FAILED == buffers[n_buffers].start)
			errno_exit("mmap");
	}
}

static void init_device(void)  //初始化设备
{
	struct v4l2_capability cap;
	struct v4l2_format fmt;
	unsigned int min;

	if(-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap)) {	//获取摄像头参数
		if(EINVAL == errno) {
			fprintf(stderr,"%s is no V4L2 device\n",dev_name);
			exit(EXIT_FAILURE);
		} else 
			errno_exit("VIDIOC_QUERYCAP");
	}

	if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
		fprintf(stderr,"%s is no video  capture device\n",dev_name);
        exit(EXIT_FAILURE);
	} else 
		printf("Capture capability is supported\n");

	if(!(cap.capabilities & V4L2_CAP_STREAMING)) {
		fprintf(stderr,"%s does not support streaming i/o\n",dev_name);
		exit(EXIT_FAILURE);
	} else 
		printf("Streaming capability is supported\n");
//
	CLEAR(fmt);       //设置帧格式
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width = 720;
	fmt.fmt.pix.height = 288;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; //V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

	if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt))
		errno_exit("VIDIOC_S_FMT");
	else
		printf("Set frame format successful!\n");
	
	init_mmap();

}

static void close_device(void)   //关闭设备
{
	if(-1 == close(fd))
		errno_exit("close");
	fd = -1;
	printf("Close the device successful!\n");
}

static void open_device(void)    //打开设备
{
	fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
	if(-1 == fd) {
		fprintf(stderr,"Cannot open %s\n",dev_name);
		exit(EXIT_FAILURE);
	}
   printf("Open the %s successful\n",dev_name);
}

int main(int argc,char* argv[])
{
   //dev_name = "/dev/video12";

   open_device();
   init_device();
   start_capturing();
   mainloop();
   stop_capturing();
   process_image();
   uninit_device();
   close_device();
   exit(EXIT_SUCCESS);
   return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值