Earth Guardian

You are not LATE!You are not EARLY!

0%

Camera Hardware 分析

Camera Hardware 分析,主要是分析 HAL 3 。

简述

术语表

  • HAL: Hardware abstraction layer 硬件抽象层
  • CPP: Camera Post Processor 相机后处理
  • VFE:VIDEO front-end 视频前端
  • VPE:Video preprocessing 视频预处理

类文件速查表

1
2
1. HAL AOSP: hardware/interfaces/camera
2. HAL qcom: hardware/qcom/camera

从 Android 8.0 开始,由于 Google 引入 Treble 架构,Framework 和 vendor 之间需要通过 HIDL 来通信,所以重写了 hardware 的代码结构。HAL 分为 AOSP :Google 标准代码部分;以及 HAL qcom :厂商具体实现部分。

HIDL 相关

HIDL 是复用了 Binder 机制,用于跨进程通信;所有跨进程通信相关的接口,都集中在 HIDL 这几个文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// hardware/interfaces/camera
./device/3.3/ICameraDeviceSession.hal
./device/3.3/types.hal
./device/3.2/ICameraDevice.hal
./device/3.2/ICameraDeviceSession.hal
./device/3.2/ICameraDeviceCallback.hal
./device/3.2/types.hal
./device/1.0/ICameraDevice.hal
./device/1.0/ICameraDevicePreviewCallback.hal
./device/1.0/ICameraDeviceCallback.hal
./device/1.0/types.hal
./metadata/3.2/types.hal
./common/1.0/types.hal
./provider/2.4/ICameraProvider.hal
./provider/2.4/ICameraProviderCallback.hal

文件生成规则

.hal 文件可以生成对应的 .h, .cpp 文件,编译规则通常在同级目录的 Android.bp 中定义,这里选取 ICameraProvider.hal 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Android.bp
filegroup {
name: "android.hardware.camera.provider@2.4_hal",
srcs: [
"ICameraProvider.hal",
"ICameraProviderCallback.hal",
],
}

genrule {
name: "android.hardware.camera.provider@2.4_genc++",
tools: ["hidl-gen"],
cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources
-randroid.hardware:hardware/interfaces
-randroid.hidl:system/libhidl/transport
android.hardware.camera.provider@2.4",
srcs: [
":android.hardware.camera.provider@2.4_hal",
],
out: [
"android/hardware/camera/provider/2.4/CameraProviderAll.cpp",
"android/hardware/camera/provider/2.4/CameraProviderCallbackAll.cpp",
],
}

genrule {
name: "android.hardware.camera.provider@2.4_genc++_headers",
tools: ["hidl-gen"],
cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers
-randroid.hardware:hardware/interfaces
-randroid.hidl:system/libhidl/transport
android.hardware.camera.provider@2.4",
srcs: [
":android.hardware.camera.provider@2.4_hal",
],
out: [
"android/hardware/camera/provider/2.4/ICameraProvider.h",
"android/hardware/camera/provider/2.4/IHwCameraProvider.h",
"android/hardware/camera/provider/2.4/BnHwCameraProvider.h",
"android/hardware/camera/provider/2.4/BpHwCameraProvider.h",
"android/hardware/camera/provider/2.4/BsCameraProvider.h",
"android/hardware/camera/provider/2.4/ICameraProviderCallback.h",
"android/hardware/camera/provider/2.4/IHwCameraProviderCallback.h",
"android/hardware/camera/provider/2.4/BnHwCameraProviderCallback.h",
"android/hardware/camera/provider/2.4/BpHwCameraProviderCallback.h",
"android/hardware/camera/provider/2.4/BsCameraProviderCallback.h",
],
}

源文件只有两个 ICameraProvider.hal, ICameraProviderCallback.hal ,通过 hidl-gen 工具来生成对应的 .h, .cpp 文件,生成文件的目录为 out/soong/.intermediates/hardware/interfaces/camera/provider/2.4 目录下:

  • 头文件
    目录名 android.hardware.camera.provider@2.4_genc++_headers,每个 .hal 会生成 4 个头文件,其中三个是 HIDL 通信机制对应的文件。
  • 源文件
    目录名 android.hardware.camera.provider@2.4_genc++,每个 .hal 文件生成一个对应的 *All.cpp 文件。

types.hal

这类 HIDL 文件用于定义数据类型,而其他 HIDL 文件参数中可能会使用到这些数据结构。

ICameraProvider.hal 相关

ICameraProvider.hal 中的函数为 Framework 向 HAL 层发起的请求;ICameraProviderCallback.hal 中函数为 HAL 向 Framework 返回的回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ICameraProvider.hal
interface ICameraProvider {

// 设置 ICameraProviderCallback 监听,返回值为 Status
setCallback(ICameraProviderCallback callback)
generates (Status status);

// 获取厂商自定义参数,返回值类型为 VendorTagSection
getVendorTags() generates (Status status,
vec<VendorTagSection> sections);

// 返回 Camera 设备 id 列表,比如支持几个摄像头
getCameraIdList()
generates (Status status, vec<string> cameraDeviceNames);

// 是否支持手电筒模式
isSetTorchModeSupported() generates (Status status, bool support);

// 根据设备名,返回 ICameraDevice 实例
getCameraDeviceInterface_V1_x(string cameraDeviceName) generates
(Status status,
android.hardware.camera.device@1.0::ICameraDevice device);
getCameraDeviceInterface_V3_x(string cameraDeviceName) generates
(Status status,
android.hardware.camera.device@3.2::ICameraDevice device);

};


// ICameraProviderCallback.hal
interface ICameraProviderCallback {

// 设备状态改变
cameraDeviceStatusChange(string cameraDeviceName,
CameraDeviceStatus newStatus);

// 手电筒模式状态改变
torchModeStatusChange(string cameraDeviceName,
TorchModeStatus newStatus);

};

ICameraDevice.hal 相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ICameraDevice.hal
interface ICameraDevice {

// 获取当前设备的资源耗费信息
getResourceCost() generates (Status status, CameraResourceCost resourceCost);

// 获取当前设备的静态属性
getCameraCharacteristics() generates
(Status status, CameraMetadata cameraCharacteristics);

// 设置闪光灯或手电筒模式
setTorchMode(TorchMode mode) generates (Status status);

// 打开当前设备
// 参数:ICameraDeviceCallback 回调
// 返回值:ICameraDeviceSession 会话
open(ICameraDeviceCallback callback) generates
(Status status, ICameraDeviceSession session);

// 打印当前设备状态信息
dumpState(handle fd);
};

// ICameraDeviceCallback.hal
interface ICameraDeviceCallback {

// 返回捕获到的结果
processCaptureResult(vec<CaptureResult> results);

// 异步通知
notify(vec<NotifyMsg> msgs);
};

ICameraDeviceSession.hal 相关

ICameraDeviceSession.hal 有两个版本: 3.2 和 3.3 ,而 3.3 版本是继承自 3.2 版本的,唯一不同的是使用 configureStreams_3_3 替代 configureStreams 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// V3.2 ICameraDeviceSession.hal
interface ICameraDeviceSession {

// 根据指定模板,构造默认请求模板设置
constructDefaultRequestSettings(RequestTemplate type) generates
(Status status, CameraMetadata requestTemplate);

// 配置流信息
configureStreams(StreamConfiguration requestedConfiguration)
generates (Status status,
HalStreamConfiguration halConfiguration);

// 处理捕获请求
processCaptureRequest(vec<CaptureRequest> requests,
vec<BufferCache> cachesToRemove)
generates (Status status, uint32_t numRequestProcessed);

// 获取捕获请求队列
getCaptureRequestMetadataQueue() generates (fmq_sync<uint8_t> queue);

// 获取捕获结果队列
getCaptureResultMetadataQueue() generates (fmq_sync<uint8_t> queue);

// 写入当前管道所有信息
flush() generates (Status status);

// 关闭当前设备
close();
};

// V3.3 ICameraDeviceSession.hal
interface ICameraDeviceSession extends @3.2::ICameraDeviceSession {

// 配置流信息
configureStreams_3_3(StreamConfiguration requestedConfiguration)
generates (Status status,
@3.3::HalStreamConfiguration halConfiguration);

};

HAL 1 对应文件

ICameraDevice.hal, ICameraDevicePreviewCallback.hal, ICameraDeviceCallback.hal 三个文件是 HAL 1 对应的文件,暂不做分析。

Hardware AOSP

对应文件目录为 hardware/interface/camera ,该文件夹主要是定义了很多 hal 文件及对应实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
├── Android.bp
├── common
│   ├── 1.0
│   └── README.md
├── device
│   ├── 1.0
│   ├── 3.2
│   ├── 3.3
│   └── README.md
├── metadata
│   ├── 3.2
│   └── README.md
├── provider
│   ├── 2.4
│   └── README.md
└── README.md

common 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├── 1.0
│   ├── Android.bp
│   ├── Android.mk
│   ├── default
│   │   ├── Android.bp
│   │   ├── CameraMetadata.cpp // HAL 3 CameraMetadata 元数据
│   │   ├── CameraModule.cpp // 封装了 qcom camera
│   │   ├── CameraParameters.cpp // HAL 1 参数
│   │   ├── HandleImporter.cpp // 导入请求时的 buffer 管理
│   │   ├── include
│   │   │   ├── CameraMetadata.h
│   │   │   ├── CameraModule.h
│   │   │   ├── CameraParameters.h
│   │   │   ├── HandleImporter.h
│   │   │   └── VendorTagDescriptor.h
│   │   └── VendorTagDescriptor.cpp // 厂商定制 tag
│   └── types.hal
└── README.md

CameraModule.cpp 封装了 qcom camera 库中相关函数,所有的交互都在这个文件中处理。

device 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
├── 1.0
│   ├── Android.bp
│   ├── default
│   │   ├── Android.bp
│   │   ├── CameraDevice_1_0.h
│   │   ├── CameraDevice.cpp
│   │   └── OWNERS
│   ├── ICameraDeviceCallback.hal
│   ├── ICameraDevice.hal
│   ├── ICameraDevicePreviewCallback.hal
│   └── types.hal
├── 3.2
│   ├── Android.bp
│   ├── default
│   │   ├── Android.bp
│   │   ├── CameraDevice_3_2.h
│   │   ├── CameraDevice.cpp // 设备
│   │   ├── CameraDeviceSession.cpp // 会话
│   │   ├── CameraDeviceSession.h
│   │   ├── convert.cpp // 数据格式的转换
│   │   ├── include
│   │   │   └── convert.h
│   │   └── OWNERS
│   ├── ICameraDeviceCallback.hal
│   ├── ICameraDevice.hal
│   ├── ICameraDeviceSession.hal
│   └── types.hal
├── 3.3
│   ├── Android.bp
│   ├── default
│   │   ├── Android.bp
│   │   ├── CameraDevice_3_3.h
│   │   ├── CameraDevice.cpp
│   │   ├── CameraDeviceSession.cpp
│   │   ├── CameraDeviceSession.h
│   │   ├── convert.cpp
│   │   ├── include
│   │   │   └── convert.h
│   │   └── OWNERS
│   ├── ICameraDeviceSession.hal
│   └── types.hal
└── README.md

device 目录有三个版本:1.0, 3.2, 3.3 ,分别实现对应的 HAL 版本。注意:当前代码中并没有 3.4 版本!!!
3.3 版本的类都是继承了 3.2 版本,区别仅仅是针对 CameraDeviceSession ,以下是 3.3 版本差异部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// CameraDevice.cpp
sp<V3_2::implementation::CameraDeviceSession> CameraDevice::createSession(
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) {
sp<CameraDeviceSession> session =
new CameraDeviceSession(device, deviceInfo, callback);
...
return session;
}

