Android Camera2 簡介

Camera2簡介

在Google 推出Android 5.0的時(shí)候, Android Camera API 版本升級到了API2(android.hardware.camera2), 之前使用的API1(android.hardware.camera)就被標(biāo)為 Deprecated 了. Camera API2相較于API1有很大不同, 并且API2是為了配合HAL3進(jìn)行使用的, API2有很多API1不支持的特性, 比如:

  1. 更先進(jìn)的API架構(gòu)
  2. 可以獲取更多的幀(預(yù)覽/拍照)信息以及手動控制每一幀的參數(shù)
  3. 對Camera的控制更加完全(比如支持調(diào)整focus distance, 剪裁預(yù)覽/拍照圖片)
  4. 支持更多圖片格式(yuv/raw)以及高速連拍
    ......

上面列舉的只是一部分, 并且實(shí)際功能支持情況要看HAL層是否完成了相關(guān)功能, 也就是說API有很多功能來滿足拍照/錄像需求, 但實(shí)際是否能用和具體設(shè)備有關(guān).

基本架構(gòu)

在API架構(gòu)方面, Camera2和之前的Camera有很大區(qū)別, APP和底層Camera之前可以想象成用管道方式連接, 如下圖:

38487b61-571f-4b9e-908f-acd99d69f4f1.png

如上圖所示, Camera APP 通過CameraCaptureSession發(fā)送CaptureRequest, CameraDevices收到請求后返回對應(yīng)數(shù)據(jù)到對應(yīng)的Surface,預(yù)覽數(shù)據(jù)一般都是到TextureView, 拍照數(shù)據(jù)則在ImageReader中, 整體來說就是一個(gè)請求--響應(yīng)過程, 請求完成后, 可以在回調(diào)中查詢到相應(yīng)的請求參數(shù)和CameraDevice當(dāng)前狀態(tài), 總的來說, Camera2中預(yù)覽/拍照/錄像數(shù)據(jù)統(tǒng)一由Surface來接收, CaptureRequest代表請求控制的Camera參數(shù), CameraMetadata(CaptureResult)則表示當(dāng)前返回幀中Camera使用的參數(shù)以及當(dāng)前狀態(tài).

使用流程

API使用流程大體如下:

8c75b772-d214-4d2f-94ca-7b4ad877d17f.png
  1. 通過context.getSystemService(Context.CAMERA_SERVICE) 獲取CameraManager.
  2. 調(diào)用CameraManager .open()方法在回調(diào)中得到CameraDevice.
  3. 通過CameraDevice.createCaptureSession() 在回調(diào)中獲取CameraCaptureSession.
  4. 構(gòu)建CaptureRequest, 有三種模式可選 預(yù)覽/拍照/錄像.
  5. 通過 CameraCaptureSession發(fā)送CaptureRequest, capture表示只發(fā)一次請求, setRepeatingRequest表示不斷發(fā)送請求.
  6. 拍照數(shù)據(jù)可以在ImageReader.OnImageAvailableListener回調(diào)中獲取, CaptureCallback中則可獲取拍照實(shí)際的參數(shù)和Camera當(dāng)前狀態(tài).

查詢Camera2功能支持情況

上面說過, 不是所以手機(jī)都支持完整的Camera2功能, 現(xiàn)在都2018了, Camera2出來都有4年左右了, 但估計(jì)還有些中低端手機(jī)使用的HAL1, 使用HAL1就會導(dǎo)致Camera2一些高級功能都沒法使用了, 下面講一下如何查詢設(shè)備對應(yīng)Camera2的支持情況.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件層面支持的Camera2功能等級, 主要分為5個(gè)等級:

  • INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
  • INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
  • INFO_SUPPORTED_HARDWARE_LEVEL_FULL
  • INFO_SUPPORTED_HARDWARE_LEVEL_3
  • INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL

LEVEL_LEGACY: 向后兼容模式, 如果是此等級, 基本沒有額外功能, HAL層大概率就是HAL1(我遇到過的都是)
LEVEL_LIMITED: 有最基本的功能, 還支持一些額外的高級功能, 這些高級功能是LEVEL_FULL的子集
LEVEL_FULL: 支持對每一幀數(shù)據(jù)進(jìn)行控制,還支持高速率的圖片拍攝
LEVEL_3: 支持YUV后處理和Raw格式圖片拍攝, 還支持額外的輸出流配置
LEVEL_EXTERNAL: API28中加入的, 應(yīng)該是外接的攝像頭, 功能和LIMITED類似

