http://hi.baidu.com/idrod/blog/item/915af8411608682bcefca336.html
首先编写一个媒体播放器
public class main extends Activity {
private static final int OPENLOCAL = 1;
private VideoView video;
private MediaController mc;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSPARENT);
setContentView(R.layout.main);
initComponents();
}
private void initComponents() {
video = (VideoView) findViewById(R.id.video);
mc = new MediaController(this);
}
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, OPENLOCAL, 0, R.string.open_local);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case OPENLOCAL:
video.setVideoPath("sdcard/Wildlife.3gp");
mc.setMediaPlayer(video);
video.setMediaController(mc);
break;
default:
return super.onContextItemSelected(item);
}
video.requestFocus();
return true;
}
}
上面是一个完整的媒体播放器源码,其中我使用了VideoView作为播放的框架。现在分析android是如何一步一步调用MediaPlayer来打开视频的。
在上诉代码中,与媒体播放有关的代码是:
video.setVideoPath("sdcard/Wildlife.3gp"); //设置媒体文件信息
mc.setMediaPlayer(video); //这2步都是跟设置媒体控制器有关
video.setMediaController(mc);
注:VideoVIew是android封装了MediaPlayer的一个类,因此这个类里面对MediaPlayer的调用方式以及顺序从另一个方面说也是最符合android标准的。
在VideoView中调用setVideoPath(String path)会自动将path作Uri转换,然后调用setVideoURI,然后初始化一些状态变量 然后调用openVideo,在openVideo中可以看到很多关于MediaPlayer的初始化动作,包括注册各种与MediaPlayer相关的事件监听函数。但这不是我所关心的,在MediaPlayer的API文档中,我们可以知道setDataSource才是跟打开视频文件有关的调用。果然,在设置完一系列的监听事件后,VideoView调用了MediaPlayer的setDataSource,将刚刚Uri化的文件路径作为参数传入。
在MediaPlayer中setDataSource(Context context, Uri uri, Map<String, String> headers)会将传入的Uri做一次解析,判断它的scheme信息,之后调用到setDataSource(String path) 这是一个native函数,这就表明它的实现是在一个jni中实现的,根据android JNI的命名规则 我们找到了 android_media_MediaPlayer
在android_media_MediaPlayer 发现setDataSource的jni调用为android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)(这里说明一下在android 2.3代码中这里会调用android_media_MediaPlayer_setDataSourceAndHeaders函数,但是在1.5中没有这个调用,1.5中调用到setDataSource就直接开始操作了,其实2者的差别并不大,所以不影响整个流程),在setDataSource中android首先调用getMediaPlayer来获取一个MediaPlayer的对象
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock); //线程锁
MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
return sp<MediaPlayer>(p);//一个模板类
}
其中Mutex 是一个封装过的线程类,它的定义在frameworks/base/include/utils/threads.h
而sp<T>则是一个模板,它的定义在frameworks/base/include/utils/RefBase.h
这里不对这2个做说明。
获得MediaPlayer的对象后android对传入的path做了一系列的合法性判断,然后将string转为C语言中的char* 指针,当然其它的参数也做类似的处理。然后将处理后的数据以参数的形式传给mp->setDataSource。
mediaplayer.cpp是对MediaPlay的实现,所以这里的mp其实就是mediaplayer,在被调用setDataSource后,mediaplayer首先调用getMediaPlayerService来获取MediaPlayer系统服务
sp<IMediaPlayerService>&
IMediaDeathNotifier::getMediaPlayerService()
{
LOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
LOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
LOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
上述代码是IPC通信相关,暂不分析。
在获得这个对象后,mediaplayer,调用create创建出一个IMediaPlayer接口的player对象,并记录下来。
到这里android就根据上层传入的视频文件地址创建出了一个指向这个地址的IMediaPlayer对象。
然后我们回到VideoView继续跟踪其它跟播放视频有关的流程。
除了setDataSource之外,VideoView还调用了MediaPlayer的setDisplay,setAudioStreamType,setScreenOnWhilePlaying以及prepareAsync,这些操作这里就不描述了,直接看跟播放有关的。
现在我已经将视频文件设置好了,那么可以开始播放了,在VideoView中播放视频是用MediaPlayerControlœ来控制,而播放则是调用MediaPlayer的start函数。
回到MediaPlayer中,发现start函数最终同样是调用到了jni接口上面,根据上面的经验,我们直接到android_media_MediaPlayer中找到android_media_MediaPlayer_start的调用。同样在这里面首先获得一个MediaPlayer的对象,然后调用这个对象的start()函数。
记得上面我们在mediaplay中创建的IMediaPlayer对象吗?这里就用到了这个对象,在设置完looping以及volume后,mediaplay 调用IMediaPlayer的start进行真正的播放
status_t start()
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
remote()->transact(START, data, &reply);
return reply.readInt32();
}
而这个调用同样是IPC通信,而最终会被IMediaPlayerService接收,然后调用到PVPlayer中,此时就进入OpenCore的核心。而视频的显示则是使用了android的Overlay系统来实现。
至此播放视频文件的基本流程就是如上所述。