v4l2打开相机获取流demo及命令打开相机设备
/*打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲 区管理)-> 循环获取数据-> 关闭设备。*/#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
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~