Compare commits
7 Commits
5ad97d03f6
...
c584583d29
Author | SHA1 | Date |
---|---|---|
Martijn Braam | c584583d29 | |
Martijn Braam | 68bba0a8a5 | |
Guido Günther | 561c10c612 | |
WebFreak001 | 974fafc1d1 | |
WebFreak001 | 7314739dfc | |
WebFreak001 | ccbaaad72b | |
WebFreak001 | 1282a75db9 |
|
@ -15,7 +15,6 @@ install_data(['postprocess.sh'],
|
||||||
install_mode: 'rwxr-xr-x')
|
install_mode: 'rwxr-xr-x')
|
||||||
|
|
||||||
settings_schemas = ['org.postmarketos.Megapixels.gschema.xml']
|
settings_schemas = ['org.postmarketos.Megapixels.gschema.xml']
|
||||||
schemas_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0' / 'schemas'
|
schemas_dir = get_option('datadir') / 'glib-2.0' / 'schemas'
|
||||||
install_data(settings_schemas, install_dir: schemas_dir)
|
install_data(settings_schemas, install_dir: schemas_dir)
|
||||||
gnome.compile_schemas(depend_files: files(settings_schemas))
|
gnome.compile_schemas(depend_files: files(settings_schemas))
|
||||||
meson.add_install_script('glib-compile-schemas', schemas_dir)
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=GTK;
|
Categories=GTK;
|
||||||
Icon=org.postmarketos.Megapixels
|
Icon=org.postmarketos.Megapixels
|
||||||
|
X-Purism-FormFactor=Workstation;Mobile;
|
|
@ -1,4 +1,4 @@
|
||||||
project('megapixels', 'c', version: '1.1.0')
|
project('megapixels', 'c', version: '1.2.0')
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
gtkdep = dependency('gtk4')
|
gtkdep = dependency('gtk4')
|
||||||
|
|
124
src/camera.c
124
src/camera.c
|
@ -6,9 +6,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define MAX_VIDEO_BUFFERS 20
|
#define MAX_VIDEO_BUFFERS 20
|
||||||
|
#define MAX_BG_TASKS 8
|
||||||
|
|
||||||
static const char *pixel_format_names[MP_PIXEL_FMT_MAX] = {
|
static const char *pixel_format_names[MP_PIXEL_FMT_MAX] = {
|
||||||
"unsupported", "BGGR8", "GBRG8", "GRBG8", "RGGB8", "BGGR10P",
|
"unsupported", "BGGR8", "GBRG8", "GRBG8", "RGGB8", "BGGR10P",
|
||||||
|
@ -219,6 +221,9 @@ struct _MPCamera {
|
||||||
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
|
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
|
||||||
uint32_t num_buffers;
|
uint32_t num_buffers;
|
||||||
|
|
||||||
|
// keeping track of background task child-PIDs for cleanup code
|
||||||
|
int child_bg_pids[MAX_BG_TASKS];
|
||||||
|
|
||||||
bool use_mplane;
|
bool use_mplane;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -250,12 +255,15 @@ mp_camera_new(int video_fd, int subdev_fd)
|
||||||
camera->has_set_mode = false;
|
camera->has_set_mode = false;
|
||||||
camera->num_buffers = 0;
|
camera->num_buffers = 0;
|
||||||
camera->use_mplane = use_mplane;
|
camera->use_mplane = use_mplane;
|
||||||
|
memset(camera->child_bg_pids, 0, sizeof(camera->child_bg_pids[0]) * MAX_BG_TASKS);
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mp_camera_free(MPCamera *camera)
|
mp_camera_free(MPCamera *camera)
|
||||||
{
|
{
|
||||||
|
mp_camera_wait_bg_tasks(camera);
|
||||||
|
|
||||||
g_warn_if_fail(camera->num_buffers == 0);
|
g_warn_if_fail(camera->num_buffers == 0);
|
||||||
if (camera->num_buffers != 0) {
|
if (camera->num_buffers != 0) {
|
||||||
mp_camera_stop_capture(camera);
|
mp_camera_stop_capture(camera);
|
||||||
|
@ -264,6 +272,83 @@ mp_camera_free(MPCamera *camera)
|
||||||
free(camera);
|
free(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mp_camera_add_bg_task(MPCamera *camera, pid_t pid)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
while (true) {
|
||||||
|
for (size_t i = 0; i < MAX_BG_TASKS; ++i) {
|
||||||
|
if (camera->child_bg_pids[i] == 0) {
|
||||||
|
camera->child_bg_pids[i] = pid;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// error == -1, still running == 0
|
||||||
|
if (waitpid(camera->child_bg_pids[i], &status, WNOHANG) <= 0)
|
||||||
|
continue; // consider errored wait still running
|
||||||
|
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
// replace exited
|
||||||
|
camera->child_bg_pids[i] = pid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for any status change on child processes
|
||||||
|
pid_t changed = waitpid(-1, &status, 0);
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
// some child exited
|
||||||
|
for (size_t i = 0; i < MAX_BG_TASKS; ++i) {
|
||||||
|
if (camera->child_bg_pids[i] == changed) {
|
||||||
|
camera->child_bg_pids[i] = pid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no luck, repeat and check if something exited maybe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mp_camera_wait_bg_tasks(MPCamera *camera)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_BG_TASKS; ++i) {
|
||||||
|
if (camera->child_bg_pids[i] != 0) {
|
||||||
|
// ignore errors
|
||||||
|
waitpid(camera->child_bg_pids[i], NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
mp_camera_check_task_complete(MPCamera *camera, pid_t pid)
|
||||||
|
{
|
||||||
|
// this method is potentially unsafe because pid could already be reused at
|
||||||
|
// this point, but extremely unlikely so we won't implement this.
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (pid == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// ignore errors (-1), no exit == 0
|
||||||
|
int pidchange = waitpid(pid, &status, WNOHANG);
|
||||||
|
if (pidchange == -1) // error or exists and runs
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
for (size_t i = 0; i < MAX_BG_TASKS; ++i) {
|
||||||
|
if (camera->child_bg_pids[i] == pid) {
|
||||||
|
camera->child_bg_pids[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
mp_camera_is_subdev(MPCamera *camera)
|
mp_camera_is_subdev(MPCamera *camera)
|
||||||
{
|
{
|
||||||
|
@ -1178,6 +1263,38 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid_t
|
||||||
|
mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
|
||||||
|
{
|
||||||
|
struct v4l2_ext_control ctrl = {};
|
||||||
|
ctrl.id = id;
|
||||||
|
ctrl.value = v;
|
||||||
|
|
||||||
|
struct v4l2_ext_controls ctrls = {
|
||||||
|
.ctrl_class = 0,
|
||||||
|
.which = V4L2_CTRL_WHICH_CUR_VAL,
|
||||||
|
.count = 1,
|
||||||
|
.controls = &ctrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = control_fd(camera);
|
||||||
|
|
||||||
|
// fork only after all the memory has been read
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
return 0; // discard errors, nothing to do in parent process
|
||||||
|
} else if (pid != 0) {
|
||||||
|
// parent process adding pid to wait list (to clear zombie processes)
|
||||||
|
mp_camera_add_bg_task(camera, pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore errors
|
||||||
|
xioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
|
||||||
|
// exit without calling exit handlers
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v)
|
mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v)
|
||||||
{
|
{
|
||||||
|
@ -1221,3 +1338,10 @@ mp_camera_control_get_bool(MPCamera *camera, uint32_t id)
|
||||||
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
|
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid_t
|
||||||
|
mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v)
|
||||||
|
{
|
||||||
|
int32_t value = v;
|
||||||
|
return mp_camera_control_set_int32_bg(camera, id, value);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <linux/v4l2-subdev.h>
|
#include <linux/v4l2-subdev.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -56,6 +57,10 @@ typedef struct _MPCamera MPCamera;
|
||||||
MPCamera *mp_camera_new(int video_fd, int subdev_fd);
|
MPCamera *mp_camera_new(int video_fd, int subdev_fd);
|
||||||
void mp_camera_free(MPCamera *camera);
|
void mp_camera_free(MPCamera *camera);
|
||||||
|
|
||||||
|
void mp_camera_add_bg_task(MPCamera *camera, pid_t pid);
|
||||||
|
void mp_camera_wait_bg_tasks(MPCamera *camera);
|
||||||
|
bool mp_camera_check_task_complete(MPCamera *camera, pid_t pid);
|
||||||
|
|
||||||
bool mp_camera_is_subdev(MPCamera *camera);
|
bool mp_camera_is_subdev(MPCamera *camera);
|
||||||
int mp_camera_get_video_fd(MPCamera *camera);
|
int mp_camera_get_video_fd(MPCamera *camera);
|
||||||
int mp_camera_get_subdev_fd(MPCamera *camera);
|
int mp_camera_get_subdev_fd(MPCamera *camera);
|
||||||
|
@ -111,7 +116,11 @@ bool mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control);
|
||||||
bool mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v);
|
bool mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v);
|
||||||
bool mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v);
|
bool mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v);
|
||||||
int32_t mp_camera_control_get_int32(MPCamera *camera, uint32_t id);
|
int32_t mp_camera_control_get_int32(MPCamera *camera, uint32_t id);
|
||||||
|
// set the value in the background, discards result
|
||||||
|
pid_t mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v);
|
||||||
|
|
||||||
bool mp_camera_control_try_bool(MPCamera *camera, uint32_t id, bool *v);
|
bool mp_camera_control_try_bool(MPCamera *camera, uint32_t id, bool *v);
|
||||||
bool mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v);
|
bool mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v);
|
||||||
bool mp_camera_control_get_bool(MPCamera *camera, uint32_t id);
|
bool mp_camera_control_get_bool(MPCamera *camera, uint32_t id);
|
||||||
|
// set the value in the background, discards result
|
||||||
|
pid_t mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v);
|
||||||
|
|
|
@ -199,7 +199,7 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
|
||||||
if (mp_camera_query_control(info->camera, V4L2_CID_FOCUS_AUTO,
|
if (mp_camera_query_control(info->camera, V4L2_CID_FOCUS_AUTO,
|
||||||
NULL)) {
|
NULL)) {
|
||||||
info->has_auto_focus_continuous = true;
|
info->has_auto_focus_continuous = true;
|
||||||
mp_camera_control_set_bool(info->camera, V4L2_CID_FOCUS_AUTO,
|
mp_camera_control_set_bool_bg(info->camera, V4L2_CID_FOCUS_AUTO,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
if (mp_camera_query_control(info->camera, V4L2_CID_AUTO_FOCUS_START,
|
if (mp_camera_query_control(info->camera, V4L2_CID_AUTO_FOCUS_START,
|
||||||
|
@ -236,6 +236,18 @@ setup(MPPipeline *pipeline, const void *data)
|
||||||
mp_device_list_free(device_list);
|
mp_device_list_free(device_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_cameras()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
|
||||||
|
struct camera_info* info = &cameras[i];
|
||||||
|
if (info->camera) {
|
||||||
|
mp_camera_free(info->camera);
|
||||||
|
info->camera = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mp_io_pipeline_start()
|
mp_io_pipeline_start()
|
||||||
{
|
{
|
||||||
|
@ -253,6 +265,8 @@ mp_io_pipeline_stop()
|
||||||
g_source_destroy(capture_source);
|
g_source_destroy(capture_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clean_cameras();
|
||||||
|
|
||||||
mp_pipeline_free(pipeline);
|
mp_pipeline_free(pipeline);
|
||||||
|
|
||||||
mp_process_pipeline_stop();
|
mp_process_pipeline_stop();
|
||||||
|
@ -350,6 +364,25 @@ void mp_io_pipeline_release_buffer(uint32_t buffer_index)
|
||||||
mp_pipeline_invoke(pipeline, (MPPipelineCallback) release_buffer, &buffer_index, sizeof(uint32_t));
|
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(struct camera_info *info)
|
||||||
|
{
|
||||||
|
// only run 1 manual focus at once
|
||||||
|
if (!mp_camera_check_task_complete(info->camera, start_focus_task)
|
||||||
|
|| !mp_camera_check_task_complete(info->camera, focus_continuous_task))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (info->has_auto_focus_continuous) {
|
||||||
|
focus_continuous_task = mp_camera_control_set_bool_bg(info->camera,
|
||||||
|
V4L2_CID_FOCUS_AUTO, 1);
|
||||||
|
} else if (info->has_auto_focus_start) {
|
||||||
|
start_focus_task = mp_camera_control_set_bool_bg(info->camera,
|
||||||
|
V4L2_CID_AUTO_FOCUS_START, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_controls()
|
update_controls()
|
||||||
{
|
{
|
||||||
|
@ -361,31 +394,24 @@ update_controls()
|
||||||
struct camera_info *info = &cameras[camera->index];
|
struct camera_info *info = &cameras[camera->index];
|
||||||
|
|
||||||
if (want_focus) {
|
if (want_focus) {
|
||||||
if (info->has_auto_focus_continuous) {
|
start_focus(info);
|
||||||
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;
|
want_focus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
|
if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
|
||||||
mp_camera_control_set_bool(info->camera, V4L2_CID_AUTOGAIN,
|
mp_camera_control_set_bool_bg(info->camera, V4L2_CID_AUTOGAIN,
|
||||||
!desired_controls.gain_is_manual);
|
!desired_controls.gain_is_manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desired_controls.gain_is_manual &&
|
if (desired_controls.gain_is_manual &&
|
||||||
current_controls.gain != desired_controls.gain) {
|
current_controls.gain != desired_controls.gain) {
|
||||||
mp_camera_control_set_int32(info->camera, info->gain_ctrl,
|
mp_camera_control_set_int32_bg(info->camera, info->gain_ctrl,
|
||||||
desired_controls.gain);
|
desired_controls.gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_controls.exposure_is_manual !=
|
if (current_controls.exposure_is_manual !=
|
||||||
desired_controls.exposure_is_manual) {
|
desired_controls.exposure_is_manual) {
|
||||||
mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE_AUTO,
|
mp_camera_control_set_int32_bg(info->camera, V4L2_CID_EXPOSURE_AUTO,
|
||||||
desired_controls.exposure_is_manual ?
|
desired_controls.exposure_is_manual ?
|
||||||
V4L2_EXPOSURE_MANUAL :
|
V4L2_EXPOSURE_MANUAL :
|
||||||
V4L2_EXPOSURE_AUTO);
|
V4L2_EXPOSURE_AUTO);
|
||||||
|
@ -393,7 +419,7 @@ update_controls()
|
||||||
|
|
||||||
if (desired_controls.exposure_is_manual &&
|
if (desired_controls.exposure_is_manual &&
|
||||||
current_controls.exposure != desired_controls.exposure) {
|
current_controls.exposure != desired_controls.exposure) {
|
||||||
mp_camera_control_set_int32(info->camera, V4L2_CID_EXPOSURE,
|
mp_camera_control_set_int32_bg(info->camera, V4L2_CID_EXPOSURE,
|
||||||
desired_controls.exposure);
|
desired_controls.exposure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,13 +470,13 @@ on_frame(MPBuffer buffer, void * _data)
|
||||||
|
|
||||||
// Restore the auto exposure and gain if needed
|
// Restore the auto exposure and gain if needed
|
||||||
if (!current_controls.exposure_is_manual) {
|
if (!current_controls.exposure_is_manual) {
|
||||||
mp_camera_control_set_int32(info->camera,
|
mp_camera_control_set_int32_bg(info->camera,
|
||||||
V4L2_CID_EXPOSURE_AUTO,
|
V4L2_CID_EXPOSURE_AUTO,
|
||||||
V4L2_EXPOSURE_AUTO);
|
V4L2_EXPOSURE_AUTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current_controls.gain_is_manual) {
|
if (!current_controls.gain_is_manual) {
|
||||||
mp_camera_control_set_bool(info->camera,
|
mp_camera_control_set_bool_bg(info->camera,
|
||||||
V4L2_CID_AUTOGAIN, true);
|
V4L2_CID_AUTOGAIN, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue