summaryrefslogtreecommitdiff
path: root/playback/player/android/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'playback/player/android/app/src/main/java')
-rw-r--r--playback/player/android/app/src/main/java/org/freedesktop/gstreamer/Player.java241
-rw-r--r--playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/GStreamerSurfaceView.java110
-rw-r--r--playback/player/android/app/src/main/java/org/freedesktop/gstreamer/player/Play.java196
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);
+ }
+}