main.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. #include <errno.h>
  2. #include <fcntl.h>
  3. #include <linux/videodev2.h>
  4. #include <linux/media.h>
  5. #include <linux/v4l2-subdev.h>
  6. #include <sys/ioctl.h>
  7. #include <sys/mman.h>
  8. #include <sys/stat.h>
  9. #include <time.h>
  10. #include <assert.h>
  11. #include <limits.h>
  12. #include <linux/kdev_t.h>
  13. #include <sys/sysmacros.h>
  14. #include <asm/errno.h>
  15. #include <wordexp.h>
  16. #include <gtk/gtk.h>
  17. #include <tiffio.h>
  18. #include "config.h"
  19. #include "ini.h"
  20. #include "quickdebayer.h"
  21. enum io_method {
  22. IO_METHOD_READ,
  23. IO_METHOD_MMAP,
  24. IO_METHOD_USERPTR,
  25. };
  26. #define TIFFTAG_FORWARDMATRIX1 50964
  27. struct buffer {
  28. void *start;
  29. size_t length;
  30. };
  31. struct camerainfo {
  32. char dev_name[260];
  33. unsigned int entity_id;
  34. char dev[260];
  35. int width;
  36. int height;
  37. int rate;
  38. int rotate;
  39. int fmt;
  40. int mbus;
  41. int fd;
  42. float colormatrix[9];
  43. float forwardmatrix[9];
  44. int blacklevel;
  45. int whitelevel;
  46. };
  47. static float colormatrix_srgb[] = {
  48. 3.2409, -1.5373, -0.4986,
  49. -0.9692, 1.8759, 0.0415,
  50. 0.0556, -0.2039, 1.0569
  51. };
  52. struct buffer *buffers;
  53. static unsigned int n_buffers;
  54. struct camerainfo rear_cam;
  55. struct camerainfo front_cam;
  56. struct camerainfo current;
  57. // Camera interface
  58. static char *media_drv_name;
  59. static unsigned int interface_entity_id;
  60. static char dev_name[260];
  61. static int media_fd;
  62. static int video_fd;
  63. static char *exif_make;
  64. static char *exif_model;
  65. // State
  66. static int ready = 0;
  67. static int capture = 0;
  68. static int current_is_rear = 1;
  69. static cairo_surface_t *surface = NULL;
  70. static int preview_width = -1;
  71. static int preview_height = -1;
  72. static char *last_path = NULL;
  73. // Widgets
  74. GtkWidget *preview;
  75. GtkWidget *error_box;
  76. GtkWidget *error_message;
  77. GtkWidget *main_stack;
  78. GtkWidget *thumb_last;
  79. static int
  80. xioctl(int fd, int request, void *arg)
  81. {
  82. int r;
  83. do {
  84. r = ioctl(fd, request, arg);
  85. } while (r == -1 && errno == EINTR);
  86. return r;
  87. }
  88. static void
  89. errno_exit(const char *s)
  90. {
  91. fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
  92. exit(EXIT_FAILURE);
  93. }
  94. static void
  95. show_error(const char *s)
  96. {
  97. gtk_label_set_text(GTK_LABEL(error_message), s);
  98. gtk_widget_show(error_box);
  99. }
  100. static void
  101. start_capturing(int fd)
  102. {
  103. enum v4l2_buf_type type;
  104. for (int i = 0; i < n_buffers; ++i) {
  105. struct v4l2_buffer buf = {
  106. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  107. .memory = V4L2_MEMORY_MMAP,
  108. .index = i,
  109. };
  110. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  111. errno_exit("VIDIOC_QBUF");
  112. }
  113. }
  114. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  115. if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) {
  116. errno_exit("VIDIOC_STREAMON");
  117. }
  118. ready = 1;
  119. }
  120. static void
  121. stop_capturing(int fd)
  122. {
  123. int i;
  124. ready = 0;
  125. printf("Stopping capture\n");
  126. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  127. if (xioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
  128. errno_exit("VIDIOC_STREAMOFF");
  129. }
  130. for (i = 0; i < n_buffers; ++i) {
  131. munmap(buffers[i].start, buffers[i].length);
  132. }
  133. }
  134. static void
  135. init_mmap(int fd)
  136. {
  137. struct v4l2_requestbuffers req = {0};
  138. req.count = 4;
  139. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  140. req.memory = V4L2_MEMORY_MMAP;
  141. if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  142. if (errno == EINVAL) {
  143. fprintf(stderr, "%s does not support memory mapping",
  144. dev_name);
  145. exit(EXIT_FAILURE);
  146. } else {
  147. errno_exit("VIDIOC_REQBUFS");
  148. }
  149. }
  150. if (req.count < 2) {
  151. fprintf(stderr, "Insufficient buffer memory on %s\n",
  152. dev_name);
  153. exit(EXIT_FAILURE);
  154. }
  155. buffers = calloc(req.count, sizeof(buffers[0]));
  156. if (!buffers) {
  157. fprintf(stderr, "Out of memory\\n");
  158. exit(EXIT_FAILURE);
  159. }
  160. for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
  161. struct v4l2_buffer buf = {
  162. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  163. .memory = V4L2_MEMORY_MMAP,
  164. .index = n_buffers,
  165. };
  166. if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
  167. errno_exit("VIDIOC_QUERYBUF");
  168. }
  169. buffers[n_buffers].length = buf.length;
  170. buffers[n_buffers].start = mmap(NULL /* start anywhere */,
  171. buf.length,
  172. PROT_READ | PROT_WRITE /* required */,
  173. MAP_SHARED /* recommended */,
  174. fd, buf.m.offset);
  175. if (MAP_FAILED == buffers[n_buffers].start) {
  176. errno_exit("mmap");
  177. }
  178. }
  179. }
  180. static int
  181. v4l2_ctrl_set(int fd, uint32_t id, int val)
  182. {
  183. struct v4l2_control ctrl = {0};
  184. ctrl.id = id;
  185. ctrl.value = val;
  186. if (xioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) {
  187. g_printerr("Failed to set control %d to %d\n", id, val);
  188. return -1;
  189. }
  190. return 0;
  191. }
  192. static void
  193. init_sensor(char *fn, int width, int height, int mbus, int rate)
  194. {
  195. int fd;
  196. struct v4l2_subdev_frame_interval interval;
  197. struct v4l2_subdev_format fmt;
  198. fd = open(fn, O_RDWR);
  199. g_printerr("Setting sensor rate to %d\n", rate);
  200. interval.pad = 0;
  201. interval.interval.numerator = 1;
  202. interval.interval.denominator = rate;
  203. if (xioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &interval) == -1) {
  204. errno_exit("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
  205. }
  206. g_printerr("Driver returned %d/%d frameinterval\n",
  207. interval.interval.numerator, interval.interval.denominator);
  208. g_printerr("Setting sensor to %dx%d fmt %d\n",
  209. width, height, mbus);
  210. fmt.pad = 0;
  211. fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
  212. fmt.format.code = mbus;
  213. fmt.format.width = width;
  214. fmt.format.height = height;
  215. fmt.format.field = V4L2_FIELD_ANY;
  216. if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
  217. errno_exit("VIDIOC_SUBDEV_S_FMT");
  218. }
  219. g_printerr("Driver returned %dx%d fmt %d\n",
  220. fmt.format.width, fmt.format.height,
  221. fmt.format.code);
  222. // Placeholder, default is also 1
  223. //v4l2_ctrl_set(fd, V4L2_CID_AUTOGAIN, 0);
  224. //v4l2_ctrl_set(fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL);
  225. //v4l2_ctrl_set(fd, V4L2_CID_EXPOSURE, height/24);
  226. //v4l2_ctrl_set(fd, V4L2_CID_GAIN, 0);
  227. close(current.fd);
  228. current.fd = fd;
  229. }
  230. static int
  231. init_device(int fd)
  232. {
  233. struct v4l2_capability cap;
  234. if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
  235. if (errno == EINVAL) {
  236. fprintf(stderr, "%s is no V4L2 device\n",
  237. dev_name);
  238. exit(EXIT_FAILURE);
  239. } else {
  240. errno_exit("VIDIOC_QUERYCAP");
  241. }
  242. }
  243. if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  244. fprintf(stderr, "%s is no video capture device\n",
  245. dev_name);
  246. exit(EXIT_FAILURE);
  247. }
  248. if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
  249. fprintf(stderr, "%s does not support streaming i/o\n",
  250. dev_name);
  251. exit(EXIT_FAILURE);
  252. }
  253. /* Select video input, video standard and tune here. */
  254. struct v4l2_cropcap cropcap = {
  255. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  256. };
  257. struct v4l2_crop crop = {0};
  258. if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
  259. crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  260. crop.c = cropcap.defrect; /* reset to default */
  261. if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
  262. switch (errno) {
  263. case EINVAL:
  264. /* Cropping not supported. */
  265. break;
  266. default:
  267. /* Errors ignored. */
  268. break;
  269. }
  270. }
  271. } else {
  272. /* Errors ignored. */
  273. }
  274. struct v4l2_format fmt = {
  275. .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
  276. };
  277. if (current.width > 0) {
  278. g_printerr("Setting camera to %dx%d fmt %d\n",
  279. current.width, current.height, current.fmt);
  280. fmt.fmt.pix.width = current.width;
  281. fmt.fmt.pix.height = current.height;
  282. fmt.fmt.pix.pixelformat = current.fmt;
  283. fmt.fmt.pix.field = V4L2_FIELD_ANY;
  284. if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  285. g_printerr("VIDIOC_S_FMT failed");
  286. show_error("Could not set camera mode");
  287. return -1;
  288. }
  289. g_printerr("Driver returned %dx%d fmt %d\n",
  290. fmt.fmt.pix.width, fmt.fmt.pix.height,
  291. fmt.fmt.pix.pixelformat);
  292. /* Note VIDIOC_S_FMT may change width and height. */
  293. } else {
  294. g_printerr("Querying camera format\n");
  295. /* Preserve original settings as set by v4l2-ctl for example */
  296. if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
  297. errno_exit("VIDIOC_G_FMT");
  298. }
  299. g_printerr("Driver returned %dx%d fmt %d\n",
  300. fmt.fmt.pix.width, fmt.fmt.pix.height,
  301. fmt.fmt.pix.pixelformat);
  302. current.width = fmt.fmt.pix.width;
  303. current.height = fmt.fmt.pix.height;
  304. }
  305. current.fmt = fmt.fmt.pix.pixelformat;
  306. /* Buggy driver paranoia. */
  307. unsigned int min = fmt.fmt.pix.width * 2;
  308. if (fmt.fmt.pix.bytesperline < min) {
  309. fmt.fmt.pix.bytesperline = min;
  310. }
  311. min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
  312. if (fmt.fmt.pix.sizeimage < min) {
  313. fmt.fmt.pix.sizeimage = min;
  314. }
  315. init_mmap(fd);
  316. return 0;
  317. }
  318. static void
  319. process_image(const int *p, int size)
  320. {
  321. time_t rawtime;
  322. struct tm tim;
  323. uint8_t *pixels;
  324. char fname[255];
  325. char timestamp[30];
  326. GdkPixbuf *pixbuf;
  327. GdkPixbuf *pixbufrot;
  328. GdkPixbuf *thumb;
  329. GError *error = NULL;
  330. double scale;
  331. cairo_t *cr;
  332. TIFF *tif;
  333. int skip = 2;
  334. long sub_offset = 0;
  335. static const short cfapatterndim[] = {2, 2};
  336. static const float neutral[] = {1.0, 1.0, 1.0};
  337. static const TIFFFieldInfo custom_fields[] = {
  338. {TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, FIELD_CUSTOM, 1, 1, "ForwardMatrix1"},
  339. };
  340. // Only process preview frames when not capturing
  341. if (capture == 0) {
  342. if(current.width > 1280) {
  343. skip = 3;
  344. }
  345. pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current.width / (skip*2), current.height / (skip*2));
  346. pixels = gdk_pixbuf_get_pixels(pixbuf);
  347. quick_debayer_bggr8((const uint8_t *)p, pixels, current.width, current.height, skip);
  348. if (current.rotate == 0) {
  349. pixbufrot = pixbuf;
  350. } else if (current.rotate == 90) {
  351. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
  352. } else if (current.rotate == 180) {
  353. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
  354. } else if (current.rotate == 270) {
  355. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
  356. }
  357. scale = (double) preview_width / gdk_pixbuf_get_width(pixbufrot);
  358. cr = cairo_create(surface);
  359. cairo_set_source_rgb(cr, 0, 0, 0);
  360. cairo_paint(cr);
  361. cairo_scale(cr, scale, scale);
  362. gdk_cairo_set_source_pixbuf(cr, pixbufrot, 0, 0);
  363. cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_NONE);
  364. cairo_paint(cr);
  365. gtk_widget_queue_draw_area(preview, 0, 0, preview_width, preview_height);
  366. } else {
  367. capture--;
  368. time(&rawtime);
  369. tim = *(localtime(&rawtime));
  370. strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
  371. sprintf(fname, "%s/Pictures/IMG%s-%d.dng", getenv("HOME"), timestamp, capture);
  372. if(!(tif = TIFFOpen(fname, "w"))) {
  373. printf("Could not open tiff\n");
  374. }
  375. // Add missing dng fields
  376. TIFFMergeFieldInfo(tif, custom_fields, sizeof(custom_fields) / sizeof(custom_fields[0]));
  377. // Define TIFF thumbnail
  378. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
  379. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, current.width >> 4);
  380. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, current.height >> 4);
  381. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  382. TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  383. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  384. TIFFSetField(tif, TIFFTAG_MAKE, "PINE64");
  385. TIFFSetField(tif, TIFFTAG_MODEL, "PinePhone");
  386. TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
  387. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
  388. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  389. TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
  390. TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);
  391. TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
  392. TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
  393. TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, "PINE64 PinePhone");
  394. if(current.colormatrix[0]) {
  395. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, current.colormatrix);
  396. } else {
  397. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
  398. }
  399. if(current.forwardmatrix[0]) {
  400. TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, current.forwardmatrix);
  401. }
  402. TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
  403. TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
  404. // Write black thumbnail, only windows uses this
  405. {
  406. unsigned char *buf = (unsigned char *)calloc(1, (int)current.width >> 4);
  407. for (int row = 0; row < current.height>>4; row++) {
  408. TIFFWriteScanline(tif, buf, row, 0);
  409. }
  410. free(buf);
  411. }
  412. TIFFWriteDirectory(tif);
  413. // Define main photo
  414. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
  415. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, current.width);
  416. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, current.height);
  417. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  418. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
  419. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
  420. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  421. TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim);
  422. TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR
  423. if(current.whitelevel) {
  424. TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &current.whitelevel);
  425. }
  426. if(current.blacklevel) {
  427. TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &current.blacklevel);
  428. }
  429. printf("Writing frame\n");
  430. unsigned char *pLine = (unsigned char*)malloc(current.width);
  431. for(int row = 0; row < current.height; row++){
  432. TIFFWriteScanline(tif, ((uint8_t *)p)+(row*current.width), row, 0);
  433. }
  434. free(pLine);
  435. TIFFClose(tif);
  436. // Update the thumbnail if this is the last frame
  437. if (capture == 0) {
  438. pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current.width / (skip*2), current.height / (skip*2));
  439. pixels = gdk_pixbuf_get_pixels(pixbuf);
  440. quick_debayer_bggr8((const uint8_t *)p, pixels, current.width, current.height, skip);
  441. if (current.rotate == 0) {
  442. pixbufrot = pixbuf;
  443. } else if (current.rotate == 90) {
  444. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
  445. } else if (current.rotate == 180) {
  446. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
  447. } else if (current.rotate == 270) {
  448. pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
  449. }
  450. thumb = gdk_pixbuf_scale_simple(pixbufrot, 24, 24, GDK_INTERP_BILINEAR);
  451. gtk_image_set_from_pixbuf(GTK_IMAGE(thumb_last), thumb);
  452. gdk_pixbuf_save(pixbufrot, fname, "jpeg", &error, "quality", "95", NULL);
  453. last_path = strdup(fname);
  454. if (error != NULL) {
  455. g_printerr("%s\n", error->message);
  456. g_clear_error(&error);
  457. }
  458. }
  459. }
  460. }
  461. static gboolean
  462. preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
  463. {
  464. cairo_set_source_surface(cr, surface, 0, 0);
  465. cairo_paint(cr);
  466. return FALSE;
  467. }
  468. static gboolean
  469. preview_configure(GtkWidget *widget, GdkEventConfigure *event)
  470. {
  471. cairo_t *cr;
  472. if (surface)
  473. cairo_surface_destroy(surface);
  474. surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget),
  475. CAIRO_CONTENT_COLOR,
  476. gtk_widget_get_allocated_width(widget),
  477. gtk_widget_get_allocated_height(widget));
  478. preview_width = gtk_widget_get_allocated_width(widget);
  479. preview_height = gtk_widget_get_allocated_height(widget);
  480. cr = cairo_create(surface);
  481. cairo_set_source_rgb(cr, 0, 0, 0);
  482. cairo_paint(cr);
  483. cairo_destroy(cr);
  484. return TRUE;
  485. }
  486. static int
  487. read_frame(int fd)
  488. {
  489. struct v4l2_buffer buf = {0};
  490. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  491. buf.memory = V4L2_MEMORY_MMAP;
  492. if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  493. switch (errno) {
  494. case EAGAIN:
  495. return 0;
  496. case EIO:
  497. /* Could ignore EIO, see spec. */
  498. /* fallthrough */
  499. default:
  500. errno_exit("VIDIOC_DQBUF");
  501. break;
  502. }
  503. }
  504. //assert(buf.index < n_buffers);
  505. process_image(buffers[buf.index].start, buf.bytesused);
  506. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  507. errno_exit("VIDIOC_QBUF");
  508. }
  509. return 1;
  510. }
  511. gboolean
  512. get_frame()
  513. {
  514. if (ready == 0)
  515. return TRUE;
  516. while (1) {
  517. fd_set fds;
  518. struct timeval tv;
  519. int r;
  520. FD_ZERO(&fds);
  521. FD_SET(video_fd, &fds);
  522. /* Timeout. */
  523. tv.tv_sec = 2;
  524. tv.tv_usec = 0;
  525. r = select(video_fd + 1, &fds, NULL, NULL, &tv);
  526. if (r == -1) {
  527. if (EINTR == errno) {
  528. continue;
  529. }
  530. errno_exit("select");
  531. } else if (r == 0) {
  532. fprintf(stderr, "select timeout\\n");
  533. exit(EXIT_FAILURE);
  534. }
  535. if (read_frame(video_fd)) {
  536. break;
  537. }
  538. /* EAGAIN - continue select loop. */
  539. }
  540. return TRUE;
  541. }
  542. int
  543. strtoint(const char *nptr, char **endptr, int base)
  544. {
  545. long x = strtol(nptr, endptr, base);
  546. assert(x <= INT_MAX);
  547. return (int) x;
  548. }
  549. static int
  550. config_ini_handler(void *user, const char *section, const char *name,
  551. const char *value)
  552. {
  553. struct camerainfo *cc;
  554. if (strcmp(section, "rear") == 0 || strcmp(section, "front") == 0) {
  555. if (strcmp(section, "rear") == 0) {
  556. cc = &rear_cam;
  557. } else {
  558. cc = &front_cam;
  559. }
  560. if (strcmp(name, "width") == 0) {
  561. cc->width = strtoint(value, NULL, 10);
  562. } else if (strcmp(name, "height") == 0) {
  563. cc->height = strtoint(value, NULL, 10);
  564. } else if (strcmp(name, "rate") == 0) {
  565. cc->rate = strtoint(value, NULL, 10);
  566. } else if (strcmp(name, "rotate") == 0) {
  567. cc->rotate = strtoint(value, NULL, 10);
  568. } else if (strcmp(name, "fmt") == 0) {
  569. if (strcmp(value, "RGGB8") == 0) {
  570. cc->fmt = V4L2_PIX_FMT_SRGGB8;
  571. } else if (strcmp(value, "BGGR8") == 0) {
  572. cc->fmt = V4L2_PIX_FMT_SBGGR8;
  573. cc->mbus = MEDIA_BUS_FMT_SBGGR8_1X8;
  574. } else if (strcmp(value, "GRBG8") == 0) {
  575. cc->fmt = V4L2_PIX_FMT_SGRBG8;
  576. } else if (strcmp(value, "GBRG8") == 0) {
  577. cc->fmt = V4L2_PIX_FMT_SGBRG8;
  578. } else {
  579. g_printerr("Unsupported pixelformat %s\n", value);
  580. exit(1);
  581. }
  582. } else if (strcmp(name, "driver") == 0) {
  583. strcpy(cc->dev_name, value);
  584. } else if (strcmp(name, "colormatrix") == 0) {
  585. sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f",
  586. cc->colormatrix+0,
  587. cc->colormatrix+1,
  588. cc->colormatrix+2,
  589. cc->colormatrix+3,
  590. cc->colormatrix+4,
  591. cc->colormatrix+5,
  592. cc->colormatrix+6,
  593. cc->colormatrix+7,
  594. cc->colormatrix+8
  595. );
  596. } else if (strcmp(name, "forwardmatrix") == 0) {
  597. sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f",
  598. cc->forwardmatrix+0,
  599. cc->forwardmatrix+1,
  600. cc->forwardmatrix+2,
  601. cc->forwardmatrix+3,
  602. cc->forwardmatrix+4,
  603. cc->forwardmatrix+5,
  604. cc->forwardmatrix+6,
  605. cc->forwardmatrix+7,
  606. cc->forwardmatrix+8
  607. );
  608. } else if (strcmp(name, "whitelevel") == 0) {
  609. cc->whitelevel = strtoint(value, NULL, 10);
  610. } else if (strcmp(name, "blacklevel") == 0) {
  611. cc->blacklevel = strtoint(value, NULL, 10);
  612. } else {
  613. g_printerr("Unknown key '%s' in [%s]\n", name, section);
  614. exit(1);
  615. }
  616. } else if (strcmp(section, "device") == 0) {
  617. if (strcmp(name, "csi") == 0) {
  618. media_drv_name = strdup(value);
  619. } else if (strcmp(name, "make") == 0) {
  620. exif_make = strdup(value);
  621. } else if (strcmp(name, "model") == 0) {
  622. exif_model = strdup(value);
  623. } else {
  624. g_printerr("Unknown key '%s' in [device]\n", name);
  625. exit(1);
  626. }
  627. } else {
  628. g_printerr("Unknown section '%s' in config file\n", section);
  629. exit(1);
  630. }
  631. return 1;
  632. }
  633. int
  634. find_dev_node(int maj, int min, char *fnbuf)
  635. {
  636. DIR *d;
  637. struct dirent *dir;
  638. struct stat info;
  639. d = opendir("/dev");
  640. while ((dir = readdir(d)) != NULL) {
  641. sprintf(fnbuf, "/dev/%s", dir->d_name);
  642. stat(fnbuf, &info);
  643. if (!S_ISCHR(info.st_mode)) {
  644. continue;
  645. }
  646. if (major(info.st_rdev) == maj && minor(info.st_rdev) == min) {
  647. return 0;
  648. }
  649. }
  650. return -1;
  651. }
  652. int
  653. setup_rear()
  654. {
  655. struct media_link_desc link = {0};
  656. // Disable the interface<->front link
  657. link.flags = 0;
  658. link.source.entity = front_cam.entity_id;
  659. link.source.index = 0;
  660. link.sink.entity = interface_entity_id;
  661. link.sink.index = 0;
  662. if (xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  663. g_printerr("Could not disable front camera link\n");
  664. return -1;
  665. }
  666. // Enable the interface<->rear link
  667. link.flags = MEDIA_LNK_FL_ENABLED;
  668. link.source.entity = rear_cam.entity_id;
  669. link.source.index = 0;
  670. link.sink.entity = interface_entity_id;
  671. link.sink.index = 0;
  672. if (xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  673. g_printerr("Could not enable rear camera link\n");
  674. return -1;
  675. }
  676. current = rear_cam;
  677. // Find camera node
  678. init_sensor(current.dev, current.width, current.height, current.mbus, current.rate);
  679. return 0;
  680. }
  681. int
  682. setup_front()
  683. {
  684. struct media_link_desc link = {0};
  685. // Disable the interface<->rear link
  686. link.flags = 0;
  687. link.source.entity = rear_cam.entity_id;
  688. link.source.index = 0;
  689. link.sink.entity = interface_entity_id;
  690. link.sink.index = 0;
  691. if (xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  692. g_printerr("Could not disable rear camera link\n");
  693. return -1;
  694. }
  695. // Enable the interface<->rear link
  696. link.flags = MEDIA_LNK_FL_ENABLED;
  697. link.source.entity = front_cam.entity_id;
  698. link.source.index = 0;
  699. link.sink.entity = interface_entity_id;
  700. link.sink.index = 0;
  701. if (xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  702. g_printerr("Could not enable front camera link\n");
  703. return -1;
  704. }
  705. current = front_cam;
  706. // Find camera node
  707. init_sensor(current.dev, current.width, current.height, current.mbus, current.rate);
  708. return 0;
  709. }
  710. int
  711. find_cameras()
  712. {
  713. struct media_entity_desc entity = {0};
  714. int ret;
  715. int found = 0;
  716. while (1) {
  717. entity.id = entity.id | MEDIA_ENT_ID_FLAG_NEXT;
  718. ret = xioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
  719. if (ret < 0) {
  720. break;
  721. }
  722. printf("At node %s, (0x%x)\n", entity.name, entity.type);
  723. if (strncmp(entity.name, front_cam.dev_name, strlen(front_cam.dev_name)) == 0) {
  724. front_cam.entity_id = entity.id;
  725. find_dev_node(entity.dev.major, entity.dev.minor, front_cam.dev);
  726. printf("Found front cam, is %s at %s\n", entity.name, front_cam.dev);
  727. found++;
  728. }
  729. if (strncmp(entity.name, rear_cam.dev_name, strlen(rear_cam.dev_name)) == 0) {
  730. rear_cam.entity_id = entity.id;
  731. find_dev_node(entity.dev.major, entity.dev.minor, rear_cam.dev);
  732. printf("Found rear cam, is %s at %s\n", entity.name, rear_cam.dev);
  733. found++;
  734. }
  735. if (entity.type == MEDIA_ENT_F_IO_V4L) {
  736. interface_entity_id = entity.id;
  737. find_dev_node(entity.dev.major, entity.dev.minor, dev_name);
  738. printf("Found v4l2 interface node at %s\n", dev_name);
  739. }
  740. }
  741. if (found < 2) {
  742. return -1;
  743. }
  744. return 0;
  745. }
  746. int
  747. find_media_fd()
  748. {
  749. DIR *d;
  750. struct dirent *dir;
  751. int fd;
  752. char fnbuf[261];
  753. struct media_device_info mdi = {0};
  754. d = opendir("/dev");
  755. while ((dir = readdir(d)) != NULL) {
  756. if (strncmp(dir->d_name, "media", 5) == 0) {
  757. sprintf(fnbuf, "/dev/%s", dir->d_name);
  758. printf("Checking %s\n", fnbuf);
  759. fd = open(fnbuf, O_RDWR);
  760. xioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi);
  761. printf("Found media device: %s\n", mdi.driver);
  762. if (strcmp(mdi.driver, media_drv_name) == 0) {
  763. media_fd = fd;
  764. return 0;
  765. }
  766. close(fd);
  767. }
  768. }
  769. return 1;
  770. }
  771. void
  772. on_open_last_clicked(GtkWidget *widget, gpointer user_data)
  773. {
  774. char uri[270];
  775. GError *error = NULL;
  776. if(!last_path) {
  777. return;
  778. }
  779. sprintf(uri, "file://%s", last_path);
  780. if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){
  781. g_printerr("Could not launch image viewer: %s\n", error->message);
  782. }
  783. }
  784. void
  785. on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
  786. {
  787. char uri[270];
  788. GError *error = NULL;
  789. sprintf(uri, "file://%s/Pictures", getenv("HOME"));
  790. if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){
  791. g_printerr("Could not launch image viewer: %s\n", error->message);
  792. }
  793. }
  794. void
  795. on_shutter_clicked(GtkWidget *widget, gpointer user_data)
  796. {
  797. capture = 5;
  798. }
  799. void
  800. on_error_close_clicked(GtkWidget *widget, gpointer user_data)
  801. {
  802. gtk_widget_hide(error_box);
  803. }
  804. void
  805. on_camera_switch_clicked(GtkWidget *widget, gpointer user_data)
  806. {
  807. stop_capturing(video_fd);
  808. close(current.fd);
  809. if (current_is_rear == 1) {
  810. setup_front();
  811. current_is_rear = 0;
  812. } else {
  813. setup_rear();
  814. current_is_rear = 1;
  815. }
  816. close(video_fd);
  817. video_fd = open(dev_name, O_RDWR);
  818. if (video_fd == -1) {
  819. g_printerr("Error opening video device: %s\n", dev_name);
  820. return;
  821. }
  822. init_device(video_fd);
  823. start_capturing(video_fd);
  824. }
  825. void
  826. on_settings_btn_clicked(GtkWidget *widget, gpointer user_data)
  827. {
  828. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  829. }
  830. void
  831. on_back_clicked(GtkWidget *widget, gpointer user_data)
  832. {
  833. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  834. }
  835. int
  836. find_config(char *conffile)
  837. {
  838. char buf[512];
  839. char *xdg_config_home;
  840. wordexp_t exp_result;
  841. FILE *fp;
  842. // Resolve XDG stuff
  843. if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
  844. xdg_config_home = "~/.config";
  845. }
  846. wordexp(xdg_config_home, &exp_result, 0);
  847. xdg_config_home = strdup(exp_result.we_wordv[0]);
  848. wordfree(&exp_result);
  849. if(access("/proc/device-tree/compatible", F_OK) != -1) {
  850. // Reads to compatible string of the current device tree, looks like:
  851. // pine64,pinephone-1.2\0allwinner,sun50i-a64\0
  852. fp = fopen("/proc/device-tree/compatible", "r");
  853. fgets(buf, 512, fp);
  854. fclose(fp);
  855. // Check config/%dt.ini in the current working directory
  856. sprintf(conffile, "config/%s.ini", buf);
  857. if(access(conffile, F_OK) != -1) {
  858. printf("Found config file at %s\n", conffile);
  859. return 0;
  860. }
  861. // Check for a config file in XDG_CONFIG_HOME
  862. sprintf(conffile, "%s/megapixels/config/%s.ini", xdg_config_home, buf);
  863. if(access(conffile, F_OK) != -1) {
  864. printf("Found config file at %s\n", conffile);
  865. return 0;
  866. }
  867. // Check user overridden /etc/megapixels/config/$dt.ini
  868. sprintf(conffile, "%s/megapixels/config/%s.ini", SYSCONFDIR, buf);
  869. if(access(conffile, F_OK) != -1) {
  870. printf("Found config file at %s\n", conffile);
  871. return 0;
  872. }
  873. // Check packaged /usr/share/megapixels/config/$dt.ini
  874. sprintf(conffile, "%s/megapixels/config/%s.ini", DATADIR, buf);
  875. if(access(conffile, F_OK) != -1) {
  876. printf("Found config file at %s\n", conffile);
  877. return 0;
  878. }
  879. printf("%s not found\n", conffile);
  880. } else {
  881. printf("Could not read device name from device tree\n");
  882. }
  883. // If all else fails, fall back to /etc/megapixels.ini
  884. conffile = "/etc/megapixels.ini";
  885. if(access(conffile, F_OK) != -1) {
  886. printf("Found config file at %s\n", conffile);
  887. return 0;
  888. }
  889. return -1;
  890. }
  891. int
  892. main(int argc, char *argv[])
  893. {
  894. char conffile[512];
  895. find_config(conffile);
  896. gtk_init(&argc, &argv);
  897. g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL);
  898. GtkBuilder *builder = gtk_builder_new_from_resource("/org/postmarketos/Megapixels/camera.glade");
  899. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  900. GtkWidget *preview_box = GTK_WIDGET(gtk_builder_get_object(builder, "preview_box"));
  901. GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
  902. GtkWidget *switch_btn = GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera"));
  903. GtkWidget *settings_btn = GTK_WIDGET(gtk_builder_get_object(builder, "settings"));
  904. GtkWidget *settings_back = GTK_WIDGET(gtk_builder_get_object(builder, "settings_back"));
  905. GtkWidget *error_close = GTK_WIDGET(gtk_builder_get_object(builder, "error_close"));
  906. GtkWidget *open_last = GTK_WIDGET(gtk_builder_get_object(builder, "open_last"));
  907. GtkWidget *open_directory = GTK_WIDGET(gtk_builder_get_object(builder, "open_directory"));
  908. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  909. error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box"));
  910. error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message"));
  911. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  912. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  913. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  914. g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
  915. g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked), NULL);
  916. g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked), NULL);
  917. g_signal_connect(settings_btn, "clicked", G_CALLBACK(on_settings_btn_clicked), NULL);
  918. g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked), NULL);
  919. g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked), NULL);
  920. g_signal_connect(open_directory, "clicked", G_CALLBACK(on_open_directory_clicked), NULL);
  921. g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
  922. g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure), NULL);
  923. GtkCssProvider *provider = gtk_css_provider_new();
  924. if (access("camera.css", F_OK) != -1) {
  925. gtk_css_provider_load_from_path(provider, "camera.css", NULL);
  926. } else {
  927. gtk_css_provider_load_from_resource(provider, "/org/postmarketos/Megapixels/camera.css");
  928. }
  929. GtkStyleContext *context = gtk_widget_get_style_context(preview_box);
  930. gtk_style_context_add_provider(context,
  931. GTK_STYLE_PROVIDER(provider),
  932. GTK_STYLE_PROVIDER_PRIORITY_USER);
  933. context = gtk_widget_get_style_context(error_box);
  934. gtk_style_context_add_provider(context,
  935. GTK_STYLE_PROVIDER(provider),
  936. GTK_STYLE_PROVIDER_PRIORITY_USER);
  937. int result = ini_parse(conffile, config_ini_handler, NULL);
  938. if (result == -1) {
  939. g_printerr("Config file not found\n");
  940. return 1;
  941. } else if (result == -2) {
  942. g_printerr("Could not allocate memory to parse config file\n");
  943. return 1;
  944. } else if (result != 0) {
  945. g_printerr("Could not parse config file\n");
  946. return 1;
  947. }
  948. if (find_media_fd() == -1) {
  949. g_printerr("Could not find the media node\n");
  950. show_error("Could not find the media node");
  951. goto failed;
  952. }
  953. if (find_cameras() == -1) {
  954. g_printerr("Could not find the cameras\n");
  955. show_error("Could not find the cameras");
  956. goto failed;
  957. }
  958. setup_rear();
  959. int fd = open(dev_name, O_RDWR);
  960. if (fd == -1) {
  961. g_printerr("Error opening video device: %s\n", dev_name);
  962. show_error("Error opening the video device");
  963. goto failed;
  964. }
  965. video_fd = fd;
  966. if(init_device(fd) < 0){
  967. goto failed;
  968. }
  969. start_capturing(fd);
  970. failed:
  971. printf("window show\n");
  972. gtk_widget_show(window);
  973. g_idle_add((GSourceFunc)get_frame, NULL);
  974. gtk_main();
  975. return 0;
  976. }