キャプチャ システムでは通常、動画ストリームと音声ストリームを記録し、圧縮します。そしてこの 2 つのストリームを多重化し、そこで生成された結果のストリームをディスクに書き込みます。
CameraX における動画キャプチャのソリューションは、VideoCapture
のユースケースです。
図 2 に示すように、CameraX 動画キャプチャには高度なアーキテクチャのコンポーネントがいくつか含まれています。
- 動画のソース用の
SurfaceProvider
。 - 音声のソース用の
AudioSource
。 - 動画 / 音声をエンコードして圧縮する 2 つのエンコーダ。
- 2 つのストリームを多重化するメディア マルチプレクサ。
- 結果を書き出すファイル セーバー。
VideoCapture API は複雑なキャプチャ エンジンを抽象化し、はるかにシンプルで簡単な API をアプリに提供します。
VideoCapture API の概要
VideoCapture
は CameraX のユースケースであり、単独で、または他のユースケースと組み合わせて使用できます。具体的にサポートされる組み合わせはカメラのハードウェア機能によって異なりますが、Preview
と VideoCapture
はどのデバイスでも利用できるユースケースの組み合わせです。
VideoCapture API は、アプリと通信する次のオブジェクトで構成されています。
VideoCapture
は最上位のユースケース クラスです。VideoCapture
は、CameraSelector
と他の CameraX ユースケースを使ってLifecycleOwner
にバインドします。これらのコンセプトと使用法について詳しくは、CameraX のアーキテクチャをご覧ください。Recorder
は、VideoCapture
と密結合された VideoOutput の実装です。Recorder
は、動画と音声のキャプチャを実行するために使用されます。アプリはRecorder
を通じて録画を作成します。PendingRecording
は録画を設定し、音声の有効化やイベント リスナーの設定などを行えるようにします。PendingRecording
を作成するには、Recorder
を使用する必要があります。PendingRecording
は何も記録しません。Recording
が実際の録画を実行します。Recording
を作成するには、PendingRecording
を使用する必要があります。
図 3 は、これらのオブジェクト間の関係を示しています。
凡例:
QualitySelector
でRecorder
を作成します。- いずれかの
OutputOptions
でRecorder
を構成します。 - 必要に応じて、
withAudioEnabled()
を使用して音声を有効にします。 VideoRecordEvent
リスナーでstart()
を呼び出して、録画を開始します。- 録画を管理するには、
Recording
のpause()
、resume()
、stop()
を使用します。 - イベント リスナー内で
VideoRecordEvents
に応答します。
詳細な API リストは、ソースコード内の current.txt にあります。
VideoCapture API の使用
CameraX VideoCapture
のユースケースをアプリに統合するには、次の手順を行います。
VideoCapture
をバインドする。- 録画を準備、設定する。
- ランタイム録画を開始、制御する。
以降のセクションでは、エンドツーエンドの録画セッションを開始するために各ステップで実行できる操作の概要を説明します。
VideoCapture をバインドする
VideoCapure
ユースケースをバインドするには、次の手順を行います。
Recorder
オブジェクトを作成する。VideoCapture
オブジェクトを作成する。Lifecycle
にバインドする。
CameraX VideoCapture API はビルダーの設計パターンに従います。アプリは Recorder.Builder
を使用して Recorder
を作成します。また、QualitySelector
オブジェクトを通じて、Recorder
の動画解像度を設定することもできます。
CameraX Recorder
は、動画解像度用にあらかじめ定義された次の Qualities
に対応しています。
Quality.UHD
: 4K Ultra HD 動画サイズ(2160p)Quality.FHD
: フル HD 動画サイズ(1080p)Quality.HD
: HD 動画サイズ(720p)Quality.SD
: SD 動画サイズ(480p)
CameraX は、アプリで許可されている場合、他の解像度を選択することもできます。
それぞれの選択に対応する正確な動画サイズは、カメラとエンコーダの機能によって異なります。詳細については、CamcorderProfile
のドキュメントをご覧ください。
アプリは、QualitySelector
を作成することで解像度を設定できます。QualitySelector
は、次のいずれかの方法で作成できます。
fromOrderedList()
を使用して優先する解像度をいくつか提供し、優先する解像度がいずれもサポートされていない場合に使用する代替案を含めます。CameraX では、選択したカメラの機能に応じて最適な代替案を決定できます。詳細については、
QualitySelector
のFallbackStrategy 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
のユースケースまたはVideoCapture
とPreview
のユースケースの組み合わせで必ず動作するようになっています。ImageCapture
またはImageAnalysis
のユースケースをバインドしても、リクエストされたカメラが目的の組み合わせに対応していない場合、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
を通じて録画オブジェクトを作成して、動画と音声のキャプチャを実行できます。アプリでは、次の手順で録画を作成します。
prepareRecording()
でOutputOptions
を設定する。- (省略可)録音を有効にする。
start()
を使用してVideoRecordEvent
リスナーを登録し、動画キャプチャを開始する。
start()
関数を呼び出すと、Recorder
は Recording
オブジェクトを返します。アプリは、この Recording
オブジェクトを使用して、キャプチャの終了や、一時停止、再開などの他のアクションを実行できます。
Recorder
は、一度に 1 つの Recording
オブジェクトをサポートします。前の Recording
オブジェクトで Recording.stop()
または Recording.close()
を呼び出すと、新しい録画を開始できます。
これらのステップを詳しく説明します。まず、アプリで Recorder.prepareRecording()
を使用してレコーダーの OutputOptions
を設定します。Recorder
では、次の種類の OutputOptions
がサポートされています。
FileDescriptorOutputOptions
:FileDescriptor
にキャプチャするために使用します。FileOutputOptions
:File
にキャプチャするために使用します。MediaStoreOutputOptions
:MediaStore
にキャプチャするために使用します。
どのタイプの OutputOptions
でも、setFileSizeLimit()
を使用して最大ファイルサイズを設定できます。その他のオプションは、個々の出力タイプに応じて異なります。たとえば、ParcelFileDescriptor
は FileDescriptorOutputOptions
に固有です。
prepareRecording()
は PendingRecording
オブジェクトを返します。これは、対応する Recording
オブジェクトの作成に使われる中間オブジェクトです。PendingRecording
は、多くの場合は表示されない一時的なクラスであり、アプリでキャッシュに保存されることはほとんどありません。
アプリでは、次のように録画をさらに設定できます。
withAudioEnabled()
で音声を有効にする。start(Executor, Consumer<VideoRecordEvent>)
を使用して、録画イベントを受信するリスナーを登録する。PendingRecording.asPersistentRecording()
を使用して、アタッチされている VideoCapture が別のカメラに再バインドされている状態でも、引き続き録画できるようにする。
録画を開始するには、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 という 3 つのオプションがあります。カメラのプレビューに合わせるため、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()
に EventListener
を登録している場合、Recording
は VideoRecordEvent
を使用して通信を行います。
VideoRecordEvent.EVENT_TYPE_STATUS
は、現在のファイルサイズや録画された期間などの統計を記録するために使用されます。VideoRecordEvent.EVENT_TYPE_FINALIZE
は結果の記録に使用され、最終ファイルの URI や関連するエラーなどの情報を含みます。
録画セッションが完了したことを示す EVENT_TYPE_FINALIZE
をアプリで受信したら、OutputOptions
で指定された場所からキャプチャした動画にアクセスできます。
参考情報
CameraX について詳しくは、以下の参考情報をご確認ください。
- CameraX のスタートガイドの Codelab
- CameraX の公式サンプルアプリ
- 最新の CameraX Video Capture API リスト
- CameraX のリリースノート
- CameraX のソースコード