上文把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