123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- #include "io_pipeline.h"
- #include "device.h"
- #include "camera.h"
- #include "pipeline.h"
- #include "process_pipeline.h"
- #include <string.h>
- #include <glib.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <errno.h>
- #include <assert.h>
- #include <stdio.h>
- struct media_link_info {
- unsigned int source_entity_id;
- unsigned int target_entity_id;
- char source_fname[260];
- char target_fname[260];
- };
- struct camera_info {
- size_t device_index;
- unsigned int pad_id;
- char dev_fname[260];
- int fd;
- MPCamera *camera;
- int gain_ctrl;
- int gain_max;
- bool has_auto_focus_continuous;
- bool has_auto_focus_start;
- // unsigned int entity_id;
- // enum v4l2_buf_type type;
- // char media_dev_fname[260];
- // char video_dev_fname[260];
- // int media_fd;
- // struct mp_media_link media_links[MP_MAX_LINKS];
- // int num_media_links;
- // int gain_ctrl;
- };
- struct device_info {
- const char *media_dev_name; // owned by camera config
- MPDevice *device;
- unsigned int interface_pad_id;
- int video_fd;
- };
- static struct camera_info cameras[MP_MAX_CAMERAS];
- static struct device_info devices[MP_MAX_CAMERAS];
- static size_t num_devices = 0;
- static const struct mp_camera_config *camera = NULL;
- static MPCameraMode mode;
- static bool just_switched_mode = false;
- static int blank_frame_count = 0;
- static int burst_length;
- static int captures_remaining = 0;
- static int preview_width;
- static int preview_height;
- struct control_state {
- bool gain_is_manual;
- int gain;
- bool exposure_is_manual;
- int exposure;
- };
- static struct control_state desired_controls = {};
- static struct control_state current_controls = {};
- static bool want_focus = false;
- static MPPipeline *pipeline;
- static GSource *capture_source;
- static void
- setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
- {
- // Find device info
- size_t device_index = 0;
- for (; device_index < num_devices; ++device_index) {
- if (strcmp(config->media_dev_name,
- devices[device_index].media_dev_name) == 0) {
- break;
- }
- }
- if (device_index == num_devices) {
- device_index = num_devices;
- // Initialize new device
- struct device_info *info = &devices[device_index];
- info->media_dev_name = config->media_dev_name;
- info->device = mp_device_list_find_remove(device_list,
- info->media_dev_name);
- if (!info->device) {
- g_printerr("Could not find /dev/media* node matching '%s'\n",
- info->media_dev_name);
- exit(EXIT_FAILURE);
- }
- const struct media_v2_entity *entity =
- mp_device_find_entity_type(info->device, MEDIA_ENT_F_IO_V4L);
- if (!entity) {
- g_printerr("Could not find device video entity\n");
- exit(EXIT_FAILURE);
- }
- const struct media_v2_pad *pad =
- mp_device_get_pad_from_entity(info->device, entity->id);
- info->interface_pad_id = pad->id;
- const struct media_v2_interface *interface =
- mp_device_find_entity_interface(info->device, entity->id);
- char dev_name[260];
- if (!mp_find_device_path(interface->devnode, dev_name, 260)) {
- g_printerr("Could not find video path\n");
- exit(EXIT_FAILURE);
- }
- info->video_fd = open(dev_name, O_RDWR);
- if (info->video_fd == -1) {
- g_printerr("Could not open %s\n", dev_name);
- exit(EXIT_FAILURE);
- }
- ++num_devices;
- }
- {
- struct camera_info *info = &cameras[config->index];
- struct device_info *dev_info = &devices[device_index];
- info->device_index = device_index;
- const struct media_v2_entity *entity =
- mp_device_find_entity(dev_info->device, config->dev_name);
- if (!entity) {
- g_printerr("Could not find camera entity matching '%s'\n",
- config->dev_name);
- exit(EXIT_FAILURE);
- }
- const struct media_v2_pad *pad =
- mp_device_get_pad_from_entity(dev_info->device, entity->id);
- info->pad_id = pad->id;
- // Make sure the camera starts out as disabled
- mp_device_setup_link(dev_info->device, info->pad_id,
- dev_info->interface_pad_id, false);
- const struct media_v2_interface *interface =
- mp_device_find_entity_interface(dev_info->device,
- entity->id);
- if (!mp_find_device_path(interface->devnode, info->dev_fname, 260)) {
- g_printerr("Could not find camera device path\n");
- exit(EXIT_FAILURE);
- }
- info->fd = open(info->dev_fname, O_RDWR);
- if (info->fd == -1) {
- g_printerr("Could not open %s\n", info->dev_fname);
- exit(EXIT_FAILURE);
- }
- info->camera = mp_camera_new(dev_info->video_fd, info->fd);
- // Trigger continuous auto focus if the sensor supports it
- if (mp_camera_query_control(info->camera, V4L2_CID_FOCUS_AUTO,
- NULL)) {
- info->has_auto_focus_continuous = true;
- mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO,
- true);
- }
- if (mp_camera_query_control(info->camera, V4L2_CID_AUTO_FOCUS_START,
- NULL)) {
- info->has_auto_focus_start = true;
- }
- MPControl control;
- if (mp_camera_query_control(info->camera, V4L2_CID_GAIN, &control)) {
- info->gain_ctrl = V4L2_CID_GAIN;
- info->gain_max = control.max;
- } else if (mp_camera_query_control(
- info->camera, V4L2_CID_ANALOGUE_GAIN, &control)) {
- info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
- info->gain_max = control.max;
- }
- }
- }
- static void
- setup(MPPipeline *pipeline, const void *data)
- {
- MPDeviceList *device_list = mp_device_list_new();
- for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
- const struct mp_camera_config *config = mp_get_camera_config(i);
- if (!config) {
- break;
- }
- setup_camera(&device_list, config);
- }
- mp_device_list_free(device_list);
- }
- 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();
- }
- static void
- update_process_pipeline()
- {
- struct camera_info *info = &cameras[camera->index];
- // Grab the latest control values
- if (!current_controls.gain_is_manual) {
- current_controls.gain =
- mp_camera_control_get_int32(info->camera, info->gain_ctrl);
- }
- if (!current_controls.exposure_is_manual) {
- current_controls.exposure =
- mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE);
- }
- struct mp_process_pipeline_state pipeline_state = {
- .camera = camera,
- .mode = mode,
- .burst_length = burst_length,
- .preview_width = preview_width,
- .preview_height = preview_height,
- .gain_is_manual = current_controls.gain_is_manual,
- .gain = current_controls.gain,
- .gain_max = info->gain_max,
- .exposure_is_manual = current_controls.exposure_is_manual,
- .exposure = current_controls.exposure,
- .has_auto_focus_continuous = info->has_auto_focus_continuous,
- .has_auto_focus_start = info->has_auto_focus_start,
- };
- mp_process_pipeline_update_state(&pipeline_state);
- }
- static void
- focus(MPPipeline *pipeline, const void *data)
- {
- want_focus = true;
- }
- void
- mp_io_pipeline_focus()
- {
- mp_pipeline_invoke(pipeline, focus, NULL, 0);
- }
- static void
- capture(MPPipeline *pipeline, const void *data)
- {
- struct camera_info *info = &cameras[camera->index];
- captures_remaining = burst_length;
- // Disable the autogain/exposure while taking the burst
- mp_camera_control_set_int32(info->camera, V4L2_CID_AUTOGAIN, 0);
- mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_MANUAL);
- // Change camera mode for capturing
- mp_camera_stop_capture(info->camera);
- mode = camera->capture_mode;
- mp_camera_set_mode(info->camera, &mode);
- just_switched_mode = true;
- mp_camera_start_capture(info->camera);
- update_process_pipeline();
- mp_process_pipeline_capture();
- }
- void
- mp_io_pipeline_capture()
- {
- mp_pipeline_invoke(pipeline, capture, NULL, 0);
- }
- static void
- update_controls()
- {
- // Don't update controls while capturing
- if (captures_remaining > 0) {
- return;
- }
- struct camera_info *info = &cameras[camera->index];
- if (want_focus) {
- if (info->has_auto_focus_continuous) {
- mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO,
- 1);
- } else if (info->has_auto_focus_start) {
- mp_camera_control_set_bool(info->camera,
- V4L2_CID_AUTO_FOCUS_START, 1);
- }
- want_focus = false;
- }
- if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
- mp_camera_control_set_bool(info->camera, V4L2_CID_AUTOGAIN,
- !desired_controls.gain_is_manual);
- }
- if (desired_controls.gain_is_manual &&
- current_controls.gain != desired_controls.gain) {
- mp_camera_control_set_int32(info->camera, info->gain_ctrl,
- desired_controls.gain);
- }
- if (current_controls.exposure_is_manual !=
- desired_controls.exposure_is_manual) {
- mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO,
- desired_controls.exposure_is_manual ?
- V4L2_EXPOSURE_MANUAL :
- V4L2_EXPOSURE_AUTO);
- }
- if (desired_controls.exposure_is_manual &&
- current_controls.exposure != desired_controls.exposure) {
- mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE,
- desired_controls.exposure);
- }
- current_controls = desired_controls;
- }
- static void
- on_frame(MPImage image, void *data)
- {
- // Only update controls right after a frame was captured
- 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 (just_switched_mode) {
- if (blank_frame_count < 20) {
- // Only check a 50x50 area
- size_t test_size =
- MIN(50, image.width) * MIN(50, image.height);
- bool image_is_blank = true;
- for (size_t i = 0; i < test_size; ++i) {
- if (image.data[i] != 0) {
- image_is_blank = false;
- }
- }
- if (image_is_blank) {
- ++blank_frame_count;
- return;
- }
- } else {
- printf("Blank image limit reached, resulting capture may be blank\n");
- }
- just_switched_mode = false;
- blank_frame_count = 0;
- }
- // Copy from the camera buffer
- size_t size =
- mp_pixel_format_width_to_bytes(image.pixel_format, image.width) *
- image.height;
- uint8_t *buffer = malloc(size);
- memcpy(buffer, image.data, size);
- image.data = buffer;
- // Send the image off for processing
- mp_process_pipeline_process_image(image);
- if (captures_remaining > 0) {
- --captures_remaining;
- if (captures_remaining == 0) {
- struct camera_info *info = &cameras[camera->index];
- // Restore the auto exposure and gain if needed
- if (!current_controls.exposure_is_manual) {
- mp_camera_control_set_int32(info->camera,
- V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_AUTO);
- }
- if (!current_controls.gain_is_manual) {
- mp_camera_control_set_bool(info->camera,
- V4L2_CID_AUTOGAIN, true);
- }
- // Go back to preview mode
- mp_camera_stop_capture(info->camera);
- mode = camera->preview_mode;
- mp_camera_set_mode(info->camera, &mode);
- just_switched_mode = true;
- mp_camera_start_capture(info->camera);
- update_process_pipeline();
- }
- }
- }
- static void
- update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
- {
- // Make sure the state isn't updated more than it needs to be by checking
- // whether this state change actually changes anything.
- bool has_changed = false;
- if (camera != state->camera) {
- has_changed = true;
- if (camera) {
- struct camera_info *info = &cameras[camera->index];
- struct device_info *dev_info = &devices[info->device_index];
- mp_camera_stop_capture(info->camera);
- mp_device_setup_link(dev_info->device, info->pad_id,
- dev_info->interface_pad_id, false);
- }
- if (capture_source) {
- g_source_destroy(capture_source);
- capture_source = NULL;
- }
- camera = state->camera;
- if (camera) {
- struct camera_info *info = &cameras[camera->index];
- struct device_info *dev_info = &devices[info->device_index];
- mp_device_setup_link(dev_info->device, info->pad_id,
- dev_info->interface_pad_id, true);
- mode = camera->preview_mode;
- mp_camera_set_mode(info->camera, &mode);
- mp_camera_start_capture(info->camera);
- capture_source = mp_pipeline_add_capture_source(
- pipeline, info->camera, on_frame, NULL);
- current_controls.gain_is_manual =
- mp_camera_control_get_int32(
- info->camera, V4L2_CID_EXPOSURE_AUTO) ==
- V4L2_EXPOSURE_MANUAL;
- current_controls.gain = mp_camera_control_get_int32(
- info->camera, info->gain_ctrl);
- current_controls.exposure_is_manual =
- mp_camera_control_get_bool(info->camera,
- V4L2_CID_AUTOGAIN) == 0;
- current_controls.exposure = mp_camera_control_get_int32(
- info->camera, V4L2_CID_EXPOSURE);
- }
- }
- has_changed = has_changed || burst_length != state->burst_length ||
- preview_width != state->preview_width ||
- preview_height != state->preview_height;
- burst_length = state->burst_length;
- preview_width = state->preview_width;
- preview_height = state->preview_height;
- if (camera) {
- struct control_state previous_desired = desired_controls;
- desired_controls.gain_is_manual = state->gain_is_manual;
- desired_controls.gain = state->gain;
- desired_controls.exposure_is_manual = state->exposure_is_manual;
- desired_controls.exposure = state->exposure;
- has_changed =
- has_changed || memcmp(&previous_desired, &desired_controls,
- sizeof(struct control_state)) != 0;
- }
- assert(has_changed);
- update_process_pipeline();
- }
- void
- mp_io_pipeline_update_state(const struct mp_io_pipeline_state *state)
- {
- mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, state,
- sizeof(struct mp_io_pipeline_state));
- }
|