Android -- MediaPlayer内部实现简析

在之前的博客中,已经介绍了使用MediaPlayer时要注意的内容。如今,这里就通过一个MediaPlayer代码实例,来进一步分析MediaPlayer内部是怎样运作、实现的;当然这里的分析仅仅截止究竟层调用播放器之前,由于播放器这块实在是没搞懂。

我们使用的样例来源于之前MediaPlayer Playback译文中的官方实例:

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

代码中主要通过5个步骤实现了媒体的播放过程,我们一步步来分析。

一、创建MediaPlayer

从MediaPlayer模块的实现层次来说,它事实上仅仅是一个暴露给外部调用的工具类;真正的媒体操作都通过JNI调用究竟层Media服务,由它们真正实现。

MediaPlayer类要使用一个libmedia_jni.so库,它的载入步骤例如以下:

   static {
System.loadLibrary("media_jni");
native_init();
}

libmedia_jni.so提供了MediaPlayer须要调用的各个JNI函数,它相应的文件是android_media_MediaPlayer.cpp。load该so库的同一时候。会调用native_init()函数进行一些前期的初始化工作:

// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)//初始化一些Field和Method域ID
{
jclass clazz; clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
} fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
} fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
} fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
} env->DeleteLocalRef(clazz); clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
} fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;"); fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I"); fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;"); env->DeleteLocalRef(clazz); gPlaybackParamsFields.init(env);
gSyncParamsFields.init(env);
}
struct fields_t {
jfieldID context;
jfieldID surface_texture; jmethodID post_event; jmethodID proxyConfigGetHost;
jmethodID proxyConfigGetPort;
jmethodID proxyConfigGetExclusionList;
};
static fields_t fields;

从代码可知,native_init()函数主要保存了一些MediaPlayer.java中定义的一些字段或方法的ID;当中获取的mNativeContext字段,用于将初始化的本地MediaPlayer对象的地址保存到该变量中,这也就给每个MediaPlayer.java实例绑定了一个Native层的MediaPlayer;另外,post_event保存了MediaPlayer::postEventFromNative()函数的ID值,它会被用来在Native层中向上层抛出事件或异常。

载入完要使用的动态库,我们就能够開始创建MediaPlayer实例了。首先看它的默认构造函数:

    /**
* Default constructor. Consider using one of the create() methods for
* synchronously instantiating a MediaPlayer from a Uri or resource.
* <p>When done with the MediaPlayer, you should call {@link #release()},
* to free the resources. If not released, too many MediaPlayer instances may
* result in an exception.</p>
*/
public MediaPlayer() { Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
} mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b); /* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaPlayer>(this));//继续调用了native_setup()函数
}

我们的MediaPlayer须要运行在消息循环中,EventHandler是MediaPlayer的一个内部类。它专门处理来自Native层的事件,这些事件一般都表明了MediaPlayer如今转移到了某个状态,我们能够在该状态处理什么回调操作。EventHandler的功能较为单一,就是依据底层上抛的事件,进行相应的回调或事件处理。这里就不再细看。

接着。调用了native_setup()函数,并传入了一个MediaPlayer类型的弱引用实例,我们看该函数的实现:

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
} // create new listener and give it to MediaPlayer
//JNIMediaPlayerListener类继承自MediaPlayer.h中声明的MediaPlayerListener,并实现了notify()方法
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);//在Native MediaPlayer实例中保存这个JNIMediaPlayerListener监听对象 // Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);//将创建的Native MediaPlayer对象转化成Long型值(地址),保存到MediaPlayer.java::mNativeContext变量中
}

该函数中主要做了三个操作:

  • 创建了一个Native MediaPlayer对象
  • 创建了一个JNIMediaPlayerListener对象,它主要用于向上层MediaPlayer(.java)对象通知事件或抛出异常
  • 将创建的Native MediaPlayer实例保存到MediaPlayer.java::mNativeContext字段中
Native MediaPlayer类继承自BnMediaPlayerClient,它是IMediaPlayerClient业务的服务端。IMediaPlayerClient业务的整个框架如图所看到的:
Native MediaPlayer的构造函数较为简单,就是一些字段的初始化操作,重要的是设置了默认的音频流类型,并将MediaPlayer的状态初始化为Idle:
MediaPlayer::MediaPlayer()
{
ALOGV("constructor");
mListener = NULL;
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;//默认音频流类型
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;//MediaPlayer的初始状态
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false;//是否循环播放
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
另外,还有默认同步状态的设置等等。

JNIMediaPlayerListener类继承自MediaPlayerListener,它声明了一个notify()函数。该函数主要用来向上层抛出事件或其它异常:

class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
JNIMediaPlayerListener();
jclass mClass; // Reference to MediaPlayer class
jobject mObject; // Weak ref to MediaPlayer Java object to call on
};

JNIMediaPlayerListener的构造函数中用之前传入的MediaPlayer弱引用实例构造了一个Native层全局的变量mObject。而且也保存了一份MediaPlayer.java的类型实例:

JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{ // Hold onto the MediaPlayer class for use in calling the static method
// that posts events to the application thread.
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
} mClass = (jclass)env->NewGlobalRef(clazz);//代表MediaPlayer.java类型的实例 // We use a weak reference so the MediaPlayer object can be garbage collected.
// The reference is only used as a proxy for callbacks. mObject = env->NewGlobalRef(weak_thiz);//weak_thiz是MediaPlayer.java实例的一个弱引用
}

JNIMediaPlayerListener::notify()函数用来向上层抛出事件或异常:

//回调MediaPlayer.java中的postEventFromNative()方法,反馈Native层发生的事件;postEventFromNative()会EventHandler(运行在MediaPalyer的线程中)
//发送附带msg參数的消息,EventHandler推断当前的事件类型,如MEDIA_PREPARED等,最后调用应用程序设置的相关回调处理相应的事件信息
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}

JNIMediaPlayerListener实例创建后,会将它保存到Native
MediaPlayer::mListener字段中。最后掉setMediaPlayer()方法将mp对象转换成地址值的形式保存到上层MediaPlayer中:

static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
env->SetLongField(thiz, fields.context, (jlong)player.get());
return old;
}

二、设置音频类型

分析到这里。我们能够推測:上层MediaPlayer和Native MediaPlayer之间的关系就相当于Client - Server结构。前者是Client,后者是Server;前者向后者发送服务请求。接着。我们调用了MediaPlayer::setAudioStreamType()函数设置了当前的音频流类型:
    /**
* Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
* for a list of stream types. Must call this method before prepare() or
* prepareAsync() in order for the target stream type to become effective
* thereafter.
*
* @param streamtype the audio stream type
* @see android.media.AudioManager
*/
public void setAudioStreamType(int streamtype) {
_setAudioStreamType(streamtype);
mStreamType = streamtype;
} private native void _setAudioStreamType(int streamtype);