// CameraDeviceSession.cpp
Return<void> CameraDeviceSession::configureStreams_3_3(
const StreamConfiguration& requestedConfiguration,
ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb) {
...
}

从实际代码比对中, 3.2 和 3.3 版本的代码,并没有什么差异!查看官网版本支持 , 3.3 版本主要有以下不同:

  • OPAQUE 和 YUV 重新处理 API 更新
  • 对深度输出缓冲区的基本支持
  • 为 camera3_stream_t 添加了 data_space 缓存字段
  • 为 camera3_stream_t 添加了 rotation 旋转字段
  • 为 camera3_stream_configuration_t 添加了 camera3 流配置操作模式 operation_mode

metadata 目录

1
2
3
4
5
6
7
metadata/
├── 3.2
│   ├── Android.bp
│   ├── Android.mk
│   ├── docs.html
│   └── types.hal
└── README.md

有效文件为 types.hal ,定义了 CameraMetadata 相关的数据结构。

provider 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
provider/
├── 2.4
│   ├── Android.bp
│   ├── default
│   │   ├── Android.bp
│   │   ├── android.hardware.camera.provider@2.4-service.rc
│   │   ├── CameraProvider.cpp
│   │   ├── CameraProvider.h
│   │   ├── OWNERS
│   │   └── service.cpp
│   ├── ICameraProviderCallback.hal
│   ├── ICameraProvider.hal
│   └── vts
│   ├── functional
│   │   ├── Android.bp
│   │   └── VtsHalCameraProviderV2_4TargetTest.cpp
│   └── OWNERS
└── README.md

provider 中包含进程启动文件 service.cpp ,以及 CameraProvider.cpp 文件。

camera.provider 进程

进程全名为: android.hardware.camera.provider@2.4-service ,可以通过 adb shell "ps -A | grep camera.provider" 中查看到。
进程源码文件所在目录:hardware/interfaces/camera/provider/2.4/default ,主要是 .rc, service.cpp 两个文件。

rc 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// android.hardware.camera.provider@2.4-service.rc
// init 进程启动名字为 camera-provider-2-4 的进程及对应路径
// 不过实际解析过程中是以路径中的二进制文件名作为进程名的
// 也就是这个进程名为:android.hardware.camera.provider@2.4-service
service camera-provider-2-4 /vendor/bin/hw/android.hardware.camera.provider@2.4-service
// class 表示类别,hal 类别的进程同时启动
class hal
// 进程用户名
user cameraserver
// 进程分组
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
writepid /dev/cpuset/camera-daemon/tasks /dev/stune/foreground/tasks

main

进程的 main 方法很简单,仅有两条有效语句:

1
2
3
4
5
6
7
8
9
10
// service.cpp
int main()
{
ALOGI("Camera provider Service is starting.");
// The camera HAL may communicate to other vendor components via
// /dev/vndbinder
android::ProcessState::initWithDriver("/dev/vndbinder");
return defaultPassthroughServiceImplementation<ICameraProvider>(
"legacy/0", /*maxThreads*/ 6);
}
  • initWithDriver
    Binder 通信时驱动设备名为 /dev/vndbinder 。
  • defaultPassthroughServiceImplementation
    默认的直通式 HAL 服务注册。

这里先简单介绍一个概念:Binder 化直通式 HAL (摘选自官网)。它实际是直通模式的 HAL 实现 Binder 化。比如 HAL 接口 a.b.c.d@M.N::IFoo,系统会创建两个软件包:

  • a.b.c.d@M.N::IFoo-impl
    包含 HAL 的实现,并暴露函数 IFoo* HIDL_FETCH_IFoo(const char* name)。在旧版设备上,此软件包经过 dlopen 处理,且实现使用 HIDL_FETCH_IFoo 进行了实例化。也可以使用 hidl-gen 和 -Lc++-impl 以及 -Landroidbp-impl 来生成基础代码。
  • a.b.c.d@M.N::IFoo-service
    打开直通式 HAL,并将其自身注册为 Binder 化服务,从而使同一 HAL 实现能够同时以直通模式和 Binder 化模式使用。
    如果有一个 IFoo ,可以调用 sp<IFoo> IFoo::getService(string name, bool getStub) ,以获取对 IFoo 实例的访问权限。如果 getStub 为 True,则 getService 会尝试仅在直通模式下打开 HAL。如果 getStub 为 False,则 getService 会尝试找到 Binder 化服务;如果未找到,则它会尝试找到直通式服务。除了在 defaultPassthroughServiceImplementation 中,其余情况一律不得使用 getStub 参数。(搭载 Android O 的设备是完全 Binder 化的设备,因此不得在直通模式下打开服务。)

从 main 源码来看,defaultPassthroughServiceImplementation 注册的是 Binder 化的直通式 HAL ,从 ICameraProvider.hal 生成的头文件中,可以看到 getStub 默认为 false ,即在 Framework 中 CameraProviderManager 通过 getService 找到的是 Binder 化服务。

1
2
3
4
5
6
7
8
9
10
// android/hardware/camera/provider/2.4/ICameraProvider.h
static ::android::sp<ICameraProvider> getService(const std::string
&serviceName="default", bool getStub=false);
static ::android::sp<ICameraProvider> getService(
const ::android::hardware::hidl_string& serviceName,
bool getStub=false)
{
std::string str(serviceName.c_str());
return getService(str, getStub);
}

服务注册流程图

注册流程,查看大图

0109-android-camera-5-hal-CameraProvider_registerAsService.png

CameraProvider 初始化

从 main 方法中的 defaultPassthroughServiceImplementation 展开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// LegacySupport.h
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
size_t maxThreads = 1) {
configureRpcThreadpool(maxThreads, true);
status_t result =
registerPassthroughServiceImplementation<Interface>(name);

if (result != OK) {
return result;
}

joinRpcThreadpool();
return 0;
}

除了配置 Binder 机制中 RPC 线程池,就是注册直通式 HAL :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// LegacySupport.h
template<class Interface>
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
std::string name = "default") {
// 获取服务
sp<Interface> service = Interface::getService(name,
// getStub 为 true,表示通过 dlopen 的方式加载 HAL
true /* getStub */);
...
status_t status = service->registerAsService(name);
...
return status;
}

传入模板的类为 ICameraProvider ,我们来看获取服务的代码,CameraProviderAll.cpp 文件是根据 ICameraProvider.hal 自动生成的 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// CameraProviderAll.cpp
const char* ICameraProvider::descriptor(
"android.hardware.camera.provider@2.4::ICameraProvider");

::android::sp<ICameraProvider> ICameraProvider::getService(
const std::string &serviceName, const bool getStub) {
using ::android::hardware::defaultServiceManager;
using ::android::hardware::details::waitForHwService;
using ::android::hardware::getPassthroughServiceManager;
using ::android::hardware::Return;
using ::android::sp;
using Transport =
::android::hidl::manager::V1_0::IServiceManager::Transport;

...
const sp<::android::hidl::manager::V1_0::IServiceManager> sm
= defaultServiceManager();
...
// Binder HAL
for (int tries = 0; !getStub && (vintfHwbinder
|| (vintfLegacy && tries == 0)); tries++) {
...
if (vintfHwbinder && tries > 0) {
waitForHwService(ICameraProvider::descriptor, serviceName);
}
Return<sp<::android::hidl::base::V1_0::IBase>> ret =
sm->get(ICameraProvider::descriptor, serviceName);
...
sp<::android::hidl::base::V1_0::IBase> base = ret;
...
Return<sp<ICameraProvider>> castRet = ICameraProvider::castFrom(
base, true /* emitError */);
...
iface = castRet;
...
return iface;
}

// 直通式 HAL
if (getStub || vintfPassthru || vintfLegacy) {
const sp<::android::hidl::manager::V1_0::IServiceManager> pm
= getPassthroughServiceManager();
if (pm != nullptr) {
Return<sp<::android::hidl::base::V1_0::IBase>> ret =
pm->get(ICameraProvider::descriptor, serviceName);
if (ret.isOk()) {
sp<::android::hidl::base::V1_0::IBase> baseInterface=ret;
if (baseInterface != nullptr) {
iface = ICameraProvider::castFrom(baseInterface);
if (!getStub || trebleTestingOverride) {
iface = new BsCameraProvider(iface);
}
}
}
}
}
return iface;
}

getStub 的值决定 getService 是 Binder 式或者直通式来获取 HAL ,主要区别在于获取 IServiceManager 方式及对应实例会不一样:

  • Binder
    通过 defaultServiceManager 获取,实际实例为 ServiceManager 。
  • 直通式
    通过 getPassthroughServiceManager 获取,实际实例为 ServiceManagement 。

getStub 为 false ,所以采用直通式,get 方法获取服务实例分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// ServiceManagement.cpp
sp<IServiceManager1_0> getPassthroughServiceManager() {
return getPassthroughServiceManager1_1();
}
sp<IServiceManager1_1> getPassthroughServiceManager1_1() {
static sp<PassthroughServiceManager> manager(
new PassthroughServiceManager());
return manager;
}

struct PassthroughServiceManager : IServiceManager1_1 {
static void openLibs(const std::string& fqName,
std::function<bool /* continue */(void* /* handle */,
const std::string& /* lib */,
const std::string& /* sym */)> eachLib) {
//fqName looks like android.hardware.foo@1.0::IFoo
...
std::string packageAndVersion = fqName.substr(0, idx);
std::string ifaceName = fqName.substr(idx + strlen("::"));

const std::string prefix = packageAndVersion + "-impl";
// 获取函数名,以 HIDL_FETCH_ 开头
const std::string sym = "HIDL_FETCH_" + ifaceName;

const int dlMode = RTLD_LAZY;
void *handle = nullptr;

dlerror(); // clear

std::vector<std::string> paths =
{HAL_LIBRARY_PATH_ODM, HAL_LIBRARY_PATH_VENDOR,
HAL_LIBRARY_PATH_VNDK_SP, HAL_LIBRARY_PATH_SYSTEM};
...
for (const std::string& path : paths) {
std::vector<std::string> libs = search(path, prefix, ".so");

for (const std::string &lib : libs) {
const std::string fullPath = path + lib;

// dlopen 加载 HAL 库
if (path != HAL_LIBRARY_PATH_SYSTEM) {
handle = android_load_sphal_library(fullPath.c_str(),
dlMode);
} else {
handle = dlopen(fullPath.c_str(), dlMode);
}
...
// 调用函数指针
if (!eachLib(handle, lib, sym)) {
return;
}
}
}
}

Return<sp<IBase>> get(const hidl_string& fqName,
const hidl_string& name) override {
sp<IBase> ret = nullptr;
// dlopen 打开库文件
openLibs(fqName, [&](void* handle, const std::string &lib,
const std::string &sym) {
IBase* (*generator)(const char* name);
// 获取 HIDL_FETCH_ICameraProvider 函数
*(void **)(&generator) = dlsym(handle, sym.c_str());
...
// 调用 HIDL_FETCH_ICameraProvider 函数
ret = (*generator)(name.c_str());
...
return false;
});

return ret;
}
...
}

IServiceManager 直通式对应的实例为 PassthroughServiceManager ,它的 get 方法会先通过 openLibs 打开 HAL 库文件,并从库文件中 dlsym 调用以 HIDL_FETCH_ 开头的函数,获取对应实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ICameraProvider.h
extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name);

