多媒体框架

多媒体播放框架

多媒体核心框架涉及到的代码文件有:
java层:
frameworks/base/media/java/android/media/MediaPlayer.java

jni层:
frameworks/base/media/jni/android_media_MediaPlayer.cpp

native层:
frameworks/av/media/libmedia/Mediaplayer.cpp
frameworks/av/include/media/Mediaplayer.h
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
frameworks/av/media/libmediaplayerservice/libmediaplayerservice/MediaPlayerService.h
frameworks/av/media/libmediaplayerservice/libmediaplayerservice/MediaPlayerFactory.cpp
MediaPlayerFactory.cpp以 下就是具体的播放器的实现了。

媒体播放的抽象层:

frameworks/av/include/media/MediaPlayerInterface.h
以上文件是播放器的abstract base class,类似于硬件抽象层(HAL),是每个具体的播放器都需要根据这个基类来实现对应的MediaPlayerInterface。

多媒体播放的核心框架主要涉及到两条典型的数据流:
一个是命令+命令的响应(cmd+respond)
一个是事件(unsolicited)
     但凡用户跟后台服务一般都有如上的两个接口,如phone,wifi等核心模块,莫不是遵循以上的结构,只不过实现方式有差异。在这里命令和事件通道都是通过ibinder来实现,而phone和wifi模块的命令和事件通道都是通过socket通讯机制来实现的。那么为什么phone和wifi不通过ibinder来实现呢?因为phone的后台服务pppd以及wifi的后台服务wpa_supplicant,他们是linux的原生态代码,这些代码是在android诞生之前就有的,所以当他们提供的通讯机制就是使用的socket,那android本着最少修改的原则,当然就是还是原用了他本身自带的socket通讯来实现的;而多媒体服务这块,则是android自己重新规划实现的,所以他自然就使用他们自己的ibinder通讯机制。在很多场景下,android在条件合适的情况下,一般都会优先使用ibinder通讯,因为ibinder通讯相比socket通讯效率更高,安全性更好,这个后面单独开blog来论述。
在讲native层的代码框架前,先上过类图:


frameworks/av/include/media/IMediaPlayer.h定义了ImediaPlayer的binder接口
frameworks/av/include/media/IMediaPlayerService.h定义了IMediaPlayerService的binder接口
frameworks/av/include/media/IMediaPlayerClient.h定义了IMediaPlayerClient的binder接口

上图中的黄色标识部分就是多媒体框架中的后台服务(media.player service),他运行于一个独立的进程中,他既包含了binder的服务器端(BnMediaPlayerService,BnMediaPlayer),也包含了binder的客服端(BpMediaPlayerClient);而蓝色标识的部分,则是媒体播放器的客服端,即player user process,他同样运行于用户所在的进程,客服端同样也包含binder的服务器端(BnMediaPlayerClient),也包含了binder的客服端(BpMediaPlayer,BpMediaPlayerService
另外media.player service进程包含了两个binder实例,即两个binder服务器端(BnMediaPlayerService,BnMediaPlayer),他们在同一个进程里。那当一个remote ipc过来时,他们怎么区分出来该调用是该路由给BnMediaPlayerService还是路由BnMediaPlayer呢?该问题同样在我的另外的blog中来阐述。在这里只是提出这个问题,为下一篇blog做些知识上的铺垫。

从binder的分类来讲:
IMediaPlayerService对应的binder是注册到service manager中去的实名binder,见如下代码段:
void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}


而ImediaPlayer和IMediaPlayerClient对应的binder都是匿名binder,在这里先说这些binder的作用。IMediaPlayerService binder其实只是起个桥梁的作用,目的是为了获取两个匿名binder:ImediaPlayer和IMediaPlayerClient。而ImediaPlayer binder对应前面讲个的用于应用通过这个binder向media service后台服务发送命令和接受命令的响应;而IMediaPlayerClient则是被media service后台服务用来向应用返回媒体播放事件的。
IMediaPlayerService实名binder的获取方式:
// establish binder interface to MediaPlayerService
/*static*/const sp<IMediaPlayerService>&
IMediaDeathNotifier::getMediaPlayerService()
{
    ALOGV("getMediaPlayerService");
    Mutex::Autolock _l(sServiceLock);
    if (sMediaPlayerService == 0) {
        sp<IServiceManager> sm = defaultServiceManager();//实名服务的管理者
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("media.player"));//提供实名服务对应的名字,返回的是binder实例在客服端进程中的引用句柄号。
            if (binder != 0) {
                break;
            }
            ALOGW("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);//将Bpbinder cast to BpMediaPlayerService

    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    return sMediaPlayerService;
}
而匿名binder是怎么获取的呢?
分两部分讲,首先说IMediaPlayerClient binder是如果告诉后台服务的:
virtual sp<IMediaPlayer> create(//frameworks/av/media/libmedia/IMediaPlayerService.cpp
            const sp<IMediaPlayerClient>& client, int audioSessionId) {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(client->asBinder());//将IMediaPlayerClient binder实体传递到binder驱动中
        data.writeInt32(audioSessionId);

        remote()->transact(CREATE, data, &reply);
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());//获取ImediaPlayer binder实体在当前进程中的引用句柄,并将它封装在BpMediaPlayer实例中。
    }

