Android Camera API和HAL版本對(duì)應(yīng)關(guān)系

前言

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();
}

上述代碼中基本判斷邏輯是:

  1. 打開(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.
  2. 如果指定了一個(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)景下有用武之地.

  1. API1 調(diào)用 HAL1
    Android 5.0之前, 幾乎所有手機(jī)都使用的場(chǎng)景,也是最開(kāi)始Camera API1設(shè)計(jì)的初衷
  2. API1 調(diào)用 HAL3
    對(duì)于支持HAL3手機(jī),并默認(rèn)使用HAL3作為HAL的版本, 此種類(lèi)型是手機(jī)想使用HAL3和Camera API2, 單由于沒(méi)法限制第三方應(yīng)用也使用Camera API2,而做出的一種兼容方式.
  3. API2 調(diào)用 HAL1
    手機(jī)不支持HAL3, 但應(yīng)用使用了Camera API2, 常見(jiàn)于中低端手機(jī), Android版本是5.0以上, 但底層不支持HAL3.
  4. 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)系:


camera_api_hal_version.JPG
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刨啸,一起剝皮案震驚了整個(gè)濱河市达舒,隨后出現(xiàn)的幾起案子档痪,更是在濱河造成了極大的恐慌,老刑警劉巖漱逸,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異猬腰,居然都是意外死亡杂穷,警方通過(guò)查閱死者的電腦和手機(jī)悍缠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耐量,“玉大人飞蚓,你說(shuō)我怎么就攤上這事±妊眩” “怎么了趴拧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)山叮。 經(jīng)常有香客問(wèn)我著榴,道長(zhǎng),這世上最難降的妖魔是什么屁倔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任脑又,我火速辦了婚禮,結(jié)果婚禮上锐借,老公的妹妹穿的比我還像新娘问麸。我一直安慰自己,他們只是感情好钞翔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布严卖。 她就那樣靜靜地躺著,像睡著了一般布轿。 火紅的嫁衣襯著肌膚如雪哮笆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天汰扭,我揣著相機(jī)與錄音稠肘,去河邊找鬼。 笑死东且,一個(gè)胖子當(dāng)著我的面吹牛启具,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播珊泳,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鲁冯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了色查?” 一聲冷哼從身側(cè)響起薯演,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秧了,沒(méi)想到半個(gè)月后跨扮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年衡创,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帝嗡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璃氢,死狀恐怖哟玷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情一也,我是刑警寧澤巢寡,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站椰苟,受9級(jí)特大地震影響抑月,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舆蝴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一隘蝎、第九天 我趴在偏房一處隱蔽的房頂上張望痒玩。 院中可真熱鬧汤求,春花似錦旭绒、人聲如沸仇轻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)篷店。三九已至祭椰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疲陕,已是汗流浹背方淤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹄殃,地道東北人携茂。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诅岩,于是被迫代替她去往敵國(guó)和親讳苦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容