从函数凝视能够。这里设置的音频流类型必须是AudioManager中定义过的,当前版本号中所支持的类型有:

    /** The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** The audio stream for system sounds */
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/** The audio stream for the phone ring */
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/** The audio stream for music playback */
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/** The audio stream for alarms */
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
/** The audio stream for notifications */
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide The audio stream for phone calls when connected to bluetooth */
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
/** The audio stream for DTMF Tones */
public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
/** @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = AudioSystem.STREAM_TTS;

最后调用native函数_setAudioStreamType()将类型值设置下去,看它的实现:

static void
android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint streamtype)
{
ALOGV("setAudioStreamType: %d", streamtype);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);//获取创建时设置到MediaPlayer.java实例中的Native MediaPlayer实例
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}

首先获取到之前保存到MediaPlayer.java::mNativeContext字段中的Native
MediaPlayer对象;最后调用process_media_player_call()函数,当中包括了通过mp实例设置音频类型的调用:MediaPlayer::setAudioStreamType()。

我们分解来分析,process_media_player_call()函数会依据函数的运行结果opStatus,以及附带的exception和message信息;推断是否须要向上反馈状态事件或抛出异常信息:
// If exception is NULL and opStatus is not OK, this method sends an error
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
//依据函数的运行结果opStatus,以及附带的exception和message信息;推断是否须要反馈操作失败事件或抛出异常信息
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {//假设无需抛出异常,但存在函数处理错误,则向上层抛出事件
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);//重要
}
} else { // Throw exception! 假设须要抛出异常,则构建相应的异常并抛出
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) BAD_VALUE ) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
// if the message is too long, don't bother displaying the status code
jniThrowException( env, exception, message);
} else {
char msg[256];
// append the status code to the message
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}

假设我们的操作无需抛出异常,而且当前的函数调用有错误,就须要通过Native MediaPlayer调用notify()向上层抛出错误:

//向应用程序反馈当前状态变化的回调事件,设置当前MediaPlayer的状态
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
bool locked = false; // TODO: In the future, we might be on the same thread if the app is
// running in the same process as the media server. In that case,
// this will deadlock.
//
// The threadId hack below works around this for the care of prepare,
// seekTo and start within the same process.
// FIXME: Remember, this is a hack, it's not even a hack that is applied
// consistently for all use-cases, this needs to be revisited.
if (mLockThreadId != getThreadId()) {
mLock.lock();
locked = true;
} // Allows calls from JNI in idle state to notify errors
if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
if (locked) mLock.unlock(); // release the lock when done.
return;
} switch (msg) {
case MEDIA_NOP: // interface test message
break;
case MEDIA_PREPARED:
ALOGV("prepared");
mCurrentState = MEDIA_PLAYER_PREPARED;
if (mPrepareSync) {
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mSignal.signal();
}
break;
case MEDIA_PLAYBACK_COMPLETE:
ALOGV("playback complete");
if (mCurrentState == MEDIA_PLAYER_IDLE) {
ALOGE("playback complete in idle state");
}
if (!mLoop) {
mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
}
break;
case MEDIA_ERROR://假设是有函数处理错误发生
// Always log errors.
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
ALOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;//则将当前状态设置为ERROR
if (mPrepareSync)
{
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = ext1;
mSignal.signal();
send = false;
}
break;
case MEDIA_INFO:
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
ALOGW("info/warning (%d, %d)", ext1, ext2);
}
break;
case MEDIA_SEEK_COMPLETE:
ALOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition) {
ALOGV("Executing queued seekTo(%d)", mSeekPosition);
mSeekPosition = -1;
seekTo_l(mCurrentPosition);
}
else {
ALOGV("All seeks complete - return to regularly scheduled program");
mCurrentPosition = mSeekPosition = -1;
}
break;
case MEDIA_BUFFERING_UPDATE:
ALOGV("buffering %d", ext1);
break;
case MEDIA_SET_VIDEO_SIZE:
ALOGV("New video size %d x %d", ext1, ext2);
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
case MEDIA_TIMED_TEXT:
ALOGV("Received timed text message");
break;
case MEDIA_SUBTITLE_DATA:
ALOGV("Received subtitle data message");
break;
case MEDIA_META_DATA:
ALOGV("Received timed metadata message");
break;
default:
ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
} //JNIMediaPlayerListener继承自MediaPlayerListener,并实现了notify()方法;声明实如今android_media_MediaPlayer.cpp中
sp<MediaPlayerListener> listener = mListener;//mListener保存了MediaPlayer实例创建时初始化的JNIMediaPlayerListener监听对象
if (locked) mLock.unlock(); // this prevents re-entrant calls into client code
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
ALOGV("callback application");
listener->notify(msg, ext1, ext2, obj);//调用JNIMediaPlayerListener类实例的notify()方法
ALOGV("back from callback");
}
}
notify()函数使用的msg推断值定义在MediaPlayer.h中:
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,
};

通过观察notify()函数的实现,我们发现正如文档介绍的那样。MediaPlayer确实有自己的内部状态;首先会依据传入的msg值更新当前的MediaPlayer状态,最后通过mListener调用它的notify()函数将状态的变化信息反馈上层MediaPlayer中。

假设须要抛出异常,则直接在JNI层抛出异常就可以。假设即没有错误,也没有异常须要抛出,则什么都不做。

process_media_player_call()方法的功能非常明显,就是向上抛出函数处理错误事件或者异常信息。它的运行又依赖某些操作的运行结果。我们接着看这部分的音频流类型设置操作:
status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type)
{
ALOGV("MediaPlayer::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {
// Can't change the stream type after prepare
ALOGE("setAudioStream called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// cache
mStreamType = type;
return OK;
}

mStreamType保存当前设置的流类型,初始值是AUDIO_STREAM_MUSIC;假设要设置的类型和当前类型一致,则直接无错误返回;否则。推断MediaPlayer的当前状态能否够进行当前操作,假设能够。则更新mStreamType的值并返回。音频流类型设置的操作就结束了,该部分实现比較简单。到此能够看到的结果。就是将须要设置的类型值保存到了Native
MediaPlayer中。另外。从这个简单调用的处理过程来看,也证实了我们前面关于Client/Server的推測。

最后。JNI中最外层的process_media_player_call()会依据setAudioStreamType()的运行结果,推断是否须要抛出函数处理错误或异常信息。

三、为MediaPlayer设置资源

要为MediaPlayer设置资源项。我们要调用MediaPlayer::setDataSource()方法。MediaPlayer中提供了非常多重载版本号的setDataSource()方法。这是由于MediaPlayer同意我们使用多种不同类型的资源。关于这部分内容,在之前的翻译文档中有具体的介绍。可是在setDataSource()中,终于基本的处理过程是获取到当前资源的文件描写叙述符。并调用native方法向下设置资源;可是。假设当前设置的是网络资源或者其它,则处理方式略有不同,我们不讨论这部分,但也可能会提及一些。看对资源文件描写叙述符设置的操作:
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException; static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
} if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

与第二部分音频流类型设置的处理操作相似,我们直接看调用MediaPlayer::setDataSource()处理的过程,process_media_player_call()函数的处理跟之前一致,兴许不再赘述。

 MediaPlayer::setDataSource函数处理:
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
status_t err = UNKNOWN_ERROR; //getMediaPlayerService()会通过ServiceManager找到MediaPlayerService服务的Client端实例,初始化service对象
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
//player实例实际是一个MediaPlayerServcie::Client实例,该内部类继承自IMediaPlayer,负责向外提供其定义的业务服务
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//通过Binder机制向MediaPlayerService请求创建IMediaPlayer对象
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
err = attachNewPlayer(player);
}
return err;
}

首先。获取一个MediaPlayerService服务的代理实例。MediaPlayerService在媒体播放框架中是一个非常重要的服务,它运行在mediaserver进程中。

MediaPlayerService服务的注冊过程在mediaserver进程创建时发生。main_mediaserver.cpp中:

 InitializeIcuOrDie();
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate();
MediaPlayerService::instantiate();//启动MediaPlayerService服务
ResourceManagerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
SoundTriggerHwService::instantiate();
RadioService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
MediaPlayerService是IMediaPlayerService业务架构中的服务端。该业务架构的图演示样例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3Nkbl9vZl9jb2Rlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

我们再看MediaPlayerService::instantiate()的处理:

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中主要涉及了三个类型的播放器:

MediaPlayerFactory提供的播放器类型
NuPlayer 用于播放网络、本地视频。或者RTSP协议的视频流等
TestPlayer 測试用途
StagefrightPlayer 提供的默认播放器,其它播放器不能播放的资源都会让它播放
当中,NuPlayer和StagefrightPlayer等Player类的继承关系如图所看到的:
介绍了有关MediaPlayerService有关的内容后,我们再返回到指定内容。在得到MediaPlayerService服务的代理service后,会去调用create()函数创建一个IMediaPlayer服务的代理对象player。IMediaPlayer业务是真正跟播放器相关的,该业务的结构如图所看到的:
它的服务端实现类是MediaPlayerService::Client这个内部类。

我们直接看MediaPlayerService::create()函数是怎样创建Player的:

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
int audioSessionId)
{
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId); //创建Client实例
//Clinet是MediaPlayerService的内部类;它集成自BnMediaPlayer,是IMediaPlayer服务的Service组件,对外提供MediaPlayer的各种服务
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid()); ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid()); wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);//mClients变量维护了MediaPlayerService中的Client实例集合
}
return c;
}

MediaPlayerService::Client类是IMediaPlayer业务的服务端,也就能够把它看成是提供MediaPlayer服务的提供者。

这里创建了一个Client实例,并将它保存到了mClients集合中,我们能够理解为mClients中保存了当前全部存在的Player对象。分析Client的构造函数:

MediaPlayerService::Client::Client(
const sp<MediaPlayerService>& service, pid_t pid,
int32_t connId, const sp<IMediaPlayerClient>& client,
int audioSessionId, uid_t uid)
{
ALOGV("Client(%d) constructor", connId);
mPid = pid;
mConnId = connId;
mService = service;//保存当前的IMediaPlayerService实例
mClient = client;//保存的是Native MediaPlayer(MediaPlayer.cpp)实例
mLoop = false;
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
mUID = uid;
mRetransmitEndpointValid = false;
mAudioAttributes = NULL; #if CALLBACK_ANTAGONIZER
ALOGD("create Antagonizer");
mAntagonizer = new Antagonizer(notify, this);
#endif
}
再返回到MediaPlayer::setDataSource()中。创建为IMediaPlayer实例后,主要会调用它的setDataSource()函数:
//MediaPlayerFactory是播放器创建的工厂,提供打分功能,以让系统在当前视频源下找到合适类型的播放器进行播放
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length);
struct stat sb;
int ret = fstat(fd, &sb);
if (ret != 0) {
ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
return UNKNOWN_ERROR;
} ALOGV("st_dev = %llu", static_cast<uint64_t>(sb.st_dev));
ALOGV("st_mode = %u", sb.st_mode);
ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
ALOGV("st_size = %llu", sb.st_size); if (offset >= sb.st_size) {
ALOGE("offset error");
::close(fd);
return UNKNOWN_ERROR;
}
if (offset + length > sb.st_size) {
length = sb.st_size - offset;
ALOGV("calculated length = %lld", length);
} player_type playerType = MediaPlayerFactory::getPlayerType(this,
fd,
offset,
length);//获取到当前适合播放该视频源的播放器类型
sp<MediaPlayerBase> p = setDataSource_pre(playerType);//setDataSource_pre()创建一个合适的Player播放器实例
if (p == NULL) {
return NO_INIT;
} // now set data source
setDataSource_post(p, p->setDataSource(fd, offset, length));//先调用播放器实例的setDataSource()方法,为它设置资源;再调用setDataSource_post()完毕收尾工作
return mStatus;
}

这里有四个重要调用:

  • MediaPlayerFactory::getPlayerType():为当前资源选择合适的播放器类型
  • setDataSource_pre():创建合适的播放器对象
  • MediaPlayerBase::setDataSource():调用播放器的setDataSource()方法,真正去设置资源
  • setDataSource_post():运行收尾工作
如今一步步看。

MediaPlayerFactory::getPlayerType()用于得到合适的播放器类型,它内部主要调用宏定义函数GET_PLAYER_TYPE_IMPL():
#define GET_PLAYER_TYPE_IMPL(a...)                      \
Mutex::Autolock lock_(&sLock); \
\
player_type ret = STAGEFRIGHT_PLAYER;//默认播放器类型 \
float bestScore = 0.0; \ //依据当前传入的视频源,对各个播放器进行比較打分,找到合适的播放器;否则使用默认播放器StagefightPlayer \
for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
\
IFactory* v = sFactoryMap.valueAt(i); \
float thisScore; \
CHECK(v != NULL); \
thisScore = v->scoreFactory(a, bestScore); \
if (thisScore > bestScore) { \
ret = sFactoryMap.keyAt(i); \
bestScore = thisScore; \
} \
} \
\
if (0.0 == bestScore) { \
ret = getDefaultPlayerType();//依据"media.stagefright.use-awesome"属性配置,选择当前默认的播放器 \
} \
\
return ret; \

该函数的实现就是遍历MediaPlayerFactory创建时注冊的各个播放器工厂对象的scoreFactory()方法。对当前设置的资源进行评估,得到最符合该资源的播放器工厂类型并返回。各个播放器的scoreFactory()方法,这里不做具体介绍。

setDataSource_pre()函数会依据得到的播放器类型去创建相应的播放器实例:
//调用createPlayer()创建播放器实例对象
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
ALOGV("player type = %d", playerType); // create the right type of player
sp<MediaPlayerBase> p = createPlayer(playerType);
if (p == NULL) {
return p;
} if (!p->hardwareOutput()) {//以StagefrightPlayer为例,它没有重写父类的MediaPlayerInterface::hardwareOutput()方法;返回false,表示音频不直接输出到硬件上.
Mutex::Autolock l(mLock);
mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
mPid, mAudioAttributes);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);//调用父类MediaPlayerInterface::setAudioSink()方法
} return p;
}

当中MediaPlayerFactory::createPlayer运行播放器创建工作:

//假设当前持有了播放器实例对象,则要推断它是否与我们须要的播放器类型相符;
//假设不相符,则删除它;再又一次创建该类型的播放器实例
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// determine if we have the right player type
sp<MediaPlayerBase> p = mPlayer;
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();//重置该sp<>指针
}
if (p == NULL) {
p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);//创建播放器对象;重要:createPlayer()里面setNotifyCallback()函数调用
} if (p != NULL) {
p->setUID(mUID);
} return p;
}

假设当前持有的播放器类型与须要的不符,则会先销毁掉它,并按类型又一次创建一个新播放器对象。MediaPlayerFactory::createPlayer()运行播放器对象的创建操作:

//找到相应视频源类型的播放器工厂实例,并创建Player对象;最后给该Player对象设置传递消息的notifyFunc回调函数
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
player_type playerType,
void* cookie,
notify_callback_f notifyFunc,
pid_t pid) {
sp<MediaPlayerBase> p;
IFactory* factory;
status_t init_result;
Mutex::Autolock lock_(&sLock); if (sFactoryMap.indexOfKey(playerType) < 0) {
ALOGE("Failed to create player object of type %d, no registered"
" factory", playerType);
return p;
} factory = sFactoryMap.valueFor(playerType);//从sFactoryMap中获取到类型相应的Player工厂对象
CHECK(NULL != factory);
p = factory->createPlayer(pid);//创建相应类型的播放器实例,直接new实例对象;如创建StagefrightPlayer,期间会实例化StagefrightPlayer::(AwesomePlayer *mPlayer)成员 if (p == NULL) {
ALOGE("Failed to create player object of type %d, create failed",
playerType);
return p;
} init_result = p->initCheck();//实现直接return OK;
if (init_result == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);//调用父类MediaPlayerBase::setNotifyCallback();cookie是当前调用MediaPlayerService::Client::createPlayer()的Client实例,notifyFunc这里是MediaPlayerService::Client::notify()函数指针.
} else {
ALOGE("Failed to create player object of type %d, initCheck failed"
" (res = %d)", playerType, init_result);
p.clear();
} return p;
}

依据须要创建的播放器类型。在sFactoryMap集合中找到相应的播放器工厂对象,并调用的createPlayer()方法真正创建实例,这里以StagefrightPlayer为例:

//StagefrightPlayer是默认播放器
class StagefrightPlayerFactory :
public MediaPlayerFactory::IFactory {
public:
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
int fd,
int64_t offset,
int64_t length,
float /*curScore*/) {
if (legacyDrm()) {
sp<DataSource> source = new FileSource(dup(fd), offset, length);
String8 mimeType;
float confidence;
if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) {
return 1.0;
}
} if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) {
char buf[20];
lseek(fd, offset, SEEK_SET);
read(fd, buf, sizeof(buf));
lseek(fd, offset, SEEK_SET); uint32_t ident = *((uint32_t*)buf); // Ogg vorbis? if (ident == 0x5367674f) // 'OggS'
return 1.0;
} return 0.0;
} virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
const char* url,
float /*curScore*/) {
if (legacyDrm() && !strncasecmp("widevine://", url, 11)) {
return 1.0;
}
return 0.0;
} virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {
ALOGV(" create StagefrightPlayer");
return new StagefrightPlayer();//实例化StagefrightPlayer对象,并初始化AwesomePlayer *mPlayer成员
}
private:
bool legacyDrm() {
char value[PROPERTY_VALUE_MAX];
if (property_get("persist.sys.media.legacy-drm", value, NULL)
&& (!strcmp("1", value) || !strcasecmp("true", value))) {
return true;
}
return false;
}
};

