MediaPlayer.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* 2 * Copyright (C) 2006 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 android.media; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.content.res.AssetFileDescriptor; 22import android.net.Uri; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.os.ParcelFileDescriptor; 27import android.os.PowerManager; 28import android.util.Log; 29import android.view.Surface; 30import android.view.SurfaceHolder; 31import android.graphics.Bitmap; 32import android.media.AudioManager; 33 34import java.io.FileDescriptor; 35import java.io.IOException; 36 37import java.lang.ref.WeakReference; 38 39/** 40 * Used to play audio and video files and streams. 41 * See the <a href="/android/toolbox/apis/media.html">Android Media APIs</a> 42 * page for help using using MediaPlayer. 43 */ 44public class MediaPlayer 45{ 46 static { 47 System.loadLibrary("media_jni"); 48 } 49 50 private final static String TAG = "MediaPlayer"; 51 52 private int mNativeContext; // accessed by native methods 53 private int mListenerContext; // accessed by native methods 54 private Surface mSurface; // accessed by native methods 55 private SurfaceHolder mSurfaceHolder; 56 private EventHandler mEventHandler; 57 private PowerManager.WakeLock mWakeLock = null; 58 private boolean mScreenOnWhilePlaying; 59 private boolean mStayAwake; 60 61 /** 62 * Default constructor. Consider using one of the create() methods for 63 * synchronously instantiating a MediaPlayer from a Uri or resource. 64 * <p>When done with the MediaPlayer, you should call {@link #release()}, 65 * to free the resources. If not released, too many MediaPlayer instances may 66 * result in an exception.</p> 67 */ 68 public MediaPlayer() { 69 70 Looper looper; 71 if ((looper = Looper.myLooper()) != null) { 72 mEventHandler = new EventHandler(this, looper); 73 } else if ((looper = Looper.getMainLooper()) != null) { 74 mEventHandler = new EventHandler(this, looper); 75 } else { 76 mEventHandler = null; 77 } 78 79 /* Native setup requires a weak reference to our object. 80 * It's easier to create it here than in C++. 81 */ 82 native_setup(new WeakReference<MediaPlayer>(this)); 83 } 84 85 /** 86 * Sets the SurfaceHolder to use for displaying the video portion of the media. 87 * This call is optional. Not calling it when playing back a video will 88 * result in only the audio track being played. 89 * 90 * @param sh the SurfaceHolder to use for video display 91 */ 92 public void setDisplay(SurfaceHolder sh) { 93 mSurfaceHolder = sh; 94 mSurface = sh.getSurface(); 95 updateSurfaceScreenOn(); 96 } 97 98 /** 99 * Convenience method to create a MediaPlayer for a given Uri. 100 * On success, {@link #prepare()} will already have been called and must not be called again. 101 * <p>When done with the MediaPlayer, you should call {@link #release()}, 102 * to free the resources. If not released, too many MediaPlayer instances will 103 * result in an exception.</p> 104 * 105 * @param context the Context to use 106 * @param uri the Uri from which to get the datasource 107 * @return a MediaPlayer object, or null if creation failed 108 */ 109 public static MediaPlayer create(Context context, Uri uri) { 110 return create (context, uri, null); 111 } 112 113 /** 114 * Convenience method to create a MediaPlayer for a given Uri. 115 * On success, {@link #prepare()} will already have been called and must not be called again. 116 * <p>When done with the MediaPlayer, you should call {@link #release()}, 117 * to free the resources. If not released, too many MediaPlayer instances will 118 * result in an exception.</p> 119 * 120 * @param context the Context to use 121 * @param uri the Uri from which to get the datasource 122 * @param holder the SurfaceHolder to use for displaying the video 123 * @return a MediaPlayer object, or null if creation failed 124 */ 125 public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) { 126 127 try { 128 MediaPlayer mp = new MediaPlayer(); 129 mp.setDataSource(context, uri); 130 if (holder != null) { 131 mp.setDisplay(holder); 132 } 133 mp.prepare(); 134 return mp; 135 } catch (IOException ex) { 136 Log.d(TAG, "create failed:", ex); 137 // fall through 138 } catch (IllegalArgumentException ex) { 139 Log.d(TAG, "create failed:", ex); 140 // fall through 141 } catch (SecurityException ex) { 142 Log.d(TAG, "create failed:", ex); 143 // fall through 144 } 145 146 return null; 147 } 148 149 /** 150 * Convenience method to create a MediaPlayer for a given resource id. 151 * On success, {@link #prepare()} will already have been called and must not be called again. 152 * <p>When done with the MediaPlayer, you should call {@link #release()}, 153 * to free the resources. If not released, too many MediaPlayer instances will 154 * result in an exception.</p> 155 * 156 * @param context the Context to use 157 * @param resid the raw resource id (<var>R.raw.<something></var>) for 158 * the resource to use as the datasource 159 * @return a MediaPlayer object, or null if creation failed 160 */ 161 public static MediaPlayer create(Context context, int resid) { 162 try { 163 AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); 164 if (afd == null) return null; 165 166 MediaPlayer mp = new MediaPlayer(); 167 mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 168 afd.close(); 169 mp.prepare(); 170 return mp; 171 } catch (IOException ex) { 172 Log.d(TAG, "create failed:", ex); 173 // fall through 174 } catch (IllegalArgumentException ex) { 175 Log.d(TAG, "create failed:", ex); 176 // fall through 177 } catch (SecurityException ex) { 178 Log.d(TAG, "create failed:", ex); 179 // fall through 180 } 181 return null; 182 } 183 184 /** 185 * Sets the data source as a content Uri. Call this after reset(), or before 186 * any other method (including setDataSource()) that might throw 187 * IllegalStateException in this class. 188 * 189 * @param context the Context to use when resolving the Uri 190 * @param uri the Content URI of the data you want to play 191 * @throws IllegalStateException if it is called 192 * in an order other than the one specified above 193 */ 194 public void setDataSource(Context context, Uri uri) 195 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 196 197 String scheme = uri.getScheme(); 198 if(scheme == null || scheme.equals("file")) { 199 setDataSource(uri.getPath()); 200 return; 201 } 202 203 ParcelFileDescriptor fd = null; 204 try { 205 ContentResolver resolver = context.getContentResolver(); 206 fd = resolver.openFileDescriptor(uri, "r"); 207 if (fd == null) { 208 return; 209 } 210 setDataSource(fd.getFileDescriptor()); 211 return; 212 } catch (SecurityException ex) { 213 } catch (IOException ex) { 214 } finally { 215 if (fd != null) { 216 fd.close(); 217 } 218 } 219 setDataSource(uri.toString()); 220 return; 221 } 222 223 /** 224 * Sets the data source (file-path or http/rtsp URL) to use. Call this after 225 * reset(), or before any other method (including setDataSource()) that might 226 * throw IllegalStateException in this class. 227 * 228 * @param path the path of the file, or the http/rtsp URL of the stream you want to play 229 * @throws IllegalStateException if it is called 230 * in an order other than the one specified above 231 */ 232 public native void setDataSource(String path) throws IOException, IllegalArgumentException, IllegalStateException; 233 234 /** 235 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 236 * to close the file descriptor. It is safe to do so as soon as this call returns. 237 * Call this after reset(), or before any other method (including setDataSource()) 238 * that might throw IllegalStateException in this class. 239 * 240 * @param fd the FileDescriptor for the file you want to play 241 * @throws IllegalStateException if it is called 242 * in an order other than the one specified above 243 */ 244 public void setDataSource(FileDescriptor fd) 245 throws IOException, IllegalArgumentException, IllegalStateException { 246 // intentionally less than LONG_MAX 247 setDataSource(fd, 0, 0x7ffffffffffffffL); 248 } 249 250 /** 251 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 252 * to close the file descriptor. It is safe to do so as soon as this call returns. 253 * Call this after reset(), or before any other method (including setDataSource()) 254 * that might throw IllegalStateException in this class. 255 * 256 * @param fd the FileDescriptor for the file you want to play 257 * @param offset the offset into the file where the data to be played starts, in bytes 258 * @param length the length in bytes of the data to be played 259 * @throws IllegalStateException if it is called 260 * in an order other than the one specified above 261 */ 262 public native void setDataSource(FileDescriptor fd, long offset, long length) 263 throws IOException, IllegalArgumentException, IllegalStateException; 264 265 /** 266 * Prepares the player for playback, synchronously. Call this after 267 * setDataSource() or stop(), and before any other method that might 268 * throw IllegalStateException in this class. 269 * 270 * After setting the datasource and the display surface, you need to either 271 * call prepare() or prepareAsync(). For files, it is OK to call prepare(), 272 * which blocks until MediaPlayer is ready for playback. 273 * 274 * @throws IllegalStateException if it is called 275 * in an order other than the one specified above 276 */ 277 public native void prepare() throws IOException, IllegalStateException; 278 279 /** 280 * Prepares the player for playback, asynchronously. Call this after 281 * setDataSource() or stop(), and before any other method that might 282 * throw IllegalStateException in this class. 283 * 284 * After setting the datasource and the display surface, you need to either 285 * call prepare() or prepareAsync(). For streams, you should call prepareAsync(), 286 * which returns immediately, rather than blocking until enough data has been 287 * buffered. 288 * 289 * @throws IllegalStateException if it is called 290 * in an order other than the one specified above 291 */ 292 public native void prepareAsync() throws IllegalStateException; 293 294 /** 295 * Starts or resumes playback. If playback had previously been paused, 296 * playback will continue from where it was paused. If playback had 297 * been stopped, or never started before, playback will start at the 298 * beginning. Call this after receiving onCompletion or onPrepared 299 * event notification from OnCompletionListener or OnPreparedListener 300 * interface, or called after prepare() or pause(). 301 * 302 * @throws IllegalStateException if it is called 303 * in an order other than the one specified above 304 */ 305 public void start() throws IllegalStateException { 306 stayAwake(true); 307 _start(); 308 } 309 310 private native void _start() throws IllegalStateException; 311 312 /** 313 * Stops playback after playback has been stopped or paused. 314 * Call this after start() or pause(), or after receiving the onPrepared 315 * event notification from OnPreparedListener interface. 316 * 317 * @throws IllegalStateException if it is called 318 * in an order other than the one specified above 319 */ 320 public void stop() throws IllegalStateException { 321 stayAwake(false); 322 _stop(); 323 } 324 325 private native void _stop() throws IllegalStateException; 326 327 /** 328 * Pauses playback. Call start() to resume. Call this after start() 329 * and before any other method that might throw IllegalStateException in this class. 330 * 331 * @throws IllegalStateException if it is called 332 * in an order other than the one specified above 333 */ 334 public void pause() throws IllegalStateException { 335 stayAwake(false); 336 _pause(); 337 } 338 339 private native void _pause() throws IllegalStateException; 340 341 /** 342 * Set the low-level power management behavior for this MediaPlayer. This 343 * can be used when the MediaPlayer is not playing through a SurfaceHolder 344 * set with {@link #setDisplay(SurfaceHolder)} and thus can use the 345 * high-level {@link #setScreenOnWhilePlaying(boolean)} feature. 346 * 347 * <p>This function has the MediaPlayer access the low-level power manager 348 * service to control the device's power usage while playing is occurring. 349 * The parameter is a combination of {@link android.os.PowerManager} wake flags. 350 * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK} 351 * permission. 352 * By default, no attempt is made to keep the device awake during playback. 353 * 354 * @param context the Context to use 355 * @param mode the power/wake mode to set 356 * @see android.os.PowerManager 357 */ 358 public void setWakeMode(Context context, int mode) { 359 boolean washeld = false; 360 if (mWakeLock != null) { 361 if (mWakeLock.isHeld()) { 362 washeld = true; 363 mWakeLock.release(); 364 } 365 mWakeLock = null; 366 } 367 368 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 369 mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName()); 370 mWakeLock.setReferenceCounted(false); 371 if (washeld) { 372 mWakeLock.acquire(); 373 } 374 } 375 376 /** 377 * Control whether we should use the attached SurfaceHolder to keep the 378 * screen on while video playback is occurring. This is the preferred 379 * method over {@link #setWakeMode} where possible, since it doesn't 380 * require that the application have permission for low-level wake lock 381 * access. 382 * 383 * @param screenOn Supply true to keep the screen on, false to allow it 384 * to turn off. 385 */ 386 public void setScreenOnWhilePlaying(boolean screenOn) { 387 if (mScreenOnWhilePlaying != screenOn) { 388 mScreenOnWhilePlaying = screenOn; 389 updateSurfaceScreenOn(); 390 } 391 } 392 393 private void stayAwake(boolean awake) { 394 if (mWakeLock != null) { 395 if (awake && !mWakeLock.isHeld()) { 396 mWakeLock.acquire(); 397 } else if (!awake && mWakeLock.isHeld()) { 398 mWakeLock.release(); 399 } 400 } 401 mStayAwake = awake; 402 updateSurfaceScreenOn(); 403 } 404 405 private void updateSurfaceScreenOn() { 406 if (mSurfaceHolder != null) { 407 mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake); 408 } 409 } 410 411 /** 412 * Returns the width of the video. Call this after setDataSource() method. 413 * 414 * @return the width of the video, or 0 if there is no video, 415 * no display surface was set, or prepare()/prepareAsync() 416 * have not completed yet 417 */ 418 public native int getVideoWidth(); 419 420 /** 421 * Returns the height of the video. Call this after setDataSource() method. 422 * 423 * @return the height of the video, or 0 if there is no video, 424 * no display surface was set, or prepare()/prepareAsync() 425 * have not completed yet 426 */ 427 public native int getVideoHeight(); 428 429 /** 430 * Checks whether the MediaPlayer is playing. Call this after 431 * setDataSource() method. 432 * 433 * @return true if currently playing, false otherwise 434 */ 435 public native boolean isPlaying(); 436 437 /** 438 * Seeks to specified time position. Call this after start(), pause(), or 439 * prepare(), or after receiving onPrepared or onCompletion event notification 440 * from OnPreparedListener or OnCompletionListener interface. 441 * 442 * @param msec the offset in milliseconds from the start to seek to 443 * @throws IllegalStateException if it is called 444 * in an order other than the one specified above 445 */ 446 public native void seekTo(int msec) throws IllegalStateException; 447 448 /** 449 * Gets the current playback position. Call this after setDataSource() method. 450 * 451 * @return the current position in milliseconds 452 */ 453 public native int getCurrentPosition(); 454 455 /** 456 * Gets the duration of the file. Call this after setDataSource() method. 457 * 458 * @return the duration in milliseconds 459 */ 460 public native int getDuration(); 461 462 /** 463 * Releases resources associated with this MediaPlayer object. 464 * It is considered good practice to call this method when you're 465 * done using the MediaPlayer. 466 */ 467 public void release() { 468 if (mWakeLock != null) mWakeLock.release(); 469 updateSurfaceScreenOn(); 470 mOnPreparedListener = null; 471 mOnBufferingUpdateListener = null; 472 mOnCompletionListener = null; 473 mOnSeekCompleteListener = null; 474 mOnErrorListener = null; 475 _release(); 476 } 477 478 private native void _release(); 479 480 /** 481 * Resets the MediaPlayer to its uninitialized state. After calling 482 * this method, you will have to initialize it again by setting the 483 * data source and calling prepare(). 484 */ 485 public void reset() { 486 _reset(); 487 // make sure none of the listeners get called anymore 488 mEventHandler.removeCallbacksAndMessages(null); 489 } 490 491 private native void _reset(); 492 493 /** 494 * Sets the audio stream type for this MediaPlayer. See {@link AudioManager} 495 * for a list of stream types. 496 * 497 * @param streamtype the audio stream type 498 * @see android.media.AudioManager 499 */ 500 public native void setAudioStreamType(int streamtype); 501 502 /** 503 * Sets the player to be looping or non-looping. Call this 504 * after setDataSource method. 505 * 506 * @param looping whether to loop or not 507 */ 508 public native void setLooping(boolean looping); 509 510 /** 511 * Sets the volume on this player. Call after setDataSource method. 512 * This API is recommended for balancing the output of audio streams 513 * within an application. Unless you are writing an application to 514 * control user settings, this API should be used in preference to 515 * AudioManager::setStreamVolume API which sets the volume of ALL streams of 516 * a particular type. Note that the passed volume values are raw scalars. 517 * UI controls should be scaled logarithmically. 518 * 519 * @param leftVolume left volume scalar 520 * @param rightVolume right volume scalar 521 */ 522 public native void setVolume(float leftVolume, float rightVolume); 523 524 /** 525 * Returns a Bitmap containing the video frame at the specified time. Call 526 * this after setDataSource() or stop(). 527 * 528 * @param msec the time at which to capture the video frame, in milliseconds 529 * @return a Bitmap containing the video frame at the specified time 530 * @throws IllegalStateException if it is called 531 * in an order other than the one specified above 532 * @hide 533 */ 534 public native Bitmap getFrameAt(int msec) throws IllegalStateException; 535 536 private native final void native_setup(Object mediaplayer_this); 537 private native final void native_finalize(); 538 protected void finalize() { native_finalize(); } 539 540 /* Do not change these values without updating their counterparts 541 * in include/media/mediaplayer.h! 542 */ 543 private static final int MEDIA_NOP = 0; // interface test message 544 private static final int MEDIA_PREPARED = 1; 545 private static final int MEDIA_PLAYBACK_COMPLETE = 2; 546 private static final int MEDIA_BUFFERING_UPDATE = 3; 547 private static final int MEDIA_SEEK_COMPLETE = 4; 548 private static final int MEDIA_ERROR = 100; 549 550 // error codes from framework that indicate content issues 551 // contained in arg1 of error message 552 553 // Seek not supported - live stream 554 private static final int ERROR_SEEK_NOT_SUPPORTED = 42; 555 556 // A/V interleave exceeds the progressive streaming buffer 557 private static final int ERROR_CONTENT_IS_POORLY_INTERLEAVED = 43; 558 559 // video decoder is falling behind - content is too complex 560 private static final int ERROR_VIDEO_TRACK_IS_FALLING_BEHIND = 44; 561 562 private class EventHandler extends Handler 563 { 564 private MediaPlayer mMediaPlayer; 565 566 public EventHandler(MediaPlayer mp, Looper looper) { 567 super(looper); 568 mMediaPlayer = mp; 569 } 570 571 @Override 572 public void handleMessage(Message msg) { 573 if (mMediaPlayer.mNativeContext == 0) { 574 Log.w(TAG, "mediaplayer went away with unhandled events"); 575 return; 576 } 577 switch(msg.what) { 578 case MEDIA_PREPARED: 579 if (mOnPreparedListener != null) 580 mOnPreparedListener.onPrepared(mMediaPlayer); 581 return; 582 583 case MEDIA_PLAYBACK_COMPLETE: 584 if (mOnCompletionListener != null) 585 mOnCompletionListener.onCompletion(mMediaPlayer); 586 stayAwake(false); 587 return; 588 589 case MEDIA_BUFFERING_UPDATE: 590 if (mOnBufferingUpdateListener != null) 591 mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); 592 return; 593 594 case MEDIA_SEEK_COMPLETE: 595 if (mOnSeekCompleteListener != null) 596 mOnSeekCompleteListener.onSeekComplete(mMediaPlayer); 597 return; 598 599 case MEDIA_ERROR: 600 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); 601 boolean error_was_handled = false; 602 if (mOnErrorListener != null) { 603 error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); 604 } 605 if (mOnCompletionListener != null && ! error_was_handled) { 606 mOnCompletionListener.onCompletion(mMediaPlayer); 607 } 608 stayAwake(false); 609 return; 610 case MEDIA_NOP: // interface test message - ignore 611 break; 612 613 default: 614 Log.e(TAG, "Unknown message type " + msg.what); 615 return; 616 } 617 } 618 } 619 620 /** 621 * Called from native code when an interesting event happens. This method 622 * just uses the EventHandler system to post the event back to the main app thread. 623 * We use a weak reference to the original MediaPlayer object so that the native 624 * code is safe from the object disappearing from underneath it. (This is 625 * the cookie passed to native_setup().) 626 */ 627 private static void postEventFromNative(Object mediaplayer_ref, 628 int what, int arg1, int arg2, Object obj) 629 { 630 MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get(); 631 if (mp == null) { 632 return; 633 } 634 635 if (mp.mEventHandler != null) { 636 Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); 637 mp.mEventHandler.sendMessage(m); 638 } 639 } 640 641 /** 642 * Interface definition for a callback to be invoked when the media 643 * file is ready for playback. 644 */ 645 public interface OnPreparedListener 646 { 647 /** 648 * Called when the media file is ready for playback. 649 * 650 * @param mp the MediaPlayer that is ready for playback 651 */ 652 void onPrepared(MediaPlayer mp); 653 } 654 655 /** 656 * Register a callback to be invoked when the media file is ready 657 * for playback. 658 * 659 * @param l the callback that will be run 660 */ 661 public void setOnPreparedListener(OnPreparedListener l) 662 { 663 mOnPreparedListener = l; 664 } 665 666 private OnPreparedListener mOnPreparedListener; 667 668 /** 669 * Interface definition for a callback to be invoked when playback of 670 * a media file has completed. 671 */ 672 public interface OnCompletionListener 673 { 674 /** 675 * Called when the end of a media file is reached during playback. 676 * 677 * @param mp the MediaPlayer that reached the end of the file 678 */ 679 void onCompletion(MediaPlayer mp); 680 } 681 682 /** 683 * Register a callback to be invoked when the end of a media file 684 * has been reached during playback. 685 * 686 * @param l the callback that will be run 687 */ 688 public void setOnCompletionListener(OnCompletionListener l) 689 { 690 mOnCompletionListener = l; 691 } 692 693 private OnCompletionListener mOnCompletionListener; 694 695 /** 696 * Interface definition of a callback to be invoked indicating buffering 697 * status of a media resource being streamed over the network. 698 */ 699 public interface OnBufferingUpdateListener 700 { 701 /** 702 * Called to update status in buffering a media stream. 703 * 704 * @param mp the MediaPlayer the update pertains to 705 * @param percent the percentage (0-100) of the buffer 706 * that has been filled thus far 707 */ 708 void onBufferingUpdate(MediaPlayer mp, int percent); 709 } 710 711 /** 712 * Register a callback to be invoked when the status of a network 713 * stream's buffer has changed. 714 * 715 * @param l the callback that will be run 716 */ 717 public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) 718 { 719 mOnBufferingUpdateListener = l; 720 } 721 722 private OnBufferingUpdateListener mOnBufferingUpdateListener; 723 724 /** 725 * Interface definition of a callback to be invoked indicating 726 * the completion of a seek operation. 727 */ 728 public interface OnSeekCompleteListener 729 { 730 /** 731 * Called to indicate the completion of a seek operation. 732 * 733 * @param mp the MediaPlayer that issued the seek operation 734 */ 735 public void onSeekComplete(MediaPlayer mp); 736 } 737 738 /** 739 * Register a callback to be invoked when a seek operation has been 740 * completed. 741 * 742 * @param l the callback that will be run 743 */ 744 public void setOnSeekCompleteListener(OnSeekCompleteListener l) 745 { 746 mOnSeekCompleteListener = l; 747 } 748 749 private OnSeekCompleteListener mOnSeekCompleteListener; 750 751 /* Do not change these values without updating their counterparts 752 * in include/media/mediaplayer.h! 753 */ 754 /** Unspecified media player error. @see #OnErrorListener */ 755 public static final int MEDIA_ERROR_UNKNOWN = 1; 756 /** Media server died. In this case, the application must release the 757 * MediaPlayer object and instantiate a new one. @see #OnErrorListener */ 758 public static final int MEDIA_ERROR_SERVER_DIED = 100; 759 760 761 /** 762 * Interface definition of a callback to be invoked when there 763 * has been an error during an asynchronous operation (other errors 764 * will throw exceptions at method call time). 765 */ 766 public interface OnErrorListener 767 { 768 /** 769 * Called to indicate an error. 770 * 771 * @param mp the MediaPlayer the error pertains to 772 * @param what the type of error that has occurred: 773 * <ul> 774 * <li>{@link #MEDIA_ERROR_UNKNOWN} 775 * <li>{@link #MEDIA_ERROR_SERVER_DIED} 776 * </ul> 777 * @param extra an extra code, specific to the error type 778 * @return True if the method handled the error, false if it didn't. 779 * Returning false, or not having an OnErrorListener at all, will 780 * cause the OnCompletionListener to be called. 781 */ 782 boolean onError(MediaPlayer mp, int what, int extra); 783 } 784 785 /** 786 * Register a callback to be invoked when an error has happened 787 * during an asynchronous operation. 788 * 789 * @param l the callback that will be run 790 */ 791 public void setOnErrorListener(OnErrorListener l) 792 { 793 mOnErrorListener = l; 794 } 795 796 private OnErrorListener mOnErrorListener; 797} 798