MediaPlayer 流程分析

来源:互联网 发布:js input只能输入数字 编辑:程序博客网 时间:2024/05/29 06:35


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系统来实现。

至此播放视频文件的基本流程就是如上所述。