Android -- 多媒體播放之MediaPlayer使用內部實現簡析

Android -- MediaPlayer內部實現簡析


在以前的博客中,已經介紹了使用MediaPlayer時要注意的內容。現在,這裏就經過一個MediaPlayer代碼實例,來進一步分析MediaPlayer內部是怎樣運做、實現的;固然這裏的分析僅僅截止究竟層調用播放器以前,因爲播放器這塊實在是沒搞懂。html

咱們使用的樣例來源於以前MediaPlayer Playback譯文中的官方實例:java

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

代碼中主要經過5個步驟實現了媒體的播放過程,咱們一步步來分析。android


1、建立MediaPlayer


從MediaPlayer模塊的實現層次來講,它事實上僅僅是一個暴露給外部調用的工具類;真正的媒體操做都經過JNI調用究竟層Media服務,由它們真正實現。cookie

MediaPlayer類要使用一個libmedia_jni.so庫,它的載入步驟例如如下:網絡

   static {
        System.loadLibrary("media_jni");
        native_init();
    }
libmedia_jni.so提供了MediaPlayer需要調用的各個JNI函數,它相應的文件是android_media_MediaPlayer.cpp。load該so庫的同一時候。會調用native_init()函數進行一些前期的初始化工做:
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)//初始化一些Field和Method域ID
{
    jclass clazz;

    clazz = env->FindClass("android/media/MediaPlayer");
    if (clazz == NULL) {
        return;
    }

    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }

    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }

    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
    if (fields.surface_texture == NULL) {
        return;
    }

    env->DeleteLocalRef(clazz);

    clazz = env->FindClass("android/net/ProxyInfo");
    if (clazz == NULL) {
        return;
    }

    fields.proxyConfigGetHost =
        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");

    fields.proxyConfigGetPort =
        env->GetMethodID(clazz, "getPort", "()I");

    fields.proxyConfigGetExclusionList =
        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");

    env->DeleteLocalRef(clazz);

    gPlaybackParamsFields.init(env);
    gSyncParamsFields.init(env);
}
struct fields_t {
    jfieldID    context;
    jfieldID    surface_texture;

    jmethodID   post_event;

    jmethodID   proxyConfigGetHost;
    jmethodID   proxyConfigGetPort;
    jmethodID   proxyConfigGetExclusionList;
};
static fields_t fields;
從代碼可知,native_init()函數主要保存了一些MediaPlayer.java中定義的一些字段或方法的ID;當中獲取的mNativeContext字段,用於將初始化的本地MediaPlayer對象的地址保存到該變量中,這也就給每一個MediaPlayer.java實例綁定了一個Native層的MediaPlayer;另外,post_event保存了MediaPlayer::postEventFromNative()函數的ID值,它會被用來在Native層中向上層拋出事件或異常。


載入完要使用的動態庫,咱們就可以開始建立MediaPlayer實例了。首先看它的默認構造函數:session

    /**
     * Default constructor. Consider using one of the create() methods for
     * synchronously instantiating a MediaPlayer from a Uri or resource.
     * <p>When done with the MediaPlayer, you should call  {@link #release()},
     * to free the resources. If not released, too many MediaPlayer instances may
     * result in an exception.</p>
     */
    public MediaPlayer() {

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }

        mTimeProvider = new TimeProvider(this);
        mOpenSubtitleSources = new Vector<InputStream>();
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);

        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaPlayer>(this));//繼續調用了native_setup()函數
    }
咱們的MediaPlayer需要運行在消息循環中,EventHandler是MediaPlayer的一個內部類。它專門處理來自Native層的事件,這些事件通常都代表了MediaPlayer現在轉移到了某個狀態,咱們可以在該狀態處理什麼回調操做。EventHandler的功能較爲單一,就是依據底層上拋的事件,進行相應的回調或事件處理。這裏就再也不細看。

接着。調用了native_setup()函數,並傳入了一個MediaPlayer類型的弱引用實例,咱們看該函數的實現:架構

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("native_setup");
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    // create new listener and give it to MediaPlayer
    //JNIMediaPlayerListener類繼承自MediaPlayer.h中聲明的MediaPlayerListener,並實現了notify()方法
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);//在Native MediaPlayer實例中保存這個JNIMediaPlayerListener監聽對象

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);//將建立的Native MediaPlayer對象轉化成Long型值(地址),保存到MediaPlayer.java::mNativeContext變量中
}
該函數中主要作了三個操做:

  • 建立了一個Native MediaPlayer對象
  • 建立了一個JNIMediaPlayerListener對象,它主要用於向上層MediaPlayer(.java)對象通知事件或拋出異常
  • 將建立的Native MediaPlayer實例保存到MediaPlayer.java::mNativeContext字段中
Native MediaPlayer類繼承自BnMediaPlayerClient,它是IMediaPlayerClient業務的服務端。IMediaPlayerClient業務的整個框架如圖所看到的:

Native MediaPlayer的構造函數較爲簡單,就是一些字段的初始化操做,重要的是設置了默認的音頻流類型,並將MediaPlayer的狀態初始化爲Idle:
MediaPlayer::MediaPlayer()
{
    ALOGV("constructor");
    mListener = NULL;
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;//默認音頻流類型
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mSeekPosition = -1;
    mCurrentState = MEDIA_PLAYER_IDLE;//MediaPlayer的初始狀態
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;//是否循環播放
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    mAudioSessionId = AudioSystem::newAudioUniqueId();
    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}
另外,還有默認同步狀態的設置等等。

JNIMediaPlayerListener類繼承自MediaPlayerListener,它聲明瞭一個notify()函數。該函數主要用來向上層拋出事件或其它異常:
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
    JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
    ~JNIMediaPlayerListener();
    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
    JNIMediaPlayerListener();
    jclass      mClass;     // Reference to MediaPlayer class
    jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
};
JNIMediaPlayerListener的構造函數中用以前傳入的MediaPlayer弱引用實例構造了一個Native層全局的變量mObject。並且也保存了一份MediaPlayer.java的類型實例:
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{

    // Hold onto the MediaPlayer class for use in calling the static method
    // that posts events to the application thread.
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find android/media/MediaPlayer");
        jniThrowException(env, "java/lang/Exception", NULL);
        return;
    }
	
    mClass = (jclass)env->NewGlobalRef(clazz);//表明MediaPlayer.java類型的實例

    // We use a weak reference so the MediaPlayer object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    
    mObject  = env->NewGlobalRef(weak_thiz);//weak_thiz是MediaPlayer.java實例的一個弱引用
}
JNIMediaPlayerListener::notify()函數用來向上層拋出事件或異常:
//回調MediaPlayer.java中的postEventFromNative()方法,反饋Native層發生的事件;postEventFromNative()會EventHandler(運行在MediaPalyer的線程中)
//發送附帶msg參數的消息,EventHandler推斷當前的事件類型,如MEDIA_PREPARED等,最後調用應用程序設置的相關回調處理相應的事件信息
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (obj && obj->dataSize() > 0) {
        jobject jParcel = createJavaParcelObject(env);
        if (jParcel != NULL) {
            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
            nativeParcel->setData(obj->data(), obj->dataSize());
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
    }
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        LOGW_EX(env);
        env->ExceptionClear();
    }
}
JNIMediaPlayerListener實例建立後,會將它保存到Native MediaPlayer::mListener字段中。最後掉setMediaPlayer()方法將mp對象轉換成地址值的形式保存到上層MediaPlayer中:
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
    Mutex::Autolock l(sLock);
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    if (player.get()) {
        player->incStrong((void*)setMediaPlayer);
    }
    if (old != 0) {
        old->decStrong((void*)setMediaPlayer);
    }
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

2、設置音頻類型


分析到這裏。咱們可以推測:上層MediaPlayer和Native MediaPlayer之間的關係就至關於Client - Server結構。前者是Client,後者是Server;前者向後者發送服務請求。接着。咱們調用了MediaPlayer::setAudioStreamType()函數設置了當前的音頻流類型:
    /**
     * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
     * for a list of stream types. Must call this method before prepare() or
     * prepareAsync() in order for the target stream type to become effective
     * thereafter.
     *
     * @param streamtype the audio stream type
     * @see android.media.AudioManager
     */
    public void setAudioStreamType(int streamtype) {
        _setAudioStreamType(streamtype);
        mStreamType = streamtype;
    }

    private native void _setAudioStreamType(int streamtype);
