Android音视频开发入门(4)MediaPlayerService

1. Client/server通过IPC的通信流程图

=============================================================================================

Android中的通信很多都是通过Binder机制来完成的,对于MediaPlayer和MediaPlayerService也不例外。

我们来看看下面的C/S Binder通信图:

在这里插入图片描述

从图中可以总结出下面几点:

  • MediaPlayer是客户端也就是 C/S中的 Client层

  • MediaPlayerService和MediaPlayerService::Client是服务端,也就是我们说的 C/S模型中的S层

  • MediaPlayerService实现 IMediaPlayerService 定义的业务逻辑

  • MediaPlayerService::Client 实现 IMediaPlayer定义的业务逻辑

  • 通过Transact()可以向远端的IBinder对象发出调用,通过 onTransact()可以使你自己的远程对象能够响应接收到的调用。

2. 相关联类图

==========================================================================

我们知道Binder通信时,需要通过 IBinder接口转化具体的实体对象,那么中间会有很多其它类,下面看看类关系图:

在这里插入图片描述

细品IPC通信图,可以得出下面的结论:

  • BnXXX 和 BpXXX都派生自两个类

class BpXXX : public IMediaPlayerService, public BpRefBase

class BnXXX : public IMediaPlayerService, public BBinder

  • BpXXX和BnXXX都派生自 IMediaPlayerService,我们之前说过,IMediaPlayerService使用来定义一些功能的接口的,而派生出两个类,所以它们虽然使用了相同的接口,但是他们有不同的实现方式

BpXXX中:把对应的binder_transaction_data 打包之后,通过BpRefBase中的 mRemote(BpBinder)发送出去,并等待结果

BnXXX中:实现对应的业务逻辑,通过调用 BnXXX 派生类中的方法来实现,如 MediaPlayerService::Client

  • IBinder用于进程间通信

BpRefBase中有一个remote()用来与Binder驱动交互

Binder是用来从Binder驱动中接收相关请求并进行相关处理的

BpBinder和BinderDriver进行互通

3. MediaPlayerService产生过程

===========================================================================================

我们从入口看,入口在 /frameworks/av/media/mediaserver/main_mediaserver.cpp:

int main(int argc __unused, char **argv __unused)

