注意:本頁面所述是指 Camera2 套件。除非應用程式需要 Camera2 的特定低階功能,否則建議使用 CameraX。CameraX 和 Camera2 均支援 Android 5.0 (API 級別 21) 以上版本。
一部 Android 裝置可以有多部相機。每部攝影機都是
CameraDevice
、
CameraDevice
可同時輸出多個串流。
之所以能這麼做,是因為
來自 CameraDevice
,最適合特定工作,例如顯示
觀看器則用於拍照或錄影
串流會做為處理原始影格的平行管道
或從鏡頭外流,一次一個影格:
![](https://developer.android.google.cn/static/images/training/camera/camera2/capture-sessions-requests-1.png?authuser=19&hl=zh-tw)
平行處理系統顯示, CPU、GPU 或其他處理器提供的處理效能如果 管道無法跟上傳入的影格,模型會開始捨棄這些影格。
每個管道都有自己的輸出格式。收到的原始資料
自動轉換為適當的
輸出格式 (根據隱含邏輯)
與各個管道相關聯這個網頁的 CameraDevice
中所用的
程式碼範例範圍不同,因此您可以先列舉
所有可用鏡頭,才能繼續操作
您可以使用 CameraDevice
建立
CameraCaptureSession
,
專屬於該 CameraDevice
CameraDevice
必須接收
使用 CameraCaptureSession
為每個原始影格設定不同的影格設定
設定可指定自動對焦、光圈、效果、
以及曝光由於硬體限制,只有單一設定
隨時都在相機感應器上啟動,稱為
active 配置。
然而,串流用途案例強化及擴充先前的 CameraDevice
使用方式
串流擷取會議,讓您最佳化攝影機串流,
適合特定用途例如,在執行最佳化作業時可延長電池續航力
視訊通話。
CameraCaptureSession
說明繫結至
CameraDevice
。工作階段建立後,就無法新增或移除管道。
CameraCaptureSession
會將佇列中的
CaptureRequest
、
這會成為使用中的設定
CaptureRequest
會將設定新增至佇列,並選取一項設定,超過
一個或所有的可用管道
CameraDevice
。您可以在擷取的生命週期內傳送許多擷取要求
會很有幫助每項要求都可以變更使用中的設定和一組輸出內容
用於接收原始映像檔的管道
透過串流用途提升效能
串流用途是一種提升 Camera2 擷取效能的方法 工作階段。可讓硬體裝置提供有關調整參數的資訊 這能針對您的特定工作,提供更好的相機體驗。
這個
可讓相機裝置最佳化相機硬體和軟體管道
根據每個串流的使用者情境
建立適當的提示進一步瞭解串流用途
如需記錄,請參閱 setStreamUseCase
。
「串流用途」可讓您指定特定相機串流在「串流」用途中的使用方式
設定範本外
CameraDevice.createCaptureRequest()
。以利相機硬體最佳化
「調整」、「感應器模式」或「相機感應器設定」等參數
並在品質或延遲方面做出取捨
串流用途包括:
DEFAULT
:涵蓋所有現有的應用程式行為。等同於不要使用 設定任何串流用途PREVIEW
:建議用於觀景窗或應用程式內圖片分析。STILL_CAPTURE
:已完成最佳化調整,具備優異的高解析度拍攝功能,不會 仍能維持類似預覽的畫面更新率VIDEO_RECORD
:最佳化處理高畫質影片,包括高畫質影片 圖片穩定功能 (如果裝置支援這項功能且由應用程式啟用)。 這個選項可能會導致輸出影格的即時延遲很大 以提供最佳的穩定功能或其他處理程序。VIDEO_CALL
:建議用於長時間執行的相機,且耗電 疑慮。PREVIEW_VIDEO_STILL
:建議用於社群媒體應用程式或單一串流用途 用途這是多用途的串流VENDOR_START
:適用於原始設備製造商 (OEM) 定義的用途。
建立 CameraCaptureSession
如要建立相機工作階段,請提供一或多個輸出緩衝區 應用程式可寫入輸出影格每個緩衝區都代表一個管道。您必須 請先操作再使用相機 裝置的內部管道,並分配記憶體緩衝區來傳送影格 所需的輸出目標
下列程式碼片段顯示如何準備相機工作階段
一個屬於
SurfaceView
和另一個
ImageReader
。將 PREVIEW
串流用途新增至 previewSurface
和
STILL_CAPTURE
訊息串用途
有了 imReaderSurface
的保護殼,裝置硬體就能針對這些串流內容進行最佳化
進一步
Kotlin
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewById<SurfaceView>(...) val imageReader = ImageReader.newInstance(...) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: List<Surface>){ val configs = mutableListOf<OutputConfiguration>() val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } ... device.createCaptureSession(session) }
Java
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis // 3. RenderScript.Allocation, if you want to do parallel processing // 4. OpenGL Texture or TextureView, although discouraged for maintainability reasons Surface surfaceView = findViewById<SurfaceView>(...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); List<Surface> targets = Arrays.asList(previewSurface, imageSurface); // Create a capture session using the predefined targets; this also involves defining the // session state callback to be notified of when the session is ready private void configureSession(CameraDevice device, List<Surface> targets){ ArrayList<OutputConfiguration> configs= new ArrayList() String streamUseCase= CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL for(Surface s : targets){ OutputConfiguration config = new OutputConfiguration(s) config.setStreamUseCase(String.toLong(streamUseCase)) configs.add(config) } device.createCaptureSession(session) }
在此階段,您尚未定義攝影機的使用中配置。 設定工作階段後,您就能建立及調度擷取內容 來執行這項要求
輸入到輸入緩衝區時,該轉換會套用
取決於每個目標的類型,且必須是
Surface
。Android 架構知道
將目前配置中的原始圖片轉換成
每個目標。轉換是由像素格式和
特定 Surface
。
這個架構會盡可能嘗試最佳做法,但有些Surface
可能無法使用,因此造成
未建立,您在分派要求時擲回執行階段錯誤,或
效能會降低該架構可保證
裝置、介面和請求參數的組合。
createCaptureSession()
敬上
提供了更多資訊
單一擷取要求
每個影格使用的設定都編碼在 CaptureRequest
中,
傳送至相機。如要建立擷取要求,您可以使用
預先定義
templates、
或使用 TEMPLATE_MANUAL
取得完整控制。當您選擇
範本,您必須提供一或多個輸出緩衝區
要求。只能使用擷取當下已定義的緩衝區
要使用的工作階段
擷取要求會使用
建構工具模式
讓開發人員有機會
選項,包括
自動曝光
自動對焦,
和
光圈。
設定欄位前,請確定
撥號
CameraCharacteristics.getAvailableCaptureRequestKeys()
敬上
檢查適合的鏡頭,確認系統支援您要的值
例如:可用的自動曝光
。
使用範本為 SurfaceView
建立擷取要求
以便直接預覽,不需修改
CameraDevice.TEMPLATE_PREVIEW
:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequest.addTarget(previewSurface);
定義擷取要求後,您現在可以調度 我剛剛回答:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed // capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null);
當輸出影格放入特定緩衝區時,
回呼
在許多情況下還會進行其他回呼,例如:
ImageReader.OnImageAvailableListener
、
其中包含的影格處理完畢後就會觸發時間
便可以從指定的緩衝區擷取圖片資料。
重複擷取要求
單一鏡頭要求很簡單,不過如要顯示即時影像, 就沒有什麼幫助在這種情況下,您必須收到 連續串流多個影格,而不只是單一影格。下列程式碼片段 會說明如何 這段課程:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null);
重複拍攝要求會導致相機裝置持續擷取
使用所提供的設定 CaptureRequest
控制圖像。Camera2 API
使用者也可以將自己傳到攝影機的影片
重複 CaptureRequests
,如以上所示
Camera2 範例
託管在 GitHub 上此外,還能擷取
重複播放快速連拍 CaptureRequests
的高速 (慢動作) 影片
Camera2 慢動作影片範例應用程式中所示
。
交錯擷取要求
如要在重複擷取要求處於啟用狀態時,傳送第二個擷取要求, 例如顯示觀景窗及讓使用者拍攝相片,您就不需要這麼做 停止進行中的重複要求而是發出不重複拍攝 持續執行。
您使用的任何輸出緩衝區都必須設為相機工作階段的一部分 請務必建立設定檔重複要求的優先順序低於 可使用單一影格或爆發要求,讓以下範例可運作:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it CaptureRequest.Builder repeatingRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); repeatingRequest.addTarget(previewSurface); session.setRepeatingRequest(repeatingRequest.build(), null, null); // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily CaptureRequest.Builder singleRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); singleRequest.addTarget(imReaderSurface); session.capture(singleRequest.build(), null, null);
但這種方法存在缺點: 不會產生單一要求下圖中,如果 A 是重複的 擷取要求,B 則是單一影格擷取要求,這就是 會處理要求佇列:
![](https://developer.android.google.cn/static/images/training/camera/camera2/capture-sessions-requests-2.png?authuser=19&hl=zh-tw)
在上次發出要求之間的延遲時間則無法保證 要求 B 之前 A,並在下次使用 A 時啟用 所以可能會出現一些略過的影格載入 Google 試算表時 如何減少這個問題:
從要求 A 新增輸出目標,以要求 B。如此一來 B 的影格已準備就緒,系統會將它複製到 A 的輸出目標中。 舉例來說,執行影片快照來維持 影格速率穩定在上述程式碼中
singleRequest.addTarget(previewSurface)
,然後再建立要求。請針對這種情況,設計合適的範本組合 例如零快門延遲