從函數凝視可以。這裏設置的音頻流類型必須是AudioManager中定義過的,當前版本號中所支持的類型有:
    /** The audio stream for phone calls */
    public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
    /** The audio stream for system sounds */
    public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
    /** The audio stream for the phone ring */
    public static final int STREAM_RING = AudioSystem.STREAM_RING;
    /** The audio stream for music playback */
    public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
    /** The audio stream for alarms */
    public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
    /** The audio stream for notifications */
    public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
    /** @hide The audio stream for phone calls when connected to bluetooth */
    public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
    /** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
    public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
    /** The audio stream for DTMF Tones */
    public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
    /** @hide The audio stream for text to speech (TTS) */
    public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
最後調用native函數_setAudioStreamType()將類型值設置下去,看它的實現:
static void
android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint streamtype)
{
    ALOGV("setAudioStreamType: %d", streamtype);
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);//獲取建立時設置到MediaPlayer.java實例中的Native MediaPlayer實例
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}
首先獲取到以前保存到MediaPlayer.java::mNativeContext字段中的Native MediaPlayer對象;最後調用process_media_player_call()函數,當中包括了經過mp實例設置音頻類型的調用:MediaPlayer::setAudioStreamType()。
咱們分解來分析,process_media_player_call()函數會依據函數的運行結果opStatus,以及附帶的exception和message信息;推斷是否需要向上反饋狀態事件或拋出異常信息:
// If exception is NULL and opStatus is not OK, this method sends an error
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
//依據函數的運行結果opStatus,以及附帶的exception和message信息;推斷是否需要反饋操做失敗事件或拋出異常信息
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
        if (opStatus != (status_t) OK) {//假設無需拋出異常,但存在函數處理錯誤,則向上層拋出事件
            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);//重要
        }
    } else {  // Throw exception! 假設需要拋出異常,則構建相應的異常並拋出
        if ( opStatus == (status_t) INVALID_OPERATION ) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        } else if ( opStatus == (status_t) BAD_VALUE ) {
            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
            jniThrowException(env, "java/lang/SecurityException", NULL);
        } else if ( opStatus != (status_t) OK ) {
            if (strlen(message) > 230) {
               // if the message is too long, don't bother displaying the status code
               jniThrowException( env, exception, message);
            } else {
               char msg[256];
                // append the status code to the message
               sprintf(msg, "%s: status=0x%X", message, opStatus);
               jniThrowException( env, exception, msg);
            }
        }
    }
}
假設咱們的操做無需拋出異常,並且當前的函數調用有錯誤,就需要經過Native MediaPlayer調用notify()向上層拋出錯誤:
//嚮應用程序反饋當前狀態變化的回調事件,設置當前MediaPlayer的狀態
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
    bool send = true;
    bool locked = false;

    // TODO: In the future, we might be on the same thread if the app is
    // running in the same process as the media server. In that case,
    // this will deadlock.
    //
    // The threadId hack below works around this for the care of prepare,
    // seekTo and start within the same process.
    // FIXME: Remember, this is a hack, it's not even a hack that is applied
    // consistently for all use-cases, this needs to be revisited.
    if (mLockThreadId != getThreadId()) {
        mLock.lock();
        locked = true;
    }

    // Allows calls from JNI in idle state to notify errors
    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
        if (locked) mLock.unlock();   // release the lock when done.
        return;
    }

    switch (msg) {
    case MEDIA_NOP: // interface test message
        break;
    case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
    case MEDIA_PLAYBACK_COMPLETE:
        ALOGV("playback complete");
        if (mCurrentState == MEDIA_PLAYER_IDLE) {
            ALOGE("playback complete in idle state");
        }
        if (!mLoop) {
            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
        }
        break;
    case MEDIA_ERROR://假設是有函數處理錯誤發生
        // Always log errors.
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        ALOGE("error (%d, %d)", ext1, ext2);
        mCurrentState = MEDIA_PLAYER_STATE_ERROR;//則將當前狀態設置爲ERROR
        if (mPrepareSync)
        {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = ext1;
            mSignal.signal();
            send = false;
        }
        break;
    case MEDIA_INFO:
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
            ALOGW("info/warning (%d, %d)", ext1, ext2);
        }
        break;
    case MEDIA_SEEK_COMPLETE:
        ALOGV("Received seek complete");
        if (mSeekPosition != mCurrentPosition) {
            ALOGV("Executing queued seekTo(%d)", mSeekPosition);
            mSeekPosition = -1;
            seekTo_l(mCurrentPosition);
        }
        else {
            ALOGV("All seeks complete - return to regularly scheduled program");
            mCurrentPosition = mSeekPosition = -1;
        }
        break;
    case MEDIA_BUFFERING_UPDATE:
        ALOGV("buffering %d", ext1);
        break;
    case MEDIA_SET_VIDEO_SIZE:
        ALOGV("New video size %d x %d", ext1, ext2);
        mVideoWidth = ext1;
        mVideoHeight = ext2;
        break;
    case MEDIA_TIMED_TEXT:
        ALOGV("Received timed text message");
        break;
    case MEDIA_SUBTITLE_DATA:
        ALOGV("Received subtitle data message");
        break;
    case MEDIA_META_DATA:
        ALOGV("Received timed metadata message");
        break;
    default:
        ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
        break;
    }

	//JNIMediaPlayerListener繼承自MediaPlayerListener,並實現了notify()方法;聲明實現在android_media_MediaPlayer.cpp中
    sp<MediaPlayerListener> listener = mListener;//mListener保存了MediaPlayer實例建立時初始化的JNIMediaPlayerListener監聽對象
    if (locked) mLock.unlock();

    // this prevents re-entrant calls into client code
    if ((listener != 0) && send) {
        Mutex::Autolock _l(mNotifyLock);
        ALOGV("callback application");
        listener->notify(msg, ext1, ext2, obj);//調用JNIMediaPlayerListener類實例的notify()方法
        ALOGV("back from callback");
    }
}
notify()函數使用的msg推斷值定義在MediaPlayer.h中:
enum media_event_type {
    MEDIA_NOP               = 0, // interface test message
    MEDIA_PREPARED          = 1,
    MEDIA_PLAYBACK_COMPLETE = 2,
    MEDIA_BUFFERING_UPDATE  = 3,
    MEDIA_SEEK_COMPLETE     = 4,
    MEDIA_SET_VIDEO_SIZE    = 5,
    MEDIA_STARTED           = 6,
    MEDIA_PAUSED            = 7,
    MEDIA_STOPPED           = 8,
    MEDIA_SKIPPED           = 9,
    MEDIA_TIMED_TEXT        = 99,
    MEDIA_ERROR             = 100,
    MEDIA_INFO              = 200,
    MEDIA_SUBTITLE_DATA     = 201,
    MEDIA_META_DATA         = 202,
};
經過觀察notify()函數的實現,咱們發現正如文檔介紹的那樣。MediaPlayer確實有本身的內部狀態;首先會依據傳入的msg值更新當前的MediaPlayer狀態,最後經過mListener調用它的notify()函數將狀態的變化信息反饋上層MediaPlayer中。

假設需要拋出異常,則直接在JNI層拋出異常就能夠。假設即沒有錯誤,也沒有異常需要拋出,則什麼都不作。


process_media_player_call()方法的功能很是明顯,就是向上拋出函數處理錯誤事件或者異常信息。它的運行又依賴某些操做的運行結果。咱們接着看這部分的音頻流類型設置操做:
status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type)
{
    ALOGV("MediaPlayer::setAudioStreamType");
    Mutex::Autolock _l(mLock);
    if (mStreamType == type) return NO_ERROR;
    if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
                MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {
        // Can't change the stream type after prepare
        ALOGE("setAudioStream called in state %d", mCurrentState);
        return INVALID_OPERATION;
    }
    // cache
    mStreamType = type;
    return OK;
}
mStreamType保存當前設置的流類型,初始值是AUDIO_STREAM_MUSIC;假設要設置的類型和當前類型一致,則直接無錯誤返回;不然。推斷MediaPlayer的當前狀態可否夠進行當前操做,假設可以。則更新mStreamType的值並返回。音頻流類型設置的操做就結束了,該部分實現比較簡單。到此可以看到的結果。就是將需要設置的類型值保存到了Native MediaPlayer中。另外。從這個簡單調用的處理過程來看,也證明了咱們前面關於Client/Server的推測。

