Android音视频框架探索(一):多媒体系统服务MediaServer

0. 前言

最近计划在写这个关于Android音视频框架的专栏,一是回顾和整理之前工作时碰到过的一些Android底层知识,二是想更了解以下Android上的音视频架构。今天先介绍下Android中多媒体系统服务MediaServer。

1 SystemServices 系统服务

在Android系统架构中,SystemServices是一类模块化、功能集中的系统功能提供者。它们往往以native进程的形式运行在系统中,App可以通过Android Framework API调用,通过Binder来对他们进行访问,以获得操作底层硬件的能力。常见的系统服务进程包括:

  • SurfaceFlinger:Android的显示管理器,负责合成和渲染不同应用窗口的内容。
  • system_server:包含各类的管理服务,如进行窗口相关的操作会用到窗口管理服务WindowManager,进行电源相关的操作会用到电源管理服务PowerManager,如通知管理服务NotifacationManager、振动管理服务Vibrator、电池管理服务BatteryManager等等。
  • CameraServer:Android的相机服务,为上层提供访问相机的能力,可以通过Camera2 API访问。
  • MediaServer:专用于处理多媒体,提供播放,编解码能力,App可以通过MediaPlayer,MediaCodec系列API进行访问。
  • ServiceManager:义如其名,Services的管理者,是Binder通信的基础,为Binder通信提供对象(名字)查找的功能。

p.s. 这里谈论到的SystemServices要和两个概念相区分:

(1)Service,Android的应用层四大组件之一,它是一个后台运行的组件,执行长时间运行且不需要用户交互的任务,由应用层定义和使用。

(2)system_server,上面也提及到了,system_server包含很多系统的管理服务,但它并不和框架层级上的SystemServices划等号,只是Android中其中的一个系统服务提供者,仍然有其他的系统设备服务独立于它,与之平级。

2. MediaServer

Android中的多媒体服务的覆盖范围很广,主要包括为App提供音视频格式解析、播放显示功能、编解码、音频混音、相机拍摄和录制等。

在Android 7.0以前,整体的多媒体服务均由单一的进程MediaServer提供,该进程包含众多的权限(相机访问权限、音频访问权限、视频驱动程序访问权限、文件访问权限、网络访问权限等)。在7.0之后,MediaServer被拆分成多个进程,所需要的权限也因此被划分到各个进程。现如今MediaServer是其中主要负责多媒体资源管理,硬件和软件编解码功能的一个系统服务。

3 MediaServer的启动

在Android系统中,MediaServer进程由init进程创建。在Linux系统中,init进程是内核启动的第一个用户空间进程,它的进程号总是1。当bootloader启动后,系统会启动kernel,kernel启动完后,会在用户空间启动init进程。init创建后,会挂载文件系统,挂载对应的分区,启动selinux安全策略,最后会通过配置的.rc文件,启动一系列系统服务。

这里借用《Android MultiMedia框架完全解析 - 从开机到MediaServer的注册过程》中的一张图来说明。init进程会启动一系列的系统服务,比如Zygote,它是Android系统的第一个Java进程,也是所有的Java进程的父进程。Zygote会孵化出System_Server,上文中有提及过该进程会包含系统各方面的管理服务。init进程还会孵化出ServiceManager,该线程用于Binder通信,为它们提供Binder对象(名字)查找的功能。 MediaServer在图中属于Native Services一类,它会向ServiceManager注册自己,等待后续App的调用。

4. 具体的构建和调用过程

MediaServer的代码在Android源码的frameworks/av/media/mediaserver/目录下,可以通过谷歌的Android Code Search访问到对应的代码。我们可以重点看下Android.bp了解其构建过程。

我们截取了Android.bp文件下的一部分来说明,这里会编译一个C++的二进制文件mediaserver,其最后的产物会放在/system/bin/mediaserver,mediaserver的编译控制中有一个条件编译选项TARGET_DYNAMIC_64_32_MEDIASERVER, 但是一般也没有打开,换言之用的默认选项只设置类init_rc属性。而init_rc属性,会在编译后将会把本目录下的mediaserver.rc放入到系统的目录system/etc/init下。

swift 复制代码
// 编译产物是二进制的可执行文件mediaserver
mediaserver_cc_binary {
    name: "mediaserver",

    defaults: ["libcodec2_hal_selection"],

    // 输入的source只有同目录下的main_mediaserver.cpp
    srcs: ["main_mediaserver.cpp"],

    // mediaserver的一些更细致的任务,都单独编译进了具体的lib之中
    shared_libs: [
        "[email protected]",
        "libicu",
        "libfmq",
        "libbinder",
        "libbinder_ndk",
        "libhidlbase",
        "liblog",
        "libmediaplayerservice",
        "libresourcemanagerservice",
        "libutils",
    ],

    static_libs: [
        "libregistermsext",
    ],

    // By default mediaserver runs in 32-bit to save memory, except
    // on 64-bit-only lunch targets.
    // ****************************************************************
    // TO ENABLE 64-BIT MEDIASERVER ON MIXED 32/64-BIT DEVICES, COMMENT
    // OUT THE FOLLOWING LINE:
    // ****************************************************************
    compile_multilib: "prefer32",

    cflags: [
        "-Werror",
        "-Wall",
    ],

    vintf_fragment_modules: ["manifest_media_c2_software.xml"],

    soong_config_variables: {
        // 一个条件编译选项,但是默认情况是没有打开
        TARGET_DYNAMIC_64_32_MEDIASERVER: {
            compile_multilib: "both",
            multilib: {
                lib32: {
                    suffix: "32",
                },
                lib64: {
                    suffix: "64",
                },
            },
            required: [
                "mediaserver.zygote64_32.rc",
                "mediaserver.zygote64.rc",
            ],
            init_rc: ["mediaserver_dynamic.rc"],
            // 默认情况下,只设置了init_rc
            conditions_default: {
                init_rc: ["mediaserver.rc"],
            },
        },
    },
}

