Android Camera启动流程和配置介绍

1. Android Camera Architecture

Figure 1: Camera architecture

2. Android Camera HAL3与上层的交互过程

Figure 2: Camera HAL与Framework交互

get_number_of_cameras: 获取camera个数,取值范围从0-N的整数,注意这里只统计built-in camera个数,不包含external camera个数。

get_camera_info: 获取camera信息,主要包括镜头旋转角度、前置还是后置、sensor相关的静态参数(static metadata)

get_vendor_tag_ops: 获取一些函数指针,用于查询厂商的扩展属性

set_callbacks: 设置两个回调函数到HAL,主要用于通知上层摄像头状态改变以及闪光灯状态改变

open: 通过index打开一个camera,返回hw_device_t结构体,上层通过这个结构体可以获取到如下这些HAL提供的API

initialize: 初始化函数,在这里我们会起一个线程,用于分发处理framework层传下来的消息

configure_streams: 新创建一路pipeline或者重置原来已经存在的pipeline。在这个API里面会告知HAL当前Pipeline的宽、高、以及格式信息。这个API必须至少包含一路输出流。同时HAL需要通过对这个API带来的一些参数赋值,比如max_buffers以及usage

construct_default_request_settings: 配置一个标准的捕获配置模板,这里的捕获case包含Preview、Record、Capture等。app层会基于这个settings来做修改,构造最终的捕获设置

process_capture_request: 调用这个API开始获取图片数据。

notify: HAL用于告知framework shutter/metadata/buffer

flush: 调用这个API主要是为了通知HAL尽快把指定设备还在处理的captures尽快返回,以便framework可以通过configure_streams来重新配置Pipeline。

close: 彻底销毁HAL创建的Camera实例

3. Android Camera HAL软件框架

Figure 3: Camera HAL Software Architecture

4. 预览、拍照、录像介绍

4.1. 预览

Preview是Android Camera最基本的功能之一。预览最重要的四个步骤如下:

1)Framework调用 construct_default_request_settings来获取预览这个usecase的默认请求的settings,HAL会返回一个模板Camera Metadata,后续的取数据的设定就是基于这个模板来构造

2)Framework调用configure_streams配置一路预览流,带有operation_mode/stream_type/width/height/format/priv等参数。HAL根据这里面的width/height/format配置MI输出一路流

3)Framework开始不断调用process_capture_request来获取预览流,每个process_capture_request里面都带有camera_metadata,HAL解析camera_metadata并根据camera_metadata做一些参数调整,然后捕获数据

4)HAL通过notify 通知Framework SHUTTER事件,接着调用process_capture_result回调函数将当前帧对应一些设置放在camera_metadata返回给Framework,最后再通过process_capture_result把buffer返回给Framework。

整体流程图如下:

Figure 4: Preview Flow

4.2. 拍照

拍照是Android Camera最基本的功能之一。流程如下:

1)Framework调用 construct_default_request_settings来获取拍照这个usecase的默认请求的settings,HAL会返回一个Camera Metadata,后续的捕获settings就是基于这个模板来构造

2)Framework调用configure_streams配置一路预览流,带有operation_mode/stream_type/width/height/format/priv/等参数。HAL根据这里面的width/height/format配置MI输出一路流。一般配置预览时,会同时配置拍照

3)Framework开始不断调用process_capture_request来获取预览流,每个process_capture_request里面都带有camera_metadata,HAL解析camera_metadata并根据camera_metadata做一些参数调整,然后捕获数据

4)HAL通过notify 通知Framework SHUTTER事件,接着调用process_capture_result回调函数将当前帧对应一些设置放在camera_metadata返回给Framework,最后再通过process_capture_result把buffer返回给Framework。

整体数据流处理流程基本和预览一致,唯一需要注意的有:

1) 拍照有两个实现方式,软编码和硬编码。默认aosp Camera2.apk是走的软编码,由APP决定的

2)如果是走软编码,预览的数据一般是从MI_SCL取NV12数据。如果是硬件编码,需要从MI_VENC获取编码后的es数据,然后按照规则组装成JPEG EXIF

3)对于软编码,上层传下来的format是HAL_PIXEL_FORMAT_YCbCr_420_888,对应的就是NV12的数据。对于走硬件编码,上层传下来的format是HAL_PIXEL_FORMAT_BLOB,拍照的buffer则是width = max.jpeg.size,height=1的buffer类型