最後。JNI中最外層的process_media_player_call()會依據setAudioStreamType()的運行結果,推斷是否需要拋出函數處理錯誤或異常信息。

3、爲MediaPlayer設置資源


要爲MediaPlayer設置資源項。咱們要調用MediaPlayer::setDataSource()方法。MediaPlayer中提供了很是多重載版本號的setDataSource()方法。這是因爲MediaPlayer贊成咱們使用多種不一樣類型的資源。關於這部份內容,在以前的翻譯文檔中有具體的介紹。但是在setDataSource()中,終於基本的處理過程是獲取到當前資源的文件描寫敘述符。並調用native方法向下設置資源;但是。假設當前設置的是網絡資源或者其它,則處理方式略有不一樣,咱們不討論這部分,但也可能會說起一些。看對資源文件描寫敘述符設置的操做:
private native void _setDataSource(FileDescriptor fd, long offset, long length)
            throws IOException, IllegalArgumentException, IllegalStateException;

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    if (fileDescriptor == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return;
    }
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    ALOGV("setDataSourceFD: fd %d", fd);
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
與第二部分音頻流類型設置的處理操做類似,咱們直接看調用MediaPlayer::setDataSource()處理的過程,process_media_player_call()函數的處理跟以前一致,興許再也不贅述。

 MediaPlayer::setDataSource函數處理:
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
    status_t err = UNKNOWN_ERROR;

	//getMediaPlayerService()會經過ServiceManager找到MediaPlayerService服務的Client端實例,初始化service對象
    const sp<IMediaPlayerService>& service(getMediaPlayerService());
    if (service != 0) {
		//player實例實際是一個MediaPlayerServcie::Client實例,該內部類繼承自IMediaPlayer,負責向外提供其定義的業務服務
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//經過Binder機制向MediaPlayerService請求建立IMediaPlayer對象
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(fd, offset, length))) {
            player.clear();
        }
        err = attachNewPlayer(player);
    }
    return err;
}
首先。獲取一個MediaPlayerService服務的代理實例。MediaPlayerService在媒體播放框架中是一個很是重要的服務,它運行在mediaserver進程中。

MediaPlayerService服務的註冊過程在mediaserver進程建立時發生。main_mediaserver.cpp中:app

 InitializeIcuOrDie();
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();//啓動MediaPlayerService服務
        ResourceManagerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        SoundTriggerHwService::instantiate();
        RadioService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
MediaPlayerService是IMediaPlayerService業務架構中的服務端。該業務架構的圖演示樣例如如下:
咱們再看MediaPlayerService::instantiate()的處理:
void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}
很是easy地建立MediaPlayerService服務實例。並註冊進系統的實現(要注意。註冊的服務名稱是「media.player」)。接着看它的構造函數實現:
//建立MediaPlayerService實例時,會初始化MediaPlayerService::sFactoryMap成員,註冊各Player工廠對象
MediaPlayerService::MediaPlayerService()
{
    ALOGV("MediaPlayerService created");
    mNextConnId = 1;

    mBatteryAudio.refCount = 0;
    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
        mBatteryAudio.deviceOn[i] = 0;
        mBatteryAudio.lastTime[i] = 0;
        mBatteryAudio.totalTime[i] = 0;
    }
    // speaker is on by default
    mBatteryAudio.deviceOn[SPEAKER] = 1;

    // reset battery stats
    // if the mediaserver has crashed, battery stats could be left
    // in bad state, reset the state upon service start.
    BatteryNotifier& notifier(BatteryNotifier::getInstance());
    notifier.noteResetVideo();
    notifier.noteResetAudio();

    MediaPlayerFactory::registerBuiltinFactories();//初始化MediaPlayerService::sFactoryMap集合對象
}
最後一步調用了MediaPlayerFactory::registerBuildinFactories()函數註冊系統中提供的各個播放器實例:
enum player_type {
    STAGEFRIGHT_PLAYER = 3,
    NU_PLAYER = 4,
    // Test players are available only in the 'test' and 'eng' builds.
    // The shared library with the test player is passed passed as an
    // argument to the 'test:' url in the setDataSource call.
    TEST_PLAYER = 5,
};

void MediaPlayerFactory::registerBuiltinFactories() {
    Mutex::Autolock lock_(&sLock);

    if (sInitComplete)
        return;

    registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER);
    registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
    registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);

    sInitComplete = true;
}
registerFactory_l()函數會推斷實例的有效性,並將它們保存到集合變量中:
MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;
bool MediaPlayerFactory::sInitComplete = false;

status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,
                                               player_type type) {
    if (NULL == factory) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, factory is"
              " NULL.", type);
        return BAD_VALUE;
    }

    if (sFactoryMap.indexOfKey(type) >= 0) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, type is"
              " already registered.", type);
        return ALREADY_EXISTS;
    }

    if (sFactoryMap.add(type, factory) < 0) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add"
              " to map.", type);
        return UNKNOWN_ERROR;
    }

    return OK;
}
MediaPlayerFactory中主要涉及了三個類型的播放器:
MediaPlayerFactory提供的播放器類型
NuPlayer 用於播放網絡、本地視頻。或者RTSP協議的視頻流等
TestPlayer 測試用途
StagefrightPlayer 提供的默認播放器,其它播放器不能播放的資源都會讓它播放

當中,NuPlayer和StagefrightPlayer等Player類的繼承關係如圖所看到的:

介紹了有關MediaPlayerService有關的內容後,咱們再返回到指定內容。在獲得MediaPlayerService服務的代理service後,會去調用create()函數建立一個IMediaPlayer服務的代理對象player。IMediaPlayer業務是真正跟播放器相關的,該業務的結構如圖所看到的:

它的服務端實現類是MediaPlayerService::Client這個內部類。

咱們直接看MediaPlayerService::create()函數是怎樣建立Player的:框架

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
        int audioSessionId)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);

	//建立Client實例
	//Clinet是MediaPlayerService的內部類;它集成自BnMediaPlayer,是IMediaPlayer服務的Service組件,對外提供MediaPlayer的各類服務
    sp<Client> c = new Client(
            this, pid, connId, client, audioSessionId,
            IPCThreadState::self()->getCallingUid());

    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
         IPCThreadState::self()->getCallingUid());

    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);//mClients變量維護了MediaPlayerService中的Client實例集合
    }
    return c;
}
MediaPlayerService::Client類是IMediaPlayer業務的服務端,也就可以把它當作是提供MediaPlayer服務的提供者。

這裏建立了一個Client實例,並將它保存到了mClients集合中,咱們可以理解爲mClients中保存了當前所有存在的Player對象。分析Client的構造函數:異步

MediaPlayerService::Client::Client(
        const sp<MediaPlayerService>& service, pid_t pid,
        int32_t connId, const sp<IMediaPlayerClient>& client,
        int audioSessionId, uid_t uid)
{
    ALOGV("Client(%d) constructor", connId);
    mPid = pid;
    mConnId = connId;
    mService = service;//保存當前的IMediaPlayerService實例
    mClient = client;//保存的是Native MediaPlayer(MediaPlayer.cpp)實例
    mLoop = false;
    mStatus = NO_INIT;
    mAudioSessionId = audioSessionId;
    mUID = uid;
    mRetransmitEndpointValid = false;
    mAudioAttributes = NULL;

#if CALLBACK_ANTAGONIZER
    ALOGD("create Antagonizer");
    mAntagonizer = new Antagonizer(notify, this);
#endif
}
再返回到MediaPlayer::setDataSource()中。建立爲IMediaPlayer實例後,主要會調用它的setDataSource()函數:
//MediaPlayerFactory是播放器建立的工廠,提供打分功能,以讓系統在當前視頻源下找到合適類型的播放器進行播放
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length);
    struct stat sb;
    int ret = fstat(fd, &sb);
    if (ret != 0) {
        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
        return UNKNOWN_ERROR;
    }

    ALOGV("st_dev  = %llu", static_cast<uint64_t>(sb.st_dev));
    ALOGV("st_mode = %u", sb.st_mode);
    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
    ALOGV("st_size = %llu", sb.st_size);

    if (offset >= sb.st_size) {
        ALOGE("offset error");
        ::close(fd);
        return UNKNOWN_ERROR;
    }
    if (offset + length > sb.st_size) {
        length = sb.st_size - offset;
        ALOGV("calculated length = %lld", length);
    }

    player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                                               fd,
                                                               offset,
                                                               length);//獲取到當前適合播放該視頻源的播放器類型
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);//setDataSource_pre()建立一個合適的Player播放器實例
    if (p == NULL) {
        return NO_INIT;
    }

    // now set data source
    setDataSource_post(p, p->setDataSource(fd, offset, length));//先調用播放器實例的setDataSource()方法,爲它設置資源;再調用setDataSource_post()完畢收尾工做
    return mStatus;
}
這裏有四個重要調用:
  • MediaPlayerFactory::getPlayerType():爲當前資源選擇合適的播放器類型
  • setDataSource_pre():建立合適的播放器對象
  • MediaPlayerBase::setDataSource():調用播放器的setDataSource()方法,真正去設置資源
  • setDataSource_post():運行收尾工做
