MediaPlayer(四)--MediaPlayer()流程

基於Android8.1代碼

#java MediaPlayer()
先從源頭開始看
frameworks/base/media/java/android/media/MediaPlayer.java

    public MediaPlayer() {
        super(new AudioAttributes.Builder().build(),
                AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);

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

        //2
        mTimeProvider = new TimeProvider(this);
        //3
        mOpenSubtitleSources = new Vector<InputStream>();

       //4
        /* 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));
        //5
        baseRegisterPlayer();
    }

1 創建EventHandler(重點
後面jni回調java時會調用到postEventFromNative, postEventFromNative會將消息發給handler處理
2 mTimeProvider
對這個不太瞭解, 這是MediaPlayer的一個內部類,繼承自MediaTimeProvider, 好像是一個用了提供Meida 播放,seek,buffer時間數據的類。
3 mOpenSubtitleSources
字幕源
4 native_setup(重點
是對 native層MediaPlayer和回調listener的創建和初始化
5 baseRegisterPlayer()
這個函數在MediaPlayer的父類PlayerBase.主要是獲取AppOpsService, 即Application Operations Service,是關於系統應用權限管理的,這些API不對第三方應用開放。

這裏主幹創建流程主要關注1 和 4. 即創建handle和native層的MediaPlayer, 後續native層即可回調java層接口,java層將信息發送給handle處理

#native_setup(new WeakReference(this));
frameworks/base/media/jni/android_media_MediaPlayer.cpp

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
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);
}

native_setup做了幾件事情,
1 創建native層播放器實例MediaPlayer()
2 創建了JNIMediaPlayerListener,將java的MediaPlayer實例傳遞進去
3 將JNIMediaPlayerListener 設置給MediaPlayer()
4 將FFMediaPlayer 設置給Java層的mNativeContext

後面native層的MediaPlayer 就通過JNIMediaPlayerListener 回調java層接口。JNIMediaPlayerListener 能夠訪問全局變量 fields.post_event, 同時擁有 java的MediaPlayer實例,所以JNIMediaPlayerListener能夠回調java層的postEventFromNative,並將MediaPlayer實例傳遞回去
關於這一部分listener的介紹可以參考
搭建ffmpeg player(一)–搭建上層框架

下面在分析一下setMediaPlayer,函數的目的是將將c++層MediaPlayer實例設置到java層的變量中。對於不熟悉智能指針的朋友可能有點繞

//假設引用計數爲n, 這裏傳參不會增加計數
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
    Mutex::Autolock l(sLock);
   //使用臨時變量old,引用計數加1, n+1
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    if (player.get()) {
        //主動增加新設置的player的引用計數, n+2
        player->incStrong((void*)setMediaPlayer);
    }
    if (old != 0) {
        //主動減少舊的player的引用計數, n+1
        old->decStrong((void*)setMediaPlayer);
    }
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}
//函數結束,如果外部沒有使用函數的返回值,則引用計數會減1,變爲n
  • 參數 sp& player
    這個參數傳的是智能指針的引用, 引用不會增加智能指針的計數,這會導致什麼問題呢。如果外部智能指針的計數爲0了,MediaPlayer的實例會被釋放掉的。如果參數是用傳值,則會增加智能指針的計數,在函數執行結束後智能指針計數會減1。
  • sp old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    這裏是先獲取java層的mNativeContext變量,因爲jni層均爲static方法,是對應java層所有實例對象的,jni層本身不保存任何的實例,所以這裏需要先獲取java層對應實例對象的變量mNativeContext,即保存的c++層MediaPlayer實例。
  • env->SetLongField(thiz, fields.context, (jlong)player.get());
    函數最後將MediaPlayer的指針傳遞給java層。使用get()返回的指針,當最後一個對應的智能指針銷燬後,指針就變爲無效了。所以需要有辦法保證MediaPlayer在銷燬之前,player智能指針的引用計數不能爲0.
    一種方法是使用全局的sp變量來引用MediaPlayer,不過Android並沒有這麼做,因爲採用全局變量,則jni層只能給唯一一個播放器應用使用,這顯然不符合要求。
    Android的做法是將sp的計數同 java層保存該指針的變量 private long mNativeContext 對應起來。首先參數採用引用sp&,不會增加計數,接着用臨時變量獲取java層實例會增加計數。接着主動增加sp的引用計數,在主動減少舊的sp的引用計數,再通過get()方法將指針傳遞給java層,這樣保證java層變量不銷燬時,sp的引用計數不爲0。不過這樣就需要在後續java層實例銷燬的流程中,主動將引用計數減1

#c++ MediaPlayer()
frameworks/av/media/libmedia/mediaplayer.cpp

MediaPlayer::MediaPlayer()
{
    ALOGV("constructor");
    mListener = NULL;     // MediaPlayerListener, JNIMediaPlayerListener的父類,回調java層
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mSeekPosition = -1;
    mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mCurrentState = MEDIA_PLAYER_IDLE;    //播放器當前狀態 IDLE
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    mAudioSessionId = (audio_session_t) 
    AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}

在構造函數裏將mCurrentState 設置成MEDIA_PLAYER_IDLE

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章