Android -- MediaPlayer内部实现简析
转载:https://www.cnblogs.com/zhchoutai/p/8715660.html
在之前的博客中,已经介绍了使用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 s
Android MediaPlayer深入解析:从创建到播放的全程剖析

本文详细分析了Android系统中MediaPlayer的内部实现,从创建、设置音频类型、设置资源、准备及启动播放的全过程。通过源码解析,揭示了MediaPlayer如何与Native层交互,涉及MediaPlayerService、StagefrightPlayer、 AwesomePlayer等多个关键组件的工作原理。通过了解这些,有助于开发者更深入地理解和使用MediaPlayer。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



