v4l2打开相机获取流demo及命令打开相机设备

/*
打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲 区管理)-> 循环获取数据-> 关闭设备。
https://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html
*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define DEVNAME "/dev/video0"

// void signal_hander(int sig)
// {
//      printf("----------signal_hander---------\n");
//      //goto lable;
// }

int main()
{
    //signal(SIGINT,signal_hander);


   //1. 打开设备
   int fd=open(DEVNAME,O_RDWR); 
   if(fd < 0) {
      printf("open dev fail\n");
      return -1;
    }

    //2.查询设备属性: VIDIOC_QUERYCAP
    struct v4l2_capability cap;
    int request;
    int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if(ret < 0) {
        printf("ioctl fail\n");
    }
    printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n"
    ,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF);

    //3.设置视频的制式和帧格式(制式包括PAL,NTSC,帧的格式个包括宽度和高度等)。
    //3.1查询并显示所有支持的格式: VIDIOC_ENUM_FMT
        /*struct v4l2_fmtdesc
	{
		u32 index; // 要查询的格式序号,应用程序设置
		enum v4l2_buf_type type; // 帧类型,应用程序设置
		u32 flags; // 是否为压缩格式
		u8 description[32]; // 格式名称
		u32 pixelformat; // 格式
		u32 reserved[4]; // 保留
	};
	*/
    struct v4l2_fmtdesc fmtdesc; 
    fmtdesc.index=0; 
    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    printf("Support format:\n");
    while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
    {
         printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
         fmtdesc.index++;
    }

    //3.2配置摄像头采集格式
    //查看或设置当前格式: VIDIOC_G_FMT, VIDIOC_S_FMT
    /* 
	struct v4l2_format
	{
		enum v4l2_buf_type type; // 帧类型,应用程序设置
		union fmt
		{
			struct v4l2_pix_format pix; // 视频设备使用
			struct v4l2_window win;
			struct v4l2_vbi_format vbi;
			struct v4l2_sliced_vbi_format sliced;
			u8 raw_data[200];
		}; 
	}; 
	
	struct v4l2_pix_format
	{
		u32 width; // 帧宽,单位像素
		u32 height; // 帧高,单位像素
		u32 pixelformat; // 帧格式
		enum v4l2_field field;
		u32 bytesperline;
		u32 sizeimage;
		enum v4l2_colorspace colorspace;
		u32 priv;
	};
	*/

    struct v4l2_format vfmt; 
    vfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;          //设置类型摄像头采集
    #if 0
	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;   //根据摄像头设置格式
        vfmt.fmt.pix.width = 1280;
        vfmt.fmt.pix.height = 720;
    #else
    //vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;   //根据摄像头设置格式
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;   //根据摄像头设置格式
    //vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; //根据摄像头设置格式

    // vfmt.fmt.pix.width = 640;
    // vfmt.fmt.pix.height = 480;
    #endif

    ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
    if(ret < 0)
    {
        printf("---------设置类型摄像头采集失败--------\n");
        return -1;
    }
    printf("Current data format information:\n\twidth:%d\n\theight:%d\n", vfmt.fmt.pix.width,vfmt.fmt.pix.height);

#if 1
{
    struct v4l2_fmtdesc fmtdesc; 
    fmtdesc.index=0; 

    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    {
        if(fmtdesc.pixelformat & vfmt.fmt.pix.pixelformat)
        {
            printf("\tformat:%s\n",fmtdesc.description);
            break;
        }
        fmtdesc.index++;
    }
}
#endif

#if 0
    //3.3检查是否支持某种帧格式
    struct v4l2_format fmt; 
    fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32; 
    if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1) {
        if(errno==EINVAL) {
            printf("not support format RGB32!\n");
        }
    }