JPEG EXIF介绍

我们打开一张JPEG,通过选项可以查看图片的详细信息,如下图:

Figure 5: EXIF 信息

这部分信息就是EXIF信息,是在HAL进行填充的,另外,我们拍完照会有一个缩略图,这个也是在HAL中产生的。所有的EXIF信息、缩略图信息、原JPEG图,组成最终一张完整的带有EXIF的JPEG图上传给APP。APP通过android提供的API可读写到这些信息,如图就是一张完整的带有EXIF的JPEG图:

Figure 6: EXIF JPEG数据排列

下图为JPEG Encoder编码出来的es流数据内容,可以看出是没有EXIF信息的,是JFIF格式(JPEG FILE Interchange Format):

Figure 7: MI VENC编码出来的ES流数据排列

完整的一张JPEG数据,是由一些标记码,EXIF,图片信息组成。图片信息包含原图JPEG信息,Thumbnail是嵌入在其中的缩略图,也是jpeg格式。所有的标记都由0xFF开头,表示一个标记码开始。标记码解释:

1)0xFFD8 :start of image,图像的开始,2字节

2)0xFFE0:Application 0,JFIF应用程序保留标记0,标记码之后包含九个具体字段:

3)0xFFE1 — 0xFFEF:Application n,其他应用程序保留标记n(n=1---15),包含两个字段:

4)0xFFDB:DQT,Define Quantization Table,定义量化表;包含具体九个字段:

5)0xFFC0:Start Of Frame,帧图像的开始,包含具体九个字段:

6)0xFFC4:Define Huffman Table,定义Huffman表,包含两个字段:

7)0xFFDD:Define Restart Interval,定义差分编码累计复位的间隔

8)0xFFDA:start of scan,扫描开始,包含具体2个字段,图像数据就是在这里面:

9)0xFFD9:end of image,图像结束

ref: https://www.cnblogs.com/sddai/p/5666924.html

我们JPEG编码器编码出来的是JFIF格式,而android需求的是EXIF格式,两种结构基本样式:

Figure 8: JFIF和EXIF对比

可以看出我们JPEG 编码器的结构JFIF和安卓要求的EXIF差别主要是在APP1和JFIF-APP0这部分,所以假设编码一张图片,流程如下:

1)根据Thumbnail size,编码出一张JFIF缩略图jpg0

2)编码原图,编码出一张JFIF结构的jpg1

3)把jpg1重新组装成带有缩略图的EXIF格式,如上图的EXIF APP1,即SOI-APP1-jpg0-jpg1

对于APP1的内容,主要填充如下tag:

Figure 9: tags

对应HAL实现如下:

1)Framework调用process_capture_request请求一张JPEG数据

2)从MI_SCL获取一张yuv buffer

3)将原图送进SCL,并crop成metadata中要求的thumbnail size

4)编码thumbnail

5)编码原图

6)将EXIF信息、缩略图、原图整合成一张EXIF结构的JPEG图片,并写到buffer_handle_t

7)通过process_capture_result返回给Framework

4.3. 录像

录像是Android Camera最基本的功能之一。对于HAL来说,行为和preview一样,HAL只要负责把YUV数据返回即可。录制的视频编码和保存是在上层完成的。

上层行为:

1)APP通过setPreviewDisplay将预览surface设置给CamerService。

2)APP通过setVideoSource(CAMER)告知创建CameraSource,将camera作为producer。内部将自动创建BufferQueue,连接CameraService作为producer,CameraSource作为consumer。

3)CamerService产生2路stream,1路preview,另1路为recording。

4)media codec从BufferQueue获取数据进行编码

5. camera_configuration.xml 配置文件介绍

camera_configuration.xml是Camera HAL的配置文件,可以在里面添加删减支持的Camera信息,Camera HAL在初始化时会来读取这个配置文件,然后根据配置的Camera信息来配置对应的Pipeline。不同Camera对应的属性值会有不同,需要根据实际进行配置。

camera_configuration.xml配置文件在设备端路径为:/vendor/etc/camera/camera_configuration.xml

Figure 10: camera_configuration.xml