現在一步步看。

MediaPlayerFactory::getPlayerType()用於獲得合適的播放器類型,它內部主要調用宏定義函數GET_PLAYER_TYPE_IMPL():
#define GET_PLAYER_TYPE_IMPL(a...)                      \
    Mutex::Autolock lock_(&sLock);                      \
                                                        \
    player_type ret = STAGEFRIGHT_PLAYER;//默認播放器類型               \
    float bestScore = 0.0;                              \

	//依據當前傳入的視頻源,對各個播放器進行比較打分,找到合適的播放器;不然使用默認播放器StagefightPlayer \
    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
                                                        \
        IFactory* v = sFactoryMap.valueAt(i);           \
        float thisScore;                                \
        CHECK(v != NULL);                               \
        thisScore = v->scoreFactory(a, bestScore);      \
        if (thisScore > bestScore) {                    \
            ret = sFactoryMap.keyAt(i);                 \
            bestScore = thisScore;                      \
        }                                               \
    }                                                   \
                                                        \
    if (0.0 == bestScore) {                             \
        ret = getDefaultPlayerType();//依據"media.stagefright.use-awesome"屬性配置,選擇當前默認的播放器                   \
    }                                                   \
                                                        \
    return ret;                                         \
該函數的實現就是遍歷MediaPlayerFactory建立時註冊的各個播放器工廠對象的scoreFactory()方法。對當前設置的資源進行評估,獲得最符合該資源的播放器工廠類型並返回。各個播放器的scoreFactory()方法,這裏不作具體介紹。

setDataSource_pre()函數會依據獲得的播放器類型去建立相應的播放器實例:
//調用createPlayer()建立播放器實例對象
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
    ALOGV("player type = %d", playerType);

    // create the right type of player
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) {
        return p;
    }

    if (!p->hardwareOutput()) {//以StagefrightPlayer爲例,它沒有重寫父類的MediaPlayerInterface::hardwareOutput()方法;返回false,表示音頻不直接輸出到硬件上.
        Mutex::Autolock l(mLock);
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);//調用父類MediaPlayerInterface::setAudioSink()方法
    }

    return p;
}
當中MediaPlayerFactory::createPlayer運行播放器建立工做:
//假設當前持有了播放器實例對象,則要推斷它是否與咱們需要的播放器類型相符;
//假設不相符,則刪除它;再又一次建立該類型的播放器實例
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // determine if we have the right player type
    sp<MediaPlayerBase> p = mPlayer;
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();//重置該sp<>指針
    }
    if (p == NULL) {
        p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);//建立播放器對象;重要:createPlayer()裏面setNotifyCallback()函數調用
    }

    if (p != NULL) {
        p->setUID(mUID);
    }

    return p;
}
假設當前持有的播放器類型與需要的不符,則會先銷燬掉它,並按類型又一次建立一個新播放器對象。MediaPlayerFactory::createPlayer()運行播放器對象的建立操做:
//找到相應視頻源類型的播放器工廠實例,並建立Player對象;最後給該Player對象設置傳遞消息的notifyFunc回調函數
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        void* cookie,
        notify_callback_f notifyFunc,
        pid_t pid) {
    sp<MediaPlayerBase> p;
    IFactory* factory;
    status_t init_result;
    Mutex::Autolock lock_(&sLock);

    if (sFactoryMap.indexOfKey(playerType) < 0) {
        ALOGE("Failed to create player object of type %d, no registered"
              " factory", playerType);
        return p;
    }

    factory = sFactoryMap.valueFor(playerType);//從sFactoryMap中獲取到類型相應的Player工廠對象
    CHECK(NULL != factory);
    p = factory->createPlayer(pid);//建立相應類型的播放器實例,直接new實例對象;如建立StagefrightPlayer,期間會實例化StagefrightPlayer::(AwesomePlayer *mPlayer)成員

    if (p == NULL) {
        ALOGE("Failed to create player object of type %d, create failed",
               playerType);
        return p;
    }

    init_result = p->initCheck();//實現直接return OK;
    if (init_result == NO_ERROR) {
        p->setNotifyCallback(cookie, notifyFunc);//調用父類MediaPlayerBase::setNotifyCallback();cookie是當前調用MediaPlayerService::Client::createPlayer()的Client實例,notifyFunc這裏是MediaPlayerService::Client::notify()函數指針.
    } else {
        ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
    }

    return p;
}
依據需要建立的播放器類型。在sFactoryMap集合中找到相應的播放器工廠對象,並調用的createPlayer()方法真正建立實例,這裏以StagefrightPlayer爲例:
//StagefrightPlayer是默認播放器
class StagefrightPlayerFactory :
    public MediaPlayerFactory::IFactory {
  public:
    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                               int fd,
                               int64_t offset,
                               int64_t length,
                               float /*curScore*/) {
        if (legacyDrm()) {
            sp<DataSource> source = new FileSource(dup(fd), offset, length);
            String8 mimeType;
            float confidence;
            if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) {
                return 1.0;
            }
        }

        if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) {
            char buf[20];
            lseek(fd, offset, SEEK_SET);
            read(fd, buf, sizeof(buf));
            lseek(fd, offset, SEEK_SET);

            uint32_t ident = *((uint32_t*)buf);

            // Ogg vorbis?

if (ident == 0x5367674f) // 'OggS' return 1.0; } return 0.0; } virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, const char* url, float /*curScore*/) { if (legacyDrm() && !strncasecmp("widevine://", url, 11)) { return 1.0; } return 0.0; } virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) { ALOGV(" create StagefrightPlayer"); return new StagefrightPlayer();//實例化StagefrightPlayer對象,並初始化AwesomePlayer *mPlayer成員 } private: bool legacyDrm() { char value[PROPERTY_VALUE_MAX]; if (property_get("persist.sys.media.legacy-drm", value, NULL) && (!strcmp("1", value) || !strcasecmp("true", value))) { return true; } return false; } };

建立爲StagefrightPlayer對象後,會爲該對象設置回調對象和函數指針,它調用的是父類MediaPlayerBase::setNotifyCallback()函數;這一步是很是重要的:
init_result = p->initCheck();//實現直接return OK;
    if (init_result == NO_ERROR) {
        p->setNotifyCallback(cookie, notifyFunc);//調用父類MediaPlayerBase::setNotifyCallback();cookie是當前調用MediaPlayerService::Client::createPlayer()的Client實例,notifyFunc這裏是MediaPlayerService::Client::notify()函數指針.
    } else {
        ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
    }
MediaPlayerBase::setNotifyCallback()函數的定義是:
   void        setNotifyCallback(
            void* cookie, notify_callback_f notifyFunc) {
        Mutex::Autolock autoLock(mNotifyLock);
        mCookie = cookie; mNotify = notifyFunc;
    }
它有兩個參數:第一個參數類型是void *,這裏通常指向某個對象;第二個參數類型是notify_callback_f。

notify_callback_f事實上是一個類型別名。它的定義是:

// callback mechanism for passing messages to MediaPlayer object
typedef void (*notify_callback_f)(void* cookie,
        int msg, int ext1, int ext2, const Parcel *obj);
可以看出它實際是一個特定結構的函數指針,用於向MediaPlayer拋出事件。

MediaPlayerService::Client::setDataSource_pre()在建立播放器實例結束後。還有處理:
if (!p->hardwareOutput()) {//以StagefrightPlayer爲例,它沒有重寫父類的MediaPlayerInterface::hardwareOutput()方法;返回false,表示音頻不直接輸出到硬件上.
        Mutex::Autolock l(mLock);
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);//調用父類MediaPlayerInterface::setAudioSink()方法
    }
