CameraX 錄影架構

擷取系統通常會錄製並壓縮影片和音訊 多工處理兩個資料流,然後將結果串流寫入磁碟。

影片和音訊擷取系統的概念圖
圖 1.影片和音訊擷取系統的概念圖。

在 CameraX 中,影片擷取解決方案是 VideoCapture敬上 用途

顯示相機 X 處理影片擷取應用實例的概念圖
圖 2.說明 CameraX 如何處理 VideoCapture 應用實例的概念圖表。

如圖 2 所示,CameraX 影片擷取包括幾個高階架構元件:

  • 影片來源:SurfaceProvider
  • 音訊來源:AudioSource
  • 用於對影片/音訊編碼及壓縮的兩個編碼器。
  • 一種媒體多工器,好讓這兩個串流多工傳輸。
  • 可寫入結果的檔案儲存工具。

VideoCapture API 簡化複雜的擷取引擎,並提供簡單易懂的 API 應用程式。

VideoCapture API 總覽

VideoCapture 是 CameraX 的應用實例,可單獨使用或搭配其他用途使用。特定支援的組合取決於 相機硬體功能,但 PreviewVideoCapture 屬於 對所有裝置進行有效用途組合

VideoCapture API 是由下列與應用程式通訊的物件組成:

  • VideoCapture 是頂層用途類別。VideoCapture 會透過 CameraSelector 和其他「CameraX 用途」繫結至 LifecycleOwner。如要進一步瞭解這些概念和使用方式,請參閱「CameraX 架構」
  • Recorder 是 VideoOutput 的實作,與 VideoCapture 緊密結合。Recorder 是用於執行影片和音訊擷取。應用程式會從 Recorder 建立錄製內容。
  • PendingRecording 可設定錄製內容,並提供啟用音訊及設定事件監聽器等選項。您必須使用 Recorder 才能建立 PendingRecordingPendingRecording 不會錄下任何內容。
  • Recording 會執行實際錄製內容。您必須使用 PendingRecording 才能建立 Recording

圖 3 說明這些物件之間的關係:

顯示影片擷取應用實例中發生的互動圖表
圖 3.顯示 VideoCapture 應用實例中發生的互動圖表。

圖例:

  1. 使用 QualitySelector 建立 Recorder
  2. 使用其中一種 OutputOptions 來設定 Recorder
  3. 如有需要,請使用 withAudioEnabled() 啟用音訊功能。
  4. 使用 VideoRecordEvent 事件監聽器呼叫 start() 來開始記錄。
  5. 請使用 Recording 上的 pause()/resume()/stop() 來控制錄製內容。
  6. 在事件監聽器中回應 VideoRecordEvents

詳細 API 清單位於原始碼中的 current.txt

使用 VideoCapture API

如要將 CameraX VideoCapture 用途整合至您的應用程式,請按照下列步驟操作:

  1. 繫結 VideoCapture
  2. 準備及設定錄製內容。
  3. 啟動和控制執行階段記錄內容。

以下各節將概述您在各個步驟中所需完成的事項, 端對端錄音

繫結 VideoCapture

如要繫結 VideoCapure 用途,請按照下列步驟操作:

  1. 建立 Recorder 物件。
  2. 建立 VideoCapture 物件。
  3. 繫結至 Lifecycle

CameraX VideoCapture API 採用建構工具的設計模式。應用程式會使用 Recorder.Builder 建立 Recorder。您也可以透過 QualitySelector 物件,設定 Recorder 的影片解析度。

CameraX Recorder 支援下列預先定義的 Qualities 影片解析度:

  • 4K UHD 超高畫質影片尺寸 (2160p) 的 Quality.UHD
  • Full HD 高畫質影片尺寸 (1080p) 的 Quality.FHD
  • HD 高畫質影片尺寸 (720p) 的 Quality.HD
  • SD 標準畫質影片尺寸 (480p) 的Quality.SD

請注意,CameraX 在應用程式授權後也可以選擇其他解析度。

每個選項的確切影片尺寸取決於相機和編碼器的功能。詳情請參閱 CamcorderProfile 的說明文件。

應用程式可透過建立 QualitySelector 來設定解析度。您可以使用下列任一方法建立 QualitySelector

  • 使用 fromOrderedList() 提供幾個偏好的解析度,並納入備用策略,以在系統無法支援偏好的解析度時採用。

    CameraX 可以根據所選相機的 功能,請參閱 QualitySelectorFallbackStrategy specification ,掌握更多詳細資訊。舉例來說,下列程式碼要求系統支援的 解析度,而如果沒有任何可支援要求的方法, 授權 CameraX 選擇最接近 Quality.SD 解析度的相機:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • 請先查詢相機功能,然後選擇支援的 使用 QualitySelector::from() 解析度:

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        Camera2CameraInfo
        .from(it)
        .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK
    }
    
    val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0])
    val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }
    
    // Use a simple ListView with the id of simple_quality_list_view
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })
    
        // Set up the user interaction to manually show or hide the system UI.
        setOnItemClickListener { _, _, position, _ ->
            // Inside View.OnClickListener,
            // convert Quality.* constant to QualitySelector
            val qualitySelector = QualitySelector.from(filteredQualities[position])
    
            // Create a new Recorder/VideoCapture for the new quality
            // and bind to lifecycle
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()
    
             // ...
        }
    }
    
    // A helper function to translate Quality to a string
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    
    

    請注意,從 QualitySelector.getSupportedQualities() 回傳的功能保證適用於 VideoCapture 用途或 VideoCapturePreview 用途的組合。與 ImageCaptureImageAnalysis 用途、CameraX 即使系統不支援必要的組合,繫結仍可能失敗 要求的相機