创建为StagefrightPlayer对象后,会为该对象设置回调对象和函数指针,它调用的是父类MediaPlayerBase::setNotifyCallback()函数;这一步是非常重要的:

init_result = p->initCheck();//实现直接return OK;
if (init_result == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);//调用父类MediaPlayerBase::setNotifyCallback();cookie是当前调用MediaPlayerService::Client::createPlayer()的Client实例,notifyFunc这里是MediaPlayerService::Client::notify()函数指针.
} else {
ALOGE("Failed to create player object of type %d, initCheck failed"
" (res = %d)", playerType, init_result);
p.clear();
}
MediaPlayerBase::setNotifyCallback()函数的定义是:
   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抛出事件。

MediaPlayerService::Client::setDataSource_pre()在创建播放器实例结束后。还有处理:

if (!p->hardwareOutput()) {//以StagefrightPlayer为例,它没有重写父类的MediaPlayerInterface::hardwareOutput()方法;返回false,表示音频不直接输出到硬件上.
Mutex::Autolock l(mLock);
mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
mPid, mAudioAttributes);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);//调用父类MediaPlayerInterface::setAudioSink()方法
}

假设当前播放器不把音频直接输出到硬件上,还会去调用它的setAudioSink()方法。

setDataSource_pre()方法处理完后,会调用播放器实例的setDataSource()方法将资源对象设置给播放器,这里是以StagefrightPlayer为例:
// Warning: The filedescriptor passed into this method will only be valid until
// the method returns, if you want to keep it, dup it!
//实际调用AwesomePlayer实例的setDataSource()方法
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
return mPlayer->setDataSource(dup(fd), offset, length);
}

