diff options
Diffstat (limited to 'playback/player/android/app/src/main/java')
3 files changed, 547 insertions, 0 deletions
diff --git a/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/Player.java b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/Player.java new file mode 100644 index 0000000..e2bef8c --- /dev/null +++ b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/Player.java @@ -0,0 +1,241 @@ +/* GStreamer + * + * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.freedesktop.gstreamer; + +import java.io.Closeable; +import android.view.Surface; +import android.content.Context; +import org.freedesktop.gstreamer.GStreamer; + +public class Player implements Closeable { + private static native void nativeClassInit(); + public static void init(Context context) throws Exception { + System.loadLibrary("gstreamer_android"); + GStreamer.init(context); + + System.loadLibrary("gstplayer"); + nativeClassInit(); + } + + private long native_player; + private native void nativeNew(); + public Player() { + nativeNew(); + } + + private native void nativeFree(); + @Override + public void close() { + nativeFree(); + } + + private native void nativePlay(); + public void play() { + nativePlay(); + } + + private native void nativePause(); + public void pause() { + nativePause(); + } + + private native void nativeStop(); + public void stop() { + nativeStop(); + } + + private native void nativeSeek(long position); + public void seek(long position) { + nativeSeek(position); + } + + private native String nativeGetUri(); + public String getUri() { + return nativeGetUri(); + } + + private native void nativeSetUri(String uri); + public void setUri(String uri) { + nativeSetUri(uri); + } + + private native long nativeGetPosition(); + public long getPosition() { + return nativeGetPosition(); + } + + private native long nativeGetDuration(); + public long getDuration() { + return nativeGetDuration(); + } + + private native double nativeGetVolume(); + public double getVolume() { + return nativeGetVolume(); + } + + private native void nativeSetVolume(double volume); + public void setVolume(double volume) { + nativeSetVolume(volume); + } + + private native boolean nativeGetMute(); + public boolean getMute() { + return nativeGetMute(); + } + + private native void nativeSetMute(boolean mute); + public void setMute(boolean mute) { + nativeSetMute(mute); + } + + private Surface surface; + private native void nativeSetSurface(Surface surface); + public void setSurface(Surface surface) { + this.surface = surface; + nativeSetSurface(surface); + } + + public Surface getSurface() { + return surface; + } + + public static interface PositionUpdatedListener { + abstract void positionUpdated(Player player, long position); + } + + private PositionUpdatedListener positionUpdatedListener; + public void setPositionUpdatedListener(PositionUpdatedListener listener) { + positionUpdatedListener = listener; + } + + private void onPositionUpdated(long position) { + if (positionUpdatedListener != null) { + positionUpdatedListener.positionUpdated(this, position); + } + } + + public static interface DurationChangedListener { + abstract void durationChanged(Player player, long duration); + } + + private DurationChangedListener durationChangedListener; + public void setDurationChangedListener(DurationChangedListener listener) { + durationChangedListener = listener; + } + + private void onDurationChanged(long duration) { + if (durationChangedListener != null) { + durationChangedListener.durationChanged(this, duration); + } + } + + private static final State[] stateMap = {State.STOPPED, State.BUFFERING, State.PAUSED, State.PLAYING}; + public enum State { + STOPPED, + BUFFERING, + PAUSED, + PLAYING + } + + public static interface StateChangedListener { + abstract void stateChanged(Player player, State state); + } + + private StateChangedListener stateChangedListener; + public void setStateChangedListener(StateChangedListener listener) { + stateChangedListener = listener; + } + + private void onStateChanged(int stateIdx) { + if (stateChangedListener != null) { + State state = stateMap[stateIdx]; + stateChangedListener.stateChanged(this, state); + } + } + + public static interface BufferingListener { + abstract void buffering(Player player, int percent); + } + + private BufferingListener bufferingListener; + public void setBufferingListener(BufferingListener listener) { + bufferingListener = listener; + } + + private void onBuffering(int percent) { + if (bufferingListener != null) { + bufferingListener.buffering(this, percent); + } + } + + public static interface EndOfStreamListener { + abstract void endOfStream(Player player); + } + + private EndOfStreamListener endOfStreamListener; + public void setEndOfStreamListener(EndOfStreamListener listener) { + endOfStreamListener = listener; + } + + private void onEndOfStream() { + if (endOfStreamListener != null) { + endOfStreamListener.endOfStream(this); + } + } + + // Keep these in sync with gstplayer.h + private static final Error[] errorMap = {Error.FAILED}; + public enum Error { + FAILED + } + + public static interface ErrorListener { + abstract void error(Player player, Error error, String errorMessage); + } + + private ErrorListener errorListener; + public void setErrorListener(ErrorListener listener) { + errorListener = listener; + } + + private void onError(int errorCode, String errorMessage) { + if (errorListener != null) { + Error error = errorMap[errorCode]; + errorListener.error(this, error, errorMessage); + } + } + + public static interface VideoDimensionsChangedListener { + abstract void videoDimensionsChanged(Player player, int width, int height); + } + + private VideoDimensionsChangedListener videoDimensionsChangedListener; + public void setVideoDimensionsChangedListener(VideoDimensionsChangedListener listener) { + videoDimensionsChangedListener = listener; + } + + private void onVideoDimensionsChanged(int width, int height) { + if (videoDimensionsChangedListener != null) { + videoDimensionsChangedListener.videoDimensionsChanged(this, width, height); + } + } +} diff --git a/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/GStreamerSurfaceView.java b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/GStreamerSurfaceView.java new file mode 100644 index 0000000..075f035 --- /dev/null +++ b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/GStreamerSurfaceView.java @@ -0,0 +1,110 @@ +/* GStreamer + * + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.freedesktop.gstreamer.play; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceView; +import android.view.View; + +// A simple SurfaceView whose width and height can be set from the outside +public class GStreamerSurfaceView extends SurfaceView { + public int media_width = 320; + public int media_height = 240; + + // Mandatory constructors, they do not do much + public GStreamerSurfaceView(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + public GStreamerSurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GStreamerSurfaceView (Context context) { + super(context); + } + + // Called by the layout manager to find out our size and give us some rules. + // We will try to maximize our size, and preserve the media's aspect ratio if + // we are given the freedom to do so. + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (media_width == 0 || media_height == 0) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + + int width = 0, height = 0; + int wmode = View.MeasureSpec.getMode(widthMeasureSpec); + int hmode = View.MeasureSpec.getMode(heightMeasureSpec); + int wsize = View.MeasureSpec.getSize(widthMeasureSpec); + int hsize = View.MeasureSpec.getSize(heightMeasureSpec); + + Log.i ("GStreamer", "onMeasure called with " + media_width + "x" + media_height); + // Obey width rules + switch (wmode) { + case View.MeasureSpec.AT_MOST: + if (hmode == View.MeasureSpec.EXACTLY) { + width = Math.min(hsize * media_width / media_height, wsize); + break; + } + case View.MeasureSpec.EXACTLY: + width = wsize; + break; + case View.MeasureSpec.UNSPECIFIED: + width = media_width; + } + + // Obey height rules + switch (hmode) { + case View.MeasureSpec.AT_MOST: + if (wmode == View.MeasureSpec.EXACTLY) { + height = Math.min(wsize * media_height / media_width, hsize); + break; + } + case View.MeasureSpec.EXACTLY: + height = hsize; + break; + case View.MeasureSpec.UNSPECIFIED: + height = media_height; + } + + // Finally, calculate best size when both axis are free + if (hmode == View.MeasureSpec.AT_MOST && wmode == View.MeasureSpec.AT_MOST) { + int correct_height = width * media_height / media_width; + int correct_width = height * media_width / media_height; + + if (correct_height < height) + height = correct_height; + else + width = correct_width; + } + + // Obey minimum size + width = Math.max (getSuggestedMinimumWidth(), width); + height = Math.max (getSuggestedMinimumHeight(), height); + setMeasuredDimension(width, height); + } + +} diff --git a/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/Play.java b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/Play.java new file mode 100644 index 0000000..2874f05 --- /dev/null +++ b/playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/Play.java @@ -0,0 +1,196 @@ +/* GStreamer + * + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.freedesktop.gstreamer.play; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.PowerManager; +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.Player; + +public class Play extends Activity implements SurfaceHolder.Callback, OnSeekBarChangeListener { + private PowerManager.WakeLock wake_lock; + private Player player; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + try { + Player.init(this); + } catch (Exception e) { + Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); + finish(); + return; + } + + setContentView(R.layout.main); + + player = new Player(); + + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + wake_lock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GStreamer Play"); + wake_lock.setReferenceCounted(false); + + ImageButton play = (ImageButton) this.findViewById(R.id.button_play); + play.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + player.play(); + wake_lock.acquire(); + } + }); + + ImageButton pause = (ImageButton) this.findViewById(R.id.button_pause); + pause.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + player.pause(); + wake_lock.release(); + } + }); + + final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar); + sb.setOnSeekBarChangeListener(this); + + player.setPositionUpdatedListener(new Player.PositionUpdatedListener() { + public void positionUpdated(Player player, final long position) { + runOnUiThread (new Runnable() { + public void run() { + sb.setProgress((int) (position / 1000000)); + updateTimeWidget(); + } + }); + } + }); + + player.setDurationChangedListener(new Player.DurationChangedListener() { + public void durationChanged(Player player, final long duration) { + runOnUiThread (new Runnable() { + public void run() { + sb.setMax((int) (duration / 1000000)); + updateTimeWidget(); + } + }); + } + }); + + final GStreamerSurfaceView gsv = (GStreamerSurfaceView) this.findViewById(R.id.surface_video); + player.setVideoDimensionsChangedListener(new Player.VideoDimensionsChangedListener() { + public void videoDimensionsChanged(Player player, final int width, final int height) { + runOnUiThread (new Runnable() { + public void run() { + Log.i ("GStreamer", "Media size changed to " + width + "x" + height); + gsv.media_width = width; + gsv.media_height = height; + runOnUiThread(new Runnable() { + public void run() { + gsv.requestLayout(); + } + }); + } + }); + } + }); + + SurfaceView sv = (SurfaceView) this.findViewById(R.id.surface_video); + SurfaceHolder sh = sv.getHolder(); + sh.addCallback(this); + + String mediaUri = null; + Intent intent = getIntent(); + android.net.Uri uri = intent.getData(); + Log.i ("GStreamer", "Received URI: " + uri); + if (uri.getScheme().equals("content")) { + android.database.Cursor cursor = getContentResolver().query(uri, null, null, null, null); + cursor.moveToFirst(); + mediaUri = "file://" + cursor.getString(cursor.getColumnIndex(android.provider.MediaStore.Video.Media.DATA)); + cursor.close(); + } else { + mediaUri = uri.toString(); + } + player.setUri(mediaUri); + + updateTimeWidget(); + } + + protected void onDestroy() { + player.close(); + super.onDestroy(); + } + + 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(); + final int max = sb.getMax(); + + SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + final String message = df.format(new Date (pos)) + " / " + df.format(new Date (max)); + tv.setText(message); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + Log.d("GStreamer", "Surface changed to format " + format + " width " + + width + " height " + height); + player.setSurface(holder.getSurface()); + } + + public void surfaceCreated(SurfaceHolder holder) { + Log.d("GStreamer", "Surface created: " + holder.getSurface()); + } + + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d("GStreamer", "Surface destroyed"); + player.setSurface(null); + } + + public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) { + if (!fromUser) return; + + updateTimeWidget(); + } + + public void onStartTrackingTouch(SeekBar sb) { + } + + public void onStopTrackingTouch(SeekBar sb) { + Log.d("GStreamer", "Seek to " + sb.getProgress()); + player.seek(((long) sb.getProgress()) * 1000000); + } +} |