summaryrefslogtreecommitdiff
path: root/playback/player/android/app/src/main/jni/player.c
diff options
context:
space:
mode:
Diffstat (limited to 'playback/player/android/app/src/main/jni/player.c')
-rw-r--r--playback/player/android/app/src/main/jni/player.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/playback/player/android/app/src/main/jni/player.c b/playback/player/android/app/src/main/jni/player.c
new file mode 100644
index 0000000..58d177e
--- /dev/null
+++ b/playback/player/android/app/src/main/jni/player.c
@@ -0,0 +1,497 @@
+/* GStreamer
+ *
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <jni.h>
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+
+#include <gst/player/player.h>
+
+GST_DEBUG_CATEGORY_STATIC (debug_category);
+#define GST_CAT_DEFAULT debug_category
+
+#define GET_CUSTOM_DATA(env, thiz, fieldID) (Player *)(gintptr)(*env)->GetLongField (env, thiz, fieldID)
+#define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)(gintptr)data)
+
+typedef struct _Player
+{
+ jobject java_player;
+ GstPlayer *player;
+ GstPlayerVideoRenderer *renderer;
+ ANativeWindow *native_window;
+} Player;
+
+static pthread_key_t current_jni_env;
+static JavaVM *java_vm;
+static jfieldID native_player_field_id;
+static jmethodID on_position_updated_method_id;
+static jmethodID on_duration_changed_method_id;
+static jmethodID on_state_changed_method_id;
+static jmethodID on_buffering_method_id;
+static jmethodID on_end_of_stream_method_id;
+static jmethodID on_error_method_id;
+static jmethodID on_video_dimensions_changed_method_id;
+
+/* Register this thread with the VM */
+static JNIEnv *
+attach_current_thread (void)
+{
+ JNIEnv *env;
+ JavaVMAttachArgs args;
+
+ GST_DEBUG ("Attaching thread %p", g_thread_self ());
+ args.version = JNI_VERSION_1_4;
+ args.name = NULL;
+ args.group = NULL;
+
+ if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) {
+ GST_ERROR ("Failed to attach current thread");
+ return NULL;
+ }
+
+ return env;
+}
+
+/* Unregister this thread from the VM */
+static void
+detach_current_thread (void *env)
+{
+ GST_DEBUG ("Detaching thread %p", g_thread_self ());
+ (*java_vm)->DetachCurrentThread (java_vm);
+}
+
+/* Retrieve the JNI environment for this thread */
+static JNIEnv *
+get_jni_env (void)
+{
+ JNIEnv *env;
+
+ if ((env = pthread_getspecific (current_jni_env)) == NULL) {
+ env = attach_current_thread ();
+ pthread_setspecific (current_jni_env, env);
+ }
+
+ return env;
+}
+
+/*
+ * Java Bindings
+ */
+static void
+on_position_updated (GstPlayer * unused, GstClockTime position, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player,
+ on_position_updated_method_id, position);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+on_duration_changed (GstPlayer * unused, GstClockTime duration, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player,
+ on_duration_changed_method_id, duration);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+on_state_changed (GstPlayer * unused, GstPlayerState state, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player,
+ on_state_changed_method_id, state);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+on_buffering (GstPlayer * unused, gint percent, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player,
+ on_buffering_method_id, percent);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+on_end_of_stream (GstPlayer * unused, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player, on_end_of_stream_method_id);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+on_error (GstPlayer * unused, GError * err, Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+ jstring error_msg;
+
+ error_msg = (*env)->NewStringUTF (env, err->message);
+
+ (*env)->CallVoidMethod (env, player->java_player, on_error_method_id,
+ err->code, error_msg);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+
+ (*env)->DeleteLocalRef (env, error_msg);
+}
+
+static void
+on_video_dimensions_changed (GstPlayer * unused, gint width, gint height,
+ Player * player)
+{
+ JNIEnv *env = get_jni_env ();
+
+ (*env)->CallVoidMethod (env, player->java_player,
+ on_video_dimensions_changed_method_id, width, height);
+ if ((*env)->ExceptionCheck (env)) {
+ (*env)->ExceptionDescribe (env);
+ (*env)->ExceptionClear (env);
+ }
+}
+
+static void
+native_new (JNIEnv * env, jobject thiz)
+{
+ Player *player = g_new0 (Player, 1);
+
+ player->renderer = gst_player_video_overlay_video_renderer_new (NULL);
+ player->player = gst_player_new (player->renderer, NULL);
+ SET_CUSTOM_DATA (env, thiz, native_player_field_id, player);
+ player->java_player = (*env)->NewGlobalRef (env, thiz);
+
+ g_signal_connect (player->player, "position-updated",
+ G_CALLBACK (on_position_updated), player);
+ g_signal_connect (player->player, "duration-changed",
+ G_CALLBACK (on_duration_changed), player);
+ g_signal_connect (player->player, "state-changed",
+ G_CALLBACK (on_state_changed), player);
+ g_signal_connect (player->player, "buffering",
+ G_CALLBACK (on_buffering), player);
+ g_signal_connect (player->player, "end-of-stream",
+ G_CALLBACK (on_end_of_stream), player);
+ g_signal_connect (player->player, "error", G_CALLBACK (on_error), player);
+ g_signal_connect (player->player, "video-dimensions-changed",
+ G_CALLBACK (on_video_dimensions_changed), player);
+}
+
+static void
+native_free (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ g_object_unref (player->player);
+ (*env)->DeleteGlobalRef (env, player->java_player);
+ g_free (player);
+ SET_CUSTOM_DATA (env, thiz, native_player_field_id, NULL);
+}
+
+static void
+native_play (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ gst_player_play (player->player);
+}
+
+static void
+native_pause (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ gst_player_pause (player->player);
+}
+
+static void
+native_stop (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ gst_player_stop (player->player);
+}
+
+static void
+native_seek (JNIEnv * env, jobject thiz, jlong position)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ gst_player_seek (player->player, position);
+}
+
+static void
+native_set_uri (JNIEnv * env, jobject thiz, jobject uri)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ const gchar *uri_str;
+
+ if (!player)
+ return;
+
+ uri_str = (*env)->GetStringUTFChars (env, uri, NULL);
+ g_object_set (player->player, "uri", uri_str, NULL);
+ (*env)->ReleaseStringUTFChars (env, uri, uri_str);
+}
+
+static jobject
+native_get_uri (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ jobject uri;
+ gchar *uri_str;
+
+ if (!player)
+ return NULL;
+
+ g_object_get (player->player, "uri", &uri_str, NULL);
+
+ uri = (*env)->NewStringUTF (env, uri_str);
+ g_free (uri_str);
+
+ return uri;
+}
+
+static jlong
+native_get_position (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ jdouble position;
+
+ if (!player)
+ return -1;
+
+ g_object_get (player->player, "position", &position, NULL);
+
+ return position;
+}
+
+static jlong
+native_get_duration (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ jlong duration;
+
+ if (!player)
+ return -1;
+
+ g_object_get (player->player, "duration", &duration, NULL);
+
+ return duration;
+}
+
+static jdouble
+native_get_volume (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ jdouble volume;
+
+ if (!player)
+ return 1.0;
+
+ g_object_get (player->player, "volume", &volume, NULL);
+
+ return volume;
+}
+
+static void
+native_set_volume (JNIEnv * env, jobject thiz, jdouble volume)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ g_object_set (player->player, "volume", volume, NULL);
+}
+
+static jboolean
+native_get_mute (JNIEnv * env, jobject thiz)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ jboolean mute;
+
+ if (!player)
+ return FALSE;
+
+ g_object_get (player->player, "mute", &mute, NULL);
+
+ return mute;
+}
+
+static void
+native_set_mute (JNIEnv * env, jobject thiz, jboolean mute)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+
+ if (!player)
+ return;
+
+ g_object_set (player->player, "mute", mute, NULL);
+}
+
+static void
+native_set_surface (JNIEnv * env, jobject thiz, jobject surface)
+{
+ Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
+ ANativeWindow *new_native_window;
+
+ if (!player)
+ return;
+
+ new_native_window = surface ? ANativeWindow_fromSurface (env, surface) : NULL;
+ GST_DEBUG ("Received surface %p (native window %p)", surface,
+ new_native_window);
+
+ if (player->native_window) {
+ ANativeWindow_release (player->native_window);
+ }
+
+ player->native_window = new_native_window;
+ gst_player_video_overlay_video_renderer_set_window_handle
+ (GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (player->renderer),
+ (gpointer) new_native_window);
+}
+
+static void
+native_class_init (JNIEnv * env, jclass klass)
+{
+ native_player_field_id =
+ (*env)->GetFieldID (env, klass, "native_player", "J");
+ on_position_updated_method_id =
+ (*env)->GetMethodID (env, klass, "onPositionUpdated", "(J)V");
+ on_duration_changed_method_id =
+ (*env)->GetMethodID (env, klass, "onDurationChanged", "(J)V");
+ on_state_changed_method_id =
+ (*env)->GetMethodID (env, klass, "onStateChanged", "(I)V");
+ on_buffering_method_id =
+ (*env)->GetMethodID (env, klass, "onBuffering", "(I)V");
+ on_end_of_stream_method_id =
+ (*env)->GetMethodID (env, klass, "onEndOfStream", "()V");
+ on_error_method_id =
+ (*env)->GetMethodID (env, klass, "onError", "(ILjava/lang/String;)V");
+ on_video_dimensions_changed_method_id =
+ (*env)->GetMethodID (env, klass, "onVideoDimensionsChanged", "(II)V");
+
+ if (!native_player_field_id ||
+ !on_position_updated_method_id || !on_duration_changed_method_id ||
+ !on_state_changed_method_id || !on_buffering_method_id ||
+ !on_end_of_stream_method_id ||
+ !on_error_method_id || !on_video_dimensions_changed_method_id) {
+ static const gchar *message =
+ "The calling class does not implement all necessary interface methods";
+ jclass exception_class = (*env)->FindClass (env, "java/lang/Exception");
+ __android_log_print (ANDROID_LOG_ERROR, "GstPlayer", "%s", message);
+ (*env)->ThrowNew (env, exception_class, message);
+ }
+
+ gst_debug_set_threshold_for_name ("gst-player", GST_LEVEL_TRACE);
+}
+
+/* List of implemented native methods */
+static JNINativeMethod native_methods[] = {
+ {"nativeClassInit", "()V", (void *) native_class_init},
+ {"nativeNew", "()V", (void *) native_new},
+ {"nativePlay", "()V", (void *) native_play},
+ {"nativePause", "()V", (void *) native_pause},
+ {"nativeStop", "()V", (void *) native_stop},
+ {"nativeSeek", "(J)V", (void *) native_seek},
+ {"nativeFree", "()V", (void *) native_free},
+ {"nativeGetUri", "()Ljava/lang/String;", (void *) native_get_uri},
+ {"nativeSetUri", "(Ljava/lang/String;)V", (void *) native_set_uri},
+ {"nativeGetPosition", "()J", (void *) native_get_position},
+ {"nativeGetDuration", "()J", (void *) native_get_duration},
+ {"nativeGetVolume", "()D", (void *) native_get_volume},
+ {"nativeSetVolume", "(D)V", (void *) native_set_volume},
+ {"nativeGetMute", "()Z", (void *) native_get_mute},
+ {"nativeSetMute", "(Z)V", (void *) native_set_mute},
+ {"nativeSetSurface", "(Landroid/view/Surface;)V",
+ (void *) native_set_surface}
+};
+
+/* Library initializer */
+jint
+JNI_OnLoad (JavaVM * vm, void *reserved)
+{
+ JNIEnv *env = NULL;
+
+ java_vm = vm;
+
+ if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
+ "Could not retrieve JNIEnv");
+ return 0;
+ }
+ jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/Player");
+ if (!klass) {
+ __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
+ "Could not retrieve class org.freedesktop.gstreamer.Player");
+ return 0;
+ }
+ if ((*env)->RegisterNatives (env, klass, native_methods,
+ G_N_ELEMENTS (native_methods))) {
+ __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
+ "Could not register native methods for org.freedesktop.gstreamer.Player");
+ return 0;
+ }
+
+ pthread_key_create (&current_jni_env, detach_current_thread);
+
+ return JNI_VERSION_1_4;
+}