カメラのレンズと機能

注: このページでは、Camera2 パッケージについて言及しています。アプリで Camera2 の特定の低レベル機能が必要な場合を除き、CameraX を使用することをおすすめします。CameraX と Camera2 は、どちらも Android 5.0(API レベル 21)以降に対応しています。

最新の Android デバイスの多くは、デバイスの前面、背面、または両面に 2 つ以上のカメラを搭載しています。各レンズには、バースト撮影、手動制御、モーション トラッキングなどの独自の機能があります。小切手を入金するアプリでは、最初の背面カメラのみを使用する場合があります。一方、ソーシャル メディア アプリでは、デフォルトで前面カメラを使用し、利用可能なすべてのレンズを切り替えるオプションをユーザーに提供する場合があります。また、ユーザーの選択を記憶することもできます。

このページでは、カメラレンズとその機能を一覧表示する方法について説明します。これにより、特定の状況で使用するレンズをアプリ内で決定できます。次のコード スニペットは、すべてのカメラのリストを取得し、それらを反復処理します。

try {
    val cameraIdList = cameraManager.cameraIdList // may be empty

    // iterate over available camera devices.
    for (cameraId in cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
        val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API.
        val isBackwardCompatible = cameraCapabilities?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
        // ...
    }
} catch (e: CameraAccessException) {
    e.message?.let { Log.e(TAG, it) }
    // ...
}

変数 cameraLensFacing は、デバイスの画面を基準としたカメラの向きを表し、次のいずれかの値になります。

レンズの向きの設定について詳しくは、CameraCharacteristics.LENS_FACING をご覧ください。

上記のコードサンプルの変数 cameraCapabilities には、カメラが標準フレームを出力として生成できるかどうか(たとえば、深度センサーデータのみを出力するのではなく)など、さまざまな機能に関する情報が含まれています。CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE がカメラの機能リストに含まれているかどうかを確認できます。この機能は、isBackwardCompatible にフラグとして保存されます。

適切なデフォルトを選択する

アプリでは、デフォルトで特定のカメラを開くことが一般的です(利用可能な場合)。たとえば、自撮りアプリは前面カメラを開く可能性が高く、拡張現実アプリは背面カメラで起動する可能性があります。次の関数は、指定された方向を向いている最初のカメラを返します。

fun getFirstCameraIdFacing(
    cameraManager: CameraManager,
    facing: Int = CameraMetadata.LENS_FACING_BACK
): String? {
    try {
        // Get list of all compatible cameras.
        val cameraIds = cameraManager.cameraIdList.filter {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
            capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
            ) ?: false
        }

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration.
        cameraIds.forEach {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
                return it
            }
        }

        // If no camera matched desired orientation, return the first one from the list.
        return cameraIds.firstOrNull()
    } catch (e: CameraAccessException) {
        e.message?.let { Log.e(TAG, it) }
        return null
    }
}

カメラの切り替えを有効にする

多くのカメラアプリでは、ユーザーがカメラを切り替えることができます。

カメラ切り替えボタンが表示されている Google カメラアプリのインターフェース。
図 1. Google カメラ アプリのカメラ切り替えボタン

多くのデバイスには、同じ方向を向いた複数のカメラが搭載されています。外付け USB カメラを搭載しているものもあります。ユーザーが異なる向きのカメラを切り替えられる UI を提供するには、レンズの向きの設定ごとに、最初に使用可能なカメラを選択します。

次のカメラを選択するための普遍的なロジックはありませんが、次のコードはほとんどのユースケースで機能します。

fun filterCompatibleCameras(
    cameraIds: Array<String>,
    cameraManager: CameraManager
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
    }
}

fun filterCameraIdsFacing(
    cameraIds: List<String>,
    cameraManager: CameraManager,
    facing: Int
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists.
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK
    )
    val frontCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT
    )
    val externalCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL
    )

    // The recommended order of iteration is: all external, first back, first front.
    val allCameras = (
        externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull()
        )
        ).filterNotNull()

    // Get the index of the currently selected camera in the list.
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be in the list, for example it could be an
    // external camera that has been removed by the user.
    return if (cameraIndex == -1) {
        // Return the first camera from the list.
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary.
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

このコードは、さまざまな構成を持つ多数のデバイスで動作します。エッジケースの考慮に関する詳細については、CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA をご覧ください。

互換性のあるアプリを作成する

非推奨の Camera API を引き続き使用しているアプリの場合、Camera.getNumberOfCameras() が返すカメラの数は OEM の実装によって異なります。システムに論理マルチカメラがある場合、アプリの下位互換性を維持するため、このメソッドは論理カメラと基盤となる物理カメラのグループごとに 1 つのカメラのみを公開します。すべてのカメラを表示するには、Camera2 API を使用します。

カメラの向きに関する背景情報については、Camera.CameraInfo.orientation をご覧ください。

一般に、Camera.getCameraInfo() API を使用してすべてのカメラ orientation をクエリし、カメラを切り替えるユーザーに対して、利用可能な向きごとに 1 つのカメラのみを公開します。

あらゆるデバイスタイプに対応

アプリが常に 1 台または 2 台のカメラを搭載したハンドヘルド デバイスで動作することを前提としないでください。代わりに、アプリに最も適したカメラを選択します。特定のカメラが必要ない場合は、選択した方向を向いている最初のカメラを選択します。特定の方向を向いているカメラが利用できない場合は、ユーザーが別のカメラでジャーニーを完了できるかどうかを検討します。カメラ ハードウェアに基づいて、特定のデバイスでのアプリの可用性を制限しないでください。外部カメラが接続されている場合、ユーザーはデフォルトとして外部カメラを好む可能性があります。