如Figure 10所示,整个配置文件主要由几个几点组成。根节点是CameraSettings,CameraInfo节点是配置当前设备上要使用的Camera信息。Common节点配置一些功能项,目前只有IQServer一项。Settings节点可以由很多个,表示支持的Camera列表。接下来一一介绍。

5.1. CameraInfo节点

CameraInfo节点表示当前设备接入的Camera信息。实际接入几个Camera,这里就填写几个Sensor节点。字段解释:

name:Camera名称,注意需要跟下面Settings节点的name匹配,否则将会找不到Metadata信息

cameraId:即index,从0开始依次加1递增。当前默认支持两个sensor,0表示Back Camera,1表示Front Camera

chmap:根据实际接在哪个sensor pad填写,计算方法为 chmap = 2^(sensor pad),比如接在sensor pad0,那么chmap就是2的0次方1,接在sensor pad1,那么chmap就是2的1次方2,一次类推

sensorType:camera 类型,分为SENSOR_TYPE_SOC/SENSOR_TYPE_RAW/SENSOR_TYPE_USB三种,根据实际填写即可

frame.initialSkip:由于AE收敛过程导致的颜色异常,可以通过这个值来Skip掉前面未收敛前的帧数

cus3a.enable: 是否使用cus3a

5.2. Common节点

目前只有一项配置,IQServer.open,表示是否打开IQServer

5.3. Settings节点

Settings节点是个很重要的节点,里面包含了name和CameraStaticMetadata。

CameraStaticMetadata是描述Camera的静态能力属性,hal初始化时,上层会通过get_camera_info来获取这些能力属性值。

5.3.1. CameraStaticMetadata介绍

更详细的字段定义请查看官网:https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics

或者AOSP:system/media/camera/docs/docs.html

以下为部分重要节点介绍:

