Android MediaPlayer 源码分析(二) —— 设置数据源和关联MediaPlayerService

393 阅读5分钟

前言

上一篇我们分析了MediaPlayer的创建过程和Native层的消息回调流程,本文将分析MediaPlayer的设置数据源的流程,并跟随源码逐步分析到MediaPlayer与MediaPlayerService建立关联的过程。

Android MediaPlayer支持网络和本地两种不同的数据源,本文挑选相对简单的本地数据源来做分析。在MediaPlayer的Client端,网络数据源的设置流程其实与本地数据源大同小异,这里不再另行分析。

setDataSource设置数据源

当APP中创建好MediaPlayer实例后,需要将视频数据源设置到MediaPlayer中,如下方式调用:

    mMediaPlayer.setDataSource(mPath);

MediaPlayer中有多个setDataSource的重载方法,经过多重调用后,最终将本地数据源路径Path,转换成FileDescriptor文件描述符类型,并传入到native层。

// frameworks/base/media/java/android/media/MediaPlayer.java

//将path路径转换成FileDescriptor类型
public void setDataSource(...){
    final File file = new File(path);
    try (FileInputStream is = new FileInputStream(file)) {
        setDataSource(is.getFD());
    }
}

public void setDataSource(FileDescriptor fd, long offset, long length) 
        throws IOException, IllegalArgumentException, IllegalStateException{
    _setDataSource(fd, offset, length);
}

//最终调用到native方法
private native void _setDataSource(FileDescriptor fd, long offset, long length)
        throws IOException, IllegalArgumentException, IllegalStateException;

经过JNI动态注册方法,_setDataSource调用到android_media_MediaPlayer_setDataSourceFD方法中。

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

static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    //获取之前创建好的C++层的MediaPlayer实例
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);

    ...

    //从Java层FileDescriptor对象中取出对应的文件描述符。在Native层,文件描述符是一个int整型
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    ALOGV("setDataSourceFD: fd %d", fd);
    //mp->setDataSource 调用C++层的setDataSource方法
    //process_media_player_call方法用于抛出异常给Java层
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

将Java层FileDescriptor对象转换为Native层的文件描述符,并调用C++层的MediaPlayer的setDataSource方法

// frameworks/av/media/libmedia/mediaplayer.cpp

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
    status_t err = UNKNOWN_ERROR;
    //通过Binder机制,获取MediaPlayerService的接口代理对象
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
        //创建MediaPlayerService中的播放器对象,并返回播放器接口代理对象
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
        //设置重传端点,这里不做分析
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            //将fd传递给MediaPlayerService中的播放器对象
            (NO_ERROR != player->setDataSource(fd, offset, length))) {
            player.clear();
        }
        //关联MediaPlayerService的播放器对象
        err = attachNewPlayer(player);
    }
    return err;
}

MediaPlayer::setDataSource中的逻辑分为几个步骤:

  1. 获取IMediaPlayerService类型的服务端接口代理
  2. 获取IMediaPlayer类型的服务端播放器的接口代理
  3. 将fd文件描述符传递给服务端的播放器
  4. 关联服务端播放器代理对象

以上步骤可以看出,C++层的MediaPlayer对象最终通过binder通信机制,创建并关联到MediaPlayerService的服务端播放器,最终的播放器逻辑也是通过binder机制将数据传递到MediaPlayerService服务端完成。 本篇不分析MediaPlayerService的服务端逻辑,会另外再开一个系列分析MediaPlayerService和播放器的源码。 下面我们再来分析,MediaPlayer是如何与MediaPlayerService建立关联的。

与MediaPlayerService建立关联

首先来看如何获取服务端接口代理

// frameworks/av/media/libmedia/IMediaDeathNotifier.cpp