而服务器端是如何将ImediaPlayer binder实体传递到应用段所在的进程的呢?
status_t BnMediaPlayerService::onTransact(//frameworks/av/media/libmedia/IMediaPlayerService.cpp
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
        case CREATE: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            sp<IMediaPlayerClient> client =
                interface_cast<IMediaPlayerClient>(data.readStrongBinder());//获取BpMediaPlayerClient
            int audioSessionId = data.readInt32();
            sp<IMediaPlayer> player = create(client, audioSessionId);//创建BnMediaPlayer实体
            reply->writeStrongBinder(player->asBinder());//将BnMediaPlayer binder实体写到驱动中。
            return NO_ERROR;
        } break;
        case DECODE_URL: {

以上就是匿名binder实体如何通过实名binder实体来进行传递的概要说明。

而播放器的播放调用过程如下:
MediaPlayer  mMediaPlayer = new MediaPlayer( );
mMediaPlayer.setDataSource(mContext, mUri);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.prepareAsync();
mMediaPlayer.start();

下面以setDataSource调用过程为列来说明用户的命令是如何从客服端发送到媒体后台服务的。

MediaPlayer::setDataSource //MediaPlayer.cpp
  |-->const sp<IMediaPlayerService>& service(getMediaPlayerService());//获取BpMeidaPlayerService
  |-->sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//返回BpMediaPlayer
  |-->player->setDataSource(url, headers)//调用BpMediaPlayer.setDataSource(ImediaPlayer.cpp)
  以上调用会通过binder调用到binder实体对应的函数:
  MediaPlayerService::Client::setDataSource
        |-->MediaPlayerFactory::getPlayerType()//获取播放器的类型
		|-->setDataSource_pre(playerType)//根据playerType创建具体的播放器实例,例如ffplayer(见ffplayer.cpp) 
		|-->p->setDataSource//调用具体播放器的setDataSource函数

下面再说下媒体播放器的事件是如何通知到用户的。分两部分来论述:媒体后台服务器端和用户端
媒体后台服务器端:
MediaPlayerService::Client::notify //MediaPlayerService.cpp
 |-->sp<IMediaPlayerClient> c;
 |-->c = client->mClient;//获取BpMediaPlayerClient,即binder的客服端             
 |-->c->notify();
以上调用会通过binder调用到BnMediaPlayer,即binder实体端: 
MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)//MediaPlayer.cpp
|-->listener->notify()
 而以上MediaPlayerService::Client::notify函数会通过如下过程传递到具体的播放器中,以供播放器来上报事件。 
 
MediaPlayerService::Client::createPlayer
|--->MediaPlayerFactory::createPlayer(playerType, this, notify)
     |--->p->setNotifyCallback  //MediaPlayerInterface.h
          |->mNotify = notifyFunc
          |->sendEvent //上面初始化的mNotify函数,被sendEvent函数调用
             |->mNotify(mCookie, msg, ext1, ext2, obj);

所以在具体的播放器实现中,可以直接调用MediaPlayerBase::sendEvent来将事件发送到用户。

播放器的用户端:
回到MediaPlayer.cpp文件中的MediaPlayer::setListener函数,该函数设置了侦听者,到收到事件时,会调用该侦听者。该侦听者的初始化过程如下:
android_media_MediaPlayer_native_setup//android_media_MediaPlayer.cpp
   |-->sp<MediaPlayer> mp = new MediaPlayer();
   |-->sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
   |-->mp->setListener(listener);//即为<span style="font-size:18px;">MediaPlayer::setListener函数</span>
   以上过程,将<span style="font-size:18px;">侦听者设置为JNIMediaPlayerListener,所以当事件产生时,会调用
</span>   void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
      |-->env->CallStaticVoidMethod(mClass, fields.post_event...);//从jni调到MediaPlayer.java
          |--->postEventFromNative //MediaPlayer.java
               |-->Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
               |-->mp.mEventHandler.sendMessage(m);
                //MediaPlayer.mEventHandler = new EventHandler(this, looper);

具体到用户的播放器界面中,用户可以通过实现如下函数:
MediaPlayer.OnCompletionListener
MediaPlayer.OnTimedTextListener
MediaPlayer.OnInfoListener
MediaPlayer.OnPreparedListener
MediaPlayer.OnBufferingUpdateListener
MediaPlayer.OnBufferingUpdateListener
来实现对相应事件的处理。


相关的参考文档:
http://developer.android.com/guide/topics/media/mediaplayer.html


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值