Android -- 多媒体播放之MediaPlayer使用内部实现简析

Android -- MediaPlayer内部实现简析


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

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

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

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


一、创建MediaPlayer


从MediaPlayer模块的实现层次来说,它其实只是一个暴露给外部调用的工具类;真正的媒体操作都通过JNI调用到底层Media服务,由它们真正实现。MediaPlayer类要使用一个libmedia_jni.so库,它的加载过程如下:

   static {
        System.loadLibrary("media_jni");
        native_init();
    }
libmedia_jni.so提供了MediaPlayer需要调用的各个JNI函数,它对应的文件是android_media_MediaPlayer.cpp。load该so库的同时,会调用native_init()函数进行一些前期的初始化工作:
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)//初始化一些Field和Method域ID
{
    jclass clazz;

    clazz = env->FindClass("android/media/MediaPlayer");
    if (clazz == NULL) {
        return;
    }

    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }

    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }

    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
    if (fields.surface_texture == NULL) {
        return;
    }

    env->DeleteLocalRef(clazz);

    clazz = env->FindClass("android/net/ProxyInfo");
    if (clazz == NULL) {
        return;
    }

    fields.proxyConfigGetHost =
        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");

    fields.proxyConfigGetPort =
        env->GetMethodID(clazz, "getPort", "()I");

    fields.proxyConfigGetExclusionList =
        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");

    env->DeleteLocalRef(clazz);

    gPlaybackParamsFields.init(env);
    gSyncParamsFields.init(env);
}
struct fields_t {
    jfieldID    context;
    jfieldID    surface_texture;

    jmethodID   post_event;

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


加载完要使用的动态库,我们就可以开始创建MediaPlayer实例了,首先看它的默认构造函数:

    /**
     * Default constructor. Consider using one of the create() methods for
     * synchronously instantiating a MediaPlayer from a Uri or resource.
     * <p>When done with the MediaPlayer, you should call  {@link #release()},
     * to free the resources. If not released, too many MediaPlayer instances may
     * result in an exception.</p>
     */
    public MediaPlayer() {

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }

        mTimeProvider = new TimeProvider(this);
        mOpenSubtitleSources = new Vector<InputStream>();
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);

        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaPlayer>(this));//继续调用了native_setup()函数
    }
我们的MediaPlayer需要运行在消息循环中,EventHandler是MediaPlayer的一个内部类,它专门处理来自Native层的事件,这些事件一般都表明了MediaPlayer现在转移到了某个状态,我们可以在该状态处理什么回调操作。EventHandler的功能较为单一,就是根据底层上抛的事件,进行对应的回调或事件处理,这里就不再细看。接着,调用了native_setup()函数,并传入了一个MediaPlayer类型的弱引用实例,我们看该函数的实现:
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("native_setup");
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    // create new listener and give it to MediaPlayer
    //JNIMediaPlayerListener类继承自MediaPlayer.h中声明的MediaPlayerListener,并实现了notify()方法
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);//在Native MediaPlayer实例中保存这个JNIMediaPlayerListener监听对象

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);//将创建的Native MediaPlayer对象转化成Long型值(地址),保存到MediaPlayer.java::mNativeContext变量中
}
该函数中主要做了三个操作:

  • 创建了一个Native MediaPlayer对象
  • 创建了一个JNIMediaPlayerListener对象,它主要用于向上层MediaPlayer(.java)对象通知事件或抛出异常
  • 将创建的Native MediaPlayer实例保存到MediaPlayer.java::mNativeContext字段中
Native MediaPlayer类继承自BnMediaPlayerClient,它是IMediaPlayerClient业务的服务端。IMediaPlayerClient业务的整个框架如图所示:

Native MediaPlayer的构造函数较为简单,就是一些字段的初始化操作,重要的是设置了默认的音频流类型,并将MediaPlayer的状态初始化为Idle:
MediaPlayer::MediaPlayer()
{
    ALOGV("constructor");
    mListener = NULL;
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;//默认音频流类型
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mSeekPosition = -1;
    mCurrentState = MEDIA_PLAYER_IDLE;//MediaPlayer的初始状态
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;//是否循环播放
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    mAudioSessionId = AudioSystem::newAudioUniqueId();
    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}
