上文把uvc驱动的初始化部分分析的差很少了,从如今起咱们开始着手uvc的操做流程,先给出我本身写的一个调用v4l2接口的应用,方便以后的分析web
int main() { int ret; int camera_fd; //打开uvc设备,获取设备句柄 camera_fd = open("/dev/video2",O_RDWR); //获取设备支持格式 struct v4l2_fmtdesc fmt; memset(&fmt,0x0,sizeof(fmt)); fmt.index = 0; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while ((ret = ioctl(camera_fd, 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); } //查询设备能力,capabilities是用掩码表示 struct v4l2_capability cap; ret = ioctl(camera_fd, VIDIOC_QUERYCAP, &cap); if(ret < 0) { printf("get vidieo capability error,error code: %d \n", errno); return -1; } printf("{ Capability: driver:'%s', card:'%s',buf_info:'%s',version:%d,capabilities:0x%x}\n",\ cap.driver,cap.card,cap.bus_info,cap.version,cap.capabilities); //设置获取视频格式 struct v4l2_format tv4l2_format; CLEAR(tv4l2_format); tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; tv4l2_format.fmt.pix.width = MAX_WIDTH; tv4l2_format.fmt.pix.height = MAX_HEIGHT; tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED; ret = ioctl(camera_fd, VIDIOC_S_FMT, &tv4l2_format); if(ret < 0) { perror("VIDIOC_S_FMT"); } printf("{ Format width: %d, height:%d}\n",tv4l2_format.fmt.pix.width,tv4l2_format.fmt.pix.height); //设置视频流信息,关于视频fps struct v4l2_streamparm *setfps; setfps = (struct v4l2_streamparm *)calloc(1,sizeof(struct v4l2_streamparm)); memset(setfps,0,sizeof(struct v4l2_streamparm)); setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; setfps->parm.capture.timeperframe.numerator = 1; setfps->parm.capture.timeperframe.denominator = 30; if(ioctl(camera_fd, VIDIOC_S_PARM,setfps) < 0) { perror("VIDIOC_S_PARM"); } //申请一个拥有1个缓冲帧的缓冲区 struct v4l2_requestbuffers v4l2_reqbuf; memset(&v4l2_reqbuf,0,sizeof(struct v4l2_requestbuffers)); v4l2_reqbuf.count = 1; v4l2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_reqbuf.memory = V4L2_MEMORY_MMAP; ret = ioctl(camera_fd, VIDIOC_REQBUFS, &v4l2_reqbuf); if(ret < 0) perror("VIDIOC_REQBUFS"); printf("{ Reqbuffer count: %d}\n",v4l2_reqbuf.count); //获取缓冲帧的地址,长度: struct v4l2_buffer map_buffer; map_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; map_buffer.index = 0; map_buffer.memory = V4L2_MEMORY_MMAP; ret = ioctl(camera_fd,VIDIOC_QUERYBUF,&map_buffer); if(ret < 0) perror("VIDIOC_QUERYBUF"); printf("{ Querybuffer length:%d, m.offset: %d}\n",map_buffer.length,map_buffer.m.offset); //以mmap共享内存的方式将缓冲帧与应用层共享 void *map_address = mmap(NULL, \ map_buffer.length, \ PROT_READ | PROT_WRITE ,\ MAP_SHARED, \ camera_fd, map_buffer.m.offset); if(map_address == MAP_FAILED){ printf("mmap failed!\n"); exit(1); } //将缓冲帧放入缓冲队列 struct v4l2_buffer camera_buf; camera_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; camera_buf.memory = V4L2_MEMORY_MMAP; camera_buf.index = 0; if(-1 == ioctl(camera_fd, VIDIOC_QBUF, &camera_buf)) { perror("VIDIOC_QBUF"); exit(EXIT_FAILURE); } //启动视频流 enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(-1 == ioctl(camera_fd,VIDIOC_STREAMON,&type)) { perror("VIDIOC_STREAMON"); exit(EXIT_FAILURE); } fd_set fds; struct timeval tv; int camera_index; char *filename = malloc(20*sizeof(char)); for(camera_index = 0;camera_index < MAX_FPS;camera_index++) { FD_ZERO(&fds); FD_SET(camera_fd, &fds); tv.tv_sec = 2; tv.tv_usec = 0; //经过select方式监听数据,超时2s ret = select(camera_fd+1,&fds,NULL,NULL,&tv); if(ret == -1) { perror("select"); exit(EXIT_FAILURE); } if(ret == 0) { fprintf(stderr,"select time out\n"); exit(EXIT_FAILURE); } //将数据从缓冲队列取出 struct v4l2_buffer get_buffer; get_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; get_buffer.memory = V4L2_MEMORY_MMAP; if(-1 == ioctl(camera_fd,VIDIOC_DQBUF,&get_buffer)) { perror("VIDIOC_DQBUF"); exit(EXIT_FAILURE); } printf("{ Get Buffer OK index:%d length: %d time: %ld:%ld}\n",get_buffer.index,get_buffer.length,get_buffer.timestamp.tv_sec,get_buffer.timestamp.tv_usec); // 此处将获取的yuv422数据进行转化成RGB方便显示以及libjpeg压缩,不关乎v4l2操做流程因此略去 //继续将缓冲帧放入缓冲对列去获取数据 if(-1 == ioctl(camera_fd,VIDIOC_QBUF,&get_buffer)) { perror("VIDIOC_QBUF"); exit(EXIT_FAILURE); } } if(-1 == munmap(map_address,map_buffer.length)) { perror("munmap"); exit(EXIT_FAILURE); } printf("munmap,then exit\n"); return 0; }
在我本机上执行的结果以下:bash
ggj@ggj:v4l2_camera$ ./camera { pixelformat = ''YUYV'', description = ''YUV 4:2:2 (YUYV)'' } { Capability: driver:'uvcvideo', card:'Acer Crystal Eye webcam',buf_info:'usb-0000:00:1a.7-1',version:198664,capabilities:0x84000001} { Format width: 640, height:480} { Reqbuffer count: 1} { Querybuffer length:614400, m.offset: 0} { Get Buffer OK index:0 length: 614400 time: 664:888343} munmap,then exit