Split out documentation into subfolders.
[platform/upstream/gstreamer.git] / examples / tutorials / android-tutorial-5 / src / com / gst_sdk_tutorials / tutorial_5 / Tutorial5.java
1 package com.gst_sdk_tutorials.tutorial_5;
2
3 import java.io.File;
4 import java.text.SimpleDateFormat;
5 import java.util.Date;
6 import java.util.TimeZone;
7
8 import android.app.Activity;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.os.Bundle;
12 import android.os.Environment;
13 import android.os.PowerManager;
14 import android.util.Log;
15 import android.view.SurfaceHolder;
16 import android.view.SurfaceView;
17 import android.view.View;
18 import android.view.View.OnClickListener;
19 import android.widget.ImageButton;
20 import android.widget.SeekBar;
21 import android.widget.SeekBar.OnSeekBarChangeListener;
22 import android.widget.TextView;
23 import android.widget.Toast;
24
25 import org.freedesktop.gstreamer.GStreamer;
26 import com.lamerman.FileDialog;
27
28 public class Tutorial5 extends Activity implements SurfaceHolder.Callback, OnSeekBarChangeListener {
29     private native void nativeInit();     // Initialize native code, build pipeline, etc
30     private native void nativeFinalize(); // Destroy pipeline and shutdown native code
31     private native void nativeSetUri(String uri); // Set the URI of the media to play
32     private native void nativePlay();     // Set pipeline to PLAYING
33     private native void nativeSetPosition(int milliseconds); // Seek to the indicated position, in milliseconds
34     private native void nativePause();    // Set pipeline to PAUSED
35     private static native boolean nativeClassInit(); // Initialize native class: cache Method IDs for callbacks
36     private native void nativeSurfaceInit(Object surface); // A new surface is available
37     private native void nativeSurfaceFinalize(); // Surface about to be destroyed
38     private long native_custom_data;      // Native code will use this to keep private data
39
40     private boolean is_playing_desired;   // Whether the user asked to go to PLAYING
41     private int position;                 // Current position, reported by native code
42     private int duration;                 // Current clip duration, reported by native code
43     private boolean is_local_media;       // Whether this clip is stored locally or is being streamed
44     private int desired_position;         // Position where the users wants to seek to
45     private String mediaUri;              // URI of the clip being played
46
47     private final String defaultMediaUri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv";
48
49     static private final int PICK_FILE_CODE = 1;
50     private String last_folder;
51
52     private PowerManager.WakeLock wake_lock;
53
54     // Called when the activity is first created.
55     @Override
56     public void onCreate(Bundle savedInstanceState)
57     {
58         super.onCreate(savedInstanceState);
59
60         // Initialize GStreamer and warn if it fails
61         try {
62             GStreamer.init(this);
63         } catch (Exception e) {
64             Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
65             finish();
66             return;
67         }
68
69         setContentView(R.layout.main);
70
71         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
72         wake_lock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GStreamer tutorial 5");
73         wake_lock.setReferenceCounted(false);
74
75         ImageButton play = (ImageButton) this.findViewById(R.id.button_play);
76         play.setOnClickListener(new OnClickListener() {
77             public void onClick(View v) {
78                 is_playing_desired = true;
79                 wake_lock.acquire();
80                 nativePlay();
81             }
82         });
83
84         ImageButton pause = (ImageButton) this.findViewById(R.id.button_stop);
85         pause.setOnClickListener(new OnClickListener() {
86             public void onClick(View v) {
87                 is_playing_desired = false;
88                 wake_lock.release();
89                 nativePause();
90             }
91         });
92
93         ImageButton select = (ImageButton) this.findViewById(R.id.button_select);
94         select.setOnClickListener(new OnClickListener() {
95             public void onClick(View v) {
96                 Intent i = new Intent(getBaseContext(), FileDialog.class);
97                 i.putExtra(FileDialog.START_PATH, last_folder);
98                 startActivityForResult(i, PICK_FILE_CODE);
99             }
100         });
101
102         SurfaceView sv = (SurfaceView) this.findViewById(R.id.surface_video);
103         SurfaceHolder sh = sv.getHolder();
104         sh.addCallback(this);
105
106         SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar);
107         sb.setOnSeekBarChangeListener(this);
108
109         // Retrieve our previous state, or initialize it to default values
110         if (savedInstanceState != null) {
111             is_playing_desired = savedInstanceState.getBoolean("playing");
112             position = savedInstanceState.getInt("position");
113             duration = savedInstanceState.getInt("duration");
114             mediaUri = savedInstanceState.getString("mediaUri");
115             last_folder = savedInstanceState.getString("last_folder");
116             Log.i ("GStreamer", "Activity created with saved state:");
117         } else {
118             is_playing_desired = false;
119             position = duration = 0;
120             last_folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath();
121             Intent intent = getIntent();
122             android.net.Uri uri = intent.getData();
123             if (uri == null)
124                 mediaUri = defaultMediaUri;
125             else {
126                 Log.i ("GStreamer", "Received URI: " + uri);
127                 if (uri.getScheme().equals("content")) {
128                     android.database.Cursor cursor = getContentResolver().query(uri, null, null, null, null);
129                     cursor.moveToFirst();
130                     mediaUri = "file://" + cursor.getString(cursor.getColumnIndex(android.provider.MediaStore.Video.Media.DATA));
131                     cursor.close();
132                 } else
133                     mediaUri = uri.toString();
134             }
135             Log.i ("GStreamer", "Activity created with no saved state:");
136         }
137         is_local_media = false;
138         Log.i ("GStreamer", "  playing:" + is_playing_desired + " position:" + position +
139                 " duration: " + duration + " uri: " + mediaUri);
140
141         // Start with disabled buttons, until native code is initialized
142         this.findViewById(R.id.button_play).setEnabled(false);
143         this.findViewById(R.id.button_stop).setEnabled(false);
144
145         nativeInit();
146     }
147
148     protected void onSaveInstanceState (Bundle outState) {
149         Log.d ("GStreamer", "Saving state, playing:" + is_playing_desired + " position:" + position +
150                 " duration: " + duration + " uri: " + mediaUri);
151         outState.putBoolean("playing", is_playing_desired);
152         outState.putInt("position", position);
153         outState.putInt("duration", duration);
154         outState.putString("mediaUri", mediaUri);
155         outState.putString("last_folder", last_folder);
156     }
157
158     protected void onDestroy() {
159         nativeFinalize();
160         if (wake_lock.isHeld())
161             wake_lock.release();
162         super.onDestroy();
163     }
164
165     // Called from native code. This sets the content of the TextView from the UI thread.
166     private void setMessage(final String message) {
167         final TextView tv = (TextView) this.findViewById(R.id.textview_message);
168         runOnUiThread (new Runnable() {
169           public void run() {
170             tv.setText(message);
171           }
172         });
173     }
174
175     // Set the URI to play, and record whether it is a local or remote file
176     private void setMediaUri() {
177         nativeSetUri (mediaUri);
178         is_local_media = mediaUri.startsWith("file://");
179     }
180
181     // Called from native code. Native code calls this once it has created its pipeline and
182     // the main loop is running, so it is ready to accept commands.
183     private void onGStreamerInitialized () {
184         Log.i ("GStreamer", "GStreamer initialized:");
185         Log.i ("GStreamer", "  playing:" + is_playing_desired + " position:" + position + " uri: " + mediaUri);
186
187         // Restore previous playing state
188         setMediaUri ();
189         nativeSetPosition (position);
190         if (is_playing_desired) {
191             nativePlay();
192             wake_lock.acquire();
193         } else {
194             nativePause();
195             wake_lock.release();
196         }
197
198         // Re-enable buttons, now that GStreamer is initialized
199         final Activity activity = this;
200         runOnUiThread(new Runnable() {
201             public void run() {
202                 activity.findViewById(R.id.button_play).setEnabled(true);
203                 activity.findViewById(R.id.button_stop).setEnabled(true);
204             }
205         });
206     }
207
208     // The text widget acts as an slave for the seek bar, so it reflects what the seek bar shows, whether
209     // it is an actual pipeline position or the position the user is currently dragging to.
210     private void updateTimeWidget () {
211         TextView tv = (TextView) this.findViewById(R.id.textview_time);
212         SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar);
213         int pos = sb.getProgress();
214
215         SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
216         df.setTimeZone(TimeZone.getTimeZone("UTC"));
217         String message = df.format(new Date (pos)) + " / " + df.format(new Date (duration));
218         tv.setText(message);
219     }
220
221     // Called from native code
222     private void setCurrentPosition(final int position, final int duration) {
223         final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar);
224
225         // Ignore position messages from the pipeline if the seek bar is being dragged
226         if (sb.isPressed()) return;
227
228         runOnUiThread (new Runnable() {
229           public void run() {
230             sb.setMax(duration);
231             sb.setProgress(position);
232             updateTimeWidget();
233             sb.setEnabled(duration != 0);
234           }
235         });
236         this.position = position;
237         this.duration = duration;
238     }
239
240     static {
241         System.loadLibrary("gstreamer_android");
242         System.loadLibrary("tutorial-5");
243         nativeClassInit();
244     }
245
246     public void surfaceChanged(SurfaceHolder holder, int format, int width,
247             int height) {
248         Log.d("GStreamer", "Surface changed to format " + format + " width "
249                 + width + " height " + height);
250         nativeSurfaceInit (holder.getSurface());
251     }
252
253     public void surfaceCreated(SurfaceHolder holder) {
254         Log.d("GStreamer", "Surface created: " + holder.getSurface());
255     }
256
257     public void surfaceDestroyed(SurfaceHolder holder) {
258         Log.d("GStreamer", "Surface destroyed");
259         nativeSurfaceFinalize ();
260     }
261
262     // Called from native code when the size of the media changes or is first detected.
263     // Inform the video surface about the new size and recalculate the layout.
264     private void onMediaSizeChanged (int width, int height) {
265         Log.i ("GStreamer", "Media size changed to " + width + "x" + height);
266         final GStreamerSurfaceView gsv = (GStreamerSurfaceView) this.findViewById(R.id.surface_video);
267         gsv.media_width = width;
268         gsv.media_height = height;
269         runOnUiThread(new Runnable() {
270             public void run() {
271                 gsv.requestLayout();
272             }
273         });
274     }
275
276     // The Seek Bar thumb has moved, either because the user dragged it or we have called setProgress()
277     public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) {
278         if (fromUser == false) return;
279         desired_position = progress;
280         // If this is a local file, allow scrub seeking, this is, seek as soon as the slider is moved.
281         if (is_local_media) nativeSetPosition(desired_position);
282         updateTimeWidget();
283     }
284
285     // The user started dragging the Seek Bar thumb
286     public void onStartTrackingTouch(SeekBar sb) {
287         nativePause();
288     }
289
290     // The user released the Seek Bar thumb
291     public void onStopTrackingTouch(SeekBar sb) {
292         // If this is a remote file, scrub seeking is probably not going to work smoothly enough.
293         // Therefore, perform only the seek when the slider is released.
294         if (!is_local_media) nativeSetPosition(desired_position);
295         if (is_playing_desired) nativePlay();
296     }
297
298     @Override
299     protected void onActivityResult(int requestCode, int resultCode, Intent data)
300     {
301         if (resultCode == RESULT_OK && requestCode == PICK_FILE_CODE) {
302             mediaUri = "file://" + data.getStringExtra(FileDialog.RESULT_PATH);
303             position = 0;
304             last_folder = new File (data.getStringExtra(FileDialog.RESULT_PATH)).getParent();
305             Log.i("GStreamer", "Setting last_folder to " + last_folder);
306             setMediaUri();
307         }
308     }
309 }