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() 設定檔案大小上限。其他選項特定適用於個別輸出類型,例如 ParcelFileDescriptor 適用於 FileDescriptorOutputOptions

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

下列程式碼片段說明如何使用 MIRROR_MODE_ON_FRONT_ONLY 呼叫 VideoCapture.Builder.setMirrorMode()。詳情請參閱 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

如果您已使用 PendingRecording.start() 註冊 EventListenerRecording 就會使用 VideoRecordEvent 進行通訊。

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

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

其他資源

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