Compare commits

...

7 Commits

Author SHA1 Message Date
Martijn Braam c584583d29
Update version number to 1.2.0 2021-07-30 18:14:42 +02:00
Martijn Braam 68bba0a8a5
Fix glib schemas 2021-07-30 18:14:28 +02:00
Guido Günther 561c10c612
data: Add X-Purism-FormFactor to desktop file
This allows shells like phosh to filter on form factor so
the app list isn't cluttered with applications that don't
fit the screen when not docked.

Reference: https://source.puri.sm/Librem5/phosh/-/merge_requests/639
2021-07-30 17:50:59 +02:00
WebFreak001 974fafc1d1
focus now only triggerable once in parallel 2021-07-30 17:48:20 +02:00
WebFreak001 7314739dfc
free cameras on process exit 2021-07-30 17:48:19 +02:00
WebFreak001 ccbaaad72b
keep track of bg tasks for clean up 2021-07-30 17:48:18 +02:00
WebFreak001 1282a75db9
run most camera control setting in background
makes trigger focus, continuous focus, autogain, gain ctrl, auto
exposure, exposure ctrl run in background to not block the UI thread.

The camera updates the image while this is in progress, so you can for
example see the camera live focus as on common other phones.
2021-07-30 17:48:17 +02:00
6 changed files with 177 additions and 18 deletions

View File

@ -15,7 +15,6 @@ install_data(['postprocess.sh'],
install_mode: 'rwxr-xr-x')
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)
gnome.compile_schemas(depend_files: files(settings_schemas))
meson.add_install_script('glib-compile-schemas', schemas_dir)

View File

@ -5,3 +5,4 @@ Terminal=false
Type=Application
Categories=GTK;
Icon=org.postmarketos.Megapixels
X-Purism-FormFactor=Workstation;Mobile;

View File

@ -1,4 +1,4 @@
project('megapixels', 'c', version: '1.1.0')
project('megapixels', 'c', version: '1.2.0')
gnome = import('gnome')
gtkdep = dependency('gtk4')

View File

@ -6,9 +6,11 @@
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_VIDEO_BUFFERS 20
#define MAX_BG_TASKS 8
static const char *pixel_format_names[MP_PIXEL_FMT_MAX] = {
"unsupported", "BGGR8", "GBRG8", "GRBG8", "RGGB8", "BGGR10P",
@ -219,6 +221,9 @@ struct _MPCamera {
struct video_buffer buffers[MAX_VIDEO_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;
};
@ -250,12 +255,15 @@ mp_camera_new(int video_fd, int subdev_fd)
camera->has_set_mode = false;
camera->num_buffers = 0;
camera->use_mplane = use_mplane;
memset(camera->child_bg_pids, 0, sizeof(camera->child_bg_pids[0]) * MAX_BG_TASKS);
return camera;
}
void
mp_camera_free(MPCamera *camera)
{
mp_camera_wait_bg_tasks(camera);
g_warn_if_fail(camera->num_buffers == 0);
if (camera->num_buffers != 0) {
mp_camera_stop_capture(camera);
@ -264,6 +272,83 @@ mp_camera_free(MPCamera *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
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;
}
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
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);
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);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <linux/v4l2-subdev.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <stdint.h>
@ -56,6 +57,10 @@ typedef struct _MPCamera MPCamera;
MPCamera *mp_camera_new(int video_fd, int subdev_fd);
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);
int mp_camera_get_video_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_set_int32(MPCamera *camera, uint32_t id, int32_t v);
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_set_bool(MPCamera *camera, uint32_t id, bool v);
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);

View File

@ -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,
NULL)) {
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);
}
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);
}
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
mp_io_pipeline_start()
{
@ -253,6 +265,8 @@ mp_io_pipeline_stop()
g_source_destroy(capture_source);
}
clean_cameras();
mp_pipeline_free(pipeline);
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));
}
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
update_controls()
{
@ -361,31 +394,24 @@ update_controls()
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);
}
start_focus(info);
want_focus = false;
}
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);
}
if (desired_controls.gain_is_manual &&
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);
}
if (current_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 ?
V4L2_EXPOSURE_MANUAL :
V4L2_EXPOSURE_AUTO);
@ -393,7 +419,7 @@ update_controls()
if (desired_controls.exposure_is_manual &&
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);
}
@ -444,13 +470,13 @@ on_frame(MPBuffer buffer, void * _data)
// Restore the auto exposure and gain if needed
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_EXPOSURE_AUTO);
}
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);
}