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

By_Your_Side發表於2017-03-27

Android -- MediaPlayer內部實現簡析


在之前的部落格中,已經介紹了使用MediaPlayer時要注意的內容。現在,這裡就通過一個MediaPlayer程式碼例項,來進一步分析MediaPlayer內部是如何運作、實現的;當然這裡的分析只截止到底層呼叫播放器之前,因為播放器這塊實在是沒搞懂。

我們使用的例子來源於之前MediaPlayer Playback譯文中的官方例項:

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個步驟實現了媒體的播放過程,我們一步步來分析。


一、建立MediaPlayer


從MediaPlayer模組的實現層次來說,它其實只是一個暴露給外部呼叫的工具類;真正的媒體操作都通過JNI呼叫到底層Media服務,由它們真正實現。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例項了,首先看它的預設建構函式:

    /**
     * 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;
}

二、設定音訊型別


分析到這裡,我們可以猜測:上層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()的執行結果,判斷是否需要丟擲函式處理錯誤或異常資訊。

三、為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中:
 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());
}
很簡單地建立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()的執行結果,判斷是否需要丟擲函式處理錯誤或異常資訊。

四、準備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()的執行結果,判斷是否需要丟擲函式處理錯誤或異常資訊。

五、開啟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();
}
處理很簡單,設定是否迴圈播放;呼叫播放器的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()方法的。從程式碼中的1、2、3標記可知,我們只需傳入需要使用的資源標示符即可,create()會幫助我們建立MediaPlayer、同時也會去初始化和prepare它。MediaPlayer::create()讓我們使用MediaPlayer時更加簡便、快捷,在呼叫create()函式之後,我們只需呼叫start()就可使用MediaPlayer了。

另外,有了之前的分析,相信我們再看MediaPlayer的其他函式實現,也不會有太大問題了。



相關文章