在以前的博客中,已經介紹了使用MediaPlayer時要注意的內容。現在,這裏就經過一個MediaPlayer代碼實例,來進一步分析MediaPlayer內部是怎樣運做、實現的;固然這裏的分析僅僅截止究竟層調用播放器以前,因爲播放器這塊實在是沒搞懂。html
咱們使用的樣例來源於以前MediaPlayer Playback譯文中的官方實例:java
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();
代碼中主要經過5個步驟實現了媒體的播放過程,咱們一步步來分析。android
從MediaPlayer模塊的實現層次來講,它事實上僅僅是一個暴露給外部調用的工具類;真正的媒體操做都經過JNI調用究竟層Media服務,由它們真正實現。cookie
MediaPlayer類要使用一個libmedia_jni.so庫,它的載入步驟例如如下:網絡
static { System.loadLibrary("media_jni"); native_init(); }libmedia_jni.so提供了MediaPlayer需要調用的各個JNI函數,它相應的文件是android_media_MediaPlayer.cpp。load該so庫的同一時候。會調用native_init()函數進行一些前期的初始化工做:
// This function gets some field IDs, which in turn causes class initialization. // It is called from a static block in MediaPlayer, which won't run until the // first time an instance of this class is used. static void android_media_MediaPlayer_native_init(JNIEnv *env)//初始化一些Field和Method域ID { jclass clazz; clazz = env->FindClass("android/media/MediaPlayer"); if (clazz == NULL) { return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "J"); if (fields.context == NULL) { return; } fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.post_event == NULL) { return; } fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J"); if (fields.surface_texture == NULL) { return; } env->DeleteLocalRef(clazz); clazz = env->FindClass("android/net/ProxyInfo"); if (clazz == NULL) { return; } fields.proxyConfigGetHost = env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;"); fields.proxyConfigGetPort = env->GetMethodID(clazz, "getPort", "()I"); fields.proxyConfigGetExclusionList = env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;"); env->DeleteLocalRef(clazz); gPlaybackParamsFields.init(env); gSyncParamsFields.init(env); }
struct fields_t { jfieldID context; jfieldID surface_texture; jmethodID post_event; jmethodID proxyConfigGetHost; jmethodID proxyConfigGetPort; jmethodID proxyConfigGetExclusionList; }; static fields_t fields;從代碼可知,native_init()函數主要保存了一些MediaPlayer.java中定義的一些字段或方法的ID;當中獲取的mNativeContext字段,用於將初始化的本地MediaPlayer對象的地址保存到該變量中,這也就給每一個MediaPlayer.java實例綁定了一個Native層的MediaPlayer;另外,post_event保存了MediaPlayer::postEventFromNative()函數的ID值,它會被用來在Native層中向上層拋出事件或異常。
載入完要使用的動態庫,咱們就可以開始建立MediaPlayer實例了。首先看它的默認構造函數:session
/** * Default constructor. Consider using one of the create() methods for * synchronously instantiating a MediaPlayer from a Uri or resource. * <p>When done with the MediaPlayer, you should call {@link #release()}, * to free the resources. If not released, too many MediaPlayer instances may * result in an exception.</p> */ public MediaPlayer() { Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } mTimeProvider = new TimeProvider(this); mOpenSubtitleSources = new Vector<InputStream>(); IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOps = IAppOpsService.Stub.asInterface(b); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ native_setup(new WeakReference<MediaPlayer>(this));//繼續調用了native_setup()函數 }咱們的MediaPlayer需要運行在消息循環中,EventHandler是MediaPlayer的一個內部類。它專門處理來自Native層的事件,這些事件通常都代表了MediaPlayer現在轉移到了某個狀態,咱們可以在該狀態處理什麼回調操做。EventHandler的功能較爲單一,就是依據底層上拋的事件,進行相應的回調或事件處理。這裏就再也不細看。
接着。調用了native_setup()函數,並傳入了一個MediaPlayer類型的弱引用實例,咱們看該函數的實現:架構
static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { ALOGV("native_setup"); sp<MediaPlayer> mp = new MediaPlayer(); if (mp == NULL) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } // create new listener and give it to MediaPlayer //JNIMediaPlayerListener類繼承自MediaPlayer.h中聲明的MediaPlayerListener,並實現了notify()方法 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener);//在Native MediaPlayer實例中保存這個JNIMediaPlayerListener監聽對象 // Stow our new C++ MediaPlayer in an opaque field in the Java object. setMediaPlayer(env, thiz, mp);//將建立的Native MediaPlayer對象轉化成Long型值(地址),保存到MediaPlayer.java::mNativeContext變量中 }該函數中主要作了三個操做:
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; }
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; }
/** * 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()。
// 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"); } }
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, };
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的推測。
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()函數的處理跟以前一致,興許再也不贅述。
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) { ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); status_t err = UNKNOWN_ERROR; //getMediaPlayerService()會經過ServiceManager找到MediaPlayerService服務的Client端實例,初始化service對象 const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != 0) { //player實例實際是一個MediaPlayerServcie::Client實例,該內部類繼承自IMediaPlayer,負責向外提供其定義的業務服務 sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//經過Binder機制向MediaPlayerService請求建立IMediaPlayer對象 if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(fd, offset, length))) { player.clear(); } err = attachNewPlayer(player); } return err; }首先。獲取一個MediaPlayerService服務的代理實例。MediaPlayerService在媒體播放框架中是一個很是重要的服務,它運行在mediaserver進程中。
MediaPlayerService服務的註冊過程在mediaserver進程建立時發生。main_mediaserver.cpp中:app
InitializeIcuOrDie(); sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate();//啓動MediaPlayerService服務 ResourceManagerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); RadioService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool();
void MediaPlayerService::instantiate() { defaultServiceManager()->addService( String16("media.player"), new MediaPlayerService()); }很是easy地建立MediaPlayerService服務實例。並註冊進系統的實現(要注意。註冊的服務名稱是「media.player」)。接着看它的構造函數實現:
//建立MediaPlayerService實例時,會初始化MediaPlayerService::sFactoryMap成員,註冊各Player工廠對象 MediaPlayerService::MediaPlayerService() { ALOGV("MediaPlayerService created"); mNextConnId = 1; mBatteryAudio.refCount = 0; for (int i = 0; i < NUM_AUDIO_DEVICES; i++) { mBatteryAudio.deviceOn[i] = 0; mBatteryAudio.lastTime[i] = 0; mBatteryAudio.totalTime[i] = 0; } // speaker is on by default mBatteryAudio.deviceOn[SPEAKER] = 1; // reset battery stats // if the mediaserver has crashed, battery stats could be left // in bad state, reset the state upon service start. BatteryNotifier& notifier(BatteryNotifier::getInstance()); notifier.noteResetVideo(); notifier.noteResetAudio(); MediaPlayerFactory::registerBuiltinFactories();//初始化MediaPlayerService::sFactoryMap集合對象 }最後一步調用了MediaPlayerFactory::registerBuildinFactories()函數註冊系統中提供的各個播放器實例:
enum player_type { STAGEFRIGHT_PLAYER = 3, NU_PLAYER = 4, // Test players are available only in the 'test' and 'eng' builds. // The shared library with the test player is passed passed as an // argument to the 'test:' url in the setDataSource call. TEST_PLAYER = 5, }; void MediaPlayerFactory::registerBuiltinFactories() { Mutex::Autolock lock_(&sLock); if (sInitComplete) return; registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER); registerFactory_l(new NuPlayerFactory(), NU_PLAYER); registerFactory_l(new TestPlayerFactory(), TEST_PLAYER); sInitComplete = true; }registerFactory_l()函數會推斷實例的有效性,並將它們保存到集合變量中:
MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap; bool MediaPlayerFactory::sInitComplete = false; status_t MediaPlayerFactory::registerFactory_l(IFactory* factory, player_type type) { if (NULL == factory) { ALOGE("Failed to register MediaPlayerFactory of type %d, factory is" " NULL.", type); return BAD_VALUE; } if (sFactoryMap.indexOfKey(type) >= 0) { ALOGE("Failed to register MediaPlayerFactory of type %d, type is" " already registered.", type); return ALREADY_EXISTS; } if (sFactoryMap.add(type, factory) < 0) { ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add" " to map.", type); return UNKNOWN_ERROR; } return OK; }MediaPlayerFactory中主要涉及了三個類型的播放器:
NuPlayer | 用於播放網絡、本地視頻。或者RTSP協議的視頻流等 |
TestPlayer | 測試用途 |
StagefrightPlayer | 提供的默認播放器,其它播放器不能播放的資源都會讓它播放 |
咱們直接看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 }
//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; }這裏有四個重要調用:
#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()方法,這裏不作具體介紹。
//調用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?建立爲StagefrightPlayer對象後,會爲該對象設置回調對象和函數指針,它調用的是父類MediaPlayerBase::setNotifyCallback()函數;這一步是很是重要的: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; } };
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(); }
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()方法。
// 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實例負責將數據設置下去。它的實現這裏不作分析。
//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字段中 } }
//將新的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()的運行結果。推斷是否需要拋出函數處理錯誤或異常信息。
* 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; }
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; }當中:
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。
//回調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完畢後開始運行播放。
/** * Starts or resumes playback. If playback had previously been paused, * playback will continue from where it was paused. If playback had * been stopped, or never started before, playback will start at the * beginning. * * @throws IllegalStateException if it is called in an invalid state */ public void start() throws IllegalStateException { if (isRestricted()) { _setVolume(0, 0); } stayAwake(true); _start(); } private native void _start() throws IllegalStateException;調用start()後。可以是從暫停狀態又一次開始 播放;也但是全然從頭開始播放。直接看它的native層調用:
static void android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) { ALOGV("start"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } process_media_player_call( env, thiz, mp->start(), NULL, NULL ); }有了前面分析的內容鋪墊。咱們就直接進入MediaPlayer::start():
status_t MediaPlayer::start() { ALOGV("start"); status_t ret = NO_ERROR; Mutex::Autolock _l(mLock); mLockThreadId = getThreadId(); if (mCurrentState & MEDIA_PLAYER_STARTED) {//MediaPlayer當前狀態是MEDIA_PLAYER_STARTED,則直接返回 ret = NO_ERROR; } else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {//能運行start操做的狀態 mPlayer->setLooping(mLoop);//mLoop是一個布爾值,它表示當前是否需要循環播放;它的初始值是FALSE,上層可以設置該值 mPlayer->setVolume(mLeftVolume, mRightVolume);//設置音量 mPlayer->setAuxEffectSendLevel(mSendLevel); mCurrentState = MEDIA_PLAYER_STARTED;//將MediaPlayer的狀態設置爲MEDIA_PLAYER_STARTED ret = mPlayer->start();//調用MediaPlayerService::Client的start() if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; } else { if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { ALOGV("playback completed immediately following start()"); } } } else { ALOGE("start called in state %d", mCurrentState); ret = INVALID_OPERATION; } mLockThreadId = 0; return ret; }首先會推斷MediaPlayer當前的狀態可否進行start操做。接着。還會涉及到是否循環播放、音量的設置;最後調用到MediaPlayerService::Client::start()方法中:
status_t MediaPlayerService::Client::start() { ALOGV("[%d] start", mConnId); sp<MediaPlayerBase> p = getPlayer(); if (p == 0) return UNKNOWN_ERROR; p->setLooping(mLoop); return p->start(); }處理很是easy。設置是否循環播放。調用播放器的start()方法開啓播放(StagefrightPlayer爲例)。
StagefrightPlayer::start()方法實現:
status_t StagefrightPlayer::start() { ALOGV("start"); return mPlayer->play(); }終於調用到AwesomePlayer::play()方法。經過AwesomePlayer與HAL的交互來完畢視頻的播放操做。
void AwesomePlayer::notifyIfMediaStarted_l() { if (mMediaRenderingStartGeneration == mStartGeneration) { mMediaRenderingStartGeneration = -1; notifyListener_l(MEDIA_STARTED); } }它會向上層拋出MEDIA_STARTED事件。該事件的處理流程與prepare階段中的事件處理流程一致(僅僅是有最後處不處理的差異)。
最後,最外層的process_media_player_call()調用會依據start()的運行結果,推斷是否需要拋出函數處理錯誤或異常信息。
/** * Same factory method as {@link #create(Context, int)} but that lets you specify the audio * attributes and session ID to be used by the new MediaPlayer instance. * @param context the Context to use * @param resid the raw resource id (<var>R.raw.<something></var>) for * the resource to use as the datasource * @param audioAttributes the {@link AudioAttributes} to be used by the media player. * @param audioSessionId the audio session ID to be used by the media player, * see {@link AudioManager#generateAudioSessionId()} to obtain a new session. * @return a MediaPlayer object, or null if creation failed */ public static MediaPlayer create(Context context, int resid, AudioAttributes audioAttributes, int audioSessionId) { try { AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); if (afd == null) return null; MediaPlayer mp = new MediaPlayer();// 1 final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); mp.setAudioAttributes(aa); mp.setAudioSessionId(audioSessionId); mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());// 2 afd.close(); mp.prepare();// 3 return mp; } catch (IOException ex) { Log.d(TAG, "create failed:", ex); // fall through } catch (IllegalArgumentException ex) { Log.d(TAG, "create failed:", ex); // fall through } catch (SecurityException ex) { Log.d(TAG, "create failed:", ex); // fall through } return null; }經過MediaPlayer::create()的凝視可知,官方是推薦咱們使用createe()方法的。從代碼中的一、二、3標記可知。咱們僅僅需傳入需要使用的資源標示符就能夠,create()會幫助咱們建立MediaPlayer、同一時候也會去初始化和prepare它。
MediaPlayer::create()讓咱們使用MediaPlayer時更加簡便、快捷。在調用create()函數以後。咱們僅僅需調用start()就可以使用MediaPlayer了。
另外,有了以前的分析,相信咱們再看MediaPlayer的其它函數實現,也不會有太大問題了。