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.&lt;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