NativeMedia.java revision b66b0745d45f4c0c7620749185f7fb3306804ce3
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=" + height);
88            }
89
90            public void surfaceCreated(SurfaceHolder holder) {
91                Log.v(TAG, "surfaceCreated");
92                setSurface(holder.getSurface());
93            }
94
95            public void surfaceDestroyed(SurfaceHolder holder) {
96                Log.v(TAG, "surfaceDestroyed");
97            }
98
99        });
100
101        // set up the Surface 2 video sink
102        mSurfaceView2 = (SurfaceView) findViewById(R.id.surfaceview2);
103        mSurfaceHolder2 = mSurfaceView2.getHolder();
104
105        mSurfaceHolder2.addCallback(new SurfaceHolder.Callback() {
106
107            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
108                Log.v(TAG, "surfaceChanged format=" + format + ", width=" + width + ", height=" + height);
109            }
110
111            public void surfaceCreated(SurfaceHolder holder) {
112                Log.v(TAG, "surfaceCreated");
113                setSurface(holder.getSurface());
114            }
115
116            public void surfaceDestroyed(SurfaceHolder holder) {
117                Log.v(TAG, "surfaceDestroyed");
118            }
119
120        });
121
122        // create Java media player
123        mMediaPlayer = new MediaPlayer();
124
125        // set up Java media player listeners
126        mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
127
128            public void onPrepared(MediaPlayer mediaPlayer) {
129                int width = mediaPlayer.getVideoWidth();
130                int height = mediaPlayer.getVideoHeight();
131                Log.v(TAG, "onPrepared width=" + width + ", height=" + height);
132                if (width != 0 && height != 0 && mJavaMediaPlayerVideoSink != null) {
133                    mJavaMediaPlayerVideoSink.setFixedSize(width, height);
134                }
135                mMediaPlayerIsPrepared = true;
136                mediaPlayer.start();
137            }
138
139        });
140
141        mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
142
143            public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) {
144                Log.v(TAG, "onVideoSizeChanged width=" + width + ", height=" + height);
145                if (width != 0 && height != 0 && mJavaMediaPlayerVideoSink != null) {
146                    mJavaMediaPlayerVideoSink.setFixedSize(width, height);
147                }
148            }
149
150        });
151
152        // initialize content source spinner
153        Spinner sourceSpinner = (Spinner) findViewById(R.id.source_spinner);
154        ArrayAdapter<CharSequence> sourceAdapter = ArrayAdapter.createFromResource(
155                this, R.array.source_array, android.R.layout.simple_spinner_item);
156        sourceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
157        sourceSpinner.setAdapter(sourceAdapter);
158        sourceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
159
160            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
161                mSourceString = parent.getItemAtPosition(pos).toString();
162                Log.v(TAG, "onItemSelected " + mSourceString);
163            }
164
165            public void onNothingSelected(AdapterView parent) {
166                Log.v(TAG, "onNothingSelected");
167                mSourceString = null;
168            }
169
170        });
171
172        // initialize video sink spinner
173        Spinner sinkSpinner = (Spinner) findViewById(R.id.sink_spinner);
174        ArrayAdapter<CharSequence> sinkAdapter = ArrayAdapter.createFromResource(
175                this, R.array.sink_array, android.R.layout.simple_spinner_item);
176        sinkAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
177        sinkSpinner.setAdapter(sinkAdapter);
178        sinkSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
179
180            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
181                mSinkString = parent.getItemAtPosition(pos).toString();
182                Log.v(TAG, "onItemSelected " + mSinkString);
183                if ("Surface 1".equals(mSinkString)) {
184                    if (mSurfaceHolder1VideoSink == null) {
185                        mSurfaceHolder1VideoSink = new SurfaceHolderVideoSink(mSurfaceHolder1);
186                    }
187                    mSelectedVideoSink = mSurfaceHolder1VideoSink;
188                } else if ("Surface 2".equals(mSinkString)) {
189                    if (mSurfaceHolder2VideoSink == null) {
190                        mSurfaceHolder2VideoSink = new SurfaceHolderVideoSink(mSurfaceHolder2);
191                    }
192                    mSelectedVideoSink = mSurfaceHolder2VideoSink;
193                } else if ("SurfaceTexture 1".equals(mSinkString)) {
194                    if (mGLView1VideoSink == null) {
195                        mGLView1VideoSink = new GLViewVideoSink(mGLView1);
196                    }
197                    mSelectedVideoSink = mGLView1VideoSink;
198                } else if ("SurfaceTexture 2".equals(mSinkString)) {
199                    if (mGLView2VideoSink == null) {
200                        mGLView2VideoSink = new GLViewVideoSink(mGLView2);
201                    }
202                    mSelectedVideoSink = mGLView2VideoSink;
203                }
204            }
205
206            public void onNothingSelected(AdapterView parent) {
207                Log.v(TAG, "onNothingSelected");
208                mSinkString = null;
209                mSelectedVideoSink = null;
210            }
211
212        });
213
214        // initialize button click handlers
215
216        // Java MediaPlayer start/pause
217
218        ((Button) findViewById(R.id.start_java)).setOnClickListener(new View.OnClickListener() {
219
220            public void onClick(View view) {
221                if (mJavaMediaPlayerVideoSink == null) {
222                    if (mSelectedVideoSink == null) {
223                        return;
224                    }
225                    mSelectedVideoSink.useAsSinkForJava(mMediaPlayer);
226                    mJavaMediaPlayerVideoSink = mSelectedVideoSink;
227                }
228                if (!mMediaPlayerIsPrepared) {
229                    if (mSourceString != null) {
230                        try {
231                            mMediaPlayer.setDataSource(mSourceString);
232                        } catch (IOException e) {
233                            Log.e(TAG, "IOException " + e);
234                        }
235                        mMediaPlayer.prepareAsync();
236                    }
237                } else if (mMediaPlayer.isPlaying()) {
238                    mMediaPlayer.pause();
239                } else {
240                    mMediaPlayer.start();
241                }
242            }
243
244        });
245
246        // native MediaPlayer start/pause
247
248        ((Button) findViewById(R.id.start_native)).setOnClickListener(new View.OnClickListener() {
249
250            boolean created = false;
251            public void onClick(View view) {
252                if (!created) {
253                    if (mNativeMediaPlayerVideoSink == null) {
254                        if (mSelectedVideoSink == null) {
255                            return;
256                        }
257                        mSelectedVideoSink.useAsSinkForNative();
258                        mNativeMediaPlayerVideoSink = mSelectedVideoSink;
259                    }
260                    if (mSourceString != null) {
261                        created = createStreamingMediaPlayer(mSourceString);
262                    }
263                }
264                if (created) {
265                    mIsPlayingStreaming = !mIsPlayingStreaming;
266                    setPlayingStreamingMediaPlayer(mIsPlayingStreaming);
267                }
268            }
269
270        });
271
272    }
273
274    /** Called when the activity is about to be paused. */
275    @Override
276    protected void onPause()
277    {
278        mIsPlayingStreaming = false;
279        setPlayingStreamingMediaPlayer(false);
280        mGLView1.onPause();
281        mGLView2.onPause();
282        super.onPause();
283    }
284
285    @Override
286    protected void onResume() {
287        super.onResume();
288        mGLView1.onResume();
289        mGLView2.onResume();
290    }
291
292    /** Called when the activity is about to be destroyed. */
293    @Override
294    protected void onDestroy()
295    {
296        shutdown();
297        super.onDestroy();
298    }
299
300    private MyGLSurfaceView mGLView1, mGLView2;
301
302    /** Native methods, implemented in jni folder */
303    public static native void createEngine();
304    public static native boolean createStreamingMediaPlayer(String filename);
305    public static native void setPlayingStreamingMediaPlayer(boolean isPlaying);
306    public static native void shutdown();
307    public static native void setSurface(Surface surface);
308    public static native void setSurfaceTexture(SurfaceTexture surfaceTexture);
309
310    /** Load jni .so on initialization */
311    static {
312         System.loadLibrary("native-media-jni");
313    }
314
315    // VideoSink abstracts out the difference between Surface and SurfaceTexture
316    // aka SurfaceHolder and GLSurfaceView
317    static abstract class VideoSink {
318
319        abstract void setFixedSize(int width, int height);
320        abstract void useAsSinkForJava(MediaPlayer mediaPlayer);
321        abstract void useAsSinkForNative();
322
323    }
324
325    static class SurfaceHolderVideoSink extends VideoSink {
326
327        private final SurfaceHolder mSurfaceHolder;
328
329        SurfaceHolderVideoSink(SurfaceHolder surfaceHolder) {
330            mSurfaceHolder = surfaceHolder;
331        }
332
333        void setFixedSize(int width, int height) {
334            mSurfaceHolder.setFixedSize(width, height);
335        }
336
337        void useAsSinkForJava(MediaPlayer mediaPlayer) {
338            mediaPlayer.setDisplay(mSurfaceHolder);
339        }
340
341        void useAsSinkForNative() {
342            setSurface(mSurfaceHolder.getSurface());
343        }
344
345    }
346
347    static class GLViewVideoSink extends VideoSink {
348
349        private final MyGLSurfaceView mMyGLSurfaceView;
350
351        GLViewVideoSink(MyGLSurfaceView myGLSurfaceView) {
352            mMyGLSurfaceView = myGLSurfaceView;
353        }
354
355        void setFixedSize(int width, int height) {
356        }
357
358        void useAsSinkForJava(MediaPlayer mediaPlayer) {
359            // This API is @hide currently, so use reflection instead:
360            // mMediaPlayer.setTexture(surfaceTexture);
361            Class clazz = MediaPlayer.class;
362            Class[] types = new Class[] { SurfaceTexture.class };
363            Object[] arglist = new Object[] { mMyGLSurfaceView.getSurfaceTexture() };
364            try {
365                clazz.getMethod("setTexture", types).invoke(mediaPlayer, arglist);
366            } catch (NoSuchMethodException e) {
367            } catch (IllegalAccessException e) {
368            } catch (java.lang.reflect.InvocationTargetException e) {
369            }
370        }
371
372        void useAsSinkForNative() {
373            setSurfaceTexture(mMyGLSurfaceView.getSurfaceTexture());
374        }
375
376    }
377
378}
379