From a04e6b0cb2863b960c54ad5decc6d3a9a3a2bd42 Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Tue, 12 Jul 2016 14:51:47 -0700 Subject: [PATCH] new plugin: Android hardware sensor source ahssrc is a new plugin that enables Gstreamer to read from the android.hardware.Sensor Android sensors. These sensors are treated as buffers and can be passed through and manipulated by the pipeline. https://bugzilla.gnome.org/show_bug.cgi?id=768110 --- sys/androidmedia/Makefile.am | 5 + sys/androidmedia/gst-android-hardware-sensor.c | 808 +++++++++++++++++++++ sys/androidmedia/gst-android-hardware-sensor.h | 156 ++++ sys/androidmedia/gstahssrc.c | 684 +++++++++++++++++ sys/androidmedia/gstahssrc.h | 80 ++ sys/androidmedia/gstamc.c | 14 +- sys/androidmedia/gstsensors.h | 182 +++++ .../gstreamer/androidmedia/GstAhsCallback.java | 54 ++ 8 files changed, 1982 insertions(+), 1 deletion(-) create mode 100644 sys/androidmedia/gst-android-hardware-sensor.c create mode 100644 sys/androidmedia/gst-android-hardware-sensor.h create mode 100644 sys/androidmedia/gstahssrc.c create mode 100644 sys/androidmedia/gstahssrc.h create mode 100644 sys/androidmedia/gstsensors.h create mode 100644 sys/androidmedia/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java diff --git a/sys/androidmedia/Makefile.am b/sys/androidmedia/Makefile.am index 3bb4ea3..c56eee2 100644 --- a/sys/androidmedia/Makefile.am +++ b/sys/androidmedia/Makefile.am @@ -2,6 +2,7 @@ plugin_LTLIBRARIES = libgstandroidmedia.la libgstandroidmedia_la_SOURCES = \ gstahcsrc.c \ + gstahssrc.c \ gstamcaudiodec.c \ gstamc.c \ gstamcsurface.c \ @@ -10,10 +11,12 @@ libgstandroidmedia_la_SOURCES = \ gstamcvideoenc.c \ gst-android-graphics-imageformat.c \ gst-android-hardware-camera.c \ + gst-android-hardware-sensor.c \ gstjniutils.c noinst_HEADERS = \ gstahcsrc.h \ + gstahssrc.h \ gstamcaudiodec.h \ gstamc-constants.h \ gstamc.h \ @@ -23,6 +26,7 @@ noinst_HEADERS = \ gstamcvideoenc.h \ gst-android-graphics-imageformat.h \ gst-android-hardware-camera.h \ + gst-android-hardware-sensor.h \ gstjniutils.h libgstandroidmedia_la_CFLAGS = \ @@ -50,4 +54,5 @@ libgstandroidmedia_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) androidmedia_java_classesdir = $(datadir)/gst-android/ndk-build/androidmedia/ androidmedia_java_classes_DATA = \ org/freedesktop/gstreamer/androidmedia/GstAhcCallback.java \ + org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java \ org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener.java diff --git a/sys/androidmedia/gst-android-hardware-sensor.c b/sys/androidmedia/gst-android-hardware-sensor.c new file mode 100644 index 0000000..f50dd83 --- /dev/null +++ b/sys/androidmedia/gst-android-hardware-sensor.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * The UNION_CAST macro is copyright: + * Copyright (C) 2008-2016 Matt Gallagher ( http://cocoawithlove.com ). + * All rights reserved. + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted, provided that the above copyright notice + * and this permission notice appear in all copies. + + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstjniutils.h" +#include "gst-android-hardware-sensor.h" + +static jobject (*gst_android_get_application_context) (void) = NULL; + +GST_DEBUG_CATEGORY_STATIC (ahs_debug); +#define GST_CAT_DEFAULT ahs_debug + +/* + * See: + * http://www.cocoawithlove.com/2008/04/using-pointers-to-recast-in-c-is-bad.html + * for details. + */ +#define UNION_CAST(x, destType) \ + (((union {__typeof__(x) a; destType b;})x).b) + +static struct +{ + jclass klass; + jstring SENSOR_SERVICE; + jmethodID getSystemService; +} android_content_context = { +0}; + +static struct +{ + jclass klass; + jfieldID accuracy; + jfieldID values; +} android_hardware_sensor_event = { +0}; + +static struct +{ + jclass klass; + jmethodID getDefaultSensor;; + jmethodID registerListener; + jmethodID unregisterListener; +} android_hardware_sensor_manager = { +0}; + +static struct +{ + jclass klass; + jmethodID constructor; +} org_freedesktop_gstreamer_androidmedia_gstahscallback = { +0}; + +GHashTable *sensor_sizes = NULL; +static void +gst_ah_sensor_sensor_sizes_init (void) +{ + gint i; + static struct + { + gint type; + gsize size; + } types[] = { + {AHS_SENSOR_TYPE_ACCELEROMETER, sizeof (GstAHSAccelerometerValues)}, + {AHS_SENSOR_TYPE_AMBIENT_TEMPERATURE, sizeof (GstAHSAmbientTemperatureValues)}, + {AHS_SENSOR_TYPE_GAME_ROTATION_VECTOR, sizeof (GstAHSGameRotationVectorValues)}, + {AHS_SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR, sizeof (GstAHSGeomagneticRotationVectorValues)}, + {AHS_SENSOR_TYPE_GRAVITY, sizeof (GstAHSGravityValues)}, + {AHS_SENSOR_TYPE_GYROSCOPE, sizeof (GstAHSGyroscopeValues)}, + {AHS_SENSOR_TYPE_GYROSCOPE_UNCALIBRATED, sizeof (GstAHSGyroscopeUncalibratedValues)}, + {AHS_SENSOR_TYPE_HEART_RATE, sizeof (GstAHSHeartRateValues)}, + {AHS_SENSOR_TYPE_LIGHT, sizeof (GstAHSLightValues)}, + {AHS_SENSOR_TYPE_LINEAR_ACCELERATION, sizeof (GstAHSLinearAccelerationValues)}, + {AHS_SENSOR_TYPE_MAGNETIC_FIELD, sizeof (GstAHSMagneticFieldValues)}, + {AHS_SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, sizeof (GstAHSMagneticFieldUncalibratedValues)}, + {AHS_SENSOR_TYPE_ORIENTATION, sizeof (GstAHSOrientationValues)}, + {AHS_SENSOR_TYPE_PRESSURE, sizeof (GstAHSPressureValues)}, + {AHS_SENSOR_TYPE_PROXIMITY, sizeof (GstAHSProximityValues)}, + {AHS_SENSOR_TYPE_RELATIVE_HUMIDITY, sizeof (GstAHSRelativeHumidityValues)}, + {AHS_SENSOR_TYPE_ROTATION_VECTOR, sizeof (GstAHSRotationVectorValues)}, + {AHS_SENSOR_TYPE_STEP_COUNTER, sizeof (GstAHSStepCounterValues)}, + {AHS_SENSOR_TYPE_STEP_DETECTOR, sizeof (GstAHSStepDetectorValues)}, + }; + + g_assert_null (sensor_sizes); + + sensor_sizes = g_hash_table_new (g_int_hash, g_int_equal); + for (i = 0; i < G_N_ELEMENTS (types); i++) + g_hash_table_insert (sensor_sizes, &types[i].type, &types[i].size); +} + +static void +gst_ah_sensor_sensor_sizes_deinit (void) +{ + g_assert_nonnull (sensor_sizes); + + g_hash_table_unref (sensor_sizes); + sensor_sizes = NULL; +} + +gsize +gst_ah_sensor_get_sensor_data_size (gint sensor_type) +{ + return *((gsize *) g_hash_table_lookup (sensor_sizes, &sensor_type)); +} + +static void +gst_ah_sensor_on_sensor_changed (JNIEnv * env, jclass klass, + jobject sensor_event, jlong callback, jlong user_data) +{ + GstAHSensorCallback cb = (GstAHSensorCallback) (gsize) callback; + + if (cb) + cb (sensor_event, (gpointer) (gsize) user_data); +} + +static void +gst_ah_sensor_on_accuracy_changed (JNIEnv * env, jclass klass, + jobject sensor, jint accuracy, jlong callback, jlong user_data) +{ + GstAHSAccuracyCallback cb = (GstAHSAccuracyCallback) (gsize) callback; + + if (cb) + cb (sensor, accuracy, (gpointer) (gsize) user_data); +} + +static gboolean natives_registered = FALSE; + +static JNINativeMethod native_methods[] = { + {(gchar *) "gst_ah_sensor_on_sensor_changed", + (gchar *) "(Landroid/hardware/SensorEvent;JJ)V", + (void *) gst_ah_sensor_on_sensor_changed}, + {(gchar *) "gst_ah_sensor_on_accuracy_changed", + (gchar *) "(Landroid/hardware/Sensor;IJJ)V", + (void *) gst_ah_sensor_on_accuracy_changed} +}; + +static gboolean +_init_classes (void) +{ + gint32 delay; + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + jclass klass; + jfieldID fieldID; + GModule *module; + gboolean success; + gint32 type; + + /* + * Lookup the Android function to get an Android context. This function will + * be provided when the plugin is built via ndk-build. + */ + module = g_module_open (NULL, G_MODULE_BIND_LOCAL); + if (!module) + goto failed; + success = g_module_symbol (module, "gst_android_get_application_context", + (gpointer *) & gst_android_get_application_context); + if (!success || !gst_android_get_application_context) + goto failed; + g_module_close (module); + + /* android.content.Context */ + klass = android_content_context.klass = gst_amc_jni_get_class (env, &err, + "android/content/Context"); + if (!klass) + goto failed; + android_content_context.getSystemService = + gst_amc_jni_get_method_id (env, &err, klass, "getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;"); + if (!android_content_context.getSystemService) + goto failed; + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "SENSOR_SERVICE", + "Ljava/lang/String;"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_object_field (env, &err, klass, fieldID, + &android_content_context.SENSOR_SERVICE)) + goto failed; + android_content_context.SENSOR_SERVICE = + gst_amc_jni_object_make_global (env, + android_content_context.SENSOR_SERVICE); + if (!android_content_context.SENSOR_SERVICE) + goto failed; + + /* android.hardware.SensorEvent */ + klass = android_hardware_sensor_event.klass = + gst_amc_jni_get_class (env, &err, "android/hardware/SensorEvent"); + if (!klass) + goto failed; + android_hardware_sensor_event.accuracy = + gst_amc_jni_get_field_id (env, &err, klass, "accuracy", "I"); + if (!android_hardware_sensor_event.accuracy) + goto failed; + android_hardware_sensor_event.values = + gst_amc_jni_get_field_id (env, &err, klass, "values", "[F"); + if (!android_hardware_sensor_event.values) + goto failed; + + /* android.hardware.SensorManager */ + klass = android_hardware_sensor_manager.klass = + gst_amc_jni_get_class (env, &err, "android/hardware/SensorManager"); + if (!klass) + goto failed; + android_hardware_sensor_manager.getDefaultSensor = + gst_amc_jni_get_method_id (env, &err, klass, + "getDefaultSensor", "(I)Landroid/hardware/Sensor;"); + if (!android_hardware_sensor_manager.getDefaultSensor) + goto failed; + android_hardware_sensor_manager.registerListener = + gst_amc_jni_get_method_id (env, &err, klass, + "registerListener", + "(Landroid/hardware/SensorEventListener;Landroid/hardware/Sensor;I)Z"); + if (!android_hardware_sensor_manager.registerListener) + goto failed; + android_hardware_sensor_manager.unregisterListener = + gst_amc_jni_get_method_id (env, &err, klass, + "unregisterListener", "(Landroid/hardware/SensorEventListener;)V"); + if (!android_hardware_sensor_manager.unregisterListener) + goto failed; + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "SENSOR_DELAY_FASTEST", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &delay)) + goto failed; + if (delay != AHS_SENSOR_DELAY_FASTEST) { + GST_ERROR ("SENSOR_DELAY_FASTEST has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "SENSOR_DELAY_GAME", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &delay)) + goto failed; + if (delay != AHS_SENSOR_DELAY_GAME) { + GST_ERROR ("SENSOR_DELAY_GAME has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "SENSOR_DELAY_NORMAL", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &delay)) + goto failed; + if (delay != AHS_SENSOR_DELAY_NORMAL) { + GST_ERROR ("SENSOR_DELAY_NORMAL has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "SENSOR_DELAY_UI", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &delay)) + goto failed; + if (delay != AHS_SENSOR_DELAY_UI) { + GST_ERROR ("SENSOR_DELAY_UI has changed value"); + goto failed; + } + + /* android.hardware.Sensor */ + klass = gst_amc_jni_get_class (env, &err, "android/hardware/Sensor"); + if (!klass) + goto failed; + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_ACCELEROMETER", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_ACCELEROMETER) { + GST_ERROR ("TYPE_ACCELEROMETER has changed value"); + goto failed; + } + + fieldID = gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_AMBIENT_TEMPERATURE", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_AMBIENT_TEMPERATURE) { + GST_ERROR ("TYPE_AMBIENT_TEMPERATURE has changed value"); + goto failed; + } + + fieldID = gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_GAME_ROTATION_VECTOR", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_GAME_ROTATION_VECTOR) { + GST_ERROR ("TYPE_GAME_ROTATION_VECTOR has changed value"); + goto failed; + } + + fieldID = gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_GEOMAGNETIC_ROTATION_VECTOR", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR) { + GST_ERROR ("TYPE_GEOMAGNETIC_ROTATION_VECTOR has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_GRAVITY", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_GRAVITY) { + GST_ERROR ("TYPE_GRAVITY has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_GYROSCOPE", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_GYROSCOPE) { + GST_ERROR ("TYPE_GYROSCOPE has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_GYROSCOPE_UNCALIBRATED", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) { + GST_ERROR ("TYPE_GYROSCOPE_UNCALIBRATED has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_HEART_RATE", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_HEART_RATE) { + GST_ERROR ("TYPE_HEART_RATE has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_LIGHT", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_LIGHT) { + GST_ERROR ("TYPE_LIGHT has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_LINEAR_ACCELERATION", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_LINEAR_ACCELERATION) { + GST_ERROR ("TYPE_LINEAR_ACCELERATION has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_MAGNETIC_FIELD", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_MAGNETIC_FIELD) { + GST_ERROR ("TYPE_MAGNETIC_FIELD has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_MAGNETIC_FIELD_UNCALIBRATED", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED) { + GST_ERROR ("TYPE_MAGNETIC_FIELD_UNCALIBRATED has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_ORIENTATION", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_ORIENTATION) { + GST_ERROR ("TYPE_ORIENTATION has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_PRESSURE", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_PRESSURE) { + GST_ERROR ("TYPE_PRESSURE has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_PROXIMITY", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_PROXIMITY) { + GST_ERROR ("TYPE_PROXIMITY has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_RELATIVE_HUMIDITY", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_RELATIVE_HUMIDITY) { + GST_ERROR ("TYPE_RELATIVE_HUMIDITY has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_ROTATION_VECTOR", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_ROTATION_VECTOR) { + GST_ERROR ("TYPE_ROTATION_VECTOR has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, + "TYPE_SIGNIFICANT_MOTION", "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_SIGNIFICANT_MOTION) { + GST_ERROR ("TYPE_SIGNIFICANT_MOTION has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_STEP_COUNTER", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_STEP_COUNTER) { + GST_ERROR ("TYPE_STEP_COUNTER has changed value"); + goto failed; + } + + fieldID = + gst_amc_jni_get_static_field_id (env, &err, klass, "TYPE_STEP_DETECTOR", + "I"); + if (!fieldID) + goto failed; + if (!gst_amc_jni_get_static_int_field (env, &err, klass, fieldID, &type)) + goto failed; + if (type != AHS_SENSOR_TYPE_STEP_DETECTOR) { + GST_ERROR ("TYPE_STEP_DETECTOR has changed value"); + goto failed; + } + + /* org.freedesktop.gstreamer.androidmedia.GstAhsCallback */ + if (!org_freedesktop_gstreamer_androidmedia_gstahscallback.klass) { + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass = + gst_amc_jni_get_class (env, &err, + "org/freedesktop/gstreamer/androidmedia/GstAhsCallback"); + } + if (!org_freedesktop_gstreamer_androidmedia_gstahscallback.klass) + goto failed; + org_freedesktop_gstreamer_androidmedia_gstahscallback.constructor = + gst_amc_jni_get_method_id (env, &err, + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass, "", + "(JJJ)V"); + if (!org_freedesktop_gstreamer_androidmedia_gstahscallback.constructor) + goto failed; + + if ((*env)->RegisterNatives (env, + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass, + native_methods, G_N_ELEMENTS (native_methods))) { + GST_ERROR ("Failed to register native methods for GstAhsCallback"); + goto failed; + } + natives_registered = TRUE; + + return TRUE; + +failed: + if (err) { + GST_ERROR ("Failed to initialize Android classes: %s", err->message); + g_clear_error (&err); + } + + return FALSE; +} + +gboolean +gst_android_hardware_sensor_init (void) +{ + GST_DEBUG_CATEGORY_INIT (ahs_debug, "ahs", 0, + "Android Gstreamer Hardware Sensor"); + if (!_init_classes ()) { + gst_android_hardware_sensor_deinit (); + return FALSE; + } + + gst_ah_sensor_sensor_sizes_init (); + + return TRUE; +} + +void +gst_android_hardware_sensor_deinit (void) +{ + JNIEnv *env = gst_amc_jni_get_env (); + + if (android_content_context.SENSOR_SERVICE) { + gst_amc_jni_object_unref (env, android_content_context.SENSOR_SERVICE); + android_content_context.SENSOR_SERVICE = NULL; + } + + if (android_content_context.klass) { + gst_amc_jni_object_unref (env, android_content_context.klass); + android_content_context.klass = NULL; + } + + if (android_hardware_sensor_event.klass) { + gst_amc_jni_object_unref (env, android_hardware_sensor_event.klass); + android_hardware_sensor_event.klass = NULL; + } + + if (android_hardware_sensor_manager.klass) { + gst_amc_jni_object_unref (env, android_hardware_sensor_manager.klass); + android_hardware_sensor_manager.klass = NULL; + } + + if (org_freedesktop_gstreamer_androidmedia_gstahscallback.klass) { + if (natives_registered) { + (*env)->UnregisterNatives (env, + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass); + natives_registered = FALSE; + } + gst_amc_jni_object_unref (env, + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass); + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass = NULL; + } + + gst_ah_sensor_sensor_sizes_deinit (); +} + +GstAHSensorManager * +gst_ah_sensor_get_manager (void) +{ + jobject context; + GError *err = NULL; + JNIEnv *env = gst_amc_jni_get_env (); + GstAHSensorManager *manager; + jobject object; + gboolean success; + + context = gst_android_get_application_context (); + success = gst_amc_jni_call_object_method (env, &err, context, + android_content_context.getSystemService, + &object, android_content_context.SENSOR_SERVICE); + if (!success) + return NULL; + + object = gst_amc_jni_object_make_global (env, object); + if (!object) + return NULL; + + manager = g_slice_new (GstAHSensorManager); + manager->object = object; + + return manager; +} + +GstAHSensor * +gst_ah_sensor_get_default_sensor (GstAHSensorManager * self, gint32 sensor_type) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + jobject object; + GstAHSensor *sensor; + + if (!gst_amc_jni_call_object_method (env, &err, self->object, + android_hardware_sensor_manager.getDefaultSensor, + &object, sensor_type)) + return NULL; + + object = gst_amc_jni_object_make_global (env, object); + if (!object) + return NULL; + + sensor = g_slice_new (GstAHSensor); + sensor->object = object; + + return sensor; +} + +GstAHSensorEventListener * +gst_ah_sensor_create_listener (GstAHSensorCallback sensor_cb, + GstAHSAccuracyCallback accuracy_cb, gpointer user_data) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + GstAHSensorEventListener *listener; + jobject object; + + object = gst_amc_jni_new_object (env, + &err, + TRUE, + org_freedesktop_gstreamer_androidmedia_gstahscallback.klass, + org_freedesktop_gstreamer_androidmedia_gstahscallback.constructor, + UNION_CAST (sensor_cb, jlong), + UNION_CAST (accuracy_cb, jlong), + UNION_CAST (user_data, jlong)); + if (err) { + GST_ERROR ("Failed to create listener callback class"); + g_clear_error (&err); + return NULL; + } + + listener = g_slice_new (GstAHSensorEventListener); + listener->object = object; + + return listener; +} + +gboolean +gst_ah_sensor_register_listener (GstAHSensorManager * self, + GstAHSensorEventListener * listener, GstAHSensor * sensor, gint32 delay) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + gboolean success; + + gst_amc_jni_call_boolean_method (env, &err, self->object, + android_hardware_sensor_manager.registerListener, &success, + listener->object, sensor->object, (jint) delay); + if (err) { + GST_ERROR ("Failed to call android.hardware.SensorManager.registerListener: %s", + err->message); + g_clear_error (&err); + return FALSE; + } + listener->registered = TRUE; + + return TRUE; +} + +void +gst_ah_sensor_unregister_listener (GstAHSensorManager * self, + GstAHSensorEventListener * listener) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + + gst_amc_jni_call_void_method (env, &err, self->object, + android_hardware_sensor_manager.unregisterListener, listener->object); + if (err) { + GST_ERROR ("Failed to call android.hardware.SensorManager.unregisterListener: %s", + err->message); + g_clear_error (&err); + } + listener->registered = FALSE; +} + +gboolean +gst_ah_sensor_populate_event (GstAHSensorEvent * event, jobject event_object, + gint size) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GError *err = NULL; + jfloatArray object_array; + jfloat *values; + + gst_amc_jni_get_int_field (env, &err, + event_object, android_hardware_sensor_event.accuracy, &event->accuracy); + if (err) { + GST_ERROR ("Failed to get sensor accuracy field: %s", err->message); + goto error; + } + + gst_amc_jni_get_object_field (env, &err, event_object, + android_hardware_sensor_event.values, &object_array); + if (err) { + GST_ERROR ("Failed to get sensor values field: %s", err->message); + goto error; + } + + values = (*env)->GetFloatArrayElements (env, object_array, NULL); + if (!values) { + GST_ERROR ("Failed to get float array elements from object array"); + gst_amc_jni_object_local_unref (env, object_array); + return FALSE; + } + /* We can't use gst_amc_jni_object_make_global here because we need to call + * ReleaseFloatArrayElements before doing a local unref in the failure case, + * but gst_amc_jni_object_make_global would unref before we could Release. + */ + event->data.array = gst_amc_jni_object_ref (env, object_array); + if (!event->data.array) { + (*env)->ReleaseFloatArrayElements (env, object_array, values, JNI_ABORT); + gst_amc_jni_object_local_unref (env, object_array); + return FALSE; + } + event->data.values = values; + gst_amc_jni_object_local_unref (env, object_array); + + return TRUE; + +error: + g_clear_error (&err); + return FALSE; +} + +void +gst_ah_sensor_free_sensor_data (GstAHSensorData * data) +{ + JNIEnv *env = gst_amc_jni_get_env (); + + (*env)->ReleaseFloatArrayElements (env, data->array, data->values, JNI_ABORT); + gst_amc_jni_object_unref (env, data->array); +} diff --git a/sys/androidmedia/gst-android-hardware-sensor.h b/sys/androidmedia/gst-android-hardware-sensor.h new file mode 100644 index 0000000..2dc79dc --- /dev/null +++ b/sys/androidmedia/gst-android-hardware-sensor.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ANDROID_HARDWARE_SENSOR_H__ +#define __GST_ANDROID_HARDWARE_SENSOR_H__ + +#include +#include + +#include "gstsensors.h" + +G_BEGIN_DECLS + +typedef struct GstAHSensor +{ + /* < private > */ + jobject object; +} GstAHSensor; + +typedef struct GstAHSensorData +{ + jfloatArray array; + gfloat *values; +} GstAHSensorData; + +typedef struct GstAHSensorEvent +{ + /* + * Note that we don't use the Android event timestamp, as it's not reliable. + * See https://code.google.com/p/android/issues/detail?id=7981 for more + * details. + */ + gint32 accuracy; + GstAHSensorData data; +} GstAHSensorEvent; + +typedef struct GstAHSensorEventListener +{ + /* < private > */ + jobject object; + + gboolean registered; +} GstAHSensorEventListener; + +typedef struct GstAHSensorManager +{ + /* < private > */ + jobject object; +} GstAHSensorManager; + +gint gst_android_sensor_type_from_string (const gchar * type_str); + +/* android.hardware.SensorListener onSensorChanged */ +typedef void (*GstAHSensorCallback) (jobject sensor_event, gpointer user_data); + +/* android.hardware.SensorListener onAccuracyChanged */ +typedef void (*GstAHSAccuracyCallback) (jobject sensor, gint32 accuracy, + gpointer user_data); + +gboolean gst_android_hardware_sensor_init (void); +void gst_android_hardware_sensor_deinit (void); + +/* + * Example usage (excluding error checking): + * + * GstAHSensorManager *manager = gst_ah_sensor_get_manager (); + * GstAHSensor *sensor = + * gst_ah_sensor_get_default_sensor (manager, * sensor_type); + * GstAHSensorEventListener *listener = + * gst_ah_sensor_create_listener * (change_cb, accuracy_cb, self); + * gst_ah_sensor_register_listener (manager, listener, sensor, + * SensorDelay_SENSOR_DELAY_NORMAL); + */ +GstAHSensorManager *gst_ah_sensor_get_manager (void); +GstAHSensor *gst_ah_sensor_get_default_sensor (GstAHSensorManager * manager, + gint32 sensor_type); +GstAHSensorEventListener *gst_ah_sensor_create_listener (GstAHSensorCallback + sensor_cb, GstAHSAccuracyCallback accuracy_cb, gpointer user_data); +gboolean gst_ah_sensor_register_listener (GstAHSensorManager * self, + GstAHSensorEventListener * listener, GstAHSensor * sensor, gint32 delay); +void gst_ah_sensor_unregister_listener (GstAHSensorManager * self, + GstAHSensorEventListener * listener); + +gboolean gst_ah_sensor_populate_event (GstAHSensorEvent * event, + jobject event_object, gint size); +void gst_ah_sensor_free_sensor_data (GstAHSensorData * data); + +/* + * These constants come from the matching SENSOR_DELAY_* TYPE_* constants found + * in the Android documentation: + * + * SENSOR_DELAY_*: + * https://developer.android.com/reference/android/hardware/SensorManager.html + * + * TYPE_*: + * https://developer.android.com/reference/android/hardware/Sensor.html + * + * They are intended to be passed into the registerListener callback for + * listener registration. Note that, although these are hardcoded, we also do + * paranoid runtime checks during plugin init to verify that the API values + * haven't changed. This is unlikely but seems like a good precaution. When + * adding values, please keep the two lists in sync. + */ +enum +{ + AHS_SENSOR_DELAY_FASTEST = 0, + AHS_SENSOR_DELAY_GAME = 1, + AHS_SENSOR_DELAY_NORMAL = 3, + AHS_SENSOR_DELAY_UI = 2 +}; + +enum +{ + AHS_SENSOR_TYPE_ACCELEROMETER = 0x1, + AHS_SENSOR_TYPE_AMBIENT_TEMPERATURE = 0xd, + AHS_SENSOR_TYPE_GAME_ROTATION_VECTOR = 0xf, + AHS_SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR = 0x14, + AHS_SENSOR_TYPE_GRAVITY = 0x9, + AHS_SENSOR_TYPE_GYROSCOPE = 0x4, + AHS_SENSOR_TYPE_GYROSCOPE_UNCALIBRATED = 0x10, + AHS_SENSOR_TYPE_HEART_RATE = 0x15, + AHS_SENSOR_TYPE_LIGHT = 0x5, + AHS_SENSOR_TYPE_LINEAR_ACCELERATION = 0xa, + AHS_SENSOR_TYPE_MAGNETIC_FIELD = 0x2, + AHS_SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED = 0xe, + AHS_SENSOR_TYPE_ORIENTATION = 0x3, + AHS_SENSOR_TYPE_PRESSURE = 0x6, + AHS_SENSOR_TYPE_PROXIMITY = 0x8, + AHS_SENSOR_TYPE_RELATIVE_HUMIDITY = 0xc, + AHS_SENSOR_TYPE_ROTATION_VECTOR = 0xb, + AHS_SENSOR_TYPE_SIGNIFICANT_MOTION = 0x11, + AHS_SENSOR_TYPE_STEP_COUNTER = 0x13, + AHS_SENSOR_TYPE_STEP_DETECTOR = 0x12 +}; + +gsize gst_ah_sensor_get_sensor_data_size (gint sensor_type); + +G_END_DECLS +#endif /* __GST_ANDROID_HARDWARE_SENSOR_H__ */ diff --git a/sys/androidmedia/gstahssrc.c b/sys/androidmedia/gstahssrc.c new file mode 100644 index 0000000..bebb196 --- /dev/null +++ b/sys/androidmedia/gstahssrc.c @@ -0,0 +1,684 @@ +/* GStreamer android.hardware.Sensor Source + * Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/** + * SECTION:element-gstahssrc + * + * The ahssrc element reads data from Android device sensors + * (android.hardware.Sensor). + * + * + * Example launch line + * |[ + * gst-launch -v ahssrc ! fakesink + * ]| + * Push Android sensor data into a fakesink. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include "gstjniutils.h" +#include "gst-android-hardware-sensor.h" +#include "gstahssrc.h" +#include "gstsensors.h" + +GST_DEBUG_CATEGORY_STATIC (gst_ahs_src_debug); +#define GST_CAT_DEFAULT gst_ahs_src_debug + +#define parent_class gst_ahs_src_parent_class + +/* GObject */ +static void gst_ahs_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_ahs_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_ahs_src_dispose (GObject * object); + +/* GstBaseSrc */ +static gboolean gst_ahs_src_set_caps (GstBaseSrc * src, GstCaps * caps); +static gboolean gst_ahs_src_start (GstBaseSrc * src); +static gboolean gst_ahs_src_stop (GstBaseSrc * src); +static gboolean gst_ahs_src_get_size (GstBaseSrc * src, guint64 * size); +static gboolean gst_ahs_src_is_seekable (GstBaseSrc * src); +static gboolean gst_ahs_src_unlock (GstBaseSrc * src); +static gboolean gst_ahs_src_unlock_stop (GstBaseSrc * src); + +/* GstPushSrc */ +static GstFlowReturn gst_ahs_src_create (GstPushSrc * src, GstBuffer ** buf); + +/* GstAHSSrc */ +static void gst_ahs_src_on_sensor_changed (jobject sensor_event, + gpointer user_data); +static void gst_ahs_src_on_accuracy_changed (jobject sensor, gint accuracy, + gpointer user_data); +static gboolean gst_ahs_src_register_callback (GstAHSSrc * self); + +enum +{ + PROP_0, + PROP_SENSOR_DELAY, + PROP_ALPHA, + PROP_SAMPLE_INTERVAL, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +#define GST_AHS_SRC_CAPS_STR GST_SENSOR_CAPS_MAKE (GST_SENSOR_FORMATS_ALL) + +static GstStaticPadTemplate gst_ahs_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_AHS_SRC_CAPS_STR)); + + +G_DEFINE_TYPE_WITH_CODE (GstAHSSrc, gst_ahs_src, GST_TYPE_PUSH_SRC, + GST_DEBUG_CATEGORY_INIT (gst_ahs_src_debug, "ahssrc", 0, + "Android hardware sensors")); + +#define GST_TYPE_AHS_SENSOR_DELAY (gst_ahs_src_get_sensor_delay ()) +static GType +gst_ahs_src_get_sensor_delay (void) +{ + static GType ahs_src_sensor_delay = 0; + + if (!ahs_src_sensor_delay) { + static GEnumValue sensor_delay[5]; + sensor_delay[0].value = AHS_SENSOR_DELAY_FASTEST; + sensor_delay[0].value_name = "fastest"; + sensor_delay[0].value_nick = "fastest"; + sensor_delay[1].value = AHS_SENSOR_DELAY_GAME; + sensor_delay[1].value_name = "game"; + sensor_delay[1].value_nick = "game"; + sensor_delay[2].value = AHS_SENSOR_DELAY_NORMAL; + sensor_delay[2].value_name = "normal"; + sensor_delay[2].value_nick = "normal"; + sensor_delay[3].value = AHS_SENSOR_DELAY_UI; + sensor_delay[3].value_name = "ui"; + sensor_delay[3].value_nick = "ui"; + sensor_delay[4].value = 0; + sensor_delay[4].value_name = NULL; + sensor_delay[4].value_nick = NULL; + + ahs_src_sensor_delay = + g_enum_register_static ("GstAhsSrcSensorDelay", sensor_delay); + } + + return ahs_src_sensor_delay; +} + +#define GST_TYPE_AHS_SENSOR_TYPE (gst_ahs_src_get_sensor_type ()) +static GType +gst_ahs_src_get_sensor_type (void) +{ + static GType ahs_src_sensor_type = 0; + + if (!ahs_src_sensor_type) { + static const GEnumValue sensor_types[] = { + {AHS_SENSOR_TYPE_ACCELEROMETER, "accelerometer"}, + {AHS_SENSOR_TYPE_AMBIENT_TEMPERATURE, "ambient-temperature"}, + {AHS_SENSOR_TYPE_GAME_ROTATION_VECTOR, "game-rotation-vector"}, + {AHS_SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR, "geomagnetic-rotation-vector"}, + {AHS_SENSOR_TYPE_GRAVITY, "gravity"}, + {AHS_SENSOR_TYPE_GYROSCOPE, "gyroscope"}, + {AHS_SENSOR_TYPE_GYROSCOPE_UNCALIBRATED, "gyroscope-uncalibrated"}, + {AHS_SENSOR_TYPE_HEART_RATE, "heart-rate"}, + {AHS_SENSOR_TYPE_LIGHT, "light"}, + {AHS_SENSOR_TYPE_LINEAR_ACCELERATION, "linear-acceleration"}, + {AHS_SENSOR_TYPE_MAGNETIC_FIELD, "magnetic-field"}, + {AHS_SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, "magnetic-field-uncalibrated"}, + {AHS_SENSOR_TYPE_ORIENTATION, "orientation"}, + {AHS_SENSOR_TYPE_PRESSURE, "pressure"}, + {AHS_SENSOR_TYPE_PROXIMITY, "proximity"}, + {AHS_SENSOR_TYPE_RELATIVE_HUMIDITY, "relative-humidity"}, + {AHS_SENSOR_TYPE_ROTATION_VECTOR, "rotation-vector"}, + {AHS_SENSOR_TYPE_STEP_COUNTER, "step-counter"}, + {AHS_SENSOR_TYPE_STEP_DETECTOR, "step-detector"}, + {0, NULL, NULL} + }; + + ahs_src_sensor_type = + g_enum_register_static ("GstAhsSrcSensorType", sensor_types); + } + + return ahs_src_sensor_type; +} + +static void +gst_ahs_src_class_init (GstAHSSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS (klass); + + gobject_class->set_property = gst_ahs_src_set_property; + gobject_class->get_property = gst_ahs_src_get_property; + gobject_class->dispose = gst_ahs_src_dispose; + + base_src_class->set_caps = GST_DEBUG_FUNCPTR (gst_ahs_src_set_caps); + base_src_class->start = GST_DEBUG_FUNCPTR (gst_ahs_src_start); + base_src_class->stop = GST_DEBUG_FUNCPTR (gst_ahs_src_stop); + base_src_class->get_size = GST_DEBUG_FUNCPTR (gst_ahs_src_get_size); + base_src_class->is_seekable = GST_DEBUG_FUNCPTR (gst_ahs_src_is_seekable); + base_src_class->unlock = GST_DEBUG_FUNCPTR (gst_ahs_src_unlock); + base_src_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_ahs_src_unlock_stop); + + push_src_class->create = GST_DEBUG_FUNCPTR (gst_ahs_src_create); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_ahs_src_template)); + + properties[PROP_SENSOR_DELAY] = g_param_spec_enum ("sensor-delay", + "Sensor delay", "Configure the sensor rate", GST_TYPE_AHS_SENSOR_DELAY, + AHS_SENSOR_DELAY_NORMAL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SENSOR_DELAY, + properties[PROP_SENSOR_DELAY]); + + properties[PROP_ALPHA] = g_param_spec_double ("alpha", "Alpha", + "Alpha value used for exponential smoothing (between 0.0 and 1.0)", 0.0, + 1.0, 0.2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_ALPHA, + properties[PROP_ALPHA]); + + properties[PROP_SAMPLE_INTERVAL] = g_param_spec_uint ("sample-interval", + "Sample interval", + "Sample interval (for interval n, will output a smoothed average every " + "nth sample)", 1, G_MAXUINT, 1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SAMPLE_INTERVAL, + properties[PROP_SAMPLE_INTERVAL]); + + gst_element_class_set_static_metadata (element_class, + "Android hardware sensors", "Source/Sensor/Device", + "Source for Android hardware sensor data", + "Martin Kelly "); +} + +static gboolean +_data_queue_check_full (GstDataQueue * queue, guint visible, + guint bytes, guint64 time, gpointer checkdata) +{ + return FALSE; +} + +static void +gst_ahs_src_init (GstAHSSrc * self) +{ + gst_base_src_set_live (GST_BASE_SRC (self), TRUE); + gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME); + gst_base_src_set_do_timestamp (GST_BASE_SRC (self), FALSE); + + self->sensor_enum_class = g_type_class_ref (GST_TYPE_AHS_SENSOR_TYPE); + self->sensor_type_name = NULL; + + self->manager = NULL; + self->sensor = NULL; + self->listener = NULL; + self->callback_registered = FALSE; + + self->queue = gst_data_queue_new (_data_queue_check_full, NULL, NULL, NULL); + + self->previous_time = GST_CLOCK_TIME_NONE; + self->sample_index = 0; + self->current_sample = NULL; +} + +static void +gst_ahs_src_dispose (GObject * object) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GstAHSSrc *self = GST_AHS_SRC (object); + + if (self->manager) { + gst_amc_jni_object_unref (env, self->manager->object); + g_slice_free (GstAHSensorManager, self->manager); + self->manager = NULL; + } + + if (self->sensor) { + gst_amc_jni_object_unref (env, self->sensor->object); + g_slice_free (GstAHSensor, self->sensor); + self->sensor = NULL; + } + + if (self->listener) { + gst_amc_jni_object_unref (env, self->listener->object); + g_slice_free (GstAHSensorEventListener, self->listener); + self->listener = NULL; + } + + if (self->current_sample) { + g_free (self->current_sample); + self->current_sample = NULL; + } + + if (self->sensor_enum_class) { + g_type_class_unref (self->sensor_enum_class); + self->sensor_enum_class = NULL; + } + + if (self->sensor_type_name) { + g_free ((gpointer) self->sensor_type_name); + self->sensor_type_name = NULL; + } + + if (self->queue) { + g_object_unref (self->queue); + self->queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_ahs_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAHSSrc *self = GST_AHS_SRC (object); + + /* + * Take the mutex to protect against callbacks or changes to the properties + * that the callback uses (e.g. caps changes). + */ + GST_OBJECT_LOCK (self); + + switch (prop_id) { + case PROP_SENSOR_DELAY: + self->sensor_delay = g_value_get_enum (value); + /* + * If we already have a callback running, reregister with the new delay. + * Otherwise, wait for the pipeline to start before we register. + */ + if (self->callback_registered) + gst_ahs_src_register_callback (self); + break; + case PROP_ALPHA: + self->alpha = g_value_get_double (value); + break; + case PROP_SAMPLE_INTERVAL: + self->sample_interval = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + GST_OBJECT_UNLOCK (self); +} + +static void +gst_ahs_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAHSSrc *self = GST_AHS_SRC (object); + + switch (prop_id) { + case PROP_SENSOR_DELAY: + g_value_set_enum (value, self->sensor_delay); + case PROP_ALPHA: + g_value_set_double (value, self->alpha); + break; + case PROP_SAMPLE_INTERVAL: + g_value_set_uint (value, self->sample_interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static gboolean +gst_ahs_src_register_callback (GstAHSSrc * self) +{ + if (self->callback_registered) { + gst_ah_sensor_unregister_listener (self->manager, self->listener); + self->callback_registered = FALSE; + } + if (!gst_ah_sensor_register_listener (self->manager, self->listener, + self->sensor, self->sensor_delay)) { + return FALSE; + } + self->callback_registered = TRUE; + + return TRUE; +} + +static gboolean +gst_ahs_src_change_sensor_type (GstAHSSrc * self, const gchar * type_str, + gint type) +{ + JNIEnv *env = gst_amc_jni_get_env (); + + /* Replace sensor type. */ + if (self->sensor_type_name) + g_free ((gpointer) self->sensor_type_name); + self->sensor_type_name = type_str; + self->sensor_type = type; + + /* Adjust buffer and buffer size. */ + self->buffer_size = gst_ah_sensor_get_sensor_data_size (self->sensor_type); + g_assert (self->buffer_size != 0); + self->sample_length = self->buffer_size / sizeof (*self->current_sample); + self->current_sample = g_realloc (self->current_sample, self->buffer_size); + + /* Make sure we have a manager. */ + if (!self->manager) { + self->manager = gst_ah_sensor_get_manager (); + if (!self->manager) { + GST_ERROR_OBJECT (self, "Failed to get sensor manager"); + goto error_sensor_type_name; + } + } + + /* Replace sensor object. */ + if (self->sensor) { + gst_amc_jni_object_unref (env, self->sensor->object); + g_slice_free (GstAHSensor, self->sensor); + } + self->sensor = gst_ah_sensor_get_default_sensor (self->manager, + self->sensor_type); + if (!self->sensor) { + GST_ERROR_OBJECT (self, "Failed to get sensor type %s", + self->sensor_type_name); + goto error_manager; + } + + /* Register for the callback, unregistering first if necessary. */ + if (!gst_ahs_src_register_callback (self)) + goto error_sensor; + + return TRUE; + +error_sensor: + gst_amc_jni_object_unref (env, self->sensor->object); + g_slice_free (GstAHSensor, self->sensor); + self->sensor = NULL; +error_manager: + gst_amc_jni_object_unref (env, self->manager->object); + g_slice_free (GstAHSensorManager, self->manager); + self->manager = NULL; +error_sensor_type_name: + g_free ((gpointer) self->sensor_type_name); + self->sensor_type_name = NULL; + return FALSE; +} + +static gboolean +gst_ahs_src_set_caps (GstBaseSrc * src, GstCaps * caps) +{ + const GstStructure *caps_struct; + GstAHSSrc *self = GST_AHS_SRC (src); + gboolean success; + gint type; + const gchar *type_str; + GEnumValue *value; + + caps_struct = gst_caps_get_structure (caps, 0); + type_str = gst_structure_get_string (caps_struct, "type"); + value = g_enum_get_value_by_name (self->sensor_enum_class, type_str); + if (!value) { + GST_ERROR_OBJECT (self, "Failed to lookup sensor type %s", type_str); + return FALSE; + } + type_str = g_strdup (type_str); + type = value->value; + + /* + * Take the mutex while changing the sensor type in case there are concurrent + * callbacks being processed. + */ + GST_OBJECT_LOCK (self); + success = gst_ahs_src_change_sensor_type (self, type_str, type); + GST_OBJECT_UNLOCK (self); + + if (!success) + return FALSE; + + return TRUE; +} + +static gboolean +gst_ahs_src_start (GstBaseSrc * src) +{ + JNIEnv *env = gst_amc_jni_get_env (); + GstAHSSrc *self = GST_AHS_SRC (src); + + g_assert_null (self->manager); + g_assert_null (self->listener); + + self->manager = gst_ah_sensor_get_manager (); + if (!self->manager) { + GST_ERROR_OBJECT (self, "Failed to get sensor manager"); + goto error; + } + + self->previous_time = GST_CLOCK_TIME_NONE; + + self->listener = gst_ah_sensor_create_listener (gst_ahs_src_on_sensor_changed, + gst_ahs_src_on_accuracy_changed, self); + if (!self->listener) { + GST_ERROR_OBJECT (self, "Failed to create sensor listener"); + goto error_manager; + } + + return TRUE; + +error_manager: + gst_amc_jni_object_unref (env, self->manager->object); + g_slice_free (GstAHSensorManager, self->manager); + self->manager = NULL; +error: + return FALSE; +} + +static gboolean +gst_ahs_src_stop (GstBaseSrc * src) +{ + GstAHSSrc *self = GST_AHS_SRC (src); + + g_assert_nonnull (self->manager); + g_assert_nonnull (self->sensor); + g_assert_nonnull (self->listener); + + gst_ah_sensor_unregister_listener (self->manager, self->listener); + self->previous_time = GST_CLOCK_TIME_NONE; + + return TRUE; +} + +static gboolean +gst_ahs_src_get_size (GstBaseSrc * src, guint64 * size) +{ + GstAHSSrc *self = GST_AHS_SRC (src); + + return self->buffer_size; +} + +static gboolean +gst_ahs_src_is_seekable (GstBaseSrc * src) +{ + return FALSE; +} + +static gboolean +gst_ahs_src_unlock (GstBaseSrc * src) +{ + GstAHSSrc *self = GST_AHS_SRC (src); + + gst_data_queue_set_flushing (self->queue, TRUE); + + return TRUE; +} + +static gboolean +gst_ahs_src_unlock_stop (GstBaseSrc * src) +{ + GstAHSSrc *self = GST_AHS_SRC (src); + + gst_data_queue_set_flushing (self->queue, FALSE); + + return TRUE; +} + +static GstFlowReturn +gst_ahs_src_create (GstPushSrc * src, GstBuffer ** buffer) +{ + GstAHSSrc *self = GST_AHS_SRC (src); + GstDataQueueItem *item; + + if (!gst_data_queue_pop (self->queue, &item)) { + GST_INFO_OBJECT (self, "data queue is empty"); + return GST_FLOW_FLUSHING; + } + + GST_DEBUG_OBJECT (self, "creating buffer %p->%p", item, item->object); + + *buffer = GST_BUFFER (item->object); + g_slice_free (GstDataQueueItem, item); + + return GST_FLOW_OK; +} + +static void +gst_ahs_src_free_data_queue_item (GstDataQueueItem * item) +{ + gst_buffer_unref (GST_BUFFER (item->object)); + g_slice_free (GstDataQueueItem, item); +} + +static void +gst_ahs_src_update_smoothing (GstAHSSrc * self, const GstAHSensorEvent * event) +{ + gint i; + + /* + * Since we're doing exponential smoothing, the first sample needs to be + * special-cased to prevent it from being artificially lowered by the alpha + * smoothing factor. + */ + if (self->sample_index == 0) { + for (i = 0; i < self->sample_length; i++) { + self->current_sample[i] = event->data.values[i]; + } + } else { + for (i = 0; i < self->sample_length; i++) + self->current_sample[i] = + (1-self->alpha) * self->current_sample[i] + self->alpha * event->data.values[i]; + } +} + +static void +gst_ahs_src_on_sensor_changed (jobject event_object, gpointer user_data) +{ + GstBuffer *buffer; + GstClockTime buffer_time; + gfloat *data; + GstAHSensorEvent event; + GstDataQueueItem *item; + GstClock *pipeline_clock; + GstAHSSrc *self = GST_AHS_SRC (user_data); + gboolean success; + + GST_OBJECT_LOCK (self); + + pipeline_clock = GST_ELEMENT_CLOCK (self); + /* If the clock is NULL, the pipeline is not yet set to PLAYING. */ + if (pipeline_clock == NULL) + goto done; + + /* + * Unfortunately, the timestamp reported in the Android SensorEvent timestamp + * is not guaranteed to use any particular clock. On some device models, it + * uses system time, and on other models, it uses monotonic time. In addition, + * in some cases, the units are microseconds, and in other cases they are + * nanoseconds. Thus we cannot slave it to the pipeline clock or use any + * similar strategy that would allow us to correlate the two clocks. So + * instead, we approximate the buffer timestamp using the pipeline clock. + * + * See here for more details on issues with the Android SensorEvent timestamp: + * https://code.google.com/p/android/issues/detail?id=7981 + */ + buffer_time = + gst_clock_get_time (pipeline_clock) - GST_ELEMENT_CAST (self)->base_time; + + success = + gst_ah_sensor_populate_event (&event, event_object, self->buffer_size); + if (!success) { + GST_ERROR_OBJECT (self, "Failed to populate sensor event"); + goto done; + } + + gst_ahs_src_update_smoothing (self, &event); + gst_ah_sensor_free_sensor_data (&event.data); + self->sample_index++; + if (self->sample_index < self->sample_interval) + goto done; + self->sample_index = 0; + + /* + * We want to send off this sample; copy it into a separate data struct so we + * can continue using current_sample for aggregating future samples. + */ + data = g_malloc (self->buffer_size); + memcpy (data, self->current_sample, self->buffer_size); + + /* Wrap the datapoint with a buffer and add it to the queue. */ + buffer = gst_buffer_new_wrapped (data, self->buffer_size); + GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; + GST_BUFFER_PTS (buffer) = buffer_time; + + item = g_slice_new (GstDataQueueItem); + item->object = GST_MINI_OBJECT (buffer); + item->size = gst_buffer_get_size (buffer); + item->duration = GST_BUFFER_DURATION (buffer); + item->visible = TRUE; + item->destroy = (GDestroyNotify) gst_ahs_src_free_data_queue_item; + + success = gst_data_queue_push (self->queue, item); + if (!success) { + GST_ERROR_OBJECT (self, "Could not add buffer to queue"); + gst_ahs_src_free_data_queue_item (item); + goto done; + } + +done: + GST_OBJECT_UNLOCK (self); +} + +static void +gst_ahs_src_on_accuracy_changed (jobject sensor, gint accuracy, + gpointer user_data) +{ + GstAHSSrc *self = GST_AHS_SRC (user_data); + + /* TODO: Perhaps we should do something more with this information. */ + GST_DEBUG_OBJECT (self, "Accuracy changed on sensor %p and is now %d", sensor, + accuracy); +} diff --git a/sys/androidmedia/gstahssrc.h b/sys/androidmedia/gstahssrc.h new file mode 100644 index 0000000..9c00ca7 --- /dev/null +++ b/sys/androidmedia/gstahssrc.h @@ -0,0 +1,80 @@ +/* GStreamer android.hardware.Sensor Source + * Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_AHSSRC_H__ +#define _GST_AHSSRC_H__ + +#include +#include + +#include "gst-android-hardware-sensor.h" + +G_BEGIN_DECLS +#define GST_TYPE_AHS_SRC (gst_ahs_src_get_type()) +#define GST_AHS_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AHS_SRC,GstAHSSrc)) +#define GST_AHS_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AHS_SRC,GstAHSSrcClass)) +#define GST_IS_AHS_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AHS_SRC)) +#define GST_IS_AHS_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AHS_SRC)) +typedef struct _GstAHSSrc GstAHSSrc; +typedef struct _GstAHSSrcClass GstAHSSrcClass; + +struct _GstAHSSrc +{ + /* < private > */ + GstPushSrc parent; + + /* properties */ + gint32 sensor_delay; + gdouble alpha; + guint sample_interval; + + /* sensor type information */ + GEnumClass *sensor_enum_class; + gint sensor_type; + const gchar *sensor_type_name; + + /* JNI wrapper classes */ + GstAHSensorManager *manager; + GstAHSensor *sensor; + GstAHSensorEventListener *listener; + + /* timestamping */ + GstClockTime previous_time; + gfloat *current_sample; + + /* buffers */ + gboolean callback_registered; + gint sample_index; + gint sample_length; + gint buffer_size; + + /* multiprocessing */ + GstDataQueue *queue; +}; + +struct _GstAHSSrcClass +{ + GstPushSrcClass parent_class; +}; + +GType gst_ahs_src_get_type (void); + +G_END_DECLS +#endif diff --git a/sys/androidmedia/gstamc.c b/sys/androidmedia/gstamc.c index 78fd869..9eb4a50 100644 --- a/sys/androidmedia/gstamc.c +++ b/sys/androidmedia/gstamc.c @@ -30,6 +30,7 @@ #endif #include "gstahcsrc.h" +#include "gstahssrc.h" #include "gstamc.h" #include "gstamc-constants.h" @@ -3337,13 +3338,24 @@ plugin_init (GstPlugin * plugin) goto failed_graphics_imageformat; } + if (!gst_android_hardware_sensor_init ()) { + goto failed_hardware_camera; + } + if (!gst_element_register (plugin, "ahcsrc", GST_RANK_NONE, GST_TYPE_AHC_SRC)) { GST_ERROR ("Failed to register android camera source"); - goto failed_hardware_camera; + goto failed_hardware_sensor; + } + + if (!gst_element_register (plugin, "ahssrc", GST_RANK_NONE, GST_TYPE_AHS_SRC)) { + GST_ERROR ("Failed to register android sensor source"); + goto failed_hardware_sensor; } return TRUE; +failed_hardware_sensor: + gst_android_hardware_sensor_deinit (); failed_hardware_camera: gst_android_hardware_camera_deinit (); failed_graphics_imageformat: diff --git a/sys/androidmedia/gstsensors.h b/sys/androidmedia/gstsensors.h new file mode 100644 index 0000000..8b7dc89 --- /dev/null +++ b/sys/androidmedia/gstsensors.h @@ -0,0 +1,182 @@ +/* Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_AHSCAPS_H__ +#define _GST_AHSCAPS_H__ + +G_BEGIN_DECLS + +#define GST_SENSOR_FORMATS_ALL "{" \ + "accelerometer, " \ + "ambient-temperature, " \ + "game-rotation-vector, " \ + "geomagnetic-rotation-vector, " \ + "gravity, " \ + "gyroscope, " \ + "gyroscope-uncalibrated, " \ + "heart-rate, " \ + "light, " \ + "linear-acceleration, " \ + "magnetic-field, " \ + "magnetic-field-uncalibrated, " \ + "orientation, " \ + "pressure, " \ + "proximity, " \ + "relative-humidity, " \ + "rotation-vector, " \ + "significant-motion, " \ + "step-counter, " \ + "step-detector" \ + "}" + +#define GST_SENSOR_CAPS_MAKE(format) \ + "application/sensor, " \ + "type = (string) " format + +typedef struct GstAHSAccelerometerValues +{ + gfloat x; + gfloat y; + gfloat z; +} GstAHSAccelerometerValues; + +typedef struct GstAHSAmbientTemperatureValues +{ + gfloat temperature; +} GstAHSAmbientTemperatureValues; + +typedef struct GstAHSGameRotationVectorValues +{ + gfloat x; + gfloat y; + gfloat z; + gfloat cos; + gfloat accuracy; +} GstAHSGameRotationVectorValues; + +typedef struct GstAHSGeomagneticRotationVectorValues +{ + gfloat x; + gfloat y; + gfloat z; + gfloat cos; + gfloat accuracy; +} GstAHSGeomagneticRotationVectorValues; + +typedef struct GstAHSGravityValues +{ + gfloat x; + gfloat y; + gfloat z; +} GstAHSGravityValues; + +typedef struct GstAHSGyroscopeValues +{ + gfloat x; + gfloat y; + gfloat z; +} GstAHSGyroscopeValues; + +typedef struct GstAHSGyroscopeUncalibratedValues +{ + gfloat x_speed; + gfloat y_speed; + gfloat z_speed; + gfloat x_drift; + gfloat y_drift; + gfloat z_drift; +} GstAHSGyroscopeUncalibratedValues; + +typedef struct GstAHSHeartRateValues +{ + gfloat bpm; +} GstAHSHeartRateValues; + +typedef struct GstAHSLightValues +{ + gfloat lux; +} GstAHSLightValues; + +typedef struct GstAHSLinearAccelerationValues +{ + gfloat x; + gfloat y; + gfloat z; +} GstAHSLinearAccelerationValues; + +typedef struct GstAHSMagneticFieldValues +{ + gfloat x; + gfloat y; + gfloat z; +} GstAHSMagneticFieldValues; + +typedef struct GstAHSMagneticFieldUncalibratedValues +{ + gfloat x_uncalib; + gfloat y_uncalib; + gfloat z_uncalib; + gfloat x_bias; + gfloat y_bias; + gfloat z_bias; +} GstAHSMagneticFieldUncalibratedValues; + +typedef struct GstAHSOrientationValues +{ + gfloat azimuth; + gfloat pitch; + gfloat roll; +} GstAHSOrientationValues; + +typedef struct GstAHSProximity +{ + gfloat distance; +} GstAHSProximityValues; + +typedef struct GstAHSPressureValues +{ + gfloat pressure; +} GstAHSPressureValues; + +typedef struct GstAHSRelativeHumidityValues +{ + gfloat humidity; +} GstAHSRelativeHumidityValues; + +typedef struct GstAHSRotationVectorValues +{ + gfloat x; + gfloat y; + gfloat z; + gfloat cos; + gfloat accuracy; +} GstAHSRotationVectorValues; + +typedef struct GstAHSStepCounterValues +{ + gfloat count; +} GstAHSStepCounterValues; + +typedef struct GstAHSStepDetectorValues +{ + gfloat one; +} GstAHSStepDetectorValues; + +G_END_DECLS +#endif diff --git a/sys/androidmedia/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java b/sys/androidmedia/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java new file mode 100644 index 0000000..b6fb015 --- /dev/null +++ b/sys/androidmedia/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 SurroundIO + * Author: Martin Kelly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.freedesktop.gstreamer.androidmedia; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; + +public class GstAhsCallback implements SensorEventListener { + public long mUserData; + public long mSensorCallback; + public long mAccuracyCallback; + + public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event, + long callback, long user_data); + public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy, + long callback, long user_data); + + public GstAhsCallback(long sensor_callback, + long accuracy_callback, long user_data) { + mSensorCallback = sensor_callback; + mAccuracyCallback = accuracy_callback; + mUserData = user_data; + } + + @Override + public void onSensorChanged(SensorEvent event) { + gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + gst_ah_sensor_on_accuracy_changed(sensor, accuracy, + mAccuracyCallback, mUserData); + } +} -- 2.7.4