各個(gè)等級從支持的功能多少排序?yàn)? LEGACY < LIMITED < FULL < LEVEL_3
獲取代碼如下:

// CameraCharacteristics  可通過 CameraManager.getCameraCharacteristics() 獲取
private int isHardwareSupported(CameraCharacteristics characteristics) {
        Integer deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
        if (deviceLevel == null) {
            Log.e(TAG, "can not get INFO_SUPPORTED_HARDWARE_LEVEL");
            return -1;
        }
        switch (deviceLevel) {
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
                Log.w(TAG, "hardware supported level:LEVEL_FULL");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
                Log.w(TAG, "hardware supported level:LEVEL_LEGACY");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3:
                Log.w(TAG, "hardware supported level:LEVEL_3");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
                Log.w(TAG, "hardware supported level:LEVEL_LIMITED");
                break;
        }
        return deviceLevel;
    }

REQUEST_AVAILABLE_CAPABILITIES
上面講的幾個(gè)等級是從整體上說明Camera2支持情況, 我們還可以查詢更多細(xì)節(jié)功能,REQUEST_AVAILABLE_CAPABILITIES 則可以知道具體支持哪些實(shí)際功能, 具體有如下功能:

BACKWARD_COMPATIBLE READ_SENSOR_SETTINGS
MANUAL_SENSOR BURST_CAPTURE
MANUAL_POST_PROCESSING YUV_REPROCESSING
RAW DEPTH_OUTPUT
PRIVATE_REPROCESSING CONSTRAINED_HIGH_SPEED_VIDEO

各個(gè)功能具體含義請參考官網(wǎng)的解釋 :官網(wǎng) 的解釋, 我沒有深入研究.
大多數(shù)支持等級為 LEGACY 的設(shè)備, 只支持 BACKWARD_COMPATIBLE , 也就是說前面提到的Camera2新功能都不支持......

Camera2 AE/AF Region

在使用Camera2 API過程中, 設(shè)置測光和對焦區(qū)域這部分剛開始一直不知道該怎么寫代碼, 后面折騰了一番, 終于找到正確的方法了, 在此記錄一下.
AE/AF 區(qū)域需要通過用戶在屏幕上的點(diǎn)擊位置來進(jìn)行設(shè)置, 因此我們要做的就是將屏幕點(diǎn)擊點(diǎn)映射到底層對焦和測光位置點(diǎn), 同時(shí)發(fā)送請求觸發(fā)對焦這個(gè)動作. 首先要知道一點(diǎn)就是, 屏幕點(diǎn)擊點(diǎn)坐標(biāo)和Camera底層坐標(biāo)不是對應(yīng)的, 基本關(guān)系如下圖:


AF_Coordinate.jpg

x,y坐標(biāo)系表示屏幕坐標(biāo), x1,y1表示Camera對應(yīng)坐標(biāo)
可見屏幕點(diǎn)擊和底層有個(gè)90度旋轉(zhuǎn)關(guān)系, 實(shí)際設(shè)置AF/AE區(qū)域步驟如下:

1.首先獲取SENSOR_INFO_ACTIVE_ARRAY_SIZE, 即底層Camera坐標(biāo)點(diǎn)的范圍, API中通過一個(gè)Rect表示

characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)