实际是调用AwesomePlayer的同名方法,StagefrightPlayer::mPlayer在StagefrightPlayer构造时被创建:

StagefrightPlayer::StagefrightPlayer()
: mPlayer(new AwesomePlayer) {
ALOGV("StagefrightPlayer"); mPlayer->setListener(this);
}

除了创建了AwesomePlayer实例外。将当前的StagefrightPlayer实例作为监听器保存到了AwesomePlayer::mListener字段中,它的作用是当AwesomePlayer完毕了一些操作
时。 如准备完毕、seek完毕,通知上层当前的MediaPlayer状态变化。AwesomePlayer实例负责将数据设置下去。它的实现这里不做分析。

最后调用setDataSource_post()函数进行一些扫尾工作:

//p指向创建的播放器实例, status是p->setDataSource()调用结果;setDataSource()调用过程的收尾阶段
void MediaPlayerService::Client::setDataSource_post(
const sp<MediaPlayerBase>& p,
status_t status)
{
ALOGV(" setDataSource");
mStatus = status;
if (mStatus != OK) {
ALOGE(" error: %d", mStatus);
return;
} // Set the re-transmission endpoint if one was chosen.
if (mRetransmitEndpointValid) {//设置再次传输时的终端
mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
if (mStatus != NO_ERROR) {
ALOGE("setRetransmitEndpoint error: %d", mStatus);
}
} if (mStatus == OK) {
mPlayer = p;//将播放器实例保存到mPlayer字段中
}
}
须要注意的是。最后一步。会将此时创建的播放器实例保存到MediaPlayerService::Client mPlayer字段中。