{

//信号

signal(SIGPIPE, SIG_IGN);

//进程赋值处理

sp proc(ProcessState::self());

sp sm(defaultServiceManager());

ALOGI(“ServiceManager: %p”, sm.get());

InitializeIcuOrDie();

MediaPlayerService::instantiate();

ResourceManagerService::instantiate();

registerExtensions();

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

从第一个有意义的函数 defaultServiceManager(),它在/frameworks/native/libs/binder/IServiceManager.cpp:

p defaultServiceManager()

{

//如果存在一个 BpServiceManager就返回它

if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

//单例模式

{

AutoMutex _l(gDefaultServiceManagerLock);

while (gDefaultServiceManager == NULL) {

gDefaultServiceManager = interface_cast(

ProcessState::self()->getContextObject(NULL));

if (gDefaultServiceManager == NULL)

sleep(1);

}

}

return gDefaultServiceManager;

}

这种构建模式其实是我们熟悉的 单例模式。每个进程都只需要构建出一个 BpServiceManager代理,

ProcessState::self() -> getContextObject(NULL),接下来看看 getContextObject(),它在 /frameworks/native/libs/binder/ProcessState.cpp中:

sp ProcessState::getContextObject(const sp& /caller/)

{

return getStrongProxyForHandle(0);

}

sp ProcessState::getStrongProxyForHandle(int32_t handle)

{

sp result;

AutoMutex _l(mLock);

//在数组 mHandleToObject里面根据handle索引查找handle_entry结构体

handle_entry* e = lookupHandleLocked(handle);

if (e != NULL) {

//指向结构体中的binder地址

IBinder* b = e->binder;

if (b == NULL || !e->refs->attemptIncWeak(this)) {

if (handle == 0) {

Parcel data;

status_t status = IPCThreadState::self()->transact(

0, IBinder::PING_TRANSACTION, data, NULL, 0);

if (status == DEAD_OBJECT)

return NULL;

}

//BpBinder新建一个IBinder BpBinder.create(0)

b = BpBinder::create(handle);

e->binder = b;

if (b) e->refs = b->getWeakRefs();

result = b;

} else {

result.force_set(b);

e->refs->decWeak(this);

}

}

return result;

}

struct handle_entry{

IBinder* binder;

RefBase::weakref_type* refs;

}

总结代码

传入的句柄handler的值为0,表示ServiceManager,构建一个BpBinder,所以现在相当于 gDefaultServiceManager = interface_cast(BpBinder.create(0))

接下来看看 interface_cast()是什么:

inline sp interface_cast(const sp& obj)

{

return IServiceManager::asInterface(obj);

}

asInteface(){

//这里构建了一个新的BoServiceManager对象

intr = new BpServiceManager(obj)

}

其asInterface最终还是创建出一个 BpServiceManager对象。

所以defaultServiceManager()就是 句柄handle(0)创建一个 BpBinder(0),跟举这个BpBinder创建出一个 BpServiceManager代理。

下面来分析一下BpServiceManager 这个类是什么成分,代码如下:

class BpServiceManager : public BpInterface

{

public:

explicit BpServiceManager(const sp& impl)
BpInterface(impl) {}

};

这里的 它继承了 BpInterface,它是一个模板类,这里相当于它同时 继承了 BpInterfaceIServiceManager

BpInterface在用到它的构造函数时,就会 去添加服务内部

在 MediaPlayerSrvice.cpp中有一个 instantiate(),通过ServiceManager来添加MediaPlayerService服务:

void MediaPlayerService::instantiate() {

defaultServiceManager()->addService(

String16(“media.player”), new MediaPlayerService());

}

defaultServiceManager() 返回的是刚创建的BpServiceManager,并调用它的 addService()

BpMediaPlayerService作为服务端代理,那么BnMediaPlayerService一定是实现端

MediaPlayerService继承自 BnMediaPlayerService,实现了真正的的业务函数。

4. 添加服务的过程

============================================================================

下面分析一下 BpServiceManager的 addService()

virtual status_t addService(const String16& name, const sp& service,

bool allowIsolated, int dumpsysPriority) {

Parcel data, reply;

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

data.writeString16(name);

//也就是MediaPlayerService

data.writeStrongBinder(service);

data.writeInt32(allowIsolated ? 1 : 0);

data.writeInt32(dumpsysPriority);

status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

return err == NO_ERROR ? reply.readExceptionCode() : err;

}

这里的 remote函数返回的就是 前面创建的 BpBinder(0)对象:

status_t BpBinder::transact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

// Once a binder has died, it will never come back to life.

if (mAlive) {

status_t status = IPCThreadState::self()->transact(

mHandle, code, data, reply, flags);

if (status == DEAD_OBJECT) mAlive = 0;

return status;

}

return DEAD_OBJECT;

}

status_t IPCThreadState::transact(int32_t handle,

uint32_t code, const Parcel& data,

Parcel* reply, uint32_t flags)

{

//发送addTransactionData

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

} else {

//等待响应

err = waitForResponse(NULL, NULL);

}

return err;

}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,

int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)

{

//cmd BC_TRANSACTION 应用程序向Binder发送的命令

binder_transaction_data tr;

tr.target.handle = handle;

tr.code = code;

tr.flags = binderFlags;

//把命令和数据一起发送到 Parcel mOut中

mOut.writeInt32(cmd);

mOut.write(&tr, sizeof(tr));

return NO_ERROR;

}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

uint32_t cmd;

