このページでは、CameraX の構造、API との連携方法、ライフサイクルとの連携方法、ユースケースを組み合わせる方法を含め、CameraX のアーキテクチャについて説明します。
CameraX の構造
CameraX を使用すると、ユースケースと呼ばれる抽象化を通じてデバイスのカメラとインターフェースすることができます。以下のユースケースを利用できます。
- プレビュー: プレビューを表示するためのサーフェスを受け入れます(
PreviewView
など)。 - 画像解析: CPU からアクセス可能な解析用(機械学習用など)のバッファを提供します。
- 画像キャプチャ: 写真をキャプチャして保存します。
- 動画キャプチャ:
VideoCapture
で動画と音声をキャプチャします。
ユースケースは、組み合わせて同時にアクティブにすることができます。たとえばアプリにおいて、プレビュー ユースケースを使用してカメラから見える画像をユーザーが確認できるようにし、画像解析ユースケースで被写体の人が笑顔かどうかを判断して、画像キャプチャ ユースケースで被写体の人が笑顔になったときに写真を撮る、といったことが可能です。
API モデル
ライブラリを使用する場合は以下を指定します。
- 使用するユースケースと設定オプション
- 出力データの処理方法(リスナーをアタッチする)
- カメラを有効にするタイミング、データを生成するタイミングなどの対象とするフロー(ユースケースを Android アーキテクチャのライフサイクルにバインドする)
ユースケースは set()
メソッドを使用して設定され、build()
メソッドで確定されます。各ユースケース オブジェクトは、ユースケースに固有の一連の API を提供します。たとえば、画像キャプチャのユースケースは、takePicture()
メソッドの呼び出しを提供します。
アプリでは、onResume()
と onPause()
で明示的に開始および停止メソッドを呼び出すのではなく、cameraProvider.bindToLifecycle()
を使用して、カメラを関連付けるライフサイクルを指定します。
このライフサイクルからカメラのキャプチャ セッションを設定するタイミングが CameraX に通知され、ライフサイクルの遷移に合わせてカメラの状態が適切に変更されます。
各ユースケースの実装手順については、プレビューを実装する、画像を解析する、画像キャプチャ、動画キャプチャをご覧ください。
API モデルの例
プレビュー ユースケースは、ディスプレイ用の Surface
を操作します。アプリは次のコードを使用して、設定オプションが指定されたユースケースを作成します。
Kotlin
val preview = Preview.Builder().build() val viewFinder: PreviewView = findViewById(R.id.previewView) // The use case is bound to an Android Lifecycle with the following code val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // PreviewView creates a surface provider and is the recommended provider preview.setSurfaceProvider(viewFinder.getSurfaceProvider())
Java
Preview preview = new Preview.Builder().build(); PreviewView viewFinder = findViewById(R.id.view_finder); // The use case is bound to an Android Lifecycle with the following code Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview); // PreviewView creates a surface provider, using a Surface from a different // kind of view will require you to implement your own surface provider. preview.previewSurfaceProvider = viewFinder.getSurfaceProvider();
その他のサンプルコードについては、CameraX の公式サンプルアプリをご覧ください。
CameraX のライフサイクル
CameraX はライフサイクルを監視して、カメラを起動するタイミング、キャプチャ セッションを作成するタイミング、停止およびシャットダウンするタイミングを決定します。ユースケースの API は、進行状況を監視するためのメソッド呼び出しとコールバックを提供します。
ユースケースを組み合わせるで説明するように、ユースケースを組み合わせて 1 つのライフサイクルにまとめることができます。組み合わせることができないユースケースをアプリでサポートする必要がある場合は、以下のいずれかを行うことができます。
- 互換性のあるユースケースを複数のフラグメントにグループ化して、フラグメントを切り替える
- カスタムのライフサイクル コンポーネントを作成し、それを使ってカメラのライフサイクルを手動で管理する
ビューとカメラの各ユースケースのライフサイクル オーナーを分ける場合(カスタム ライフサイクルを使用する場合やフラグメントを保持する場合)は、すべてのユースケースを CameraX からバインド解除する必要があります。そのためには、ProcessCameraProvider.unbindAll()
を使用するか、または各ユースケースを個別にバインド解除します。また、ユースケースをライフサイクルにバインドしている場合は、キャプチャ セッションの開始と終了、ユースケースのバインド解除を CameraX に管理させることができます。
すべてのカメラ機能が 1 つのライフサイクル対応コンポーネント(AppCompatActivity
や AppCompat
フラグメントなど)のライフサイクルに対応している場合、使用するすべてのユースケースをバインドするときにそのコンポーネントのライフサイクルを利用することで、ライフサイクル対応コンポーネントがアクティブなときにカメラ機能をすぐに使用できます。アクティブでないときは、安全にカメラ機能を破棄してリソースの消費を避けられます。
カスタムの LifecycleOwner
高度な手法として、標準の Android LifecycleOwner
を使用する代わりに、カスタムの LifecycleOwner
を作成することで、CameraX のセッション ライフサイクルをアプリで明示的に制御できます。
次のコードサンプルは、シンプルなカスタムの LifecycleOwner を作成する方法を示しています。
Kotlin
class CustomLifecycle : LifecycleOwner { private val lifecycleRegistry: LifecycleRegistry init { lifecycleRegistry = LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED) } ... fun doOnResume() { lifecycleRegistry.markState(State.RESUMED) } ... override fun getLifecycle(): Lifecycle { return lifecycleRegistry } }
Java
public class CustomLifecycle implements LifecycleOwner { private LifecycleRegistry lifecycleRegistry; public CustomLifecycle() { lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } ... public void doOnResume() { lifecycleRegistry.markState(State.RESUMED); } ... public Lifecycle getLifecycle() { return lifecycleRegistry; } }
アプリでこの LifecycleOwner
を使用すると、コード内の任意の位置で状態遷移を行うことができます。この機能をアプリに実装する方法について詳しくは、カスタムの LifecycleOwner の実装をご覧ください。
ユースケースを同時に実行する
複数のユースケースを同時に実行できます。ユースケースはライフサイクルに連続的にバインドできますが、CameraProcessProvider.bindToLifecycle()
の 1 回の呼び出しですべてのユースケースをバインドするほうが効率的です。設定の変更に関するおすすめの方法について詳しくは、設定の変更への対処をご覧ください。
次のコードサンプルでは、アプリで 2 つのユースケースを作成して同時に実行するように指定しています。また、両方のユースケースに使用するライフサイクルを指定して、どちらもそのライフサイクルに従って開始および停止するようにしています。
Kotlin
private lateinit var imageCapture: ImageCapture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener(Runnable { // Camera provider is now guaranteed to be available val cameraProvider = cameraProviderFuture.get() // Set up the preview use case to display camera preview. val preview = Preview.Builder().build() // Set up the capture use case to allow users to take photos. imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() // Choose the camera by requiring a lens facing val cameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_FRONT) .build() // Attach use cases to the camera with the same lifecycle owner val camera = cameraProvider.bindToLifecycle( this as LifecycleOwner, cameraSelector, preview, imageCapture) // Connect the preview use case to the previewView preview.setSurfaceProvider( previewView.getSurfaceProvider()) }, ContextCompat.getMainExecutor(this)) }
Java
private ImageCapture imageCapture; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PreviewView previewView = findViewById(R.id.previewView); ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { // Camera provider is now guaranteed to be available ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // Set up the view finder use case to display camera preview Preview preview = new Preview.Builder().build(); // Set up the capture use case to allow users to take photos imageCapture = new ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build(); // Choose the camera by requiring a lens facing CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(lensFacing) .build(); // Attach use cases to the camera with the same lifecycle owner Camera camera = cameraProvider.bindToLifecycle( ((LifecycleOwner) this), cameraSelector, preview, imageCapture); // Connect the preview use case to the previewView preview.setSurfaceProvider( previewView.getSurfaceProvider()); } catch (InterruptedException | ExecutionException e) { // Currently no exceptions thrown. cameraProviderFuture.get() // shouldn't block since the listener is being called, so no need to // handle InterruptedException. } }, ContextCompat.getMainExecutor(this)); }
次の構成の組み合わせはサポートが保証されています(プレビューまたは動画キャプチャが必要であるが、同時に両方が必要でない場合)。
プレビューまたは動画キャプチャ | 画像キャプチャ | 分析 | 説明 |
---|---|---|---|
プレビューの表示または動画の撮影、写真の撮影、画像ストリームの解析を行います。 | |||
写真の撮影、画像ストリームの解析を行います。 | |||
プレビューの表示または動画の撮影、写真の撮影を行います。 | |||
プレビューの表示または動画の撮影、画像ストリームの解析を行います。 |
プレビューと動画キャプチャの両方が必要な場合、以下のユースケースの組み合わせが条件付きでサポートされます。
プレビュー | 動画キャプチャ | 画像キャプチャ | 分析 | 特別な要件 |
---|---|---|---|---|
すべてのカメラで保証されます | ||||
LIMITED(またはそれ以上)のカメラデバイス。 | ||||
LEVEL_3(またはそれ以上)のカメラデバイス。 |
加えて次の点にもご注意ください。
- すべてのユースケースは単独で機能します。たとえば、アプリはプレビューを使用せずに動画を撮影できます。
- 拡張機能が有効になっている場合、
ImageCapture
とPreview
の組み合わせのみ、機能することが保証されます。OEM の実装によっては、ImageAnalysis
も追加できない場合があります。VideoCapture
ユースケースでは拡張機能を有効にできません。詳しくは、拡張機能のリファレンス ドキュメントをご覧ください。 - カメラの機能によっては、一部のカメラで低解像度モードで組み合わせをサポートする場合もありますが、高解像度では同じ組み合わせをサポートできない場合があります。
サポートされているハードウェア レベルは Camera2CameraInfo
から取得できます。たとえば、次のコードでは、デフォルトの背面カメラが LEVEL_3
のデバイスかどうかを確認しています。
Kotlin
@androidx.annotation.OptIn(ExperimentalCamera2Interop::class) fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.availableCameraInfos) .firstOrNull() ?.let { Camera2CameraInfo.from(it) } ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 } return false }
Java
@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class) Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { List\filteredCameraInfos = CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.getAvailableCameraInfos()); if (!filteredCameraInfos.isEmpty()) { return Objects.equals( Camera2CameraInfo.from(filteredCameraInfos.get(0)).getCameraCharacteristic( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL), CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3); } } return false; }
権限
アプリには CAMERA
権限が必要です。画像をファイルに保存するには、Android 10 以降を実行しているデバイスを除き、WRITE_EXTERNAL_STORAGE
権限も必要になります。
アプリの権限の設定方法について詳しくは、アプリの権限をリクエストするをご覧ください。
要件
CameraX には以下の最小バージョン要件があります。
- Android API レベル 21
- Android アーキテクチャ コンポーネント 1.1.1
ライフサイクル対応のアクティビティには、FragmentActivity
または AppCompatActivity
を使用します。
依存関係の宣言
CameraX への依存関係を追加するには、Google の Maven リポジトリをプロジェクトに追加する必要があります。
プロジェクトの settings.gradle
ファイルを開き、下記のように google()
リポジトリを追加します。
Groovy
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Android ブロックの末尾に以下を追加します。
Groovy
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
Kotlin
android { compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
アプリの各モジュールの build.gradle
ファイルに以下を追加します。
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.3.0-alpha04" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" // If you want to additionally use the CameraX Lifecycle library implementation "androidx.camera:camera-lifecycle:${camerax_version}" // If you want to additionally use the CameraX VideoCapture library implementation "androidx.camera:camera-video:${camerax_version}" // If you want to additionally use the CameraX View class implementation "androidx.camera:camera-view:${camerax_version}" // If you want to additionally add CameraX ML Kit Vision Integration implementation "androidx.camera:camera-mlkit-vision:${camerax_version}" // If you want to additionally use the CameraX Extensions library implementation "androidx.camera:camera-extensions:${camerax_version}" }
Kotlin
dependencies { // CameraX core library using the camera2 implementation val camerax_version = "1.3.0-alpha04" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation("androidx.camera:camera-core:${camerax_version}") implementation("androidx.camera:camera-camera2:${camerax_version}") // If you want to additionally use the CameraX Lifecycle library implementation("androidx.camera:camera-lifecycle:${camerax_version}") // If you want to additionally use the CameraX VideoCapture library implementation("androidx.camera:camera-video:${camerax_version}") // If you want to additionally use the CameraX View class implementation("androidx.camera:camera-view:${camerax_version}") // If you want to additionally add CameraX ML Kit Vision Integration implementation("androidx.camera:camera-mlkit-vision:${camerax_version}") // If you want to additionally use the CameraX Extensions library implementation("androidx.camera:camera-extensions:${camerax_version}") }
これらの要件に準拠するようにアプリを設定する方法について詳しくは、依存関係の宣言をご覧ください。
参考情報
CameraX について詳しくは、以下の参考情報をご確認ください。
Codelab
コードサンプル