我们在回到外层MediaPlayer::setDataSource(),之后会调用attachNewPlayer()函数关联对象:
//将新的IMediaPlayer对象保存到mPlayer中,并清理之前的MediaPlayer实例
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
{ // scope for the lock
Mutex::Autolock _l(mLock); if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {//推断当前的状态是否同意调用attachNewPlayer()函数
ALOGE("attachNewPlayer called in state %d", mCurrentState);
return INVALID_OPERATION;
} clear_l();//清理状态
p = mPlayer;
mPlayer = player;//保存这次的IMediaPlayer对象,实际是MediaPlayerServcie::Client实例
if (player != 0) {
mCurrentState = MEDIA_PLAYER_INITIALIZED;//将当前状态切换到INITIALIZED
err = NO_ERROR;
} else {
ALOGE("Unable to create media player");
}
} if (p != 0) {//假设之前的IMediaPlayer没有清理
p->disconnect();//对这次对象的资源进行清理
} return err;
}

该函数的主要就是更新Native MediaPlayer保存的IMediaPlayer实例,并对旧的对象进行清理操作;随后还会将当前MediaPlayer的状态切换到MEDIA_PLAYER_INITIALIZED。MediaPlayerService::Client的disconnect()方法会清掉一些底层资源:

void MediaPlayerService::Client::disconnect()
{
ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
// grab local reference and clear main reference to prevent future
// access to object
sp<MediaPlayerBase> p;
{
Mutex::Autolock l(mLock);
p = mPlayer;
mClient.clear();
} mPlayer.clear(); // clear the notification to prevent callbacks to dead client
// and reset the player. We assume the player will serialize
// access to itself if necessary.
if (p != 0) {
p->setNotifyCallback(0, 0);
#if CALLBACK_ANTAGONIZER
ALOGD("kill Antagonizer");
mAntagonizer->kill();
#endif
p->reset();//重置状态
} disconnectNativeWindow(); IPCThreadState::self()->flushCommands();
}

至此,MediaPlayer在调用setDateSource()后转换到了NITIALIZED状态;最后。JNI中最外层的process_media_player_call()会依据setDateSource()的运行结果。推断是否须要抛出函数处理错误或异常信息。

四、准备MediaPlayer实例

给MediaPlayer设置完资源后,我们还须要对它调用prepare()方法进行准备操作:
     * Prepares the player for playback, synchronously.
*
* After setting the datasource and the display surface, you need to either
* call prepare() or prepareAsync(). For files, it is OK to call prepare(),
* which blocks until MediaPlayer is ready for playback.
*
* @throws IllegalStateException if it is called in an invalid state
*/
public void prepare() throws IOException, IllegalStateException {
_prepare();
scanInternalSubtitleTracks();
} private native void _prepare() throws IOException, IllegalStateException;

直接看Native层的函数实现:

//MediaPlayer::prepare()函数
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
} // Handle the case where the display surface was set before the mp was
// initialized. We try again to make it stick.
sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
mp->setVideoSurfaceTexture(st); process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
} static void
android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
} // Handle the case where the display surface was set before the mp was
// initialized. We try again to make it stick.
sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
mp->setVideoSurfaceTexture(st); process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
}

本例中,我们没有设置纹理等;直接看MediaPlayer::prepare()的实现:

// one defined in the Android framework and one provided by the implementation
// that generated the error. The sync version of prepare returns only 1 error
// code.
status_t MediaPlayer::prepare()
{
ALOGV("prepare");
Mutex::Autolock _l(mLock);
mLockThreadId = getThreadId();
if (mPrepareSync) {
mLockThreadId = 0;
return -EALREADY;
}
mPrepareSync = true; //表示是否是同步操作
status_t ret = prepareAsync_l();// 1
if (ret != NO_ERROR) {
mLockThreadId = 0;
return ret;
} if (mPrepareSync) {
mSignal.wait(mLock); // wait for prepare done
mPrepareSync = false;
}
ALOGV("prepare complete - status=%d", mPrepareStatus);
mLockThreadId = 0;
return mPrepareStatus;
}
而异步地prepareAsync()函数的实现是:
status_t MediaPlayer::prepareAsync()
{
ALOGV("prepareAsync");
Mutex::Autolock _l(mLock);
return prepareAsync_l();
}

对照两个函数的实现内容,就可以发现这里同步、异步的差别主要是在是否等待mLock这个锁上面。

我们分析真正的实现函数prepare_l()方法:

// must call with lock held
//mPlayer实际是一个MediaPlayerServcie::Client类型的实例,它是MediaPlayerServcie的服务端
status_t MediaPlayer::prepareAsync_l()
{
if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
if (mAudioAttributesParcel != NULL) {//假设有參数,须要进行设置
mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
} else {
mPlayer->setAudioStreamType(mStreamType);//否则设置之前的音频流类型
}
mCurrentState = MEDIA_PLAYER_PREPARING;//变更状态:MEDIA_PLAYER_PREPARING
return mPlayer->prepareAsync();//调用MediaPlayerServcie::Client的prepareAsync()
}
ALOGE("prepareAsync called in state %d", mCurrentState);
return INVALID_OPERATION;
}

假设当前Native MediaPlayer实例持有IMediaPlayer对象。且当前状态能够调用prepare()方法,则去调用MediaPlayerService::Client的prepareAsync()函数进行准备工作:

status_t MediaPlayerService::Client::prepareAsync()
{
ALOGV("[%d] prepareAsync", mConnId);
sp<MediaPlayerBase> p = getPlayer();//getPlayer(){return mPlayer;}返回创建的播放器实例,是MediaPlayerBase的子类;以StagefrightPlayer为例
if (p == 0) return UNKNOWN_ERROR;
status_t ret = p->prepareAsync();
#if CALLBACK_ANTAGONIZER
ALOGD("start Antagonizer");
if (ret == NO_ERROR) mAntagonizer->start();
#endif
return ret;
}

getPlayer()函数返回setDataSource()过程中的创建的播放器实例:

 sp<MediaPlayerBase>     getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }

本文中播放器都是以StagefrightPlayer为例,所以接着去调用StagefrightPlayer::prepareAsync():

status_t StagefrightPlayer::prepareAsync() {
return mPlayer->prepareAsync();
}

最后调用AwesomePlayer的prepareAsync()函数进行准备工作。我们忽略AwesomePlayer运行prepare操作的中间过程,直接看它是怎么向外抛出MEDIA_PREPARED事件的。在AwesomePlayer的prepare操作完毕后。会调用AwesomePlayer::finishAsyncPrepare_l()方法:

