基於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