process_pipeline.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. #include "process_pipeline.h"
  2. #include "pipeline.h"
  3. #include "zbar_pipeline.h"
  4. #include "io_pipeline.h"
  5. #include "main.h"
  6. #include "config.h"
  7. #include "quickpreview.h"
  8. #include "gl_quickpreview.h"
  9. #include <tiffio.h>
  10. #include <assert.h>
  11. #include <math.h>
  12. #include <wordexp.h>
  13. #include <gtk/gtk.h>
  14. #include "gl_utils.h"
  15. #include <EGL/egl.h>
  16. #include <EGL/eglext.h>
  17. #include <gdk/gdkwayland.h>
  18. #include <sys/mman.h>
  19. #include <drm/drm_fourcc.h>
  20. #define TIFFTAG_FORWARDMATRIX1 50964
  21. static const float colormatrix_srgb[] = { 3.2409, -1.5373, -0.4986, -0.9692, 1.8759,
  22. 0.0415, 0.0556, -0.2039, 1.0569 };
  23. static MPPipeline *pipeline;
  24. static char burst_dir[23];
  25. static char processing_script[512];
  26. static volatile bool is_capturing = false;
  27. static volatile int frames_processed = 0;
  28. static volatile int frames_received = 0;
  29. static const struct mp_camera_config *camera;
  30. static MPCameraMode mode;
  31. static int burst_length;
  32. static int captures_remaining = 0;
  33. static int preview_width;
  34. static int preview_height;
  35. // static bool gain_is_manual;
  36. static int gain;
  37. static int gain_max;
  38. static bool exposure_is_manual;
  39. static int exposure;
  40. static char capture_fname[255];
  41. static void
  42. register_custom_tiff_tags(TIFF *tif)
  43. {
  44. static const TIFFFieldInfo custom_fields[] = {
  45. { TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, FIELD_CUSTOM, 1, 1,
  46. "ForwardMatrix1" },
  47. };
  48. // Add missing dng fields
  49. TIFFMergeFieldInfo(tif, custom_fields,
  50. sizeof(custom_fields) / sizeof(custom_fields[0]));
  51. }
  52. static bool
  53. find_processor(char *script)
  54. {
  55. char *xdg_config_home;
  56. char filename[] = "postprocess.sh";
  57. wordexp_t exp_result;
  58. // Resolve XDG stuff
  59. if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
  60. xdg_config_home = "~/.config";
  61. }
  62. wordexp(xdg_config_home, &exp_result, 0);
  63. xdg_config_home = strdup(exp_result.we_wordv[0]);
  64. wordfree(&exp_result);
  65. // Check postprocess.h in the current working directory
  66. sprintf(script, "%s", filename);
  67. if (access(script, F_OK) != -1) {
  68. sprintf(script, "./%s", filename);
  69. printf("Found postprocessor script at %s\n", script);
  70. return true;
  71. }
  72. // Check for a script in XDG_CONFIG_HOME
  73. sprintf(script, "%s/megapixels/%s", xdg_config_home, filename);
  74. if (access(script, F_OK) != -1) {
  75. printf("Found postprocessor script at %s\n", script);
  76. return true;
  77. }
  78. // Check user overridden /etc/megapixels/postprocessor.sh
  79. sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
  80. if (access(script, F_OK) != -1) {
  81. printf("Found postprocessor script at %s\n", script);
  82. return true;
  83. }
  84. // Check packaged /usr/share/megapixels/postprocessor.sh
  85. sprintf(script, "%s/megapixels/%s", DATADIR, filename);
  86. if (access(script, F_OK) != -1) {
  87. printf("Found postprocessor script at %s\n", script);
  88. return true;
  89. }
  90. return false;
  91. }
  92. static void
  93. setup(MPPipeline *pipeline, const void *data)
  94. {
  95. TIFFSetTagExtender(register_custom_tiff_tags);
  96. if (!find_processor(processing_script)) {
  97. g_printerr("Could not find any post-process script\n");
  98. exit(1);
  99. }
  100. }
  101. void
  102. mp_process_pipeline_start()
  103. {
  104. pipeline = mp_pipeline_new();
  105. mp_pipeline_invoke(pipeline, setup, NULL, 0);
  106. mp_zbar_pipeline_start();
  107. }
  108. void
  109. mp_process_pipeline_stop()
  110. {
  111. mp_pipeline_free(pipeline);
  112. mp_zbar_pipeline_stop();
  113. }
  114. void
  115. mp_process_pipeline_sync()
  116. {
  117. mp_pipeline_sync(pipeline);
  118. }
  119. #define NUM_BUFFERS 4
  120. static GLQuickPreview *gl_quick_preview_state = NULL;
  121. static EGLDisplay egl_display = EGL_NO_DISPLAY;
  122. static EGLContext egl_context = EGL_NO_CONTEXT;
  123. // struct buffer {
  124. // GLuint texture_id;
  125. // EGLImage egl_image;
  126. // int dma_fd;
  127. // int dma_stride;
  128. // int dma_offset;
  129. // void *data;
  130. // };
  131. // static struct buffer input_buffers[NUM_BUFFERS];
  132. // static struct buffer output_buffers[NUM_BUFFERS];
  133. static PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
  134. static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
  135. static const char *
  136. egl_get_error_str()
  137. {
  138. EGLint error = eglGetError();
  139. switch (error) {
  140. case EGL_SUCCESS: return "EGL_SUCCESS";
  141. case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
  142. case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
  143. case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
  144. case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
  145. case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
  146. case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
  147. case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
  148. case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
  149. case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
  150. case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
  151. case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
  152. case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
  153. case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
  154. case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
  155. }
  156. return "Unknown";
  157. }
  158. #define RENDERDOC
  159. #ifdef RENDERDOC
  160. #include <dlfcn.h>
  161. #include <renderdoc/app.h>
  162. RENDERDOC_API_1_1_2 *rdoc_api = NULL;
  163. #endif
  164. static void
  165. init_gl(MPPipeline *pipeline, GdkWindow **window)
  166. {
  167. #ifdef RENDERDOC
  168. {
  169. void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
  170. if (mod)
  171. {
  172. pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
  173. int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
  174. assert(ret == 1);
  175. }
  176. else
  177. {
  178. printf("Renderdoc not found\n");
  179. }
  180. }
  181. #endif
  182. GdkDisplay *gdk_display = gdk_window_get_display(*window);
  183. egl_display = eglGetDisplay((EGLNativeDisplayType) gdk_wayland_display_get_wl_display(gdk_display));
  184. assert(egl_display != EGL_NO_DISPLAY);
  185. EGLint major, minor;
  186. if (!eglInitialize(egl_display, &major, &minor)) {
  187. printf("Failed to initialize egl: %s\n", egl_get_error_str());
  188. return;
  189. }
  190. if (!eglBindAPI(EGL_OPENGL_ES_API)) {
  191. printf("Failed to bind OpenGL ES: %s\n", egl_get_error_str());
  192. return;
  193. }
  194. printf("extensions: %s\n", eglQueryString(egl_display, EGL_EXTENSIONS));
  195. EGLint config_attrs[1] = {
  196. EGL_NONE
  197. };
  198. EGLConfig config;
  199. EGLint num_configs = 0;
  200. if (!eglChooseConfig(egl_display, config_attrs, &config, 1, &num_configs)) {
  201. printf("Failed to pick a egl config: %s\n", egl_get_error_str());
  202. return;
  203. }
  204. if (num_configs != 1) {
  205. printf("No egl configs found: %s\n", egl_get_error_str());
  206. return;
  207. }
  208. EGLint context_attrs[5] = {
  209. EGL_CONTEXT_CLIENT_VERSION,
  210. 2,
  211. EGL_CONTEXT_FLAGS_KHR,
  212. EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
  213. EGL_NONE,
  214. };
  215. egl_context = eglCreateContext(egl_display, config, NULL, context_attrs);
  216. if (egl_context == EGL_NO_CONTEXT) {
  217. printf("Failed to create OpenGL ES context: %s\n", egl_get_error_str());
  218. return;
  219. }
  220. if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context))
  221. {
  222. printf("Failed to set current OpenGL context: %s\n", egl_get_error_str());
  223. return;
  224. }
  225. check_gl();
  226. eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)
  227. eglGetProcAddress("eglExportDMABUFImageMESA");
  228. eglExportDMABUFImageQueryMESA = (PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)
  229. eglGetProcAddress("eglExportDMABUFImageQueryMESA");
  230. // Generate textures for the buffers
  231. // GLuint textures[NUM_BUFFERS * 2];
  232. // glGenTextures(NUM_BUFFERS * 2, textures);
  233. // for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  234. // input_buffers[i].texture_id = textures[i];
  235. // input_buffers[i].egl_image = EGL_NO_IMAGE;
  236. // input_buffers[i].dma_fd = -1;
  237. // }
  238. // for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  239. // output_buffers[i].texture_id = textures[NUM_BUFFERS + i];
  240. // output_buffers[i].egl_image = EGL_NO_IMAGE;
  241. // output_buffers[i].dma_fd = -1;
  242. // }
  243. gl_quick_preview_state = gl_quick_preview_new();
  244. check_gl();
  245. printf("Initialized OpenGL\n");
  246. }
  247. void
  248. mp_process_pipeline_init_gl(GdkWindow *window)
  249. {
  250. mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &window, sizeof(GdkWindow *));
  251. }
  252. static cairo_surface_t *
  253. process_image_for_preview(const uint8_t *image)
  254. {
  255. cairo_surface_t *surface;
  256. clock_t t1 = clock();
  257. if (gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)) {
  258. #ifdef RENDERDOC
  259. if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
  260. #endif
  261. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  262. check_gl();
  263. GLuint textures[2];
  264. glGenTextures(2, textures);
  265. glBindTexture(GL_TEXTURE_2D, textures[0]);
  266. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  267. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  268. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  269. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  270. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
  271. check_gl();
  272. glBindTexture(GL_TEXTURE_2D, textures[1]);
  273. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  274. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  275. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, preview_width, preview_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  276. check_gl();
  277. gl_quick_preview(
  278. gl_quick_preview_state,
  279. textures[1], preview_width, preview_height,
  280. textures[0], mode.width, mode.height,
  281. mode.pixel_format,
  282. camera->rotate, camera->mirrored,
  283. camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
  284. camera->blacklevel);
  285. check_gl();
  286. surface = cairo_image_surface_create(
  287. CAIRO_FORMAT_RGB24, preview_width, preview_height);
  288. uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(surface);
  289. glFinish();
  290. clock_t t2 = clock();
  291. printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
  292. // {
  293. // glBindTexture(GL_TEXTURE_2D, textures[1]);
  294. // EGLImage egl_image = eglCreateImage(egl_display, egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(size_t)textures[1], NULL);
  295. // // Make sure it's in the expected format
  296. // int fourcc;
  297. // eglExportDMABUFImageQueryMESA(egl_display, egl_image, &fourcc, NULL, NULL);
  298. // assert(fourcc == DRM_FORMAT_ABGR8888);
  299. // int dmabuf_fd;
  300. // int stride, offset;
  301. // eglExportDMABUFImageMESA(egl_display, egl_image, &dmabuf_fd, &stride, &offset);
  302. // int fsize = lseek(dmabuf_fd, 0, SEEK_END);
  303. // printf("SIZE %d STRIDE %d OFFSET %d SIZE %d:%d\n", fsize, stride, offset, preview_width, preview_height);
  304. // size_t size = stride * preview_height;
  305. // uint32_t *data = mmap(NULL, fsize, PROT_READ, MAP_SHARED, dmabuf_fd, 0);
  306. // assert(data != MAP_FAILED);
  307. // int pixel_stride = stride / 4;
  308. // for (size_t y = 0; y < preview_height; ++y) {
  309. // for (size_t x = 0; x < preview_width; ++x) {
  310. // uint32_t p = data[x + y * pixel_stride];
  311. // pixels[x + y * preview_width] = p;
  312. // // uint16_t p = data[x + y * stride];
  313. // // uint32_t r = (p & 0b11111);
  314. // // uint32_t g = ((p >> 5) & 0b11111);
  315. // // uint32_t b = ((p >> 10) & 0b11111);
  316. // // pixels[x + y * preview_width] = (r << 16) | (g << 8) | b;
  317. // }
  318. // // memcpy(pixels + preview_width * y, data + stride * y, preview_width * sizeof(uint32_t));
  319. // }
  320. // {
  321. // FILE *f = fopen("test.raw", "w");
  322. // fwrite(data, fsize, 1, f);
  323. // fclose(f);
  324. // }
  325. // // memcpy(pixels, data, size);
  326. // munmap(data, size);
  327. // close(dmabuf_fd);
  328. // }
  329. glReadPixels(0, 0, preview_width, preview_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  330. check_gl();
  331. glBindTexture(GL_TEXTURE_2D, 0);
  332. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  333. #ifdef RENDERDOC
  334. if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
  335. #endif
  336. } else {
  337. uint32_t surface_width, surface_height, skip;
  338. quick_preview_size(&surface_width, &surface_height, &skip, preview_width,
  339. preview_height, mode.width, mode.height,
  340. mode.pixel_format, camera->rotate);
  341. surface = cairo_image_surface_create(
  342. CAIRO_FORMAT_RGB24, surface_width, surface_height);
  343. uint8_t *pixels = cairo_image_surface_get_data(surface);
  344. quick_preview((uint32_t *)pixels, surface_width, surface_height, image,
  345. mode.width, mode.height, mode.pixel_format,
  346. camera->rotate, camera->mirrored,
  347. camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
  348. camera->blacklevel, skip);
  349. }
  350. // Create a thumbnail from the preview for the last capture
  351. cairo_surface_t *thumb = NULL;
  352. if (captures_remaining == 1) {
  353. printf("Making thumbnail\n");
  354. thumb = cairo_image_surface_create(
  355. CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE);
  356. cairo_t *cr = cairo_create(thumb);
  357. draw_surface_scaled_centered(
  358. cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface);
  359. cairo_destroy(cr);
  360. }
  361. // Pass processed preview to main and zbar
  362. mp_zbar_pipeline_process_image(cairo_surface_reference(surface));
  363. mp_main_set_preview(surface);
  364. return thumb;
  365. }
  366. static void
  367. process_image_for_capture(const uint8_t *image, int count)
  368. {
  369. time_t rawtime;
  370. time(&rawtime);
  371. struct tm tim = *(localtime(&rawtime));
  372. char datetime[20] = { 0 };
  373. strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
  374. char fname[255];
  375. sprintf(fname, "%s/%d.dng", burst_dir, count);
  376. TIFF *tif = TIFFOpen(fname, "w");
  377. if (!tif) {
  378. printf("Could not open tiff\n");
  379. }
  380. // Define TIFF thumbnail
  381. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
  382. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width >> 4);
  383. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height >> 4);
  384. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  385. TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  386. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  387. TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make());
  388. TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model());
  389. uint16_t orientation;
  390. if (camera->rotate == 0) {
  391. orientation = camera->mirrored ? ORIENTATION_TOPRIGHT :
  392. ORIENTATION_TOPLEFT;
  393. } else if (camera->rotate == 90) {
  394. orientation = camera->mirrored ? ORIENTATION_RIGHTBOT :
  395. ORIENTATION_LEFTBOT;
  396. } else if (camera->rotate == 180) {
  397. orientation = camera->mirrored ? ORIENTATION_BOTLEFT :
  398. ORIENTATION_BOTRIGHT;
  399. } else {
  400. orientation = camera->mirrored ? ORIENTATION_LEFTTOP :
  401. ORIENTATION_RIGHTTOP;
  402. }
  403. TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
  404. TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
  405. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
  406. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  407. TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
  408. long sub_offset = 0;
  409. TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);
  410. TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
  411. TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
  412. char uniquecameramodel[255];
  413. sprintf(uniquecameramodel, "%s %s", mp_get_device_make(),
  414. mp_get_device_model());
  415. TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
  416. if (camera->colormatrix[0]) {
  417. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
  418. } else {
  419. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
  420. }
  421. if (camera->forwardmatrix[0]) {
  422. TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
  423. }
  424. static const float neutral[] = { 1.0, 1.0, 1.0 };
  425. TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
  426. TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
  427. // Write black thumbnail, only windows uses this
  428. {
  429. unsigned char *buf =
  430. (unsigned char *)calloc(1, (int)mode.width >> 4);
  431. for (int row = 0; row < (mode.height >> 4); row++) {
  432. TIFFWriteScanline(tif, buf, row, 0);
  433. }
  434. free(buf);
  435. }
  436. TIFFWriteDirectory(tif);
  437. // Define main photo
  438. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
  439. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width);
  440. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height);
  441. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  442. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
  443. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
  444. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  445. static const short cfapatterndim[] = { 2, 2 };
  446. TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim);
  447. #if (TIFFLIB_VERSION < 20201219) && !LIBTIFF_CFA_PATTERN
  448. TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR
  449. #else
  450. TIFFSetField(tif, TIFFTAG_CFAPATTERN, 4, "\002\001\001\000"); // BGGR
  451. #endif
  452. printf("TIFF version %d\n", TIFFLIB_VERSION);
  453. if (camera->whitelevel) {
  454. TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &camera->whitelevel);
  455. }
  456. if (camera->blacklevel) {
  457. TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &camera->blacklevel);
  458. }
  459. TIFFCheckpointDirectory(tif);
  460. printf("Writing frame to %s\n", fname);
  461. unsigned char *pLine = (unsigned char *)malloc(mode.width);
  462. for (int row = 0; row < mode.height; row++) {
  463. TIFFWriteScanline(tif, (void *) image + (row * mode.width), row, 0);
  464. }
  465. free(pLine);
  466. TIFFWriteDirectory(tif);
  467. // Add an EXIF block to the tiff
  468. TIFFCreateEXIFDirectory(tif);
  469. // 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority
  470. if (!exposure_is_manual) {
  471. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2);
  472. } else {
  473. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
  474. }
  475. TIFFSetField(tif, EXIFTAG_EXPOSURETIME,
  476. (mode.frame_interval.numerator /
  477. (float)mode.frame_interval.denominator) /
  478. ((float)mode.height / (float)exposure));
  479. uint16_t isospeed[1];
  480. isospeed[0] = (uint16_t)remap(gain - 1, 0, gain_max, camera->iso_min,
  481. camera->iso_max);
  482. TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed);
  483. TIFFSetField(tif, EXIFTAG_FLASH, 0);
  484. TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
  485. TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
  486. if (camera->fnumber) {
  487. TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber);
  488. }
  489. if (camera->focallength) {
  490. TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength);
  491. }
  492. if (camera->focallength && camera->cropfactor) {
  493. TIFFSetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM,
  494. (short)(camera->focallength * camera->cropfactor));
  495. }
  496. uint64_t exif_offset = 0;
  497. TIFFWriteCustomDirectory(tif, &exif_offset);
  498. TIFFFreeDirectory(tif);
  499. // Update exif pointer
  500. TIFFSetDirectory(tif, 0);
  501. TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
  502. TIFFRewriteDirectory(tif);
  503. TIFFClose(tif);
  504. }
  505. static void
  506. post_process_finished(GSubprocess *proc, GAsyncResult *res, cairo_surface_t *thumb)
  507. {
  508. char *stdout;
  509. g_subprocess_communicate_utf8_finish(proc, res, &stdout, NULL, NULL);
  510. // The last line contains the file name
  511. int end = strlen(stdout);
  512. // Skip the newline at the end
  513. stdout[--end] = '\0';
  514. char *path = path = stdout + end - 1;
  515. do {
  516. if (*path == '\n') {
  517. path++;
  518. break;
  519. }
  520. --path;
  521. } while (path > stdout);
  522. mp_main_capture_completed(thumb, path);
  523. }
  524. static void
  525. process_capture_burst(cairo_surface_t *thumb)
  526. {
  527. time_t rawtime;
  528. time(&rawtime);
  529. struct tm tim = *(localtime(&rawtime));
  530. char timestamp[30];
  531. strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
  532. if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
  533. sprintf(capture_fname,
  534. "%s/IMG%s",
  535. g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
  536. timestamp);
  537. } else if (getenv("XDG_PICTURES_DIR") != NULL) {
  538. sprintf(capture_fname,
  539. "%s/IMG%s",
  540. getenv("XDG_PICTURES_DIR"),
  541. timestamp);
  542. } else {
  543. sprintf(capture_fname,
  544. "%s/Pictures/IMG%s",
  545. getenv("HOME"),
  546. timestamp);
  547. }
  548. // Start post-processing the captured burst
  549. g_print("Post process %s to %s.ext\n", burst_dir, capture_fname);
  550. GError *error = NULL;
  551. GSubprocess *proc = g_subprocess_new(
  552. G_SUBPROCESS_FLAGS_STDOUT_PIPE,
  553. &error,
  554. processing_script,
  555. burst_dir,
  556. capture_fname,
  557. NULL);
  558. if (!proc) {
  559. g_printerr("Failed to spawn postprocess process: %s\n",
  560. error->message);
  561. return;
  562. }
  563. g_subprocess_communicate_utf8_async(
  564. proc,
  565. NULL,
  566. NULL,
  567. (GAsyncReadyCallback)post_process_finished,
  568. thumb);
  569. }
  570. static void
  571. process_image(MPPipeline *pipeline, const MPBuffer *buffer)
  572. {
  573. size_t size =
  574. mp_pixel_format_width_to_bytes(mode.pixel_format, mode.width) *
  575. mode.height;
  576. uint8_t *image = malloc(size);
  577. memcpy(image, buffer->data, size);
  578. mp_io_pipeline_release_buffer(buffer->index);
  579. cairo_surface_t *thumb = process_image_for_preview(image);
  580. if (captures_remaining > 0) {
  581. int count = burst_length - captures_remaining;
  582. --captures_remaining;
  583. process_image_for_capture(image, count);
  584. if (captures_remaining == 0) {
  585. assert(thumb);
  586. process_capture_burst(thumb);
  587. } else {
  588. assert(!thumb);
  589. }
  590. } else {
  591. assert(!thumb);
  592. }
  593. free(image);
  594. ++frames_processed;
  595. if (captures_remaining == 0) {
  596. is_capturing = false;
  597. }
  598. }
  599. void
  600. mp_process_pipeline_process_image(MPBuffer buffer)
  601. {
  602. // If we haven't processed the previous frame yet, drop this one
  603. if (frames_received != frames_processed && !is_capturing) {
  604. printf("Dropped frame at capture\n");
  605. mp_io_pipeline_release_buffer(buffer.index);
  606. return;
  607. }
  608. ++frames_received;
  609. mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &buffer,
  610. sizeof(MPBuffer));
  611. }
  612. static void
  613. capture()
  614. {
  615. char template[] = "/tmp/megapixels.XXXXXX";
  616. char *tempdir;
  617. tempdir = mkdtemp(template);
  618. if (tempdir == NULL) {
  619. g_printerr("Could not make capture directory %s\n", template);
  620. exit(EXIT_FAILURE);
  621. }
  622. strcpy(burst_dir, tempdir);
  623. captures_remaining = burst_length;
  624. }
  625. void
  626. mp_process_pipeline_capture()
  627. {
  628. is_capturing = true;
  629. mp_pipeline_invoke(pipeline, capture, NULL, 0);
  630. }
  631. static void
  632. update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
  633. {
  634. camera = state->camera;
  635. mode = state->mode;
  636. burst_length = state->burst_length;
  637. preview_width = state->preview_width;
  638. preview_height = state->preview_height;
  639. // gain_is_manual = state->gain_is_manual;
  640. gain = state->gain;
  641. gain_max = state->gain_max;
  642. exposure_is_manual = state->exposure_is_manual;
  643. exposure = state->exposure;
  644. struct mp_main_state main_state = {
  645. .camera = camera,
  646. .mode = mode,
  647. .gain_is_manual = state->gain_is_manual,
  648. .gain = gain,
  649. .gain_max = gain_max,
  650. .exposure_is_manual = exposure_is_manual,
  651. .exposure = exposure,
  652. .has_auto_focus_continuous = state->has_auto_focus_continuous,
  653. .has_auto_focus_start = state->has_auto_focus_start,
  654. };
  655. mp_main_update_state(&main_state);
  656. }
  657. void
  658. mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_state)
  659. {
  660. mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state,
  661. sizeof(struct mp_process_pipeline_state));
  662. }