void AwesomePlayer::finishAsyncPrepare_l() {
if (mIsAsyncPrepare) {
if (mVideoSource == NULL) {
notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
} else {
notifyVideoSize_l();
} notifyListener_l(MEDIA_PREPARED);//抛出事件
}
......
}

它会调用AwesomePlayer::notifyListener_l()函数向外抛出事件:

void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
if ((mListener != NULL) && !mAudioTearDown) {
sp<MediaPlayerBase> listener = mListener.promote(); if (listener != NULL) {
listener->sendEvent(msg, ext1, ext2);
}
}
}

这里的listener对象指向mListener实例。StagefrightPlayer对象创建时,会顺带初始化AwesomePlayer实例mPlayer,同一时候会将当前的StagefrightPlayer设置到AwesomePlayer中,保存到mListener字段中,作为往外抛出事件的钩子。明白了这些。再看代码实现,就会发现notifyListener_l()中的处理就是直接调用StagefrightPlayer的sendEvent()方法。又依据之前介绍的StagefrightPlayer的继承关系和它的类实现来看,事实上就是调用它的父类MediaPlayerBase的sendEvent()方法:

    void        MediaPlayerBase::setNotifyCallback(
void* cookie, notify_callback_f notifyFunc) {
Mutex::Autolock autoLock(mNotifyLock);
mCookie = cookie; mNotify = notifyFunc;
} void MediaPlayerBase::sendEvent(int msg, int ext1=0, int ext2=0,
const Parcel *obj=NULL) {
notify_callback_f notifyCB;
void* cookie;
{
Mutex::Autolock autoLock(mNotifyLock);
notifyCB = mNotify;
cookie = mCookie;
} if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
}

如今回想一下之前介绍的setDataSource()中的内容:在MediaPlayerFactory中创建我们须要的播放器对象时。在创建完Player对象后。我们就给它设置了这里的notifyCB和cookie对象:

//找到相应视频源类型的播放器工厂实例,并创建Player对象;最后给该Player对象设置传递消息的notifyFunc回调函数
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
player_type playerType,
void* cookie,
notify_callback_f notifyFunc,
pid_t pid) {
... init_result = p->initCheck();//实现直接return OK;
if (init_result == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);//调用父类MediaPlayerBase::setNotifyCallback();cookie是当前调用MediaPlayerService::Client::createPlayer()的Client实例,notifyFunc这里是MediaPlayerService::Client::notify()函数指针.
} else {
ALOGE("Failed to create player object of type %d, initCheck failed"
" (res = %d)", playerType, init_result);
p.clear();
} return p;
}

当中:

  • cookie指向当前调用MediaPlayerService::Client::createPlayer()的Client实例
  • notifyFunc是一个函数指针,这里指向MediaPlayerService::Client::notify()函数
所以可知sendEvent():
void        MediaPlayerBase::sendEvent(int msg, int ext1=0, int ext2=0,
const Parcel *obj=NULL) {
notify_callback_f notifyCB;
void* cookie;
{
Mutex::Autolock autoLock(mNotifyLock);
notifyCB = mNotify;
cookie = mCookie;
} if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
}

就是调用当前播放器所属的MediaPlayerService::Client的notify()函数。

既然明白了这些,我们就直接看notify()函数实现:

//用于向MediaPlayer对象传递消息;參数void* cookie实际指向当前的Client实例;msg參数是事件类型信息
void MediaPlayerService::Client::notify(
void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
Client* client = static_cast<Client*>(cookie);//首先得到当前的Client对象
if (client == NULL) {
return;
} sp<IMediaPlayerClient> c;
{
Mutex::Autolock l(client->mLock);
c = client->mClient;//得到该Client保存的IMediaPlayerClient对象信息,它实际指向一个Native MediaPlayer实例
if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {//假设当前事件是MEDIA_PLAYBACK_COMPLETE,表明当前播放已经结束。而且我们设置了下一个mNextClient
if (client->mAudioOutput != NULL) //就须要切换运行mNextClient;效果就是当前播放结束后,会自己主动切换到下一个播放
client->mAudioOutput->switchToNextOutput();
client->mNextClient->start();
client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);//并调用notify()方法,向外告知MEDIA_INFO_STARTED_AS_NEXT事件
}
} if (MEDIA_INFO == msg &&
MEDIA_INFO_METADATA_UPDATE == ext1) {//msg为MEDIA_INFO的情况
const media::Metadata::Type metadata_type = ext2; if(client->shouldDropMetadata(metadata_type)) {
return;
} // Update the list of metadata that have changed. getMetadata
// also access mMetadataUpdated and clears it.
client->addNewMetadataUpdate(metadata_type);
} if (c != NULL) {
ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
c->notify(msg, ext1, ext2, obj);//调用这一次的MediaPlayerService::Client实例相应的Native MediaPlayer实例的notify()方法
}
}

首先我们得到当前的Client实例,靠它再进一步得到与它相应的Native MediaPlayer实例(MediaPlayerService::Client构建时会保存该实例)。

这里处理几种特殊情况,当我们设置了mNextClient时(通过MediaPlayerService::Client::setNextPlayer()设置),假设msg符合情况。就会自己主动切换到下一个播放。msg为MEDIA_INFO的情况,也会有一些特殊的处理。接着,就调用MediaPlayer::notify()方法,该函数的内容之前已经介绍过一些,这里在贴一遍它的实现:

//向应用程序反馈当前状态变化的回调事件,设置当前MediaPlayer的状态
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
bool locked = false; // TODO: In the future, we might be on the same thread if the app is
// running in the same process as the media server. In that case,
// this will deadlock.
//
// The threadId hack below works around this for the care of prepare,
// seekTo and start within the same process.
// FIXME: Remember, this is a hack, it's not even a hack that is applied
// consistently for all use-cases, this needs to be revisited.
if (mLockThreadId != getThreadId()) {
mLock.lock();
locked = true;
} // Allows calls from JNI in idle state to notify errors
if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
if (locked) mLock.unlock(); // release the lock when done.
return;
} switch (msg) {
case MEDIA_NOP: // interface test message
break;
case MEDIA_PREPARED://符合当前的情况,处理Prepare完毕的情况
ALOGV("prepared");
mCurrentState = MEDIA_PLAYER_PREPARED;
if (mPrepareSync) {
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mSignal.signal();
}
break;
case MEDIA_PLAYBACK_COMPLETE:
ALOGV("playback complete");
if (mCurrentState == MEDIA_PLAYER_IDLE) {
ALOGE("playback complete in idle state");
}
if (!mLoop) {
mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
}
break;
case MEDIA_ERROR:
// Always log errors.
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
ALOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
if (mPrepareSync)
{
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = ext1;
mSignal.signal();
send = false;
}
break;
case MEDIA_INFO:
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
ALOGW("info/warning (%d, %d)", ext1, ext2);
}
break;
case MEDIA_SEEK_COMPLETE:
ALOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition) {
ALOGV("Executing queued seekTo(%d)", mSeekPosition);
mSeekPosition = -1;
seekTo_l(mCurrentPosition);
}
else {
ALOGV("All seeks complete - return to regularly scheduled program");
mCurrentPosition = mSeekPosition = -1;
}
break;
case MEDIA_BUFFERING_UPDATE:
ALOGV("buffering %d", ext1);
break;
case MEDIA_SET_VIDEO_SIZE:
ALOGV("New video size %d x %d", ext1, ext2);
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
case MEDIA_TIMED_TEXT:
ALOGV("Received timed text message");
break;
case MEDIA_SUBTITLE_DATA:
ALOGV("Received subtitle data message");
break;
case MEDIA_META_DATA:
ALOGV("Received timed metadata message");
break;
default:
ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
} //JNIMediaPlayerListener继承自MediaPlayerListener,并实现了notify()方法;声明实如今android_media_MediaPlayer.cpp中
sp<MediaPlayerListener> listener = mListener;//mListener保存了MediaPlayer实例创建时初始化的JNIMediaPlayerListener监听对象
if (locked) mLock.unlock(); // this prevents re-entrant calls into client code
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
ALOGV("callback application");
listener->notify(msg, ext1, ext2, obj);//调用JNIMediaPlayerListener类实例的notify()方法
ALOGV("back from callback");
}
}

