main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. #include <errno.h>
  2. #include <fcntl.h>
  3. #include <linux/videodev2.h>
  4. #include <sys/ioctl.h>
  5. #include <sys/mman.h>
  6. #include <asm/errno.h>
  7. #include <gtk/gtk.h>
  8. #include "ini.h"
  9. enum io_method {
  10. IO_METHOD_READ,
  11. IO_METHOD_MMAP,
  12. IO_METHOD_USERPTR,
  13. };
  14. struct buffer {
  15. void *start;
  16. size_t length;
  17. };
  18. struct buffer *buffers;
  19. static unsigned int n_buffers;
  20. static char *rear_dev_name;
  21. static char *front_dev_name;
  22. static char *dev_name;
  23. static enum io_method io = IO_METHOD_MMAP;
  24. static int preview_width = -1;
  25. static int preview_height = -1;
  26. static int preview_fmt = V4L2_PIX_FMT_RGB24;
  27. GObject *preview_image;
  28. static int
  29. xioctl(int fd, int request, void *arg)
  30. {
  31. int r;
  32. do {
  33. r = ioctl(fd, request, arg);
  34. } while (r == -1 && errno == EINTR);
  35. return r;
  36. }
  37. static void
  38. errno_exit(const char *s)
  39. {
  40. fprintf(stderr, "%s error %d, %s\\n", s, errno, strerror(errno));
  41. exit(EXIT_FAILURE);
  42. }
  43. static void
  44. start_capturing(int fd)
  45. {
  46. enum v4l2_buf_type type;
  47. switch (io) {
  48. case IO_METHOD_READ:
  49. /* Nothing to do. */
  50. break;
  51. case IO_METHOD_MMAP:
  52. for (int i = 0; i < n_buffers; ++i) {
  53. struct v4l2_buffer buf = {
  54. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  55. .memory = V4L2_MEMORY_MMAP,
  56. .index = i,
  57. };
  58. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  59. errno_exit("VIDIOC_QBUF");
  60. }
  61. }
  62. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  63. if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) {
  64. errno_exit("VIDIOC_STREAMON");
  65. }
  66. break;
  67. case IO_METHOD_USERPTR:
  68. for (int i = 0; i < n_buffers; ++i) {
  69. struct v4l2_buffer buf = {
  70. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  71. .memory = V4L2_MEMORY_USERPTR,
  72. .index = i,
  73. };
  74. buf.m.userptr = (unsigned long)buffers[i].start;
  75. buf.length = buffers[i].length;
  76. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  77. errno_exit("VIDIOC_QBUF");
  78. }
  79. }
  80. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  81. if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) {
  82. errno_exit("VIDIOC_STREAMON");
  83. }
  84. break;
  85. }
  86. }
  87. static void
  88. init_mmap(int fd)
  89. {
  90. struct v4l2_requestbuffers req = { 0 };
  91. req.count = 4;
  92. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  93. req.memory = V4L2_MEMORY_MMAP;
  94. if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  95. if (errno == EINVAL) {
  96. fprintf(stderr, "%s does not support memory mapping",
  97. dev_name);
  98. exit(EXIT_FAILURE);
  99. } else {
  100. errno_exit("VIDIOC_REQBUFS");
  101. }
  102. }
  103. if (req.count < 2) {
  104. fprintf(stderr, "Insufficient buffer memory on %s\n",
  105. dev_name);
  106. exit(EXIT_FAILURE);
  107. }
  108. buffers = calloc(req.count, sizeof(buffers[0]));
  109. if (!buffers) {
  110. fprintf(stderr, "Out of memory\\n");
  111. exit(EXIT_FAILURE);
  112. }
  113. for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
  114. struct v4l2_buffer buf = {
  115. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  116. .memory = V4L2_MEMORY_MMAP,
  117. .index = n_buffers,
  118. };
  119. if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
  120. errno_exit("VIDIOC_QUERYBUF");
  121. }
  122. buffers[n_buffers].length = buf.length;
  123. buffers[n_buffers].start = mmap(NULL /* start anywhere */,
  124. buf.length,
  125. PROT_READ | PROT_WRITE /* required */,
  126. MAP_SHARED /* recommended */,
  127. fd, buf.m.offset);
  128. if (MAP_FAILED == buffers[n_buffers].start) {
  129. errno_exit("mmap");
  130. }
  131. }
  132. }
  133. static void
  134. init_device(int fd)
  135. {
  136. struct v4l2_capability cap;
  137. if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
  138. if (errno == EINVAL) {
  139. fprintf(stderr, "%s is no V4L2 device\n",
  140. dev_name);
  141. exit(EXIT_FAILURE);
  142. } else {
  143. errno_exit("VIDIOC_QUERYCAP");
  144. }
  145. }
  146. if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  147. fprintf(stderr, "%s is no video capture device\n",
  148. dev_name);
  149. exit(EXIT_FAILURE);
  150. }
  151. switch (io) {
  152. case IO_METHOD_READ:
  153. if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
  154. fprintf(stderr, "%s does not support read i/o\n",
  155. dev_name);
  156. exit(EXIT_FAILURE);
  157. }
  158. break;
  159. case IO_METHOD_MMAP:
  160. case IO_METHOD_USERPTR:
  161. if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
  162. fprintf(stderr, "%s does not support streaming i/o\n",
  163. dev_name);
  164. exit(EXIT_FAILURE);
  165. }
  166. break;
  167. }
  168. /* Select video input, video standard and tune here. */
  169. struct v4l2_cropcap cropcap = {
  170. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  171. };
  172. struct v4l2_crop crop = { 0 };
  173. if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
  174. crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  175. crop.c = cropcap.defrect; /* reset to default */
  176. if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
  177. switch (errno) {
  178. case EINVAL:
  179. /* Cropping not supported. */
  180. break;
  181. default:
  182. /* Errors ignored. */
  183. break;
  184. }
  185. }
  186. } else {
  187. /* Errors ignored. */
  188. }
  189. struct v4l2_format fmt = {
  190. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  191. };
  192. if (preview_width > 0) {
  193. fmt.fmt.pix.width = preview_width;
  194. fmt.fmt.pix.height = preview_height;
  195. fmt.fmt.pix.pixelformat = preview_fmt;
  196. fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  197. if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  198. errno_exit("VIDIOC_S_FMT");
  199. }
  200. /* Note VIDIOC_S_FMT may change width and height. */
  201. } else {
  202. /* Preserve original settings as set by v4l2-ctl for example */
  203. if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
  204. errno_exit("VIDIOC_G_FMT");
  205. }
  206. }
  207. /* Buggy driver paranoia. */
  208. unsigned int min = fmt.fmt.pix.width * 2;
  209. if (fmt.fmt.pix.bytesperline < min) {
  210. fmt.fmt.pix.bytesperline = min;
  211. }
  212. min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
  213. if (fmt.fmt.pix.sizeimage < min) {
  214. fmt.fmt.pix.sizeimage = min;
  215. }
  216. switch (io) {
  217. case IO_METHOD_READ:
  218. //init_read(fmt.fmt.pix.sizeimage);
  219. break;
  220. case IO_METHOD_MMAP:
  221. init_mmap(fd);
  222. break;
  223. case IO_METHOD_USERPTR:
  224. //init_userp(fmt.fmt.pix.sizeimage);
  225. break;
  226. }
  227. }
  228. static void
  229. process_image(const void *p, int size)
  230. {
  231. GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(p, GDK_COLORSPACE_RGB,
  232. FALSE, 8, 640, 480, 2 * 640, NULL, NULL);
  233. gtk_image_set_from_pixbuf(preview_image, pixbuf);
  234. }
  235. static int
  236. read_frame(int fd)
  237. {
  238. struct v4l2_buffer buf = { 0 };
  239. switch (io) {
  240. case IO_METHOD_READ:
  241. if (read(fd, buffers[0].start, buffers[0].length) == -1) {
  242. switch (errno) {
  243. case EAGAIN:
  244. return 0;
  245. case EIO:
  246. /* Could ignore EIO, see spec. */
  247. /* fallthrough */
  248. default:
  249. errno_exit("read");
  250. break;
  251. }
  252. }
  253. process_image(buffers[0].start, buffers[0].length);
  254. break;
  255. case IO_METHOD_MMAP:
  256. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  257. buf.memory = V4L2_MEMORY_MMAP;
  258. if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  259. switch (errno) {
  260. case EAGAIN:
  261. return 0;
  262. case EIO:
  263. /* Could ignore EIO, see spec. */
  264. /* fallthrough */
  265. default:
  266. errno_exit("VIDIOC_DQBUF");
  267. break;
  268. }
  269. }
  270. //assert(buf.index < n_buffers);
  271. process_image(buffers[buf.index].start, buf.bytesused);
  272. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  273. errno_exit("VIDIOC_QBUF");
  274. }
  275. break;
  276. case IO_METHOD_USERPTR:
  277. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  278. buf.memory = V4L2_MEMORY_USERPTR;
  279. if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  280. switch (errno) {
  281. case EAGAIN:
  282. return 0;
  283. case EIO:
  284. /* Could ignore EIO, see spec. */
  285. /* fallthrough */
  286. default:
  287. errno_exit("VIDIOC_DQBUF");
  288. break;
  289. }
  290. }
  291. unsigned int i;
  292. for (i = 0; i < n_buffers; ++i) {
  293. if (buf.m.userptr == (unsigned long)buffers[i].start
  294. && buf.length == buffers[i].length) {
  295. break;
  296. }
  297. }
  298. //assert(i < n_buffers);
  299. process_image((void *)buf.m.userptr, buf.bytesused);
  300. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  301. errno_exit("VIDIOC_QBUF");
  302. }
  303. break;
  304. }
  305. return 1;
  306. }
  307. static void
  308. get_frame(int fd)
  309. {
  310. while (1) {
  311. fd_set fds;
  312. struct timeval tv;
  313. int r;
  314. FD_ZERO(&fds);
  315. FD_SET(fd, &fds);
  316. /* Timeout. */
  317. tv.tv_sec = 2;
  318. tv.tv_usec = 0;
  319. r = select(fd + 1, &fds, NULL, NULL, &tv);
  320. if (r == -1) {
  321. if (EINTR == errno) {
  322. continue;
  323. }
  324. errno_exit("select");
  325. } else if (r == 0) {
  326. fprintf(stderr, "select timeout\\n");
  327. exit(EXIT_FAILURE);
  328. }
  329. if (read_frame(fd)) {
  330. break;
  331. }
  332. /* EAGAIN - continue select loop. */
  333. }
  334. }
  335. static int
  336. config_ini_handler(void *user, const char *section, const char *name,
  337. const char *value) {
  338. if (strcmp(section, "preview") == 0) {
  339. if (strcmp(name, "width") == 0) {
  340. preview_width = strtol(value, NULL, 10);
  341. } else if (strcmp(name, "height") == 0) {
  342. preview_height = strtol(value, NULL, 10);
  343. } else if (strcmp(name, "fmt") == 0) {
  344. preview_fmt = strtol(value, NULL, 10);
  345. } else {
  346. g_printerr("Unknown key '%s' in [preview]", name);
  347. exit(1);
  348. }
  349. } else if (strcmp(section, "device") == 0) {
  350. if (strcmp(name, "rear") == 0) {
  351. rear_dev_name = strdup(value);
  352. } else if (strcmp(name, "front") == 0) {
  353. front_dev_name = strdup(value);
  354. } else {
  355. g_printerr("Unknown key '%s' in [device]", name);
  356. exit(1);
  357. }
  358. } else {
  359. g_printerr("Unknown section '%s' in config file", section);
  360. exit(1);
  361. }
  362. return 1;
  363. }
  364. int
  365. main(int argc, char *argv[])
  366. {
  367. if (argc != 2) {
  368. g_printerr("Usage: camera configfile\n");
  369. return 1;
  370. }
  371. GError *error = NULL;
  372. gtk_init(&argc, &argv);
  373. GtkBuilder *builder = gtk_builder_new();
  374. if (gtk_builder_add_from_file(builder, "camera.glade", &error) == 0) {
  375. g_printerr("Error loading file: %s\n", error->message);
  376. g_clear_error(&error);
  377. return 1;
  378. }
  379. GObject *window = gtk_builder_get_object(builder, "window");
  380. GObject *preview_box = gtk_builder_get_object(builder, "preview_box");
  381. preview_image = gtk_builder_get_object(builder, "preview");
  382. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  383. GtkCssProvider *provider = gtk_css_provider_new();
  384. gtk_css_provider_load_from_path(provider, "camera.css", NULL);
  385. GtkStyleContext *context = gtk_widget_get_style_context(preview_box);
  386. gtk_style_context_add_provider(context,
  387. GTK_STYLE_PROVIDER(provider),
  388. GTK_STYLE_PROVIDER_PRIORITY_USER);
  389. int result = ini_parse(argv[1], config_ini_handler, NULL);
  390. if (result == -1) {
  391. g_printerr("Config file not found\n");
  392. return 1;
  393. } else if (result == -2) {
  394. g_printerr("Could not allocate memory to parse config file\n");
  395. return 1;
  396. } else if (result != 0) {
  397. g_printerr("Could not parse config file\n");
  398. return 1;
  399. }
  400. dev_name = rear_dev_name;
  401. int fd = open(dev_name, O_RDWR);
  402. if (fd == -1) {
  403. g_printerr("Error opening video device: %s\n", dev_name);
  404. return 1;
  405. }
  406. init_device(fd);
  407. start_capturing(fd);
  408. get_frame(fd);
  409. gtk_widget_show(window);
  410. gtk_main();
  411. return 0;
  412. }