再前一个博客中大家已经对整个播放框架有了一个整体的了解接下来的这篇博客将针对每个具体细节进行介绍:
再进行介绍之前我们看下使用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; clazz = env->FindClass("android/media/MediaPlayer"); fields.context = env->GetFieldID(clazz, "mNativeContext", "J"); 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; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } 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(); sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener); 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; jobject mObject; };
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 = (jclass)env->NewGlobalRef(clazz); mObject = env->NewGlobalRef(weak_thiz); }
JNIMediaPlayerListener::~JNIMediaPlayerListener() { 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) { 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中最主要的就是notify方法,它将数据封装完后通过JNI接口调用Java层的postEventFromNative方法。
总结:
至此MediaPlayer初始化完成,Native层的MediaPlayer完成初始化后通过setMediaPlayer将其赋给Java层的mNativeContext,Java层的MediaPlayer在构造方法中调用native_setup将其引用设置到native层,并传递给JNIMediaPlayerListener。JNIMediaPlayerListener负责将native层的事件通知到Java层的MediaPlayer上。并交给EventHandler处理。整个结构大致如下:
整个调用过程如下:
为了避免整个博客的篇幅太长我将一个阶段对应一篇博客,下个博客将向大家介绍setDataSource–创建播放引擎,设置数据源,关于今天的这篇博客如果大家有什么疑问,或者发现有错误的地方欢迎大家留言或者发邮件告诉我。