假設當前播放器不把音頻直接輸出到硬件上,還會去調用它的setAudioSink()方法。

setDataSource_pre()方法處理完後,會調用播放器實例的setDataSource()方法將資源對象設置給播放器,這裏是以StagefrightPlayer爲例:
// Warning: The filedescriptor passed into this method will only be valid until
// the method returns, if you want to keep it, dup it!
//實際調用AwesomePlayer實例的setDataSource()方法
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
    return mPlayer->setDataSource(dup(fd), offset, length);
}
實際是調用AwesomePlayer的同名方法,StagefrightPlayer::mPlayer在StagefrightPlayer構造時被建立:
StagefrightPlayer::StagefrightPlayer()
    : mPlayer(new AwesomePlayer) {
    ALOGV("StagefrightPlayer");

    mPlayer->setListener(this);
}
除了建立了AwesomePlayer實例外。將當前的StagefrightPlayer實例做爲監聽器保存到了AwesomePlayer::mListener字段中,它的做用是當AwesomePlayer完畢了一些操做 時。 如準備完畢、seek完畢,通知上層當前的MediaPlayer狀態變化。AwesomePlayer實例負責將數據設置下去。它的實現這裏不作分析。

最後調用setDataSource_post()函數進行一些掃尾工做:
//p指向建立的播放器實例, status是p->setDataSource()調用結果;setDataSource()調用過程的收尾階段
void MediaPlayerService::Client::setDataSource_post(
        const sp<MediaPlayerBase>& p,
        status_t status)
{
    ALOGV(" setDataSource");
    mStatus = status;
    if (mStatus != OK) {
        ALOGE("  error: %d", mStatus);
        return;
    }

    // Set the re-transmission endpoint if one was chosen.
    if (mRetransmitEndpointValid) {//設置再次傳輸時的終端
        mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
        if (mStatus != NO_ERROR) {
            ALOGE("setRetransmitEndpoint error: %d", mStatus);
        }
    }

    if (mStatus == OK) {
        mPlayer = p;//將播放器實例保存到mPlayer字段中
    }
}
需要注意的是。最後一步。會將此時建立的播放器實例保存到MediaPlayerService::Client mPlayer字段中。


咱們在回到外層MediaPlayer::setDataSource(),以後會調用attachNewPlayer()函數關聯對象:
//將新的IMediaPlayer對象保存到mPlayer中,並清理以前的MediaPlayer實例
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
    status_t err = UNKNOWN_ERROR;
    sp<IMediaPlayer> p;
    { // scope for the lock
        Mutex::Autolock _l(mLock);

        if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
                (mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {//推斷當前的狀態是否贊成調用attachNewPlayer()函數
            ALOGE("attachNewPlayer called in state %d", mCurrentState);
            return INVALID_OPERATION;
        }

        clear_l();//清理狀態
        p = mPlayer;
        mPlayer = player;//保存此次的IMediaPlayer對象,實際是MediaPlayerServcie::Client實例
        if (player != 0) {
            mCurrentState = MEDIA_PLAYER_INITIALIZED;//將當前狀態切換到INITIALIZED
            err = NO_ERROR;
        } else {
            ALOGE("Unable to create media player");
        }
    }

    if (p != 0) {//假設以前的IMediaPlayer沒有清理
        p->disconnect();//對此次對象的資源進行清理
    }

    return err;
}
該函數的主要就是更新Native MediaPlayer保存的IMediaPlayer實例,並對舊的對象進行清理操做;隨後還會將當前MediaPlayer的狀態切換到MEDIA_PLAYER_INITIALIZED。MediaPlayerService::Client的disconnect()方法會清掉一些底層資源:
void MediaPlayerService::Client::disconnect()
{
    ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
    // grab local reference and clear main reference to prevent future
    // access to object
    sp<MediaPlayerBase> p;
    {
        Mutex::Autolock l(mLock);
        p = mPlayer;
        mClient.clear();
    }

    mPlayer.clear();

    // clear the notification to prevent callbacks to dead client
    // and reset the player. We assume the player will serialize
    // access to itself if necessary.
    if (p != 0) {
        p->setNotifyCallback(0, 0);
#if CALLBACK_ANTAGONIZER
        ALOGD("kill Antagonizer");
        mAntagonizer->kill();
#endif
        p->reset();//重置狀態
    }

    disconnectNativeWindow();

    IPCThreadState::self()->flushCommands();
}
至此,MediaPlayer在調用setDateSource()後轉換到了NITIALIZED狀態;最後。JNI中最外層的process_media_player_call()會依據setDateSource()的運行結果。推斷是否需要拋出函數處理錯誤或異常信息。


4、準備MediaPlayer實例


給MediaPlayer設置完資源後,咱們還需要對它調用prepare()方法進行準備操做:
     * Prepares the player for playback, synchronously.
     *
     * After setting the datasource and the display surface, you need to either
     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
     * which blocks until MediaPlayer is ready for playback.
     *
     * @throws IllegalStateException if it is called in an invalid state
     */
    public void prepare() throws IOException, IllegalStateException {
        _prepare();
        scanInternalSubtitleTracks();
    }

    private native void _prepare() throws IOException, IllegalStateException;
直接看Native層的函數實現:
//MediaPlayer::prepare()函數
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    // Handle the case where the display surface was set before the mp was
    // initialized. We try again to make it stick.
    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
    mp->setVideoSurfaceTexture(st);

    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
}

static void
android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    // Handle the case where the display surface was set before the mp was
    // initialized. We try again to make it stick.
    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
    mp->setVideoSurfaceTexture(st);

    process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
}
本例中,咱們沒有設置紋理等;直接看MediaPlayer::prepare()的實現:
// one defined in the Android framework and one provided by the implementation
// that generated the error. The sync version of prepare returns only 1 error
// code.
status_t MediaPlayer::prepare()
{
    ALOGV("prepare");
    Mutex::Autolock _l(mLock);
    mLockThreadId = getThreadId();
    if (mPrepareSync) {
        mLockThreadId = 0;
        return -EALREADY;
    }
    mPrepareSync = true; //表示是不是同步操做
    status_t ret = prepareAsync_l();// 1
    if (ret != NO_ERROR) {
        mLockThreadId = 0;
        return ret;
    }

    if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }
    ALOGV("prepare complete - status=%d", mPrepareStatus);
    mLockThreadId = 0;
    return mPrepareStatus;
}
而異步地prepareAsync()函數的實現是:
status_t MediaPlayer::prepareAsync()
{
    ALOGV("prepareAsync");
    Mutex::Autolock _l(mLock);
    return prepareAsync_l();
}
對照兩個函數的實現內容,就能夠發現這裏同步、異步的差異主要是在是否等待mLock這個鎖上面。
咱們分析真正的實現函數prepare_l()方法:
// must call with lock held
//mPlayer實際是一個MediaPlayerServcie::Client類型的實例,它是MediaPlayerServcie的服務端
status_t MediaPlayer::prepareAsync_l()
{
    if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
        if (mAudioAttributesParcel != NULL) {//假設有參數,需要進行設置
            mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
        } else {
            mPlayer->setAudioStreamType(mStreamType);//不然設置以前的音頻流類型
        }
        mCurrentState = MEDIA_PLAYER_PREPARING;//變動狀態:MEDIA_PLAYER_PREPARING
        return mPlayer->prepareAsync();//調用MediaPlayerServcie::Client的prepareAsync()
    }
    ALOGE("prepareAsync called in state %d", mCurrentState);
    return INVALID_OPERATION;
}
假設當前Native MediaPlayer實例持有IMediaPlayer對象。且當前狀態可以調用prepare()方法,則去調用MediaPlayerService::Client的prepareAsync()函數進行準備工做:
status_t MediaPlayerService::Client::prepareAsync()
{
    ALOGV("[%d] prepareAsync", mConnId);
    sp<MediaPlayerBase> p = getPlayer();//getPlayer(){return mPlayer;}返回建立的播放器實例,是MediaPlayerBase的子類;以StagefrightPlayer爲例
    if (p == 0) return UNKNOWN_ERROR;
    status_t ret = p->prepareAsync();
#if CALLBACK_ANTAGONIZER
    ALOGD("start Antagonizer");
    if (ret == NO_ERROR) mAntagonizer->start();
#endif
    return ret;
}
getPlayer()函數返回setDataSource()過程當中的建立的播放器實例:
 sp<MediaPlayerBase>     getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