得到``Rect```通常為支持的最大圖片尺寸, 比如(0, 0, 4160, 3120)

2.坐標(biāo)映射
我們需要將屏幕點(diǎn)擊點(diǎn)映射到 SENSOR_INFO_ACTIVE_ARRAY_SIZE對應(yīng)的Rect中, 從圖中可以很容易看出, 坐標(biāo)轉(zhuǎn)換關(guān)系為 : x1 = y , y1 = previewWidth - x, 坐標(biāo)轉(zhuǎn)換完成后只需乘以 圖片寬度/預(yù)覽寬度的比例即可得到轉(zhuǎn)換后的坐標(biāo), 然后以坐標(biāo)點(diǎn)為中心生成一個(gè)矩形, 這個(gè)矩形就是我們要的結(jié)果, 實(shí)際代碼片段如下:

    private MeteringRectangle calcTapAreaForCamera2(CameraCharacteristics c, int areaSize, int
            weight) {
        // 獲取Size
        Rect rect = c.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        Log.d(TAG, "active Rect:" + rect.toString());
        Rect newRect;
        int leftPos, topPos;
        // 坐標(biāo)轉(zhuǎn)換
        float newX = currentY;
        float newY = previewWidth - currentX;
        // 大小轉(zhuǎn)換
        leftPos = (int) ((newX / previewHeight) * rect.right);
        topPos = (int) ((newY / previewWidth) * rect.bottom);
        // 以坐標(biāo)點(diǎn)為中心生成一個(gè)矩形, 需要防止上下左右的值溢出
        int left = clamp(leftPos - areaSize, 0, rect.right);
        int top = clamp(topPos - areaSize, 0, rect.bottom);
        int right = clamp(leftPos + areaSize, leftPos, rect.right);
        int bottom = clamp(topPos + areaSize, topPos, rect.bottom);
        newRect = new Rect(left, top, right, bottom);
        Log.d(TAG, newRect.toString());
        // 構(gòu)造MeteringRectangle
        return new MeteringRectangle(newRect, weight);
    }

    private int clamp(int x, int min, int max) {
        if (x > max) {
            return max;
        }
        if (x < min) {
            return min;
        }
        return x;
    }

注: 此處坐標(biāo)映射可以通過Matrix進(jìn)行, 熟悉Matrix的同學(xué)可以嘗試下

3.設(shè)置AE/AF區(qū)域并觸發(fā)對焦, 代碼片段如下

public void startControlAFRequest(MeteringRectangle rect,
                                        CameraCaptureSession.CaptureCallback captureCallback) {

    MeteringRectangle[] rectangle = new MeteringRectangle[]{rect};
    // 對焦模式必須設(shè)置為AUTO
    mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_AUTO);
    //AE
    mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS,rectangle);
    //AF 此處AF和AE用的同一個(gè)rect, 實(shí)際AE矩形面積比AF稍大, 這樣測光效果更好
    mPreviewBuilder.set(CaptureRequest.CONTROL_AF_REGIONS,rectangle);
    try {
        // AE/AF區(qū)域設(shè)置通過setRepeatingRequest不斷發(fā)請求
        mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
    //觸發(fā)對焦
    mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CaptureRequest.CONTROL_AF_TRIGGER_START);
    try {
        //觸發(fā)對焦通過capture發(fā)送請求, 因?yàn)橛脩酎c(diǎn)擊屏幕后只需觸發(fā)一次對焦
        mSession.capture(mPreviewBuilder.build(), captureCallback, mHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

總結(jié)

Camera2 API和舊的Camera API區(qū)別很大, 剛開始用可能會很不習(xí)慣, 但Camera2有很多優(yōu)勢, 提供了非常多的參數(shù)供我們控制, 后面API1可能會被移除, 所以可以盡早將項(xiàng)目用Camera2重寫, 另外如果對API和HAL版本對應(yīng)關(guān)系不清楚的, 可以參考我之前寫的文章 Android Camera API和HAL版本對應(yīng)關(guān)系.
基于Camera2 API, 我寫了個(gè)Demo, 功能還算完善, 有興趣的可以看下: Github: Camera2, 目前沒有錄像功能, 后續(xù)會加上.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末知牌,一起剝皮案震驚了整個(gè)濱河市宴抚,隨后出現(xiàn)的幾起案子默色,更是在濱河造成了極大的恐慌谎势,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虐沥,死亡現(xiàn)場離奇詭異熊经,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)欲险,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門镐依,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人天试,你說我怎么就攤上這事槐壳。” “怎么了喜每?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵务唐,是天一觀的道長。 經(jīng)常有香客問我带兜,道長枫笛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任鞋真,我火速辦了婚禮崇堰,結(jié)果婚禮上沃于,老公的妹妹穿的比我還像新娘涩咖。我一直安慰自己,他們只是感情好繁莹,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布檩互。 她就那樣靜靜地躺著,像睡著了一般咨演。 火紅的嫁衣襯著肌膚如雪闸昨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天薄风,我揣著相機(jī)與錄音饵较,去河邊找鬼。 笑死遭赂,一個(gè)胖子當(dāng)著我的面吹牛循诉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撇他,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼茄猫,長吁一口氣:“原來是場噩夢啊……” “哼狈蚤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起划纽,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脆侮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后勇劣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靖避,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年芭毙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筋蓖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡退敦,死狀恐怖粘咖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侈百,我是刑警寧澤瓮下,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站钝域,受9級特大地震影響讽坏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜例证,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一路呜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧织咧,春花似錦胀葱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捅位,卻和暖如春轧葛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艇搀。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工尿扯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焰雕。 一個(gè)月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓衷笋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淀散。 傳聞我的和親對象是個(gè)殘疾皇子右莱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

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