一、 Camera模组的工作原理以及组成部分
二、 Camera的常见技术指标
三、 基于Qualcomm平台的Camera相关代码
一、Camera模组的工作原理以及组成部分
Camera模组是影响捕捉至关重要的电子器件。其工作原理:物体通过镜头(LENS)聚集的光,通过CMOS或者CCD集成电路,把光信号转换成电信号,再经过内部图像处理器(ISP)转换成数字图像信号输出到数字信号处理器(DSP)加工处理,转换成标准的RGB、YUV等格式图像信号。
Camera模组包含四大件:镜头(LENS)、传感器(Sensor)、图像处理芯片(ISP)以及软板(FPC),决定一个Camera好坏的重要部分是lens、isp和sensor。
镜头(lens)是相机的灵魂,镜头(lens)对成像的效果有很重要的作用,是利用透镜的折射原理,景物光线通过镜头,在聚焦平面上形成清晰的影像,通过感光材料CMOS或CCD感光器记录景物的影像。
传感器(sensor)是CCM的核心模块,目前广泛使用的有两种:一种是广泛使用的CCD(电荷藕合)元件;另一种是CMOS(互补金属氧化物导体)器件。而目前市面上手机常用的是CMOS器件,其具体的工作原理可查百度。
图像处理芯片(ISP)是CCM的重要组成部分,它的作用是将感光芯片获得的数据及时快速地传递中央处理器并刷新感光芯片,因此ISP芯片的好坏,直接影响画面品质(比如色彩饱和度,清晰度等)。
FPC柔性电路板(柔性PCB): 简称"软板", 又称"柔性线路板",连接芯片和手机,起到电信号传输作用。
根据Camera模组的装配方式,可分为定焦模组和自动变焦模组。
二、 Camera的常见技术指标
在我们Tuning Camera时常打交道的一些技术指标:
1、 图像解析度/分辨率(Resolution):
软件代码里面常见与分辨率有关的参数有
Preview_sizes:Camera 预览尺寸,此参数如果不和模组做最佳尺寸匹配,将会影响到相机预览界面的清晰度;
Picture_sizes: 图片大小,即Camera模组能支持的拍照图片的尺寸大小;
Video_sizes:Camera模组能支持录制视频的分辨率;
2、 图像饱和度(Saturation):
饱和度是指色彩的鲜艳程度,也成色彩的纯度。饱和度取决于该色中含色成分和灰阶等级的比例。含色成分越大,饱和度越大;灰阶等级越高,饱和度越小。
3、 图像对比度(Contrast):
对比度参数简单讲即相机预览时整个屏幕在全白状态的亮度和全黑状态的亮度相除值。高对比度对于图像的清晰度、细节表现、灰度层次表现都有很大帮助。一般情况下对比度越大,图像越清晰醒目,色彩也越鲜明艳丽,反之可能让整个画满感觉灰蒙蒙的。
4、 图像锐度/清晰对(Sharpness):
图像的锐度/清晰度反应图像平面的清晰度和图像边缘锐利程度的一个指标。此指标值在适当的情况下能让图像平面上的细节更加真实、清晰明了,但过高的锐度会使图像严重失真。
5、 图像曝光(Exposure):
曝光度即控制camera模组的在预览和拍照时的进光量。比如在拍暗处的画面时可以将曝光值增大,可以使画面预览时更明亮清楚。
6、 图像白平衡(White Balance):
图像白平衡指在特定的光源下拍摄时出现的偏色现象,通过加强对应的补色来进行补偿。在手机相机中,一般选择自动白平衡(AWB)就足够了。
在Camera Tuning过程中,不同的平台有不同的Tuning方式,但归根结底,就是通过调试上述的各种参数让Camera效果达到最优。
三、 基于Qualcomm平台的Camera相关代码
基于Qualcomm平台Camera主要设计到的硬件模块:
1、 Application processor
主要用来装载操作系统,以及用来控制Camera相关的硬件模块,比如时钟、GPIO的设置,Camera Sensor 驱动的加载;
2、 Camera Control Interface (CCI):用于Camera子系统的专用控制接口,如I2C总线、Reset、Standby等GPIO的控制;
3、 Video Front End(VFE4)
用于处理Camera帧数据的图像处理硬件模块;是平台对Camera像素限制的一个主要因素。比如MSM8916平台的VFE可以支持13M的后置Camera和5M的前置Camera。正常模式下VFE4的时钟频率是320MHz,Turbo mode下的时钟频率可以达到465MHz.
4、 JPEG Encoder/Decoder
高通平台有专用于JPEG Encoding的硬件模块,Decoding的LIBGPEG软解码库。
5、 Camera Postprocessor(CPP)
用于对VFE输出的帧数据进行翻转/旋转、去噪、光滑/锐化、修剪以及尺寸缩放等处理。常模式下VFE4的时钟频率是320MHz,Turbo mode下的时钟频率可以达到465MHz。
Qualcomm的Camera Code分布情况:
Kernel Space:
kernel/drivers/media/platform/msm/camera_v2 (我们调试时sensor部分的驱动代码就是放在此目录sensor的文件夹中)
kernel/arch/arm/boot/dts(此文件主要是配置Camera i2c地址、各路供电情况,引脚配置等信息)
kernel/arch/arm/mach-msm/clock-xxxx.c(配置Camera的MCLK时序)
Vendor Space:
vendor/qcom/proprietary/mm-camera/mm-camera2
HAL Space:
hardware/qcom/camera/QCamera2
Framework Space:
frameworks/av/camera(代码生成libcamera_client.so)
frameworks/av/services/camera/libcameraservice/(代码生成libcameraservice.so)
frameworks/base/core/java/android/hardware/Camera.java(提供直接与JNI层中定义的接口交互的类)
APK Space:
packages/apps/Gallery2
Android原生的Camera采用Android标准的C/S架构,Client与Server两个独立的线程之间使用Binder通信,Camera在Android系统开机的时候就会注册一个CameraService,以提供给Client随时使用。
Camera apk调用的Camera相关的操作API都是由android.hardware.Camera,android.hardware.Camera :frameworks/base/core/java/android/hardware/Camera.java
frameworks/base/core/java/android/hardware/Camera.java主要通过调用JNI层的API实现。
frameworks/base/core/jni/android_hardware_Camera.cpp
frameworks/av/camera/*:此目录下的代码提供Camera client的类以及用于和Camera Service 之前基于IPC的Binder通信接口的实现。在Camera Client的类里面没有跟底层有实质性的交互。它主要作用就是与Camera Service通信从而达到操作Camera硬件。
frameworks/av/services/camera/libcameraservice/*:此目录下的代码提供Camera Service的类。在CameraService里可以与Camera HAL层进行交互从而获取到Camera硬件信息。通过与SurfaceFlinger通信将Camera的的数据刷到LCD上实现预览。
hardware/qcom/camera/QCamera2:CameraHardwareInterface,高通实现于高通Camera驱动架构相关联的Google 标准的HAL APIs。
QTI Linux V4L2 camera driver:用来控制Camera硬件,与高通专有模块(vendor/qcom/proprietary/mm-camera/mm-camera2)进行交互。
Camera HAL层与mm-camera(Qualcomm专用)通过一种内存buffer共享机制的域套接字(Domain Socket)进行通信交互。
通过buffer 信息进行分类,大概分为以下几种:
CAM_MAPPING_BUF_TYPE_CAPABILITY — Camera capability buffer
CAM_MAPPING_BUF_TYPE_PARM_BUF—Camera parameters buffer
CAM_MAPPING_BUF_TYPE_STREAM_BUF—Stream buffers
CAM_MAPPING_BUF_TYPE_STREAM_INFO—Stream information buffer
CAM_MAPPING_BUF_TYPE_OFFLINE_BUF—Offline reprocess input buffer
相关代码路径:
Socket implementation –
QCamera2\stack\mm-camera-interface\src\ mm_camera_sock.c
Message transmitter– QCamera2\stack\mm-camera-interface\src\mm_camera_stream.c
Message receiver – mm-camera\mm-camera2\server-imaging\server.c
QTI’s camera HAL communicates with kernel through V4L2 IOCTLs on the /dev/videoX nodes
高通Camera HAL与Kernel层通过dev/videox节点用标准的Linux V4L2 协议进行交互:
VIDIOC_S_FMT – Used to set preview and snapshot frame format to YUV420
VIDIOC_S_CTRL – Used to pass control information to device
VIDIOC_G_CTRL – Used to get control information from device
VIDIOC_QBUF – Used to queue/allocate preview buffers
VIDIOC_DQBUF – Used to dequeue/deallocate preview buffers
VIDIOC_STREAMON – Used to start camera preview
VIDIOC_STREAMOFF – Used to stop camera preview
VIDIOC_QUERYCAP – Used to query device capability
VIDIOC_QUERYBUF – Used to query buffer information
VIDIOC_REQBUFS – Used to request buffer registration
VIDIOC_DQEVENT – Used to dequeue event from kernel
关于porting一个新的Camera模组到系统里面去,我们需要关心的是以下几点:
1、 新的Sensor驱动代码,比如ov8865.c、sp2529.c等,此文件放入kernel/drivers/media/platform/msm/camera_v2/sensor目录下;
2、 在相关的camera的dtsi文件中,将Camera的一些配置信息添加进去;
qcom,camera@20 {
compatible = "ovti,ov8865_ofilm";
reg = <0x20>;
qcom,slave-id = <0x20 0x300b 0x8865>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
// qcom,eeprom-src = <&eeprom3>;
qcom,led-flash-src = <&led_flash0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "ov8865_q8v18a";
cam_vdig-supply = <&pm8110_l14>;
cam_vana-supply = <&pm8110_l19>;
cam_vio-supply = <&pm8110_l14>;
qcom,cam-vreg-name="cam_vdig", "cam_vio", "cam_vana";
qcom,cam-vreg-type = <0 0 0>;
qcom,cam-vreg-min-voltage=<1800000 1800000 2850000>;
qcom,cam-vreg-max-voltage=<1800000 1800000 2850000>;
qcom,cam-vreg-op-mode = <200000 200000 80000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 13 0>,
<&msmgpio 21 0>,
<&msmgpio 20 0>,
<&msmgpio 7 0>,
<&pm8110_gpios 2 0>;
qcom,gpio-reset = <1>;
qcom,gpio-standby = <2>;
qcom,gpio-vio = <3>;
qcom,gpio-af-pwdm = <4>;
qcom,gpio-req-tbl-num = <0 1 2 3 4>;
qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY",
"CAM_8MDVDD_EN",
"CAM_AF_PWDM";
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x7>;
qcom,sensor-position = <0>;
qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
3、 在kernel/arch/arm/mach-msm/clock-xxxx.c中添加Camera的MCLK配置;
如CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-0020"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-0020"),
根据原理图确认模组需要配置哪路MCLK,6-0020:6是i2c适配器编号,0020即模组的i2c地址。
4、 在vendor/qcom/proprietary/mm-camera/mm-camera2目录下添加对应sensor的sensor_libs;
此目录是高通平台Camera部分的代码。而在porting时,Soc 和Rawdata格式的模组有一定的区别。
Soc格式的camera模组只需要在vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs目录添加相应的libs库即可,因为具体的参数调用都在驱动文件里已经完成了。而Rawdata格式的模组不仅要在此目录下添加libs文件,还需要在
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0301/libchromatix目录下添加各种模式(preview、snapshot、video等)的详细模组参数,因为Rawdata的模组自身不带isp,它传输过来的原始数据需要平台的ISP去处理。
完成上面4个步骤,一个新sensor的模组porting基本上已经完成。在代码编译没问题且驱动配置正常的情况,相机的Camera基本上能正常使用了。
Porting过程中需要特别注意的几点:
1、 Sensor的电源配置,电源配置如有问题,Camera是不能正常工作的;
2、 Sensor的MCLK必须正常,在电源配置OK,MCLK异常同样可以导致Sensor不能正常工作;
3、 根据Sensor IC的Spec确认Sensor的上电时序,此处不正常会导致Sensor在powerup的时候失败,平台与sensor之间的i2c总线不能正常工作;
4、 上电正常后,根据Spec确认Sensor的i2c地址和ID,确保i2c通信正常;
驱动部分上面4步确认OK后,Sensor基本上能正常工作了。
5、确保vendor目录下相应的sensor_libs正常加载了,不然即使驱动工作正常,手机的相机也是不能正常打开的;该文件主要设置了VFE、Sensor的输出格式、LENS等模组和平台Camera相关的配置。(Rawdata格式的还要将libchromatix的参数porting进去,不然Camera也是打不开的)
Camera APP层分析之对camera framework层封装解析
Camera APP层分析之对camera framework层封装解析 Android4.4版本的camera和4.0版本的设计差距还是很大的,4.0版本以前的camera在是camera 主activity中直接调用camera hal层的的接口(如android.hardware.camera.open(),
android.hardware.camera.setPreviewDisplay(),android.hardware.camera..startPreview()等)与camera device通信。Android4.4版本camera app对camera device的访问又封装了一次,对camera device的访问必须经过camera manager接口,camera manager接口定义了camera基本操作,这样便于控制和管理camera device。下图为相关类关系图。
1.CameraManager接口:提供的访问camera设备基本的操作,实现类必须调用
CameraManager.cameraOpen获取CameraManager.CameraProxy接口对象来控制camera,实现CameraManager接口的类必须拥有一个独立于主线程的线程来实现对camera的操作,CameraManager接口也包装回调函数
2.CameraProxy接口,封装在CameraManager接口之内,接收对camera操作请求,发送消息到camer handle。所有对camera的操作都经由改接口,进行异步处理
3. AndroidCameraManagerImpl类,该类实现了CameraManager接口,与camera framework层接口直接通信,起到camera app 与camera framework对话中介的作用。
4.AndroidCameraProxyImpl类,AndroidCameraManagerImpl内部类,实现CameraManager.CameraProxy接口,CameraProxy已经介绍过了就是控制对camera的访问,AndroidCameraProxyImpl类实现了具体的操作。
5. CameraManagerFactory类,这个类的实现很简单,看到Factory,会想到软件设计中的工厂模式,这里就是封装了创建CameraManager对象的细节
6. CameraHolder类,看名字就可以看出这个类的基本功能了,就用来保存camera实力对象的,用在不同的module之间快速切换
以上是我们对camera app对camera framework层接口封装了的介绍。下面我们来看camera初始化过程。时序图如下
Step 1. CameraHolder .instance()
public static synchronized CameraHolder instance() {
if (sHolder == null) {
sHolder = new CameraHolder();
}
return sHolder;
}
这个函数定义在Camera2/src/com/android/cameraCameraHolder.java中
这个函数很简单就是创建一个CameraHolder实例,并保存在变量sHolder中,如果是第一次调用sHolder肯定是null,接着new 一个CameraHolder实例对象,这样的设计在android中经常见到,一种单例设计模式。现在我们来看看CameraHolder都做了些什么
private CameraHolder() {
HandlerThread ht = new HandlerThread("CameraHolder");
ht.start();
mHandler = new MyHandler(ht.getLooper());
if (mMockCameraInfo != null) {
mNumberOfCameras = mMockCameraInfo.length;
mInfo = mMockCameraInfo;
} else {
mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
mInfo = new CameraInfo[mNumberOfCameras];
for (int i = 0; i < mNumberOfCameras; i++) {
mInfo[i] = new CameraInfo();
android.hardware.Camera.getCameraInfo(i, mInfo[i]);
}
}
// get the first (smallest) back and first front camera id
for (int i = 0; i < mNumberOfCameras; i++) {
if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {
mBackCameraId = i;
} else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {
mFrontCameraId = i;
}
}
}
CameraHolder对象,首先创建了一个HandlerThread线程,独立于main thread,启动该线程。接着创建自己MyHandler,用于在HandlerThread中处理自己的事物。最后初始化mInfo变量,保存camerainfo。
Step 2. CameraHolder .open() public synchronized CameraProxy open( Handler handler, int cameraId,
CameraManager.CameraOpenErrorCallback cb) {
mCameraDevice.setParameters(mParameters); }
mCameraOpened = true;
mHandler.removeMessages(RELEASE_CAMERA); mKeepBeforeTime = 0; return mCameraDevice; }
该函数首先判断当前camera是否已经打开,如果已经打开。需要先调用mCameraDevice.release()释放掉,在重新调用CameraManagerFactory获取mCameraDevice实例对象。
Step 3. CameraManagerFactory. getAndroidCameraManager () Public
static
synchronized
CameraManager
getAndroidCameraManager() {
if (sAndroidCameraManager == null) { sAndroidCameraManager =
new
AndroidCameraManagerImpl(); }
return sAndroidCameraManager; } 该函数定义在Camera2/src/com/android/camera/
CameraManagerFactory.java文件中,代码很简单new AndroidCameraManagerImpl对象返回给调用者。 AndroidCameraManagerImpl() {
HandlerThread ht = new HandlerThread("Camera Handler Thread");
ht.start();
mCameraHandler = new CameraHandler(ht.getLooper()); }
从这个类的初始化中可以看到,该对象内部创建了自己的HandlerThread,并启动,这个很重要,后期的client对camera的操作都在这个线程中完成的。后面遇到具体操作我们在分析。
Step 4. AndroidCameraManagerImpl. cameraOpen () public CameraManager.CameraProxy cameraOpen(
Handler handler, int cameraId, CameraOpenErrorCallback callback) {
mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,
CameraOpenErrorCallbackForward.getNewInstance(
handler, callback)).sendToTarget(); mCameraHandler.waitDone(); if (mCamera != null) {
return new AndroidCameraProxyImpl(); } else { return null; } }
这个函数定义在
Camera2/src/com/android/camera/
AndroidCameraManagerImpl.java文件中
函数首先通过mCameraHandler获取OPEN_CAMERA消息并发送给mCameraHandler处理,mCameraHandler处理是在AndroidCameraManagerImpl初始化的时候创建的HandlerThread线程中处理的。发送完消息之后调用mCameraHandler.waitDone()
阻塞当前主线程,等待framework层的camera开启。 Step 5. android.hardware.Camera.open
在上一步发送了OPEN_CAMERA,指令给mCameraHandler。mCameraHandler收到这个指令之后调用framework层camera接口android.hardware.Camera.open开启camera device。当这一步完成之后,主线程会被唤醒,继续执行下一步。 Step 6 AndroidCameraProxyImpl
当主线程再次被唤醒的时候,判断camera device是否成功开启,如果成功开启,将创建AndroidCameraProxyImpl实例,后续对camera所有的操作的都要经过AndroidCameraProxyImpl统一发送到mCameraHandler进行处理。