另外,还有默认同步状态的设置等等。
JNIMediaPlayerListener类继承自MediaPlayerListener,它声明了一个notify()函数,该函数主要用来向上层抛出事件或其他异常:
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
    JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
    ~JNIMediaPlayerListener();
    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
    JNIMediaPlayerListener();
    jclass      mClass;     // Reference to MediaPlayer class
    jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
};
JNIMediaPlayerListener的构造函数中用之前传入的MediaPlayer弱引用实例构造了一个Native层全局的变量mObject,并且也保存了一份MediaPlayer.java的类型实例:
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{

    // Hold onto the MediaPlayer class for use in calling the static method
    // that posts events to the application thread.
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find android/media/MediaPlayer");
        jniThrowException(env, "java/lang/Exception", NULL);
        return;
    }
	
    mClass = (jclass)env->NewGlobalRef(clazz);//代表MediaPlayer.java类型的实例

    // We use a weak reference so the MediaPlayer object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    
    mObject  = env->NewGlobalRef(weak_thiz);//weak_thiz是MediaPlayer.java实例的一个弱引用
}
JNIMediaPlayerListener::notify()函数用来向上层抛出事件或异常:
//回调MediaPlayer.java中的postEventFromNative()方法,反馈Native层发生的事件;postEventFromNative()会EventHandler(运行在MediaPalyer的线程中)
//发送附带msg参数的消息,EventHandler判断当前的事件类型,如MEDIA_PREPARED等,最后调用应用程序设置的相关回调处理相应的事件信息
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (obj && obj->dataSize() > 0) {
        jobject jParcel = createJavaParcelObject(env);
        if (jParcel != NULL) {
            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
            nativeParcel->setData(obj->data(), obj->dataSize());
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
    }
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        LOGW_EX(env);
        env->ExceptionClear();
    }
}
JNIMediaPlayerListener实例创建后,会将它保存到Native MediaPlayer::mListener字段中。最后掉setMediaPlayer()方法将mp对象转换成地址值的形式保存到上层MediaPlayer中:
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
    Mutex::Autolock l(sLock);
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    if (player.get()) {
        player->incStrong((void*)setMediaPlayer);
    }
    if (old != 0) {
        old->decStrong((void*)setMediaPlayer);
    }
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

二、设置音频类型


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

    private native void _setAudioStreamType(int streamtype);
从函数注释可以,这里设置的音频流类型必须是AudioManager中定义过的,当前版本中所支持的类型有:
    /** The audio stream for phone calls */
    public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
    /** The audio stream for system sounds */
    public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
    /** The audio stream for the phone ring */
    public static final int STREAM_RING = AudioSystem.STREAM_RING;
    /** The audio stream for music playback */
    public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
    /** The audio stream for alarms */
    public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
    /** The audio stream for notifications */
    public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
    /** @hide The audio stream for phone calls when connected to bluetooth */
    public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
    /** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
    public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
    /** The audio stream for DTMF Tones */
    public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
    /** @hide The audio stream for text to speech (TTS) */
    public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
