Android -- 多媒體播放之MediaPlayer使用內部實現簡析
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中:
通過觀察notify()函式的實現,我們發現正如文件介紹的那樣,MediaPlayer確實有自己的內部狀態;首先會根據傳入的msg值更新當前的MediaPlayer狀態,最後通過mListener呼叫它的notify()函式將狀態的變化資訊反饋上層MediaPlayer中。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,
};
如果需要丟擲異常,則直接在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中主要涉及了三個型別的播放器: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()函式的定義是:
MediaPlayerService::Client::setDataSource_pre()在建立播放器例項結束後,還有處理: 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丟擲事件。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為例:
最後呼叫setDataSource_post()函式進行一些掃尾工作:
// 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()函式的實現是:
我們分析真正的實現函式prepare_l()方法:status_t MediaPlayer::prepareAsync()
{
ALOGV("prepareAsync");
Mutex::Autolock _l(mLock);
return prepareAsync_l();
}
對比兩個函式的實現內容,即可發現這裡同步、非同步的區別主要是在是否等待mLock這個鎖上面。// 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()函式:
在EventHandler的處理中,如果mOnPreparedListener不為空(表明使用者註冊了這個回撥,那麼這時就去呼叫它,以完成使用者需要的操作)。
這一部分除了分析prepare()函式外,我們還要注意的是MediaPlayer的狀態事件是如何從底層一步一步傳到上層來的,這對我們理解MediaPlayer的整個實現過程是很重要的。
//回撥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()方法實現:
與MediaPlayer的prepare操作類似,當AwesomePlayer完成start()後,就會呼叫AwesomePlayer::notifyIfMediaStarted_l()函式:
/**
* 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()函式,來簡化我們使用的步驟。我們只看一個比較具代表性的例子:
另外,有了之前的分析,相信我們再看MediaPlayer的其他函式實現,也不會有太大問題了。
/**
* 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的其他函式實現,也不會有太大問題了。
相關文章
- Android多媒體之認識MP3與內建媒體播放(MediaPlayer)Android
- Android多媒體之視訊播放器(基於MediaPlayer)Android播放器
- Android音視訊之MediaPlayer音視訊播放Android
- Android多媒體之認識聲音、錄音與播放(PCM)Android
- 利用ATL實現QuickTime多媒體檔案播放 (轉)UI
- C# DirectShow 播放多媒體C#
- Android 媒體播放框架MediaSession分析與實踐Android框架Session
- CPU簡介(2)內部結構簡析
- 使用Html5 多媒體實現微信語音功能HTML
- [Android多媒體技術] 播放Raw/Assets音視訊方法總結Android
- 多媒體播放器哪款值得入手?播放器
- OmniPlayer Pro for Mac(全能多媒體播放器)Mac播放器
- Android記憶體測試方法梳理和實現原理簡析Android記憶體
- Android 多媒體之 Silk 格式音訊解碼Android音訊
- Android多媒體之Camera的相關操作Android
- [流媒體]ijkplayer 播放器,android平臺使用說明播放器Android
- Entitas實現簡析
- 在VB中用API實現多媒體 (轉)API
- OpenHarmony 3.2 Beta多媒體系列——音影片播放框架框架
- OmniPlayer Pro for Mac(全能多媒體播放器)1.4.8Mac播放器
- Android Media(多媒體)獲取手機記憶體卡下的音樂並播放Android記憶體
- Android音訊開發之MediaRecorder/MediaPlayerAndroid音訊
- 自帶多媒體視訊播放器Infuse pro播放器
- 解決MPLAYER播放不了多媒體檔案的方法。
- kafka的內部實現、安裝和使用Kafka
- Android多媒體之Camera2的相關操作Android
- Android多媒體之SoundPool+pcm流的音訊操作Android音訊
- 內部社會化媒體生態系統
- JMeter流媒體線上播放HLS外掛BlazeMeter - HLS Plugin實現視訊線上播放壓測JMeterPlugin
- 基於Android的MediaPlayer的音樂播放器的封裝Android播放器封裝
- 支援M1、Infuse Pro for Mac「多媒體播放器」Mac播放器
- CES展:FIC展示Linux多媒體播放器(轉)Linux播放器
- 【多媒體之聲音總結】
- KVO的內部實現
- 人人都是昏析師:社交媒體需要簡化使用者使用協議條款協議
- 直播軟體搭建,Android實現文字滾動播放效果Android
- Android的IPC機制(二)——AIDL實現原理簡析AndroidAI
- Android實現線上播放音樂Android