diff options
Diffstat (limited to 'tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java')
-rw-r--r-- | tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java b/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java new file mode 100644 index 0000000..f3d5977 --- /dev/null +++ b/tutorials/android-tutorial-4/src/com/gst_sdk_tutorials/tutorial_4/Tutorial4.java @@ -0,0 +1,250 @@ +package com.gst_sdk_tutorials.tutorial_4; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; +import android.widget.Toast; + +import org.freedesktop.gstreamer.GStreamer; + +public class Tutorial4 extends Activity implements SurfaceHolder.Callback, OnSeekBarChangeListener { + 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 nativeSetPosition(int milliseconds); // Seek to the indicated position, in milliseconds + private native void nativePause(); // Set pipeline to PAUSED + private static native boolean nativeClassInit(); // Initialize native class: cache Method IDs for callbacks + private native void nativeSurfaceInit(Object surface); // A new surface is available + private native void nativeSurfaceFinalize(); // Surface about to be destroyed + private long native_custom_data; // Native code will use this to keep private data + + private boolean is_playing_desired; // Whether the user asked to go to PLAYING + private int position; // Current position, reported by native code + private int duration; // Current clip duration, reported by native code + private boolean is_local_media; // Whether this clip is stored locally or is being streamed + private int desired_position; // Position where the users wants to seek to + private String mediaUri; // URI of the clip being played + + private final String defaultMediaUri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv"; + + // Called when the activity is first created. + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + // Initialize GStreamer and warn if it fails + try { + GStreamer.init(this); + } catch (Exception e) { + Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); + finish(); + return; + } + + setContentView(R.layout.main); + + ImageButton play = (ImageButton) this.findViewById(R.id.button_play); + play.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + is_playing_desired = true; + nativePlay(); + } + }); + + ImageButton pause = (ImageButton) this.findViewById(R.id.button_stop); + pause.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + is_playing_desired = false; + nativePause(); + } + }); + + SurfaceView sv = (SurfaceView) this.findViewById(R.id.surface_video); + SurfaceHolder sh = sv.getHolder(); + sh.addCallback(this); + + SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar); + sb.setOnSeekBarChangeListener(this); + + // Retrieve our previous state, or initialize it to default values + if (savedInstanceState != null) { + is_playing_desired = savedInstanceState.getBoolean("playing"); + position = savedInstanceState.getInt("position"); + duration = savedInstanceState.getInt("duration"); + mediaUri = savedInstanceState.getString("mediaUri"); + Log.i ("GStreamer", "Activity created with saved state:"); + } else { + is_playing_desired = false; + position = duration = 0; + mediaUri = defaultMediaUri; + Log.i ("GStreamer", "Activity created with no saved state:"); + } + is_local_media = false; + Log.i ("GStreamer", " playing:" + is_playing_desired + " position:" + position + + " duration: " + duration + " uri: " + mediaUri); + + // 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); + + nativeInit(); + } + + protected void onSaveInstanceState (Bundle outState) { + Log.d ("GStreamer", "Saving state, playing:" + is_playing_desired + " position:" + position + + " duration: " + duration + " uri: " + mediaUri); + outState.putBoolean("playing", is_playing_desired); + outState.putInt("position", position); + outState.putInt("duration", duration); + outState.putString("mediaUri", mediaUri); + } + + protected void onDestroy() { + nativeFinalize(); + super.onDestroy(); + } + + // 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() { + public void run() { + tv.setText(message); + } + }); + } + + // Set the URI to play, and record whether it is a local or remote file + private void setMediaUri() { + nativeSetUri (mediaUri); + is_local_media = mediaUri.startsWith("file://"); + } + + // 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 () { + Log.i ("GStreamer", "GStreamer initialized:"); + Log.i ("GStreamer", " playing:" + is_playing_desired + " position:" + position + " uri: " + mediaUri); + + // Restore previous playing state + setMediaUri (); + nativeSetPosition (position); + if (is_playing_desired) { + nativePlay(); + } else { + nativePause(); + } + + // Re-enable buttons, now that GStreamer is initialized + final Activity activity = this; + runOnUiThread(new Runnable() { + public void run() { + activity.findViewById(R.id.button_play).setEnabled(true); + activity.findViewById(R.id.button_stop).setEnabled(true); + } + }); + } + + // The text widget acts as an slave for the seek bar, so it reflects what the seek bar shows, whether + // it is an actual pipeline position or the position the user is currently dragging to. + private void updateTimeWidget () { + final TextView tv = (TextView) this.findViewById(R.id.textview_time); + final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar); + final int pos = sb.getProgress(); + + SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + final String message = df.format(new Date (pos)) + " / " + df.format(new Date (duration)); + tv.setText(message); + } + + // Called from native code + private void setCurrentPosition(final int position, final int duration) { + final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar); + + // Ignore position messages from the pipeline if the seek bar is being dragged + if (sb.isPressed()) return; + + runOnUiThread (new Runnable() { + public void run() { + sb.setMax(duration); + sb.setProgress(position); + updateTimeWidget(); + } + }); + this.position = position; + this.duration = duration; + } + + static { + System.loadLibrary("gstreamer_android"); + System.loadLibrary("tutorial-4"); + nativeClassInit(); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + Log.d("GStreamer", "Surface changed to format " + format + " width " + + width + " height " + height); + nativeSurfaceInit (holder.getSurface()); + } + + public void surfaceCreated(SurfaceHolder holder) { + Log.d("GStreamer", "Surface created: " + holder.getSurface()); + } + + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d("GStreamer", "Surface destroyed"); + nativeSurfaceFinalize (); + } + + // Called from native code when the size of the media changes or is first detected. + // Inform the video surface about the new size and recalculate the layout. + 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(); + } + }); + } + + // The Seek Bar thumb has moved, either because the user dragged it or we have called setProgress() + public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) { + if (fromUser == false) return; + desired_position = progress; + // If this is a local file, allow scrub seeking, this is, seek as soon as the slider is moved. + if (is_local_media) nativeSetPosition(desired_position); + updateTimeWidget(); + } + + // The user started dragging the Seek Bar thumb + public void onStartTrackingTouch(SeekBar sb) { + nativePause(); + } + + // The user released the Seek Bar thumb + public void onStopTrackingTouch(SeekBar sb) { + // If this is a remote file, scrub seeking is probably not going to work smoothly enough. + // Therefore, perform only the seek when the slider is released. + if (!is_local_media) nativeSetPosition(desired_position); + if (is_playing_desired) nativePlay(); + } +} |