Settings Values Descriptions
Control.aeAvailableAntibandingModes [AUTO,50HZ,60HZ,OFF] SOC:AUTO
RAW:50HZ,60HZ //以排在首位作为初始化配置
Control.aeAvailableModes [OFF,ON,ON_AUTO_FLASH,ON_ALWAYS_FLASH] OFF:不支持FLASH时设置为OFF
ON/ON_AUTO_FLASH/ON_ALWAYS_FLASH:支持flash时的选项
Control.aeAvailableTargetFpsRanges int32 x 2 x n 这个设置项有多个限制需要注意:
1)录像必需要有一组恒定帧率,加入帧率为x,那就要包含(x,x)
2)录像帧率必须至少一组大于24帧
3)第一组必须Min<=15.所以第一组一般为(15,x)
4)各组帧率需要按升序排列
升序的具体意义为,假设有定义有两组帧率:(min1,max1),(min2,max2),则max2>=max1,max2==max1时,还需要满足min1 <= min2.
注:min fps可用于控制拍照预览时的最小帧率,也就是控制最大曝光时间,可以根据需要进行调整,但是设置多小会影响拍照速度,但在较暗的情况下能获得更好的预览效果。在设置template时,rk是选择最大的一组,也就是最后一组,从配置来看,都是固定分辨率,比如[30,30],[60,60]
Control.afAvailableModes [AUTO,CONTINUOUS_VIDEO,CONTINUOUTS_PICTURE,OFF] OFF:Camera不支持AF AUTO/CONTINUOUS_VIDEO/CONTINUOUTS_PICTURE/OFF: Camera具有支持AF功能 注:在构造template时,rk的做法是对于STILL_CAPTURE/ZERO_SHUTTER_LAG/PREVIEW,如果支持CONTINUOUTS_PICTURE,则为CONTINUOUS_PICTURE。对于VIDEO_RECORD/VIDEO_SNAPSHOT,如果支持CONTINUOUS_VIDEO,则为CONTINUOUS_VIDEO。对于INTENT_MANUAL,如果支持MODE_OFF,则为MODE_OFF。其他情况下为AUTO。CONTINUOUS_VIDEO表示慢对焦,CONTINUOUS_PICTURE表示快对焦
Control.awbAvailableModes [AUTO,INCANDESCENT(白炽灯),FLUORESCENT(荧光灯),DAYLIGHT(白天),CLOUDY_DAYLIGHT] SOC: AUTO
RAW:AUTO,INCANDESCENT(白炽灯),FLUORESCENT(荧光灯),DAYLIGHT(白天),CLOUDY_DAYLIGHT
Jpeg.maxSize int 计算公式如下:
最大分辨率为:scaler.availableStreamConfigurations中BLOB项最大的分辨
Jpeg.maxSize >= max_blob_w * max_blob_h * 3 / 2
Lens.info.availableApertures todo 可选光圈
lens.info.availableFocalLengths todo 可选焦长
lens.info.miniumFocusDistance 0和非0 0:不支持AF
非0:支持AF时,需要根据模组规格数来设置
scaler.availableMaxDigitalZoom 4 最大缩放倍数,按照以往经验,设置为4倍是比较稳妥的。如果影响帧率,可适当降低缩放倍数
scaler.availableStreamConfigurations format,res,output/input HAL层支持的分辨率列表,有如下限制:
1)需要按照分辨率依次降序排列
2)为了满足CTS要求,需要包含352x288,320x240,176x144配置项
3) 列表中需要支持BLOB,YCbCr_420_888,IMPLEMENTATION_DEFINED三种格式输出配置,三种格式中支持的分辨率要相同,例如
BLOB,1920x1080,OUTPUT,
BLOB,640x480,OUTPUT,
BLOB,352x288,OUTPUT,
BLOB,320x240,OUTPUT,
BLOB,176x144,OUTPUT,
YCbCr_420_888,1920x1080,OUTPUT,
YCbCr_420_888,640x480,OUTPUT,
YCbCr_420_888,352x288,OUTPUT,
YCbCr_420_888,320x240,OUTPUT,
YCbCr_420_888,176x144,OUTPUT,
IMPLEMENTATION_DEFINED,1920x1080,OUTPUT,
IMPLEMENTATION_DEFINED,640x480,OUTPUT,
IMPLEMENTATION_DEFINED,352x288,OUTPUT,
IMPLEMENTATION_DEFINED,320x240,OUTPUT,/br>
IMPLEMENTATION_DEFINED,176x144,OUTPUT
scaler.availableMinFrameDurations format,res,duration
配置scaler.availableStreamConfigurations中各个分辨率下最小帧间隔(即最大帧率),需要满足以下条件:
需要包含scaler.availableStreamConfigurations中定义的所有格式,分辨率,如:
BLOB,1920x1080,33333333,
BLOB,640x480,33333333,
BLOB,352x288,33333333,
BLOB,320x240,33333333,
BLOB,176x144,33333333,
YCbCr_420_888,1920x1080,33333333,
YCbCr_420_888,640x480,33333333,
YCbCr_420_888,352x288,33333333,
YCbCr_420_888,320x240,33333333,
YCbCr_420_888,176x144,33333333,
IMPLEMENTATION_DEFINED,1920x1080,33333333,
IMPLEMENTATION_DEFINED,640x480,33333333,
IMPLEMENTATION_DEFINED,352x288,33333333,
IMPLEMENTATION_DEFINED,320x240,33333333,
IMPLEMENTATION_DEFINED,176x144,33333333"
scaler.availableStallDurations format,res,duration
配置scaler.availableStreamConfigurations中BLOB格式各个分辨率的允许的最大间隔时长,可直接复制scaler.availableMinFrameDuraitions中BLOB选项,也可以设置大于scaler.availableMinFrameDurations中的值,只需要满足小于sensor.info.maxFrameDuration中配置的最大间隔即可。设置大一点有利于CTS拍照相关测试项的稳定性。如:
BLOB,1920x1080,33333333,
BLOB,640x480,33333333,
BLOB,352x288,33333333,
BLOB,320x240,33333333,
BLOB,176x144,33333333"
sensor.info.activeArraySize 0,0,1920,1080 设置成sensor驱动输出的最大分辨率
sensor.info.physicalSize todo sensor的物理尺寸,可从模组规格书中获取,与FOV计算相关
sensor.info.pixelArraySize 1920x1080 设置成sensor驱动输出的最大分辨率
sensor.orientation 0,90,180,270 模组安装方向,可设置0,90,180,270。客户根据摄像头安装方向进行调整,需要通过CTS verifier相关项的测试
flash.info.available TRUE,FALSE FALSE:不支持闪光灯
TRUE: 支持闪光灯
sensorType SENSOR_TYPE_YUV
SENSOR_TYPE_RAW
SENSOR_TYPE_USB

