diff --git a/gst-sdk/tutorials/android-tutorial-4/AndroidManifest.xml b/gst-sdk/tutorials/android-tutorial-4/AndroidManifest.xml
index f09dd03933..3af467bd6e 100644
--- a/gst-sdk/tutorials/android-tutorial-4/AndroidManifest.xml
+++ b/gst-sdk/tutorials/android-tutorial-4/AndroidManifest.xml
@@ -15,8 +15,5 @@
-
-
diff --git a/gst-sdk/tutorials/android-tutorial-4/jni/Android.mk b/gst-sdk/tutorials/android-tutorial-4/jni/Android.mk
index ea48c78cd2..9d78b4214d 100644
--- a/gst-sdk/tutorials/android-tutorial-4/jni/Android.mk
+++ b/gst-sdk/tutorials/android-tutorial-4/jni/Android.mk
@@ -16,5 +16,5 @@ GSTREAMER_SDK_ROOT := $(GSTREAMER_SDK_ROOT_ANDROID)
endif
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_SDK_ROOT)/share/gst-android/ndk-build/
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
-GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_EFFECTS)
+GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_SYS)
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer.mk
diff --git a/gst-sdk/tutorials/android-tutorial-4/jni/tutorial-4.c b/gst-sdk/tutorials/android-tutorial-4/jni/tutorial-4.c
index e739623c2a..115389548f 100644
--- a/gst-sdk/tutorials/android-tutorial-4/jni/tutorial-4.c
+++ b/gst-sdk/tutorials/android-tutorial-4/jni/tutorial-4.c
@@ -30,7 +30,6 @@ typedef struct _CustomData {
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 */
- GstElement *video_sink; /* The video sink element which receives XOverlay commands */
ANativeWindow *native_window; /* The Android native window where video will be rendered */
} CustomData;
@@ -41,6 +40,7 @@ static JavaVM *java_vm;
static jfieldID custom_data_field_id;
static jmethodID set_message_method_id;
static jmethodID on_gstreamer_initialized_method_id;
+static jmethodID on_media_size_changed_method_id;
/*
* Private methods
@@ -122,6 +122,31 @@ static void state_changed_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
}
}
+/* Called when Pad Caps change on the video sink */
+static void caps_cb (GstPad *pad, GParamSpec *pspec, CustomData *data) {
+ JNIEnv *env = get_jni_env ();
+ GstVideoFormat fmt;
+ int width;
+ int height;
+ GstCaps *caps;
+
+ caps = gst_pad_get_negotiated_caps (pad);
+ if (gst_video_format_parse_caps(caps, &fmt, &width, &height)) {
+ int par_n, par_d;
+ if (gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) {
+ width = width * par_n / par_d;
+ }
+ GST_DEBUG ("Media size changed to %dx%d", width, height);
+
+ (*env)->CallVoidMethod (env, data->app, on_media_size_changed_method_id, (jint)width, (jint)height);
+ if ((*env)->ExceptionCheck (env)) {
+ GST_ERROR ("Failed to call Java method");
+ (*env)->ExceptionClear (env);
+ }
+ }
+ gst_caps_unref(caps);
+}
+
/* 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) {
@@ -130,7 +155,7 @@ static void check_initialization_complete (CustomData *data) {
GST_DEBUG ("Initialization complete, notifying application. native_window:%p main_loop:%p", data->native_window, data->main_loop);
/* The main loop is running and we received a native window, inform the sink about it */
- gst_x_overlay_set_window_handle (GST_X_OVERLAY (data->video_sink), (guintptr)data->native_window);
+ gst_x_overlay_set_window_handle (GST_X_OVERLAY (data->pipeline), (guintptr)data->native_window);
(*env)->CallVoidMethod (env, data->app, on_gstreamer_initialized_method_id);
if ((*env)->ExceptionCheck (env)) {
@@ -147,7 +172,7 @@ static void *app_function (void *userdata) {
GstBus *bus;
CustomData *data = (CustomData *)userdata;
GSource *bus_source;
- GError *error = NULL;
+ guint flags;
GST_DEBUG ("Creating pipeline in CustomData at %p", data);
@@ -156,24 +181,19 @@ static void *app_function (void *userdata) {
g_main_context_push_thread_default(data->context);
/* Build pipeline */
- data->pipeline = gst_parse_launch("videotestsrc ! warptv ! ffmpegcolorspace ! autovideosink", &error);
- if (error) {
- gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message);
- g_clear_error (&error);
- set_ui_message(message, data);
- g_free (message);
+ data->pipeline = gst_element_factory_make ("playbin2", NULL);
+ if (!data->pipeline) {
+ set_ui_message("Unable to build pipeline", data);
return NULL;
}
+ g_object_get (data->pipeline, "flags", &flags, NULL);
+ /* Disable subtitles for now */
+ flags &= ~0x00000004;
+ g_object_set (data->pipeline, "flags", flags, NULL);
/* Set the pipeline to READY, so it can already accept a window handle, if we have one */
gst_element_set_state(data->pipeline, GST_STATE_READY);
- data->video_sink = gst_bin_get_by_interface(GST_BIN(data->pipeline), GST_TYPE_X_OVERLAY);
- if (!data->video_sink) {
- GST_ERROR ("Could not retrieve video sink");
- return NULL;
- }
-
/* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
bus = gst_element_get_bus (data->pipeline);
bus_source = gst_bus_create_watch (bus);
@@ -197,7 +217,6 @@ static void *app_function (void *userdata) {
g_main_context_pop_thread_default(data->context);
g_main_context_unref (data->context);
gst_element_set_state (data->pipeline, GST_STATE_NULL);
- gst_object_unref (data->video_sink);
gst_object_unref (data->pipeline);
return NULL;
@@ -235,6 +254,16 @@ static void gst_native_finalize (JNIEnv* env, jobject thiz) {
GST_DEBUG ("Done finalizing");
}
+void gst_native_set_uri (JNIEnv* env, jobject thiz, jstring uri) {
+ CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id);
+ if (!data || !data->pipeline) return;
+ const jbyte *char_uri = (*env)->GetStringUTFChars (env, uri, NULL);
+ GST_DEBUG ("Setting URI to %s", char_uri);
+ gst_element_set_state (data->pipeline, GST_STATE_READY);
+ g_object_set(data->pipeline, "uri", char_uri, NULL);
+ (*env)->ReleaseStringUTFChars (env, uri, char_uri);
+}
+
/* Set pipeline to PLAYING state */
static void gst_native_play (JNIEnv* env, jobject thiz) {
CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id);
@@ -256,8 +285,10 @@ static jboolean gst_native_class_init (JNIEnv* env, jclass klass) {
custom_data_field_id = (*env)->GetFieldID (env, klass, "native_custom_data", "J");
set_message_method_id = (*env)->GetMethodID (env, klass, "setMessage", "(Ljava/lang/String;)V");
on_gstreamer_initialized_method_id = (*env)->GetMethodID (env, klass, "onGStreamerInitialized", "()V");
+ on_media_size_changed_method_id = (*env)->GetMethodID (env, klass, "onMediaSizeChanged", "(II)V");
- if (!custom_data_field_id || !set_message_method_id || !on_gstreamer_initialized_method_id) {
+ if (!custom_data_field_id || !set_message_method_id || !on_gstreamer_initialized_method_id ||
+ !on_media_size_changed_method_id) {
/* We emit this message through the Android log instead of the GStreamer log because the later
* has not been initialized yet.
*/
@@ -277,9 +308,9 @@ static void gst_native_surface_init (JNIEnv *env, jobject thiz, jobject surface)
ANativeWindow_release (data->native_window);
if (data->native_window == new_native_window) {
GST_DEBUG ("New native window is the same as the previous one", data->native_window);
- if (data->video_sink) {
- gst_x_overlay_expose(GST_X_OVERLAY (data->video_sink));
- gst_x_overlay_expose(GST_X_OVERLAY (data->video_sink));
+ if (data->pipeline) {
+ gst_x_overlay_expose(GST_X_OVERLAY (data->pipeline));
+ gst_x_overlay_expose(GST_X_OVERLAY (data->pipeline));
}
return;
} else {
@@ -297,8 +328,8 @@ static void gst_native_surface_finalize (JNIEnv *env, jobject thiz) {
if (!data) return;
GST_DEBUG ("Releasing Native Window %p", data->native_window);
- if (data->video_sink) {
- gst_x_overlay_set_window_handle (GST_X_OVERLAY (data->video_sink), (guintptr)NULL);
+ if (data->pipeline) {
+ gst_x_overlay_set_window_handle (GST_X_OVERLAY (data->pipeline), (guintptr)NULL);
gst_element_set_state (data->pipeline, GST_STATE_READY);
}
@@ -311,6 +342,7 @@ static void gst_native_surface_finalize (JNIEnv *env, jobject thiz) {
static JNINativeMethod native_methods[] = {
{ "nativeInit", "()V", (void *) gst_native_init},
{ "nativeFinalize", "()V", (void *) gst_native_finalize},
+ { "nativeSetUri", "(Ljava/lang/String;)V", (void *) gst_native_set_uri},
{ "nativePlay", "()V", (void *) gst_native_play},
{ "nativePause", "()V", (void *) gst_native_pause},
{ "nativeSurfaceInit", "(Ljava/lang/Object;)V", (void *) gst_native_surface_init},
diff --git a/gst-sdk/tutorials/android-tutorial-4/res/layout/main.xml b/gst-sdk/tutorials/android-tutorial-4/res/layout/main.xml
index c3baac85a6..e7329d218e 100644
--- a/gst-sdk/tutorials/android-tutorial-4/res/layout/main.xml
+++ b/gst-sdk/tutorials/android-tutorial-4/res/layout/main.xml
@@ -2,16 +2,21 @@
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dip"
+ android:gravity="center_horizontal" />
-
-
+
+
+
-
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal" />
-
+
\ No newline at end of file
diff --git a/gst-sdk/tutorials/android-tutorial-4/res/values/strings.xml b/gst-sdk/tutorials/android-tutorial-4/res/values/strings.xml
index 99d0a0cb5d..d26d1e6e86 100644
--- a/gst-sdk/tutorials/android-tutorial-4/res/values/strings.xml
+++ b/gst-sdk/tutorials/android-tutorial-4/res/values/strings.xml
@@ -1,18 +1,6 @@
- Android tutorial 4
+ GStreamer tutorial 4
Play
Stop
- Select
- Android tutorial 1
- Location
- folder can\'t be read!
- New
- Select
- File name:
- Cancel
- Save
- No Data
- #ffff0000
- Error
diff --git a/gst-sdk/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java b/gst-sdk/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java
index edefde331f..3b3c6d1b23 100644
--- a/gst-sdk/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java
+++ b/gst-sdk/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java
@@ -16,6 +16,7 @@ import com.gstreamer.GStreamer;
public class Tutorial4 extends Activity implements SurfaceHolder.Callback {
private native void nativeInit(); // Initialize native code, build pipeline, etc
private native void nativeFinalize(); // Destroy pipeline and shutdown native code
+ private native void nativeSetUri(String uri); // Set the URI of the media to play
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
@@ -25,6 +26,8 @@ public class Tutorial4 extends Activity implements SurfaceHolder.Callback {
private boolean is_playing_desired; // Whether the user asked to go to PLAYING
+ private String mediaUri = "http://docs.gstreamer.com/media/sintel_trailer-480p.ogv";
+
// Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
@@ -102,6 +105,7 @@ public class Tutorial4 extends Activity implements SurfaceHolder.Callback {
private void onGStreamerInitialized () {
Log.i ("GStreamer", "Gst initialized. Restoring state, playing:" + is_playing_desired);
// Restore previous playing state
+ nativeSetUri (mediaUri);
if (is_playing_desired) {
nativePlay();
} else {
@@ -140,4 +144,16 @@ public class Tutorial4 extends Activity implements SurfaceHolder.Callback {
nativeSurfaceFinalize ();
}
+ private void onMediaSizeChanged (int width, int height) {
+ Log.i ("GStreamer", "Media size changed to " + width + "x" + height);
+ final GStreamerSurfaceView gsv = (GStreamerSurfaceView) this.findViewById(R.id.surface_video);
+ gsv.media_width = width;
+ gsv.media_height = height;
+ runOnUiThread(new Runnable() {
+ public void run() {
+ gsv.requestLayout();
+ }
+ });
+ }
+
}