Camera API2从android 5.0就已经支持了,API1早已经被谷歌标记为过时了,谷歌也一直在推camera API2,并且前不久伴随着Android 10的发布,谷歌推出了CameraX 库,为了更方便的使用API2。但目前仍然有很多camera相关的应用因为一开始使用的就是API1,业务框架流程都已经稳定,不会耗费人力去迁移camera API2。所以在很长一段时间内,camera API1还是会有应用在使用,当然后续API2也会分析的,毕竟这是大趋势。
本文是基于android 6.0源码,搭配camera HAL1进行分析,这对Android 8.0之前的系统来说都是通用的。android 8.0以后加入了Treble机制,cameraService层和HAL层之间加入了一层Camera HIDL层,流程和结构上发生了些变化,但核心没变,后续有时间会分析下。android 9.0以后的新设备都必须升级到HAL3,后续也会分析HAL3,这也是大势所趋。
尽管camera api1的的相关流程都已经被写烂了,之所以还写,一是想作为一个自己学习的记录,慢慢培养写东西的习惯;二是只有自己真正的去理一遍代码流程,才能真正的搞清楚代码逻辑,印象也更加深刻,就好比只看代码是永远也学不会编程的,必须要实操。
0 创建camera framework层实例
camera API1的接口简单易用,现在依然有很多人喜欢使用这套接口快捷的编写camera apk。本文应用相关的代码来源于android develop 官方示例。
// 官方示例
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
- frameworks/base/core/java/android/hardware/Camera.java
- 获取当前设备挂载的camera个数,查询后置摄像头并创建camera实例
public static Camera open() {
int numberOfCameras = getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return new Camera(i);
}
}
return null;
}
/** used by Camera#open, Camera#open(int) */
Camera(int cameraId) {
int err = cameraInitNormal(cameraId);
if (checkInitErrors(err)) {
if (err == -EACCES) {
throw new RuntimeException("Fail to connect to camera service");
} else if (err == -ENODEV) {
throw new RuntimeException("Camera initialization failed");
}
// Should never hit this.
throw new RuntimeException("Unknown camera error");
}
}
private int cameraInitNormal(int cameraId) {
return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
}
- 注意上面的实参是CAMERA_HAL_API_VERSION_NORMAL_CONNECT,后面会用到
- native_setup 是本地函数调用,函数实现在JNI层
private int cameraInitVersion(int cameraId, int halVersion) {
....
return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
ActivityThread.currentOpPackageName());
}
1 camera 本地设置
- frameworks/base/core/jni/android_hardware_Camera.cpp
{
"native_setup",
"(Ljava/lang/Object;IILjava/lang/String;)I",
(void*)android_hardware_Camera_native_setup },
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
……
sp<Camera> camera;
if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
// Default path: hal version is don't care, do normal camera connect.
camera = Camera::connect(cameraId, clientName,
Camera::USE_CALLING_UID, Camera::USE_CALLING_PID); 1-1
} else {
jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
Camera::USE_CALLING_UID, camera);
if (status != NO_ERROR) {
return status;
}
}
……
// We use a weak reference so the Camera object can be garbage collected.
// The reference is only used as a proxy for callbacks.
sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera); 1-2
context->incStrong((void*)android_hardware_Camera_native_setup);
camera->setListener(context); 1-3
}
- 因为之前传入的API版本是CAMERA_HAL_API_VERSION_NORMAL_CONNECT,所以会调用
1-1
处的Camera::connect,这个函数是个静态函数,直接用类名调用 1-2
处的JNICameraContext继承了CameraListener,负责回调拍照、预览、录像等数据。1-3
设置监听对象,供camera数据回调
2 Camera本地客户端层
2.1 Camera::connect
- frameworks/av/camera/Camera.cpp
- frameworks/av/camera/CameraBase.cpp
// Camera.cpp
sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
int clientUid, int clientPid)
{
return CameraBaseT::connect(cameraId, clientPackageName, clientUid, clientPid);
}
// CameraBase.cpp
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
const String16& clientPackageName,
int clientUid)
{
ALOGV("%s: connect", __FUNCTION__);
sp<TCam> c = new TCam(cameraId);
sp<TCamCallbacks> cl = c;
status_t status = NO_ERROR;
const sp<ICameraService>& cs = getCameraService(); 2-1
if (cs != 0) {
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
status = (cs.get()->connect)(cl, cameraId, clientPackageName, clientUid, /*out*/ c->mCamera); 2-2
}
if