// establish binder interface to MediaPlayerService
/*static*/const sp<IMediaPlayerService>
IMediaDeathNotifier::getMediaPlayerService()
{
    ALOGV("getMediaPlayerService");
    Mutex::Autolock _l(sServiceLock);
    if (sMediaPlayerService == 0) {
        //获取ServiceManager的接口代理对象
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            //通过ServiceManager获取服务注册名称为"media.player"的Service,返回IBinder对象
            binder = sm->getService(String16("media.player"));
            if (binder != 0) {
                break;
            }
            ALOGW("Media player service not published, waiting...");
            usleep(500000); // 0.5 s
        } while (true);

        if (sDeathNotifier == NULL) {
            sDeathNotifier = new DeathNotifier();
        }
        //设置IBinder的死亡监听
        binder->linkToDeath(sDeathNotifier);
        //将IBinder对象转换为IMediaPlayerService对象
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    return sMediaPlayerService;
}

通过ServiceManager获取服务注册名称为"media.player"的Service,再转换为IMediaPlayerService类型。

下面是通过MediaPlayerService服务端接口代理,创建IMediaPlayer播放器接口代理

// frameworks/av/media/libmedia/IMediaPlayerService.cpp

class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
    
    ...
    
    virtual sp<IMediaPlayer> create(
            const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
        //将远程调用参数封装到Parcel中
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(IInterface::asBinder(client));
        data.writeInt32(audioSessionId);
        //调用远程transact方法
        remote()->transact(CREATE, data, &reply);
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());
    }
    
    ...
}

status_t BnMediaPlayerService::onTransact(
    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());
            audio_session_t audioSessionId = (audio_session_t) data.readInt32();
            //这里会调用到MediaPlayerService中的create方法,返回IMediaPlayer对象
            sp<IMediaPlayer> player = create(client, audioSessionId);
            reply->writeStrongBinder(IInterface::asBinder(player));
            return NO_ERROR;
        } break;
        
        ...
        
    }
}

这里看起来比较复杂,其实是一段经典的binder机制的通信流程,简单来说就是,MediaPlayer Client端,通过IMediaPlayerService接口代理,调用MediaPlayerService的create方法,创建Player播放器对象,并返回IMediaPlayer的播放器接口代理对象。

那么获取到播放器接口代理以后,通过播放器接口代理,将fd等参数传给服务端播放器。

// frameworks/av/media/libmedia/IMediaPlayer.cpp

class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
    
    ...
    
    status_t setDataSource(int fd, int64_t offset, int64_t length) {
        //将远程调用参数封装到Parcel中
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        data.writeFileDescriptor(fd);
        data.writeInt64(offset);
        data.writeInt64(length);
        //调用远程transact方法
        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
        return reply.readInt32();
    }
    
    ...
}

status_t BnMediaPlayer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
       
        ...
       
        case SET_DATA_SOURCE_FD: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            int fd = data.readFileDescriptor();
            int64_t offset = data.readInt64();
            int64_t length = data.readInt64();
            //这里会调用到BnMediaPlayer的setDataSource方法
            reply->writeInt32(setDataSource(fd, offset, length));
            return NO_ERROR;
        }
        
        ...

    }
}

同样,player->setDataSource也是通过binder机制,调用到服务端的播放器setDataSource方法,最终将fd等参数传递到服务端播放器中。

// frameworks/av/media/libmedia/mediaplayer.cpp

status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
    status_t err = UNKNOWN_ERROR;
    sp<IMediaPlayer> p;
    { // scope for the lock
        Mutex::Autolock _l(mLock);

        ...

        clear_l();
        p = mPlayer;
        //关联到mPlayer属性中
        mPlayer = player;
        
        ...
    }
    if (p != 0) {
        //断开旧的player对象
        p->disconnect();
    }
    return err;
}

最后,使用sp mPlayer属性,关联服务端播放器代理对象,方便后续的播放器逻辑进行调用。

小结

本文分析了MediaPlayer的setDataSource方法流程,从Java层将数据传递到native层,再从native层通过binder机制,最终传递到MediaPlayerService中。

由于篇幅有限,我们只分析了MediaPlayer的Client端的调用流程。而MediaPlayerService源码和binder机制,需要另外再开一个系列分析。