本文中播放器都是以StagefrightPlayer爲例,因此接着去調用StagefrightPlayer::prepareAsync():
status_t StagefrightPlayer::prepareAsync() {
    return mPlayer->prepareAsync();
}
最後調用AwesomePlayer的prepareAsync()函數進行準備工做。咱們忽略AwesomePlayer運行prepare操做的中間過程,直接看它是怎麼向外拋出MEDIA_PREPARED事件的。在AwesomePlayer的prepare操做完畢後。會調用AwesomePlayer::finishAsyncPrepare_l()方法:
void AwesomePlayer::finishAsyncPrepare_l() {
    if (mIsAsyncPrepare) {
        if (mVideoSource == NULL) {
            notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
        } else {
            notifyVideoSize_l();
        }

        notifyListener_l(MEDIA_PREPARED);//拋出事件
    }
......
}
它會調用AwesomePlayer::notifyListener_l()函數向外拋出事件:
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
    if ((mListener != NULL) && !mAudioTearDown) {
        sp<MediaPlayerBase> listener = mListener.promote();

        if (listener != NULL) {
            listener->sendEvent(msg, ext1, ext2);
        }
    }
}
這裏的listener對象指向mListener實例。StagefrightPlayer對象建立時,會順帶初始化AwesomePlayer實例mPlayer,同一時候會將當前的StagefrightPlayer設置到AwesomePlayer中,保存到mListener字段中,做爲往外拋出事件的鉤子。明白了這些。再看代碼實現,就會發現notifyListener_l()中的處理就是直接調用StagefrightPlayer的sendEvent()方法。又依據以前介紹的StagefrightPlayer的繼承關係和它的類實現來看,事實上就是調用它的父類MediaPlayerBase的sendEvent()方法:
    void        MediaPlayerBase::setNotifyCallback(
            void* cookie, notify_callback_f notifyFunc) {
        Mutex::Autolock autoLock(mNotifyLock);
        mCookie = cookie; mNotify = notifyFunc;
    }

    void        MediaPlayerBase::sendEvent(int msg, int ext1=0, int ext2=0,
                          const Parcel *obj=NULL) {
        notify_callback_f notifyCB;
        void* cookie;
        {
            Mutex::Autolock autoLock(mNotifyLock);
            notifyCB = mNotify;
            cookie = mCookie;
        }

        if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
    }
現在回想一下以前介紹的setDataSource()中的內容:在MediaPlayerFactory中建立咱們需要的播放器對象時。在建立完Player對象後。咱們就給它設置了這裏的notifyCB和cookie對象:
//找到相應視頻源類型的播放器工廠實例,並建立Player對象;最後給該Player對象設置傳遞消息的notifyFunc回調函數
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        void* cookie,
        notify_callback_f notifyFunc,
        pid_t pid) {
    ...

    init_result = p->initCheck();//實現直接return OK;
    if (init_result == NO_ERROR) {
        p->setNotifyCallback(cookie, notifyFunc);//調用父類MediaPlayerBase::setNotifyCallback();cookie是當前調用MediaPlayerService::Client::createPlayer()的Client實例,notifyFunc這裏是MediaPlayerService::Client::notify()函數指針.
    } else {
        ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
    }

    return p;
}
當中:
  • cookie指向當前調用MediaPlayerService::Client::createPlayer()的Client實例
  • notifyFunc是一個函數指針,這裏指向MediaPlayerService::Client::notify()函數
因此可知sendEvent():
void        MediaPlayerBase::sendEvent(int msg, int ext1=0, int ext2=0,
                          const Parcel *obj=NULL) {
        notify_callback_f notifyCB;
        void* cookie;
        {
            Mutex::Autolock autoLock(mNotifyLock);
            notifyCB = mNotify;
            cookie = mCookie;
        }

        if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
    }
就是調用當前播放器所屬的MediaPlayerService::Client的notify()函數。

既然明白了這些,咱們就直接看notify()函數實現:

//用於向MediaPlayer對象傳遞消息;參數void* cookie實際指向當前的Client實例;msg參數是事件類型信息
void MediaPlayerService::Client::notify(
        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
    Client* client = static_cast<Client*>(cookie);//首先獲得當前的Client對象
    if (client == NULL) {
        return;
    }

    sp<IMediaPlayerClient> c;
    {
        Mutex::Autolock l(client->mLock);
        c = client->mClient;//獲得該Client保存的IMediaPlayerClient對象信息,它實際指向一個Native MediaPlayer實例
        if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {//假設當前事件是MEDIA_PLAYBACK_COMPLETE,代表當前播放已經結束。並且咱們設置了下一個mNextClient
            if (client->mAudioOutput != NULL)                               //就需要切換運行mNextClient;效果就是當前播放結束後,會本身主動切換到下一個播放
                client->mAudioOutput->switchToNextOutput();
            client->mNextClient->start();
            client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);//並調用notify()方法,向外告知MEDIA_INFO_STARTED_AS_NEXT事件
        }
    }

    if (MEDIA_INFO == msg &&
        MEDIA_INFO_METADATA_UPDATE == ext1) {//msg爲MEDIA_INFO的狀況
        const media::Metadata::Type metadata_type = ext2;

        if(client->shouldDropMetadata(metadata_type)) {
            return;
        }

        // Update the list of metadata that have changed. getMetadata
        // also access mMetadataUpdated and clears it.
        client->addNewMetadataUpdate(metadata_type);
    }

    if (c != NULL) {
        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
        c->notify(msg, ext1, ext2, obj);//調用這一次的MediaPlayerService::Client實例相應的Native MediaPlayer實例的notify()方法
    }
}
首先咱們獲得當前的Client實例,靠它再進一步獲得與它相應的Native MediaPlayer實例(MediaPlayerService::Client構建時會保存該實例)。

這裏處理幾種特殊狀況,當咱們設置了mNextClient時(經過MediaPlayerService::Client::setNextPlayer()設置),假設msg符合狀況。就會本身主動切換到下一個播放。msg爲MEDIA_INFO的狀況,也會有一些特殊的處理。接着,就調用MediaPlayer::notify()方法,該函數的內容以前已經介紹過一些,這裏在貼一遍它的實現:

