3、MediaServer 进程启动:init.rc 启动脚本、main_mediaserver.cpp 入口分析、关键初始化流程

好,咱们今天来聊聊 MediaServer 是怎么启动的。说实话,这个进程是整个 Android 多媒体系统的“大管家”。你想想看,所有音频、视频的播放录制,底层硬件编解码器的调度,全都要经过它。要是它起不来,整个多媒体栈就瘫了。

我个人习惯,拿到一个系统服务,先看它怎么被拉起来的。这就像看一个人,先看他怎么出生的。MediaServer 的“出生证明”,就在 init.rc 里。

3.1 init.rc 启动脚本:谁叫醒了 MediaServer?

Android 系统启动后,init 进程会解析 init.rc 文件。这里面定义了一堆 service,MediaServer 就是其中之一。我贴一段我实际项目中看过的代码,你感受一下:

service media /system/bin/mediaserver
    class main
    user media
    group audio camera drmrpc mediadrm
    ioprio rt 4
    priority 10
    capabilities SYS_NICE
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart drmserver

这段配置信息量很大。我一条条给你拆解:

  • service media:定义了一个叫 media 的服务,可执行文件路径是 /system/bin/mediaserver。
  • class main:它属于 main 类。init 进程启动时,会按 class 来批量启动服务。main 类是最核心的一批。
  • user media / group ...:以 media 用户身份运行,权限组包括 audio、camera 等。说白了,它天生就有操作音频、摄像头、DRM 的权限。
  • ioprio rt 4 / priority 10:实时 I/O 优先级,进程优先级 10。这保证了多媒体处理的实时性。我曾经遇到过一个问题,MediaServer 被降权后,音频播放出现卡顿,查了半天才发现是优先级被改了。
  • onrestart restart ...:这行很关键。如果 MediaServer 挂了,它会自动重启 audioserver、cameraserver、drmserver。为什么?因为这些服务都依赖 MediaServer 提供的底层能力。MediaServer 一挂,它们也活不了,干脆一起重启。

核心要点:MediaServer 是 main class 服务,系统启动早期就会被拉起。它拥有 audio、camera 等硬件访问权限,并且是实时优先级进程。

3.2 main_mediaserver.cpp 入口:一切从这里开始

好,init 把进程拉起来了,接下来执行的就是 main 函数。入口文件是 frameworks/av/media/mediaserver/main_mediaserver.cpp。代码不长,但每一步都踩在关键点上。我直接上代码:

int main(int argc __unused, char **argv __unused)
{
    // 1. 获取 ProcessState 实例,初始化 Binder 驱动
    sp<ProcessState> proc(ProcessState::self());
    // 2. 获取 ServiceManager 的代理
    sp<IServiceManager> sm = defaultServiceManager();
    // 3. 初始化音频效果框架
    AudioEffect::initCheck();
    // 4. 注册各个多媒体服务
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    CameraService::instantiate();
    AudioFlinger::instantiate();
    AudioPolicyService::instantiate();
    RadioService::instantiate();
    SoundTriggerHwService::instantiate();
    // 5. 启动 Binder 线程池
    ProcessState::self()->startThreadPool();
    // 6. 主线程加入线程池,等待处理请求
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

嗯,这里要注意几个关键点:

  1. ProcessState::self():这是 Binder 通信的基础。它负责打开 Binder 驱动,做内存映射。没有这一步,后面所有服务都没法和外界通信。
  2. defaultServiceManager():拿到 ServiceManager 的代理。ServiceManager 是 Android 服务的“黄页”,所有服务都要去那里注册。
  3. AudioEffect::initCheck():初始化音频效果引擎。我记得有一次,某个定制 ROM 的音频 EQ 不生效,查到最后发现是这行代码之前被注释掉了,导致效果框架根本没初始化。
  4. 各个 Service 的 instantiate():这是真正的服务注册。每个 instantiate 内部会创建服务对象,然后调用 ServiceManager 的 addService 把自己注册进去。
  5. startThreadPool + joinThreadPool:启动 Binder 线程池,然后主线程也加入进去。这样 MediaServer 就进入了事件循环,等待客户端来调用。

个人经验:我曾经在调试一个第三方应用播放视频黑屏的问题时,发现 MediaServer 的 Binder 线程池被耗尽了。原因是某个服务在 onTransact 里做了耗时操作,导致其他请求排队超时。所以,不要在 Binder 回调里做耗时操作,这是血泪教训。

3.3 关键初始化流程:服务注册的幕后

上面 main 函数里调用了 MediaPlayerService::instantiate() 等函数。那 instantiate 内部到底干了什么?我以 MediaPlayerService 为例,给你拆解一下:

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"),
            new MediaPlayerService());
}

