libgstandroidmedia_la_SOURCES = \
gstahcsrc.c \
+ gstahssrc.c \
gstamcaudiodec.c \
gstamc.c \
gstamcsurface.c \
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 \
gstamcvideoenc.h \
gst-android-graphics-imageformat.h \
gst-android-hardware-camera.h \
+ gst-android-hardware-sensor.h \
gstjniutils.h
libgstandroidmedia_la_CFLAGS = \
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
--- /dev/null
+/*
+ * Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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 <glib.h>
+#include <gmodule.h>
+
+#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, "<init>",
+ "(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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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 <gst/gst.h>
+#include <jni.h>
+
+#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__ */
--- /dev/null
+/* GStreamer android.hardware.Sensor Source
+ * Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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).
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v ahssrc ! fakesink
+ * ]|
+ * Push Android sensor data into a fakesink.
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/gstclock.h>
+#include <gst/base/gstbasesrc.h>
+#include <gst/base/gstpushsrc.h>
+#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 <martin@surround.io>");
+}
+
+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);
+}
--- /dev/null
+/* GStreamer android.hardware.Sensor Source
+ * Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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 <gst/base/gstdataqueue.h>
+#include <gst/base/gstpushsrc.h>
+
+#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
#endif
#include "gstahcsrc.h"
+#include "gstahssrc.h"
#include "gstamc.h"
#include "gstamc-constants.h"
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:
--- /dev/null
+/* Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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
--- /dev/null
+/*
+ * Copyright (C) 2016 SurroundIO
+ * Author: Martin Kelly <martin@surround.io>
+ *
+ * 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);
+ }
+}