mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
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:
parent
ea443a3fcc
commit
a04e6b0cb2
8 changed files with 1982 additions and 1 deletions
|
@ -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
|
||||
|
|
808
sys/androidmedia/gst-android-hardware-sensor.c
Normal file
808
sys/androidmedia/gst-android-hardware-sensor.c
Normal 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);
|
||||
}
|
156
sys/androidmedia/gst-android-hardware-sensor.h
Normal file
156
sys/androidmedia/gst-android-hardware-sensor.h
Normal 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__ */
|
684
sys/androidmedia/gstahssrc.c
Normal file
684
sys/androidmedia/gstahssrc.c
Normal 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);
|
||||
}
|
80
sys/androidmedia/gstahssrc.h
Normal file
80
sys/androidmedia/gstahssrc.h
Normal 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
|
|
@ -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:
|
||||
|
|
182
sys/androidmedia/gstsensors.h
Normal file
182
sys/androidmedia/gstsensors.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue