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
This commit is contained in:
Martin Kelly 2016-07-12 14:51:47 -07:00 committed by Olivier Crête
parent ea443a3fcc
commit a04e6b0cb2
8 changed files with 1982 additions and 1 deletions

View file

@ -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

View file

@ -0,0 +1,808 @@
/*
* 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);
}

View file

@ -0,0 +1,156 @@
/*
* 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__ */

View file

@ -0,0 +1,684 @@
/* 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);
}

View file

@ -0,0 +1,80 @@
/* 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

View file

@ -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:

View file

@ -0,0 +1,182 @@
/* 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

View file

@ -0,0 +1,54 @@
/*
* 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);
}
}