3 * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
24 #include <android/log.h>
25 #include <android/native_window.h>
26 #include <android/native_window_jni.h>
28 #include <gst/player/gstplayer.h>
29 #include <gst/player/gstplayer-video-overlay-video-renderer.h>
31 GST_DEBUG_CATEGORY_STATIC (debug_category);
32 #define GST_CAT_DEFAULT debug_category
34 #define GET_CUSTOM_DATA(env, thiz, fieldID) (Player *)(gintptr)(*env)->GetLongField (env, thiz, fieldID)
35 #define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)(gintptr)data)
37 typedef struct _Player
41 GstPlayerVideoRenderer *renderer;
42 ANativeWindow *native_window;
45 static pthread_key_t current_jni_env;
46 static JavaVM *java_vm;
47 static jfieldID native_player_field_id;
48 static jmethodID on_position_updated_method_id;
49 static jmethodID on_duration_changed_method_id;
50 static jmethodID on_state_changed_method_id;
51 static jmethodID on_buffering_method_id;
52 static jmethodID on_end_of_stream_method_id;
53 static jmethodID on_error_method_id;
54 static jmethodID on_video_dimensions_changed_method_id;
56 /* Register this thread with the VM */
58 attach_current_thread (void)
61 JavaVMAttachArgs args;
63 GST_DEBUG ("Attaching thread %p", g_thread_self ());
64 args.version = JNI_VERSION_1_4;
68 if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) {
69 GST_ERROR ("Failed to attach current thread");
76 /* Unregister this thread from the VM */
78 detach_current_thread (void *env)
80 GST_DEBUG ("Detaching thread %p", g_thread_self ());
81 (*java_vm)->DetachCurrentThread (java_vm);
84 /* Retrieve the JNI environment for this thread */
90 if ((env = pthread_getspecific (current_jni_env)) == NULL) {
91 env = attach_current_thread ();
92 pthread_setspecific (current_jni_env, env);
102 on_position_updated (GstPlayer * unused, GstClockTime position, Player * player)
104 JNIEnv *env = get_jni_env ();
106 (*env)->CallVoidMethod (env, player->java_player,
107 on_position_updated_method_id, position);
108 if ((*env)->ExceptionCheck (env)) {
109 (*env)->ExceptionDescribe (env);
110 (*env)->ExceptionClear (env);
115 on_duration_changed (GstPlayer * unused, GstClockTime duration, Player * player)
117 JNIEnv *env = get_jni_env ();
119 (*env)->CallVoidMethod (env, player->java_player,
120 on_duration_changed_method_id, duration);
121 if ((*env)->ExceptionCheck (env)) {
122 (*env)->ExceptionDescribe (env);
123 (*env)->ExceptionClear (env);
128 on_state_changed (GstPlayer * unused, GstPlayerState state, Player * player)
130 JNIEnv *env = get_jni_env ();
132 (*env)->CallVoidMethod (env, player->java_player,
133 on_state_changed_method_id, state);
134 if ((*env)->ExceptionCheck (env)) {
135 (*env)->ExceptionDescribe (env);
136 (*env)->ExceptionClear (env);
141 on_buffering (GstPlayer * unused, gint percent, Player * player)
143 JNIEnv *env = get_jni_env ();
145 (*env)->CallVoidMethod (env, player->java_player,
146 on_buffering_method_id, percent);
147 if ((*env)->ExceptionCheck (env)) {
148 (*env)->ExceptionDescribe (env);
149 (*env)->ExceptionClear (env);
154 on_end_of_stream (GstPlayer * unused, Player * player)
156 JNIEnv *env = get_jni_env ();
158 (*env)->CallVoidMethod (env, player->java_player, on_end_of_stream_method_id);
159 if ((*env)->ExceptionCheck (env)) {
160 (*env)->ExceptionDescribe (env);
161 (*env)->ExceptionClear (env);
166 on_error (GstPlayer * unused, GError * err, Player * player)
168 JNIEnv *env = get_jni_env ();
171 error_msg = (*env)->NewStringUTF (env, err->message);
173 (*env)->CallVoidMethod (env, player->java_player, on_error_method_id,
174 err->code, error_msg);
175 if ((*env)->ExceptionCheck (env)) {
176 (*env)->ExceptionDescribe (env);
177 (*env)->ExceptionClear (env);
180 (*env)->DeleteLocalRef (env, error_msg);
184 on_video_dimensions_changed (GstPlayer * unused, gint width, gint height,
187 JNIEnv *env = get_jni_env ();
189 (*env)->CallVoidMethod (env, player->java_player,
190 on_video_dimensions_changed_method_id, width, height);
191 if ((*env)->ExceptionCheck (env)) {
192 (*env)->ExceptionDescribe (env);
193 (*env)->ExceptionClear (env);
198 native_new (JNIEnv * env, jobject thiz)
200 Player *player = g_new0 (Player, 1);
202 player->renderer = gst_player_video_overlay_video_renderer_new (NULL);
203 player->player = gst_player_new (player->renderer, NULL);
204 SET_CUSTOM_DATA (env, thiz, native_player_field_id, player);
205 player->java_player = (*env)->NewGlobalRef (env, thiz);
207 g_signal_connect (player->player, "position-updated",
208 G_CALLBACK (on_position_updated), player);
209 g_signal_connect (player->player, "duration-changed",
210 G_CALLBACK (on_duration_changed), player);
211 g_signal_connect (player->player, "state-changed",
212 G_CALLBACK (on_state_changed), player);
213 g_signal_connect (player->player, "buffering",
214 G_CALLBACK (on_buffering), player);
215 g_signal_connect (player->player, "end-of-stream",
216 G_CALLBACK (on_end_of_stream), player);
217 g_signal_connect (player->player, "error", G_CALLBACK (on_error), player);
218 g_signal_connect (player->player, "video-dimensions-changed",
219 G_CALLBACK (on_video_dimensions_changed), player);
223 native_free (JNIEnv * env, jobject thiz)
225 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
230 g_object_unref (player->player);
231 (*env)->DeleteGlobalRef (env, player->java_player);
233 SET_CUSTOM_DATA (env, thiz, native_player_field_id, NULL);
237 native_play (JNIEnv * env, jobject thiz)
239 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
244 gst_player_play (player->player);
248 native_pause (JNIEnv * env, jobject thiz)
250 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
255 gst_player_pause (player->player);
259 native_stop (JNIEnv * env, jobject thiz)
261 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
266 gst_player_stop (player->player);
270 native_seek (JNIEnv * env, jobject thiz, jlong position)
272 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
277 gst_player_seek (player->player, position);
281 native_set_uri (JNIEnv * env, jobject thiz, jobject uri)
283 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
284 const gchar *uri_str;
289 uri_str = (*env)->GetStringUTFChars (env, uri, NULL);
290 g_object_set (player->player, "uri", uri_str, NULL);
291 (*env)->ReleaseStringUTFChars (env, uri, uri_str);
295 native_get_uri (JNIEnv * env, jobject thiz)
297 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
304 g_object_get (player->player, "uri", &uri_str, NULL);
306 uri = (*env)->NewStringUTF (env, uri_str);
313 native_get_position (JNIEnv * env, jobject thiz)
315 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
321 g_object_get (player->player, "position", &position, NULL);
327 native_get_duration (JNIEnv * env, jobject thiz)
329 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
335 g_object_get (player->player, "duration", &duration, NULL);
341 native_get_volume (JNIEnv * env, jobject thiz)
343 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
349 g_object_get (player->player, "volume", &volume, NULL);
355 native_set_volume (JNIEnv * env, jobject thiz, jdouble volume)
357 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
362 g_object_set (player->player, "volume", volume, NULL);
366 native_get_mute (JNIEnv * env, jobject thiz)
368 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
374 g_object_get (player->player, "mute", &mute, NULL);
380 native_set_mute (JNIEnv * env, jobject thiz, jboolean mute)
382 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
387 g_object_set (player->player, "mute", mute, NULL);
391 native_set_surface (JNIEnv * env, jobject thiz, jobject surface)
393 Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
394 ANativeWindow *new_native_window;
399 new_native_window = surface ? ANativeWindow_fromSurface (env, surface) : NULL;
400 GST_DEBUG ("Received surface %p (native window %p)", surface,
403 if (player->native_window) {
404 ANativeWindow_release (player->native_window);
407 player->native_window = new_native_window;
408 gst_player_video_overlay_video_renderer_set_window_handle
409 (GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (player->renderer),
410 (gpointer) new_native_window);
411 gst_player_video_overlay_video_renderer_expose
412 (GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (player->renderer));
416 native_class_init (JNIEnv * env, jclass klass)
418 native_player_field_id =
419 (*env)->GetFieldID (env, klass, "native_player", "J");
420 on_position_updated_method_id =
421 (*env)->GetMethodID (env, klass, "onPositionUpdated", "(J)V");
422 on_duration_changed_method_id =
423 (*env)->GetMethodID (env, klass, "onDurationChanged", "(J)V");
424 on_state_changed_method_id =
425 (*env)->GetMethodID (env, klass, "onStateChanged", "(I)V");
426 on_buffering_method_id =
427 (*env)->GetMethodID (env, klass, "onBuffering", "(I)V");
428 on_end_of_stream_method_id =
429 (*env)->GetMethodID (env, klass, "onEndOfStream", "()V");
431 (*env)->GetMethodID (env, klass, "onError", "(ILjava/lang/String;)V");
432 on_video_dimensions_changed_method_id =
433 (*env)->GetMethodID (env, klass, "onVideoDimensionsChanged", "(II)V");
435 if (!native_player_field_id ||
436 !on_position_updated_method_id || !on_duration_changed_method_id ||
437 !on_state_changed_method_id || !on_buffering_method_id ||
438 !on_end_of_stream_method_id ||
439 !on_error_method_id || !on_video_dimensions_changed_method_id) {
440 static const gchar *message =
441 "The calling class does not implement all necessary interface methods";
442 jclass exception_class = (*env)->FindClass (env, "java/lang/Exception");
443 __android_log_print (ANDROID_LOG_ERROR, "GstPlayer", "%s", message);
444 (*env)->ThrowNew (env, exception_class, message);
447 gst_debug_set_threshold_for_name ("gst-player", GST_LEVEL_TRACE);
450 /* List of implemented native methods */
451 static JNINativeMethod native_methods[] = {
452 {"nativeClassInit", "()V", (void *) native_class_init},
453 {"nativeNew", "()V", (void *) native_new},
454 {"nativePlay", "()V", (void *) native_play},
455 {"nativePause", "()V", (void *) native_pause},
456 {"nativeStop", "()V", (void *) native_stop},
457 {"nativeSeek", "(J)V", (void *) native_seek},
458 {"nativeFree", "()V", (void *) native_free},
459 {"nativeGetUri", "()Ljava/lang/String;", (void *) native_get_uri},
460 {"nativeSetUri", "(Ljava/lang/String;)V", (void *) native_set_uri},
461 {"nativeGetPosition", "()J", (void *) native_get_position},
462 {"nativeGetDuration", "()J", (void *) native_get_duration},
463 {"nativeGetVolume", "()D", (void *) native_get_volume},
464 {"nativeSetVolume", "(D)V", (void *) native_set_volume},
465 {"nativeGetMute", "()Z", (void *) native_get_mute},
466 {"nativeSetMute", "(Z)V", (void *) native_set_mute},
467 {"nativeSetSurface", "(Landroid/view/Surface;)V",
468 (void *) native_set_surface}
471 /* Library initializer */
473 JNI_OnLoad (JavaVM * vm, void *reserved)
479 if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
480 __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
481 "Could not retrieve JNIEnv");
484 jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/Player");
486 __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
487 "Could not retrieve class org.freedesktop.gstreamer.Player");
490 if ((*env)->RegisterNatives (env, klass, native_methods,
491 G_N_ELEMENTS (native_methods))) {
492 __android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
493 "Could not register native methods for org.freedesktop.gstreamer.Player");
497 pthread_key_create (¤t_jni_env, detach_current_thread);
499 return JNI_VERSION_1_4;