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 interface ICameraProvider { setCallback(ICameraProviderCallback callback) generates (Status status); getVendorTags() generates (Status status, vec<VendorTagSection> sections); getCameraIdList() generates (Status status, vec<string> cameraDeviceNames); isSetTorchModeSupported() generates (Status status, bool support); 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); }; 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 interface ICameraDevice { getResourceCost() generates (Status status, CameraResourceCost resourceCost); getCameraCharacteristics() generates (Status status, CameraMetadata cameraCharacteristics); setTorchMode(TorchMode mode) generates (Status status); open(ICameraDeviceCallback callback) generates (Status status, ICameraDeviceSession session); dumpState(handle fd); }; 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 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; } 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
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 int main () { ALOGI("Camera provider Service is starting." ); android::ProcessState::initWithDriver("/dev/vndbinder" ); return defaultPassthroughServiceImplementation<ICameraProvider>( "legacy/0" , 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 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); }
服务注册流程图 注册流程,查看大图
CameraProvider
初始化从 main
方法中的 defaultPassthroughServiceImplementation
展开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 template <class Interface >__attribute__((warn_unused_result)) status_t registerPassthroughServiceImplementation ( std ::string name = "default" ) { sp<Interface> service = Interface::getService(name, true ); ... 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 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(); ... 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 ); ... iface = castRet; ... return iface; } 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 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 (void * , const std ::string & , const std ::string & )> eachLib) { ... std ::string packageAndVersion = fqName.substr(0 , idx); std ::string ifaceName = fqName.substr(idx + strlen ("::" )); const std ::string prefix = packageAndVersion + "-impl" ; const std ::string sym = "HIDL_FETCH_" + ifaceName; const int dlMode = RTLD_LAZY; void *handle = nullptr ; dlerror(); 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; 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 ; openLibs(fqName, [&](void * handle, const std ::string &lib, const std ::string &sym) { IBase* (*generator)(const char * name); *(void **)(&generator) = dlsym(handle, sym.c_str()); ... 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 extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider (const char * name) ;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/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 #define CAMERA_HARDWARE_MODULE_ID "camera" 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 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); 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; } } 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 static const char *variant_keys[] = { "ro.hardware" , "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 #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 #define HAL_MODULE_INFO_SYM HMI #define HAL_MODULE_INFO_SYM_AS_STR "HMI" 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); } ... 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 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 { 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 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)(); 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 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
中。
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
设计的目的
通常情况下搭配原则:API 1 + HAL 1
和 API 2 + HAL 3
。
HAL 1/3
对应功能概述
HAL 1
被设计为具有高级控件和以下三种运行模式的黑盒子:预览、视频录制、静态拍摄。三种模式具有略有不同又相互重叠的功能,这样就难以实现介于其中两种运行模式之间的新功能,例如连拍模式等。
HAL 3
子系统将多个运行模式整合为一个统一的视图,可以使用这种视图实现之前的任何模式以及一些其他模式,例如连拍模式。相机子系统被塑造为一个管道,该管道可按照 1:1 的基准将传入的帧捕获请求转化为帧。这些请求会封装有关帧的捕获和处理的所有配置信息:分辨率、像素格式、手动传感器、镜头、闪光灯控件、3A
运行模式、RAW->YUV
处理控件/统计信息生成等等。这样一来,便可以提高用户对聚焦、曝光以及更多后期处理(例如降噪、对比度和锐化)效果的控制能力。简单来说,应用框架从相机子系统请求帧,然后相机子系统将结果返回到输出流。此外系统还会针对每组结果生成包含色彩空间和镜头阴影等信息的元数据。
可以将 HAL 3
看作 HAL 1
的单向流管道,它会将每个捕获请求转化为传感器捕获的一张图像,这张图像将被处理成:
包含有关捕获的元数据的结果对象
图像数据的 1 到 N
个缓冲区,每个缓冲区会进入自己的目的地 Surface
可能的输出 Surface
组经过预配置:
每个 Surface
都是一个固定分辨率的图像缓冲区流的目标位置
一次只能将少量 Surface
配置为输出(约 3 个)
一个请求中包含所需的全部捕获设置,以及要针对该请求将图像缓冲区(从总配置组)推送到其中的输出 Surface
的列表。请求可以只发生一次 capture()
,也可以无限重复 setRepeatingRequest()
,捕获的优先级高于重复请求的优先级。
HAL 3
原理总结
异步拍摄请求来源于框架
硬件抽象层 HAL
按顺序处理这些请求。收到请求后,HAL
产生输出结果元数据以及一个或多个输出图像缓冲区
该进程遵循先进先出 FIFO
原则处理请求、结果以及后续请求参考的流
给定请求的所有输出的时间戳必须相同。如有需要,框架会一同匹配这些时间戳
所有拍摄配置和状态均属于拍摄请求和结果的一部分
应用程序可以请求带有特定设置的帧,例如,曝光设置和 HAL3
( FULL
模式),从而确保拍摄请求设置和产生的实际图像保持精确同步
接口区别 HAL 1 VS HAL 3,查看原图
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 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; } bool CameraProvider::initialize () { ... mPreferredHal3MinorVersion = property_get_int32("ro.camera.wrapper.hal3TrebleMinorVersion" ,3 ); switch (mPreferredHal3MinorVersion) { case 2 : case 3 : 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() { ... 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; 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 ) { 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) { 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 int QCamera3HardwareInterface::getCamInfo (uint32_t cameraId, struct camera_info *info) {... #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 typedef enum { CAM_HAL_V1 = 1 , CAM_HAL_V3 = 3 } cam_hal_version_t ;
高通 HAL
架构 整体架构图 高通 Camera 架构图 ,查看原图
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
子系统架构图子系统架构图,查看原图
Camera
前端子系统Frontend 子系统,查看原图
HAL 1
流程图HAL 1 流程图,查看原图
HAL 3
流程图HAL 3 流程图,查看原图
kernel
架构图kernel 架构,查看原图
HAL 3
请求和回调流程图预览 预览一,查看原图 , 预览二,查看原图
拍照 拍照流程,查看原图
录像 录像流程,查看原图
请求流程 CameraProvider
初始化CameraProvider 初始化,查看大图
CameraDeviceSession 配置流,查看原图
捕获请求流程 processCaptureRequest
捕获请求流程 processCaptureRequest
,查看大图
回调流程 捕获结果 Callback
关联过程 当 HAL qcom
产生捕获结果后,将数据一路传递到 Framework
,我们重点分析下回调函数的关联过程!先从 hal
文件入手分析:
1 2 3 4 5 6 7 8 9 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 class Camera3Device : public CameraDeviceBase, virtual public hardware::camera::device::V3_2::ICameraDeviceCallback, private camera3_callback_ops { ... private : 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 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 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 struct CameraDeviceSession : public virtual RefBase, protected camera3_callback_ops {..} bool CameraDeviceSession::initialize () { status_t res = mDevice->ops->initialize(mDevice, this ); ... } int QCamera3HardwareInterface::initialize ( const struct camera3_callback_ops *callback_ops) { ... mCallbackOps = callback_ops; ... }
至此,在 QCamera3HardwareInterface
中通过 mCallbackOps
可以直接回调到 Framework
中,关联完毕。
processCaptureResult
触发流程图捕获结果回调流程,查看原图
从回调流程图中可以看出,由三个线程负责处理回调:
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 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::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; ... } 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 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 typedef void (*mm_camera_buf_notify_t ) (mm_camera_super_buf_t *bufs, void *user_data) ;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 ; 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) ;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) { ... ... 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_stream_data_cb_t buf_cb[MM_CAMERA_STREAM_BUF_CB_MAX];typedef struct { mm_camera_buf_notify_t cb; void *user_data; int8_t cb_count; mm_camera_stream_cb_type cb_type; } mm_stream_data_cb_t ; int32_t mm_stream_config (mm_stream_t *my_obj, mm_camera_stream_config_t *config) { ... if (config->stream_cb_sync != NULL ) { 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 ; 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 ; 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 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 ) { ... 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 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; 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 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 ); 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); } } 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; 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 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 ; 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; cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node); 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 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++){ 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) { 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 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 ; 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
文件调用及回调关系
体现各个类之间通过变量连接的关系(代码中有较多的函数指针,类中变量在调用函数时有时候不记得指向的是哪个类了,通过该图能直观的找到对应关系)
关系查看大图
常见数据结构图
摄像头默认属性 摄像头的默认属性,比如 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 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); 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 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 int QCamera3HardwareInterface::getCamInfo (uint32_t cameraId, struct camera_info *info) { ... 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 机制,查看大图
这里的 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
。
信道类继承关系,查看原图
示例:在预览和拍照时,设置两个输出 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 typedef struct native_handle { int version; int numFds; int numInts; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #endif int data[0 ]; #if defined(__clang__) #pragma clang diagnostic pop #endif } native_handle_t ; typedef const native_handle_t * buffer_handle_t ;#ifdef __cplusplus struct private_handle_t : public native_handle {#else struct private_handle_t { native_handle_t nativeHandle; #endif enum { PRIV_FLAGS_FRAMEBUFFER = 0x00000001 , ... }; int fd; int fd_metadata; int magic; int flags; unsigned int size; unsigned int offset; int bufferType; uint64_t base __attribute__((aligned(8 ))); unsigned int offset_metadata; uint64_t gpuaddr __attribute__((aligned(8 ))); int format; int width; int height; uint64_t base_metadata __attribute__((aligned(8 ))); int unaligned_width; int unaligned_height; #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
中有几个整形数据。下图为一个大致的描述:
所以 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 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 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 存储格式,查看大图
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 { void *data=NULL ; if (offset.num_planes > 1 ){ 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; } } }
后续
画中画
多屏显示
如何利用平台的硬件来做图像识别和处理,都使用软件效率是否能达到?
参考文档