在播放一个本地音视频文件或网络传输的音视频流时,apk中一般会调用类似如下代码(本文以播放一个网络视频流为例进行分析):
MediaPlayer mp = new MediaPlayer();(1) //创建一个播放器
mp.setDataSource("rtsp://10.0.149.217:554/stream1"); (2)//参数指定路径或url
mp.prepare(); (3)
mp.start(); (4)
上面的代码中总共有4条语句,本文重点分析(1)和(2),它涉及到播放器是如何一层层调用到多媒体框架中去.
我们先看(1)的调用流程:
public MediaPlayer() {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper); //EventHandler用于接收native层发送过来的消息
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
mTimeProvider = new TimeProvider(this);
mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
mOpenSubtitleSources = new Vector<InputStream>();
mInbandSubtitleTracks = new SubtitleTrack[0];
/* 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()的jni层实现函数为android_media_MediaPlayer_native_setup().定义在android_media_MediaPlayer.cpp中.
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer(); //此处创建了一个native层的MediaPlayer对象
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
env->SetIntField(thiz, fields.context, (int)player.get()); //此处将创建的native层的MediaPlayer对象保存到fields对象的context成员中.
return old;
}
我们再看(2)的流程:
java层MediaPlayer的setDataSource方法被重载了多次,主要是设置播放音视频的路径不同,但最终都调用到native方法_setDataSource(),它对应的jni实现为
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