Android攝像頭相關原始碼分析: 裝置驅動, HAL, Framework
Hardware的分析可以自底向上, 首先看V4L2Camera, 再看CameraHardware, 再到CameraFactory. Framework的程式碼自底向上看東西就太多了, 因此先從SDK中的攝像頭部分看起. HAL和Framework說的都是C++的東西, 實現了安卓的底層. 但是實際上在開發app的時候用是SDK是JAVA語言編寫的. 我們知道JAVA可以通過JNI來呼叫C++程式碼, 接下來就來看看ADK中是如何使用的.
首先考慮一段呼叫攝像頭預覽的程式碼:
Camera cam = Camera.open(); // 獲取一個攝像頭例項
cam.setPreviewDisplay( surfaceHolder); // 設定預覽視窗
cam.startPreview(); // 開始預覽
第一行開啟的是預設攝像頭, 也可以換成 Camera.open(1)
開啟其他攝像頭,
這幾個函式的定義在ADK中位於Camera.java中, open函式為:
frameworks/base/core/java/android/hardware/Camera.java
public static Camera open(int cameraId) {
return new Camera(cameraId);
}
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;
}
可以看到, 直接open不加任何引數開啟的其實是第一個後置攝像頭. 總之最後open返回了一個Camera物件. 這裡看到了一個熟悉的函式getNumberOfCameras
,
在HAL中的camera_module_t中, 除了必須的hw_module_t, 還有兩個函式指標 get_number_of_cameras
和get_camera_info
,
估計這個 getNumberOfCameras
最終就是呼叫了get_number_of_cameras
.
於是來看這個函式:
/**
* Returns the number of physical cameras available on this device.
*/
public native static int getNumberOfCameras();
這個函式在Camera.java中只有一個宣告, 表明這是一個native函式, 於是就要找其對應的JNI的定義.
frameworks/base/core/jni/android_hardware_Camera.cpp
static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz)
{
return Camera::getNumberOfCameras();
}
再來找這個C++中的Camera類, 這個類已經位於android framework中了, 但是getNumberOfCameras的定義其實是在它的父類CameraBase中:
frameworks/av/camera/CameraBase.cpp
template <typename TCam, typename TCamTraits>
int CameraBase<TCam, TCamTraits>::getNumberOfCameras() {
const sp<ICameraService> cs = getCameraService();
if (!cs.get()) {
// as required by the public Java APIs
return 0;
}
return cs->getNumberOfCameras();
}
可以看到這裡只是簡單的獲取CameraService, 然後呼叫其getNumberOfCameras 函式, 再來看這個函式:
frameworks/av/camera/CameraBase.cpp
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
Mutex::Autolock _l(gLock);
if (gCameraService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16(kCameraServiceName));
if (binder != 0) {
break;
}
ALOGW("CameraService not published, waiting...");
usleep(kCameraServicePollDelay);
} while(true);
if (gDeathNotifier == NULL) {
gDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(gDeathNotifier);
gCameraService = interface_cast<ICameraService>(binder);
}
ALOGE_IF(gCameraService == 0, "no CameraService!?");
return gCameraService;
}
可以看到gCameraService是一個sp<ICameraService>型別的單例, 第一次呼叫這個函式的時候對gCameraService初始化, 以後每次只是簡單地返回這個變數. 在初始化的過程中, 用到了defaultServiceManager獲取了一個sm, 並通過sm->getService獲取到CameraService. defaultServiceManager這個函式位於frameworks/native/lib/binder/IServiceManager.cpp, 屬於binder通訊的一部分, 超出了本文的範圍, 以後有空再寫一篇部落格說明.
open函式呼叫完之後, 就是setPreviewDisplay和startPreiview, 這兩個函式同樣是native的, 其實現類似, 下面就只看看startPreview:
frameworks/base/core/jni/android_hardware_Camera.cpp
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
ALOGV("startPreview");
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
if (camera->startPreview() != NO_ERROR) {
jniThrowRuntimeException(env, "startPreview failed");
return;
}
}
這段程式碼首先獲取了一個Camera物件, 然後對其呼叫startPreview, get_native_camera的實習如下:
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
sp<Camera> camera;
Mutex::Autolock _l(sLock);
JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetLongField(thiz, fields.context));
if (context != NULL) {
camera = context->getCamera();
}
ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
if (camera == 0) {
jniThrowRuntimeException(env,
"Camera is being used after Camera.release() was called");
}
if (pContext != NULL) *pContext = context;
return camera;
}
該函式通過env->GetLongField獲取了一個JNICameraContext的物件的指標, 然後就能通過getCamera得到Camera物件了, 而這個JNICameraContext的物件的指標是在native_setup中設定的:
1: static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
2: jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
3: {
4: // Convert jstring to String16
5: const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
6: jsize rawClientNameLen = env->GetStringLength(clientPackageName);
7: String16 clientName(rawClientName, rawClientNameLen);
8: env->ReleaseStringChars(clientPackageName, rawClientName);
9:
10: sp<Camera> camera;
11: if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
12: // Default path: hal version is don't care, do normal camera connect.
13: camera = Camera::connect(cameraId, clientName,
14: Camera::USE_CALLING_UID);
15: } else {
16: jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
17: Camera::USE_CALLING_UID, camera);
18: if (status != NO_ERROR) {
19: return status;
20: }
21: }
22:
23: if (camera == NULL) {
24: return -EACCES;
25: }
26:
27: // make sure camera hardware is alive
28: if (camera->getStatus() != NO_ERROR) {
29: return NO_INIT;
30: }
31:
32: jclass clazz = env->GetObjectClass(thiz);
33: if (clazz == NULL) {
34: // This should never happen
35: jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
36: return INVALID_OPERATION;
37: }
38:
39: // We use a weak reference so the Camera object can be garbage collected.
40: // The reference is only used as a proxy for callbacks.
41: sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
42: context->incStrong((void*)android_hardware_Camera_native_setup);
43: camera->setListener(context);
44:
45: // save context in opaque field
46: env->SetLongField(thiz, fields.context, (jlong)context.get());
47: return NO_ERROR;
48: }
注意第13行, 通過Camera::connect獲取到了一個Camera物件, 這裡終於又從ADK 層進入到了Framework層.