Automatically rotate image based on accelerometer values
Device orientation is provided by net.hadess.SensorProxy iio-proxy-service. If there is no accelerometer on target platform, then window orientation from X11/wayland is used as before.
This commit is contained in:
@@ -27,6 +27,9 @@ finish-args:
|
|||||||
# camera shutter feedback
|
# camera shutter feedback
|
||||||
- --talk-name=org.sigxcpu.Feedback
|
- --talk-name=org.sigxcpu.Feedback
|
||||||
|
|
||||||
|
# accelerometer sensor for orientation
|
||||||
|
- --system-talk-name=net.hadess.SensorProxy
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
- name: megapixels
|
- name: megapixels
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
|
@@ -54,6 +54,7 @@ executable('megapixels',
|
|||||||
'src/matrix.c',
|
'src/matrix.c',
|
||||||
'src/pipeline.c',
|
'src/pipeline.c',
|
||||||
'src/process_pipeline.c',
|
'src/process_pipeline.c',
|
||||||
|
'src/rotation.c',
|
||||||
'src/zbar_pipeline.c',
|
'src/zbar_pipeline.c',
|
||||||
'src/dcp.c',
|
'src/dcp.c',
|
||||||
resources,
|
resources,
|
||||||
@@ -126,6 +127,8 @@ if clang_format.found()
|
|||||||
'src/pipeline.h',
|
'src/pipeline.h',
|
||||||
'src/process_pipeline.c',
|
'src/process_pipeline.c',
|
||||||
'src/process_pipeline.h',
|
'src/process_pipeline.h',
|
||||||
|
'src/rotation.c',
|
||||||
|
'src/rotation.h',
|
||||||
'src/zbar_pipeline.c',
|
'src/zbar_pipeline.c',
|
||||||
'src/zbar_pipeline.h',
|
'src/zbar_pipeline.h',
|
||||||
'tools/camera_test.c',
|
'tools/camera_test.c',
|
||||||
|
@@ -96,6 +96,8 @@ update_process_pipeline()
|
|||||||
.preview_width = state_io.preview_width,
|
.preview_width = state_io.preview_width,
|
||||||
.preview_height = state_io.preview_height,
|
.preview_height = state_io.preview_height,
|
||||||
.device_rotation = state_io.device_rotation,
|
.device_rotation = state_io.device_rotation,
|
||||||
|
.device_accel_rotation = state_io.device_accel_rotation,
|
||||||
|
.device_accel_rotation_good = state_io.device_accel_rotation_good,
|
||||||
|
|
||||||
.gain.control = state_io.gain.control,
|
.gain.control = state_io.gain.control,
|
||||||
.gain.auto_control = state_io.gain.auto_control,
|
.gain.auto_control = state_io.gain.auto_control,
|
||||||
@@ -669,6 +671,8 @@ update_state(MPPipeline *pipeline, const mp_state_io *new_state)
|
|||||||
state_io.preview_width = new_state->preview_width;
|
state_io.preview_width = new_state->preview_width;
|
||||||
state_io.preview_height = new_state->preview_height;
|
state_io.preview_height = new_state->preview_height;
|
||||||
state_io.device_rotation = new_state->device_rotation;
|
state_io.device_rotation = new_state->device_rotation;
|
||||||
|
state_io.device_accel_rotation = new_state->device_accel_rotation;
|
||||||
|
state_io.device_accel_rotation_good = new_state->device_accel_rotation_good;
|
||||||
|
|
||||||
if (state_io.camera) {
|
if (state_io.camera) {
|
||||||
state_io.gain.value = new_state->gain.value;
|
state_io.gain.value = new_state->gain.value;
|
||||||
|
16
src/main.c
16
src/main.c
@@ -4,6 +4,7 @@
|
|||||||
#include "gl_util.h"
|
#include "gl_util.h"
|
||||||
#include "io_pipeline.h"
|
#include "io_pipeline.h"
|
||||||
#include "process_pipeline.h"
|
#include "process_pipeline.h"
|
||||||
|
#include "rotation.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include <asm/errno.h>
|
#include <asm/errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -120,6 +121,8 @@ update_io_pipeline()
|
|||||||
.preview_width = state.preview_width,
|
.preview_width = state.preview_width,
|
||||||
.preview_height = state.preview_height,
|
.preview_height = state.preview_height,
|
||||||
.device_rotation = state.device_rotation,
|
.device_rotation = state.device_rotation,
|
||||||
|
.device_accel_rotation = state.device_accel_rotation,
|
||||||
|
.device_accel_rotation_good = state.device_accel_rotation_good,
|
||||||
|
|
||||||
.gain.control = state.gain.control,
|
.gain.control = state.gain.control,
|
||||||
.gain.auto_control = state.gain.auto_control,
|
.gain.auto_control = state.gain.auto_control,
|
||||||
@@ -1291,6 +1294,17 @@ xevent_handler(GdkDisplay *display, XEvent *xevent, gpointer data)
|
|||||||
}
|
}
|
||||||
#endif // GDK_WINDOWING_X11
|
#endif // GDK_WINDOWING_X11
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_sensor_handler(int rotation, bool good)
|
||||||
|
{
|
||||||
|
if (rotation != state.device_accel_rotation ||
|
||||||
|
good != state.device_accel_rotation_good) {
|
||||||
|
state.device_accel_rotation = rotation;
|
||||||
|
state.device_accel_rotation_good = good;
|
||||||
|
update_io_pipeline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
activate(GtkApplication *app, gpointer data)
|
activate(GtkApplication *app, gpointer data)
|
||||||
{
|
{
|
||||||
@@ -1472,6 +1486,7 @@ activate(GtkApplication *app, gpointer data)
|
|||||||
GDBusConnection *conn =
|
GDBusConnection *conn =
|
||||||
g_application_get_dbus_connection(G_APPLICATION(app));
|
g_application_get_dbus_connection(G_APPLICATION(app));
|
||||||
mp_flash_gtk_init(conn);
|
mp_flash_gtk_init(conn);
|
||||||
|
mp_device_rotation_init(rotation_sensor_handler);
|
||||||
|
|
||||||
if (state.configuration->count > 0) {
|
if (state.configuration->count > 0) {
|
||||||
mp_io_pipeline_start();
|
mp_io_pipeline_start();
|
||||||
@@ -1501,6 +1516,7 @@ shutdown(GApplication *app, gpointer data)
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
mp_io_pipeline_stop();
|
mp_io_pipeline_stop();
|
||||||
mp_flash_gtk_clean();
|
mp_flash_gtk_clean();
|
||||||
|
mp_device_rotation_clean();
|
||||||
|
|
||||||
g_clear_object(&fb_settings);
|
g_clear_object(&fb_settings);
|
||||||
g_clear_object(&capture_event);
|
g_clear_object(&capture_event);
|
||||||
|
@@ -1564,7 +1564,9 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
|
|||||||
new_state->camera->current_mode) ||
|
new_state->camera->current_mode) ||
|
||||||
state_proc.preview_width != new_state->preview_width ||
|
state_proc.preview_width != new_state->preview_width ||
|
||||||
state_proc.preview_height != new_state->preview_height ||
|
state_proc.preview_height != new_state->preview_height ||
|
||||||
state_proc.device_rotation != new_state->device_rotation;
|
state_proc.device_rotation != new_state->device_rotation ||
|
||||||
|
state_proc.device_accel_rotation != new_state->device_accel_rotation ||
|
||||||
|
state_proc.device_accel_rotation_good != new_state->device_accel_rotation_good;
|
||||||
|
|
||||||
bool format_changed = state_proc.mode == NULL;
|
bool format_changed = state_proc.mode == NULL;
|
||||||
|
|
||||||
@@ -1579,6 +1581,8 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
|
|||||||
state_proc.preview_height = new_state->preview_height;
|
state_proc.preview_height = new_state->preview_height;
|
||||||
|
|
||||||
state_proc.device_rotation = new_state->device_rotation;
|
state_proc.device_rotation = new_state->device_rotation;
|
||||||
|
state_proc.device_accel_rotation = new_state->device_accel_rotation;
|
||||||
|
state_proc.device_accel_rotation_good = new_state->device_accel_rotation_good;
|
||||||
state_proc.burst_length = new_state->burst_length;
|
state_proc.burst_length = new_state->burst_length;
|
||||||
|
|
||||||
state_proc.balance[0] = new_state->balance[0];
|
state_proc.balance[0] = new_state->balance[0];
|
||||||
@@ -1602,9 +1606,12 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (output_changed || camera_changed) {
|
if (output_changed || camera_changed) {
|
||||||
|
int rotation;
|
||||||
|
rotation = state_proc.device_accel_rotation_good ?
|
||||||
|
state_proc.device_accel_rotation :
|
||||||
|
state_proc.device_rotation;
|
||||||
state_proc.camera_rotation = mod(
|
state_proc.camera_rotation = mod(
|
||||||
state_proc.mode->rotation - state_proc.device_rotation, 360);
|
state_proc.mode->rotation - rotation, 360);
|
||||||
|
|
||||||
on_output_changed(format_changed);
|
on_output_changed(format_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
182
src/rotation.c
Normal file
182
src/rotation.c
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#include "gtk/gtk.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "rotation.h"
|
||||||
|
|
||||||
|
struct sensor_cb_data {
|
||||||
|
sensor_cb rotation_update;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GDBusProxy *iio_proxy;
|
||||||
|
static bool rotation_valid = false;
|
||||||
|
static int rotation;
|
||||||
|
struct sensor_cb_data cb_data;
|
||||||
|
|
||||||
|
static int
|
||||||
|
rotation_string_to_degree(const gchar* val)
|
||||||
|
{
|
||||||
|
int rot = -1;
|
||||||
|
if (!g_ascii_strcasecmp(val, "normal"))
|
||||||
|
rot = 0;
|
||||||
|
if (!g_ascii_strcasecmp(val, "right-up"))
|
||||||
|
rot = 270;
|
||||||
|
if (!g_ascii_strcasecmp(val, "left-up"))
|
||||||
|
rot = 90;
|
||||||
|
if (!g_ascii_strcasecmp(val, "bottom-up"))
|
||||||
|
rot = 180;
|
||||||
|
return rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_properties_changed (GDBusProxy *proxy,
|
||||||
|
GVariant *changed_properties,
|
||||||
|
GStrv invalidated_properties,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GVariant *v;
|
||||||
|
GVariantDict dict;
|
||||||
|
const gchar *val;
|
||||||
|
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
|
||||||
|
|
||||||
|
g_variant_dict_init (&dict, changed_properties);
|
||||||
|
|
||||||
|
if (g_variant_dict_contains (&dict, "HasAccelerometer")) {
|
||||||
|
v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
|
||||||
|
rotation_valid = g_variant_get_boolean (v);
|
||||||
|
g_debug ("accelerometer changed: %d\n", rotation_valid);
|
||||||
|
g_variant_unref (v);
|
||||||
|
}
|
||||||
|
if (g_variant_dict_contains (&dict, "AccelerometerOrientation")) {
|
||||||
|
v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
|
||||||
|
val = g_variant_get_string (v, NULL);
|
||||||
|
rotation = rotation_string_to_degree(val);
|
||||||
|
g_variant_unref (v);
|
||||||
|
}
|
||||||
|
g_variant_dict_clear (&dict);
|
||||||
|
|
||||||
|
g_debug ("sensor rotation changed: %d %d\n",
|
||||||
|
rotation, rotation_valid);
|
||||||
|
cb_data->rotation_update(rotation, rotation_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_get_initial_values (struct sensor_cb_data *cb_data)
|
||||||
|
{
|
||||||
|
GVariant *v;
|
||||||
|
const gchar *val;
|
||||||
|
|
||||||
|
v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
|
||||||
|
rotation_valid = g_variant_get_boolean (v);
|
||||||
|
if (rotation_valid) {
|
||||||
|
g_variant_unref (v);
|
||||||
|
v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
|
||||||
|
val = g_variant_get_string (v, NULL);
|
||||||
|
rotation = rotation_string_to_degree(val);
|
||||||
|
g_debug ("rotation: %d\n", rotation);
|
||||||
|
} else
|
||||||
|
g_debug ("no accelerometer\n");
|
||||||
|
g_variant_unref (v);
|
||||||
|
cb_data->rotation_update(rotation, rotation_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_accelerometer_claimed(GDBusProxy *proxy,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
|
||||||
|
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GVariant) result = g_dbus_proxy_call_finish(proxy, res, &error);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
g_warning ("Failed to claim accelerometer: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rotation_get_initial_values (cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_iio_proxy_init (GObject *src,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
|
||||||
|
g_autoptr(GError) err = NULL;
|
||||||
|
|
||||||
|
iio_proxy = g_dbus_proxy_new_finish(res, &err);
|
||||||
|
if (!iio_proxy || err) {
|
||||||
|
g_warning ("Failed to connect to sensor proxy service %s\n",
|
||||||
|
err->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_connect (G_OBJECT (iio_proxy), "g-properties-changed",
|
||||||
|
G_CALLBACK (rotation_properties_changed),
|
||||||
|
cb_data);
|
||||||
|
|
||||||
|
g_dbus_proxy_call (iio_proxy,
|
||||||
|
"ClaimAccelerometer",
|
||||||
|
NULL,
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
NULL,
|
||||||
|
(GAsyncReadyCallback)rotation_accelerometer_claimed,
|
||||||
|
cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_iio_proxy_appeared_cb (GDBusConnection *connection,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *name_owner,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_debug ("iio-sensor-proxy appeared\n");
|
||||||
|
g_dbus_proxy_new (connection,
|
||||||
|
G_DBUS_PROXY_FLAGS_NONE,
|
||||||
|
NULL,
|
||||||
|
"net.hadess.SensorProxy",
|
||||||
|
"/net/hadess/SensorProxy",
|
||||||
|
"net.hadess.SensorProxy",
|
||||||
|
NULL,
|
||||||
|
(GAsyncReadyCallback)rotation_iio_proxy_init,
|
||||||
|
user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotation_iio_proxy_vanished_cb (GDBusConnection *connection,
|
||||||
|
const gchar *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
|
||||||
|
|
||||||
|
if (iio_proxy) {
|
||||||
|
g_clear_object (&iio_proxy);
|
||||||
|
g_debug ("iio-sensor-proxy vanished\n");
|
||||||
|
|
||||||
|
rotation_valid = false;
|
||||||
|
rotation = 0;
|
||||||
|
cb_data->rotation_update(rotation, rotation_valid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mp_device_rotation_init(sensor_cb cb)
|
||||||
|
{
|
||||||
|
g_assert(cb != NULL);
|
||||||
|
cb_data.rotation_update = cb;
|
||||||
|
g_bus_watch_name (G_BUS_TYPE_SYSTEM,
|
||||||
|
"net.hadess.SensorProxy",
|
||||||
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||||
|
rotation_iio_proxy_appeared_cb,
|
||||||
|
rotation_iio_proxy_vanished_cb,
|
||||||
|
&cb_data,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mp_device_rotation_clean(void)
|
||||||
|
{
|
||||||
|
g_object_unref(iio_proxy);
|
||||||
|
}
|
9
src/rotation.h
Normal file
9
src/rotation.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef _MP_ROTATION_H
|
||||||
|
#define _MP_ROTATION_H
|
||||||
|
|
||||||
|
typedef void(*sensor_cb)(int, bool);
|
||||||
|
|
||||||
|
void mp_device_rotation_init(sensor_cb cb);
|
||||||
|
void mp_device_rotation_clean(void);
|
||||||
|
|
||||||
|
#endif
|
@@ -29,6 +29,8 @@ typedef struct state_main {
|
|||||||
int preview_buffer_height;
|
int preview_buffer_height;
|
||||||
|
|
||||||
int device_rotation;
|
int device_rotation;
|
||||||
|
int device_accel_rotation;
|
||||||
|
bool device_accel_rotation_good;
|
||||||
|
|
||||||
int burst_length;
|
int burst_length;
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@ typedef struct state_io {
|
|||||||
int preview_width;
|
int preview_width;
|
||||||
int preview_height;
|
int preview_height;
|
||||||
int device_rotation;
|
int device_rotation;
|
||||||
|
int device_accel_rotation;
|
||||||
|
bool device_accel_rotation_good;
|
||||||
} mp_state_io;
|
} mp_state_io;
|
||||||
|
|
||||||
typedef enum aaa_mode {
|
typedef enum aaa_mode {
|
||||||
@@ -93,6 +97,8 @@ typedef struct state_proc {
|
|||||||
|
|
||||||
// Device orientation in degrees
|
// Device orientation in degrees
|
||||||
int device_rotation;
|
int device_rotation;
|
||||||
|
int device_accel_rotation;
|
||||||
|
bool device_accel_rotation_good;
|
||||||
// Camera orientation as number
|
// Camera orientation as number
|
||||||
int camera_rotation;
|
int camera_rotation;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user