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不支持的特性, 比如:
- 更先進(jìn)的API架構(gòu)
- 可以獲取更多的幀(預(yù)覽/拍照)信息以及手動控制每一幀的參數(shù)
- 對Camera的控制更加完全(比如支持調(diào)整focus distance, 剪裁預(yù)覽/拍照圖片)
- 支持更多圖片格式(yuv/raw)以及高速連拍
......
上面列舉的只是一部分, 并且實(shí)際功能支持情況要看HAL層是否完成了相關(guān)功能, 也就是說API有很多功能來滿足拍照/錄像需求, 但實(shí)際是否能用和具體設(shè)備有關(guān).
基本架構(gòu)
在API架構(gòu)方面, Camera2和之前的Camera有很大區(qū)別, APP和底層Camera之前可以想象成用管道方式連接, 如下圖:
如上圖所示, 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使用流程大體如下:
- 通過
context.getSystemService(Context.CAMERA_SERVICE)
獲取CameraManager
. - 調(diào)用
CameraManager .open()
方法在回調(diào)中得到CameraDevice
. - 通過
CameraDevice.createCaptureSession()
在回調(diào)中獲取CameraCaptureSession
. - 構(gòu)建
CaptureRequest
, 有三種模式可選 預(yù)覽/拍照/錄像. - 通過
CameraCaptureSession
發(fā)送CaptureRequest
, capture表示只發(fā)一次請求, setRepeatingRequest表示不斷發(fā)送請求. - 拍照數(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)系如下圖:
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ù)會加上.