所以,我们能在Android的system/bin/下看到mediaserver,这个就是服务的可执行文件。 配套的,我们能在system/etc/init/下找到对应的配置文件mediaserver.rc

当init进程启动后,就会解析/system/etc/init/目录下的.rc配置文件。以下是init进程源码中,查找启动script的方法。

c++ 复制代码
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {          // 这里就会解析和调用/system/ect/init下的配置文件,启动MediaServer服务。
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

此后,MediaServer正常启动,我们可以通过ps在手机中查到对应的进程,可以看见其父进程的pid就是1,正是刚刚上文中提到的init进程。

5. MediaServer的主逻辑

我们简单看一下MediaServer的主要逻辑。MediaServer进程的入口在frameworks/av/media/mediaserver/main_mediaserver.cpp中。可以看见,它在main函数中主要进行

c++ 复制代码
int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);

    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());  // 这里拿到全局的serviceManager
    ALOGI("ServiceManager: %p", sm.get());
    MediaPlayerService::instantiate();                // 实例化MediaPlayerService服务
    ResourceManagerService::instantiate();            // 实例化ResourceManagerService服务
    registerExtensions();

    bool aidl = ::android::IsCodec2AidlHalSelected();
    if (!aidl) {
        ::android::hardware::configureRpcThreadpool(kCodecThreadPoolCount, false);
    } else {
        ABinderProcess_setThreadPoolMaxThreadCount(
                kCodecThreadPoolCount + kDefaultBinderThreadPoolCount);
    }
    ProcessState::self()->startThreadPool();          // 进入循环,监听Binder事件。
    IPCThreadState::self()->joinThreadPool();
}

MediaPlayerService::instantiate()方法,便是创建一个MediaPlayerService服务,并向ServiceManager进行注册。

C++ 复制代码
void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}

// MediaPlayerService是继承自BnMediaPlayerService,是具体实现了IMediaPlayerService的Binder服务端
// class MediaPlayerService : public BnMediaPlayerService

ResourceManagerSerResourceManagerService::instantiate()方法,也是类似的

C++ 复制代码
void ResourceManagerService::instantiate() {
    // ResourceManagerService继承自BnResourceManagerService,也是Binder对象
    std::shared_ptr<ResourceManagerService> service = Create();
    binder_status_t status =
                        AServiceManager_addServiceWithFlags(
                        service->asBinder().get(), getServiceName(),
                        AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED);
    if (status != STATUS_OK) {
        return;
    }
    
    // ResourceObserverService继承自BnResourceObserverService的Binder对象,与ResourceManagerService一起使用
    std::shared_ptr<ResourceObserverService> observerService =
            ResourceObserverService::instantiate();

    if (observerService != nullptr) {
        service->setObserverService(observerService);
    }
    // TODO: mediaserver main() is already starting the thread pool,
    // move this to mediaserver main() when other services in mediaserver
    // are converted to ndk-platform aidl.
    //ABinderProcess_startThreadPool();
}

我们可以看到,MediaServer的主逻辑其实就是创建了MediaPlayerServiceResourceManagerService这两个Service,其后续的工作也因此分担至两个具体的Service之中。后续我们将从App的使用,具体来分析他们的作用。

参考资料

  1. Android MultiMedia框架完全解析 - 从开机到MediaServer的注册过程
  2. Android 10.0系统启动之init进程
  3. Android Binder框架实现之Native层addService详解之请求的发送
  4. 《Android音视频开发》:何俊林
相关推荐
小狗很可爱14 分钟前
将Django连接到mysql
android·mysql·django
QING61826 分钟前
Android RecyclerView 性能优化指南
android·性能优化·app
故事与他6451 小时前
vulhub-Billu-b0x攻略
android·linux·运维·服务器·web安全·github
QING6181 小时前
一文带你吃透Android中显示Intent与隐式Intent的区别
android·kotlin·app
QING6181 小时前
Android 性能优化全面指南 —— 大纲
android·性能优化·app
_祝你今天愉快1 小时前
安卓源码学习之【系统属性与 ContentObserver】
android·源码
&有梦想的咸鱼&2 小时前
Android Fresco 框架兼容模块源码深度剖析(六)
android
半盏茶香4 小时前
启幕数据结构算法雅航新章,穿梭C++梦幻领域的探索之旅——二叉树序列构造探秘——堆的奥义与实现诗篇
android·数据结构·c++·python·算法·leetcode
QING6184 小时前
Android 应用【内存优化】指南
android·性能优化·app