在Google 推出Android 5.0的時候, Android Camera API 版本升級到了API2(android.hardware.camera2), 以前使用的API1(android.hardware.camera)就被標爲 Deprecated 了. Camera API2相較於API1有很大不一樣, 而且API2是爲了配合HAL3進行使用的, API2有不少API1不支持的特性, 好比:android
上面列舉的只是一部分, 而且實際功能支持狀況要看HAL層是否完成了相關功能, 也就是說API有不少功能來知足拍照/錄像需求, 但實際是否能用和具體設備有關.git
在API架構方面, Camera2和以前的Camera有很大區別, APP和底層Camera以前能夠想象成用管道方式鏈接, 以下圖:github
如上圖所示, Camera APP 經過CameraCaptureSession發送CaptureRequest, CameraDevices收到請求後返回對應數據到對應的Surface,預覽數據通常都是到TextureView, 拍照數據則在ImageReader中, 總體來講就是一個請求--響應過程, 請求完成後, 能夠在回調中查詢到相應的請求參數和CameraDevice當前狀態, 總的來講, Camera2中預覽/拍照/錄像數據統一由Surface來接收, CaptureRequest表明請求控制的Camera參數, CameraMetadata(CaptureResult)則表示當前返回幀中Camera使用的參數以及當前狀態.架構
API使用流程大致以下:ui
context.getSystemService(Context.CAMERA_SERVICE)
獲取CameraManager
.CameraManager .open()
方法在回調中獲得CameraDevice
.CameraDevice.createCaptureSession()
在回調中獲取CameraCaptureSession
.CaptureRequest
, 有三種模式可選 預覽/拍照/錄像.CameraCaptureSession
發送CaptureRequest
, capture表示只發一次請求, setRepeatingRequest表示不斷髮送請求.ImageReader.OnImageAvailableListener
回調中獲取, CaptureCallback
中則可獲取拍照實際的參數和Camera當前狀態.上面說過, 不是因此手機都支持完整的Camera2功能, 如今都2018了, Camera2出來都有4年左右了, 但估計還有些中低端手機使用的HAL1, 使用HAL1就會致使Camera2一些高級功能都無法使用了, 下面講一下如何查詢設備對應Camera2的支持狀況.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件層面支持的Camera2功能等級, 主要分爲5個等級:google
LEVEL_LEGACY: 向後兼容模式, 若是是此等級, 基本沒有額外功能, HAL層大機率就是HAL1(我遇到過的都是)
LEVEL_LIMITED: 有最基本的功能, 還支持一些額外的高級功能, 這些高級功能是LEVEL_FULL的子集
LEVEL_FULL: 支持對每一幀數據進行控制,還支持高速率的圖片拍攝
LEVEL_3: 支持YUV後處理和Raw格式圖片拍攝, 還支持額外的輸出流配置
LEVEL_EXTERNAL: API28中加入的, 應該是外接的攝像頭, 功能和LIMITED相似spa
各個等級從支持的功能多少排序爲: LEGACY < LIMITED < FULL < LEVEL_3
獲取代碼以下:code
// 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
上面講的幾個等級是從總體上說明Camera2支持狀況, 咱們還能夠查詢更多細節功能,REQUEST_AVAILABLE_CAPABILITIES 則能夠知道具體支持哪些實際功能, 具體有以下功能:排序
BACKWARD_COMPATIBLE | READ_SENSOR_SETTINGS |
---|---|
MANUAL_SENSOR | BURST_CAPTURE |
MANUAL_POST_PROCESSING | YUV_REPROCESSING |
RAW | DEPTH_OUTPUT |
PRIVATE_REPROCESSING | CONSTRAINED_HIGH_SPEED_VIDEO |
各個功能具體含義請參考官網的解釋 :官網 的解釋, 我沒有深刻研究.
大多數支持等級爲 LEGACY 的設備, 只支持 BACKWARD_COMPATIBLE , 也就是說前面提到的Camera2新功能都不支持......圖片
在使用Camera2 API過程當中, 設置測光和對焦區域這部分剛開始一直不知道該怎麼寫代碼, 後面折騰了一番, 終於找到正確的方法了, 在此記錄一下.
AE/AF 區域須要經過用戶在屏幕上的點擊位置來進行設置, 所以咱們要作的就是將屏幕點擊點映射到底層對焦和測光位置點, 同時發送請求觸發對焦這個動做. 首先要知道一點就是, 屏幕點擊點座標和Camera底層座標不是對應的, 基本關係以下圖:
AF_Coordinate.jpg
x,y座標系表示屏幕座標, x1,y1表示Camera對應座標
可見屏幕點擊和底層有個90度旋轉關係, 實際設置AF/AE區域步驟以下:
1.首先獲取SENSOR_INFO_ACTIVE_ARRAY_SIZE
, 即底層Camera座標點的範圍, API中經過一個Rect
表示
characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
獲得``Rect```一般爲支持的最大圖片尺寸, 好比(0, 0, 4160, 3120)
2.座標映射
咱們須要將屏幕點擊點映射到 SENSOR_INFO_ACTIVE_ARRAY_SIZE
對應的Rect
中, 從圖中能夠很容易看出, 座標轉換關係爲 : x1 = y , y1 = previewWidth - x
, 座標轉換完成後只需乘以 圖片寬度/預覽寬度
的比例便可獲得轉換後的座標, 而後以座標點爲中心生成一個矩形, 這個矩形就是咱們要的結果, 實際代碼片斷以下:
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; // 座標轉換 float newX = currentY; float newY = previewWidth - currentX; // 大小轉換 leftPos = (int) ((newX / previewHeight) * rect.right); topPos = (int) ((newY / previewWidth) * rect.bottom); // 以座標點爲中心生成一個矩形, 須要防止上下左右的值溢出 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()); // 構造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; }
注: 此處座標映射能夠經過Matrix進行, 熟悉Matrix的同窗能夠嘗試下
3.設置AE/AF區域並觸發對焦, 代碼片斷以下
public void startControlAFRequest(MeteringRectangle rect, CameraCaptureSession.CaptureCallback captureCallback) { MeteringRectangle[] rectangle = new MeteringRectangle[]{rect}; // 對焦模式必須設置爲AUTO mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_AUTO); //AE mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS,rectangle); //AF 此處AF和AE用的同一個rect, 實際AE矩形面積比AF稍大, 這樣測光效果更好 mPreviewBuilder.set(CaptureRequest.CONTROL_AF_REGIONS,rectangle); try { // AE/AF區域設置經過setRepeatingRequest不斷髮請求 mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } //觸發對焦 mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CaptureRequest.CONTROL_AF_TRIGGER_START); try { //觸發對焦經過capture發送請求, 由於用戶點擊屏幕後只需觸發一次對焦 mSession.capture(mPreviewBuilder.build(), captureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
Camera2 API和舊的Camera API區別很大, 剛開始用可能會很不習慣, 但Camera2有不少優點, 提供了很是多的參數供咱們控制, 後面API1可能會被移除, 因此能夠儘早將項目用Camera2重寫, 另外若是對API和HAL版本對應關係不清楚的, 能夠參考我以前寫的文章 Android Camera API和HAL版本對應關係.
基於Camera2 API, 我寫了個Demo, 功能還算完善, 有興趣的能夠看下: Github: Camera2, 目前沒有錄像功能, 後續會加上.