process_pipeline.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  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 "gles2_debayer.h"
  8. #include <tiffio.h>
  9. #include <assert.h>
  10. #include <math.h>
  11. #include <wordexp.h>
  12. #include <gtk/gtk.h>
  13. #include "gl_util.h"
  14. #include <sys/mman.h>
  15. #include <drm/drm_fourcc.h>
  16. #define TIFFTAG_FORWARDMATRIX1 50964
  17. static const float colormatrix_srgb[] = { 3.2409, -1.5373, -0.4986, -0.9692, 1.8759,
  18. 0.0415, 0.0556, -0.2039, 1.0569 };
  19. static MPPipeline *pipeline;
  20. static char burst_dir[23];
  21. static char processing_script[512];
  22. static volatile bool is_capturing = false;
  23. static volatile int frames_processed = 0;
  24. static volatile int frames_received = 0;
  25. static const struct mp_camera_config *camera;
  26. static int camera_rotation;
  27. static MPCameraMode mode;
  28. static int burst_length;
  29. static int captures_remaining = 0;
  30. static int preview_width;
  31. static int preview_height;
  32. static int device_rotation;
  33. static int output_buffer_width = -1;
  34. static int output_buffer_height = -1;
  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, "data/%s", filename);
  67. if (access(script, F_OK) != -1) {
  68. sprintf(script, "./data/%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. struct _MPProcessPipelineBuffer {
  121. GLuint texture_id;
  122. _Atomic(int) refcount;
  123. };
  124. static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS];
  125. void
  126. mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf)
  127. {
  128. ++buf->refcount;
  129. }
  130. void
  131. mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf)
  132. {
  133. --buf->refcount;
  134. }
  135. uint32_t
  136. mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
  137. {
  138. return buf->texture_id;
  139. }
  140. static GLES2Debayer *gles2_debayer = NULL;
  141. static GdkGLContext *context;
  142. #define RENDERDOC
  143. #ifdef RENDERDOC
  144. #include <renderdoc/app.h>
  145. extern RENDERDOC_API_1_1_2 *rdoc_api;
  146. #endif
  147. static void
  148. init_gl(MPPipeline *pipeline, GdkSurface **surface)
  149. {
  150. GError *error = NULL;
  151. context = gdk_surface_create_gl_context(*surface, &error);
  152. if (context == NULL) {
  153. printf("Failed to initialize OpenGL context: %s\n", error->message);
  154. g_clear_error(&error);
  155. return;
  156. }
  157. gdk_gl_context_set_use_es(context, true);
  158. gdk_gl_context_set_required_version(context, 2, 0);
  159. gdk_gl_context_set_forward_compatible(context, false);
  160. #ifdef DEBUG
  161. gdk_gl_context_set_debug_enabled(context, true);
  162. #else
  163. gdk_gl_context_set_debug_enabled(context, false);
  164. #endif
  165. gdk_gl_context_realize(context, &error);
  166. if (error != NULL) {
  167. printf("Failed to create OpenGL context: %s\n", error->message);
  168. g_clear_object(&context);
  169. g_clear_error(&error);
  170. return;
  171. }
  172. gdk_gl_context_make_current(context);
  173. check_gl();
  174. // Make a VAO for OpenGL
  175. if (!gdk_gl_context_get_use_es(context)) {
  176. GLuint vao;
  177. glGenVertexArrays(1, &vao);
  178. glBindVertexArray(vao);
  179. check_gl();
  180. }
  181. gles2_debayer = gles2_debayer_new(MP_PIXEL_FMT_BGGR8);
  182. check_gl();
  183. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  184. check_gl();
  185. gles2_debayer_use(gles2_debayer);
  186. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  187. glGenTextures(1, &output_buffers[i].texture_id);
  188. glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
  189. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  190. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  191. }
  192. glBindTexture(GL_TEXTURE_2D, 0);
  193. gboolean is_es = gdk_gl_context_get_use_es(context);
  194. int major, minor;
  195. gdk_gl_context_get_version(context, &major, &minor);
  196. printf("Initialized %s %d.%d\n", is_es ? "OpenGL ES" : "OpenGL", major, minor);
  197. }
  198. void
  199. mp_process_pipeline_init_gl(GdkSurface *surface)
  200. {
  201. mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &surface, sizeof(GdkSurface *));
  202. }
  203. static GdkTexture *
  204. process_image_for_preview(const uint8_t *image)
  205. {
  206. #ifdef PROFILE_DEBAYER
  207. clock_t t1 = clock();
  208. #endif
  209. // Pick an available buffer
  210. MPProcessPipelineBuffer *output_buffer = NULL;
  211. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  212. if (output_buffers[i].refcount == 0) {
  213. output_buffer = &output_buffers[i];
  214. }
  215. }
  216. if (output_buffer == NULL) {
  217. return NULL;
  218. }
  219. assert(output_buffer != NULL);
  220. #ifdef RENDERDOC
  221. if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
  222. #endif
  223. // Copy image to a GL texture. TODO: This can be avoided
  224. GLuint input_texture;
  225. glGenTextures(1, &input_texture);
  226. glBindTexture(GL_TEXTURE_2D, input_texture);
  227. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  228. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  229. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  230. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  231. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
  232. check_gl();
  233. gles2_debayer_process(
  234. gles2_debayer, output_buffer->texture_id, input_texture);
  235. check_gl();
  236. glFinish();
  237. glDeleteTextures(1, &input_texture);
  238. #ifdef PROFILE_DEBAYER
  239. clock_t t2 = clock();
  240. printf("process_image_for_preview %fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
  241. #endif
  242. #ifdef RENDERDOC
  243. if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
  244. #endif
  245. mp_process_pipeline_buffer_ref(output_buffer);
  246. mp_main_set_preview(output_buffer);
  247. // Create a thumbnail from the preview for the last capture
  248. GdkTexture *thumb = NULL;
  249. if (captures_remaining == 1) {
  250. printf("Making thumbnail\n");
  251. size_t size = output_buffer_width * output_buffer_height * sizeof(uint32_t);
  252. uint32_t *data = g_malloc_n(size, 1);
  253. glReadPixels(0, 0, output_buffer_width, output_buffer_height, GL_RGBA, GL_UNSIGNED_BYTE, data);
  254. check_gl();
  255. // Flip vertically
  256. for (size_t y = 0; y < output_buffer_height / 2; ++y) {
  257. for (size_t x = 0; x < output_buffer_width; ++x) {
  258. uint32_t tmp = data[(output_buffer_height - y - 1) * output_buffer_width + x];
  259. data[(output_buffer_height - y - 1) * output_buffer_width + x] = data[y * output_buffer_width + x];
  260. data[y * output_buffer_width + x] = tmp;
  261. }
  262. }
  263. thumb = gdk_memory_texture_new(
  264. output_buffer_width,
  265. output_buffer_height,
  266. GDK_MEMORY_R8G8B8A8,
  267. g_bytes_new_take(data, size),
  268. output_buffer_width * sizeof(uint32_t));
  269. }
  270. return thumb;
  271. }
  272. static void
  273. process_image_for_capture(const uint8_t *image, int count)
  274. {
  275. time_t rawtime;
  276. time(&rawtime);
  277. struct tm tim = *(localtime(&rawtime));
  278. char datetime[20] = { 0 };
  279. strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
  280. char fname[255];
  281. sprintf(fname, "%s/%d.dng", burst_dir, count);
  282. TIFF *tif = TIFFOpen(fname, "w");
  283. if (!tif) {
  284. printf("Could not open tiff\n");
  285. }
  286. // Define TIFF thumbnail
  287. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
  288. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width >> 4);
  289. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height >> 4);
  290. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  291. TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  292. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  293. TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make());
  294. TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model());
  295. uint16_t orientation;
  296. if (camera_rotation == 0) {
  297. orientation = camera->mirrored ? ORIENTATION_TOPRIGHT :
  298. ORIENTATION_TOPLEFT;
  299. } else if (camera_rotation == 90) {
  300. orientation = camera->mirrored ? ORIENTATION_RIGHTBOT :
  301. ORIENTATION_LEFTBOT;
  302. } else if (camera_rotation == 180) {
  303. orientation = camera->mirrored ? ORIENTATION_BOTLEFT :
  304. ORIENTATION_BOTRIGHT;
  305. } else {
  306. orientation = camera->mirrored ? ORIENTATION_LEFTTOP :
  307. ORIENTATION_RIGHTTOP;
  308. }
  309. TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
  310. TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
  311. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
  312. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  313. TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
  314. long sub_offset = 0;
  315. TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);
  316. TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
  317. TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
  318. char uniquecameramodel[255];
  319. sprintf(uniquecameramodel, "%s %s", mp_get_device_make(),
  320. mp_get_device_model());
  321. TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
  322. if (camera->colormatrix[0]) {
  323. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
  324. } else {
  325. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
  326. }
  327. if (camera->forwardmatrix[0]) {
  328. TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
  329. }
  330. static const float neutral[] = { 1.0, 1.0, 1.0 };
  331. TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
  332. TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
  333. // Write black thumbnail, only windows uses this
  334. {
  335. unsigned char *buf =
  336. (unsigned char *)calloc(1, (int)mode.width >> 4);
  337. for (int row = 0; row < (mode.height >> 4); row++) {
  338. TIFFWriteScanline(tif, buf, row, 0);
  339. }
  340. free(buf);
  341. }
  342. TIFFWriteDirectory(tif);
  343. // Define main photo
  344. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
  345. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width);
  346. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height);
  347. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  348. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
  349. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
  350. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  351. static const short cfapatterndim[] = { 2, 2 };
  352. TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim);
  353. #if (TIFFLIB_VERSION < 20201219) && !LIBTIFF_CFA_PATTERN
  354. TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR
  355. #else
  356. TIFFSetField(tif, TIFFTAG_CFAPATTERN, 4, "\002\001\001\000"); // BGGR
  357. #endif
  358. printf("TIFF version %d\n", TIFFLIB_VERSION);
  359. if (camera->whitelevel) {
  360. TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &camera->whitelevel);
  361. }
  362. if (camera->blacklevel) {
  363. TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &camera->blacklevel);
  364. }
  365. TIFFCheckpointDirectory(tif);
  366. printf("Writing frame to %s\n", fname);
  367. unsigned char *pLine = (unsigned char *)malloc(mode.width);
  368. for (int row = 0; row < mode.height; row++) {
  369. TIFFWriteScanline(tif, (void *) image + (row * mode.width), row, 0);
  370. }
  371. free(pLine);
  372. TIFFWriteDirectory(tif);
  373. // Add an EXIF block to the tiff
  374. TIFFCreateEXIFDirectory(tif);
  375. // 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority
  376. if (!exposure_is_manual) {
  377. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2);
  378. } else {
  379. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
  380. }
  381. TIFFSetField(tif, EXIFTAG_EXPOSURETIME,
  382. (mode.frame_interval.numerator /
  383. (float)mode.frame_interval.denominator) /
  384. ((float)mode.height / (float)exposure));
  385. uint16_t isospeed[1];
  386. isospeed[0] = (uint16_t)remap(gain - 1, 0, gain_max, camera->iso_min,
  387. camera->iso_max);
  388. TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed);
  389. TIFFSetField(tif, EXIFTAG_FLASH, 0);
  390. TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
  391. TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
  392. if (camera->fnumber) {
  393. TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber);
  394. }
  395. if (camera->focallength) {
  396. TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength);
  397. }
  398. if (camera->focallength && camera->cropfactor) {
  399. TIFFSetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM,
  400. (short)(camera->focallength * camera->cropfactor));
  401. }
  402. uint64_t exif_offset = 0;
  403. TIFFWriteCustomDirectory(tif, &exif_offset);
  404. TIFFFreeDirectory(tif);
  405. // Update exif pointer
  406. TIFFSetDirectory(tif, 0);
  407. TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
  408. TIFFRewriteDirectory(tif);
  409. TIFFClose(tif);
  410. }
  411. static void
  412. post_process_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
  413. {
  414. char *stdout;
  415. g_subprocess_communicate_utf8_finish(proc, res, &stdout, NULL, NULL);
  416. // The last line contains the file name
  417. int end = strlen(stdout);
  418. // Skip the newline at the end
  419. stdout[--end] = '\0';
  420. char *path = path = stdout + end - 1;
  421. do {
  422. if (*path == '\n') {
  423. path++;
  424. break;
  425. }
  426. --path;
  427. } while (path > stdout);
  428. mp_main_capture_completed(thumb, path);
  429. }
  430. static void
  431. process_capture_burst(GdkTexture *thumb)
  432. {
  433. time_t rawtime;
  434. time(&rawtime);
  435. struct tm tim = *(localtime(&rawtime));
  436. char timestamp[30];
  437. strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
  438. if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
  439. sprintf(capture_fname,
  440. "%s/IMG%s",
  441. g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
  442. timestamp);
  443. } else if (getenv("XDG_PICTURES_DIR") != NULL) {
  444. sprintf(capture_fname,
  445. "%s/IMG%s",
  446. getenv("XDG_PICTURES_DIR"),
  447. timestamp);
  448. } else {
  449. sprintf(capture_fname,
  450. "%s/Pictures/IMG%s",
  451. getenv("HOME"),
  452. timestamp);
  453. }
  454. // Start post-processing the captured burst
  455. g_print("Post process %s to %s.ext\n", burst_dir, capture_fname);
  456. GError *error = NULL;
  457. GSubprocess *proc = g_subprocess_new(
  458. G_SUBPROCESS_FLAGS_STDOUT_PIPE,
  459. &error,
  460. processing_script,
  461. burst_dir,
  462. capture_fname,
  463. NULL);
  464. if (!proc) {
  465. g_printerr("Failed to spawn postprocess process: %s\n",
  466. error->message);
  467. return;
  468. }
  469. g_subprocess_communicate_utf8_async(
  470. proc,
  471. NULL,
  472. NULL,
  473. (GAsyncReadyCallback)post_process_finished,
  474. thumb);
  475. }
  476. static void
  477. process_image(MPPipeline *pipeline, const MPBuffer *buffer)
  478. {
  479. #ifdef PROFILE_PROCESS
  480. clock_t t1 = clock();
  481. #endif
  482. size_t size =
  483. mp_pixel_format_width_to_bytes(mode.pixel_format, mode.width) *
  484. mode.height;
  485. uint8_t *image = malloc(size);
  486. memcpy(image, buffer->data, size);
  487. mp_io_pipeline_release_buffer(buffer->index);
  488. MPZBarImage *zbar_image = mp_zbar_image_new(image, mode.pixel_format, mode.width, mode.height, camera_rotation, camera->mirrored);
  489. mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image));
  490. #ifdef PROFILE_PROCESS
  491. clock_t t2 = clock();
  492. #endif
  493. GdkTexture *thumb = process_image_for_preview(image);
  494. if (captures_remaining > 0) {
  495. int count = burst_length - captures_remaining;
  496. --captures_remaining;
  497. process_image_for_capture(image, count);
  498. if (captures_remaining == 0) {
  499. assert(thumb);
  500. process_capture_burst(thumb);
  501. } else {
  502. assert(!thumb);
  503. }
  504. } else {
  505. assert(!thumb);
  506. }
  507. mp_zbar_image_unref(zbar_image);
  508. ++frames_processed;
  509. if (captures_remaining == 0) {
  510. is_capturing = false;
  511. }
  512. #ifdef PROFILE_PROCESS
  513. clock_t t3 = clock();
  514. printf("process_image %fms, step 1:%fms, step 2:%fms\n",
  515. (float)(t3 - t1) / CLOCKS_PER_SEC * 1000,
  516. (float)(t2 - t1) / CLOCKS_PER_SEC * 1000,
  517. (float)(t3 - t2) / CLOCKS_PER_SEC * 1000);
  518. #endif
  519. }
  520. void
  521. mp_process_pipeline_process_image(MPBuffer buffer)
  522. {
  523. // If we haven't processed the previous frame yet, drop this one
  524. if (frames_received != frames_processed && !is_capturing) {
  525. mp_io_pipeline_release_buffer(buffer.index);
  526. return;
  527. }
  528. ++frames_received;
  529. mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &buffer,
  530. sizeof(MPBuffer));
  531. }
  532. static void
  533. capture()
  534. {
  535. char template[] = "/tmp/megapixels.XXXXXX";
  536. char *tempdir;
  537. tempdir = mkdtemp(template);
  538. if (tempdir == NULL) {
  539. g_printerr("Could not make capture directory %s\n", template);
  540. exit(EXIT_FAILURE);
  541. }
  542. strcpy(burst_dir, tempdir);
  543. captures_remaining = burst_length;
  544. }
  545. void
  546. mp_process_pipeline_capture()
  547. {
  548. is_capturing = true;
  549. mp_pipeline_invoke(pipeline, capture, NULL, 0);
  550. }
  551. static void
  552. on_output_changed()
  553. {
  554. output_buffer_width = mode.width / 2;
  555. output_buffer_height = mode.height / 2;
  556. if (camera->rotate != 0 || camera->rotate != 180) {
  557. int tmp = output_buffer_width;
  558. output_buffer_width = output_buffer_height;
  559. output_buffer_height = tmp;
  560. }
  561. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  562. glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
  563. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_buffer_width, output_buffer_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  564. }
  565. glBindTexture(GL_TEXTURE_2D, 0);
  566. gles2_debayer_configure(
  567. gles2_debayer,
  568. output_buffer_width, output_buffer_height,
  569. mode.width, mode.height,
  570. camera->rotate, camera->mirrored,
  571. camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
  572. camera->blacklevel);
  573. }
  574. static int
  575. mod(int a, int b)
  576. {
  577. int r = a % b;
  578. return r < 0 ? r + b : r;
  579. }
  580. static void
  581. update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
  582. {
  583. const bool output_changed =
  584. !mp_camera_mode_is_equivalent(&mode, &state->mode)
  585. || preview_width != state->preview_width
  586. || preview_height != state->preview_height
  587. || device_rotation != state->device_rotation;
  588. camera = state->camera;
  589. mode = state->mode;
  590. preview_width = state->preview_width;
  591. preview_height = state->preview_height;
  592. device_rotation = state->device_rotation;
  593. burst_length = state->burst_length;
  594. // gain_is_manual = state->gain_is_manual;
  595. gain = state->gain;
  596. gain_max = state->gain_max;
  597. exposure_is_manual = state->exposure_is_manual;
  598. exposure = state->exposure;
  599. if (output_changed) {
  600. camera_rotation = mod(camera->rotate - device_rotation, 360);
  601. on_output_changed();
  602. }
  603. struct mp_main_state main_state = {
  604. .camera = camera,
  605. .mode = mode,
  606. .image_width = output_buffer_width,
  607. .image_height = output_buffer_height,
  608. .gain_is_manual = state->gain_is_manual,
  609. .gain = gain,
  610. .gain_max = gain_max,
  611. .exposure_is_manual = exposure_is_manual,
  612. .exposure = exposure,
  613. .has_auto_focus_continuous = state->has_auto_focus_continuous,
  614. .has_auto_focus_start = state->has_auto_focus_start,
  615. };
  616. mp_main_update_state(&main_state);
  617. }
  618. void
  619. mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_state)
  620. {
  621. mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state,
  622. sizeof(struct mp_process_pipeline_state));
  623. }
  624. // GTK4 seems to require this
  625. void pango_fc_font_get_languages() {}