// ICameraProvider.cpp
ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
if (strcmp(name, kLegacyProviderName) != 0) {
return nullptr;
}
CameraProvider* provider = new CameraProvider();
if (provider == nullptr) {
ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
return nullptr;
}
if (provider->isInitFailed()) {
ALOGE("%s: camera provider init failed!", __FUNCTION__);
delete provider;
return nullptr;
}
return provider;
}

HIDL_FETCH_ICameraProvider 主要任务是新建 ICameraProvider 对象。
整个流程下来,从 service.cpp 开启进程,注册 Binder 化直通式 HAL 时,通过 dlopen 方式加载 HAL 库,并通过 HIDL_FETCH_ICameraProvider 来新建 ICameraProvider 对象。

Hardware AOSP 和 Hardware qcom 关联过程

hardware/interfaces/camera 是 Android 系统的标准接口,即 Hardware AOSP ;但是每个芯片厂商的具体实现都会不一样,高通平台在 hardware/qcom/camera 中实现了对 vendor camera 代码中的封装,即 Hardware qcom。
hardware/qcom/camera 代码生成对应库文件为 camera.msm8937.so ,hardware/interfaces/camera 是通过 dlopen 来加载这个库的,具体由 CameraProvider.cpp 加载,我们分析加载及函数指针关联的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Camera_common.h
#define CAMERA_HARDWARE_MODULE_ID "camera"

// CameraProvider.cpp
CameraProvider::CameraProvider() :
camera_module_callbacks_t({sCameraDeviceStatusChange,
sTorchModeStatusChange}) {
mInitFailed = initialize();
}

bool CameraProvider::initialize() {
camera_module_t *rawModule;
int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
(const hw_module_t **)&rawModule);
...
}

CameraProvider::initialize 初始化过程中,通过 hw_get_module 来加载库,并拿到 camera_module_t 结构体对应的函数指针;CAMERA_HARDWARE_MODULE_ID 是在 Camera_common.h 中定义的 camera:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// hardware.c
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module);
}

int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i = 0;
char prop[PATH_MAX] = {0};
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};


if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);

// 先通过 ro.hardware.camera 查找库文件是否存在
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}

// 再根据其他可能存在的变种查找
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}

// 如果都找不到,再查找 camera.default.so
if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
goto found;
}

return -ENOENT;

found:
// 找到了就加载
return load(class_id, path, module);
}

整个代码流程,是逐步查找库文件的过程,如果找到了再加载,这里重点看下几个可能存在的变种命名方式,规则是 <MODULE_ID>.variant.so :

1
2
3
4
5
6
7
8
9
10
11
12
// hardware.c

static const char *variant_keys[] = {
"ro.hardware", /* This goes first so that it can pick up a different
file on the emulator. */
"ro.product.board",
"ro.board.platform",
"ro.arch"
};

static const int HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));

当前平台,camera 库文件变种可能为:caemra.qcom.so, camera.QC_Reference_Phone.so, camera.msm8937.so ,实际上只有 camera.8937.so 是存在的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// hardware.c
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif

static int hw_module_exists(char *path, size_t path_len,const char *name,
const char *subname)
{
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH3, name, subname);
if (access(path, R_OK) == 0)
return 0;

snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;

snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;

return -ENOENT;
}

根据平台是否为 64 位,分别从 /system, /vendor/, /odm 几个目录下去搜索,直到找到为止,当前平台库文件绝对路径为:/vendor/lib/hw/camera.msm8937.so ,找到后加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// hardware.h
#define HAL_MODULE_INFO_SYM HMI
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"

// hardware.c
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status = -EINVAL;
void *handle = NULL;
struct hw_module_t *hmi = NULL;

if (strncmp(path, "/system/", 8) == 0) {
handle = dlopen(path, RTLD_NOW);
} else {
handle = android_load_sphal_library(path, RTLD_NOW);
}
...

/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
...
*pHmi = hmi;
return status;
}

通过 dlopen 的方式加载库文件 /vendor/lib/hw/camera.msm8937.so ,并找到 HAL_MODULE_INFO_SYM 结构体变量,它是一组函数指针的关联,返回的是 hw_module_t 结构体指针,我们看头文件的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// hardware.h
/**
* Every hardware module must have a data structure
* named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct hw_module_t {
uint32_t tag;
uint16_t module_api_version;

#define version_major module_api_version
uint16_t hal_api_version;
#define version_minor hal_api_version

const char *id;
const char *name;
const char *author;
struct hw_module_methods_t* methods;
void* dso;

#ifdef __LP64__
uint64_t reserved[32-7];
#else
uint32_t reserved[32-7];
#endif

} hw_module_t;

typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);

} hw_module_methods_t;

从注释中可以看到,hardware 中任何模块,必须有一个命名为 HAL_MODULE_INFO_SYM 数据结构(dlsym 查找时固定命名),并且结构体的第一个数据结构必须是 hw_module_t 的。
在 CameraProvider::initialize 中 camera_module_t, hw_module_t 这两个结构体直接转换了,我们来看 camera_module_t 的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Camera_common.h
typedef struct camera_module {
hw_module_t common;

int (*get_number_of_cameras)(void);
int (*get_camera_info)(int camera_id, struct camera_info *info);
int (*set_callbacks)(const camera_module_callbacks_t *callbacks);
void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
int (*open_legacy)(const struct hw_module_t* module, const char* id,
uint32_t halVersion, struct hw_device_t** device);
int (*set_torch_mode)(const char* camera_id, bool enabled);
int (*init)();

/* reserved for future use */
void* reserved[5];
} camera_module_t;

camera_module_t 结构体的定义完全符合 hardware.h 中对 hw_module_t 的注释,因为是第一个数据结构,所以可以直接转换。
我们到 camera.msm8937.so 对应代码模块 hardware/qcom/camera 中去查找定义了 HAL_MODULE_INFO_SYM 的文件 QCamera2Hal.cpp :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// QCamera2Hal.cpp
static hw_module_t camera_common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "QCamera Module",
.author = "Qualcomm Innovation Center Inc",
.methods = &qcamera::QCamera2Factory::mModuleMethods,
.dso = NULL,
.reserved = {0}
};

camera_module_t HAL_MODULE_INFO_SYM = {
.common = camera_common,
.get_number_of_cameras = qcamera::QCamera2Factory::get_number_of_cameras,
.get_camera_info = qcamera::QCamera2Factory::get_camera_info,
.set_callbacks = qcamera::QCamera2Factory::set_callbacks,
.get_vendor_tag_ops = qcamera::QCamera3VendorTags::get_vendor_tag_ops,
.open_legacy = qcamera::QCamera2Factory::open_legacy,
.set_torch_mode = qcamera::QCamera2Factory::set_torch_mode,
.init = NULL,
.reserved = {0}
};

至此:CameraProvider::initialize 中,通过 hw_get_module 加载了 camera.msm8937.so 库,并拿到了 HAL_MODULE_INFO_SYM 对应的数据结构指针,最终会保存到 CameraModule 中。

Hardware qcom 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
camera/
├── Android.mk
├── CleanSpec.mk
├── common.mk
├── mm-image-codec
│   ├── Android.mk
│   ├── qexif
│   └── qomx_core
├── MODULE_LICENSE_BSD
├── QCamera2
│   ├── Android.mk
│   ├── HAL
│   ├── HAL3
│   ├── QCamera2Factory.cpp
│   ├── QCamera2Factory.h
│   ├── QCamera2Hal.cpp
│   ├── QCameraFormat.h
│   ├── stack
│   └── util
├── QCamera_Intf.h
├── QCameraParameters.h
└── usbcamcore
├── inc
└── src

QCamera_Intf.h 定义了很多数据结构(接口层); QCameraParameters.h 是 HAL 1 中使用的参数; mm-image-codec 目录涉及到高通平台图片的软硬解码; usbcamcore 是 usb 摄像头相关代码; 而 QCamera2 目录用来封装高通平台私有代码,也是需要重点熟悉的目录。

QCamera2 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
QCamera2
├── Android.mk
├── HAL
│   ├── android
│   ├── CameraParameters.cpp
│   ├── CameraParameters.h
│   ├── DCSBokehProcessor.cpp
│   ├── DCSBokehProcessor.h
│   ├── DCSSuperNightProcessor.cpp
│   ├── DCSSuperNightProcessor.h
│   ├── QCamera2HWICallbacks.cpp
│   ├── QCamera2HWI.cpp
│   ├── QCamera2HWI.h
│   ├── QCameraAllocator.h
│   ├── QCameraChannel.cpp
│   ├── QCameraChannel.h
│   ├── QCameraMem.cpp
│   ├── QCameraMem.h
│   ├── QCameraMuxer.cpp
│   ├── QCameraMuxer.h
│   ├── QCameraParameters.cpp
│   ├── QCameraParameters.h
│   ├── QCameraParametersIntf.cpp
│   ├── QCameraParametersIntf.h
│   ├── QCameraPostProc.cpp
│   ├── QCameraPostProc.h
│   ├── QCameraStateMachine.cpp
│   ├── QCameraStateMachine.h
│   ├── QCameraStream.cpp
│   ├── QCameraStream.h
│   ├── QCameraThermalAdapter.cpp
│   ├── QCameraThermalAdapter.h
│   ├── test
│   ├── tsMakeuplib
│   ├── westalgo
│   └── wrapper
├── HAL3
│   ├── android
│   ├── QCamera3Channel.cpp
│   ├── QCamera3Channel.h
│   ├── QCamera3CropRegionMapper.cpp
│   ├── QCamera3CropRegionMapper.h
│   ├── QCamera3HALHeader.h
│   ├── QCamera3HWI.cpp
│   ├── QCamera3HWI.h
│   ├── QCamera3Mem.cpp
│   ├── QCamera3Mem.h
│   ├── QCamera3PostProc.cpp
│   ├── QCamera3PostProc.h
│   ├── QCamera3Stream.cpp
│   ├── QCamera3Stream.h
│   ├── QCamera3StreamMem.cpp
│   ├── QCamera3StreamMem.h
│   ├── QCamera3VendorTags.cpp
│   ├── QCamera3VendorTags.h
│   └── test
├── QCamera2Factory.cpp
├── QCamera2Factory.h
├── QCamera2Hal.cpp
├── QCameraFormat.h
├── stack
│   ├── Android.mk
│   ├── common
│   ├── mm-camera-interface
│   ├── mm-camera-test
│   ├── mm-jpeg-interface
│   └── mm-lib2d-interface
└── util
├── QCameraBufferMaps.cpp
├── QCameraBufferMaps.h
├── QCameraCmdThread.cpp
├── QCameraCmdThread.h
├── QCameraCommon.cpp
├── QCameraCommon.h
├── QCameraDisplay.cpp
├── QCameraDisplay.h
├── QCameraFlash.cpp
├── QCameraFlash.h
├── QCameraPerf.cpp
├── QCameraPerf.h
├── QCameraQueue.cpp
├── QCameraQueue.h
└── QCameraTrace.h

