test_vl2.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #include <asm/types.h>
  2. #include <assert.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <getopt.h>
  6. #include <linux/fb.h>
  7. #include <linux/videodev2.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <sys/ioctl.h>
  12. #include <sys/mman.h>
  13. #include <sys/stat.h>
  14. #include <unistd.h>
  15. #define CLEAN(x) (memset(&(x), 0, sizeof(x)))
  16. #define WIDTH 1280
  17. #define HEIGHT 720
  18. typedef struct BufferSt {
  19. void *start;
  20. unsigned int length;
  21. } BufferSt;
  22. int fd;
  23. int out_fd;
  24. static BufferSt *buffer = NULL;
  25. static int query_set_format()
  26. {
  27. // 查询设备信息
  28. struct v4l2_capability cap;
  29. if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
  30. perror("VIDIOC_QUERYCAP");
  31. return -1;
  32. }
  33. printf("DriverName:%s\nCard Name:%s\nBus info:%s\nDriverVersion:%u.%u.%u\n",
  34. cap.driver, cap.card, cap.bus_info, (cap.version >> 16) & 0xFF, (cap.version >> 8) & 0xFF, (cap.version) & 0xFF);
  35. // 查询帧格式
  36. struct v4l2_fmtdesc fmtdesc;
  37. fmtdesc.index = 0;
  38. fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  39. while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) {
  40. printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);
  41. fmtdesc.index++;
  42. }
  43. // 设置帧格式
  44. struct v4l2_format fmt;
  45. CLEAN(fmt);
  46. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  47. fmt.fmt.pix.width = WIDTH;
  48. fmt.fmt.pix.height = HEIGHT;
  49. fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  50. if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  51. printf("VIDIOC_S_FMT IS ERROR! LINE:%d\n", __LINE__);
  52. //return -1;
  53. }
  54. // 上面设置帧格式可能失败,这里需要查看一下实际帧格式
  55. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  56. if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
  57. printf("VIDIOC_G_FMT IS ERROR! LINE:%d\n", __LINE__);
  58. return -1;
  59. }
  60. printf("width:%d\nheight:%d\npixelformat:%c%c%c%c\n",
  61. fmt.fmt.pix.width, fmt.fmt.pix.height,
  62. fmt.fmt.pix.pixelformat & 0xFF,
  63. (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
  64. (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
  65. (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
  66. return 0;
  67. }
  68. static int request_allocate_buffers()
  69. {
  70. // 申请帧缓冲区
  71. struct v4l2_requestbuffers req;
  72. CLEAN(req);
  73. req.count = 4;
  74. req.memory = V4L2_MEMORY_MMAP; // 使用内存映射缓冲区
  75. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  76. // 申请4个帧缓冲区,在内核空间中
  77. if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  78. printf("VIDIOC_REQBUFS IS ERROR! LINE:%d\n", __LINE__);
  79. return -1;
  80. }
  81. // 获取每个帧信息,并映射到用户空间
  82. buffer = (BufferSt *)calloc(req.count, sizeof(BufferSt));
  83. if (buffer == NULL) {
  84. printf("calloc is error! LINE:%d\n", __LINE__);
  85. return -1;
  86. }
  87. struct v4l2_buffer buf;
  88. int buf_index = 0;
  89. for (buf_index = 0; buf_index < req.count; buf_index++) {
  90. CLEAN(buf);
  91. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  92. buf.index = buf_index;
  93. buf.memory = V4L2_MEMORY_MMAP;
  94. if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) // 获取每个帧缓冲区的信息 如length和offset
  95. {
  96. printf("VIDIOC_QUERYBUF IS ERROR! LINE:%d\n", __LINE__);
  97. return -1;
  98. }
  99. // 将内核空间中的帧缓冲区映射到用户空间
  100. buffer[buf_index].length = buf.length;
  101. buffer[buf_index].start = mmap(NULL, // 由内核分配映射的起始地址
  102. buf.length, // 长度
  103. PROT_READ | PROT_WRITE, // 可读写
  104. MAP_SHARED, // 可共享
  105. fd,
  106. buf.m.offset);
  107. if (buffer[buf_index].start == MAP_FAILED) {
  108. printf("MAP_FAILED LINE:%d\n", __LINE__);
  109. return -1;
  110. }
  111. // 将帧缓冲区放入视频输入队列
  112. if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  113. printf("VIDIOC_QBUF IS ERROR! LINE:%d\n", __LINE__);
  114. return -1;
  115. }
  116. printf("Frame buffer :%d address :0x%x length:%d\n", buf_index, (__u32)buffer[buf_index].start, buffer[buf_index].length);
  117. }
  118. }
  119. static void start_capture()
  120. {
  121. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  122. if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
  123. printf("VIDIOC_STREAMON IS ERROR! LINE:%d\n", __LINE__);
  124. exit(1);
  125. }
  126. }
  127. static void end_capture()
  128. {
  129. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  130. if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
  131. printf("VIDIOC_STREAMOFF IS ERROR! LINE:%d\n", __LINE__);
  132. exit(1);
  133. }
  134. }
  135. static int read_frame()
  136. {
  137. struct v4l2_buffer buf;
  138. int ret = 0;
  139. CLEAN(buf);
  140. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  141. buf.memory = V4L2_MEMORY_MMAP;
  142. if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  143. printf("VIDIOC_DQBUF! LINEL:%d\n", __LINE__);
  144. return -1;
  145. }
  146. ret = write(out_fd, buffer[buf.index].start, buf.bytesused);
  147. if (ret == -1) {
  148. printf("write is error !\n");
  149. return -1;
  150. }
  151. if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  152. printf("VIDIOC_QBUF! LINE:%d\n", __LINE__);
  153. return -1;
  154. }
  155. return 0;
  156. }
  157. static void unmap_buffer()
  158. {
  159. int i = 0;
  160. for (i = 0; i < 4; i++) {
  161. munmap(buffer[i].start, buffer[i].length);
  162. }
  163. free(buffer);
  164. }
  165. static int capture_frame()
  166. {
  167. struct timeval tvptr;
  168. int ret;
  169. tvptr.tv_usec = 0;
  170. tvptr.tv_sec = 2;
  171. fd_set fdread;
  172. FD_ZERO(&fdread);
  173. FD_SET(fd, &fdread);
  174. ret = select(fd + 1, &fdread, NULL, NULL, &tvptr);
  175. if (ret == -1) {
  176. perror("select");
  177. exit(1);
  178. }
  179. if (ret == 0) {
  180. printf("timeout! \n");
  181. return -1;
  182. }
  183. read_frame();
  184. }
  185. int main(int argc, char *argv[])
  186. {
  187. // 1、打开摄像头
  188. fd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
  189. if (fd == -1) {
  190. printf("can not open '%s'\n", "/dev/video0");
  191. return -1;
  192. }
  193. out_fd = open("./out.yuv", O_RDWR | O_CREAT, 0777);
  194. if (out_fd == -1) {
  195. printf("open out file is error!\n");
  196. return -1;
  197. }
  198. // 2、查询设备、设置视频格式
  199. query_set_format();
  200. // 3、请求和分配缓冲区
  201. request_allocate_buffers();
  202. // 4 、开始捕获视频
  203. start_capture();
  204. // 5、获取和处理视频帧
  205. for (int i = 0; i < 20; i++) {
  206. capture_frame();
  207. printf("frame:%d\n", i);
  208. }
  209. // 6、停止捕获视频
  210. end_capture();
  211. // 7、解除缓冲区内存映射
  212. unmap_buffer();
  213. // 8、关闭摄像头
  214. close(fd);
  215. close(out_fd);
  216. return 0;
  217. }