前言
Android在版本更新和迭代過(guò)程中, Camera相關(guān)代碼也經(jīng)歷了幾個(gè)版本的更新, 主要表現(xiàn)為Camera HAL版本更新(HAL1 -> HAL2 -> HAL3), Camera API版本更新(Camera API1 -> Camera API2), 由于Android版本是向后兼容的, 所以為了解決兼容問(wèn)題, 勢(shì)必要做一些特殊的處理來(lái)保證不同版本調(diào)用問(wèn)題, 本文主要說(shuō)明 Camera HAL1, Camera HAL3, API1, API2之間的調(diào)用關(guān)系, 由于Camera HAL2只是一個(gè)過(guò)渡版本, 并且實(shí)際上并沒(méi)有使用, 所以不做討論.
說(shuō)明: Camera API1泛指 android.hardware.camera
包下的相關(guān)API, Camera API2泛指
android.hardware.camera2
包下的相關(guān)API.
HAL版本和Java API版本是否能交叉調(diào)用?
由于HAL版本有HAL1和HAL3, Java API有 API1和API2. 兩兩組合調(diào)用就要4種方式, 那么這4種方式在Android中是否都有其使用場(chǎng)景呢?
答案是肯定的, 即4種組合(API1 -> HAL1, API1 -> HAL3, API2 -> HAL1, API2 -> HAL3)在Android系統(tǒng)中都能正常調(diào)用, 根據(jù)Android系統(tǒng)HAL層支持情況和API使用情況不同, 就會(huì)出現(xiàn)上述四種組合的使用場(chǎng)景, 下面就具體介紹一下4種組合是在那種情況下使用到的, 以及基本實(shí)現(xiàn)原理.
版本轉(zhuǎn)換以及代碼位置
對(duì)于Google最初的設(shè)計(jì)初衷, Camera API1就是用來(lái)調(diào)用HAL1的, Camera API2則是用來(lái)調(diào)用HAL3的, 但由于Android碎片化嚴(yán)重, 加上要兼容版本, 所以API1也可以調(diào)用HAL3, API2也可以調(diào)用HAL1,轉(zhuǎn)換原理如下:
說(shuō)明: 以下所有代碼片段為高通平臺(tái), Android 7.1.1源碼中的代碼
API1調(diào)用HAL3
API1調(diào)用HAL3是在CameraService中完成轉(zhuǎn)換, 使用了不同的Client(Client泛指CameraService和HAL層之間的接口, 有三個(gè)版本), 代碼如下:
源碼位置: frameworks/av/services/camera/libcameraservice/CameraService.cpp
代碼:
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, int cameraId,
int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
// Default path: HAL version is unspecified by caller, create CameraClient
// based on device version reported by the HAL.
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, getpid(), legacyMode);
} else { // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
"Camera device \"%d\" HAL version %d does not support camera2 API",
cameraId, deviceVersion);
}
break;
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
case CAMERA_DEVICE_API_VERSION_3_2:
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, servicePid, legacyMode);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
facing, clientPid, clientUid, servicePid);
}
break;
default:
// Should not be reachable
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Camera device \"%d\" has unknown HAL version %d",
cameraId, deviceVersion);
}
} else {
// A particular HAL version is requested by caller. Create CameraClient
// based on the requested HAL version.
if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraId, facing,
clientPid, clientUid, servicePid, legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
" opened as HAL %x device", halVersion, deviceVersion,
CAMERA_DEVICE_API_VERSION_1_0);
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Camera device \"%d\" (HAL version %d) cannot be opened as HAL version %d",
cameraId, deviceVersion, halVersion);
}
}
return Status::ok();
}
上述代碼中基本判斷邏輯是:
- 打開(kāi)HAL版本沒(méi)有特殊指定(即正常調(diào)用Camera.open()),或者指定的HAL版本和要打開(kāi)的Camera默認(rèn)的HAL版本相同, 則根據(jù)要打開(kāi)的Camera的默認(rèn)HAL版本創(chuàng)建Client, 此時(shí),如果HAL層默認(rèn)使用HAL1,則創(chuàng)建
CameraClient
, 如果HAL層默認(rèn)使用HAL3, 根據(jù)使用API進(jìn)行判斷, 使用API1則創(chuàng)建Camera2Client
, 使用API2則創(chuàng)建CameraDeviceClient
. - 如果指定了一個(gè)特殊的HAL版本, 并且不是默認(rèn)的HAL版本, 此時(shí)只會(huì)創(chuàng)建
CameraClient
, 其他情況不支持, 原因注釋里面也說(shuō)明了.
上述幾個(gè)Client代碼位置為:
frameworks/av/services/camera/libcameraservice/api1/
frameworks/av/services/camera/libcameraservice/api2/
其中 CameraClient.cpp
是API調(diào)用HAL1的接口, Camera2Client.cpp
是API1調(diào)用HAL3接口, CameraDeviceClient
是API2調(diào)用HAL3接口, 是不是發(fā)現(xiàn)沒(méi)有API2調(diào)用HAL1接口? 接下來(lái)就會(huì)講這個(gè)了, 和上述轉(zhuǎn)換有區(qū)別.
API2調(diào)用HAL1
在4種調(diào)用組合里面, 有3種是通過(guò)在CameraService中創(chuàng)建不同的Client來(lái)實(shí)現(xiàn)的, 只有API2調(diào)用HAL1是在API層面實(shí)現(xiàn)的,即把Camera API2的API調(diào)用轉(zhuǎn)為Camera API1, 下面通過(guò)源碼來(lái)看下使用API2調(diào)用HAL1時(shí)打開(kāi)Camera操作是如何轉(zhuǎn)換的.
Camera API2 打開(kāi)Camera流程
frameworks/base/core/java/android/hardware/camera2/CameraManager.java
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}
調(diào)用 openCameraForUid
public void openCameraForUid(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
int clientUid)
throws CameraAccessException {
if (cameraId == null) {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
throw new IllegalArgumentException("callback was null");
} else if (handler == null) {
if (Looper.myLooper() != null) {
handler = new Handler();
} else {
throw new IllegalArgumentException(
"Handler argument is null, but no looper exists in the calling thread");
}
}
openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
}
調(diào)用 openCameraDeviceUserAsync
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Handler handler, final int uid)
throws CameraAccessException {
//部分源碼省略......
ICameraDeviceUser cameraUser = null;
try {
if (supportsCamera2ApiLocked(cameraId)) {
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, id,
mContext.getOpPackageName(), uid);
} else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
}
}
//部分源碼省略......
上面的 supportsCamera2ApiLocked(cameraId)
就是判斷是否支持HAL3, 如果不支持, 就調(diào)用
CameraDeviceUserShim.connectBinderShim(callbacks, id);
來(lái)獲取
ICameraDeviceUser
, 最后看下函數(shù) connectBinderShim()
代碼:
import android.hardware.Camera;
public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
int cameraId) {
//部分代碼省略...
// TODO: Make this async instead of blocking
int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
Camera legacyCamera = init.getCamera();
//部分代碼省略...
LegacyCameraDevice device = new LegacyCameraDevice(
cameraId, legacyCamera, characteristics, threadCallbacks);
return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
}
從上面import語(yǔ)句和 Camera legacyCamera = init.getCamera();
就能看出來(lái),此處是調(diào)用Camera API1接口來(lái)實(shí)現(xiàn)的, 所以Camera API2調(diào)用HAL1其實(shí)是在Framework層將API2接口轉(zhuǎn)為了API1, 對(duì)CameraService來(lái)說(shuō), 就是使用API1來(lái)調(diào)用. 轉(zhuǎn)換代碼路徑在: frameworks/base/core/java/android/hardware/camera2/legacy/
, 有興趣的可以看下.
各種版本調(diào)用使用場(chǎng)景
從上面的分析大家都了解了API和HAL之間不同版本轉(zhuǎn)換關(guān)系, 之所以有這么多轉(zhuǎn)換, 就是為了實(shí)現(xiàn)兼容, 因此下面簡(jiǎn)單說(shuō)一下各個(gè)版本在什么場(chǎng)景下有用武之地.
- API1 調(diào)用 HAL1
Android 5.0之前, 幾乎所有手機(jī)都使用的場(chǎng)景,也是最開(kāi)始Camera API1設(shè)計(jì)的初衷 - API1 調(diào)用 HAL3
對(duì)于支持HAL3手機(jī),并默認(rèn)使用HAL3作為HAL的版本, 此種類(lèi)型是手機(jī)想使用HAL3和Camera API2, 單由于沒(méi)法限制第三方應(yīng)用也使用Camera API2,而做出的一種兼容方式. - API2 調(diào)用 HAL1
手機(jī)不支持HAL3, 但應(yīng)用使用了Camera API2, 常見(jiàn)于中低端手機(jī), Android版本是5.0以上, 但底層不支持HAL3. - API2 調(diào)用 HAL3
Camera API2設(shè)計(jì)初衷就是調(diào)用HAL3的, 現(xiàn)在大多數(shù)中高端手機(jī)基本都是API2調(diào)用HAL3, 最大限度使用Camera提供的功能
總結(jié)
- 不同API和HAL版本直接調(diào)用可簡(jiǎn)單總結(jié)為以下幾點(diǎn):
API1 -> HAL1, 原始Camera流程, CameraService中使用的Client為CameraClient
API1 -> HAL3, 兼容使用API1的應(yīng)用, CameraService中使用的Client為Camera2Client
API2 -> HAL1, 底層只支持HAL1, 為了兼容API2應(yīng)用, 通過(guò)Framework層將API2轉(zhuǎn)為API1實(shí)現(xiàn)
API2 -> HAL3, 未來(lái)趨勢(shì), 提供更好的Camera控制能力, CameraService使用的Client為CameraDeviceClient
- API1調(diào)用HAL3和API2調(diào)用HAL1對(duì)于應(yīng)用來(lái)說(shuō), 都沒(méi)有發(fā)揮HAL3和API2的功能, 只是一個(gè)兼容方案.
最后通過(guò)一張圖片來(lái)直觀明了的說(shuō)明版本直接關(guān)系: