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 
17 package com.example.nativemedia;
18 
19 import android.app.Activity;
20 import android.graphics.SurfaceTexture;
21 import android.media.MediaPlayer;
22 import android.os.Bundle;
23 import android.util.Log;
24 import android.view.Surface;
25 import android.view.SurfaceHolder;
26 import android.view.SurfaceView;
27 import android.view.View;
28 import android.widget.AdapterView;
29 import android.widget.ArrayAdapter;
30 import android.widget.Button;
31 import android.widget.Spinner;
32 import java.io.IOException;
33 
34 import android.content.Context;
35 
36 import android.graphics.SurfaceTexture;
37 
38 import android.media.MediaPlayer.OnPreparedListener;
39 import android.media.MediaPlayer;
40 
41 public 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
onCreate(Bundle icicle)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
onPause()302     protected void onPause()
303     {
304         mIsPlayingStreaming = false;
305         setPlayingStreamingMediaPlayer(false);
306         mGLView1.onPause();
307         mGLView2.onPause();
308         super.onPause();
309     }
310 
311     @Override
onResume()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
onDestroy()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 */
createEngine()329     public static native void createEngine();
createStreamingMediaPlayer(String filename)330     public static native boolean createStreamingMediaPlayer(String filename);
setPlayingStreamingMediaPlayer(boolean isPlaying)331     public static native void setPlayingStreamingMediaPlayer(boolean isPlaying);
shutdown()332     public static native void shutdown();
setSurface(Surface surface)333     public static native void setSurface(Surface surface);
rewindStreamingMediaPlayer()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 
setFixedSize(int width, int height)345         abstract void setFixedSize(int width, int height);
useAsSinkForJava(MediaPlayer mediaPlayer)346         abstract void useAsSinkForJava(MediaPlayer mediaPlayer);
useAsSinkForNative()347         abstract void useAsSinkForNative();
348 
349     }
350 
351     static class SurfaceHolderVideoSink extends VideoSink {
352 
353         private final SurfaceHolder mSurfaceHolder;
354 
SurfaceHolderVideoSink(SurfaceHolder surfaceHolder)355         SurfaceHolderVideoSink(SurfaceHolder surfaceHolder) {
356             mSurfaceHolder = surfaceHolder;
357         }
358 
setFixedSize(int width, int height)359         void setFixedSize(int width, int height) {
360             mSurfaceHolder.setFixedSize(width, height);
361         }
362 
useAsSinkForJava(MediaPlayer mediaPlayer)363         void useAsSinkForJava(MediaPlayer mediaPlayer) {
364             mediaPlayer.setDisplay(mSurfaceHolder);
365         }
366 
useAsSinkForNative()367         void useAsSinkForNative() {
368             setSurface(mSurfaceHolder.getSurface());
369         }
370 
371     }
372 
373     static class GLViewVideoSink extends VideoSink {
374 
375         private final MyGLSurfaceView mMyGLSurfaceView;
376 
GLViewVideoSink(MyGLSurfaceView myGLSurfaceView)377         GLViewVideoSink(MyGLSurfaceView myGLSurfaceView) {
378             mMyGLSurfaceView = myGLSurfaceView;
379         }
380 
setFixedSize(int width, int height)381         void setFixedSize(int width, int height) {
382         }
383 
useAsSinkForJava(MediaPlayer mediaPlayer)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 
useAsSinkForNative()391         void useAsSinkForNative() {
392             Surface surface = new Surface(mMyGLSurfaceView.getSurfaceTexture());
393             setSurface(surface);
394             surface.release();
395         }
396 
397     }
398 
399 }
400