1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.example.nativemedia;
18
19import android.app.Activity;
20import android.graphics.SurfaceTexture;
21import android.media.MediaPlayer;
22import android.os.Bundle;
23import android.util.Log;
24import android.view.Surface;
25import android.view.SurfaceHolder;
26import android.view.SurfaceView;
27import android.view.View;
28import android.widget.AdapterView;
29import android.widget.ArrayAdapter;
30import android.widget.Button;
31import android.widget.Spinner;
32import java.io.IOException;
33
34import android.content.Context;
35
36import android.graphics.SurfaceTexture;
37
38import android.media.MediaPlayer.OnPreparedListener;
39import android.media.MediaPlayer;
40
41public class NativeMedia extends Activity {
42    static final String TAG = "NativeMedia";
43
44    String mSourceString = null;
45    String mSinkString = null;
46
47    // member variables for Java media player
48    MediaPlayer mMediaPlayer;
49    boolean mMediaPlayerIsPrepared = false;
50    SurfaceView mSurfaceView1;
51    SurfaceHolder mSurfaceHolder1;
52
53    // member variables for native media player
54    boolean mIsPlayingStreaming = false;
55    SurfaceView mSurfaceView2;
56    SurfaceHolder mSurfaceHolder2;
57
58    VideoSink mSelectedVideoSink;
59    VideoSink mJavaMediaPlayerVideoSink;
60    VideoSink mNativeMediaPlayerVideoSink;
61
62    SurfaceHolderVideoSink mSurfaceHolder1VideoSink, mSurfaceHolder2VideoSink;
63    GLViewVideoSink mGLView1VideoSink, mGLView2VideoSink;
64
65    /** Called when the activity is first created. */
66    @Override
67    public void onCreate(Bundle icicle) {
68        super.onCreate(icicle);
69        setContentView(R.layout.main);
70
71        mGLView1 = (MyGLSurfaceView) findViewById(R.id.glsurfaceview1);
72        mGLView2 = (MyGLSurfaceView) findViewById(R.id.glsurfaceview2);
73
74        //setContentView(mGLView);
75        //setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
76
77        // initialize native media system
78        createEngine();
79
80        // set up the Surface 1 video sink
81        mSurfaceView1 = (SurfaceView) findViewById(R.id.surfaceview1);
82        mSurfaceHolder1 = mSurfaceView1.getHolder();
83
84        mSurfaceHolder1.addCallback(new SurfaceHolder.Callback() {
85
86            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
87                Log.v(TAG, "surfaceChanged format=" + format + ", width=" + width + ", height=" +
88                        height);
89            }
90
91            public void surfaceCreated(SurfaceHolder holder) {
92                Log.v(TAG, "surfaceCreated");
93                setSurface(holder.getSurface());
94            }
95
96            public void surfaceDestroyed(SurfaceHolder holder) {
97                Log.v(TAG, "surfaceDestroyed");
98            }
99
100        });
101
102        // set up the Surface 2 video sink
103        mSurfaceView2 = (SurfaceView) findViewById(R.id.surfaceview2);
104        mSurfaceHolder2 = mSurfaceView2.getHolder();
105
106        mSurfaceHolder2.addCallback(new SurfaceHolder.Callback() {
107
108            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
109                Log.v(TAG, "surfaceChanged format=" + format + ", width=" + width + ", height=" +
110                        height);
111            }
112
113            public void surfaceCreated(SurfaceHolder holder) {
114                Log.v(TAG, "surfaceCreated");
115                setSurface(holder.getSurface());
116            }
117
118            public void surfaceDestroyed(SurfaceHolder holder) {
119                Log.v(TAG, "surfaceDestroyed");
120            }
121
122        });
123
124        // create Java media player
125        mMediaPlayer = new MediaPlayer();
126
127        // set up Java media player listeners
128        mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
129
130            public void onPrepared(MediaPlayer mediaPlayer) {
131                int width = mediaPlayer.getVideoWidth();
132                int height = mediaPlayer.getVideoHeight();
133                Log.v(TAG, "onPrepared width=" + width + ", height=" + height);
134                if (width != 0 && height != 0 && mJavaMediaPlayerVideoSink != null) {
135                    mJavaMediaPlayerVideoSink.setFixedSize(width, height);
136                }
137                mMediaPlayerIsPrepared = true;
138                mediaPlayer.start();
139            }
140
141        });
142
143        mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
144
145            public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) {
146                Log.v(TAG, "onVideoSizeChanged width=" + width + ", height=" + height);
147                if (width != 0 && height != 0 && mJavaMediaPlayerVideoSink != null) {
148                    mJavaMediaPlayerVideoSink.setFixedSize(width, height);
149                }
150            }
151
152        });
153
154        // initialize content source spinner
155        Spinner sourceSpinner = (Spinner) findViewById(R.id.source_spinner);
156        ArrayAdapter<CharSequence> sourceAdapter = ArrayAdapter.createFromResource(
157                this, R.array.source_array, android.R.layout.simple_spinner_item);
158        sourceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
159        sourceSpinner.setAdapter(sourceAdapter);
160        sourceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
161
162            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
163                mSourceString = parent.getItemAtPosition(pos).toString();
164                Log.v(TAG, "onItemSelected " + mSourceString);
165            }
166
167            public void onNothingSelected(AdapterView parent) {
168                Log.v(TAG, "onNothingSelected");
169                mSourceString = null;
170            }
171
172        });
173
174        // initialize video sink spinner
175        Spinner sinkSpinner = (Spinner) findViewById(R.id.sink_spinner);
176        ArrayAdapter<CharSequence> sinkAdapter = ArrayAdapter.createFromResource(
177                this, R.array.sink_array, android.R.layout.simple_spinner_item);
178        sinkAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
179        sinkSpinner.setAdapter(sinkAdapter);
180        sinkSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
181
182            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
183                mSinkString = parent.getItemAtPosition(pos).toString();
184                Log.v(TAG, "onItemSelected " + mSinkString);
185                if ("Surface 1".equals(mSinkString)) {
186                    if (mSurfaceHolder1VideoSink == null) {
187                        mSurfaceHolder1VideoSink = new SurfaceHolderVideoSink(mSurfaceHolder1);
188                    }
189                    mSelectedVideoSink = mSurfaceHolder1VideoSink;
190                } else if ("Surface 2".equals(mSinkString)) {
191                    if (mSurfaceHolder2VideoSink == null) {
192                        mSurfaceHolder2VideoSink = new SurfaceHolderVideoSink(mSurfaceHolder2);
193                    }
194                    mSelectedVideoSink = mSurfaceHolder2VideoSink;
195                } else if ("SurfaceTexture 1".equals(mSinkString)) {
196                    if (mGLView1VideoSink == null) {
197                        mGLView1VideoSink = new GLViewVideoSink(mGLView1);
198                    }
199                    mSelectedVideoSink = mGLView1VideoSink;
200                } else if ("SurfaceTexture 2".equals(mSinkString)) {
201                    if (mGLView2VideoSink == null) {
202                        mGLView2VideoSink = new GLViewVideoSink(mGLView2);
203                    }
204                    mSelectedVideoSink = mGLView2VideoSink;
205                }
206            }
207
208            public void onNothingSelected(AdapterView parent) {
209                Log.v(TAG, "onNothingSelected");
210                mSinkString = null;
211                mSelectedVideoSink = null;
212            }
213
214        });
215
216        // initialize button click handlers
217
218        // Java MediaPlayer start/pause
219
220        ((Button) findViewById(R.id.start_java)).setOnClickListener(new View.OnClickListener() {
221
222            public void onClick(View view) {
223                if (mJavaMediaPlayerVideoSink == null) {
224                    if (mSelectedVideoSink == null) {
225                        return;
226                    }
227                    mSelectedVideoSink.useAsSinkForJava(mMediaPlayer);
228                    mJavaMediaPlayerVideoSink = mSelectedVideoSink;
229                }
230                if (!mMediaPlayerIsPrepared) {
231                    if (mSourceString != null) {
232                        try {
233                            mMediaPlayer.setDataSource(mSourceString);
234                        } catch (IOException e) {
235                            Log.e(TAG, "IOException " + e);
236                        }
237                        mMediaPlayer.prepareAsync();
238                    }
239                } else if (mMediaPlayer.isPlaying()) {
240                    mMediaPlayer.pause();
241                } else {
242                    mMediaPlayer.start();
243                }
244            }
245
246        });
247
248        // native MediaPlayer start/pause
249
250        ((Button) findViewById(R.id.start_native)).setOnClickListener(new View.OnClickListener() {
251
252            boolean created = false;
253            public void onClick(View view) {
254                if (!created) {
255                    if (mNativeMediaPlayerVideoSink == null) {
256                        if (mSelectedVideoSink == null) {
257                            return;
258                        }
259                        mSelectedVideoSink.useAsSinkForNative();
260                        mNativeMediaPlayerVideoSink = mSelectedVideoSink;
261                    }
262                    if (mSourceString != null) {
263                        created = createStreamingMediaPlayer(mSourceString);
264                    }
265                }
266                if (created) {
267                    mIsPlayingStreaming = !mIsPlayingStreaming;
268                    setPlayingStreamingMediaPlayer(mIsPlayingStreaming);
269                }
270            }
271
272        });
273
274        // Java MediaPlayer rewind
275
276        ((Button) findViewById(R.id.rewind_java)).setOnClickListener(new View.OnClickListener() {
277
278            public void onClick(View view) {
279                if (mMediaPlayerIsPrepared) {
280                    mMediaPlayer.seekTo(0);
281                }
282            }
283
284        });
285
286        // native MediaPlayer rewind
287
288        ((Button) findViewById(R.id.rewind_native)).setOnClickListener(new View.OnClickListener() {
289
290            public void onClick(View view) {
291                if (mNativeMediaPlayerVideoSink != null) {
292                    rewindStreamingMediaPlayer();
293                }
294            }
295
296        });
297
298    }
299
300    /** Called when the activity is about to be paused. */
301    @Override
302    protected void onPause()
303    {
304        mIsPlayingStreaming = false;
305        setPlayingStreamingMediaPlayer(false);
306        mGLView1.onPause();
307        mGLView2.onPause();
308        super.onPause();
309    }
310
311    @Override
312    protected void onResume() {
313        super.onResume();
314        mGLView1.onResume();
315        mGLView2.onResume();
316    }
317
318    /** Called when the activity is about to be destroyed. */
319    @Override
320    protected void onDestroy()
321    {
322        shutdown();
323        super.onDestroy();
324    }
325
326    private MyGLSurfaceView mGLView1, mGLView2;
327
328    /** Native methods, implemented in jni folder */
329    public static native void createEngine();
330    public static native boolean createStreamingMediaPlayer(String filename);
331    public static native void setPlayingStreamingMediaPlayer(boolean isPlaying);
332    public static native void shutdown();
333    public static native void setSurface(Surface surface);
334    public static native void rewindStreamingMediaPlayer();
335
336    /** Load jni .so on initialization */
337    static {
338         System.loadLibrary("native-media-jni");
339    }
340
341    // VideoSink abstracts out the difference between Surface and SurfaceTexture
342    // aka SurfaceHolder and GLSurfaceView
343    static abstract class VideoSink {
344
345        abstract void setFixedSize(int width, int height);
346        abstract void useAsSinkForJava(MediaPlayer mediaPlayer);
347        abstract void useAsSinkForNative();
348
349    }
350
351    static class SurfaceHolderVideoSink extends VideoSink {
352
353        private final SurfaceHolder mSurfaceHolder;
354
355        SurfaceHolderVideoSink(SurfaceHolder surfaceHolder) {
356            mSurfaceHolder = surfaceHolder;
357        }
358
359        void setFixedSize(int width, int height) {
360            mSurfaceHolder.setFixedSize(width, height);
361        }
362
363        void useAsSinkForJava(MediaPlayer mediaPlayer) {
364            mediaPlayer.setDisplay(mSurfaceHolder);
365        }
366
367        void useAsSinkForNative() {
368            setSurface(mSurfaceHolder.getSurface());
369        }
370
371    }
372
373    static class GLViewVideoSink extends VideoSink {
374
375        private final MyGLSurfaceView mMyGLSurfaceView;
376
377        GLViewVideoSink(MyGLSurfaceView myGLSurfaceView) {
378            mMyGLSurfaceView = myGLSurfaceView;
379        }
380
381        void setFixedSize(int width, int height) {
382        }
383
384        void useAsSinkForJava(MediaPlayer mediaPlayer) {
385            SurfaceTexture st = mMyGLSurfaceView.getSurfaceTexture();
386            Surface s = new Surface(st);
387            mediaPlayer.setSurface(s);
388            s.release();
389        }
390
391        void useAsSinkForNative() {
392            Surface surface = new Surface(mMyGLSurfaceView.getSurfaceTexture());
393            setSurface(surface);
394            surface.release();
395        }
396
397    }
398
399}
400