getframe.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include <libmegapixels.h>
  2. #include <stdio.h>
  3. #include <limits.h>
  4. #include <string.h>
  5. #include <linux/videodev2.h>
  6. #include <sys/ioctl.h>
  7. #include <stdlib.h>
  8. #include <sys/types.h>
  9. #include <errno.h>
  10. #include <sys/mman.h>
  11. #include <getopt.h>
  12. #include <ctype.h>
  13. struct buffer {
  14. void *start;
  15. size_t length;
  16. };
  17. struct buffer *buffers;
  18. int
  19. xioctl(int fd, int request, void *arg)
  20. {
  21. int r;
  22. do {
  23. r = ioctl(fd, request, arg);
  24. } while (r == -1 && errno == EINTR);
  25. return r;
  26. }
  27. void
  28. usage(char *name)
  29. {
  30. fprintf(stderr, "Usage: %s [-h] [-n offset] [-c camera] [-o file]\n", name);
  31. fprintf(stderr, "Get a frame using libmegapixels\n\n");
  32. fprintf(stderr, "Arguments:\n");
  33. fprintf(stderr, " -n offset Get the Nth frame from the output (defaults to 5)\n");
  34. fprintf(stderr, " -c camera Use a specific camera number\n");
  35. fprintf(stderr, " -o file File to store the frame in\n");
  36. fprintf(stderr, " -h Display this help text\n");
  37. }
  38. int
  39. main(int argc, char *argv[])
  40. {
  41. int c;
  42. int camera_id = 0;
  43. long res;
  44. char *end;
  45. char *outfile = NULL;
  46. int count = 5;
  47. while ((c = getopt(argc, argv, "hc:n:o:")) != -1) {
  48. switch (c) {
  49. case 'c':
  50. res = strtol(optarg, &end, 10);
  51. if (end == optarg || end == NULL || *end != '\0') {
  52. fprintf(stderr, "Invalid number for -c\n");
  53. return 1;
  54. }
  55. camera_id = (int) res;
  56. break;
  57. case 'o':
  58. outfile = optarg;
  59. break;
  60. case 'n':
  61. res = strtol(optarg, &end, 10);
  62. if (end == optarg || end == NULL || *end != '\0') {
  63. fprintf(stderr, "Invalid number for -n\n");
  64. return 1;
  65. }
  66. count = (int) res;
  67. break;
  68. case 'h':
  69. usage(argv[0]);
  70. return 0;
  71. break;
  72. case '?':
  73. if (optopt == 'd' || optopt == 'l') {
  74. fprintf(stderr, "Option -%c requires an argument.\n", optopt);
  75. } else if (isprint(optopt)) {
  76. fprintf(stderr, "Unknown option '-%c'\n", optopt);
  77. } else {
  78. fprintf(stderr, "Unknown option character x%x\n", optopt);
  79. }
  80. return 1;
  81. default:
  82. usage(argv[0]);
  83. return 1;
  84. }
  85. }
  86. char configpath[PATH_MAX];
  87. int ret = libmegapixels_find_config(configpath);
  88. libmegapixels_devconfig *config = {0};
  89. libmegapixels_init(&config);
  90. if (ret) {
  91. printf("Using config: %s\n", configpath);
  92. libmegapixels_load_file(config, configpath);
  93. } else {
  94. fprintf(stderr, "No config found for this device\n");
  95. }
  96. libmegapixels_load_uvc(config);
  97. if (config->count == 0) {
  98. fprintf(stderr, "No valid camera configuration\n");
  99. return 1;
  100. }
  101. if (camera_id > config->count - 1) {
  102. fprintf(stderr, "Camera id %d does not exist\n", camera_id);
  103. return 1;
  104. }
  105. libmegapixels_camera *camera = config->cameras[camera_id];
  106. if (libmegapixels_open(camera) != 0) {
  107. fprintf(stderr, "Could not open default camera\n");
  108. return 1;
  109. }
  110. libmegapixels_mode *mode = camera->modes[0];
  111. unsigned int frame_size = libmegapixels_select_mode(camera, mode);
  112. if (frame_size == 0) {
  113. fprintf(stderr, "Could not select mode\n");
  114. return 1;
  115. }
  116. // Do the reqular V4L2 stuff to get a frame
  117. struct v4l2_capability cap;
  118. if (ioctl(camera->video_fd, VIDIOC_QUERYCAP, &cap) != 0) {
  119. fprintf(stderr, "VIDIOC_QUERYCAP failed\n");
  120. return 1;
  121. }
  122. if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
  123. fprintf(stderr, "Device does not support streaming i/o\n");
  124. return 1;
  125. }
  126. struct v4l2_requestbuffers req = {0};
  127. req.count = 4;
  128. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  129. req.memory = V4L2_MEMORY_MMAP;
  130. if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
  131. fprintf(stderr, "VIDIOC_REQBUFS failed\n");
  132. return 1;
  133. }
  134. buffers = calloc(req.count, sizeof(*buffers));
  135. for (int i = 0; i < req.count; i++) {
  136. struct v4l2_buffer buf = {0};
  137. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  138. buf.memory = V4L2_MEMORY_MMAP;
  139. buf.index = i;
  140. if (xioctl(camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
  141. fprintf(stderr, "VIDIOC_QUERYBUF failed\n");
  142. return 1;
  143. }
  144. buffers[i].length = buf.length;
  145. buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->video_fd, buf.m.offset);
  146. if (buffers[i].start == MAP_FAILED) {
  147. fprintf(stderr, "mmap() failed\n");
  148. return 1;
  149. }
  150. }
  151. for (int i = 0; i < req.count; i++) {
  152. struct v4l2_buffer qbuf = {0};
  153. qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  154. qbuf.memory = V4L2_MEMORY_MMAP;
  155. qbuf.index = i;
  156. if (xioctl(camera->video_fd, VIDIOC_QBUF, &qbuf) == -1) {
  157. fprintf(stderr, "VIDIOC_QBUF failed\n");
  158. return 1;
  159. }
  160. }
  161. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  162. if (xioctl(camera->video_fd, VIDIOC_STREAMON, &type) == -1) {
  163. fprintf(stderr, "VIDIOC_STREAMON failed\n");
  164. return 1;
  165. }
  166. while (count-- > 0) {
  167. while (1) {
  168. fd_set(fds);
  169. FD_ZERO(&fds);
  170. FD_SET(camera->video_fd, &fds);
  171. int sr = select(FD_SETSIZE, &fds, NULL, NULL, NULL);
  172. if (sr == -1) {
  173. if (errno == EINTR) {
  174. count++;
  175. continue;
  176. }
  177. fprintf(stderr, "select() failed: %s\n", strerror(errno));
  178. return 1;
  179. }
  180. struct v4l2_buffer buf = {0};
  181. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  182. buf.memory = V4L2_MEMORY_MMAP;
  183. if (xioctl(camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
  184. fprintf(stderr, "VIDIOC_DQBUF failed\n");
  185. return 1;
  186. }
  187. fprintf(stderr, "received frame\n");
  188. if (count == 0 && outfile != NULL) {
  189. FILE *fp = fopen(outfile, "w");
  190. fwrite(buffers[buf.index].start, buf.bytesused, 1, fp);
  191. fclose(fp);
  192. printf("Stored frame to: %s\n", outfile);
  193. printf("Format: %dx%d\n", mode->width, mode->height);
  194. char fourcc[5] = {0};
  195. fourcc[0] = (char) (mode->v4l_pixfmt & 0xff);
  196. fourcc[1] = (char) ((mode->v4l_pixfmt >> 8) & 0xff);
  197. fourcc[2] = (char) ((mode->v4l_pixfmt >> 16) & 0xff);
  198. fourcc[3] = (char) ((mode->v4l_pixfmt >> 24) & 0xff);
  199. printf("Pixfmt: %s\n", fourcc);
  200. }
  201. if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
  202. fprintf(stderr, "VIDIOC_DQBUF failed\n");
  203. return 1;
  204. }
  205. break;
  206. }
  207. }
  208. return 0;
  209. }