2. 源码环境搭建:AOSP源码下载、MediaServer模块定位、编译与烧录、开发机与模拟器调试

好,咱们直接进入正题。上一章聊了MediaServer的整体架构,这一章就得动手了。你想想看,光看代码不动手,就像学游泳不下水,永远学不会。我个人习惯是,先把环境搭好,再慢慢啃源码。这一章我就带你走一遍完整的流程——从下载AOSP源码,到找到MediaServer模块,再到编译烧录,最后用开发机和模拟器调试。

2.1 AOSP源码下载:别被十几G吓到

很多人一听到AOSP源码,第一反应就是“太大了,我硬盘不够”。确实,完整下载下来大概要30-40GB,编译完更是直奔100GB+。但我建议你一开始别贪多,先下载主干分支,够用就行。

核心要点:MediaServer模块在AOSP中属于核心媒体服务,路径稳定,不会轻易变动。

2.1.1 环境准备

你需要一台Linux机器,Ubuntu 18.04或20.04都行。我个人推荐20.04,踩坑少一些。硬件方面,内存至少16GB,硬盘建议留出200GB空闲空间。CPU核心数越多越好,编译时间能差好几倍。

安装必要的工具包:

sudo apt-get update
sudo apt-get install git-core gnupg flex bison build-essential \
  zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
  libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev \
  lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig

小技巧:如果你用的是Ubuntu 22.04,记得把libncurses5换成libncurses5-dev,不然会报错。我曾经在这上面浪费了半天时间。

2.1.2 下载源码

AOSP源码下载主要靠repo工具。先安装repo:

mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

然后初始化并同步代码。我建议你选android-13.0.0_r41这个分支,稳定且MediaServer改动不大:

mkdir aosp-13
cd aosp-13
repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r41
repo sync -j8 -c

-j8表示8个线程同时下载,如果你的网络带宽够大,可以改成-j16。同步时间取决于网速,我一般挂一晚上,第二天早上就好了。

注意:国内网络访问Google源可能不稳定。你可以用清华镜像或中科大镜像替换manifest中的地址。具体方法是在repo init时加上-u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest

2.2 MediaServer模块定位:找到它,理解它

源码下载完后,怎么找到MediaServer?其实路径很直接。MediaServer的代码主要分布在两个地方:

模块 路径 说明
mediaserver主进程 frameworks/av/media/mediaserver/ 入口main函数,启动各种服务
MediaPlayerService frameworks/av/media/libmediaplayerservice/ 核心播放服务实现
Stagefright frameworks/av/media/libstagefright/ 解码框架,MediaServer内部调用
AudioFlinger frameworks/av/services/audioflinger/ 音频混音和输出
CameraService frameworks/av/services/camera/libcameraservice/ 相机服务

我个人习惯是先看main_mediaserver.cpp,了解进程启动流程。然后顺着服务注册的线索,一个个看各个子服务。说白了,MediaServer就是一个“服务大管家”,它负责启动和管理所有媒体相关的服务。

2.2.1 关键文件一览

frameworks/av/media/mediaserver/目录下,最重要的文件就两个:

  • main_mediaserver.cpp:入口文件,创建MediaServer进程,注册服务
  • Android.bp:构建配置文件,定义编译规则

打开main_mediaserver.cpp,你会看到类似这样的代码:

int main(int argc __unused, char **argv __unused) {
    // 初始化Binder通信
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    
    // 注册各个服务
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    
    // 启动Binder线程池
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    
    return 0;
}

嗯,这里要注意,instantiate()函数内部会调用defaultServiceManager()->addService(),把服务注册到ServiceManager中。这样其他进程就能通过ServiceManager找到MediaServer提供的服务了。

2.3 编译与烧录:从源码到真机

编译AOSP是个体力活,但也是必经之路。我第一次编译时,整整等了3个小时。后来换了台32核的机器,20分钟搞定。所以,硬件好真的能省时间。

2.3.1 编译MediaServer模块

如果你只想编译MediaServer模块,不用全编译。这样可以节省大量时间:

source build/envsetup.sh
lunch aosp_arm64-eng
make mediaserver -j16

lunch命令选择目标设备。我一般用aosp_arm64-eng,这是针对模拟器的64位工程版本。如果你有真机,比如Pixel 6,就选aosp_oriole-eng