QCamera2 目录中,QCamera2Hal.cpp 函数指针和结构体赋值,用于关联 hardware AOSP 和 hardware qcom ; QCamera2Factory.h/cpp 工厂类,封装了 HAL 1/3 作为统一入口;子目录有如下几个:

  • HAL
    HAL 1 对应代码目录,其中 QCamera2HWI.cpp 为统一的接口文件。
  • HAL3
    HAL 3 对应代码目录,其中 QCamera3HWI.cpp 为统一的接口文件;QCamera3Channel 信道处理对应的流 QCamera3Stream 。
  • stack
    common 目录仅仅定义了头文件,其他三个 interface 目录分别为:mm-camera-interface 用于和 vendor/qcom/mm-camera 目录通信的接口目录;mm-jpeg-interface 软硬解码的接口目录;mm-lib2d-interface 没有参与编译。
  • util
    工具目录。

stack 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
stack/
├── Android.mk
├── common
│   ├── cam_intf.h
│   ├── cam_list.h
│   ├── cam_queue.h
│   ├── cam_semaphore.h
│   ├── cam_types.h
│   ├── mm_camera_interface.h
│   ├── mm_camera_shim.h
│   └── mm_jpeg_interface.h
├── mm-camera-interface
│   ├── Android.mk
│   ├── inc
│   │   ├── mm_camera_dbg.h
│   │   ├── mm_camera.h
│   │   └── mm_camera_sock.h
│   └── src
│   ├── mm_camera.c // 总控制
│   ├── mm_camera_channel.c // channel 处理
│   ├── mm_camera_interface.c // 对 HAL 的统一接口
│   ├── mm_camera_sock.c // 和 vendor 是通过 socket 通信
│   ├── mm_camera_stream.c // stream 处理
│   └── mm_camera_thread.c // 多线程
├── mm-camera-test
│   ├── ...
├── mm-jpeg-interface
│   ├── Android.mk
│   ├── inc
│   │   ├── mm_jpeg_dbg.h
│   │   ├── mm_jpeg.h
│   │   ├── mm_jpeg_inlines.h
│   │   ├── mm_jpeg_ionbuf.h
│   │   └── mm_jpeg_mpo.h
│   ├── src
│   │   ├── mm_jpeg.c
│   │   ├── mm_jpegdec.c
│   │   ├── mm_jpegdec_interface.c
│   │   ├── mm_jpeg_exif.c
│   │   ├── mm_jpeg_interface.c
│   │   ├── mm_jpeg_ionbuf.c
│   │   ├── mm_jpeg_mpo_composer.c
│   │   └── mm_jpeg_queue.c
│   └── test
│   ├── ...
└── mm-lib2d-interface
├── ...

这个目录的核心代码主要是 mm-camera-interface 目录下的代码:

  • 接口层 mm_camera_interface.c
    不管 HAL 1 还是 HAL 3 ,高通平台 vendor 下的代码都是同一套,是没有区别的, mm_camera_interface.c 是对 HAL 统一的接口。
  • 实现层 mm_camera.c
    管理 channel, stream, thread, socket 等,实现和 vendor 的通信。当前高通平台 msm8937 中 HAL 和 vendor 的通信有两部分:一是通过 socket 来映射内存信息;二是 ioctl 向 v4l2 驱动发送命令做信息交互(查看高通网站上的文档,后续会取消 socket 通信,统一使用 mshim 管理;在 mm_camera 中,这两套方式都实现了:如果定义了 DAEMON_PRESENT 则 socket 通信,否则 mshim 方式管理)。

接口层的几个基本概念

  • Channel
    信道,一个松散的概念,用于将多个图像流捆绑在一起处理;即一个信道包含多个流(这句话更准确的说法是:一个信道 ID 对应多个信道实例 QCamera3MetadataChannel, QCamera3PicChannel 等,而每个信道实例仅对应一个流 QCamera3Stream ;也就是说实际上是一个信道 ID 包含多个流)。常见信道:ZSL, Capture, Preview, Snapshot, Video, Raw, Metadata 。
  • Stream
    流,最小流元素,每个流只能有一种格式;它是在相机硬件和应用程序之间交换捕获图像缓冲区的接口。常见流有:Preview, Postview, Metadata, Raw, Snapshot, Video, Reprocessing 。
  • Stream bundling
    流集合,在信道内捆绑的多个流:在打开所有捆绑流之前,硬件不会启动流;当第一个捆绑流关闭时,硬件停止。在捕获请求开始时,会查询信道 ID 中所有的流,并捆绑设置到每个信道实例中。

Socket 通信的意义

Socket 在这里主要功能是在 Hardware qcom 和 vendor qcom 两个进程间共享缓存信息。交换的缓存信息有如下几种类型:

  • CAM_MAPPING_BUF_TYPE_CAPABILITY :缓存区大小
  • CAM_MAPPING_BUF_TYPE_PARM_BUF :参数缓存区
  • CAM_MAPPING_BUF_TYPE_STREAM_BUF :流缓存区
  • CAM_MAPPING_BUF_TYPE_STREAM_INFO :流信息缓存区
  • CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF :离线重新处理输入的缓存区

Socket 相关代码位置及对应功能:

  • 实现类:QCamera2\stack\mm-camera-interface\src\mm_camera_sock.c
  • 消息发送者:QCamera2\stack\mm-camera-interface\src\mm_camera_stream.c
  • 消息接收者:mm-camera\mm-camera2\server-imaging\server.c

ioctl 支持的命令

高通 Camera HAL 和 kernel 通信是通过 V4L2 IOCTLs 命令来和 /dev/videoX 节点通信的,支持的命令列表如下:

  • VIDIOC_S_FMT :设置预览和拍照的格式为 YUV420
  • VIDIOC_S_CTRL :向设备传输控制信息
  • VIDIOC_G_CTRL :从设备获取控制信息
  • VIDIOC_QBUF :入队或分配预览缓存区 Buffers
  • VIDIOC_DQBUF :出队或释放预览缓存区 Buffers
  • VIDIOC_STREAMON 开始预览
  • VIDIOC_STREAMOFF :停止预览
  • VIDIOC_QUERYCAP :查询设备支持的能力
  • VIDIOC_QUERYBUF :查询缓存区 Buffers 的信息
  • VIDIOC_REQBUFS :请求注册缓存区 Buffers
  • VIDIOC_DQEVENT :注册到 kernel 的事件出队

HAL 1/3 差异

API 1/2 设计的目的

  • Camera API 1 子系统被设计为具备高级控制功能的黑盒

    • 应用程序能够发出请求,但无法控制图像缓冲区和元数据
    • 应用程序无法在帧层面控制传感器特性
    • 应用程序无法通过访问和修改元数据信息(如 3A 信息)对捕获的帧应用任何增强功能
  • Camera API 2 旨在大幅提高应用程序控制摄像头子系统的能力

    • 应用程序框架向摄像头子系统请求一帧图像,摄像头子系统将请求结果与相关元数据信息(如色彩空间)一起返回到输出流;同时为每个结果数据流生成镜头阴影信息
    • 应用程序对整个摄像头管道的控制能力得到提升。每个拍摄请求会产生一个带有拍摄元数据的结果对象和一些图像数据缓冲区
    • 元数据信息有助于应用程序了解摄像头管道的当前状态( 3A 和 ISP 状态)以对缓冲区进行相关处理

通常情况下搭配原则:API 1 + HAL 1 和 API 2 + HAL 3 。

HAL 1/3 对应功能概述

  • HAL 1 被设计为具有高级控件和以下三种运行模式的黑盒子:预览、视频录制、静态拍摄。三种模式具有略有不同又相互重叠的功能,这样就难以实现介于其中两种运行模式之间的新功能,例如连拍模式等。
    0109-android-camera-5-hal1_simple_model.png

  • HAL 3 子系统将多个运行模式整合为一个统一的视图,可以使用这种视图实现之前的任何模式以及一些其他模式,例如连拍模式。相机子系统被塑造为一个管道,该管道可按照 1:1 的基准将传入的帧捕获请求转化为帧。这些请求会封装有关帧的捕获和处理的所有配置信息:分辨率、像素格式、手动传感器、镜头、闪光灯控件、3A 运行模式、RAW->YUV 处理控件/统计信息生成等等。这样一来,便可以提高用户对聚焦、曝光以及更多后期处理(例如降噪、对比度和锐化)效果的控制能力。简单来说,应用框架从相机子系统请求帧,然后相机子系统将结果返回到输出流。此外系统还会针对每组结果生成包含色彩空间和镜头阴影等信息的元数据。
    0109-android-camera-5-hal3_simple_model.png

可以将 HAL 3 看作 HAL 1 的单向流管道,它会将每个捕获请求转化为传感器捕获的一张图像,这张图像将被处理成:

  • 包含有关捕获的元数据的结果对象
  • 图像数据的 1 到 N 个缓冲区,每个缓冲区会进入自己的目的地 Surface

可能的输出 Surface 组经过预配置:

  • 每个 Surface 都是一个固定分辨率的图像缓冲区流的目标位置
  • 一次只能将少量 Surface 配置为输出(约 3 个)

一个请求中包含所需的全部捕获设置,以及要针对该请求将图像缓冲区(从总配置组)推送到其中的输出 Surface 的列表。请求可以只发生一次 capture() ,也可以无限重复 setRepeatingRequest() ,捕获的优先级高于重复请求的优先级。

HAL 3 原理总结

  • 异步拍摄请求来源于框架
  • 硬件抽象层 HAL 按顺序处理这些请求。收到请求后,HAL 产生输出结果元数据以及一个或多个输出图像缓冲区
  • 该进程遵循先进先出 FIFO 原则处理请求、结果以及后续请求参考的流
  • 给定请求的所有输出的时间戳必须相同。如有需要,框架会一同匹配这些时间戳
  • 所有拍摄配置和状态均属于拍摄请求和结果的一部分
  • 应用程序可以请求带有特定设置的帧,例如,曝光设置和 HAL3( FULL 模式),从而确保拍摄请求设置和产生的实际图像保持精确同步

0109-android-camera-5-hal3-overview.png

接口区别

HAL 1 VS HAL 3,查看原图

0109-android-camera-5-hal-1-vs-hal-3.png

HAL, DEVICE, MODULE 各版本

这些版本号都是在 Camera_common.h 文件中定义的。

Module 版本

Module 没有兼容性问题,总是使用最新版本;当前为 CAMERA_MODULE_API_VERSION_2_4 。

1
2
3
4
5
6
7
8
#define CAMERA_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0)
#define CAMERA_MODULE_API_VERSION_2_0 HARDWARE_MODULE_API_VERSION(2, 0)
#define CAMERA_MODULE_API_VERSION_2_1 HARDWARE_MODULE_API_VERSION(2, 1)
#define CAMERA_MODULE_API_VERSION_2_2 HARDWARE_MODULE_API_VERSION(2, 2)
#define CAMERA_MODULE_API_VERSION_2_3 HARDWARE_MODULE_API_VERSION(2, 3)
#define CAMERA_MODULE_API_VERSION_2_4 HARDWARE_MODULE_API_VERSION(2, 4)

#define CAMERA_MODULE_API_VERSION_CURRENT CAMERA_MODULE_API_VERSION_2_4

HAL 版本

HAL 版本在代码中实际指的是 Device 的版本。因为要兼容早期的 Camera API ,通常必须支持 1.0 版本;2.x 版本已经废弃不再支持;而 3.0 及以上版本是为 Camera API 2 使用的。