此时msg是MEDIA_PREPARED。看它的处理过程:

case MEDIA_PREPARED:
ALOGV("prepared");
mCurrentState = MEDIA_PLAYER_PREPARED;
if (mPrepareSync) {
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mSignal.signal();
}
break;

主要地,将MediaPlayer的当前状态设置为了MEDIA_PLAYER_PREPARED。

最后。调用JNIMediaPlayerListener::notify()函数:
//回调MediaPlayer.java中的postEventFromNative()方法,反馈Native层发生的事件;postEventFromNative()会EventHandler(运行在MediaPalyer的线程中)
//发送附带msg參数的消息,EventHandler推断当前的事件类型,如MEDIA_PREPARED等,最后调用应用程序设置的相关回调处理相应的事件信息
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}

JNIMediaPlayerListener::mObject字段是一个指向上层MediaPlayer的全局引用。终于的主要操作就是借助JNI,通过fields.post_event字段的ID值在Native层调用Java层MediaPlayer的方法-MediaPlayer::postEventFromNative():

    /*
* Called from native code when an interesting event happens. This method
* just uses the EventHandler system to post the event back to the main app thread.
* We use a weak reference to the original MediaPlayer object so that the native
* code is safe from the object disappearing from underneath it. (This is
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();//拿到给弱引用指向的MediaPlayer实例
if (mp == null) {
return;
} if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {//假设是自己主动切换到下一个播放的事件或是MEDIA_INFO
// this acquires the wakelock if needed, and sets the client side state
mp.start();//就直接start()
}
if (mp.mEventHandler != null) {//最后将该事件发送到EventHandler中处理
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);//此时what是MEDIA_PREPARED
mp.mEventHandler.sendMessage(m);
}
}

最后会将该事件发送到EventHandler中处理:

    private class EventHandler extends Handler
{
private MediaPlayer mMediaPlayer; public EventHandler(MediaPlayer mp, Looper looper) {
super(looper);
mMediaPlayer = mp;
} @Override
public void handleMessage(Message msg) {
if (mMediaPlayer.mNativeContext == 0) {
Log.w(TAG, "mediaplayer went away with unhandled events");
return;
}
switch(msg.what) {
case MEDIA_PREPARED:
try {
scanInternalSubtitleTracks();
} catch (RuntimeException e) {
// send error message instead of crashing;
// send error message instead of inlining a call to onError
// to avoid code duplication.
Message msg2 = obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
sendMessage(msg2);
}
if (mOnPreparedListener != null)
mOnPreparedListener.onPrepared(mMediaPlayer);
return; case MEDIA_PLAYBACK_COMPLETE:
if (mOnCompletionListener != null)
mOnCompletionListener.onCompletion(mMediaPlayer);
stayAwake(false);
return; case MEDIA_STOPPED:
{
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onStopped();
}
}
break; case MEDIA_STARTED:
case MEDIA_PAUSED:
{
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onPaused(msg.what == MEDIA_PAUSED);
}
}
break; case MEDIA_BUFFERING_UPDATE:
if (mOnBufferingUpdateListener != null)
mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
return; case MEDIA_SEEK_COMPLETE:
if (mOnSeekCompleteListener != null) {
mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
}
// fall through case MEDIA_SKIPPED:
{
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onSeekComplete(mMediaPlayer);
}
}
return; case MEDIA_SET_VIDEO_SIZE:
if (mOnVideoSizeChangedListener != null) {
mOnVideoSizeChangedListener.onVideoSizeChanged(
mMediaPlayer, msg.arg1, msg.arg2);
}
return; case MEDIA_ERROR:
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
boolean error_was_handled = false;
if (mOnErrorListener != null) {
error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
}
if (mOnCompletionListener != null && ! error_was_handled) {
mOnCompletionListener.onCompletion(mMediaPlayer);
}
stayAwake(false);
return; case MEDIA_INFO:
switch (msg.arg1) {
case MEDIA_INFO_VIDEO_TRACK_LAGGING:
Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
break;
case MEDIA_INFO_METADATA_UPDATE:
try {
scanInternalSubtitleTracks();
} catch (RuntimeException e) {
Message msg2 = obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
sendMessage(msg2);
}
// fall through case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
// update default track selection
if (mSubtitleController != null) {
mSubtitleController.selectDefaultTrack();
}
break;
case MEDIA_INFO_BUFFERING_START:
case MEDIA_INFO_BUFFERING_END:
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
}
break;
} if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
}
// No real default action so far.
return;
case MEDIA_TIMED_TEXT:
if (mOnTimedTextListener == null)
return;
if (msg.obj == null) {
mOnTimedTextListener.onTimedText(mMediaPlayer, null);
} else {
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel)msg.obj;
TimedText text = new TimedText(parcel);
parcel.recycle();
mOnTimedTextListener.onTimedText(mMediaPlayer, text);
}
}
return; case MEDIA_SUBTITLE_DATA:
if (mOnSubtitleDataListener == null) {
return;
}
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel) msg.obj;
SubtitleData data = new SubtitleData(parcel);
parcel.recycle();
mOnSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
}
return; case MEDIA_META_DATA:
if (mOnTimedMetaDataAvailableListener == null) {
return;
}
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel) msg.obj;
TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
parcel.recycle();
mOnTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
}
return; case MEDIA_NOP: // interface test message - ignore
break; default:
Log.e(TAG, "Unknown message type " + msg.what);
return;
}
}
}

当前事件是MEDIA_PREPARED。它的处理过程是:

case MEDIA_PREPARED:
try {
scanInternalSubtitleTracks();
} catch (RuntimeException e) {
// send error message instead of crashing;
// send error message instead of inlining a call to onError
// to avoid code duplication.
Message msg2 = obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
sendMessage(msg2);
}
if (mOnPreparedListener != null)
mOnPreparedListener.onPrepared(mMediaPlayer);
return;

当中要注意最后一部分。mOnPreparedListener对象是一个回调,它一般通过setOnPreparedListener()函数注冊:

    /**
* Register a callback to be invoked when the media source is ready
* for playback.
*
* @param listener the callback that will be run
*/
public void setOnPreparedListener(OnPreparedListener listener)
{
mOnPreparedListener = listener;
}