代码很简单,就两步:

  • new 一个 MediaPlayerService 对象。
  • 通过 ServiceManager 的 addService 注册,服务名为 "media.player"。

但这里有个细节:MediaPlayerService 的构造函数里做了什么? 我翻过源码,构造函数里其实没做太多事,只是初始化了一些成员变量。真正的初始化工作,是在 onFirstRef() 里完成的。因为 MediaPlayerService 继承自 RefBase,当引用计数第一次变为 1 时,会触发 onFirstRef。这是 Android 智能指针的一个惯用技巧。

类似的,AudioFlinger 的初始化就更复杂了。它会在 instantiate 时打开 ALSA 驱动,创建混音线程,初始化音频硬件抽象层。我画了一张图,帮你理清整个初始化流程:

MediaServer 进程启动与初始化流程 1. init.rc 解析 class main / user media 2. main_mediaserver.cpp ProcessState / ServiceManager 3. 服务注册 instantiate() 调用 注册到 ServiceManager 的服务 MediaPlayerService AudioFlinger AudioPolicyService CameraService ResourceManagerService RadioService / SoundTrigger Binder 线程池启动,进入事件循环

从图上你可以看到,整个流程是线性的:init.rc 拉起进程 → main 函数初始化 Binder → 注册各个服务 → 启动线程池等待请求。每一步都环环相扣。

3.4 避坑指南:MediaServer 启动常见问题

做多媒体开发这些年,我在 MediaServer 启动上踩过不少坑。这里分享几个典型的:

问题现象 根本原因 解决方案
MediaServer 启动后立即 crash Binder 驱动未打开,或 SELinux 策略阻止了 Binder 通信 检查 dmesg 和 logcat,确认 Binder 驱动状态;调整 SELinux policy
服务注册失败,log 显示 "addService failed" ServiceManager 未就绪,或服务名冲突 确认 ServiceManager 进程已启动;检查是否有同名服务重复注册
音频服务初始化卡死 ALSA 驱动加载失败,或音频 HAL 库缺失 检查 /dev/snd/ 设备节点是否存在;确认 audio.primary.xxx.so 已正确编译
MediaServer 进程权限不足 init.rc 中 user/group 配置错误,或缺少 capabilities 对照原生配置,检查 user、group、capabilities 字段

我曾经踩过的坑:有一次在移植 Android 到新硬件平台时,MediaServer 启动后一直报 "Cannot find service media.player"。我查了两天,最后发现是 init.rc 中 service 的 name 写成了 "mediaplayer"(少了个点),而客户端代码查找的是 "media.player"。就这一个字符的差异,导致整个多媒体栈不可用。所以,服务名一定要前后一致,这是最基础也最容易忽略的问题。

3.5 小结

好了,这一章我们走通了 MediaServer 从出生到上岗的全过程。从 init.rc 的配置,到 main 函数的执行,再到各个服务的注册,最后进入 Binder 事件循环。你想想看,这个过程其实和开一家公司很像:init.rc 是营业执照,main 函数是开业典礼,服务注册是挂牌营业,Binder 线程池就是前台接待。

下一章,我们会深入其中一个核心服务——AudioFlinger,看看音频数据是怎么在底层流转的。不过那是后话了,先把今天的内容消化掉。


公众号:蓝海资料掘金营,微信 deep3321