SENSOR_TYPE_SOC:自带ISP的模组,可输出YUV格式,不需要经过主芯片的ISP。走MI流程。
SENSOR_TYPE_RAW:输出raw数据,需要经过主芯片的ISP。走MI流程。
SENSOR_TYPE_USB:usbcam,不需要经过主芯片的ISP,会走V4L2,不走MI流程,所以跟SENSOR_TYPE_SOC区分开来
frame.initialSkip int 打开camera应用时,预览前几帧3A未收敛,可能在不同场景下存在前几帧颜色不对,可以设置过滤前面几帧来避免这个现象。此外,由于CTS很多帧率相关项是以发送固定的request数量,然后得到相应的帧结果时间来统计的,前面几个request结果返回可能会比较慢,也可以通过设置合适的过滤帧数来避免这个问题。
info.supportedHardwareLevel byte 支持的硬件等级,分为FULL 和LIMITED mode,FULL mode需要做到per-frame-control,而我们ISP目前最快也要下一帧才能生效,且3A至少要3帧才能生效。所以我们只能选择LIMITED mode

6. 如何适配一个新的Camera

  1. 配置对应sensor的驱动

    1)sensor driver源码路径在vendor/sigmastar/alkaid/sdk/driver/SensorDriver/drv/src下面,编译完成之后,需要将对应的ko拷贝到aosp/device/sigmastar/pioneer5/kernel下面

    2)sensor driver编译方式:

    source build/envsetup.sh
    lunch xxxx
    cd vendor/sigmastar/alkaid/sdk/driver/SensorDriver
    make
    

    生成的ko在vendor/sigmastar/alkaid/sdk/driver/SensorDriver/drv/src下

  2. 配置dts

    dts配置请参考Linux上的sensor配置

    修改dts之后需要重新编译kernel

    ./sstar_make.sh -k
    
  3. 配置camera_configuration.xml

    1)在CameraInfo节点中添加对应新的sensor的节点

    2)配置一个新的Settings节点。

    3)camera_configuration.xml在源码树的位置在/device/sigmastar/pioneer5/product/camera_configuration.xml

  4. 修改/device/sigmastar/pioneer5/modpram.json里面的E_MI_MODULE_ID_SNR节点,如下:

    Figure 11: modparam节点配置

    每一个配置项都是一个数组,数组同一个index对应的是同一个camera配置。比如上图IMX307_HDR对应的chmap是1,mclk是36M,lane_num是4,hdr_lane_num是4,IMX415_HDR对应的chmap是4,mclk用default值,lane_num是4。sensor_name可以从sensor driver里面SENSOR_DRV_ENTRY_IMPL_BEGIN_EX这个宏定义的参数获得

  5. ./sstar_make.sh -a -r重新打包升级

7. IQ调试

由于Android不允许Camera HAL联网,所以iqserver无法在Camera HAL中打开。目前的实现方式是把iqserver放在单独一个可执行文件来单独执行。

代码路径:vendor/sigmastar/hardware/camera/sstar_iqserver

可执行文件在板端的位置: /vendor/bin/sstar_iqserver

使用方式:

  1. 板子联网,确认和iqtool所在电脑互通

  2. 打开iqserver

    cd /vendor/bin
    
    ./sstar_iqserver &
    或者在camera_configuration.xml中把IQServer.open设置为TRUE,打开camera即可自动打开iqserver
    
  3. iq tool连接

8. CUS3A 使用

cus3a的代码在vendor/sigmastar/hardware/camera/cus3a ,主要是参考alkaid下isp_cus3a_if.c写的。实现方式如下:

Figure 12: cus3A实现

9. DEBUG相关

  1. 判断进程是否都正常启动

    Figure 13: camera相关进程

  2. dumpsys media.camera

    Figure 14: dumpsys

  3. logcat中搜索带有“CAM_" 的log

10. Misc

主要介绍Camera相关的路径及配置在AOSP的对应位置

  1. device配置:device/sigmastar/pioneer5/pioneer5_sdp/device.mk

    Figure 15: Device配置

  2. camera_configuration.xml在aosp位置: device/sigmastar/pioneer5/product/camera_configuration.xml

  3. default iqfile以及iqbin在aosp位置: device/sigmastar/pioneer5/sdk下

  4. HAL代码: vendor/sigmastar/hardware/camera