像我们使用异步prepareAsync()时,由于它的运行会立马返回。所以我们就须要系统告诉我们MediaPlayer的prepare工作何时已经完毕。并在完毕时让系统调用我们注冊的回调。让我们的MediaPlayer在prepare完毕后開始运行播放。
在EventHandler的处理中,假设mOnPreparedListener不为空(表明用户注冊了这个回调,那么这时就去调用它。以完毕用户须要的操作)。

这一部分除了分析prepare()函数外,我们还要注意的是MediaPlayer的状态事件是怎样从底层一步一步传到上层来的,这对我们理解MediaPlayer的整个实现过程是非常重要的。

最后,在JNI部分的处理中,最外层的process_media_player_call()会依据prepare()的运行结果,推断是否须要抛出函数处理错误或异常信息。

五、开启MediaPlayer

在MediaPlayer运行完prepare之后,我们就能够正式开启它開始播放了。看MediaPlayer::start()方法实现:
    /**
* 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()的运行结果,推断是否须要抛出函数处理错误或异常信息。

至此,MediaPlayer使用演示样例代码中的5个重要步骤的底层实现就介绍完了。

事实上MediaPlayer还提供了一系列重载的create()函数。来简化我们使用的步骤。我们仅仅看一个比較具代表性的样例:
    /**
* Same factory method as {@link #create(Context, int)} but that lets you specify the audio
* attributes and session ID to be used by the new MediaPlayer instance.
* @param context the Context to use
* @param resid the raw resource id (<var>R.raw.<something></var>) for
* the resource to use as the datasource
* @param audioAttributes the {@link AudioAttributes} to be used by the media player.
* @param audioSessionId the audio session ID to be used by the media player,
* see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
* @return a MediaPlayer object, or null if creation failed
*/
public static MediaPlayer create(Context context, int resid,
AudioAttributes audioAttributes, int audioSessionId) {
try {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null; MediaPlayer mp = new MediaPlayer();// 1 final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
mp.setAudioSessionId(audioSessionId); mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());// 2
afd.close();
mp.prepare();// 3
return mp;
} catch (IOException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (IllegalArgumentException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (SecurityException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
}
return null;
}

通过MediaPlayer::create()的凝视可知,官方是推荐我们使用createe()方法的。从代码中的1、2、3标记可知。我们仅仅需传入须要使用的资源标示符就可以,create()会帮助我们创建MediaPlayer、同一时候也会去初始化和prepare它。

MediaPlayer::create()让我们使用MediaPlayer时更加简便、快捷。在调用create()函数之后。我们仅仅需调用start()就可使用MediaPlayer了。

另外,有了之前的分析,相信我们再看MediaPlayer的其它函数实现,也不会有太大问题了。

Android -- 多媒体播放之MediaPlayer使用内部实现简析的更多相关文章

  1. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  2. 功能强大的图片截取修剪神器:Android SimpleCropView及其实例代码重用简析(转)

    功能强大的图片截取修剪神器:Android SimpleCropView及其实例代码重用简析 SimpleCropView是github上第一个第三方开源的图片修剪截取利器,功能强大,设计良好.我个人 ...

  3. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  4. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  5. zxing二维码扫描的流程简析(Android版)

    目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...

  6. Android MVP 简析

    原地址:https://segmentfault.com/a/1190000003927200 在Android中应用MVP模式的原因与方法的简析,写的简单易懂.

  7. android Studio keytool' 不是内部或外部命令,也不是可运行的程序 或批处理文件

    //android Studio  keytool' 不是内部或外部命令,也不是可运行的程序 或批处理文件 遇到这个问题好久了,一直没解决今天搜集了大量的资料,有的说什么Java没配置好,不是扯犊子吗 ...

  8. Android 11(R) Power HAL AIDL简析 -- 基本接口

    Android 11(R) Power HAL AIDL将分三篇文章来介绍: Android 11(R) Power HAL AIDL简析 -- 基本接口 Android 11(R) Power HA ...

  9. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

随机推荐

  1. Execute a terminal command from a Cocoa app

    http://stackoverflow.com/questions/412562/execute-a-terminal-command-from-a-cocoa-app in the spirit ...

  2. IC卡文件系统的逻辑结构【转】

    转自:http://bbs.ednchina.com/BLOG_ARTICLE_172028.HTM 文件系统是COS的重要模块之一,它负责组织.管理.维护IC卡内存储的所有数据.文件系统的设计和实现 ...

  3. [ 总结 ] 删除通过find查找到的文件

    [root@cloud abc]# touch test{,,,} [root@cloud abc]# ls shadow test test1 test2 test3 test5 [root@clo ...

  4. 移植WordPress到Ubuntu16.04

    移植WordPress到Ubuntu16.04 新建 模板 小书匠 移植WordPress到Ubuntu16.04 搭建好LAMP环境后,可以按照以下方法,将本地站点移植到服务器上. 以WordPre ...

  5. selenium 3.0与2.0 打开火狐浏览器的区别

    3.0的selenium需要引入gecko.driver驱动 ,因为没有在系统环境path中配置相关路径,因此需要特别指出,为了方便使用,建议直接和火狐安装包中的.exe文件放在同一目录下. 2.0的 ...

  6. python带cookie提交表单自动登录

    import urllib import urllib2 import cookielib login_url = "xxxxxxxxxxxxx" cj = cookielib.C ...

  7. sublime text3插件TrailingSpaces无法使用的解决方法

    TrailingSpaces是很好用的一款插件,可以清除代码结尾打多了几个空格或Tab,对于代码洁癖绝对是个福音,我的sublime text3本来安装了这款插件,也可以正常使用,今天突然不能用了,即 ...

  8. Mysql错误:Every derived table must have its own alias

    Mysql报错:Every derived table must have its own alias    <缺少一个别名> 在子查询中经常会犯的错误 .这个别名其实没啥用途....  ...

  9. docker 与 yarn

    有时我们的项目是使用yarn去发布的,当需要使用docker发布这个项目时,安装yarn是必须的,但是平时使用的npm install -g yarn此时却不可用 从网站上找到解决的方法 地址:htt ...

  10. POJ 1163.The Triangle-动态规划

    The Triangle Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 50122   Accepted: 30285 De ...