From 4b608c3ca7d325ea4ccea9ab13ba92b49463cc22 Mon Sep 17 00:00:00 2001 From: Xavi Artigas Date: Mon, 8 Oct 2012 15:36:16 +0200 Subject: [PATCH] Add comments. Whitespace. Some function renaming. --- .../android-tutorial-2/jni/Android.mk | 1 + .../android-tutorial-2/jni/tutorial-2.c | 48 +++++++++++++------ .../tutorial_2/Tutorial2.java | 37 ++++++++------ 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/gst-sdk/tutorials/android-tutorial-2/jni/Android.mk b/gst-sdk/tutorials/android-tutorial-2/jni/Android.mk index 68e586ce79..3f40633938 100644 --- a/gst-sdk/tutorials/android-tutorial-2/jni/Android.mk +++ b/gst-sdk/tutorials/android-tutorial-2/jni/Android.mk @@ -5,6 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := tutorial-2 LOCAL_SRC_FILES := tutorial-2.c LOCAL_SHARED_LIBRARIES := gstreamer_android +LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) ifndef GSTREAMER_SDK_ROOT diff --git a/gst-sdk/tutorials/android-tutorial-2/jni/tutorial-2.c b/gst-sdk/tutorials/android-tutorial-2/jni/tutorial-2.c index 08de737aa7..5b2d9241e8 100644 --- a/gst-sdk/tutorials/android-tutorial-2/jni/tutorial-2.c +++ b/gst-sdk/tutorials/android-tutorial-2/jni/tutorial-2.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -18,14 +19,16 @@ GST_DEBUG_CATEGORY_STATIC (debug_category); # define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)(jint)data) #endif +/* Structure to contain all our information, so we can pass it to callbacks */ typedef struct _CustomData { - jobject app; - GstElement *pipeline; - GMainContext *context; - GMainLoop *main_loop; - gboolean initialized; + jobject app; /* Application instance, used to call its methods. A global reference is kept. */ + GstElement *pipeline; /* The running pipeline */ + GMainContext *context; /* GLib context used to run the main loop */ + GMainLoop *main_loop; /* GLib main loop */ + gboolean initialized; /* To avoid informing the UI multiple times about the initialization */ } CustomData; +/* These global variables cache values which are not changing during execution */ static pthread_t gst_app_thread; static pthread_key_t current_jni_env; static JavaVM *java_vm; @@ -36,6 +39,8 @@ static jmethodID on_gstreamer_initialized_method_id; /* * Private methods */ + +/* Register this thread with the VM */ static JNIEnv *attach_current_thread (void) { JNIEnv *env; JavaVMAttachArgs args; @@ -53,11 +58,13 @@ static JNIEnv *attach_current_thread (void) { 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; @@ -69,6 +76,7 @@ static JNIEnv *get_jni_env (void) { return env; } +/* Change the content of the UI's TextView */ static void set_ui_message (const gchar *message, CustomData *data) { JNIEnv *env = get_jni_env (); GST_DEBUG ("Setting message to: %s", message); @@ -81,6 +89,7 @@ static void set_ui_message (const gchar *message, CustomData *data) { (*env)->DeleteLocalRef (env, jmessage); } +/* Retrieve errors from the bus and show them on the UI */ static void error_cb (GstBus *bus, GstMessage *msg, CustomData *data) { GError *err; gchar *debug_info; @@ -95,8 +104,8 @@ static void error_cb (GstBus *bus, GstMessage *msg, CustomData *data) { gst_element_set_state (data->pipeline, GST_STATE_NULL); } +/* Notify UI about pipeline state changes */ static void state_changed_cb (GstBus *bus, GstMessage *msg, CustomData *data) { - JNIEnv *env = get_jni_env (); GstState old_state, new_state, pending_state; gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state); /* Only pay attention to messages coming from the pipeline, not its children */ @@ -107,10 +116,10 @@ static void state_changed_cb (GstBus *bus, GstMessage *msg, CustomData *data) { } } +/* Check if all conditions are met to report GStreamer as initialized. + * These conditions will change depending on the application */ static void check_initialization_complete (CustomData *data) { JNIEnv *env = get_jni_env (); - /* Check if all conditions are met to report GStreamer as initialized. - * These conditions will change depending on the application */ if (!data->initialized && data->main_loop) { GST_DEBUG ("Initialization complete, notifying application. main_loop:%p", data->main_loop); (*env)->CallVoidMethod (env, data->app, on_gstreamer_initialized_method_id); @@ -122,6 +131,7 @@ static void check_initialization_complete (CustomData *data) { } } +/* Main method for the native code. This is executed on its own thread. */ static void *app_function (void *userdata) { JavaVMAttachArgs args; GstBus *bus; @@ -174,15 +184,19 @@ static void *app_function (void *userdata) { /* * Java Bindings */ + +/* Instruct the native code to create its internal data structure, pipeline and thread */ void gst_native_init (JNIEnv* env, jobject thiz) { CustomData *data = g_new0 (CustomData, 1); SET_CUSTOM_DATA (env, thiz, custom_data_field_id, data); + gst_debug_set_threshold_for_name("tutorial-2", GST_LEVEL_DEBUG); GST_DEBUG ("Created CustomData at %p", data); data->app = (*env)->NewGlobalRef (env, thiz); GST_DEBUG ("Created GlobalRef for app object at %p", data->app); pthread_create (&gst_app_thread, NULL, &app_function, data); } +/* Quit the main loop, remove the native thread and free resources */ void gst_native_finalize (JNIEnv* env, jobject thiz) { CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id); if (!data) return; @@ -198,6 +212,7 @@ void gst_native_finalize (JNIEnv* env, jobject thiz) { GST_DEBUG ("Done finalizing"); } +/* Set pipeline to PLAYING state */ void gst_native_play (JNIEnv* env, jobject thiz) { CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id); if (!data) return; @@ -205,6 +220,7 @@ void gst_native_play (JNIEnv* env, jobject thiz) { gst_element_set_state (data->pipeline, GST_STATE_PLAYING); } +/* Set pipeline to PAUSED state */ void gst_native_pause (JNIEnv* env, jobject thiz) { CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id); if (!data) return; @@ -212,30 +228,32 @@ void gst_native_pause (JNIEnv* env, jobject thiz) { gst_element_set_state (data->pipeline, GST_STATE_PAUSED); } -jboolean gst_class_init (JNIEnv* env, jclass klass) { - gst_debug_set_threshold_for_name("tutorial-2", GST_LEVEL_DEBUG); +/* Static class initializer: retrieve method and field IDs */ +jboolean gst_native_class_init (JNIEnv* env, jclass klass) { custom_data_field_id = (*env)->GetFieldID (env, klass, "native_custom_data", "J"); - GST_DEBUG ("The FieldID for the native_custom_data field is %p", custom_data_field_id); set_message_method_id = (*env)->GetMethodID (env, klass, "setMessage", "(Ljava/lang/String;)V"); - GST_DEBUG ("The MethodID for the setMessage method is %p", set_message_method_id); on_gstreamer_initialized_method_id = (*env)->GetMethodID (env, klass, "onGStreamerInitialized", "()V"); - GST_DEBUG ("The MethodID for the onGStreamerInitialized method is %p", on_gstreamer_initialized_method_id); if (!custom_data_field_id || !set_message_method_id || !on_gstreamer_initialized_method_id) { - GST_ERROR ("The calling class does not implement all necessary interface methods"); + /* We emit this message through the Android log instead of the GStreamer log because the later + * has not been initialized yet. + */ + __android_log_print (ANDROID_LOG_ERROR, "tutorial-2", "The calling class does not implement all necessary interface methods"); return JNI_FALSE; } return JNI_TRUE; } +/* List of implemented native methods */ static JNINativeMethod native_methods[] = { { "nativeInit", "()V", (void *) gst_native_init}, { "nativeFinalize", "()V", (void *) gst_native_finalize}, { "nativePlay", "()V", (void *) gst_native_play}, { "nativePause", "()V", (void *) gst_native_pause}, - { "classInit", "()Z", (void *) gst_class_init} + { "nativeClassInit", "()Z", (void *) gst_native_class_init} }; +/* Library initializer */ jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; diff --git a/gst-sdk/tutorials/android-tutorial-2/src/com/gst_sdk_tutorials/tutorial_2/Tutorial2.java b/gst-sdk/tutorials/android-tutorial-2/src/com/gst_sdk_tutorials/tutorial_2/Tutorial2.java index 6abc8d351f..9cbcb35efe 100644 --- a/gst-sdk/tutorials/android-tutorial-2/src/com/gst_sdk_tutorials/tutorial_2/Tutorial2.java +++ b/gst-sdk/tutorials/android-tutorial-2/src/com/gst_sdk_tutorials/tutorial_2/Tutorial2.java @@ -12,16 +12,16 @@ import android.widget.Toast; import com.gst_sdk.GStreamer; public class Tutorial2 extends Activity { - private native void nativeInit(); - private native void nativeFinalize(); - private native void nativePlay(); - private native void nativePause(); - private static native boolean classInit(); - private long native_custom_data; + private native void nativeInit(); /* Initialize native code, build pipeline, etc */ + private native void nativeFinalize(); /* Destroy pipeline and shutdown native code */ + private native void nativePlay(); /* Set pipeline to PLAYING */ + private native void nativePause(); /* Set pipeline to PAUSED */ + private static native boolean nativeClassInit(); /* Initialize native class: cache Method IDs for callbacks */ + private long native_custom_data; /* Native code will use this to keep private data */ - private boolean is_playing_desired; + private boolean is_playing_desired; /* Whether the user asked to go to PLAYING */ - private Bundle initialization_data; + private Bundle initialization_data; /* onCreate parameters kept for later */ /* Called when the activity is first created. */ @Override @@ -29,8 +29,9 @@ public class Tutorial2 extends Activity { { super.onCreate(savedInstanceState); + /* Initialize GStreamer and warn if it fails */ try { - GStreamer.init(this); + GStreamer.init(this); } catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); finish(); @@ -55,16 +56,19 @@ public class Tutorial2 extends Activity { } }); + /* Keep the instance state for later, since we will not perform our initialization + * until the native code reports that it is itself initialized. + */ initialization_data = savedInstanceState; - /* Start with disabled buttons, until GStreamer is initialized */ + /* Start with disabled buttons, until native code is initialized */ this.findViewById(R.id.button_play).setEnabled(false); this.findViewById(R.id.button_stop).setEnabled(false); is_playing_desired = false; nativeInit(); } - + protected void onSaveInstanceState (Bundle outState) { Log.d ("GStreamer", "Saving state, playing:" + is_playing_desired); outState.putBoolean("playing", is_playing_desired); @@ -75,7 +79,7 @@ public class Tutorial2 extends Activity { super.onDestroy(); } - /* Called from native code */ + /* Called from native code. This sets the content of the TextView from the UI thread. */ private void setMessage(final String message) { final TextView tv = (TextView) this.findViewById(R.id.textview_message); runOnUiThread (new Runnable() { @@ -85,8 +89,11 @@ public class Tutorial2 extends Activity { }); } - /* Called from native code */ - private void onGStreamerInitialized () { + /* Called from native code. Native code calls this once it has created its pipeline and + * the main loop is running, so it is ready to accept commands. + */ + private void onGStreamerInitialized () { + /* If initialization data is present, retrieve it */ if (initialization_data != null) { is_playing_desired = initialization_data.getBoolean("playing"); Log.i ("GStreamer", "Restoring state, playing:" + is_playing_desired); @@ -107,7 +114,7 @@ public class Tutorial2 extends Activity { static { System.loadLibrary("gstreamer_android"); System.loadLibrary("tutorial-2"); - classInit(); + nativeClassInit(); } }