1
2
3
4
5
6
7
8
9
10
11
12
#define CAMERA_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0) // DEPRECATED
#define CAMERA_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0) // NO LONGER SUPPORTED
#define CAMERA_DEVICE_API_VERSION_2_1 HARDWARE_DEVICE_API_VERSION(2, 1) // NO LONGER SUPPORTED
#define CAMERA_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0) // NO LONGER SUPPORTED
#define CAMERA_DEVICE_API_VERSION_3_1 HARDWARE_DEVICE_API_VERSION(3, 1) // NO LONGER SUPPORTED
#define CAMERA_DEVICE_API_VERSION_3_2 HARDWARE_DEVICE_API_VERSION(3, 2)
#define CAMERA_DEVICE_API_VERSION_3_3 HARDWARE_DEVICE_API_VERSION(3, 3)
#define CAMERA_DEVICE_API_VERSION_3_4 HARDWARE_DEVICE_API_VERSION(3, 4)

// Device version 3.4 is current, older HAL camera device versions are not
// recommended for new devices.
#define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_3_4

每个 DEVICE_API_VERSION 对应的值为:

1
2
3
4
5
6
7
CAMERA_DEVICE_API_VERSION_1_0 = 256
CAMERA_DEVICE_API_VERSION_2_0 = 512
CAMERA_DEVICE_API_VERSION_3_0 = 768
CAMERA_DEVICE_API_VERSION_3_1 = 769
CAMERA_DEVICE_API_VERSION_3_2 = 770
CAMERA_DEVICE_API_VERSION_3_3 = 771
CAMERA_DEVICE_API_VERSION_3_4 = 772

我们通常所说的 HAL 1 指的是 CAMERA_DEVICE_API_VERSION_1_0 版本;HAL 3 泛指 CAMERA_DEVICE_API_VERSION_3_0 及以上版本。

HIDL 中生成的 deviceName

返回值格式:'device@<major>.<minor>/<type>/<id>' ,比如:

  • device@1.0/legacy/1 :表示 HAL 版本号为 CAMERA_DEVICE_API_VERSION_1_0 , id=1 表示摄像头为前置摄像头
  • device@3.3/legacy/0 :表示 HAL 版本号为 CAMERA_DEVICE_API_VERSION_3_3 ,id=0 表示摄像头为后置摄像头

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// CameraProvider.cpp
std::string CameraProvider::getHidlDeviceName(
std::string cameraId, int deviceVersion) {
if (deviceVersion != CAMERA_DEVICE_API_VERSION_1_0 &&
deviceVersion != CAMERA_DEVICE_API_VERSION_3_2 &&
deviceVersion != CAMERA_DEVICE_API_VERSION_3_3 &&
deviceVersion != CAMERA_DEVICE_API_VERSION_3_4 ) {
return hidl_string("");
}
bool isV1 = deviceVersion == CAMERA_DEVICE_API_VERSION_1_0;
int versionMajor = isV1 ? 1 : 3;
int versionMinor = isV1 ? 0 : mPreferredHal3MinorVersion;
char deviceName[kMaxCameraDeviceNameLen];
snprintf(deviceName, sizeof(deviceName), "device@%d.%d/legacy/%s",
versionMajor, versionMinor, cameraId.c_str());
return deviceName;
}

// CameraProvider 在初始化时,mPreferredHal3MinorVersion 赋值为 3
bool CameraProvider::initialize() {
...
mPreferredHal3MinorVersion =
property_get_int32("ro.camera.wrapper.hal3TrebleMinorVersion",3);
switch(mPreferredHal3MinorVersion) {
case 2:
case 3:
// OK
break;
default:
ALOGW(...);
mPreferredHal3MinorVersion = 3;
}
...
}

从代码中可以看出,通过 CameraProvider::getHidlDeviceName 对外提供的 HAL 版本只有两种:CAMERA_DEVICE_API_VERSION_1_0 和 CAMERA_DEVICE_API_VERSION_3_3 。

平台当前使用哪个 HAL 版本

平台当前使用哪个 HAL 版本,是在 QCamera2Factory 的构造函数中决定的,当 isHAL3Enabled 并且 is_yuv_sensor 当前摄像头不是 yuv sensor 时,才会设置为 HAL 3 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
QCamera2Factory::QCamera2Factory()
{
...
// 1. 是否启用 HAL 3
property_get("persist.camera.HAL3.enabled", prop, "0");
int isHAL3Enabled = atoi(prop);
#ifndef QCAMERA_HAL1_SUPPORT
isHAL3Enabled = 1;
#endif

...
if((mNumOfCameras > 0) &&(mNumOfCameras <= MM_CAMERA_MAX_NUM_SENSORS)){
mHalDescriptors = new hal_desc[mNumOfCameras];
if ( NULL != mHalDescriptors) {
uint32_t cameraId = 0;

for (int i = 0; i < mNumOfCameras ; i++, cameraId++) {
mHalDescriptors[i].cameraId = cameraId;
// Set Device version to 3.x when both HAL3 is enabled
// & its BAYER sensor
// 2. 是否为 yuv sensor
if (isHAL3Enabled && !(is_yuv_sensor(cameraId))) {
mHalDescriptors[i].device_version =
CAMERA_DEVICE_API_VERSION_3_0;
} else {
mHalDescriptors[i].device_version =
CAMERA_DEVICE_API_VERSION_1_0;
}
}
} else {
LOGE("Not enough resources to allocate HAL descriptor table!");
}
}
}

高通平台开启 HAL 3 的情况下,为了兼容性同时也会支持 HAL 1 。

高通代码中混乱的 HAL 版本号

高通代码中 HAL/DEVICE 的具体版本号,不同的函数返回的版本号不一样,有点混乱:

  • QCamera2Factory 中
    mHalDescriptors[camera_id].device_version 保存的版本号,在构造函数中初始化为 CAMERA_DEVICE_API_VERSION_3_0 (768) ;mHalDescriptors 并不对外提供信息,仅在 QCamera2Factory::cameraDeviceOpen 时会用来区分 HAL1/3 。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    int QCamera2Factory::cameraDeviceOpen(int camera_id,
    struct hw_device_t **hw_device)
    {
    ...
    if ( mHalDescriptors[camera_id].device_version
    == CAMERA_DEVICE_API_VERSION_3_0 ) {
    // HAL 3 使用 QCamera3HardwareInterface
    QCamera3HardwareInterface *hw = new QCamera3HardwareInterface(
    mHalDescriptors[camera_id].cameraId, mCallbacks);
    if (!hw) {
    LOGE("Allocation of hardware interface failed");
    return NO_MEMORY;
    }
    rc = hw->openCamera(hw_device);
    if (rc != 0) {
    delete hw;
    }
    }
    #ifdef QCAMERA_HAL1_SUPPORT
    else if (mHalDescriptors[camera_id].device_version
    == CAMERA_DEVICE_API_VERSION_1_0) {
    // HAL 1 使用 QCamera2HardwareInterface
    QCamera2HardwareInterface *hw =
    new QCamera2HardwareInterface((uint32_t)camera_id);
    if (!hw) {
    LOGE("Allocation of hardware interface failed");
    return NO_MEMORY;
    }
    rc = hw->openCamera(hw_device);
    if (rc != NO_ERROR) {
    delete hw;
    }
    }
    #endif
    else {
    LOGE("Device version for camera id %d invalid %d",
    camera_id, mHalDescriptors[camera_id].device_version);
    return BAD_VALUE;
    }
    ...
    }
  • QCamera3HWI 中
    函数 getCamInfo 获取到的版本号为 CAMERA_DEVICE_API_VERSION_3_4 (772) ;这导致所有通过 CameraModule.getCameraInfo 获取到的版本号都将是该值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // QCamera3HWI.cpp
    int QCamera3HardwareInterface::getCamInfo(uint32_t cameraId,
    struct camera_info *info)
    {
    ...
    // 不管之前 HAL 版本是多少,在这都会被更新为 3.4 或者 3.3
    // 这里代码实际走到的是 3.4
    #ifndef USE_HAL_3_3
    info->device_version = CAMERA_DEVICE_API_VERSION_3_4;
    #else
    info->device_version = CAMERA_DEVICE_API_VERSION_3_3;
    #endif
    ..
    }
  • CameraProviderManager 中
    函数 getHighestSupportedVersion 获取到的版本号为 CAMERA_DEVICE_API_VERSION_3_3 (771) ,它是根据 CameraProvider::getHidlDeviceName 来解析的,而这个名字默认的只提供两种版本号 3.3 和 1.0 。

所以在 Framework 中 CameraService.cpp 将 halVersion 和 deviceVersion 做了区分(实际上如果代码全部统一,就没必要区分了);而 Hardware 中通常简化为 HAL 1, HAL 3 。

1
2
3
4
5
// cam_types.h
typedef enum {
CAM_HAL_V1 = 1,
CAM_HAL_V3 = 3
} cam_hal_version_t;

高通 HAL 架构

整体架构图

高通 Camera 架构图 ,查看原图

0109-android-camera-5-hal-overview.png

  • API 和 Framework 之间
    基于 AIDL 的 Binder 跨进程通信。
  • Framework 和 HAL AOSP 之间
    Android 8 开始,Framework 和 HAL 是基于 HIDL 的 Binder 跨进程通信的。
  • HAL AOSP 和 HAL qcom 之间
    通过 dlopen, dlsym 动态加载库文件,直接调用方法的。
  • HAL qcom 和 vendor qcom 之间
    主要是通过 ioctl 和 v4l2 驱动交互的(老版本平台两者之间会通过 socket 映射缓存区,或者 mshim 层加载库文件直接调用)。
    • hal 到 vendor 主要是在 v4l2(stream) 中事件通知方式进行;对于 map/unmap 则直接通过 domain socket 方式进行
    • vendor 到 hal 是通过 v4l2(control) 的事件通知机制进行的
    • v4l2(stream) 是通过 msm-camera 获取到相关信息; v4l2(control) 是通过 msm-config 获取到相关信息

高通文档对 HAL, vendor 的解释:

  • CameraService (libcamerservice)
    Camera service layer interacts with:
    • HAL for camera control APIs
    • SurfaceFlinger for delivering preview frames on LCD
  • CameraHardwareInterface (derived object)
    • HAL for actual camera hardware
    • QTI’s HAL implements Google HAL APIs to hook up QTI native camera driver in kernel
  • QTI Linux V4L2 camera driver (kernel)
    • Controls access to camera hardware
    • Proxy to access the QTI-proprietary modules on user space

Camera 子系统架构图

子系统架构图,查看原图

0109-android-camera-5-hal-architecture-overview.png

Camera 前端子系统

Frontend 子系统,查看原图

0109-android-camera-5-hal-frontend.png

HAL 1 流程图

HAL 1 流程图,查看原图

0109-android-camera-5-hal-1-and-mm-camera-interface.png

HAL 3 流程图

HAL 3 流程图,查看原图

0109-android-camera-5_qcom_hal-3-channel-request-result.png

kernel 架构图

kernel 架构,查看原图

0109-android-camera-5-hal-kernel.png

HAL 3 请求和回调流程图

预览

预览一,查看原图 , 预览二,查看原图

0109-android-camera-5-hal3-preview-1.png

0109-android-camera-5-hal3-preview-2.png

拍照

拍照流程,查看原图

0109-android-camera-5-hal3-snapshot.png

录像

录像流程,查看原图

0109-android-camera-5-hal3-viedo-recording.png

请求流程

CameraProvider 初始化

CameraProvider 初始化,查看大图

0109-android-camera-5-hal-CameraProvider_HAL3_init.jpg

configureStreams 配置流

CameraDeviceSession 配置流,查看原图

0109-android-camera-5-hal-CameraDeviceSession_configureStreams.jpg

捕获请求流程 processCaptureRequest

捕获请求流程 processCaptureRequest ,查看大图

0109-android-camera-5-hal-CameraDeviceSession_processCaptureRequest.jpg

