Android音视频开发入门(3)C++中MediaPlayer的C/S架构

前面一层中,都是通过Java层调用到JNI层中,而JNI层向下到C++层并未介绍。
本节首先分析Java层的一个函数在C++层MediaPlayer中的过程。(路径为:/frameworks/av/media/libmedia/mediaplayer.cpp)

下面用 mp->setDataSource() 后,从C++层的setDataSource() 来看看C/S模式的过程

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;
        //通过IMediaPlayerService获取service端的MediaPlayerService
        const sp<IMediaPlayerService> service(getMediaPlayerService());
        if (service != 0) {
            //如果Service不为空,则调用service的create函数
            sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
            if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
                    (NO_ERROR != player->setDataSource(fd, offset, length))) {
                player.clear();
            }
            err = attachNewPlayer(player);
        }
        return err;
    }

setDataSource获取到了 IMediaPlayerService,我们都知道,这种带I的类名, 一般都是服务端的类在 客户端的代理,我们调用它的方法,实际上是调用 服务端的 这个去掉I的类的方法,所以我们要去 /frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp 里面,走它service的create流程。而这个时候,代码的角度也从 C(Client)来到了 S(service)层:

    sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
                                                audio_session_t audioSessionId)
    {
        //获取进程
        pid_t pid = IPCThreadState::self()->getCallingPid();
        int32_t connId = android_atomic_inc(&mNextConnId);
        //表示校验调用仿的uid,用来进行身份验证
        sp<Client> c = new Client(
                this, pid, connId, client, audioSessionId,
                IPCThreadState::self()->getCallingUid());
        ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
                IPCThreadState::self()->getCallingUid());

        //把构造的Client强引用对象赋值成 弱引用对象
        wp<Client> w = c;
        {
            Mutex::Autolock lock(mLock);
            //mClient声明为 SortedVector< wp<Client> >
            mClients.add(w);
        }
        return c;
    }

create总结:
将传入的clientId以及 Pid、ConnId进行身份验证,创建出一个 Client端在Service端的引用,并将该强引用转换成弱引用。
创建的过程是 通过一个 IPCThreadState,每一个线程都有一个IPCThreadState实例等级在Linux线程的上下文附属数据中。主要负责Binder的读取、写入和请求处理。

其实就是在Service层获取一个Client的引用而已。拿到Client不就可以相互通信了吗~
而这个Client又是具体指代什么,我们在头文件查看和从上一节的C/S图中发现,Client是继承BnMediaPlayer,并包含了IMediaPlayer相关接口

总结一下,继承关系为 Client -> BnMediaPlayer -> IMediaPlayer。create中,创建了一个Client并将这个Client添加到了全局列表mClients,这个mClients是一个 SortedVector,紧接着在Client中执行了 player->setDataSource(url,headers),即Client::setDataSource。可以直接认为 player==client

那么在C++中,这个 Client和MediaPlayer又是什么关系呢?

  • Client是MediaPlayerService的内部类,我们从上面代码已知,因为MediaPlayerService运行在服务端,故Client也运行在服务端
  • Client在MediaPlayerService.h中,那接着看看MediaPlayerService中的实现,实现过程中调用过MediaPlayerService的一些函数,同样回到serDataSource(),代码如下:
        //判断是否通过contentprovider提供的数据
        if (strncmp(url, "content://", 10) == 0) {
            // get a filedescriptor for the content Uri and
            // pass it to the setDataSource(fd) method

            String16 url16(url);
            int fd = android::openContentProviderFile(url16);
            if (fd < 0)
            {
                ALOGE("Couldn't open fd for %s", url);
                return UNKNOWN_ERROR;
            }
            //通过标识符去设置
            status_t status = setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
            close(fd);
            return mStatus = status;
        } else {
            player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
            sp<MediaPlayerBase> p = setDataSource_pre(playerType);
            if (p == NULL) {
                return NO_INIT;
            }

            return mStatus =
                    setDataSource_post(
                            p, p->setDataSource(httpService, url, headers));
        }
    }

而Client中的函数都是和 下面图(上一节中看过)对应的:
在这里插入图片描述
在一开始的代码中,最后执行了 err =attachNewPlayer(player)

status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
    status_t err = UNKNOWN_ERROR;
    //IMediaPlayer就是Client声明在客户端处的代理类
    sp<IMediaPlayer> p;
    { 
        //加锁
        Mutex::Autolock _l(mLock);

        if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
                (mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {
            ALOGE("attachNewPlayer called in state %d", mCurrentState);
            return INVALID_OPERATION;
        }

        clear_l();
        //赋值给代理类,mPlayer在MediaPlayer.h中声明,
        p = mPlayer;
        mPlayer = player;
        if (player != 0) {
            mCurrentState = MEDIA_PLAYER_INITIALIZED;
            err = NO_ERROR;
        } else {
            ALOGE("Unable to create media player");
        }
    }

    if (p != 0) {
        p->disconnect();
    }

    return err;
}

上面的函数,一个是MediaPlayer的setDataSource,会调用到attachNewPlayer(),这个函数最终会调用服务器端Client对应的函数。
这时候出现IMediaPlayer.hmediaplayer.hImediaPlayerClient.h,看的人都晕了,这个时候来分别看一下:

  • 从包结构上:IMediaPlayer和IMediaPlayerClient.h都在 /frameworks/av/media/libmedia中,而mediaplayer.h在/av/include/media中
  • 从功能上:他们的责任也不一样

而IMediaPlayer.h中都是虚函数,所以它的功能是作为 MediaPlayer功能的接口。
接下来来看看IMediaPlayerClient.h:

namespace android {

enum {
    NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
};

class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
{
public:
    explicit BpMediaPlayerClient(const sp<IBinder>& impl)
        : BpInterface<IMediaPlayerClient>(impl)
    {
    }

    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
        data.writeInt32(msg);
        data.writeInt32(ext1);
        data.writeInt32(ext2);
        if (obj && obj->dataSize() > 0) {
            data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
        }
        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
    }
};

IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.media.IMediaPlayerClient");

// ----------------------------------------------------------------------

status_t BnMediaPlayerClient::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
        case NOTIFY: {
            CHECK_INTERFACE(IMediaPlayerClient, data, reply);
            int msg = data.readInt32();
            int ext1 = data.readInt32();
            int ext2 = data.readInt32();
            Parcel obj;
            if (data.dataAvail() > 0) {
                obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
            }

            notify(msg, ext1, ext2, &obj);
            return NO_ERROR;
        } break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

}

来总结一下上面的代码:
在内部定义一个 BpMediaPlayerClient,也就是Client的父类。然后有一个onTranscat(),它是Binder通信中的回到函数。
所以说前面的player是C/S模式,IMediaPlayerClient.h的功能时一个MediaPlayer客户端的接口。

综上所述:

  1. mediaplayer.h的功能时对外(JNI层)的接口类,它最主要的是定义了一个MediaPlayer类(C++层),我们在android_media_MediaPlayer.cpp中就引入了 media/mediaplayer.
  2. IMediaPlayer.h则是一个实现 MediaPlayer(C++)功能的接口
  3. IMediaPlayerClient.h的功能时描述一个MediaPlayer(这里暂且为前面说的Client)客户端的接口
发布了248 篇原创文章 · 获赞 99 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/rikkatheworld/article/details/102916252