//嚮應用程序反饋當前狀態變化的回調事件,設置當前MediaPlayer的狀態
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
    bool send = true;
    bool locked = false;

    // TODO: In the future, we might be on the same thread if the app is
    // running in the same process as the media server. In that case,
    // this will deadlock.
    //
    // The threadId hack below works around this for the care of prepare,
    // seekTo and start within the same process.
    // FIXME: Remember, this is a hack, it's not even a hack that is applied
    // consistently for all use-cases, this needs to be revisited.
    if (mLockThreadId != getThreadId()) {
        mLock.lock();
        locked = true;
    }

    // Allows calls from JNI in idle state to notify errors
    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
        if (locked) mLock.unlock();   // release the lock when done.
        return;
    }

    switch (msg) {
    case MEDIA_NOP: // interface test message
        break;
    case MEDIA_PREPARED://符合當前的狀況,處理Prepare完畢的狀況
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
    case MEDIA_PLAYBACK_COMPLETE:
        ALOGV("playback complete");
        if (mCurrentState == MEDIA_PLAYER_IDLE) {
            ALOGE("playback complete in idle state");
        }
        if (!mLoop) {
            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
        }
        break;
    case MEDIA_ERROR:
        // Always log errors.
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        ALOGE("error (%d, %d)", ext1, ext2);
        mCurrentState = MEDIA_PLAYER_STATE_ERROR;
        if (mPrepareSync)
        {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = ext1;
            mSignal.signal();
            send = false;
        }
        break;
    case MEDIA_INFO:
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
            ALOGW("info/warning (%d, %d)", ext1, ext2);
        }
        break;
    case MEDIA_SEEK_COMPLETE:
        ALOGV("Received seek complete");
        if (mSeekPosition != mCurrentPosition) {
            ALOGV("Executing queued seekTo(%d)", mSeekPosition);
            mSeekPosition = -1;
            seekTo_l(mCurrentPosition);
        }
        else {
            ALOGV("All seeks complete - return to regularly scheduled program");
            mCurrentPosition = mSeekPosition = -1;
        }
        break;
    case MEDIA_BUFFERING_UPDATE:
        ALOGV("buffering %d", ext1);
        break;
    case MEDIA_SET_VIDEO_SIZE:
        ALOGV("New video size %d x %d", ext1, ext2);
        mVideoWidth = ext1;
        mVideoHeight = ext2;
        break;
    case MEDIA_TIMED_TEXT:
        ALOGV("Received timed text message");
        break;
    case MEDIA_SUBTITLE_DATA:
        ALOGV("Received subtitle data message");
        break;
    case MEDIA_META_DATA:
        ALOGV("Received timed metadata message");
        break;
    default:
        ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
        break;
    }

	//JNIMediaPlayerListener繼承自MediaPlayerListener,並實現了notify()方法;聲明實現在android_media_MediaPlayer.cpp中
    sp<MediaPlayerListener> listener = mListener;//mListener保存了MediaPlayer實例建立時初始化的JNIMediaPlayerListener監聽對象
    if (locked) mLock.unlock();

    // this prevents re-entrant calls into client code
    if ((listener != 0) && send) {
        Mutex::Autolock _l(mNotifyLock);
        ALOGV("callback application");
        listener->notify(msg, ext1, ext2, obj);//調用JNIMediaPlayerListener類實例的notify()方法
        ALOGV("back from callback");
    }
}
此時msg是MEDIA_PREPARED。看它的處理過程:
case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
主要地,將MediaPlayer的當前狀態設置爲了MEDIA_PLAYER_PREPARED。

最後。調用JNIMediaPlayerListener::notify()函數:
//回調MediaPlayer.java中的postEventFromNative()方法,反饋Native層發生的事件;postEventFromNative()會EventHandler(運行在MediaPalyer的線程中)
//發送附帶msg參數的消息,EventHandler推斷當前的事件類型,如MEDIA_PREPARED等,最後調用應用程序設置的相關回調處理相應的事件信息
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (obj && obj->dataSize() > 0) {
        jobject jParcel = createJavaParcelObject(env);
        if (jParcel != NULL) {
            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
            nativeParcel->setData(obj->data(), obj->dataSize());
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
    }
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        LOGW_EX(env);
        env->ExceptionClear();
    }
}
JNIMediaPlayerListener::mObject字段是一個指向上層MediaPlayer的全局引用。終於的主要操做就是藉助JNI,經過fields.post_event字段的ID值在Native層調用Java層MediaPlayer的方法-MediaPlayer::postEventFromNative():
    /*
     * Called from native code when an interesting event happens.  This method
     * just uses the EventHandler system to post the event back to the main app thread.
     * We use a weak reference to the original MediaPlayer object so that the native
     * code is safe from the object disappearing from underneath it.  (This is
     * the cookie passed to native_setup().)
     */
    private static void postEventFromNative(Object mediaplayer_ref,
                                            int what, int arg1, int arg2, Object obj)
    {
        MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();//拿到給弱引用指向的MediaPlayer實例
        if (mp == null) {
            return;
        }

        if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {//假設是本身主動切換到下一個播放的事件或是MEDIA_INFO
            // this acquires the wakelock if needed, and sets the client side state
            mp.start();//就直接start()
        }
        if (mp.mEventHandler != null) {//最後將該事件發送到EventHandler中處理
            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);//此時what是MEDIA_PREPARED
            mp.mEventHandler.sendMessage(m);
        }
    }