#endif

    //4.图像的缩放 VIDIOC_CROPCAP
    /*
    int ioctl(int fd, int request, struct v4l2_cropcap *argp);
    int ioctl(int fd, int request, struct v4l2_crop *argp);
    int ioctl(int fd, int request, const struct v4l2_crop *argp);
    */

   //5.VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 用来查询和选则当前的 input,一个 video 设备 节点可能对应多个视频源,
   //比如 saf7113 可以最多支持四路 cvbs 输入,如果上层想在四 个cvbs视频输入间切换,那么就要调用 ioctl(fd, VIDIOC_S_INPUT, &input) 来切换。
   //VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回当前的 video input和output的index.

    //6. 申请和管理缓冲区
    //4.申请内核空间
	//应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping) 和用户指针。
	//向设备申请缓冲区 VIDIOC_REQBUFS
	/*相关函数:int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
	相关结构体:
	struct v4l2_requestbuffers
	{
		u32 count; // 缓冲区内缓冲帧的数目
		enum v4l2_buf_type type; // 缓冲帧数据格式
		enum v4l2_memory memory; // 区别是内存映射还是用户指针方式
		u32 reserved[2];
	};
	enum v4l2_memory
	{
		V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR
	};
	//count,type,memory 都要应用程序设置
   */
    //6.1 向设备申请缓冲区 VIDIOC_REQBUFS
    //申请一个拥有四个缓冲帧的缓冲区
    struct v4l2_requestbuffers req; 
    req.count=4; //申请4个缓冲区
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; // 缓冲帧数据格式
    req.memory=V4L2_MEMORY_MMAP; //内存映射
    if(ioctl(fd,VIDIOC_REQBUFS,&req) < 0)
    {
        printf("-----申请队列空间失败-VIDIOC_REQBUFS-fail------\n");
    }
   
    //获取缓冲帧的地址,长度:VIDIOC_QUERYBUF
    // struct buffer
    // {
    //     void* start;
    //     unsigned int length;
    // }*buffers;
    // buffers = (struct buffer*)calloc (req.count, sizeof (*buffers));
    // if (!buffers) {
    //     // 映射
    //     printf("Out of memory\n");
    //     return -1;
    // }

    //把内核的缓冲区队列映射到用户空间
	//获取缓冲帧的地址,长度:VIDIOC_QUERYBUF
	//相关函数:int ioctl(int fd, int request, struct v4l2_buffer *argp);
	/*相关结构体:
	struct v4l2_buffer
	{
		u32 index; //buffer 序号
		enum v4l2_buf_type type; //buffer 类型
		u32 byteused; //buffer 中已使用的字节数
		u32 flags; // 区分是MMAP 还是USERPTR
		enum v4l2_field field;
		struct timeval timestamp; // 获取第一个字节时的系统时间
		struct v4l2_timecode timecode;
		u32 sequence; // 队列中的序号
		enum v4l2_memory memory; //IO 方式,被应用程序设置
		union m
		{
			u32 offset; // 缓冲帧地址,只对MMAP 有效
			unsigned long userptr;
		};	
		u32 length; // 缓冲帧长度
		u32 input;
		u32 reserved;
	};
	*/

    unsigned char *mptr[4];  //保护映射后用户空间的首地址
	unsigned int size[4];
	struct v4l2_buffer mapbuffer;
    for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers)
    {
        memset(&mapbuffer,0,sizeof(mapbuffer));                     //清空
        mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;         //设置类型摄像头采集
        mapbuffer.memory = V4L2_MEMORY_MMAP;                  //内存映射  IO 方式,被应用程序设置
        //mapbuffer.memory = V4L2_MEMORY_USERPTR;
        mapbuffer.index = n_buffers;                          //buffer 序号
        // 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
        if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &mapbuffer)){
            printf("----查询内核空间队列失败--VIDIOC_QUERYBUF-fail------\n");
             return -1;
        }
        size[n_buffers] = mapbuffer.length;
        // 映射内存
        mptr[n_buffers] =(unsigned char *)mmap (NULL,mapbuffer.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, mapbuffer.m.offset);


        //VIDIOC_QBUF// 把帧放入队列
		//VIDIOC_DQBUF// 从队列中取出帧
        if(ioctl (fd, VIDIOC_QBUF, &mapbuffer) < 0) {// 将缓冲帧放入队列
             printf("----------帧放入队列失败------------\n");
             return -1;
        }
    }

    //7.缓冲区处理好之后,就可以开始获取数据了
    //启动 或 停止数据流 VIDIOC_STREAMON, VIDIOC_STREAMOFF
    //例:把四个缓冲帧放入队列,并启动数据流
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(ioctl (fd, VIDIOC_STREAMON, &type) < 0) {
        printf("-----------开启失败-------\n");
        return -1;
    }




    //int flag = 1;
    #if 01
    FILE *file = fopen("my.yuv","w+");
    #else
    FILE *file = fopen("my.h264","w+");
    #endif

    while (1)
    {
       //8.采集数据
		//从队列中提取一帧数据
		struct v4l2_buffer readbuffer;
        memset(&readbuffer,0,sizeof(readbuffer));
		readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        readbuffer.memory = V4L2_MEMORY_MMAP;
       printf("--------------1-----------\n");
		ret = ioctl (fd, VIDIOC_DQBUF, &readbuffer); // 从缓冲区取出一个缓冲帧
		if(ret < 0)
		{
			perror("提取数据失败");
		}
       printf("--------------2-----------\n");

        //把一帧数据写入文件
        //if(1 == flag) {
            fwrite(mptr[readbuffer.index],readbuffer.length,1,file);
            fflush(file);
            //fclose(file); 
            //flag = -1;
        //}

        //通知内核已经使用完毕
		ret = ioctl (fd,VIDIOC_QBUF,&readbuffer);
		if(ret < 0)
		{
			perror("放回队列失败");
		}
    }
    
     //9.停止采集
    ret = ioctl (fd,VIDIOC_STREAMOFF,&type);

  
    //10.释放映射
	for(int i=0; i<4; i++)
    {
	    munmap(mptr[i], size[i]);// 断开映射
    }

	//11.关闭设备
	close(fd);
    printf("------------end------------\n");

    return 0;
}

#查看dev/video设备信息
v4l2-ctl --list-formats -d /dev/FPV
打开相机获取yuv
ffmpeg -f video4linux2 -s 1920x1080 -pix_fmt yuyv422 -i /dev/video0 out.yuv
window获取相机列表
ffmpeg -list_devices true -f dshow -i dummy
window播放相机设备
ffplay -f dshow -i video=“Integrated Camera”
linux播放相机设备
ffmpeg -f v4l2 -i video=/dev/video0

查看摄像头所支持的分辨率
v4l2-ctl --list-framesizes=MJPG -d /dev/video0
查看摄像头支持的视频参数
sudo v4l2-ctl --all --list-formats-ext
#查看相机当前支持格式
v4l2-ctl --list-formats -d /dev/video0 --all
v4l2参数意义
https://blog.csdn.net/liujun3512159/article/details/123946141

相关v4l2链接:
https://blog.csdn.net/abcamus/article/details/52940412
https://blog.csdn.net/wishfly/article/details/50856799
https://blog.csdn.net/wyc1522510/article/details/121959665
https://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html

你可能感兴趣的:(音视频,linux)