NativeMedia.java revision 3bbd3bb7fcb082eba313614f072c11c41b38ebfe
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 // Java MediaPlayer rewind 273 274 ((Button) findViewById(R.id.rewind_java)).setOnClickListener(new View.OnClickListener() { 275 276 public void onClick(View view) { 277 if (mMediaPlayerIsPrepared) { 278 mMediaPlayer.seekTo(0); 279 } 280 } 281 282 }); 283 284 // FIXME native MediaPlayer rewind 285 286 } 287 288 /** Called when the activity is about to be paused. */ 289 @Override 290 protected void onPause() 291 { 292 mIsPlayingStreaming = false; 293 setPlayingStreamingMediaPlayer(false); 294 mGLView1.onPause(); 295 mGLView2.onPause(); 296 super.onPause(); 297 } 298 299 @Override 300 protected void onResume() { 301 super.onResume(); 302 mGLView1.onResume(); 303 mGLView2.onResume(); 304 } 305 306 /** Called when the activity is about to be destroyed. */ 307 @Override 308 protected void onDestroy() 309 { 310 shutdown(); 311 super.onDestroy(); 312 } 313 314 private MyGLSurfaceView mGLView1, mGLView2; 315 316 /** Native methods, implemented in jni folder */ 317 public static native void createEngine(); 318 public static native boolean createStreamingMediaPlayer(String filename); 319 public static native void setPlayingStreamingMediaPlayer(boolean isPlaying); 320 public static native void shutdown(); 321 public static native void setSurface(Surface surface); 322 public static native void setSurfaceTexture(SurfaceTexture surfaceTexture); 323 324 /** Load jni .so on initialization */ 325 static { 326 System.loadLibrary("native-media-jni"); 327 } 328 329 // VideoSink abstracts out the difference between Surface and SurfaceTexture 330 // aka SurfaceHolder and GLSurfaceView 331 static abstract class VideoSink { 332 333 abstract void setFixedSize(int width, int height); 334 abstract void useAsSinkForJava(MediaPlayer mediaPlayer); 335 abstract void useAsSinkForNative(); 336 337 } 338 339 static class SurfaceHolderVideoSink extends VideoSink { 340 341 private final SurfaceHolder mSurfaceHolder; 342 343 SurfaceHolderVideoSink(SurfaceHolder surfaceHolder) { 344 mSurfaceHolder = surfaceHolder; 345 } 346 347 void setFixedSize(int width, int height) { 348 mSurfaceHolder.setFixedSize(width, height); 349 } 350 351 void useAsSinkForJava(MediaPlayer mediaPlayer) { 352 mediaPlayer.setDisplay(mSurfaceHolder); 353 } 354 355 void useAsSinkForNative() { 356 setSurface(mSurfaceHolder.getSurface()); 357 } 358 359 } 360 361 static class GLViewVideoSink extends VideoSink { 362 363 private final MyGLSurfaceView mMyGLSurfaceView; 364 365 GLViewVideoSink(MyGLSurfaceView myGLSurfaceView) { 366 mMyGLSurfaceView = myGLSurfaceView; 367 } 368 369 void setFixedSize(int width, int height) { 370 } 371 372 void useAsSinkForJava(MediaPlayer mediaPlayer) { 373 // This API is @hide currently, so use reflection instead: 374 // mMediaPlayer.setTexture(surfaceTexture); 375 Class clazz = MediaPlayer.class; 376 Class[] types = new Class[] { SurfaceTexture.class }; 377 Object[] arglist = new Object[] { mMyGLSurfaceView.getSurfaceTexture() }; 378 try { 379 clazz.getMethod("setTexture", types).invoke(mediaPlayer, arglist); 380 } catch (NoSuchMethodException e) { 381 } catch (IllegalAccessException e) { 382 } catch (java.lang.reflect.InvocationTargetException e) { 383 } 384 } 385 386 void useAsSinkForNative() { 387 setSurfaceTexture(mMyGLSurfaceView.getSurfaceTexture()); 388 } 389 390 } 391 392} 393