最後會將該事件發送到EventHandler中處理:
    private class EventHandler extends Handler
    {
        private MediaPlayer mMediaPlayer;

        public EventHandler(MediaPlayer mp, Looper looper) {
            super(looper);
            mMediaPlayer = mp;
        }

        @Override
        public void handleMessage(Message msg) {
            if (mMediaPlayer.mNativeContext == 0) {
                Log.w(TAG, "mediaplayer went away with unhandled events");
                return;
            }
            switch(msg.what) {
            case MEDIA_PREPARED:
                try {
                    scanInternalSubtitleTracks();
                } catch (RuntimeException e) {
                    // send error message instead of crashing;
                    // send error message instead of inlining a call to onError
                    // to avoid code duplication.
                    Message msg2 = obtainMessage(
                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
                    sendMessage(msg2);
                }
                if (mOnPreparedListener != null)
                    mOnPreparedListener.onPrepared(mMediaPlayer);
                return;

            case MEDIA_PLAYBACK_COMPLETE:
                if (mOnCompletionListener != null)
                    mOnCompletionListener.onCompletion(mMediaPlayer);
                stayAwake(false);
                return;

            case MEDIA_STOPPED:
                {
                    TimeProvider timeProvider = mTimeProvider;
                    if (timeProvider != null) {
                        timeProvider.onStopped();
                    }
                }
                break;

            case MEDIA_STARTED:
            case MEDIA_PAUSED:
                {
                    TimeProvider timeProvider = mTimeProvider;
                    if (timeProvider != null) {
                        timeProvider.onPaused(msg.what == MEDIA_PAUSED);
                    }
                }
                break;

            case MEDIA_BUFFERING_UPDATE:
                if (mOnBufferingUpdateListener != null)
                    mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
                return;

            case MEDIA_SEEK_COMPLETE:
                if (mOnSeekCompleteListener != null) {
                    mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
                }
                // fall through

            case MEDIA_SKIPPED:
                {
                    TimeProvider timeProvider = mTimeProvider;
                    if (timeProvider != null) {
                        timeProvider.onSeekComplete(mMediaPlayer);
                    }
                }
                return;

            case MEDIA_SET_VIDEO_SIZE:
                if (mOnVideoSizeChangedListener != null) {
                    mOnVideoSizeChangedListener.onVideoSizeChanged(
                        mMediaPlayer, msg.arg1, msg.arg2);
                }
                return;

            case MEDIA_ERROR:
                Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
                boolean error_was_handled = false;
                if (mOnErrorListener != null) {
                    error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
                }
                if (mOnCompletionListener != null && ! error_was_handled) {
                    mOnCompletionListener.onCompletion(mMediaPlayer);
                }
                stayAwake(false);
                return;

            case MEDIA_INFO:
                switch (msg.arg1) {
                case MEDIA_INFO_VIDEO_TRACK_LAGGING:
                    Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
                    break;
                case MEDIA_INFO_METADATA_UPDATE:
                    try {
                        scanInternalSubtitleTracks();
                    } catch (RuntimeException e) {
                        Message msg2 = obtainMessage(
                                MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
                        sendMessage(msg2);
                    }
                    // fall through

                case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
                    msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
                    // update default track selection
                    if (mSubtitleController != null) {
                        mSubtitleController.selectDefaultTrack();
                    }
                    break;
                case MEDIA_INFO_BUFFERING_START:
                case MEDIA_INFO_BUFFERING_END:
                    TimeProvider timeProvider = mTimeProvider;
                    if (timeProvider != null) {
                        timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
                    }
                    break;
                }

                if (mOnInfoListener != null) {
                    mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
                }
                // No real default action so far.
                return;
            case MEDIA_TIMED_TEXT:
                if (mOnTimedTextListener == null)
                    return;
                if (msg.obj == null) {
                    mOnTimedTextListener.onTimedText(mMediaPlayer, null);
                } else {
                    if (msg.obj instanceof Parcel) {
                        Parcel parcel = (Parcel)msg.obj;
                        TimedText text = new TimedText(parcel);
                        parcel.recycle();
                        mOnTimedTextListener.onTimedText(mMediaPlayer, text);
                    }
                }
                return;

            case MEDIA_SUBTITLE_DATA:
                if (mOnSubtitleDataListener == null) {
                    return;
                }
                if (msg.obj instanceof Parcel) {
                    Parcel parcel = (Parcel) msg.obj;
                    SubtitleData data = new SubtitleData(parcel);
                    parcel.recycle();
                    mOnSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
                }
                return;

            case MEDIA_META_DATA:
                if (mOnTimedMetaDataAvailableListener == null) {
                    return;
                }
                if (msg.obj instanceof Parcel) {
                    Parcel parcel = (Parcel) msg.obj;
                    TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
                    parcel.recycle();
                    mOnTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
                }
                return;

            case MEDIA_NOP: // interface test message - ignore
                break;

            default:
                Log.e(TAG, "Unknown message type " + msg.what);
                return;
            }
        }
    }
當前事件是MEDIA_PREPARED。它的處理過程是:
case MEDIA_PREPARED:
                try {
                    scanInternalSubtitleTracks();
                } catch (RuntimeException e) {
                    // send error message instead of crashing;
                    // send error message instead of inlining a call to onError
                    // to avoid code duplication.
                    Message msg2 = obtainMessage(
                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
                    sendMessage(msg2);
                }
                if (mOnPreparedListener != null)
                    mOnPreparedListener.onPrepared(mMediaPlayer);
                return;
當中要注意最後一部分。mOnPreparedListener對象是一個回調,它通常經過setOnPreparedListener()函數註冊:
    /**
     * Register a callback to be invoked when the media source is ready
     * for playback.
     *
     * @param listener the callback that will be run
     */
    public void setOnPreparedListener(OnPreparedListener listener)
    {
        mOnPreparedListener = listener;
    }
像咱們使用異步prepareAsync()時,因爲它的運行會立馬返回。因此咱們就需要系統告訴咱們MediaPlayer的prepare工做什麼時候已經完畢。並在完畢時讓系統調用咱們註冊的回調。讓咱們的MediaPlayer在prepare完畢後開始運行播放。
在EventHandler的處理中,假設mOnPreparedListener不爲空(代表用戶註冊了這個回調,那麼這時就去調用它。以完畢用戶需要的操做)。

這一部分除了分析prepare()函數外,咱們還要注意的是MediaPlayer的狀態事件是怎樣從底層一步一步傳到上層來的,這對咱們理解MediaPlayer的整個實現過程是很是重要的。
最後,在JNI部分的處理中,最外層的process_media_player_call()會依據prepare()的運行結果,推斷是否需要拋出函數處理錯誤或異常信息。

5、開啓MediaPlayer


在MediaPlayer運行完prepare以後,咱們就可以正式開啓它開始播放了。看MediaPlayer::start()方法實現:
    /**
     * Starts or resumes playback. If playback had previously been paused,
     * playback will continue from where it was paused. If playback had
     * been stopped, or never started before, playback will start at the
     * beginning.
     *
     * @throws IllegalStateException if it is called in an invalid state
     */
    public void start() throws IllegalStateException {
        if (isRestricted()) {
            _setVolume(0, 0);
        }
        stayAwake(true);
        _start();
    }

    private native void _start() throws IllegalStateException;
調用start()後。可以是從暫停狀態又一次開始 播放;也但是全然從頭開始播放。直接看它的native層調用:
static void
android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
{
    ALOGV("start");
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
}
有了前面分析的內容鋪墊。咱們就直接進入MediaPlayer::start():
status_t MediaPlayer::start()
{
    ALOGV("start");

    status_t ret = NO_ERROR;
    Mutex::Autolock _l(mLock);

    mLockThreadId = getThreadId();

    if (mCurrentState & MEDIA_PLAYER_STARTED) {//MediaPlayer當前狀態是MEDIA_PLAYER_STARTED,則直接返回
        ret = NO_ERROR;
    } else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
                    MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {//能運行start操做的狀態
        mPlayer->setLooping(mLoop);//mLoop是一個布爾值,它表示當前是否需要循環播放;它的初始值是FALSE,上層可以設置該值
        mPlayer->setVolume(mLeftVolume, mRightVolume);//設置音量
        mPlayer->setAuxEffectSendLevel(mSendLevel);
        mCurrentState = MEDIA_PLAYER_STARTED;//將MediaPlayer的狀態設置爲MEDIA_PLAYER_STARTED
        ret = mPlayer->start();//調用MediaPlayerService::Client的start()
        if (ret != NO_ERROR) {
            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
        } else {
            if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
                ALOGV("playback completed immediately following start()");
            }
        }
    } else {
        ALOGE("start called in state %d", mCurrentState);
        ret = INVALID_OPERATION;
    }

    mLockThreadId = 0;

    return ret;
}
首先會推斷MediaPlayer當前的狀態可否進行start操做。接着。還會涉及到是否循環播放、音量的設置;最後調用到MediaPlayerService::Client::start()方法中:
status_t MediaPlayerService::Client::start()
{
    ALOGV("[%d] start", mConnId);
    sp<MediaPlayerBase> p = getPlayer();
    if (p == 0) return UNKNOWN_ERROR;
    p->setLooping(mLoop);
    return p->start();
}
處理很是easy。設置是否循環播放。調用播放器的start()方法開啓播放(StagefrightPlayer爲例)。

StagefrightPlayer::start()方法實現:

status_t StagefrightPlayer::start() {
    ALOGV("start");

    return mPlayer->play();
}
終於調用到AwesomePlayer::play()方法。經過AwesomePlayer與HAL的交互來完畢視頻的播放操做。
與MediaPlayer的prepare操做類似。當AwesomePlayer完畢start()後,就會調用AwesomePlayer::notifyIfMediaStarted_l()函數:
void AwesomePlayer::notifyIfMediaStarted_l() {
    if (mMediaRenderingStartGeneration == mStartGeneration) {
        mMediaRenderingStartGeneration = -1;
        notifyListener_l(MEDIA_STARTED);
    }
}
它會向上層拋出MEDIA_STARTED事件。該事件的處理流程與prepare階段中的事件處理流程一致(僅僅是有最後處不處理的差異)。

最後,最外層的process_media_player_call()調用會依據start()的運行結果,推斷是否需要拋出函數處理錯誤或異常信息。


至此,MediaPlayer使用演示樣例代碼中的5個重要步驟的底層實現就介紹完了。


事實上MediaPlayer還提供了一系列重載的create()函數。來簡化咱們使用的步驟。咱們僅僅看一個比較具表明性的樣例:
    /**
     * Same factory method as {@link #create(Context, int)} but that lets you specify the audio
     * attributes and session ID to be used by the new MediaPlayer instance.
     * @param context the Context to use
     * @param resid the raw resource id (<var>R.raw.<something></var>) for
     *              the resource to use as the datasource
     * @param audioAttributes the {@link AudioAttributes} to be used by the media player.
     * @param audioSessionId the audio session ID to be used by the media player,
     *     see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
     * @return a MediaPlayer object, or null if creation failed
     */
    public static MediaPlayer create(Context context, int resid,
            AudioAttributes audioAttributes, int audioSessionId) {
        try {
            AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
            if (afd == null) return null;

            MediaPlayer mp = new MediaPlayer();// 1

            final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                new AudioAttributes.Builder().build();
            mp.setAudioAttributes(aa);
            mp.setAudioSessionId(audioSessionId);

            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());// 2
            afd.close();
            mp.prepare();// 3
            return mp;
        } catch (IOException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "create failed:", ex);
           // fall through
        } catch (SecurityException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        }
        return null;
    }
經過MediaPlayer::create()的凝視可知,官方是推薦咱們使用createe()方法的。從代碼中的一、二、3標記可知。咱們僅僅需傳入需要使用的資源標示符就能夠,create()會幫助咱們建立MediaPlayer、同一時候也會去初始化和prepare它。

MediaPlayer::create()讓咱們使用MediaPlayer時更加簡便、快捷。在調用create()函數以後。咱們僅僅需調用start()就可以使用MediaPlayer了。

另外,有了以前的分析,相信咱們再看MediaPlayer的其它函數實現,也不會有太大問題了。

相關文章
相關標籤/搜索