最后调用native函数_setAudioStreamType()将类型值设置下去,看它的实现:
static void
android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint streamtype)
{
    ALOGV("setAudioStreamType: %d", streamtype);
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);//获取创建时设置到MediaPlayer.java实例中的Native MediaPlayer实例
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}
首先获取到之前保存到MediaPlayer.java::mNativeContext字段中的Native MediaPlayer对象;最后调用process_media_player_call()函数,其中包含了通过mp实例设置音频类型的调用:MediaPlayer::setAudioStreamType()。
我们分解来分析,process_media_player_call()函数会根据函数的执行结果opStatus,以及附带的exception和message信息;判断是否需要向上反馈状态事件或抛出异常信息:
// If exception is NULL and opStatus is not OK, this method sends an error
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
//根据函数的执行结果opStatus,以及附带的exception和message信息;判断是否需要反馈操作失败事件或抛出异常信息
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
        if (opStatus != (status_t) OK) {//如果无需抛出异常,但存在函数处理错误,则向上层抛出事件
            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);//重要
        }
    } else {  // Throw exception! 如果需要抛出异常,则构建对应的异常并抛出
        if ( opStatus == (status_t) INVALID_OPERATION ) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        } else if ( opStatus == (status_t) BAD_VALUE ) {
            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
            jniThrowException(env, "java/lang/SecurityException", NULL);
        } else if ( opStatus != (status_t) OK ) {
            if (strlen(message) > 230) {
               // if the message is too long, don't bother displaying the status code
               jniThrowException( env, exception, message);
            } else {
               char msg[256];
                // append the status code to the message
               sprintf(msg, "%s: status=0x%X", message, opStatus);
               jniThrowException( env, exception, msg);
            }
        }
    }
}
如果我们的操作无需抛出异常,并且当前的函数调用有错误,就需要通过Native MediaPlayer调用notify()向上层抛出错误:
//向应用程序反馈当前状态变化的回调事件,设置当前MediaPlayer的状态
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
    bool send = true;
    bool locked = false;

    // TODO: In the future, we might be on the same thread if the app is
    // running in the same process as the media server. In that case,
    // this will deadlock.
    //
    // The threadId hack below works around this for the care of prepare,
    // seekTo and start within the same process.
    // FIXME: Remember, this is a hack, it's not even a hack that is applied
    // consistently for all use-cases, this needs to be revisited.
    if (mLockThreadId != getThreadId()) {
        mLock.lock();
        locked = true;
    }

    // Allows calls from JNI in idle state to notify errors
    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
        if (locked) mLock.unlock();   // release the lock when done.
        return;
    }

    switch (msg) {
    case MEDIA_NOP: // interface test message
        break;
    case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
    case MEDIA_PLAYBACK_COMPLETE:
        ALOGV("playback complete");
        if (mCurrentState == MEDIA_PLAYER_IDLE) {
            ALOGE("playback complete in idle state");
        }
        if (!mLoop) {
            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
        }
        break;
    case MEDIA_ERROR://如果是有函数处理发生错误
        // Always log errors.
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        ALOGE("error (%d, %d)", ext1, ext2);
        mCurrentState = MEDIA_PLAYER_STATE_ERROR;//则将当前状态设置为ERROR
        if (mPrepareSync)
        {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = ext1;
            mSignal.signal();
            send = false;
        }
        break;
    case MEDIA_INFO:
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
            ALOGW("info/warning (%d, %d)", ext1, ext2);
        }
        break;
    case MEDIA_SEEK_COMPLETE:
        ALOGV("Received seek complete");
        if (mSeekPosition != mCurrentPosition) {
            ALOGV("Executing queued seekTo(%d)", mSeekPosition);
            mSeekPosition = -1;
            seekTo_l(mCurrentPosition);
        }
        else {
            ALOGV("All seeks complete - return to regularly scheduled program");
            mCurrentPosition = mSeekPosition = -1;
        }
        break;
    case MEDIA_BUFFERING_UPDATE:
        ALOGV("buffering %d", ext1);
        break;
    case MEDIA_SET_VIDEO_SIZE:
        ALOGV("New video size %d x %d", ext1, ext2);
        mVideoWidth = ext1;
        mVideoHeight = ext2;
        break;
    case MEDIA_TIMED_TEXT:
        ALOGV("Received timed text message");
        break;
    case MEDIA_SUBTITLE_DATA:
        ALOGV("Received subtitle data message");
        break;
    case MEDIA_META_DATA:
        ALOGV("Received timed metadata message");
        break;
    default:
        ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
        break;
    }

	//JNIMediaPlayerListener继承自MediaPlayerListener,并实现了notify()方法;声明实现在android_media_MediaPlayer.cpp中
    sp<MediaPlayerListener> listener = mListener;//mListener保存了MediaPlayer实例创建时初始化的JNIMediaPlayerListener监听对象
    if (locked) mLock.unlock();

    // this prevents re-entrant calls into client code
    if ((listener != 0) && send) {
        Mutex::Autolock _l(mNotifyLock);
        ALOGV("callback application");
        listener->notify(msg, ext1, ext2, obj);//调用JNIMediaPlayerListener类实例的notify()方法
        ALOGV("back from callback");
    }
}
notify()函数使用的msg判断值定义在MediaPlayer.h中:
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业务架构中的服务端,该业务架构的图示如下:
我们再看MediaPlayerService::instantiate()的处理:
void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}
很简单地创建MediaPlayerService服务实例,并注册进系统的实现(要注意,注册的服务名称是“media.player”)。接着看它的构造函数实现:
//创建MediaPlayerService实例时,会初始化MediaPlayerService::sFactoryMap成员,注册各Player工厂对象
MediaPlayerService::MediaPlayerService()
{
    ALOGV("MediaPlayerService created");
    mNextConnId = 1;

    mBatteryAudio.refCount = 0;
    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
        mBatteryAudio.deviceOn[i] = 0;
        mBatteryAudio.lastTime[i] = 0;
        mBatteryAudio.totalTime[i] = 0;
    }
    // speaker is on by default
    mBatteryAudio.deviceOn[SPEAKER] = 1;

    // reset battery stats
    // if the mediaserver has crashed, battery stats could be left
    // in bad state, reset the state upon service start.
    BatteryNotifier& notifier(BatteryNotifier::getInstance());
    notifier.noteResetVideo();
    notifier.noteResetAudio();

    MediaPlayerFactory::registerBuiltinFactories();//初始化MediaPlayerService::sFactoryMap集合对象
}
最后一步调用了MediaPlayerFactory::registerBuildinFactories()函数注册系统中提供的各个播放器实例:
enum player_type {
    STAGEFRIGHT_PLAYER = 3,
    NU_PLAYER = 4,
    // Test players are available only in the 'test' and 'eng' builds.
    // The shared library with the test player is passed passed as an
    // argument to the 'test:' url in the setDataSource call.
    TEST_PLAYER = 5,
};

void MediaPlayerFactory::registerBuiltinFactories() {
    Mutex::Autolock lock_(&sLock);

    if (sInitComplete)
        return;

    registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER);
    registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
    registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);

    sInitComplete = true;
}
registerFactory_l()函数会判断实例的有效性,并将它们保存到集合变量中:
MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;
bool MediaPlayerFactory::sInitComplete = false;

status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,
                                               player_type type) {
    if (NULL == factory) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, factory is"
              " NULL.", type);
        return BAD_VALUE;
    }

    if (sFactoryMap.indexOfKey(type) >= 0) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, type is"
              " already registered.", type);
        return ALREADY_EXISTS;
    }

    if (sFactoryMap.add(type, factory) < 0) {
        ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add"
              " to map.", type);
        return UNKNOWN_ERROR;
    }

    return OK;
}
MediaPlayerFactory中主要涉及了三个类型的播放器:
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();
}
处理很简单,设置是否循环播放;调用播放器的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的其他函数实现,也不会有太大问题了。



  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值