123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- #include "io_pipeline.h"
- #include "camera.h"
- #include "flash.h"
- #include "main.h"
- #include "pipeline.h"
- #include "process_pipeline.h"
- #include "state.h"
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <glib.h>
- #include <math.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/prctl.h>
- mp_state_io state_io;
- MPCamera *mpcamera = NULL;
- static MPPipeline *pipeline;
- static GSource *capture_source;
- static bool pipeline_changed = true;
- typedef struct invoke_set_control {
- MPControl *control;
- int32_t int_value;
- bool bool_value;
- } invoke_set_control;
- static void
- setup(MPPipeline *pipeline, const void *data)
- {
- prctl(PR_SET_NAME, "megapixels-io", NULL, NULL, NULL);
- }
- void
- mp_io_pipeline_start()
- {
- mp_process_pipeline_start();
- pipeline = mp_pipeline_new();
- mp_pipeline_invoke(pipeline, setup, NULL, 0);
- }
- void
- mp_io_pipeline_stop()
- {
- if (capture_source) {
- g_source_destroy(capture_source);
- }
- mp_pipeline_free(pipeline);
- mp_process_pipeline_stop();
- }
- /*
- * Update state from IO -> Process
- */
- static void
- update_process_pipeline()
- {
- if (!pipeline_changed) {
- return;
- }
- pipeline_changed = false;
- // Grab the latest control values
- if (!state_io.gain.manual && state_io.gain.control.id) {
- state_io.gain.value =
- mp_camera_control_get_int32(&state_io.gain.control);
- }
- if (!state_io.exposure.manual && state_io.exposure.control.id) {
- state_io.exposure.value =
- mp_camera_control_get_int32(&state_io.exposure.control);
- }
- if (!state_io.focus.manual && state_io.focus.control.id) {
- state_io.focus.value =
- mp_camera_control_get_int32(&state_io.focus.control);
- }
- float balance_red = 1.0f;
- float balance_blue = 1.0f;
- if (state_io.red.control.id && state_io.blue.control.id) {
- int red = mp_camera_control_get_int32(&state_io.red.control);
- int blue = mp_camera_control_get_int32(&state_io.blue.control);
- balance_red = (float)red / (float)state_io.red.max;
- balance_blue = (float)blue / (float)state_io.blue.max;
- }
- mp_state_proc new_state = {
- .camera = state_io.camera,
- .configuration = state_io.configuration,
- .burst_length = state_io.burst_length,
- .preview_width = state_io.preview_width,
- .preview_height = state_io.preview_height,
- .device_rotation = state_io.device_rotation,
- .gain.control = state_io.gain.control,
- .gain.auto_control = state_io.gain.auto_control,
- .gain.value = state_io.gain.value,
- .gain.max = state_io.gain.max,
- .gain.manual = state_io.gain.manual,
- .dgain.control = state_io.dgain.control,
- .dgain.auto_control = state_io.dgain.auto_control,
- .dgain.value = state_io.dgain.value,
- .dgain.max = state_io.dgain.max,
- .dgain.manual = state_io.dgain.manual,
- .exposure.control = state_io.exposure.control,
- .exposure.auto_control = state_io.exposure.auto_control,
- .exposure.value = state_io.exposure.value,
- .exposure.max = state_io.exposure.max,
- .exposure.manual = state_io.exposure.manual,
- .focus.control = state_io.focus.control,
- .focus.auto_control = state_io.focus.auto_control,
- .focus.value = state_io.focus.value,
- .focus.max = state_io.focus.max,
- .focus.manual = state_io.focus.manual,
- .balance = { balance_red, 1.0f, balance_blue },
- .flash_enabled = state_io.flash_enabled,
- };
- mp_process_pipeline_update_state(&new_state);
- }
- static void
- focus(MPPipeline *pipeline, const void *data)
- {
- state_io.trigger_af = true;
- }
- void
- mp_io_pipeline_focus()
- {
- mp_pipeline_invoke(pipeline, focus, NULL, 0);
- }
- static void
- set_control_int32(MPPipeline *pipeline, const void *data)
- {
- const invoke_set_control *control_data = (const invoke_set_control *)data;
- mp_camera_control_set_int32(control_data->control, control_data->int_value);
- }
- void
- mp_io_pipeline_set_control_int32(MPControl *control, uint32_t value)
- {
- invoke_set_control data = { 0 };
- data.control = control;
- data.int_value = value;
- mp_pipeline_invoke(
- pipeline, set_control_int32, &data, sizeof(invoke_set_control));
- }
- static void
- capture(MPPipeline *pipeline, const void *data)
- {
- float gain_norm;
- // Disable the autogain/exposure while taking the burst
- mp_camera_control_set_int32(&state_io.gain.auto_control, 0);
- mp_camera_control_set_int32(&state_io.exposure.auto_control,
- V4L2_EXPOSURE_MANUAL);
- // Get current gain to calculate a burst length;
- // with low gain there's 3, with the max automatic gain of the ov5640
- // the value seems to be 248 which creates a 5 frame burst
- // for manual gain you can go up to 11 frames
- state_io.gain.value = mp_camera_control_get_int32(&state_io.gain.control);
- state_io.dgain.value = mp_camera_control_get_int32(&state_io.dgain.control);
- gain_norm = (float)state_io.gain.value / (float)state_io.gain.max;
- state_io.burst_length = (int)fmax(sqrtf(gain_norm) * 10, 2) + 1;
- state_io.burst_length = MAX(1, state_io.burst_length);
- state_io.captures_remaining = state_io.burst_length;
- // Change camera mode for capturing
- mp_process_pipeline_sync();
- mp_camera_stop_capture(mpcamera);
- struct v4l2_format format = { 0 };
- libmegapixels_select_mode(state_io.camera, state_io.mode_capture, &format);
- state_io.flush_pipeline = true;
- mp_camera_start_capture(mpcamera);
- // Enable flash
- if (state_io.flash_enabled) {
- mp_flash_enable(state_io.camera);
- }
- update_process_pipeline();
- mp_process_pipeline_capture();
- }
- void
- mp_io_pipeline_capture()
- {
- mp_pipeline_invoke(pipeline, capture, NULL, 0);
- }
- static void
- release_buffer(MPPipeline *pipeline, const uint32_t *buffer_index)
- {
- mp_camera_release_buffer(mpcamera, *buffer_index);
- }
- void
- mp_io_pipeline_release_buffer(uint32_t buffer_index)
- {
- mp_pipeline_invoke(pipeline,
- (MPPipelineCallback)release_buffer,
- &buffer_index,
- sizeof(uint32_t));
- }
- static pid_t focus_continuous_task = 0;
- static pid_t start_focus_task = 0;
- static void
- start_focus()
- {
- // only run 1 manual focus at once
- if (!mp_camera_check_task_complete(mpcamera, start_focus_task) ||
- !mp_camera_check_task_complete(mpcamera, focus_continuous_task))
- return;
- if (state_io.focus.control.id) {
- focus_continuous_task = mp_camera_control_set_bool_bg(
- mpcamera, &state_io.focus.control, 1);
- } else if (state_io.can_af_trigger) {
- // TODO improve
- MPControl auto_focus_start_control;
- auto_focus_start_control.id = V4L2_CID_AUTO_FOCUS_START;
- auto_focus_start_control.fd = state_io.camera->sensor_fd;
- start_focus_task = mp_camera_control_set_bool_bg(
- mpcamera, &auto_focus_start_control, 1);
- }
- }
- static void
- update_controls()
- {
- bool state_changed = false;
- // Don't update controls while capturing
- if (state_io.captures_remaining > 0) {
- return;
- }
- if (state_io.trigger_af) {
- state_io.trigger_af = false;
- start_focus();
- }
- // Change focus manual/auto if it got changed by the user
- if (state_io.focus.manual != state_io.focus.manual_req) {
- state_io.focus.manual = state_io.focus.manual_req;
- if (state_io.focus.auto_control.id > 0) {
- mp_camera_control_set_bool_bg(mpcamera,
- &state_io.focus.auto_control,
- !state_io.gain.manual_req);
- }
- state_changed = true;
- }
- // If focus is manual, OR auto but no auto control, and the value got
- // updated by the program/user, update the manual control
- if ((state_io.focus.manual ||
- (!state_io.focus.manual && state_io.focus.auto_control.id == 0)) &&
- state_io.focus.value != state_io.focus.value_req) {
- mp_camera_control_set_int32_bg(
- mpcamera, &state_io.focus.control, state_io.focus.value_req);
- state_io.focus.value = state_io.focus.value_req;
- state_changed = true;
- }
- if (state_io.gain.manual != state_io.gain.manual_req) {
- mp_camera_control_set_bool_bg(mpcamera,
- &state_io.gain.auto_control,
- !state_io.gain.manual_req);
- state_io.gain.manual = state_io.gain.manual_req;
- state_changed = true;
- }
- if ((state_io.gain.manual ||
- (!state_io.gain.manual && state_io.gain.auto_control.id == 0)) &&
- state_io.gain.value != state_io.gain.value_req) {
- mp_camera_control_set_int32_bg(
- mpcamera, &state_io.gain.control, state_io.gain.value_req);
- state_io.gain.value = state_io.gain.value_req;
- state_changed = true;
- }
- if (state_io.exposure.manual != state_io.exposure.manual_req) {
- mp_camera_control_set_bool_bg(mpcamera,
- &state_io.exposure.auto_control,
- state_io.exposure.manual_req ?
- V4L2_EXPOSURE_MANUAL :
- V4L2_EXPOSURE_AUTO);
- state_io.exposure.manual = state_io.exposure.manual_req;
- state_changed = true;
- }
- if (state_io.exposure.manual &&
- state_io.exposure.value != state_io.exposure.value_req) {
- mp_camera_control_set_int32_bg(mpcamera,
- &state_io.exposure.control,
- state_io.exposure.value_req);
- state_io.exposure.value = state_io.exposure.value_req;
- state_changed = true;
- }
- if (state_changed) {
- pipeline_changed = true;
- update_process_pipeline();
- }
- }
- static void
- do_aaa()
- {
- bool auto_exposure =
- !state_io.exposure.manual && state_io.exposure.auto_control.id == 0;
- if (auto_exposure) {
- int direction = state_io.stats.exposure;
- int step = 0;
- if (direction > 0) {
- // Preview is too dark
- // Try raising the exposure time first
- if (state_io.exposure.value < state_io.exposure.max) {
- step = state_io.exposure.value / 16;
- state_io.exposure.value_req =
- state_io.exposure.value + (step * direction);
- printf("Expose + %d\n", state_io.exposure.value_req);
- } else {
- // Raise sensor gain if exposure limit is hit
- step = state_io.gain.value / 16;
- state_io.gain.value_req =
- state_io.gain.value + (step * direction);
- printf("Gain + %d\n", state_io.gain.value_req);
- }
- } else if (direction < 0) {
- // Preview is too bright
- // Lower the sensor gain first to have less noise
- if (state_io.gain.value > 0) {
- step = state_io.gain.value / 16;
- state_io.gain.value_req =
- state_io.gain.value + (step * direction);
- printf("Gain - %d\n", state_io.gain.value_req);
- } else {
- // Shorten the exposure time to go even darker
- step = state_io.exposure.value / 16;
- state_io.exposure.value_req =
- state_io.exposure.value + (step * direction);
- printf("Expose - %d\n", state_io.exposure.value_req);
- }
- }
- }
- }
- static void
- on_frame(MPBuffer buffer, void *_data)
- {
- // Don't process frame when the window is not active, unless we're capturing
- // an image, in which case the flash window may be active instead of this
- // window
- if (!check_window_active() && state_io.captures_remaining == 0) {
- return;
- }
- pipeline_changed = true;
- // Only update controls right after a frame was captured
- do_aaa();
- update_controls();
- // When the mode is switched while capturing we get a couple blank frames,
- // presumably from buffers made ready during the switch. Ignore these.
- if (state_io.flush_pipeline) {
- if (state_io.blank_frame_count < 20) {
- // Only check a 10x10 area
- size_t test_size =
- MIN(10, state_io.camera->current_mode->width) *
- MIN(10, state_io.camera->current_mode->height);
- bool image_is_blank = true;
- for (size_t i = 0; i < test_size; ++i) {
- if (buffer.data[i] != 0) {
- image_is_blank = false;
- }
- }
- if (image_is_blank) {
- ++state_io.blank_frame_count;
- return;
- }
- } else {
- printf("Blank image limit reached, resulting capture may be blank\n");
- }
- state_io.flush_pipeline = false;
- state_io.blank_frame_count = 0;
- }
- // Send the image off for processing
- mp_process_pipeline_process_image(buffer);
- if (state_io.captures_remaining > 0) {
- --state_io.captures_remaining;
- if (state_io.captures_remaining == 0) {
- // Restore the auto exposure and gain if needed
- if (!state_io.exposure.manual) {
- mp_camera_control_set_int32_bg(
- mpcamera,
- &state_io.exposure.auto_control,
- V4L2_EXPOSURE_AUTO);
- }
- if (!state_io.gain.manual) {
- mp_camera_control_set_bool_bg(
- mpcamera, &state_io.gain.auto_control, true);
- }
- // Go back to preview mode
- mp_process_pipeline_sync();
- mp_camera_stop_capture(mpcamera);
- struct v4l2_format format = { 0 };
- libmegapixels_select_mode(
- state_io.camera, state_io.mode_preview, &format);
- state_io.flush_pipeline = true;
- mp_camera_start_capture(mpcamera);
- // Disable flash
- if (state_io.flash_enabled) {
- mp_flash_disable(state_io.camera);
- }
- update_process_pipeline();
- }
- }
- }
- static void
- init_controls()
- {
- MPControl focus_control;
- // V4L2_CID_FOCUS_ABSOLUTE exists on the sensor for PP, but on the lens for
- // PPP. Check both, if applicable.
- if ((state_io.camera->lens_fd > 0 &&
- mp_camera_query_control(state_io.camera->lens_fd,
- V4L2_CID_FOCUS_ABSOLUTE,
- &focus_control)) ||
- mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_FOCUS_ABSOLUTE,
- &focus_control)) {
- state_io.focus.control = focus_control;
- state_io.focus.max = focus_control.max;
- state_io.focus.value =
- mp_camera_control_get_int32(&state_io.focus.control);
- } else {
- state_io.focus.control.id = 0;
- }
- MPControl auto_focus_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_FOCUS_AUTO,
- &auto_focus_control)) {
- mp_camera_control_set_bool_bg(mpcamera, &auto_focus_control, true);
- state_io.focus.auto_control = auto_focus_control;
- } else {
- state_io.focus.auto_control.id = 0;
- }
- state_io.can_af_trigger = mp_camera_query_control(
- state_io.camera->sensor_fd, V4L2_CID_AUTO_FOCUS_START, NULL);
- MPControl gain_control;
- if (mp_camera_query_control(
- state_io.camera->sensor_fd, V4L2_CID_GAIN, &gain_control)) {
- state_io.gain.control = gain_control;
- state_io.gain.max = gain_control.max;
- } else if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_ANALOGUE_GAIN,
- &gain_control)) {
- state_io.gain.control = gain_control;
- state_io.gain.max = gain_control.max;
- } else {
- state_io.gain.max = 0;
- state_io.gain.control.id = 0;
- }
- if (state_io.gain.control.id) {
- state_io.gain.value =
- mp_camera_control_get_int32(&state_io.gain.control);
- } else {
- state_io.gain.value = 0;
- }
- MPControl auto_gain_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_AUTOGAIN,
- &auto_gain_control)) {
- state_io.gain.auto_control = auto_gain_control;
- state_io.gain.manual =
- mp_camera_control_get_bool(&auto_gain_control) == 0;
- } else {
- state_io.gain.auto_control.id = 0;
- }
- MPControl dgain_control;
- if (mp_camera_query_control(
- state_io.camera->sensor_fd, V4L2_CID_DIGITAL_GAIN, &dgain_control)) {
- state_io.dgain.control = dgain_control;
- state_io.dgain.max = dgain_control.max;
- printf("Have d/gain, max: %d\n", dgain_control.max);
- } else {
- state_io.dgain.max = 0;
- state_io.dgain.control.id = 0;
- }
- mp_camera_control_set_int32(&state_io.gain.control, 768);
- if (state_io.dgain.control.id) {
- state_io.dgain.value =
- mp_camera_control_get_int32(&state_io.dgain.control);
- } else {
- state_io.dgain.value = 0;
- }
- MPControl exposure_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_EXPOSURE,
- &exposure_control)) {
- state_io.exposure.control = exposure_control;
- state_io.exposure.max = exposure_control.max;
- state_io.exposure.value =
- mp_camera_control_get_int32(&exposure_control);
- } else {
- state_io.exposure.control.id = 0;
- }
- MPControl auto_exposure_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_EXPOSURE_AUTO,
- &auto_exposure_control)) {
- state_io.exposure.auto_control = auto_exposure_control;
- state_io.exposure.manual =
- mp_camera_control_get_int32(&auto_exposure_control) ==
- V4L2_EXPOSURE_MANUAL;
- } else {
- state_io.exposure.auto_control.id = 0;
- }
- MPControl red_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_RED_BALANCE,
- &red_control)) {
- state_io.red.control = red_control;
- state_io.red.max = red_control.max;
- } else {
- state_io.red.control.id = 0;
- }
- MPControl blue_control;
- if (mp_camera_query_control(state_io.camera->sensor_fd,
- V4L2_CID_BLUE_BALANCE,
- &blue_control)) {
- state_io.blue.control = blue_control;
- state_io.blue.max = blue_control.max;
- } else {
- state_io.blue.control.id = 0;
- }
- pipeline_changed = true;
- update_process_pipeline();
- }
- /*
- * State transfer from Main -> IO
- */
- static void
- update_state(MPPipeline *pipeline, const mp_state_io *new_state)
- {
- if (state_io.camera != new_state->camera) {
- if (state_io.camera != NULL) {
- mp_process_pipeline_sync();
- mp_camera_stop_capture(mpcamera);
- libmegapixels_close(state_io.camera);
- }
- if (capture_source) {
- g_source_destroy(capture_source);
- capture_source = NULL;
- }
- state_io.camera = new_state->camera;
- if (state_io.camera) {
- mpcamera = mp_camera_new(state_io.camera);
- state_io.mode_preview = NULL;
- state_io.mode_capture = NULL;
- float score = 0;
- int area_preview =
- state_io.preview_width * state_io.preview_height;
- if (area_preview == 0) {
- area_preview = 1280 * 720;
- }
- for (int m = 0; m < state_io.camera->num_modes; m++) {
- float mscore = 0;
- if (state_io.camera->modes[m]->rate > 29) {
- mscore += 1;
- }
- int mode_area = state_io.camera->modes[m]->width *
- state_io.camera->modes[m]->height;
- mscore += 1.0f -
- (float)(ABS(mode_area - area_preview) /
- area_preview);
- if (mscore > score) {
- state_io.mode_preview =
- state_io.camera->modes[m];
- score = mscore;
- }
- }
- long area = 0;
- for (int m = 0; m < state_io.camera->num_modes; m++) {
- long this_pixels = state_io.camera->modes[m]->width *
- state_io.camera->modes[m]->height;
- if (this_pixels > area) {
- area = this_pixels;
- state_io.mode_capture =
- state_io.camera->modes[m];
- }
- }
- if (state_io.mode_preview == NULL &&
- state_io.mode_capture != NULL) {
- // If no fast preview mode is available, make due
- // with slow modes.
- state_io.mode_preview = state_io.mode_capture;
- }
- if (state_io.mode_preview != NULL) {
- if (state_io.camera->video_fd == 0) {
- libmegapixels_open(state_io.camera);
- }
- struct v4l2_format format = { 0 };
- libmegapixels_select_mode(state_io.camera,
- state_io.mode_preview,
- &format);
- }
- mp_camera_start_capture(mpcamera);
- capture_source = mp_pipeline_add_capture_source(
- pipeline, mpcamera, on_frame, NULL);
- init_controls();
- }
- }
- state_io.configuration = new_state->configuration;
- state_io.burst_length = new_state->burst_length;
- state_io.preview_width = new_state->preview_width;
- state_io.preview_height = new_state->preview_height;
- state_io.device_rotation = new_state->device_rotation;
- if (state_io.camera) {
- state_io.gain.value = new_state->gain.value;
- state_io.gain.value_req = new_state->gain.value_req;
- state_io.gain.manual = new_state->gain.manual;
- state_io.gain.manual_req = new_state->gain.manual_req;
- state_io.dgain.value = new_state->dgain.value;
- state_io.dgain.value_req = new_state->dgain.value_req;
- state_io.dgain.manual = new_state->dgain.manual;
- state_io.dgain.manual_req = new_state->dgain.manual_req;
- state_io.exposure.value = new_state->exposure.value;
- state_io.exposure.value_req = new_state->exposure.value_req;
- state_io.exposure.manual = new_state->exposure.manual;
- state_io.exposure.manual_req = new_state->exposure.manual_req;
- state_io.focus.value = new_state->focus.value;
- state_io.focus.value_req = new_state->focus.value_req;
- state_io.focus.manual = new_state->focus.manual;
- state_io.focus.manual_req = new_state->focus.manual_req;
- state_io.flash_enabled = new_state->flash_enabled;
- state_io.stats.exposure = new_state->stats.exposure;
- state_io.stats.temp = new_state->stats.temp;
- state_io.stats.tint = new_state->stats.tint;
- state_io.stats.focus = new_state->stats.focus;
- }
- update_process_pipeline();
- }
- void
- mp_io_pipeline_update_state(const mp_state_io *state)
- {
- if (!pipeline) {
- printf("no pipeline\n");
- exit(1);
- }
- mp_pipeline_invoke(pipeline,
- (MPPipelineCallback)update_state,
- state,
- sizeof(mp_state_io));
- }
|