123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- #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;
-
-
-
-
-
-
-
-
- };
- struct device_info {
- const char *media_dev_name;
- 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 int
- xioctl(int fd, int request, void *arg)
- {
- int c = 0;
- int r;
- do {
- r = ioctl(fd, request, arg);
- ++c;
- if (c > 1)
- printf("ioctl retry %d\n", c);
- } while (r == -1 && errno == EINTR);
- return r;
- }
- static int
- v4l2_ctrl_set(int fd, uint32_t id, int val)
- {
- struct v4l2_control ctrl = {0};
- ctrl.id = id;
- ctrl.value = val;
- if (xioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) {
- g_printerr("Failed to set control %d to %d\n", id, val);
- return -1;
- }
- return 0;
- }
- static int
- v4l2_ctrl_get(int fd, uint32_t id)
- {
- struct v4l2_control ctrl = {0};
- ctrl.id = id;
- if (xioctl(fd, VIDIOC_G_CTRL, &ctrl) == -1) {
- g_printerr("Failed to get control %d\n", id);
- return -1;
- }
- return ctrl.value;
- }
- static int
- v4l2_ctrl_get_max(int fd, uint32_t id)
- {
- struct v4l2_queryctrl queryctrl;
- int ret;
- memset(&queryctrl, 0, sizeof(queryctrl));
- queryctrl.id = id;
- ret = xioctl(fd, VIDIOC_QUERYCTRL, &queryctrl);
- if (ret)
- return 0;
- if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
- return 0;
- }
- return queryctrl.maximum;
- }
- static int
- v4l2_has_control(int fd, int control_id)
- {
- struct v4l2_queryctrl queryctrl;
- int ret;
- memset(&queryctrl, 0, sizeof(queryctrl));
- queryctrl.id = control_id;
- ret = xioctl(fd, VIDIOC_QUERYCTRL, &queryctrl);
- if (ret)
- return 0;
- if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
- return 0;
- }
- return 1;
- }
- static void setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
- {
-
- 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;
-
- 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(info->device, info->media_dev_name);
- if (!entity) {
- g_printerr("Count 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("Count 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("Count 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;
-
- 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("Count 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);
-
- if (v4l2_has_control(info->fd, V4L2_CID_FOCUS_AUTO)) {
- info->has_auto_focus_continuous = true;
- v4l2_ctrl_set(info->fd, V4L2_CID_FOCUS_AUTO, 1);
- }
- if (v4l2_has_control(info->fd, V4L2_CID_AUTO_FOCUS_START)) {
- info->has_auto_focus_start = true;
- }
- if (v4l2_has_control(info->fd, V4L2_CID_GAIN)) {
- info->gain_ctrl = V4L2_CID_GAIN;
- info->gain_max = v4l2_ctrl_get_max(info->fd, V4L2_CID_GAIN);
- }
- if (v4l2_has_control(info->fd, V4L2_CID_ANALOGUE_GAIN)) {
- info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
- info->gain_max = v4l2_ctrl_get_max(info->fd, V4L2_CID_ANALOGUE_GAIN);
- }
- }
- }
- 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];
-
- if (!current_controls.gain_is_manual) {
- current_controls.gain = v4l2_ctrl_get(info->fd, info->gain_ctrl);
- }
- if (!current_controls.exposure_is_manual) {
- current_controls.exposure = v4l2_ctrl_get(info->fd, 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;
-
- v4l2_ctrl_set(info->fd, V4L2_CID_AUTOGAIN, 0);
- v4l2_ctrl_set(info->fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL);
-
- 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()
- {
-
- if (captures_remaining > 0) {
- return;
- }
- struct camera_info *info = &cameras[camera->index];
- if (want_focus) {
- if (info->has_auto_focus_continuous) {
- v4l2_ctrl_set(info->fd, V4L2_CID_FOCUS_AUTO, 1);
- } else if (info->has_auto_focus_start) {
- v4l2_ctrl_set(info->fd, V4L2_CID_AUTO_FOCUS_START, 1);
- }
- want_focus = false;
- }
- if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
- v4l2_ctrl_set(info->fd, V4L2_CID_AUTOGAIN, desired_controls.gain_is_manual ? 0 : 1);
- }
- if (!desired_controls.gain_is_manual && current_controls.gain != desired_controls.gain) {
- v4l2_ctrl_set(info->fd, info->gain_ctrl, desired_controls.gain);
- }
- if (current_controls.exposure_is_manual != desired_controls.exposure_is_manual) {
- v4l2_ctrl_set(
- info->fd,
- 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) {
- v4l2_ctrl_set(info->fd, V4L2_CID_EXPOSURE, desired_controls.exposure);
- }
- current_controls = desired_controls;
- }
- static void
- on_frame(MPImage image, void *data)
- {
-
- update_controls();
-
-
- if (just_switched_mode)
- {
- if (blank_frame_count < 20) {
-
- 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;
- }
-
- 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;
-
- mp_process_pipeline_process_image(image);
- if (captures_remaining > 0) {
- --captures_remaining;
- if (captures_remaining == 0) {
- struct camera_info *info = &cameras[camera->index];
-
- if (!current_controls.exposure_is_manual) {
- v4l2_ctrl_set(info->fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_AUTO);
- }
- if (!current_controls.gain_is_manual) {
- v4l2_ctrl_set(info->fd, V4L2_CID_AUTOGAIN, 1);
- }
-
- 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)
- {
-
-
- 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 = v4l2_ctrl_get(info->fd, V4L2_CID_EXPOSURE_AUTO) == V4L2_EXPOSURE_MANUAL;
- current_controls.gain = v4l2_ctrl_get(info->fd, info->gain_ctrl);
- current_controls.exposure_is_manual = v4l2_ctrl_get(info->fd, V4L2_CID_AUTOGAIN) == 0;
- current_controls.exposure = v4l2_ctrl_get(info->fd, 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));
- }
|