從 Android 5.0 開始,Google 引入了一套全新的相機框架 Camera2(android.hardware.camera2)而且廢棄了舊的相機框架 Camera1(android.hardware.Camera)。做爲一個專門從事相機應用開發的開發者來講,這一刻我等了過久了,Camera1 那寥寥無幾的 API 和極差的靈活性早已不能知足日益複雜的相機功能開發。Camera2 的出現給相機應用程序帶來了巨大的變革,由於它的目的是爲了給應用層提供更多的相機控制權限,從而構建出更高質量的相機應用程序。本文是 Camera2 教程的開篇做,本章將介紹如下幾個內容:android
本章涉及的代碼不多,由於咱們會在接下來的教程中深刻介紹 Camera2 的 API。框架
Camera2 的 API 模型被設計成一個 Pipeline(管道),它按順序處理每一幀的請求並返回請求結果給客戶端。下面這張來自官方的圖展現了 Pipeline 的工做流程,咱們會經過一個簡單的例子詳細解釋這張圖。async
Pipeline 示意圖性能
爲了解釋上面的示意圖,假設咱們想要同時拍攝兩張不一樣尺寸的圖片,而且在拍攝的過程當中閃光燈必須亮起來。整個拍攝流程以下:spa
一個新的 CaptureRequest 會被放入一個被稱做 Pending Request Queue 的隊列中等待被執行,當 In-Flight Capture Queue 隊列空閒的時候就會從 Pending Request Queue 獲取若干個待處理的 CaptureRequest,而且根據每個 CaptureRequest 的配置進行 Capture 操做。最後咱們從不一樣尺寸的 Surface 中獲取圖片數據而且還會獲得一個包含了不少與本次拍照相關的信息的 CaptureResult,流程結束。線程
相機功能的強大與否和硬件息息相關,不一樣廠商對 Camera2 的支持程度也不一樣,因此 Camera2 定義了一個叫作 Supported Hardware Level 的重要概念,其做用是將不一樣設備上的 Camera2 根據功能的支持狀況劃分紅多個不一樣級別以便開發者可以大概瞭解當前設備上 Camera2 的支持狀況。截止到 Android P 爲止,從低到高一共有 LEGACY、LIMITED、FULL 和 LEVEL_3 四個級別:設計
相機的全部操做和參數配置最終都是服務於圖像捕獲,例如對焦是爲了讓某一個區域的圖像更加清晰,調節曝光補償是爲了調節圖像的亮度。所以,在 Camera2 裏面全部的相機操做和參數配置都被抽象成 Capture(捕獲),因此不要簡單的把 Capture 直接理解成是拍照,由於 Capture 操做可能僅僅是爲了讓預覽畫面更清晰而進行對焦而已。若是你熟悉 Camera1,那你可能會問 setFlashMode()
在哪?setFocusMode()
在哪?takePicture()
在哪?告訴你,它們都是經過 Capture 來實現的。code
Capture 從執行方式上又被細分爲【單次模式】、【屢次模式】和【重複模式】三種,咱們來一一解釋下:教程
單次模式(One-shot):指的是隻執行一次的 Capture 操做,例如設置閃光燈模式、對焦模式和拍一張照片等。多個一次性模式的 Capture 會進入隊列按順序執行。接口
屢次模式(Burst):指的是連續屢次執行指定的 Capture 操做,該模式和屢次執行單次模式的最大區別是連續屢次 Capture 期間不容許插入其餘任何 Capture 操做,例如連續拍攝 100 張照片,在拍攝這 100 張照片期間任何新的 Capture 請求都會排隊等待,直到拍完 100 張照片。多組屢次模式的 Capture 會進入隊列按順序執行。
重複模式(Repeating):指的是不斷重複執行指定的 Capture 操做,當有其餘模式的 Capture 提交時會暫停該模式,轉而執行其餘被模式的 Capture,當其餘模式的 Capture 執行完畢後又會自動恢復繼續執行該模式的 Capture,例如顯示預覽畫面就是不斷 Capture 獲取每一幀畫面。該模式的 Capture 是全局惟一的,也就是新提交的重複模式 Capture 會覆蓋舊的重複模式 Capture。
CameraManager 是一個負責查詢和創建相機鏈接的系統服務,它的功能很少,這裏列出幾個 CameraManager 的關鍵功能:
CameraCharacteristics 是一個只讀的相機信息提供者,其內部攜帶大量的相機信息,包括表明相機朝向的 LENS_FACING
;判斷閃光燈是否可用的 FLASH_INFO_AVAILABLE
;獲取全部可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES
等等。若是你對 Camera1 比較熟悉,那麼 CameraCharacteristics 有點像 Camera1 的 Camera.CameraInfo
或者 Camera.Parameters
。
CameraDevice 表明當前鏈接的相機設備,它的職責有如下四個:
熟悉 Camera1 的人可能會說 CameraDevice 就是 Camera1 的 Camera 類,實則不是,Camera 類幾乎負責了全部相機的操做,而 CameraDevice 的功能則十分的單一,就是隻負責創建相機鏈接的事務,而更加細化的相機操做則交給了稍後會介紹的 CameraCaptureSession。
Surface 是一塊用於填充圖像數據的內存空間,例如你可使用 SurfaceView 的 Surface 接收每一幀預覽數據用於顯示預覽畫面,也可使用 ImageReader 的 Surface 接收 JPEG 或 YUV 數據。每個 Surface 均可以有本身的尺寸和數據格式,你能夠從 CameraCharacteristics 獲取某一個數據格式支持的尺寸列表。
CameraCaptureSession 實際上就是配置了目標 Surface 的 Pipeline 實例,咱們在使用相機功能以前必須先建立 CameraCaptureSession 實例。一個 CameraDevice 一次只能開啓一個 CameraCaptureSession,絕大部分的相機操做都是經過向 CameraCaptureSession 提交一個 Capture 請求實現的,例如拍照、連拍、設置閃光燈模式、觸摸對焦、顯示預覽畫面等等。
CaptureRequest 是向 CameraCaptureSession 提交 Capture 請求時的信息載體,其內部包括了本次 Capture 的參數配置和接收圖像數據的 Surface。CaptureRequest 能夠配置的信息很是多,包括圖像格式、圖像分辨率、傳感器控制、閃光燈控制、3A 控制等等,能夠說絕大部分的相機參數都是經過 CaptureRequest 配置的。值得注意的是每個 CaptureRequest 表示一幀畫面的操做,這意味着你能夠精確控制每一幀的 Capture 操做。
CaptureResult 是每一次 Capture 操做的結果,裏面包括了不少狀態信息,包括閃光燈狀態、對焦狀態、時間戳等等。例如你能夠在拍照完成的時候,經過 CaptureResult 獲取本次拍照時的對焦狀態和時間戳。須要注意的是,CaptureResult 並不包含任何圖像數據,前面咱們在介紹 Surface 的時候說了,圖像數據都是從 Surface 獲取的。
若是要我給出強有力的理由解釋爲何要使用 Camera2,那麼經過 Camera2 提供的高級特性能夠構建出更加高質量的相機應用程序應該是最佳理由了。
在開啓相機以前檢查相機信息
出於某些緣由,你可能須要先檢查相機信息再決定是否開啓相機,例如檢查閃光燈是否可用。在 Caemra1 上,你沒法在開機相機以前檢查詳細的相機信息,由於這些信息都是經過一個已經開啓的相機實例提供的。在 Camera2 上,咱們有了和相機實例徹底剝離的 CameraCharacteristics 實例專門提供相機信息,因此咱們能夠在不開啓相機的前提下檢查幾乎全部的相機信息。
在不開啓預覽的狀況下拍照
在 Camera1 上,開啓預覽是一個很重要的環節,由於只有在開啓預覽以後才能進行拍照,所以即便顯示預覽畫面與實際業務需求相違背的時候,你也不得不開啓預覽。而 Camera2 則不強制要求你必須先開啓預覽才能拍照。
一次拍攝多張不一樣格式和尺寸的圖片
在 Camera1 上,一次只能拍攝一張圖片,更不一樣談多張不一樣格式和尺寸的圖片了。而 Camera2 則支持一次拍攝多張圖片,甚至是多張格式和尺寸都不一樣的圖片。例如你能夠同時拍攝一張 1440x1080 的 JPEG 圖片和一張全尺寸的 RAW 圖片。
控制曝光時間
在暗環境下拍照的時候,若是可以適當延長曝光時間,就可讓圖像畫面的亮度獲得提升。在 Camera2 上,你能夠在規定的曝光時長範圍內配置拍照的曝光時間,從而實現拍攝長曝光圖片,你甚至能夠延長每一幀預覽畫面的曝光時間讓整個預覽畫面在暗環境下也能保證必定的亮度。而在 Camera1 上你只能 YY 一下。
連拍
連拍 30 張圖片這樣的功能在 Camera2 出現以前恐怕只有系統相機才能作到了(經過 OpenGL 截取預覽畫面的作法除外),也多是出於這個緣由,市面上的第三方相機無一例外都不支持連拍。有了 Camera2,你徹底可讓你的相機應用程序支持連拍功能,甚至是連續拍 30 張使用不一樣曝光時間的圖片。
靈活的 3A 控制
3A(AF、AE、AWB)的控制在 Camera2 上獲得了最大化的放權,應用層能夠根據業務需求靈活配置 3A 流程而且實時獲取 3A 狀態,而 Camera1 在 3A 的控制和監控方面提供的接口則要少了不少。例如你能夠在拍照前進行 AE 操做,而且監聽本此次拍照是否點亮閃光燈。
若是你熟悉 Camera1,而且打算從 Camera1 遷移到 Camera2 的話,但願如下幾個建議能夠對你起到幫助:
Camera1 嚴格區分了預覽和拍照兩個流程,而 Camera2 則把這兩個流程都抽象成了 Capture 行爲,只不過一個是不斷重複的 Capture,一個是一次性的 Capture 而已,因此建議你不要帶着過多的 Camera1 思惟使用 Camera2,避免由於思惟上的束縛而沒法充分利用 Camera2 靈活的 API。
如同 Camera1 同樣,Camera2 的一些 API 調用也會耗時,因此建議你使用獨立的線程執行全部的相機操做,儘可能避免直接在主線程調用 Camera2 的 API,HandlerThread 是一個不錯的選擇。
Camera2 全部的相機操做均可以註冊相關的回調接口,而後在不一樣的回調方法裏寫業務邏輯,這可能會讓你的代碼由於不夠線性而錯綜複雜,建議你能夠嘗試使用子線程的阻塞方式來儘量地保證代碼的線性執行(熟悉 Dart 的人必定很喜歡它的 async 和 await 操做)。例如在子線程阻塞等待 CaptureResult,而後繼續執行後續的操做,而不是將代碼拆分到到 CaptureCallback.onCaptureCompleted()
方法裏。
你能夠認爲 Camera1 是 Camera2 的一個子集,也就是說 Camera1 能作的事情 Camera2 必定能作,反過來則不必定行得通。
若是你的應用程序須要同時兼容 Camera1 和 Camera2,我的建議分開維護,由於 Camera1 蹩腳的 API 設計極可能讓 Camera2 靈活的 API 沒法獲得充分的發揮,另外將兩個設計上徹底不兼容的東西攪和在一塊兒帶來的痛苦可能遠大於其帶來便利性,多寫一些冗餘的代碼也許還更開心。
官方說 Camera2 的性能會更好,這句話聽聽就好,起碼在較早期的一些機器上運行 Camera2 的性能並無比 Camera1 好。
當設備的 Supported Hardware Level 低於 FULL 的時候,建議仍是使用 Camera1,由於 FULL 級別如下的 Camera2 能提供的功能幾乎和 Camera1 同樣,因此倒不如選擇更加穩定的 Camera1。
本章到此結束,主要是介紹了 Camera2 的一些基礎概念,讓你們可以基本瞭解 Camera2 的工做流程和基礎概念,而且知道使用 Camera2 可以作些什麼。若是你對 Camera2 仍是感到很陌生,沒關係,後續的教程會帶領你們逐步深刻了解 Camera2。