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字段中
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的推測。
三、为MediaPlayer设置资源
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中:
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; }这里有四个重要调用:
- MediaPlayerFactory::getPlayerType():为当前资源选择合适的播放器类型
- setDataSource_pre():创建合适的播放器对象
- MediaPlayerBase::setDataSource():调用播放器的setDataSource()方法,真正去设置资源
- setDataSource_post():运行收尾工作
#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?创建为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实例负责将数据设置下去。它的实现这里不做分析。
最后调用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字段中 } }
//将新的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实例
* 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; }当中:
- cookie指向当前调用MediaPlayerService::Client::createPlayer()的Client实例
- notifyFunc是一个函数指针,这里指向MediaPlayerService::Client::notify()函数
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完毕后開始运行播放。
在EventHandler的处理中,假设mOnPreparedListener不为空(表明用户注冊了这个回调,那么这时就去调用它。以完毕用户须要的操作)。
这一部分除了分析prepare()函数外,我们还要注意的是MediaPlayer的状态事件是怎样从底层一步一步传到上层来的,这对我们理解MediaPlayer的整个实现过程是非常重要的。
五、开启MediaPlayer
/** * 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的交互来完毕视频的播放操作。
与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()的运行结果,推断是否须要抛出函数处理错误或异常信息。
/** * 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的其它函数实现,也不会有太大问题了。