mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-06-01 07:08:12 +00:00
251 lines
10 KiB
Java
251 lines
10 KiB
Java
package org.freedesktop.gstreamer.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();
|
|
}
|
|
}
|