再前一个博客中大家已经对整个播放框架有了一个整体的了解接下来的这篇博客将针对每个具体细节进行介绍:
再进行介绍之前我们看下使用MediaPlayer进行播放本地音频视频文件时的一般调用步骤,以及每个步骤所处理的任务:

每个阶段的任务

我们先来看下一个播放过程上层的调用情况:

  • 创建MediaPlayer对象
    在这部分中主要完成如下任务:

    1. 从JNI层获取应用层MediaPlayer相关域和方法的引用,这是由于MediaPlayer对象在上层创建会比在下层创建来得容易些。
    2. 创建EventHandler,用于处理底层播放引擎向应用层传递的事件。
  • setDataSource() 创建播放引擎并为其设置数据源
    在这部分中主要完成如下任务:

    1. 根据本地音视频文件的mimetype对其进行匹配,选择适当的播放引擎并实例化
    2. 创建AudioOutput,将其设置到步骤1中创建的播放引擎,后续解码后的音频数据就可以通过它输出到硬件设备上。
    3. 实例化数据源FileSource。根据文件的Mimetype创建相应的Extractor,根据文件类型创建相应的数据容器,并将设置到播放引擎中作为解码器的输入数据来源
  • setDisplay()

    1. 在这部分中主要为视频设置渲染画板,视频流就是通过这个接口显示到显示屏上的。
  • prepare()

    1. 根据上面MediaExtractor从文件中抽取出的mimetype类型搜寻并加载匹配的解码器。
    2. 配置解码器设置解码器的监听器
    3. 设置解码器Buffer的尺寸大小数据
  • start()

    1. 创建AudioPlayer并设置对应的参数,从音视频文件的数据容器中读取数据,将数据传送给解码器进行解码,最后将解码后的数据返回给播放引擎
    2. 通过AudioPlayer向硬件输出音频数据。
    3. 创建视频Render来渲染解码好的视频数据
MediaPlayer播放框架源代码解析:
创建MediaPlayer对象
建立底层MediaPlayer与Java层MediaPlayer事件传递的通道

在MediaPlayer加载后它会先执行如下代码,由于这个是静态代码块只有在类第一次加载的时候会被调用,因此在这个地方加载动态库libmedia_jni.so是最为合适的。

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

在native_init方法中获取一些属性的id为后续的初始化做准备,由于这些field id是比较常用的,所以在类加载的时候直接获取可以避免后续操作的时候每次都要获取降低了执行效率。

static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
//从上层获取Context
clazz = env->FindClass("android/media/MediaPlayer");
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
//在介绍native_setup的时候会介绍这个值域,用于将native层的事件传递到Java层
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
env->DeleteLocalRef(clazz);
clazz = env->FindClass("android/net/ProxyInfo");
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);
}

紧接着我们就需要调用MediaPlayer类的构造方法创建MediaPlayer对象:
在这部分我们就来详细看下上层和native层是如何进行消息传递的:

public MediaPlayer() {
Looper looper;
//创建了EventHandler用于处理从native层传递过来的消息
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
//.....
//创建一个MediaPlayer的软引用传递给native_setup方法
native_setup(new WeakReference<MediaPlayer>(this));
}

首先这里创建了一个EventHandler,上面提到过这个Handler用于处理从native层传递过来的消息,然后调用native_setup

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
sp<MediaPlayer> mp = new MediaPlayer();
// 创建一个JNIMediaPlayerListener 并将其设置到上面new出来的MediaPlayer
// JNIMediaPlayerListener 实际上是将native层的通知,通过调用Java层的postEventFromNative方法传递上去,这个工作是在frameworks/base/media/jni/android_media_MediaPlayer.cpp:JNIMediaPlayerListener::notify方法中完成的。这个方法中的fields.post_event正是在上面介绍的android_media_MediaPlayer_native_init中从Java层中获取到的
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
//将上述创建的JNIMediaPlayerListener赋给MediaPlayer本地类的mListener成员变量
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}

在native_setup中我们创建一个上层的MediaPlayer对象以及JNIMediaPlayerListener对象,JNIMediaPlayerListener 实际上是将native层的通知通过调用Java层的postEventFromNative方法传递上去,这个工作是在frameworks/base/media/jni/android_media_MediaPlayer.cpp:JNIMediaPlayerListener::notify方法中完成的。这个方法中的fields.post_event正是在上面介绍的android_media_MediaPlayer_native_init中从Java层中获取到的。

下面是MediaPlayer native的构造方法,这里比较简单只是对一些成员变量的初始化。

MediaPlayer::MediaPlayer()
{
mListener = NULL; //用于与Java层通信的Listener JNIMediaPlayerListener
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC; //Stream 类型
mAudioAttributesParcel = NULL;
mCurrentPosition = -1; //当前位置
mSeekPosition = -1; //Seek位置
mCurrentState = MEDIA_PLAYER_IDLE; //当前MediaPlayer的状态
mPrepareSync = false; //是否同步Prepare
mPrepareStatus = NO_ERROR; //Prepare状态
mLoop = false; //是否循环播放
mLeftVolume = mRightVolume = 1.0; //左右音量
mVideoWidth = mVideoHeight = 0; //视频的宽度和高度值
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioUniqueId(); //AudioSession Id
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1); //获取AudioSessionId
mSendLevel = 0;
mRetransmitEndpointValid = false;
}

当native MediaPlayer创建后通过调用setMediaPlayer将创建的MediaPlayer native层对象赋给Java层的Context。

static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
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;
}

要了解native层是怎样将事件传递到上层就先要了解JNIMediaPlayerListener

JNIMediaPlayerListener是连接native层的MediaPlayer与Java层MediaPlayer的重要监听器。它通过JNI调用Java层的postEventFromNative方法将事件传递到Java层。最终传递给Java层MediaPlayer中的EventHandler进行处理。

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; // 指向native层 MediaPlayer的引用
jobject mObject; // Java层MediaPlayer的弱引用
};

JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
//初始化mClass和mObject 成员变量
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewGlobalRef(weak_thiz);
}

JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
//析构掉mClass和mObject 成员变量
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}

void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
//创建要传递的ParcelObject对象
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
//设置要发送的数据
nativeParcel->setData(obj->data(), obj->dataSize());
//调用Java层的postEventFromNative方法
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中最主要的就是notify方法,它将数据封装完后通过JNI接口调用Java层的postEventFromNative方法。

总结:
至此MediaPlayer初始化完成,Native层的MediaPlayer完成初始化后通过setMediaPlayer将其赋给Java层的mNativeContext,Java层的MediaPlayer在构造方法中调用native_setup将其引用设置到native层,并传递给JNIMediaPlayerListener。JNIMediaPlayerListener负责将native层的事件通知到Java层的MediaPlayer上。并交给EventHandler处理。整个结构大致如下:

整个调用过程如下:

为了避免整个博客的篇幅太长我将一个阶段对应一篇博客,下个博客将向大家介绍setDataSource–创建播放引擎,设置数据源,关于今天的这篇博客如果大家有什么疑问,或者发现有错误的地方欢迎大家留言或者发邮件告诉我。

Contents
  1. 1. 每个阶段的任务
  2. 2. MediaPlayer播放框架源代码解析:
    1. 2.1. 创建MediaPlayer对象
    2. 2.2. 建立底层MediaPlayer与Java层MediaPlayer事件传递的通道