回调流程

捕获结果 Callback 关联过程

当 HAL qcom 产生捕获结果后,将数据一路传递到 Framework ,我们重点分析下回调函数的关联过程!先从 hal 文件入手分析:

1
2
3
4
5
6
7
8
9
// ICameraDeviceCallback.hal
interface ICameraDeviceCallback {

// 返回捕获到的结果
processCaptureResult(vec<CaptureResult> results);

// 异步通知
notify(vec<NotifyMsg> msgs);
};

在 Framework 层,由 Camera3Device 实现并完成回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Camera3Device.h
class Camera3Device :
public CameraDeviceBase,
virtual public hardware::camera::device::V3_2::ICameraDeviceCallback,
private camera3_callback_ops {
...
private:
/**
* Implementation of
* android::hardware::camera::device::V3_2::ICameraDeviceCallback
*/
hardware::Return<void> processCaptureResult(
const hardware::hidl_vec<
hardware::camera::device::V3_2::CaptureResult>& results)
override;
hardware::Return<void> notify(
const hardware::hidl_vec<
hardware::camera::device::V3_2::NotifyMsg>& msgs)
override;

...
/**
* Static callback forwarding methods from HAL to instance
*/
static callbacks_process_capture_result_t sProcessCaptureResult;
static callbacks_notify_t sNotify;
}

从头文件中可以看出, Camera3Device 继承并实现了 ICameraDeviceCallback.hal 以及 camera3_callback_ops ,实际上这两个都是实现了相同的回调函数。从 LOG 上看,主要是 hal 文件接口回调的。在 open 流程中,这个 ICameraDeviceCallback 会经过如下流程;

1
2
3
4
Camera3Device ->
CameraProviderManager ->
CameraDevice ->
CameraDeviceSession

传入 HAL AOSP 的 CameraDeviceSession 中,并保存在 ResultBatcher 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CameraDeviceSession::CameraDeviceSession(
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<ICameraDeviceCallback>& callback) :
camera3_callback_ops({&sProcessCaptureResult, &sNotify}),
mDevice(device),
mDeviceVersion(device->common.version),
mIsAELockAvailable(false),
mDerivePostRawSensKey(false),
mNumPartialResults(1),
mResultBatcher(callback) {...}

CameraDeviceSession::ResultBatcher::ResultBatcher(
const sp<ICameraDeviceCallback>& callback) : mCallback(callback) {};

从构造函数中,同时看到将 camera3_callback_ops 中函数指针进行了关联,它定义的函数指针对应的是 ICameraDeviceCallback.hal 的函数:

1
2
3
4
5
6
7
// camera3.h
typedef struct camera3_callback_ops {
void (*process_capture_result)(const struct camera3_callback_ops *,
const camera3_capture_result_t *result);
void (*notify)(const struct camera3_callback_ops *,
const camera3_notify_msg_t *msg);
} camera3_callback_ops_t;

而 camera3_callback_ops 是 HAL qcom 向 HAL AOSP 发出的回调;在初始化过程中,传递到 QCamera3HardwareInterface 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// CameraDeviceSession.h
struct CameraDeviceSession :
public virtual RefBase,
protected camera3_callback_ops {..}

// CameraDeviceSession.cpp
bool CameraDeviceSession::initialize() {
/** Initialize device with callback functions */
status_t res = mDevice->ops->initialize(mDevice, this);
...
}

// QCamera3HardwareInterface.cpp
int QCamera3HardwareInterface::initialize(
const struct camera3_callback_ops *callback_ops)
{
...
mCallbackOps = callback_ops;
...
}

至此,在 QCamera3HardwareInterface 中通过 mCallbackOps 可以直接回调到 Framework 中,关联完毕。

processCaptureResult 触发流程图

捕获结果回调流程,查看原图

0109-android-camera-5-hal-CameraDeviceSession_processCaptureResult.jpg

从回调流程图中可以看出,由三个线程负责处理回调:

  • mm_camera_poll_fn 线程
    负责轮训处理 pipe 管道事件,如果是数据事件 MM_CAMERA_POLL_TYPE_DATA 触发回调。
  • mm_camera_cmd_thread 线程
    负责循环处理 cmd 命令,如果有数据结果 MM_CAMERA_CMD_TYPE_DATA_CB 触发回调。
  • QCameraCmdThread 线程
    负责循环处理流命令,如果拿到了数据结果 CAMERA_CMD_TYPE_DO_NEXT_JOB 触发回调。

回调函数指针关联

流程图中三个线程的回调函数关联过程,我们先从 QCamera3HardwareInterface 逐步分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// QCamera3HWI.cpp
void QCamera3HardwareInterface::orchestrateResult(
camera3_capture_result_t *result)
{
uint32_t frameworkFrameNumber;
int32_t rc = _orchestrationDb.getFrameworkFrameNumber(
result->frame_number, frameworkFrameNumber);
if (rc != NO_ERROR) {
LOGE("Cannot find translated frameworkFrameNumber");
assert(0);
} else {
if (frameworkFrameNumber == EMPTY_FRAMEWORK_FRAME_NUMBER) {
LOGD("CAM_DEBUG Internal Request drop the result");
} else {
result->frame_number = frameworkFrameNumber;
mCallbackOps->process_capture_result(mCallbackOps, result);
}
}
}

查找 QCamera3HardwareInterface 整个文件,只有 orchestrateResult 函数中调用了回调 process_capture_result 。从 LOG 中查看的调用关系,该函数是在 captureResultCb 中调用的,而它会在新建信道对象时传入到 QCamera3Channel 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// QCamera3Channel.cpp
QCamera3Channel::QCamera3Channel(uint32_t cam_handle,
uint32_t channel_handle,
mm_camera_ops_t *cam_ops,
channel_cb_routine cb_routine,
channel_cb_buffer_err cb_buffer_err,
cam_padding_info_t *paddingInfo,
cam_feature_mask_t postprocess_mask,
void *userData, uint32_t numBuffers)
{
...
mChannelCB = cb_routine;
...
}

// QCamera3Channel.h
typedef void (*channel_cb_routine)(mm_camera_super_buf_t *metadata,
camera3_stream_buffer_t *buffer,
uint32_t frame_number, bool isInputBuffer,
void *userdata);

channel_cb_routine 是一个函数指针,指向 QCamera3HardwareInterface::captureResultCb 函数;而 mChannelCB 大部分都是在 QCamera3Channel 子类的 streamCbRoutine 函数中调用的,而 streamCbRoutine 会在 QCamera3Stream 初始化时传入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// QCamera3Channel.cpp
int32_t QCamera3Channel::addStream(cam_stream_type_t streamType,
cam_format_t streamFormat,
cam_dimension_t streamDim,
cam_rotation_t streamRotation,
uint8_t minStreamBufNum,
cam_feature_mask_t postprocessMask,
cam_is_type_t isType,
uint32_t batchSize)
{
...
QCamera3Stream *pStream = new QCamera3Stream(m_camHandle,
m_handle,
m_camOps,
&mPaddingInfo,
this);
...
rc = pStream->init(streamType, streamFormat, streamDim, streamRotation,
NULL, minStreamBufNum, postprocessMask, isType, batchSize,
streamCbRoutine, this);
...
}

而在 QCamera3Stream::init 初始化时,会将 stream_cb 保存在 mDataCB 中, hal3_stream_cb_routine 是一个函数指针,指向 QCamera3Channel::streamCbRoutine :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// mm_camera_interface.h
typedef void (*mm_camera_buf_notify_t) (mm_camera_super_buf_t *bufs,
void *user_data);
// mm_camera_interface.h
typedef struct {
cam_stream_info_t *stream_info;
cam_padding_info_t padding_info;
mm_camera_stream_mem_vtbl_t mem_vtbl;
mm_camera_buf_notify_t stream_cb_sync;
mm_camera_buf_notify_t stream_cb;
void *userdata;
} mm_camera_stream_config_t;

// QCamera3Stream.h
typedef void (*hal3_stream_cb_routine)(mm_camera_super_buf_t *frame,
QCamera3Stream *stream,
void *userdata);
static void dataNotifyCB(mm_camera_super_buf_t *recvd_frame, void *userdata);

// QCamera3Stream.cpp
int32_t QCamera3Stream::init(cam_stream_type_t streamType,
cam_format_t streamFormat,
cam_dimension_t streamDim,
cam_rotation_t streamRotation,
cam_stream_reproc_config_t* reprocess_config,
uint8_t minNumBuffers,
cam_feature_mask_t postprocess_mask,
cam_is_type_t is_type,
uint32_t batchSize,
hal3_stream_cb_routine stream_cb,
void *userdata)
{
...
// Configure the stream
...
stream_config.stream_cb = dataNotifyCB;
stream_config.stream_cb_sync = NULL;

rc = mCamOps->config_stream(mCamHandle,
mChannelHandle, mHandle, &stream_config);
...
mDataCB = stream_cb;
...
}

同时,在流配置时将 dataNotifyCB 函数作为流回调赋值给了 stream_config.stream_cb ,stream_cb 是指向它的函数指针,在流配置时向下传递到 mm_camera_stream :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// mm_camera.h
mm_stream_data_cb_t buf_cb[MM_CAMERA_STREAM_BUF_CB_MAX];
typedef struct {
mm_camera_buf_notify_t cb;
void *user_data;
/* cb_count = -1: infinite
* cb_count > 0: register only for required times */
int8_t cb_count;
mm_camera_stream_cb_type cb_type;
} mm_stream_data_cb_t;

// mm_camera_stream.c
int32_t mm_stream_config(mm_stream_t *my_obj,
mm_camera_stream_config_t *config)
{
...
if (config->stream_cb_sync != NULL) {
/* SYNC callback is always placed at index 0*/
my_obj->buf_cb[cb_index].cb = config->stream_cb_sync;
my_obj->buf_cb[cb_index].user_data = config->userdata;
my_obj->buf_cb[cb_index].cb_count = -1; /* infinite by default */
my_obj->buf_cb[cb_index].cb_type = MM_CAMERA_STREAM_CB_TYPE_SYNC;
cb_index++;
}
my_obj->buf_cb[cb_index].cb = config->stream_cb;
my_obj->buf_cb[cb_index].user_data = config->userdata;
my_obj->buf_cb[cb_index].cb_count = -1; /* infinite by default */
my_obj->buf_cb[cb_index].cb_type = MM_CAMERA_STREAM_CB_TYPE_ASYNC;

...
}

最终回调函数实际保存在了 buf_cb[cb_index].cb 中,而它是在 mm_stream_dispatch_app_data 中触发调用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// mm_camera_stream.c
static void mm_stream_dispatch_app_data(mm_camera_cmdcb_t *cmd_cb,
void* user_data)
{
int i;
mm_stream_t * my_obj = (mm_stream_t *)user_data;
mm_camera_buf_info_t* buf_info = NULL;
mm_camera_super_buf_t super_buf;
...
for(i = 0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
if(NULL != my_obj->buf_cb[i].cb
&& (my_obj->buf_cb[i].cb_type !=
MM_CAMERA_STREAM_CB_TYPE_SYNC)) {
if (my_obj->buf_cb[i].cb_count != 0) {
/* if <0, means infinite CB
* if >0, means CB for certain times
* both case we need to call CB */
...
/* callback */
my_obj->buf_cb[i].cb(&super_buf,
my_obj->buf_cb[i].user_data);
}

...
}
}
...
}