取得 QualitySelector 後,應用程式就能建立 VideoCapture 物件並執行繫結。請注意,此繫結與其他用途相同:

val recorder = Recorder.Builder()
    .setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

try {
    // Bind use cases to camera
    cameraProvider.bindToLifecycle(
            this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

請注意,bindToLifecycle() 會傳回 Camera 物件。如需進一步瞭解如何控制相機輸出內容 (例如縮放和曝光),請參閱這份指南

Recorder 會選取最適合系統的格式。最常見的影片轉碼器為 H.264 AVC),容器格式為 MPEG-4

設定及建立影片錄製

應用程式可透過 Recorder 建立錄製物件,執行影片和音訊擷取。應用程式會透過以下方式建立錄製內容 包括:

  1. 使用 prepareRecording() 設定 OutputOptions
  2. (選用) 啟用錄音功能。
  3. 使用 start() 註冊 VideoRecordEvent 監聽器,然後開始錄影。

呼叫 start() 函式時,Recorder 會回傳 Recording 物件。您的應用程式可使用此 Recording 物件來完成錄製或執行其他操作,例如暫停或繼續。

Recorder 一次支援一個 Recording 物件。在前一個 Recording 物件上呼叫 Recording.stop()Recording.close() 後,即可建立新的錄製內容。

以下將詳細介紹這些步驟。首先,應用程式會使用 Recorder.prepareRecording() 為錄製工具設定 OutputOptionsRecorder 支援下列 OutputOptions 類型:

  • 用來錄製 FileDescriptorFileDescriptorOutputOptions
  • 用來錄製 FileFileOutputOptions
  • 用來錄製 MediaStoreMediaStoreOutputOptions

所有 OutputOptions 類型都支援透過 setFileSizeLimit() 設定檔案大小上限。其他選項僅適用於個別輸出內容 類型,例如 FileDescriptorOutputOptionsParcelFileDescriptor

prepareRecording() 會傳回 PendingRecording 物件,也就是 中繼物件,可用來建立 Recording 物件。PendingRecording 是暫時性類別,在大部分情況下應該不會顯示,而且應用程式很少快取。

應用程式可進一步設定錄製內容,例如:

  • 啟用包含 withAudioEnabled() 的音訊。
  • 透過 start(Executor, Consumer<VideoRecordEvent>) 註冊監聽器,以便接收錄製內容。
  • 允許錄影時,在連接 VideoCapture 時持續錄影 即可重新繫結至另一個相機 PendingRecording.asPersistentRecording()

如要開始錄製,請呼叫 PendingRecording.start()。CameraX 會 將 PendingRecording 傳入 Recording,將錄製要求排入佇列, 並將新建立的 Recording 物件傳回應用程式 當對應的相機裝置開始錄製後,CameraX 就會傳送 VideoRecordEvent.EVENT_TYPE_START 事件。

以下範例說明如何將影片和音訊錄製為 MediaStore 檔案:

// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
        SimpleDateFormat(FILENAME_FORMAT, Locale.US)
                .format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
   put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
                              MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                              .setContentValues(contentValues)
                              .build()

// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
                .prepareRecording(context, mediaStoreOutput)
                .withAudioEnabled()
                .start(ContextCompat.getMainExecutor(this), captureListener)

根據預設,相機預覽畫面與前置鏡頭雙向同步 VideoCapture 依預設不會雙向同步。採用 CameraX 1.3 後 就能以鏡像方式投放影片 前置鏡頭預覽畫面和 錄製的影片相符。

MirrorMode 選項有三種:MIRROR_MODE_OFF、MIRROR_MODE_ON MIRROR_MODE_ON_FRONT_ONLY。如要對齊 相機預覽,Google 建議使用 MIROR_MODE_ON_FRONT_ONLY 並 後置鏡頭未啟用鏡像功能,但前置鏡頭已啟用 相機上如要進一步瞭解 MirrorMode,請參閱 MirrorMode constants

下列程式碼片段說明如何呼叫 VideoCapture.Builder.setMirrorMode()使用 MIRROR_MODE_ON_FRONT_ONLY。適用對象 詳情請參閱 setMirrorMode()

Kotlin


val recorder = Recorder.Builder().build()

val videoCapture = VideoCapture.Builder(recorder)
    .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
    .build()

useCases.add(videoCapture);

Java


Recorder.Builder builder = new Recorder.Builder();
if (mVideoQuality != QUALITY_AUTO) {
    builder.setQualitySelector(
        QualitySelector.from(mVideoQuality));
}
  VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

控制進行中的錄製

您可以使用以下方法暫停、繼續及停止進行中的 Recording

  • pause 可暫停目前進行中的錄製。
  • resume() 可恢復已暫停的進行中錄製。
  • stop() 可完成錄製,並清除任何相關聯的錄製物件。
  • mute()敬上 將目前的錄音檔靜音或取消靜音。

請注意,您可以呼叫 stop() 終止 Recording,無論如何 判斷錄製內容目前或進行中的錄製狀態。

如果您已透過EventListener PendingRecording.start()Recording 會透過宣告 方法是使用 VideoRecordEvent

  • VideoRecordEvent.EVENT_TYPE_STATUS 是用於記錄目前檔案大小和記錄時距等統計資料。
  • VideoRecordEvent.EVENT_TYPE_FINALIZE 是用於記錄結果,包括最終檔案的 URI 和其他相關錯誤等資訊。

應用程式收到表示錄製成功的 EVENT_TYPE_FINALIZE 後,您就可以透過 OutputOptions 中指定的位置存取錄製的影片。

其他資源

如要進一步瞭解 CameraX,請參閱下列其他資源: