process_pipeline.c 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386
  1. #include "process_pipeline.h"
  2. #include "gles2_debayer.h"
  3. #include "io_pipeline.h"
  4. #include "main.h"
  5. #include "pipeline.h"
  6. #include "state.h"
  7. #include "zbar_pipeline.h"
  8. #include <assert.h>
  9. #include <gtk/gtk.h>
  10. #include <math.h>
  11. #ifndef SYSCONFDIR
  12. #include "config.h"
  13. #endif
  14. #include "medianame.h"
  15. #include "dcp.h"
  16. #include "gl_util.h"
  17. #include "libdng.h"
  18. #include <jpeglib.h>
  19. #include <sys/mman.h>
  20. #include <sys/prctl.h>
  21. #include <sys/time.h>
  22. static const float colormatrix_srgb[] = { 3.2409f, -1.5373f, -0.4986f,
  23. -0.9692f, 1.8759f, 0.0415f,
  24. 0.0556f, -0.2039f, 1.0569f };
  25. static MPPipeline *pipeline;
  26. mp_state_proc state_proc;
  27. static char burst_dir[255];
  28. static volatile bool is_capturing = false;
  29. static volatile int frames_processed = 0;
  30. static volatile int frames_received = 0;
  31. libmegapixels_camera *pr_camera;
  32. static int output_buffer_width = -1;
  33. static int output_buffer_height = -1;
  34. static bool flash_enabled;
  35. static int framecounter = 0;
  36. static char capture_fname[255], movie_script[255];
  37. static GSettings *settings;
  38. void
  39. mp_process_find_all_processors(GtkListStore *store)
  40. {
  41. GtkTreeIter iter;
  42. char buffer[512];
  43. // Find all the original postprocess.sh locations
  44. // Check postprocess.sh in the current working directory
  45. if (access("./data/postprocess.sh", F_OK) != -1) {
  46. gtk_list_store_insert(store, &iter, -1);
  47. gtk_list_store_set(store,
  48. &iter,
  49. 0,
  50. "./data/postprocess.sh",
  51. 1,
  52. "(cwd) postprocess.sh",
  53. -1);
  54. }
  55. // Check for a script in XDG_CONFIG_HOME
  56. sprintf(buffer, "%s/megapixels/postprocess.sh", g_get_user_config_dir());
  57. if (access(buffer, F_OK) != -1) {
  58. gtk_list_store_insert(store, &iter, -1);
  59. gtk_list_store_set(
  60. store, &iter, 0, buffer, 1, "(user) postprocess.sh", -1);
  61. }
  62. // Check user overridden /etc/megapixels/postprocess.sh
  63. sprintf(buffer, "%s/megapixels/postprocess.sh", SYSCONFDIR);
  64. if (access(buffer, F_OK) != -1) {
  65. gtk_list_store_insert(store, &iter, -1);
  66. gtk_list_store_set(
  67. store, &iter, 0, buffer, 1, "(system) postprocess.sh", -1);
  68. }
  69. // Check user overridden /usr/share/megapixels/postprocess.sh
  70. sprintf(buffer, "%s/megapixels/postprocess.sh", DATADIR);
  71. if (access(buffer, F_OK) != -1) {
  72. gtk_list_store_insert(store, &iter, -1);
  73. gtk_list_store_set(
  74. store, &iter, 0, buffer, 1, "(built-in) postprocess.sh", -1);
  75. }
  76. // Find extra packaged postprocessor scripts
  77. // These should be packaged in
  78. // /usr/share/megapixels/postprocessor.d/executable
  79. sprintf(buffer, "%s/megapixels/postprocessor.d", DATADIR);
  80. DIR *d;
  81. struct dirent *dir;
  82. d = opendir(buffer);
  83. if (d) {
  84. while ((dir = readdir(d)) != NULL) {
  85. if (dir->d_name[0] == '.') {
  86. continue;
  87. }
  88. sprintf(buffer,
  89. "%s/megapixels/postprocessor.d/%s",
  90. DATADIR,
  91. dir->d_name);
  92. gtk_list_store_insert(store, &iter, -1);
  93. gtk_list_store_set(
  94. store, &iter, 0, buffer, 1, dir->d_name, -1);
  95. }
  96. closedir(d);
  97. }
  98. }
  99. bool
  100. mp_process_find_processor(char *script, char *filename)
  101. {
  102. // Check postprocess.sh in the current working directory
  103. sprintf(script, "./data/%s", filename);
  104. if (access(script, F_OK) != -1) {
  105. sprintf(script, "./data/%s", filename);
  106. printf("Found postprocessor script at %s\n", script);
  107. return true;
  108. }
  109. // Check for a script in XDG_CONFIG_HOME
  110. sprintf(script, "%s/megapixels/%s", g_get_user_config_dir(), filename);
  111. if (access(script, F_OK) != -1) {
  112. printf("Found postprocessor script at %s\n", script);
  113. return true;
  114. }
  115. // Check user overridden /etc/megapixels/postprocessor.sh
  116. sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
  117. if (access(script, F_OK) != -1) {
  118. printf("Found postprocessor script at %s\n", script);
  119. return true;
  120. }
  121. // Check packaged /usr/share/megapixels/postprocessor.sh
  122. sprintf(script, "%s/megapixels/%s", DATADIR, filename);
  123. if (access(script, F_OK) != -1) {
  124. printf("Found postprocessor script at %s\n", script);
  125. return true;
  126. }
  127. return false;
  128. }
  129. static void setup_capture(void)
  130. {
  131. char template[] = "/tmp/megapixels.XXXXXX";
  132. char *tempdir;
  133. tempdir = mkdtemp(template);
  134. if (tempdir == NULL) {
  135. g_printerr("Could not make capture directory %s\n", template);
  136. exit(EXIT_FAILURE);
  137. }
  138. strcpy(burst_dir, tempdir);
  139. }
  140. static void
  141. setup(MPPipeline *pipeline, const void *data)
  142. {
  143. libdng_init();
  144. settings = g_settings_new(APP_ID);
  145. prctl(PR_SET_NAME, "megapixels-pr", NULL, NULL, NULL);
  146. state_proc.mode_balance = AAA_BY_POST;
  147. state_proc.mode_exposure = AAA_BY_V4L2_CONTROLS;
  148. state_proc.mode_focus = AAA_DISABLED;
  149. if (!mp_process_find_processor(movie_script, "movie.sh")) {
  150. printf("movie.sh not found\n");
  151. exit(1);
  152. }
  153. setup_capture();
  154. }
  155. void
  156. mp_process_pipeline_start()
  157. {
  158. pipeline = mp_pipeline_new();
  159. mp_pipeline_invoke(pipeline, setup, NULL, 0);
  160. mp_zbar_pipeline_start();
  161. }
  162. void
  163. mp_process_pipeline_stop()
  164. {
  165. mp_pipeline_free(pipeline);
  166. mp_zbar_pipeline_stop();
  167. }
  168. void
  169. mp_process_pipeline_sync()
  170. {
  171. mp_pipeline_sync(pipeline);
  172. }
  173. #define NUM_BUFFERS 4
  174. struct _MPProcessPipelineBuffer {
  175. GLuint texture_id;
  176. _Atomic(int) refcount;
  177. };
  178. static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS];
  179. void
  180. mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf)
  181. {
  182. ++buf->refcount;
  183. }
  184. void
  185. mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf)
  186. {
  187. --buf->refcount;
  188. }
  189. uint32_t
  190. mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
  191. {
  192. return buf->texture_id;
  193. }
  194. static void
  195. repack_image_sequencial(const uint8_t *src_buf,
  196. uint8_t *dst_buf,
  197. libmegapixels_mode *mode)
  198. {
  199. uint16_t pixels[4];
  200. uint32_t row_length =
  201. libmegapixels_mode_width_to_bytes(mode->format, mode->width);
  202. uint32_t padding_bytes =
  203. libmegapixels_mode_width_to_padding(mode->format, mode->width);
  204. size_t si = 0;
  205. // Image data must be 10-bit packed
  206. assert(libmegapixels_format_bits_per_pixel(mode->format) == 10);
  207. /*
  208. * Repack 40 bits stored in sensor format into sequencial format
  209. *
  210. * src_buf: 11111111 22222222 33333333 44444444 11223344 ...
  211. * dst_buf: 11111111 11222222 22223333 33333344 44444444 ...
  212. */
  213. for (size_t i = 0; i < row_length * mode->height; i += 5) {
  214. // Skip padding bytes in source buffer
  215. if (i && i % row_length == 0)
  216. si += padding_bytes;
  217. /* Extract pixels from packed sensor format */
  218. pixels[0] = (src_buf[si] << 2) | (src_buf[si + 4] >> 6);
  219. pixels[1] = (src_buf[si + 1] << 2) | (src_buf[si + 4] >> 4 & 0x03);
  220. pixels[2] = (src_buf[si + 2] << 2) | (src_buf[si + 4] >> 2 & 0x03);
  221. pixels[3] = (src_buf[si + 3] << 2) | (src_buf[si + 4] & 0x03);
  222. /* Pack pixels into sequencial format */
  223. dst_buf[i] = (pixels[0] >> 2 & 0xff);
  224. dst_buf[i + 1] = (pixels[0] << 6 & 0xff) | (pixels[1] >> 4 & 0x3f);
  225. dst_buf[i + 2] = (pixels[1] << 4 & 0xff) | (pixels[2] >> 6 & 0x0f);
  226. dst_buf[i + 3] = (pixels[2] << 2 & 0xff) | (pixels[3] >> 8 & 0x03);
  227. dst_buf[i + 4] = (pixels[3] & 0xff);
  228. si += 5;
  229. }
  230. }
  231. static GLES2Debayer *gles2_debayer = NULL;
  232. static GdkGLContext *context;
  233. // #define RENDERDOC
  234. #ifdef RENDERDOC
  235. #include <renderdoc/app.h>
  236. extern RENDERDOC_API_1_1_2 *rdoc_api;
  237. #endif
  238. static void
  239. init_gl(MPPipeline *pipeline, GdkSurface **surface)
  240. {
  241. GError *error = NULL;
  242. context = gdk_surface_create_gl_context(*surface, &error);
  243. if (context == NULL) {
  244. printf("Failed to initialize OpenGL context: %s\n", error->message);
  245. g_clear_error(&error);
  246. return;
  247. }
  248. gdk_gl_context_set_use_es(context, true);
  249. gdk_gl_context_set_required_version(context, 2, 0);
  250. gdk_gl_context_set_forward_compatible(context, false);
  251. #ifdef DEBUG
  252. gdk_gl_context_set_debug_enabled(context, true);
  253. #else
  254. gdk_gl_context_set_debug_enabled(context, false);
  255. #endif
  256. gdk_gl_context_realize(context, &error);
  257. if (error != NULL) {
  258. printf("Failed to create OpenGL context: %s\n", error->message);
  259. g_clear_object(&context);
  260. g_clear_error(&error);
  261. return;
  262. }
  263. gdk_gl_context_make_current(context);
  264. check_gl();
  265. // Make a VAO for OpenGL
  266. if (!gdk_gl_context_get_use_es(context)) {
  267. GLuint vao;
  268. glGenVertexArrays(1, &vao);
  269. glBindVertexArray(vao);
  270. check_gl();
  271. }
  272. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  273. check_gl();
  274. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  275. glGenTextures(1, &output_buffers[i].texture_id);
  276. glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
  277. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  278. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  279. }
  280. glBindTexture(GL_TEXTURE_2D, 0);
  281. gboolean is_es = gdk_gl_context_get_use_es(context);
  282. int major, minor;
  283. gdk_gl_context_get_version(context, &major, &minor);
  284. printf("Initialized %s %d.%d\n",
  285. is_es ? "OpenGL ES" : "OpenGL",
  286. major,
  287. minor);
  288. }
  289. void
  290. mp_process_pipeline_init_gl(GdkSurface *surface)
  291. {
  292. mp_pipeline_invoke(pipeline,
  293. (MPPipelineCallback)init_gl,
  294. &surface,
  295. sizeof(GdkSurface *));
  296. }
  297. float
  298. clamp_float(float value, float min, float max)
  299. {
  300. if (value > max)
  301. return max;
  302. if (value < min)
  303. return min;
  304. return value;
  305. }
  306. static void
  307. clamp_control(controlstate *control)
  308. {
  309. if (control->value_req > control->max) {
  310. control->value_req = control->max;
  311. }
  312. }
  313. static void
  314. process_aaa()
  315. {
  316. bool auto_exposure =
  317. !state_proc.exposure.manual && state_proc.exposure.auto_control == 0;
  318. bool auto_focus =
  319. !state_proc.focus.manual && state_proc.focus.auto_control == 0;
  320. bool auto_balance = TRUE;
  321. if (!auto_exposure && !auto_focus && !auto_balance) {
  322. return;
  323. }
  324. int width = output_buffer_width;
  325. int height = output_buffer_height / 3;
  326. uint32_t *center = g_malloc_n(width * height * sizeof(uint32_t), 1);
  327. glReadPixels(0, height, width, height, GL_RGBA, GL_UNSIGNED_BYTE, center);
  328. libmegapixels_aaa_set_matrix(&state_proc.stats,
  329. state_proc.calibration.color_matrix_1,
  330. state_proc.calibration.color_matrix_2);
  331. libmegapixels_aaa_software_statistics(
  332. &state_proc.stats, center, width, height);
  333. state_proc.blacklevel -= (float)state_proc.stats.blacklevel * 0.001f;
  334. state_proc.blacklevel = clamp_float(state_proc.blacklevel, 0.0f, 0.07f);
  335. if (auto_exposure) {
  336. int direction = state_proc.stats.exposure;
  337. int step = 0;
  338. if (direction > 0) {
  339. // Preview is too dark
  340. // Try raising the exposure time first
  341. if (state_proc.exposure.value < state_proc.exposure.max) {
  342. step = state_proc.exposure.value / 4;
  343. step = step < 4 ? 4 : step;
  344. state_proc.exposure.value_req =
  345. state_proc.exposure.value +
  346. (step * direction);
  347. printf("Expose + %d\n",
  348. state_proc.exposure.value_req);
  349. } else {
  350. // Raise sensor gain if exposure limit is hit
  351. step = state_proc.gain.value / 4;
  352. step = step < 4 ? 4 : step;
  353. state_proc.gain.value_req =
  354. state_proc.gain.value + (step * direction);
  355. printf("Gain + %d\n", state_proc.gain.value_req);
  356. }
  357. } else if (direction < 0) {
  358. // Preview is too bright
  359. // Lower the sensor gain first to have less noise
  360. if (state_proc.gain.value > 0) {
  361. step = state_proc.gain.value / 4;
  362. state_proc.gain.value_req =
  363. state_proc.gain.value + (step * direction);
  364. printf("Gain - %d\n", state_proc.gain.value_req);
  365. } else {
  366. // Shorten the exposure time to go even darker
  367. step = state_proc.exposure.value / 4;
  368. state_proc.exposure.value_req =
  369. state_proc.exposure.value +
  370. (step * direction);
  371. printf("Expose - %d\n",
  372. state_proc.exposure.value_req);
  373. }
  374. }
  375. clamp_control(&state_proc.gain);
  376. clamp_control(&state_proc.exposure);
  377. mp_io_pipeline_set_control_int32(state_proc.gain.control,
  378. state_proc.gain.value_req);
  379. mp_io_pipeline_set_control_int32(state_proc.exposure.control,
  380. state_proc.exposure.value_req);
  381. state_proc.gain.value = state_proc.gain.value_req;
  382. state_proc.exposure.value = state_proc.exposure.value_req;
  383. }
  384. if (auto_balance) {
  385. float r = state_proc.stats.avg_r;
  386. float g = state_proc.stats.avg_g;
  387. float b = state_proc.stats.avg_b;
  388. // Revert the current gains set on the preview
  389. b /= state_proc.red;
  390. b /= state_proc.blue;
  391. float t = 2.0f;
  392. if (r < t && g < t && b < t) {
  393. // Don't try to AWB on very dark frames
  394. } else {
  395. // Calculate the new R/B gains based on the average color of
  396. // the frame
  397. float new_r = g / clamp_float(r, 1.0f, 999.0f);
  398. float new_b = g / clamp_float(b, 1.0f, 999.0f);
  399. state_proc.red = clamp_float(new_r, 0.01f, 4.0f);
  400. state_proc.blue = clamp_float(new_b, 0.01f, 4.0f);
  401. }
  402. }
  403. gles2_debayer_set_shading(gles2_debayer,
  404. state_proc.red,
  405. state_proc.blue,
  406. state_proc.blacklevel);
  407. }
  408. static GdkTexture *
  409. process_image_for_preview(const uint8_t *image)
  410. {
  411. #ifdef PROFILE_DEBAYER
  412. clock_t t1 = clock();
  413. #endif
  414. // Pick an available buffer
  415. MPProcessPipelineBuffer *output_buffer = NULL;
  416. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  417. if (output_buffers[i].refcount == 0) {
  418. output_buffer = &output_buffers[i];
  419. }
  420. }
  421. if (output_buffer == NULL) {
  422. return NULL;
  423. }
  424. assert(output_buffer != NULL);
  425. #ifdef RENDERDOC
  426. if (rdoc_api) {
  427. rdoc_api->StartFrameCapture(NULL, NULL);
  428. }
  429. #endif
  430. // Copy image to a GL texture. TODO: This can be avoided
  431. GLuint input_texture;
  432. glGenTextures(1, &input_texture);
  433. glBindTexture(GL_TEXTURE_2D, input_texture);
  434. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  435. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  436. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  437. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  438. glTexImage2D(
  439. GL_TEXTURE_2D,
  440. 0,
  441. GL_LUMINANCE,
  442. libmegapixels_mode_width_to_bytes(state_proc.mode->format,
  443. state_proc.mode->width) +
  444. libmegapixels_mode_width_to_padding(state_proc.mode->format,
  445. state_proc.mode->width),
  446. state_proc.mode->height,
  447. 0,
  448. GL_LUMINANCE,
  449. GL_UNSIGNED_BYTE,
  450. image);
  451. check_gl();
  452. gles2_debayer_process(
  453. gles2_debayer, output_buffer->texture_id, input_texture);
  454. check_gl();
  455. glFinish();
  456. glDeleteTextures(1, &input_texture);
  457. #ifdef PROFILE_DEBAYER
  458. clock_t t2 = clock();
  459. printf("process_image_for_preview %fms\n",
  460. (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
  461. #endif
  462. #ifdef RENDERDOC
  463. if (rdoc_api) {
  464. rdoc_api->EndFrameCapture(NULL, NULL);
  465. }
  466. #endif
  467. mp_process_pipeline_buffer_ref(output_buffer);
  468. mp_main_set_preview(output_buffer);
  469. if (framecounter++ == 2) {
  470. framecounter = 0;
  471. process_aaa();
  472. }
  473. // Create a thumbnail from the preview for the last capture
  474. GdkTexture *thumb = NULL;
  475. if (state_proc.captures_remaining == 1) {
  476. printf("Making thumbnail\n");
  477. size_t size = output_buffer_width * output_buffer_height *
  478. sizeof(uint32_t);
  479. uint32_t *data = g_malloc_n(size, 1);
  480. glReadPixels(0,
  481. 0,
  482. output_buffer_width,
  483. output_buffer_height,
  484. GL_RGBA,
  485. GL_UNSIGNED_BYTE,
  486. data);
  487. check_gl();
  488. // Flip vertically
  489. for (size_t y = 0; y < output_buffer_height / 2; ++y) {
  490. for (size_t x = 0; x < output_buffer_width; ++x) {
  491. uint32_t tmp = data[(output_buffer_height - y - 1) *
  492. output_buffer_width +
  493. x];
  494. data[(output_buffer_height - y - 1) *
  495. output_buffer_width +
  496. x] = data[y * output_buffer_width + x];
  497. data[y * output_buffer_width + x] = tmp;
  498. }
  499. }
  500. thumb = gdk_memory_texture_new(output_buffer_width,
  501. output_buffer_height,
  502. GDK_MEMORY_R8G8B8A8,
  503. g_bytes_new_take(data, size),
  504. output_buffer_width *
  505. sizeof(uint32_t));
  506. }
  507. return thumb;
  508. }
  509. static void
  510. format_timestamp(char *timestamp)
  511. {
  512. static char capture_fname[255];
  513. time_t rawtime;
  514. time(&rawtime);
  515. struct tm tim = *(localtime(&rawtime));
  516. strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
  517. }
  518. static void
  519. format_movie_name(char *capture_fname)
  520. {
  521. char timestamp[30];
  522. format_timestamp(timestamp);
  523. if (g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS) != NULL) {
  524. sprintf(capture_fname,
  525. "%s/VID%s.mkv",
  526. g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS),
  527. timestamp);
  528. } else if (getenv("XDG_VIDOES_DIR") != NULL) {
  529. sprintf(capture_fname,
  530. "%s/VID%s.mkv",
  531. getenv("XDG_VIDEOS_DIR"),
  532. timestamp);
  533. } else {
  534. sprintf(capture_fname,
  535. "%s/Videos/VID%s.mkv",
  536. getenv("HOME"),
  537. timestamp);
  538. }
  539. }
  540. int movie_recording;
  541. static char movie_fname[255];
  542. static char stdout_buf[1024];
  543. static void on_read_complete(GObject *source_object, GAsyncResult *res, gpointer user_data) {
  544. GInputStream *stream = G_INPUT_STREAM(source_object);
  545. GError *error = NULL;
  546. gssize bytes_read;
  547. // Read the output from the stream
  548. bytes_read = g_input_stream_read_finish(stream, res, &error);
  549. if (bytes_read == 0) {
  550. // End of file reached, close the stream
  551. g_input_stream_close(stream, NULL, NULL);
  552. g_object_unref(stream);
  553. notify_movie_progress();
  554. return;
  555. }
  556. if (bytes_read < 0) {
  557. // Error occurred
  558. g_print("Error reading subprocess output: %s\n", error->message);
  559. g_error_free(error);
  560. g_object_unref(stream);
  561. return;
  562. }
  563. //g_print("Got buffer: %.*s", (int)bytes_read, stdout_buf);
  564. stdout_buf[bytes_read] = 0;
  565. {
  566. char msg[] = "Message: ";
  567. int l = sizeof(msg);
  568. if (!strncmp(stdout_buf, msg, l-1)) {
  569. char *c = strchr(stdout_buf, '\n');
  570. if (!c)
  571. return;
  572. *c = 0;
  573. notify_movie_message(strdup(stdout_buf + l - 1));
  574. }
  575. }
  576. // Continue reading asynchronously
  577. g_input_stream_read_async(stream, stdout_buf, sizeof(stdout_buf), G_PRIORITY_DEFAULT, NULL,
  578. on_read_complete, NULL);
  579. }
  580. static void
  581. spawn_movie(char *cmd)
  582. {
  583. g_autoptr(GError) error = NULL;
  584. char *mode;
  585. switch (state_proc.mode->v4l_pixfmt) {
  586. case V4L2_PIX_FMT_UYVY:
  587. case V4L2_PIX_FMT_YUYV:
  588. case V4L2_PIX_FMT_YVYU:
  589. case V4L2_PIX_FMT_VYUY:
  590. mode = "grw";
  591. break;
  592. default:
  593. mode = "dng";
  594. break;
  595. }
  596. GSubprocess *proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE,
  597. &error,
  598. movie_script,
  599. cmd,
  600. burst_dir,
  601. movie_fname,
  602. "305",
  603. mode,
  604. NULL);
  605. if (!proc) {
  606. g_printerr("Failed to spawn postprocess process: %s\n",
  607. error->message);
  608. return;
  609. }
  610. GInputStream *stdout_stream;
  611. // Get the stdout stream of the subprocess
  612. stdout_stream = g_subprocess_get_stdout_pipe(proc);
  613. // Read the output of the subprocess asynchronously
  614. g_input_stream_read_async(stdout_stream, stdout_buf, sizeof(stdout_buf), G_PRIORITY_DEFAULT, NULL,
  615. on_read_complete, NULL);
  616. }
  617. void
  618. on_movie_start(void)
  619. {
  620. format_movie_name(movie_fname);
  621. movie_recording = 1;
  622. printf("movie recording on\n");
  623. spawn_movie("start");
  624. }
  625. void
  626. on_movie_stop(void)
  627. {
  628. movie_recording = 0;
  629. printf("movie recording off\n");
  630. spawn_movie("stop");
  631. }
  632. static void
  633. save_grw(const uint8_t *image, char *fname)
  634. {
  635. FILE *outfile;
  636. if ((outfile = fopen(fname, "wb")) == NULL) {
  637. g_printerr("grw open %s: error %d, %s\n",
  638. fname,
  639. errno,
  640. strerror(errno));
  641. return;
  642. }
  643. int width = state_proc.mode->width;
  644. int height = state_proc.mode->height;
  645. int size = width * height * 2;
  646. char *format;
  647. switch (state_proc.mode->v4l_pixfmt) {
  648. case V4L2_PIX_FMT_YUYV:
  649. format = "YUY2";
  650. break;
  651. default:
  652. printf("Please fill appropriate translation for YUV.\n");
  653. }
  654. fwrite(image, size, 1, outfile);
  655. char buf[1024];
  656. buf[0] = 0;
  657. int header = sprintf(buf+1,
  658. "Caps: video/x-raw,format=%s,width=%d,height=%d\nSize: %d\nGRW",
  659. format, width, height, size);
  660. fwrite(buf, header+1, 1, outfile);
  661. fclose(outfile);
  662. }
  663. static void
  664. save_jpeg(const uint8_t *image, char *fname)
  665. {
  666. FILE *outfile;
  667. if ((outfile = fopen(fname, "wb")) == NULL) {
  668. g_printerr("jpeg open %s: error %d, %s\n",
  669. fname,
  670. errno,
  671. strerror(errno));
  672. return;
  673. }
  674. int width = state_proc.mode->width;
  675. struct jpeg_compress_struct cinfo;
  676. struct jpeg_error_mgr jerr;
  677. cinfo.err = jpeg_std_error(&jerr);
  678. jpeg_create_compress(&cinfo);
  679. jpeg_stdio_dest(&cinfo, outfile);
  680. cinfo.image_width = state_proc.mode->width & -1;
  681. cinfo.image_height = state_proc.mode->height & -1;
  682. cinfo.input_components = 3;
  683. cinfo.in_color_space = JCS_YCbCr;
  684. jpeg_set_defaults(&cinfo);
  685. jpeg_set_quality(&cinfo, 92, TRUE);
  686. jpeg_start_compress(&cinfo, TRUE);
  687. uint8_t *row = malloc(width * 3);
  688. JSAMPROW row_pointer[1];
  689. row_pointer[0] = row;
  690. unsigned int y1 = 0;
  691. unsigned int u = 1;
  692. unsigned int y2 = 2;
  693. unsigned int v = 3;
  694. switch (state_proc.mode->v4l_pixfmt) {
  695. case V4L2_PIX_FMT_UYVY:
  696. u = 0;
  697. y1 = 1;
  698. v = 2;
  699. y2 = 3;
  700. break;
  701. case V4L2_PIX_FMT_YUYV:
  702. y1 = 0;
  703. u = 1;
  704. y2 = 2;
  705. v = 3;
  706. break;
  707. case V4L2_PIX_FMT_YVYU:
  708. y1 = 0;
  709. v = 1;
  710. y2 = 2;
  711. u = 3;
  712. break;
  713. case V4L2_PIX_FMT_VYUY:
  714. v = 0;
  715. y1 = 1;
  716. u = 2;
  717. y2 = 3;
  718. break;
  719. }
  720. while (cinfo.next_scanline < cinfo.image_height) {
  721. unsigned int i, j = 0;
  722. unsigned int offset = cinfo.next_scanline * cinfo.image_width * 2;
  723. for (i = 0; i < cinfo.image_width * 2; i += 4) {
  724. row[j + 0] = image[offset + i + y1];
  725. row[j + 1] = image[offset + i + u];
  726. row[j + 2] = image[offset + i + v];
  727. row[j + 3] = image[offset + i + y2];
  728. row[j + 4] = image[offset + i + u];
  729. row[j + 5] = image[offset + i + v];
  730. j += 6;
  731. }
  732. jpeg_write_scanlines(&cinfo, row_pointer, 1);
  733. }
  734. jpeg_finish_compress(&cinfo);
  735. fclose(outfile);
  736. jpeg_destroy_compress(&cinfo);
  737. }
  738. static void
  739. process_image_for_capture_yuv(const uint8_t *image, int count)
  740. {
  741. char fname[255];
  742. sprintf(fname, "%s/%d.jpeg", burst_dir, count);
  743. save_jpeg(image, fname);
  744. }
  745. static void
  746. save_dng(const uint8_t *image, char *fname, int count)
  747. {
  748. uint16_t orientation;
  749. if (state_proc.device_rotation == 0) {
  750. orientation = state_proc.mode->mirrored ?
  751. LIBDNG_ORIENTATION_TOPRIGHT :
  752. LIBDNG_ORIENTATION_TOPLEFT;
  753. } else if (state_proc.device_rotation == 90) {
  754. orientation = state_proc.mode->mirrored ?
  755. LIBDNG_ORIENTATION_RIGHTBOT :
  756. LIBDNG_ORIENTATION_LEFTBOT;
  757. } else if (state_proc.device_rotation == 180) {
  758. orientation = state_proc.mode->mirrored ?
  759. LIBDNG_ORIENTATION_BOTLEFT :
  760. LIBDNG_ORIENTATION_BOTRIGHT;
  761. } else {
  762. orientation = state_proc.mode->mirrored ?
  763. LIBDNG_ORIENTATION_LEFTTOP :
  764. LIBDNG_ORIENTATION_RIGHTTOP;
  765. }
  766. libdng_info dng = { 0 };
  767. libdng_new(&dng);
  768. libdng_set_datetime_now(&dng);
  769. libdng_set_mode_from_pixfmt(&dng, state_proc.mode->v4l_pixfmt);
  770. if (state_proc.configuration->make != NULL &&
  771. state_proc.configuration->model != NULL) {
  772. libdng_set_make_model(&dng,
  773. state_proc.configuration->make,
  774. state_proc.configuration->model);
  775. }
  776. libdng_set_orientation(&dng, orientation);
  777. libdng_set_software(&dng, "Megapixels");
  778. libdng_set_neutral(&dng, state_proc.red, 1.0f, state_proc.blue);
  779. libdng_set_analog_balance(&dng,
  780. state_proc.balance[0],
  781. state_proc.balance[1],
  782. state_proc.balance[2]);
  783. if (!state_proc.exposure.manual) {
  784. libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_NORMAL);
  785. } else {
  786. libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_MANUAL);
  787. }
  788. //printf("Writing frame to %s, %d x %d\n", fname, state_proc.mode->width, state_proc.mode->height);
  789. libdng_write(&dng,
  790. fname,
  791. state_proc.mode->width,
  792. state_proc.mode->height,
  793. image,
  794. count);
  795. libdng_free(&dng);
  796. /*
  797. TIFFSetField(tif,
  798. EXIFTAG_EXPOSURETIME,
  799. (mode.frame_interval.numerator /
  800. (float)mode.frame_interval.denominator) /
  801. ((float)mode.height / (float)exposure));
  802. if (pr_camera->iso_min && pr_camera->iso_max) {
  803. uint16_t isospeed = remap(
  804. gain - 1, 0, gain_max, pr_camera->iso_min,
  805. pr_camera->iso_max); TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1,
  806. &isospeed);
  807. }
  808. if (!pr_camera->has_flash) {
  809. // No flash function
  810. TIFFSetField(tif, EXIFTAG_FLASH, 0x20);
  811. } else if (flash_enabled) {
  812. // Flash present and fired
  813. TIFFSetField(tif, EXIFTAG_FLASH, 0x1);
  814. } else {
  815. // Flash present but not fired
  816. TIFFSetField(tif, EXIFTAG_FLASH, 0x0);
  817. }
  818. */
  819. /*
  820. if (pr_camera->fnumber) {
  821. TIFFSetField(tif, EXIFTAG_FNUMBER, pr_camera->fnumber);
  822. }
  823. if (pr_camera->focallength) {
  824. TIFFSetField(tif, EXIFTAG_FOCALLENGTH, pr_camera->focallength);
  825. }
  826. if (pr_camera->focallength && pr_camera->cropfactor) {
  827. TIFFSetField(tif,
  828. EXIFTAG_FOCALLENGTHIN35MMFILM,
  829. (short)(pr_camera->focallength *
  830. pr_camera->cropfactor));
  831. }
  832. */
  833. }
  834. static void
  835. process_image_for_capture_bayer(const uint8_t *image, int count)
  836. {
  837. char fname[255];
  838. sprintf(fname, "%s/%d.dng", burst_dir, count);
  839. save_dng(image, fname, count);
  840. }
  841. static void
  842. process_image_for_capture(const uint8_t *image, int count)
  843. {
  844. switch (state_proc.mode->v4l_pixfmt) {
  845. case V4L2_PIX_FMT_UYVY:
  846. case V4L2_PIX_FMT_YUYV:
  847. case V4L2_PIX_FMT_YVYU:
  848. case V4L2_PIX_FMT_VYUY:
  849. process_image_for_capture_yuv(image, count);
  850. break;
  851. default:
  852. process_image_for_capture_bayer(image, count);
  853. break;
  854. }
  855. }
  856. static void
  857. post_process_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
  858. {
  859. char *stdout;
  860. g_subprocess_communicate_utf8_finish(proc, res, &stdout, NULL, NULL);
  861. // The last line contains the file name
  862. int end = strlen(stdout);
  863. // Skip the newline at the end
  864. stdout[--end] = '\0';
  865. char *path = path = stdout + end - 1;
  866. do {
  867. if (*path == '\n') {
  868. path++;
  869. break;
  870. }
  871. --path;
  872. } while (path > stdout);
  873. mp_main_capture_completed(thumb, path);
  874. }
  875. static void
  876. process_capture_burst(GdkTexture *thumb)
  877. {
  878. static char capture_fname[255];
  879. char timestamp[30];
  880. format_timestamp(timestamp);
  881. if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
  882. sprintf(capture_fname,
  883. "%s/IMG%s",
  884. g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
  885. timestamp);
  886. } else if (getenv("XDG_PICTURES_DIR") != NULL) {
  887. sprintf(capture_fname,
  888. "%s/IMG%s",
  889. getenv("XDG_PICTURES_DIR"),
  890. timestamp);
  891. } else {
  892. sprintf(capture_fname,
  893. "%s/Pictures/IMG%s",
  894. getenv("HOME"),
  895. timestamp);
  896. }
  897. bool save_dng = g_settings_get_boolean(settings, "save-raw");
  898. char *postprocessor = g_settings_get_string(settings, "postprocessor");
  899. if (postprocessor == NULL) {
  900. g_printerr("Postprocessor setting is null\n");
  901. return;
  902. }
  903. char save_dng_s[2] = "0";
  904. if (save_dng) {
  905. save_dng_s[0] = '1';
  906. }
  907. // Start post-processing the captured burst
  908. g_print("Post process %s to %s.ext (save-dng %s)\n",
  909. burst_dir,
  910. capture_fname,
  911. save_dng_s);
  912. g_autoptr(GError) error = NULL;
  913. GSubprocess *proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE,
  914. &error,
  915. postprocessor,
  916. burst_dir,
  917. capture_fname,
  918. save_dng_s,
  919. NULL);
  920. if (!proc) {
  921. g_printerr("Failed to spawn postprocess process: %s\n",
  922. error->message);
  923. return;
  924. }
  925. g_subprocess_communicate_utf8_async(
  926. proc, NULL, NULL, (GAsyncReadyCallback)post_process_finished, thumb);
  927. }
  928. static void
  929. process_image(MPPipeline *pipeline, const MPBuffer *buffer)
  930. {
  931. #ifdef PROFILE_PROCESS
  932. clock_t t1 = clock();
  933. #endif
  934. size_t size = (libmegapixels_mode_width_to_bytes(state_proc.mode->format,
  935. state_proc.mode->width) +
  936. libmegapixels_mode_width_to_padding(state_proc.mode->format,
  937. state_proc.mode->width)) *
  938. state_proc.mode->height;
  939. uint8_t *image = malloc(size);
  940. memcpy(image, buffer->data, size);
  941. mp_io_pipeline_release_buffer(buffer->index);
  942. if (movie_recording) {
  943. char name[1024];
  944. switch (state_proc.mode->v4l_pixfmt) {
  945. case V4L2_PIX_FMT_UYVY:
  946. case V4L2_PIX_FMT_YUYV:
  947. case V4L2_PIX_FMT_YVYU:
  948. case V4L2_PIX_FMT_VYUY:
  949. get_name(name, burst_dir, "grw");
  950. save_grw(image, name);
  951. break;
  952. default:
  953. get_name(name, burst_dir, "dng");
  954. save_dng(image, name, 1);
  955. break;
  956. }
  957. }
  958. MPZBarImage *zbar_image = mp_zbar_image_new(image,
  959. state_proc.mode->format,
  960. state_proc.mode->width,
  961. state_proc.mode->height,
  962. state_proc.camera_rotation,
  963. state_proc.mode->mirrored);
  964. mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image));
  965. #ifdef PROFILE_PROCESS
  966. clock_t t2 = clock();
  967. #endif
  968. GdkTexture *thumb = process_image_for_preview(image);
  969. if (state_proc.captures_remaining > 0) {
  970. --state_proc.captures_remaining;
  971. process_image_for_capture(image, state_proc.counter++);
  972. if (state_proc.captures_remaining == 0) {
  973. assert(thumb);
  974. process_capture_burst(thumb);
  975. } else {
  976. assert(!thumb);
  977. }
  978. } else {
  979. assert(!thumb);
  980. }
  981. mp_zbar_image_unref(zbar_image);
  982. ++frames_processed;
  983. if (state_proc.captures_remaining == 0) {
  984. is_capturing = false;
  985. }
  986. #ifdef PROFILE_PROCESS
  987. clock_t t3 = clock();
  988. printf("process_image %fms, step 1:%fms, step 2:%fms\n",
  989. (float)(t3 - t1) / CLOCKS_PER_SEC * 1000,
  990. (float)(t2 - t1) / CLOCKS_PER_SEC * 1000,
  991. (float)(t3 - t2) / CLOCKS_PER_SEC * 1000);
  992. #endif
  993. }
  994. void
  995. mp_process_pipeline_process_image(MPBuffer buffer)
  996. {
  997. #ifdef DEBUG_FPS
  998. static clock_t last, now;
  999. static int last_n, now_n;
  1000. now_n++;
  1001. now = clock();
  1002. if (now - last > CLOCKS_PER_SEC * 10) {
  1003. printf("period %fms -- %d -- %f fps\n",
  1004. (float)(now - last) / CLOCKS_PER_SEC * 1000,
  1005. now_n - last_n,
  1006. ((float) now_n - last_n) / ((now - last) / CLOCKS_PER_SEC));
  1007. last = now;
  1008. last_n = now_n;
  1009. }
  1010. #endif
  1011. // If we haven't processed the previous frame yet, drop this one
  1012. if (frames_received != frames_processed && !is_capturing) {
  1013. printf("Dropping frame\n");
  1014. mp_io_pipeline_release_buffer(buffer.index);
  1015. return;
  1016. }
  1017. ++frames_received;
  1018. mp_pipeline_invoke(pipeline,
  1019. (MPPipelineCallback)process_image,
  1020. &buffer,
  1021. sizeof(MPBuffer));
  1022. }
  1023. static void
  1024. capture()
  1025. {
  1026. setup_capture();
  1027. state_proc.captures_remaining = state_proc.burst_length;
  1028. state_proc.counter = 0;
  1029. }
  1030. void
  1031. mp_process_pipeline_capture()
  1032. {
  1033. is_capturing = true;
  1034. mp_pipeline_invoke(pipeline, capture, NULL, 0);
  1035. }
  1036. static void
  1037. on_output_changed(bool format_changed)
  1038. {
  1039. output_buffer_width = state_proc.mode->width / 2;
  1040. output_buffer_height = state_proc.mode->height / 2;
  1041. if (state_proc.mode->rotation != 0 && state_proc.mode->rotation != 180) {
  1042. int tmp = output_buffer_width;
  1043. output_buffer_width = output_buffer_height;
  1044. output_buffer_height = tmp;
  1045. }
  1046. for (size_t i = 0; i < NUM_BUFFERS; ++i) {
  1047. glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
  1048. glTexImage2D(GL_TEXTURE_2D,
  1049. 0,
  1050. GL_RGBA,
  1051. output_buffer_width,
  1052. output_buffer_height,
  1053. 0,
  1054. GL_RGBA,
  1055. GL_UNSIGNED_BYTE,
  1056. NULL);
  1057. }
  1058. glBindTexture(GL_TEXTURE_2D, 0);
  1059. // Create new gles2_debayer on format change
  1060. if (format_changed) {
  1061. if (gles2_debayer)
  1062. gles2_debayer_free(gles2_debayer);
  1063. gles2_debayer = gles2_debayer_new(state_proc.mode->format);
  1064. check_gl();
  1065. gles2_debayer_use(gles2_debayer);
  1066. }
  1067. state_proc.blacklevel = 0.0f;
  1068. state_proc.red = 1.0f;
  1069. state_proc.blue = 1.0f;
  1070. gles2_debayer_configure(gles2_debayer,
  1071. output_buffer_width,
  1072. output_buffer_height,
  1073. state_proc.mode->width,
  1074. state_proc.mode->height,
  1075. state_proc.mode->rotation,
  1076. state_proc.mode->mirrored,
  1077. state_proc.calibration);
  1078. }
  1079. static int
  1080. mod(int a, int b)
  1081. {
  1082. int r = a % b;
  1083. return r < 0 ? r + b : r;
  1084. }
  1085. static void
  1086. update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
  1087. {
  1088. bool camera_changed = state_proc.camera != new_state->camera;
  1089. state_proc.configuration = new_state->configuration;
  1090. state_proc.camera = new_state->camera;
  1091. state_proc.gain.control = new_state->gain.control;
  1092. state_proc.gain.auto_control = new_state->gain.auto_control;
  1093. state_proc.gain.value = new_state->gain.value;
  1094. state_proc.gain.max = new_state->gain.max;
  1095. state_proc.gain.manual = new_state->gain.manual;
  1096. state_proc.exposure.control = new_state->exposure.control;
  1097. state_proc.exposure.auto_control = new_state->exposure.auto_control;
  1098. state_proc.exposure.value = new_state->exposure.value;
  1099. state_proc.exposure.max = new_state->exposure.max;
  1100. state_proc.exposure.manual = new_state->exposure.manual;
  1101. state_proc.focus.control = new_state->focus.control;
  1102. state_proc.focus.auto_control = new_state->focus.auto_control;
  1103. state_proc.focus.value = new_state->focus.value;
  1104. state_proc.focus.max = new_state->focus.max;
  1105. state_proc.focus.manual = new_state->focus.manual;
  1106. const bool output_changed =
  1107. !libmegapixels_mode_equals(state_proc.mode,
  1108. new_state->camera->current_mode) ||
  1109. state_proc.preview_width != new_state->preview_width ||
  1110. state_proc.preview_height != new_state->preview_height ||
  1111. state_proc.device_rotation != new_state->device_rotation;
  1112. bool format_changed = state_proc.mode == NULL;
  1113. if (!format_changed && state_proc.mode->v4l_pixfmt !=
  1114. new_state->camera->current_mode->v4l_pixfmt) {
  1115. format_changed = true;
  1116. }
  1117. state_proc.mode = new_state->camera->current_mode;
  1118. state_proc.preview_width = new_state->preview_width;
  1119. state_proc.preview_height = new_state->preview_height;
  1120. state_proc.device_rotation = new_state->device_rotation;
  1121. state_proc.burst_length = new_state->burst_length;
  1122. state_proc.balance[0] = new_state->balance[0];
  1123. state_proc.balance[1] = new_state->balance[1];
  1124. state_proc.balance[2] = new_state->balance[2];
  1125. state_proc.flash_enabled = new_state->flash_enabled;
  1126. if (output_changed) {
  1127. state_proc.camera_rotation = mod(
  1128. state_proc.mode->rotation - state_proc.device_rotation, 360);
  1129. on_output_changed(format_changed);
  1130. }
  1131. if (camera_changed) {
  1132. char cf[PATH_MAX];
  1133. if (find_calibration(cf, state_proc.camera->name)) {
  1134. state_proc.calibration = parse_calibration_file(cf);
  1135. } else {
  1136. printf("No calibration for %s\n", state_proc.camera->name);
  1137. }
  1138. }
  1139. mp_state_main new_main = {
  1140. .camera = pr_camera,
  1141. .has_auto_focus_continuous = false,
  1142. .has_auto_focus_start = false,
  1143. .preview_buffer_width = output_buffer_width,
  1144. .preview_buffer_height = output_buffer_height,
  1145. .gain.control = state_proc.gain.control,
  1146. .gain.auto_control = state_proc.gain.auto_control,
  1147. .gain.value = state_proc.gain.value,
  1148. .gain.value_req = state_proc.gain.value_req,
  1149. .gain.max = state_proc.gain.max,
  1150. .gain.manual = state_proc.gain.manual,
  1151. .exposure.control = state_proc.exposure.control,
  1152. .exposure.auto_control = state_proc.exposure.auto_control,
  1153. .exposure.value = state_proc.exposure.value,
  1154. .exposure.value_req = state_proc.exposure.value_req,
  1155. .exposure.max = state_proc.exposure.max,
  1156. .exposure.manual = state_proc.exposure.manual,
  1157. .focus.control = state_proc.focus.control,
  1158. .focus.auto_control = state_proc.focus.auto_control,
  1159. .focus.value = state_proc.focus.value,
  1160. .focus.max = state_proc.focus.max,
  1161. .focus.manual = state_proc.focus.manual,
  1162. .stats.exposure = state_proc.stats.exposure,
  1163. .stats.temp = state_proc.stats.temp,
  1164. .stats.tint = state_proc.stats.tint,
  1165. .stats.focus = state_proc.stats.focus,
  1166. .flash_enabled = state_proc.flash_enabled,
  1167. };
  1168. mp_main_update_state(&new_main);
  1169. }
  1170. void
  1171. mp_process_pipeline_update_state(const mp_state_proc *new_state)
  1172. {
  1173. mp_pipeline_invoke(pipeline,
  1174. (MPPipelineCallback)update_state,
  1175. new_state,
  1176. sizeof(mp_state_proc));
  1177. }
  1178. // GTK4 seems to require this
  1179. void
  1180. pango_fc_font_get_languages()
  1181. {
  1182. }