在开启流过程中,如果有回调函数则启动 CAM_StrmAppData 回调线程,并将 mm_stream_dispatch_app_data 加到线程中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// mm_camera_stream.c
int32_t mm_stream_fsm_reg(mm_stream_t * my_obj,
mm_stream_evt_type_t evt,
void * in_val,
void * out_val)
{
...
case MM_STREAM_EVT_START:
{
uint8_t has_cb = 0;
uint8_t i;
/* launch cmd thread if CB is not null */
pthread_mutex_lock(&my_obj->cb_lock);
for (i = 0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
if((NULL != my_obj->buf_cb[i].cb) &&
(my_obj->buf_cb[i].cb_type
!= MM_CAMERA_STREAM_CB_TYPE_SYNC)) {
has_cb = 1;
break;
}
}
pthread_mutex_unlock(&my_obj->cb_lock);

pthread_mutex_lock(&my_obj->cmd_lock);
if (has_cb) {
snprintf(my_obj->cmd_thread.threadName,
THREAD_NAME_SIZE, "CAM_StrmAppData");
mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
mm_stream_dispatch_app_data,
(void *)my_obj);
}
...
}
...
}

再到 mm_camera_thread 线程中查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// mm_camera_thread.c
static void *mm_camera_cmd_thread(void *data)
{
int running = 1;
int ret;
mm_camera_cmd_thread_t *cmd_thread =
(mm_camera_cmd_thread_t *)data;
mm_camera_cmdcb_t* node = NULL;

mm_camera_cmd_thread_name(cmd_thread->threadName);
do {
do {
ret = cam_sem_wait(&cmd_thread->cmd_sem);
if (ret != 0 && errno != EINVAL) {
LOGE("cam_sem_wait error (%s)",
strerror(errno));
return NULL;
}
} while (ret != 0);

/* we got notified about new cmd avail in cmd queue */
node = (mm_camera_cmdcb_t*)cam_queue_deq(&cmd_thread->cmd_queue);
while (node != NULL) {
switch (node->cmd_type) {
case MM_CAMERA_CMD_TYPE_EVT_CB:
case MM_CAMERA_CMD_TYPE_DATA_CB:
case MM_CAMERA_CMD_TYPE_REQ_DATA_CB:
case MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB:
case MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY:
case MM_CAMERA_CMD_TYPE_START_ZSL:
case MM_CAMERA_CMD_TYPE_STOP_ZSL:
case MM_CAMERA_CMD_TYPE_GENERAL:
case MM_CAMERA_CMD_TYPE_FLUSH_QUEUE:
if (NULL != cmd_thread->cb) {
cmd_thread->cb(node, cmd_thread->user_data);
}
break;
case MM_CAMERA_CMD_TYPE_EXIT:
default:
running = 0;
break;
}
free(node);
node = (mm_camera_cmdcb_t*)cam_queue_deq(&cmd_thread->cmd_queue);
} /* (node != NULL) */
} while (running);
return NULL;
}

int32_t mm_camera_cmd_thread_launch(mm_camera_cmd_thread_t * cmd_thread,
mm_camera_cmd_cb_t cb,
void* user_data)
{
int32_t rc = 0;

cam_sem_init(&cmd_thread->cmd_sem, 0);
cam_sem_init(&cmd_thread->sync_sem, 0);
cam_queue_init(&cmd_thread->cmd_queue);
cmd_thread->cb = cb;
cmd_thread->user_data = user_data;
cmd_thread->is_active = TRUE;

/* launch the thread */
pthread_create(&cmd_thread->cmd_pid,
NULL,
mm_camera_cmd_thread,
(void *)cmd_thread);
return rc;
}

线程 launch 时,指定了 mm_camera_cmd_thread 函数,而该函数的主要功能就是执行回调 cmd_thread->cb(node, cmd_thread->user_data); ,当满足执行条件: cmd_sem 通知以及 cmd_queue 中有数据时,就会执行回调。继续跟踪代码,查看哪里触发满足执行条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// mm_camera_stream.c
static void mm_stream_data_notify(void* user_data)
{
mm_stream_t *my_obj = (mm_stream_t*)user_data;
int32_t i, rc;
uint8_t has_cb = 0, length = 0;
mm_camera_buf_info_t buf_info;

...
memset(&buf_info, 0, sizeof(mm_camera_buf_info_t));
rc = mm_stream_read_msm_frame(my_obj, &buf_info,
(uint8_t)length);
if (rc != 0) {
return;
}
...
mm_stream_handle_rcvd_buf(my_obj, &buf_info, has_cb);
}

void mm_stream_handle_rcvd_buf(mm_stream_t *my_obj,
mm_camera_buf_info_t *buf_info,
uint8_t has_cb)
{
int32_t rc = 0;
...

if(has_cb && my_obj->cmd_thread.is_active) {
mm_camera_cmdcb_t* node = NULL;

/* send cam_sem_post to wake up cmd thread to dispatch dataCB */
node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
if (NULL != node) {
memset(node, 0, sizeof(mm_camera_cmdcb_t));
node->cmd_type = MM_CAMERA_CMD_TYPE_DATA_CB;
node->u.buf = *buf_info;

/* enqueue to cmd thread */
cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

/* wake up cmd thread */
cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
} else {
LOGE("No memory for mm_camera_node_t");
}
}
pthread_mutex_unlock(&my_obj->cmd_lock);
}

mm_stream_handle_rcvd_buf 触发了回调流程,由 mm_stream_data_notify 发起,而在信道开启流程过程中,会为每个流分配及注册 Buffer :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// mm_camera_stream.c
int32_t mm_stream_reg_buf(mm_stream_t * my_obj)
{
int32_t rc = 0;
uint8_t i;

rc = mm_stream_request_buf(my_obj);
...

my_obj->queued_buffer_count = 0;
for(i = 0; i < my_obj->buf_num; i++){
/* check if need to qbuf initially */
if (my_obj->buf_status[i].initial_reg_flag) {
rc = mm_stream_qbuf(my_obj, &my_obj->buf[i]);
...
}
}
return rc;
}

int32_t mm_stream_qbuf(mm_stream_t *my_obj, mm_camera_buf_def_t *buf)
{
...
if (1 == my_obj->queued_buffer_count) {
/* Add fd to data poll thread */
rc = mm_camera_poll_thread_add_poll_fd(
&my_obj->ch_obj->poll_thread[0],
my_obj->my_hdl, my_obj->fd, mm_stream_data_notify,
(void*)my_obj, mm_camera_async_call);
...
}
...
return rc;
}

在注册 Buffer 的过程中,将 mm_stream_data_notify 添加到 mm_camera_poll_thread 线程中,而这个线程的功能是监听 pipe 管道,如果管道中有数据则触发回调,这个线程循环执行的函数为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// mm_camera.h
typedef void (*mm_camera_poll_notify_t)(void *user_data);

typedef struct {
int32_t fd;
mm_camera_poll_notify_t notify_cb;
uint32_t handler;
void* user_data;
} mm_camera_poll_entry_t;


// mm_camera_thread.c
static void *mm_camera_poll_fn(mm_camera_poll_thread_t *poll_cb)
{
int rc = 0, i;
...
do {
...
rc=poll(poll_cb->poll_fds,poll_cb->num_fds, poll_cb->timeoutms);
if(rc > 0) {
if ((poll_cb->poll_fds[0].revents & POLLIN) &&
...
} else {
for(i=1; i<poll_cb->num_fds; i++) {
...
if ((MM_CAMERA_POLL_TYPE_DATA==poll_cb->poll_type) &&
(poll_cb->poll_fds[i].revents & POLLIN) &&
(poll_cb->poll_fds[i].revents & POLLRDNORM)) {
if(NULL != poll_cb->poll_entries[i-1].notify_cb){
poll_cb->poll_entries[i-1].notify_cb(
poll_cb->poll_entries[i-1].user_data);
}
}
}
}
} else {
...
}
} while ((poll_cb != NULL) &&
(poll_cb->state == MM_CAMERA_POLL_TASK_STATE_POLL));
return NULL;
}

而代码中的 notify_cb 函数指针指向的就是 mm_stream_data_notify 函数。至此,回调过程全部关联完毕!

其他

类关联及 hal 文件

下面这幅图是使用 UML 组件图画的,仅仅是因为组件的方框和依赖的箭头画起来方便,并不是体现组件关系。该图的意义:

  • 体现 hal 文件调用及回调关系
  • 体现各个类之间通过变量连接的关系(代码中有较多的函数指针,类中变量在调用函数时有时候不记得指向的是哪个类了,通过该图能直观的找到对应关系)

关系查看大图

0109-android-camera-5-hal-class-callback-hal.png

常见数据结构图

摄像头默认属性

摄像头的默认属性,比如 ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL 硬件支持级别等,都是在 QCamera3HardwareInterface::initStaticMetadata 中初始化的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Camera3HWI.cpp
int QCamera3HardwareInterface::initStaticMetadata(uint32_t cameraId)
{
int rc = 0;
CameraMetadata staticInfo;
size_t count = 0;
bool limitedDevice = false;
char prop[PROPERTY_VALUE_MAX];
bool supportBurst = false;

supportBurst = supportBurstCapture(cameraId);

/* If sensor is YUV sensor (no raw support) or if per-frame control is not
* guaranteed or if min fps of max resolution is less than 20 fps, its
* advertised as limited device*/
limitedDevice = gCamCapability[cameraId]->no_per_frame_control_support ||
(CAM_SENSOR_YUV == gCamCapability[cameraId]->sensor_type.sens_type) ||
(CAM_SENSOR_MONO == gCamCapability[cameraId]->sensor_type.sens_type) ||
!supportBurst;

uint8_t supportedHwLvl = limitedDevice ?
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED :
#ifndef USE_HAL_3_3
// LEVEL_3 - This device will support level 3.
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3;
#else
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
#endif

staticInfo.update(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
&supportedHwLvl, 1);
...
}

resource_cost 的计算方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Camera3HWI.cpp
int QCamera3HardwareInterface::getCamInfo(uint32_t cameraId,
struct camera_info *info)
{
...
//resource cost is 100 * MIN(1.0, m/M),
//where m is throughput requirement with maximum stream configuration
//and M is CPP maximum throughput.
float max_fps = 0.0;
for (uint32_t i = 0;
i < gCamCapability[cameraId]->fps_ranges_tbl_cnt; i++) {
if(max_fps<gCamCapability[cameraId]->fps_ranges_tbl[i].max_fps)
max_fps=gCamCapability[cameraId]->fps_ranges_tbl[i].max_fps;
}
float ratio = 1.0 * MAX_PROCESSED_STREAMS *
gCamCapability[cameraId]->active_array_size.width *
gCamCapability[cameraId]->active_array_size.height * max_fps /
gCamCapability[cameraId]->max_pixel_bandwidth;
info->resource_cost = 100 * MIN(1.0, ratio);
LOGI("camera %d resource cost is %d", cameraId,
info->resource_cost);
...
}

poll 机制

参考转载:camera 中的 poll 机制

poll 机制,查看大图

0109-android-camera-5-hal-poll-pipe.png

这里的 poll 机制里面是嵌套了一个 pipe 机制;每次添加一个 poll thread 时,会给这个 poll thread 创建一个 pipe ,对于这个 poll thread 来说 pipe 也是文件 fd ,调用一次 poll 时可以传递给该函数 fd 数组,poll 会去查看每一个 fd ,一旦哪个 fd 的 poll 有返回,则该 poll thread 就会对其进行处理。那么 qcom 这里期望做一个可以动态添加 poll fd 的机制,他们利用了 pipe ,这个 pipe 就是 fds[0] ,也就是每个 poll thread 自带 fds[0] ,该 fd 响应添加新 fd 的操作,所以当 open 一个 dev 就可以把这个 dev 的 fd 通过 write fds[0] 添加进来。