while (1) {

//和Driver进行沟通后获得结果

if ((err=talkWithDriver()) < NO_ERROR) break;

cmd = (uint32_t)mIn.readInt32();

switch (cmd) {

case BR_TRANSACTION_COMPLETE:

break;

}

}

return err;

}

status_t IPCThreadState::talkWithDriver(bool doReceive) {

binder_write_read bwr;

bwr.write_size = outAvail;

//写入 mOut的数据

bwr.write_buffer = (uintptr_t) mOut.data();

bwr.read_size = mIn.dataCapacity();

bwr.read_buffer = (uintptr_t) mIn.data();

//把mOut写到Binder中,并读取mIn的数据

if (ioctl(mProcess -> mDriverFD, BINDER_WRITE_READ, & bwr) >=0)

err = NO_ERROR;

return err;

}

joinThreadPool、startThreadPool 进入线程循环

talkWithDriver等待客户端Client请求,从Binder读取命令请求进行处理。

到现在为止,MediaPlayerService的服务端已经向总管 ServiceManager注册了

5. 通过BinderDriver和MediaPlayer通信的过程

====================================================================================================

下面看看客户端是如何获得服务的代理并和服务端通信的。

我们以 MediaPlayer的业务函数解码解析播放一个 网络视频的URL为例。

首先在 MediaPlayer decode(url)时,首先调用了 getMediaPlayerService()

我们来看看这个函数:

IMediaDeathNotifier::

getMediaPlayerService() {

//生成一个BpServiceManager代理对象

sp sm = defaultServiceManager();

sp binder;

do {

//获取String16(“media.player”)的服务对象

binder = sm -> getService(String16(“media.player”));

if (binder != 0) {

break;

}

usleep(500000); // 0.5 s

} while (true);

sMediaPlayerService = interface_cast < IMediaPlayerService > (binder);

return sMediaPlayerService;

}

Client端的MediaPlayer先获取一个 BpServiceManager的代理,然后调用其 getService()向服务总管 ServiceManager查询名叫 String16(“media.player”)的服务,这个getService的方法处于 /frameworks/native/libs/binder/ 的 IServiceManager.cpp:

class BpServiceManager :public BpInterface

{

public:

virtual sp getService(const String16 & name) const

{

int n = 0;

while (uptimeMillis() < timeout) {

n++;

//调用checkService函数

sp svc = checkService(name);

if (svc != NULL) return svc;

}

return NULL;

}

virtual sp checkService( const String16 & name) const

{

Parcel data, reply;

//首先调用writeInterfaceToken,然后写入 android.os.IServiceManager

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor ());

//写入media.player

data.writeString16(name);

//然后通过Binder把data及reply的数据通知出去,在onTransact函数中进行回调

remote()->transact(CHECK_SERVICE_TRANSACTION, data, & reply);

return reply.readStrongBinder();

}

}

这里首先将请求打包成Parcel格式,然后调用 remote->transact函数,前面我们分析过 BpServiceManager::remote返回的就是 newBpBinder(0),对应的句柄为 ServiceManager。继续在BpBinder中寻找实现代码:

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){

IPCThreadState::self()->transact(mHandle, code, data, reply, flags);

}

最后调用 IPCThreadState的transact函数,在Android中 ProcessState是哭护短和服务端的公共部分,作为Binder通信的基础,ProcessState是一个singleton类, 每个进程只有一个对象,这个对象负责打开Binder驱动,建立线程池,让其进程里面的所有线程都能通过Binder通信。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

Android 基础知识点

Java 基础知识点

Android 源码相关分析

常见的一些原理性问题

希望大家在今年一切顺利,进到自己想进的公司,共勉!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

img-3BVyKGsA-1712247667895)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

Android 基础知识点

Java 基础知识点

Android 源码相关分析

常见的一些原理性问题

[外链图片转存中…(img-eyRr4NfZ-1712247667896)]

希望大家在今年一切顺利,进到自己想进的公司,共勉!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值