介绍 Camera build, system, vendor 等等其他相关知识。
编译与调试
Android 模块编译方法
Android 的编译系统最开始是 make, makefile 体系,但是从 7.0 开始切换到 Android.bp, ninja 等工具。部分源码目录中,Android.mk, Android.bp 两者都有,或者只有其中一个编译配置文件,这里简要介绍下编译区别。
Android.mk
查看LOCAL_MODULE中定义的名字,在根目录下直接执行:make *** -j32。Android.bp
由cc_**开头,比如:cc_library, cc_library_shared, cc_binary等等,同样是执行make *** -j32。
当然也可以直接到该目录下执行 mm ,但如果该模块存在外部依赖可能会导致编译失败,所以最好还是在根目录下执行 make 。
常见模块的编译
下面是 Camera 中常见模块(注意已经发布的 .hal 文件是不能直接修改的,涉及到 hal 版本升级),对应的编译方法及生成文件路径如下:
frameworks/av/services/camera/libcameraservice
编译命令:make libcameraservice -j32,生成文件路径:1
2system/lib64/libcameraservice.so
system/lib/libcameraservice.sohardware/interfaces/camera/common/1.0/default/
查看Android.bp可以看出,该模块编译后生成一个静态文件。1
2
3
4
5
6
7
8// Android.bp ,静态库
cc_library_static {
name: "android.hardware.camera.common@1.0-helper",
vendor_available: true,
defaults: ["hidl_defaults"],
srcs: [
...
}编译命令:
make android.hardware.camera.common@1.0-helper -j32,生成文件路径:1
2
3// 生成文件路径
./obj/STATIC_LIBRARIES/android.hardware.camera.common@1.0-helper_intermediates/android.hardware.camera.common@1.0-helper.a
./obj_arm/STATIC_LIBRARIES/android.hardware.camera.common@1.0-helper_intermediates/android.hardware.camera.common@1.0-helper.a所有依赖静态文件的模块,都需要重新编译并
push到手机中:1
2
3
4
5
6
7
8// 查找其他 Android.bp 文件可以看到,有多处依赖它
interfaces/camera/provider/2.4/vts/functional/Android.bp:34: "android.hardware.camera.common@1.0-helper",
interfaces/camera/provider/2.4/default/Android.bp:29: "android.hardware.camera.common@1.0-helper"
interfaces/camera/common/1.0/default/Android.bp:2: name: "android.hardware.camera.common@1.0-helper",
interfaces/camera/device/3.2/default/Android.bp:22: "android.hardware.camera.common@1.0-helper"
interfaces/camera/device/1.0/default/Android.bp:28: "android.hardware.camera.common@1.0-helper"
interfaces/camera/device/3.3/default/Android.bp:24: "android.hardware.camera.common@1.0-helper"
qcom/camera/QCamera2/Android.mk:121:LOCAL_STATIC_LIBRARIES := android.hardware.camera.common@1.0-helperhardware/interfaces/camera/provider/2.4/default
编译命令make android.hardware.camera.provider@2.4-impl -j32,生成的文件路径:1
2vendor/lib/hw/android.hardware.camera.provider@2.4-impl.so
vendor/lib64/hw/android.hardware.camera.provider@2.4-impl.sohardware/qcom/camera/QCamera2
编译命令:make camera.msm8937 -j32;,其中msm8937表示当前项目所属平台名称,生成文件路径:1
2
3
4
5
6
7
8// hardware/qcom/camera/QCamera2/Android.mk
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE := camera.$(TARGET_BOARD_PLATFORM)
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
// 生成文件地址
vendor/lib/hw/camera.msm8937.sohardware/interfaces/camera/device/1.0/default/
编译命令:make camera.device@1.0-impl -j32,生成文件路径:1
2./vendor/lib/camera.device@1.0-impl.so
./vendor/lib64/camera.device@1.0-impl.sohardware/qcom/camera/QCamera2/stack/mm-camera-interface
编译命令:make libmmcamera_interface -j32,生成文件路径:./vendor/lib/libmmcamera_interface.sovendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging目录
编译命令:make mm-qcamera-daemon -j32,生成文件路径:./vendor/bin/mm-qcamera-daemon
调试
Java文件
可以直接在Log中打印调用关系:Log.d("tag", "message", new Throwable());。cpp文件
堆栈打印需要添加libutils,如果是Android P及以上添加libutilscallstack,在cpp文件中,调用android:CallStack cs来打印堆栈。1
2#include <utils/CallStack.h>
android::CallStack cs("XMT:tag");c文件c文件打印堆栈比较麻烦,需要先封装一个cpp文件,并使用extern "C"来给c调用。
如果出现堆栈错误,根据地址查找代码行数,使用 addr2line 工具来查看:addr2line -C -f -e ./symbols/vendor/lib/libmmcamera_interface.so 00015414 。
Camera 分层设计
模块
Camera 是一个比较完整的模块,从 APP, Framework, Hardware, System, Vendor, Kernel Driver 每部分都会涉及到,主要关注的是 Framework, Hardware 两块。
APP调用Framework Java提供的API实现对应功能Framework Java作为客户端通过AIDL和Framework Native服务端完成跨进程通信Framework服务作为客户端通过HIDL和Hardware AOSP的.hal接口服务完成跨进程通信Hardware AOSP通过dlopen加载Hardware qcom库,实现对应的功能Hardware qcom通过socket(也有可能是一个mshim层直接加载库)以及Kernel V4L2 Driver和Vendor实现通信System目录中会提供一些公共的函数或数据结构,方便在各个模块中使用统一结构传递数据
分层设计中,我们只需要关心每一层之间的接口就行,而每层的逻辑处理都封装在这一层内部。
服务
Camera 相关一共会注册三个服务:
media.cameramain_cameraserver.cpp注册的,Framework camera的核心,即CameraService,实现所有api对应功能。media.camera.proxyCameraServiceProxy.java注册的,是代理类,主要实现CameraService向系统发回的消息。legacy/0HIDL注册的服务,即CameraProvider,封装了HAL实现的功能;在hardware/interfaces/camera/provider/2.4/default/service.cpp中注册。
进程
Camera 相关主要涉及到三个进程的通信:
cameraserverCameraService服务所在进程,实现Framework对应功能;代码所在位置:frameworks/av/camera/cameraserver/main_cameraserver.cppandroid.hardware.camera.provider@2.4-serviceCameraProvider服务所在进程,实现HAL对应功能;代码所在位置:hardware/interfaces/camera/provider/2.4/default/service.cppmm-qcamera-daemonCamera Vendor的进程,功能的最终实现;代码所在位置:vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server.c
Vendor Qcom
vendor qcom 中,并不区分 Camera API 1/2 ,也不区分 HAL 1/3 ;不管上面是什么接口,在 HAL qcom 中都会转换为对应的功能函数,而在 vendor qcom 只会有一套代码架构,不同的函数来响应他们。
mm-camera-daemon
mm-camera-daemon 进程源码所在目录:
1 | server-imaging/ |
mm-camera-daemon 的服务进程是开机自动运行的,主要作用是介于应用和驱动之间翻译 ioctl 的中间层(委托处理),其目的是将 VFE/ISP 具体操作策略的私有化代码封闭等等。
在驱动设计中,存在 msm-config, msm-camera 两种 MCT ,前者用于获取事件通知 V4L2 驱动信息;后者用于获取 sensor 操作 V4L2 驱动信息。一个完整的调用流程大致是: app==>v4l2(stream)==>daemon==>v4l2(control)==>app :
app到daemon主要是在v4l2(stream)中事件通知方式进行的,对于map/unmap则直接通过domain socket方式进行daemon到app是通过v4l2(control)的事件通知机制进行的v4l2(stream)是通过msm-camera获取到相关信息的,v4l2(control)是通过msm-config获取到相关信息的
mm-camera-daemon 是老版本的架构图,新版本中使用了 mct_shim_layer 来管理,取消了 IPC 通信,简化了流程。 mm-camera-daemon 老版本架构图:

mct_shim_layer 新版本架构图,通过 dlopen/dlsym 加载 vendor 库:

模块和端口
Camera 的所有功能划分为不同的模块,让模块自己来决定自己的事情(高内聚,低耦合),模块需要有统一的接口和格式。模块中有端口,通过端口把模块连接起来,又把模块挂在总线上。每一个端口的连接就是一个流,把这些流用 pipeline 来管理。每次启动一个 camera 就创建一个会话,由这个会话来管理此 Camera 的一切事物。对于每一个会话,模块是共享的,它可以是 Camera 的硬件资源也可以是其它资源(如一些软件算法等资源)。
- 端口
端口属于模块,如果这个模块只有source端口,那么它就是一个src模块;如果只有sink端口就是sink模块;如果都有就是中间模块。没有端口的模块是不能连接到流中的,但他可以完成一些其他的功能,比如接收引擎的设置,报告事件到bus等。连接到流中的端口,也就是说流事件set/get主要通过端口来处理。而来自于引擎的set/get通过模块来处理,当然端口也可以把事件交给模块来处理。 - 模块线程
每个模块可以有一个线程来处理模块的事情。一个线程对应一个队列,线程就是从队列中取出数据处理,然后应答回去。 - 总线回调
当一个模块向总线注册时,总线向其提供一个回调函数,当模块有事件发生时,调用这个函数向bus发消息,然后总线把这个消息提交给管道,管道把这个消息顺着流发下去。
核心控制代码路径如下,目录结构为:
1 | // vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller |
高通控制模块架构图:

System
System 中定义的数据结构体,贯穿整个 Framework, HAL, Vendor ,在这三层中定义统一的数据传递格式。
目录结构
System 中和 Camera 相关的代码:
1 | system/core/libsystem/include/system/camera.h |
分为几部分:
- 头文件
1
2system/core/libsystem/include/system/camera.h
system/media/private/camera/include/camera_metadata_hidden.h - 权限
system/sepolicy/ - 系统共有文件
system/media/camera
生成 Metadata 文件
Camera Metadata 相关的属性,都是自动生成的,不能直接在代码中修改。这些属性是在 system/media/docs/ 中的 metadata_properties.xml, ndk_metadata_properties.xml 两个配置文件中定义的, .mako 指定了生成规则,通过解析工具解析后生成对应的 .java, .h 文件。
1 | frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java |
camera_metadata 数据结构
camera_metadata 数据结构贯穿整个 Camera 架构,用于传递 Camera 相关参数设置。camera_metadata_tags.h 中定义了所有的枚举值,特别是 camera_metadata 数据段相关的性质:
1 | // camera_metadata_tags.h |
主要包含如上几个数据段,且每个数据段大小为 1 << 16 即 64K 大小,也是定义的枚举值:
1 | // camera_metadata_tags.h |
这里举例说明 ANDROID_FLASH 相关参数,都是从 ANDROID_FLASH_START 位置开始,并以 ANDROID_FLASH_END 结尾,在 64K 空间内包含了如下几个参数:
1 | // camera_metadata_tags.h |

而 camera_metadata 的内存结构体定义如下:
1 | // camera_metadata.c |
常见调试对应属性
persist.camera.dumpimg
设置为 1 时,每次拍照预览都会在/data/misc/camera目录记录当前帧图片。persist.camera.global.debug全局调试输出LOGpersist.camera.hal.debugHAL模块输出LOGpersist.camera.mci.debugmci模块输出LOGpersist.camera.mmstill.logsJPEG拍照输出LOG
参考文档
- [高通:kba-160629230034:how_to_catch_android_log]
- [高通 linux_android_camera_overview]
- 高通camera学习笔记概述(一二三四五)
- CameraMetadata用于从APP到HAL的参数交互
- CameraMetadata分析