介绍 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.so
vendor/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.camera
main_cameraserver.cpp
注册的,Framework camera
的核心,即CameraService
,实现所有api
对应功能。media.camera.proxy
CameraServiceProxy.java
注册的,是代理类,主要实现CameraService
向系统发回的消息。legacy/0
HIDL
注册的服务,即CameraProvider
,封装了HAL
实现的功能;在hardware/interfaces/camera/provider/2.4/default/service.cpp
中注册。
进程
Camera
相关主要涉及到三个进程的通信:
cameraserver
CameraService
服务所在进程,实现Framework
对应功能;代码所在位置:frameworks/av/camera/cameraserver/main_cameraserver.cpp
android.hardware.camera.provider@2.4-service
CameraProvider
服务所在进程,实现HAL
对应功能;代码所在位置:hardware/interfaces/camera/provider/2.4/default/service.cpp
mm-qcamera-daemon
Camera 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
全局调试输出LOG
persist.camera.hal.debug
HAL
模块输出LOG
persist.camera.mci.debug
mci
模块输出LOG
persist.camera.mmstill.logs
JPEG
拍照输出LOG
参考文档
- [高通:kba-160629230034:how_to_catch_android_log]
- [高通 linux_android_camera_overview]
- 高通camera学习笔记概述(一二三四五)
- CameraMetadata用于从APP到HAL的参数交互
- CameraMetadata分析