信道 Channel 与流 Stream

先看一段代码,在信道中添加流时的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int32_t QCamera3Channel::addStream(cam_stream_type_t streamType,
cam_format_t streamFormat,
cam_dimension_t streamDim,
cam_rotation_t streamRotation,
uint8_t minStreamBufNum,
cam_feature_mask_t postprocessMask,
cam_is_type_t isType,
uint32_t batchSize)
{
int32_t rc = NO_ERROR;

if (m_numStreams >= 1) {
LOGE("Only one stream per channel supported in v3 Hal");
return BAD_VALUE;
}
...
}

注释为: HAL 3 中一个信道实例中只能有一个流!每个信道都有一个信道 ID ,它是在 QCamera3HardwareInterface 初始化时创建的 ID 号,也就是只会存在同一个信道 ID 。信道 QCamera3Channel 有很多实现类: QCamera3MetadataChannel, QCamera3PicChannel 等等,它们都共享了同一个 channel id 。

信道类继承关系,查看原图

0109-android-camera-5-hal-channel-class-uml.png

示例:在预览和拍照时,设置两个输出 Surface ,这个过程中会创建一个信道 id,5 个信道实例,4 条流:

  • QCamera3MetadataChannel
    获取 Metadata 元信息,对应的流类型和格式为 stream type: CAM_STREAM_TYPE_METADATA 7, format: 124 。
  • QCamera3SupportChannel
    HAL 内部消费会使用到,对应的流类型和格式为 stream type: CAM_STREAM_TYPE_ANALYSIS 11, format: CAM_FORMAT_YUV_420_NV12_VENUS 7 。
  • QCamera3RegularChannel
    预览,对应的流类型和格式为 stream type: CAM_STREAM_TYPE_PREVIEW 1, format: CAM_FORMAT_YUV_420_NV12_VENUS 7 。
  • QCamera3PicChannel
    拍照,对应的流类型和格式为 stream type: CAM_STREAM_TYPE_SNAPSHOT 3, format: CAM_FORMAT_YUV_420_NV21 2 。
  • QCamera3ProcessingChannel
    不需要添加流,参考其头文件注释:是用来处理直接从 HAL 发回给 Framework 所有的流,这些流并没有经过 HAL 后处理 QCamera3PostProc 。

对应的 Log 为,公共信道 ID: 2048 :

1
2
3
4
5
6
7
8
E QCamera : mm_camera_intf_add_stream: 773: E handle = 1792 ch_id = 2048
E QCamera : mm_camera_intf_config_stream: 852: E handle = 1792, ch_id = 2048,stream_id = 2304
E QCamera : mm_camera_intf_add_stream: 773: E handle = 1792 ch_id = 2048
E QCamera : mm_camera_intf_config_stream: 852: E handle = 1792, ch_id = 2048,stream_id = 2561
E QCamera : mm_camera_intf_add_stream: 773: E handle = 1792 ch_id = 2048
E QCamera : mm_camera_intf_config_stream: 852: E handle = 1792, ch_id = 2048,stream_id = 2818
E QCamera : mm_camera_intf_add_stream: 773: E handle = 1792 ch_id = 2048
E QCamera : mm_camera_intf_config_stream: 852: E handle = 1792, ch_id = 2048,stream_id = 3075

常用 LOG 开关

  • 开启 HAL 层 LOG 信息 adb root;adb shell setprop persist.camera.hal.debug 1
  • persist.camera.dumpimg 设置为 1 时,每次拍照预览都会在 /data/misc/camera 目录保存当前帧图片

Buffer 管理

Camera Buffer 相关管理,需要先搞清楚 Android 的图形显示系统;简单来讲:图形显示系统是按照生产者和消费者模型来架构的,在打开 Camera 时,需要创建需要显示(消费者)的 Surface ,当 Camera 设备捕获到帧数据时(生产者),直接将缓存区 Buffers 交给 Surface 来显示。

buffer_handle_t 的作用

先看几个数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// native_handle.h
typedef struct native_handle
{
int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; /* number of ints at &data[numFds] */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#endif
int data[0]; /* numFds + numInts ints */
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} native_handle_t;

typedef const native_handle_t* buffer_handle_t;

// gralloc_priv.h
#ifdef __cplusplus
struct private_handle_t : public native_handle {
#else
struct private_handle_t {
native_handle_t nativeHandle;
#endif
enum {
PRIV_FLAGS_FRAMEBUFFER = 0x00000001,
...
};

// file-descriptors
int fd;
int fd_metadata; // fd for the meta-data
// ints
int magic;
int flags;
unsigned int size;
unsigned int offset;
int bufferType;
uint64_t base __attribute__((aligned(8)));
unsigned int offset_metadata;
// The gpu address mapped into the mmu.
uint64_t gpuaddr __attribute__((aligned(8)));
int format;
int width; // holds aligned width of the actual buffer allocated
int height; // holds aligned height of the actual buffer allocated
uint64_t base_metadata __attribute__((aligned(8)));
int unaligned_width; // holds width client asked to allocate
int unaligned_height; // holds height client asked to allocate

#ifdef __cplusplus
static const int sNumFds = 2;
static inline int sNumInts() {
return ((sizeof(private_handle_t) - sizeof(native_handle_t)) /
sizeof(int)) - sNumFds;
}
static const int sMagic = 'gmsm';
...
}

简单来说:native_handle_t, buffer_handle_t, private_handle_t 都是用来描述一块缓存区的,可以将它们理解为一个句柄,用于跨进程传输数据;将 Camera 设备捕获到的帧数据缓存区,传递给显示系统直接显示。
buffer_handle_t 是一个 native_handle_t 型指针;这里重点关注成员 data ,它是一个大小为 0 的数组,data 指向成员 numInts 后的一个地址,这个地址通常表示 private_handle_t 结构中的数据;numFds 表示 private_handle_t 中有几个文件描述符;numInts 表示 private_handle_t 中有几个整形数据。下图为一个大致的描述:

0109-android-camera-5-hal-buffer_handle_t.png

所以 buffer_handle_t 可以理解为是传递给 Framework 中的 Surface 来消费的;因为涉及跨进程,直接拿到数据帧的地址指针并没有实际意义。

携带 Buffer 的数据结构

  • HAL qcom 中携带捕获帧数据的数据结构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // mm_camera_interface.h
    typedef struct mm_camera_buf_def {
    uint32_t stream_id;
    cam_stream_type_t stream_type;
    cam_stream_buf_type buf_type;
    uint32_t buf_idx;
    uint8_t is_uv_subsampled;
    struct timespec ts;
    uint32_t frame_idx;
    union {
    mm_camera_plane_buf_def_t planes_buf;
    mm_camera_user_buf_def_t user_buf;
    };
    int fd;
    void *buffer; // 捕获的帧数据指针
    size_t frame_len;
    void *mem_info;
    uint32_t flags;
    } mm_camera_buf_def_t;

    typedef struct {
    uint32_t camera_handle;
    uint32_t ch_id;
    uint32_t num_bufs;
    uint8_t bUnlockAEC;
    uint8_t bReadyForPrepareSnapshot;
    mm_camera_buf_def_t* bufs[MAX_STREAM_NUM_IN_BUNDLE];
    } mm_camera_super_buf_t;
  • HAL AOSP 中携带捕获帧数据的数据结构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // camera3.h
    typedef struct camera3_capture_result {
    uint32_t frame_number;
    const camera_metadata_t *result; // 元信息
    uint32_t num_output_buffers;
    const camera3_stream_buffer_t *output_buffers; // 输出缓冲区
    const camera3_stream_buffer_t *input_buffer;
    uint32_t partial_result;
    } camera3_capture_result_t;

    typedef struct camera3_stream_buffer {
    camera3_stream_t *stream; // 流信息
    buffer_handle_t *buffer; // 句柄,指向帧数据
    int status;
    int acquire_fence;
    int release_fence;
    } camera3_stream_buffer_t;

从数据结构中可以看出,在 HAL qcom 中,mm_camera_buf_def_t->buffer 可以直接拿到设备捕获帧数据的地址;而 HAL AOSP 因为需要将数据返回给 Framework ,涉及到跨进程通信,所以并不是直接地址,而是一个 buffer_handle_t 句柄 camera3_stream_buffer_t->buffer 。Framework 中 CaptureResult 只保留了这个句柄以及图像的元信息。

QCamera3Mem

这个文件中有三个类:

  • QCamera3Memory
    基类
  • QCamera3HeapMemory
    派生类,主要用于 HAL qcom 内部交互,getPtr 获取捕获的每帧数据地址 mm_camera_buf_def_t->buffer ,拿到这个地址可以直接做图像处理。
  • QCamera3GrallocMemory
    派生类,用于传递给 Framework 中,因为并不会直接向 Framework 返回帧地址,而是返回 buffer_handle_t 指针,即 camera3_stream_buffer->buffer ,图形显示时需要的数据结构。它实际对应的是同一块内存中的帧数据,只是把这块内存封装好了(暂时不清楚如何通过它获取帧数据)。

HAL AOSP, Framework 中拿到的都是 buffer_handle_t 指针,会直接在 Surface 中使用,我理解如果想对 Camera 捕获帧数据做实时处理,只能在 HAL qcom, vendor 这两层处理。

示例:灰度化帧数据

整个数据回调过程中,图像帧数据是以指针的形式传回来的,所以不会超过 Binder 1M 大小;但并不是每个 result 都包含图片 buffer 信息,按照信道不同,回调的数据是不一样的,比如 QCamera3MetadataChannel::streamCbRoutine 回调的结果中,只向 Framework 上报元信息,数据 output_buffers 是空的。
示例将预览的实时图像灰度化,预览流的格式为 CAM_FORMAT_YUV_420_NV12_VENUS ,即帧数据存储是 YUV_420_NV12 格式的,而当前平台这个格式存储特点(以 QCIF: 176*144 为例)是:

NV12-QCIF 存储格式,查看大图

0109-android-camera-5-hal-NV12-QCIF.png

  • plane 有两条:一条为 Y 数据,一条为 UV 数据
  • stride 行步进值,因为会数据对齐,所以每行并不是实际的宽度 width

想把 YUV 格式像素数据变成灰度图像,只需要将 U, V 分量设置成 128 即可。这是因为 U, V 是图像中的经过偏置处理的色度分量,色度分量在偏置处理前的取值范围是 -128~127,这时候的无色对应的是 0 值。经过偏置后色度分量取值变成了 0~255,因此无色对应的就是 128 了。
算法结果就是找到 UV 分量的地址,全部设置为 128 ,如下为代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// QCamera3Channel.cpp
// QCamera3ProcessingChannel::streamCbRoutine 函数中添加如下代码
//start
{
void *data=NULL;
if(offset.num_planes > 1){ // UV plane
uint32_t index = offset.mp[0].len;
for (int j=0; j < offset.mp[1].height; j++) {
data=(void*)((uint8_t*)super_frame->bufs[0]->buffer + index);
memset(data, 128, (size_t)offset.mp[1].width); // 灰度化
index += (uint32_t)offset.mp[1].stride; // 行步进
}
}
}
//end

后续

  • 画中画
  • 多屏显示
  • 如何利用平台的硬件来做图像识别和处理,都使用软件效率是否能达到?

参考文档