提示:编译时加上-j参数,数值设为CPU核心数的2倍。比如8核CPU就用-j16。这样能充分利用多核性能。

编译完成后,生成的二进制文件在out/target/product/generic_arm64/system/bin/mediaserver。你可以用adb push把它推到设备上替换原有文件。但要注意,这种方式只适合调试,正式烧录还是得做完整镜像。

2.3.2 完整编译与烧录

完整编译AOSP需要执行:

make -j16

编译完成后,生成的各种镜像文件在out/target/product/<设备名>/目录下。烧录到真机的方法因设备而异。以Pixel系列为例:

adb reboot bootloader
fastboot flashall -w

-w参数会清除用户数据,第一次烧录建议加上。后续调试时可以不加,保留数据。

警告:烧录前一定要备份数据!我曾经手滑把开发机上的照片全清空了,那叫一个心疼。另外,确保你的设备已经解锁bootloader,否则无法烧录。

2.4 开发机与模拟器调试:两种方式,各有千秋

调试MediaServer,我一般用两种方式:模拟器调试和真机调试。各有优缺点,我分别说说。

2.4.1 模拟器调试

模拟器调试最大的好处是方便。你不需要真机,直接在电脑上跑。启动模拟器:

emulator -avd my_avd -show-kernel -logcat-output log.txt

然后通过adb连接:

adb shell
ps -A | grep mediaserver
logcat -s mediaserver

模拟器调试的局限在于,有些硬件功能模拟不了,比如摄像头、硬件解码器。如果你只调试软件逻辑,模拟器完全够用。

2.4.2 真机调试

真机调试更接近真实场景。我建议你准备一台专门的开发机,比如Pixel 3或Pixel 4,价格不贵,而且AOSP支持很好。

真机调试的步骤:

  1. 编译完整镜像并烧录到真机
  2. 用adb连接真机
  3. 修改代码后,单独编译mediaserver模块
  4. 用adb push替换设备上的二进制文件
  5. 重启mediaserver进程:adb shell killall mediaserver

这里有个小技巧:你可以写一个脚本,一键完成编译、推送、重启的操作:

#!/bin/bash
make mediaserver -j16
adb root
adb remount
adb push out/target/product/generic_arm64/system/bin/mediaserver /system/bin/
adb shell killall mediaserver
adb logcat -c
adb logcat -s mediaserver

核心经验:调试MediaServer时,日志是最好的朋友。我习惯在关键路径上加ALOGDALOGV,然后通过logcat观察。曾经有一次,我遇到一个诡异的崩溃问题,就是靠逐行加日志定位到的。

2.5 本章知识体系

下面这张图总结了本章的核心流程,从源码下载到调试运行,每一步都有对应的工具和方法:

MediaServer 源码环境搭建流程 1. 源码下载 repo sync -j8 -c 2. 模块定位 frameworks/av/media/mediaserver/ 3. 编译 make mediaserver -j16 烧录到真机 fastboot flashall -w 调试运行 adb shell killall mediaserver 模拟器调试 emulator -avd my_avd 真机调试 adb push + killall 核心工具链:repo → make → fastboot/adb → logcat

这张图把整个流程串起来了。你从左上角开始,一步步往下走,最后到达调试环节。我个人觉得,最关键的还是第三步——编译。编译环境配好了,后面的事情就水到渠成了。

2.6 避坑指南

最后,分享几个我踩过的坑,希望能帮你少走弯路:

  • 磁盘空间不足:编译AOSP时,out目录会膨胀得很快。我建议你用df -h随时监控磁盘使用率。如果空间不够,可以执行make clean清理中间文件。
  • Python版本问题:AOSP 13需要Python 3.8以上。如果你系统默认Python版本太低,可以用update-alternatives切换。
  • Java版本问题:AOSP 13推荐使用OpenJDK 11。我之前用JDK 8编译,结果报了一堆错。
  • 模拟器启动失败:如果模拟器起不来,检查一下KVM是否开启。执行kvm-ok命令,如果显示不支持,需要在BIOS里开启虚拟化技术。

最后一个小建议:调试MediaServer时,别急着改大段代码。先加日志,观察行为,理解流程。我曾经花了一周时间追一个bug,最后发现只是少加了一个空指针判断。嗯,从那以后,我写代码都会先检查指针。


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