Open1

【Google I/O 2023】Building high quality Android camera experiences

watabeewatabee

https://www.youtube.com/watch?v=rNe2xGKjtvc&list=PLOU2XLYxmsIIwZQkAPhJZg8jaNrrHk1DH&index=33

  • このセッションで紹介すること
    • Polished camera previews
    • Premium visual quality
    • What else is new on Android 14

Camera Previews (2:05~)

  • プレビューの重要性
    • アスペクト比が合ってなくて歪んだプレビューはプレミアム感が損なわれる
    • ユーザーはプレビューと実際に保存された写真、動画が合っていることを期待する

Preview Stabilization (2:52~)

  • Android 5 から Video Stabilization がある
  • Android 13 以降の端末は Preview Stabilization が設定できる
    • カメラプレビューと動画の内容が一致する

Camera2 で Preview Stabilization を設定するコードは以下。

// Camera2: When configuring your capture request, check to enable Preview Stabilization.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun setPreviewStabilization(builder: CaptureRequest.Builder, manager: CameraManager, cameraId: String) {
    val availableVideoStabilizationModes = manager
        .getCameraCharacteristics(cameraId)
        .get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)
        
    if (availableVideoStabilizationModes?.contains(CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) == true) {
        builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
                    CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
    }
}

CameraX の場合は以下。

// CameraX: Use Camera2Interop to set the underlying Camera2 CaptureRequest.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@ExperimentalCamera2Interop
fun setPreviewStabilization(camera: Camera) {
    val availableVideoStabilizationModes = Camera2CameraInfo.from(camera.cameraInfo)
        .getCameraCharacteristic(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)
        
    if (availableVideoStabilizationModes?.contains(CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) == true) {
        Camera2CameraControl.from(camera.cameraControl).setCaputreRequestOptions(
            CaptureRequestOptions.Builder().setCaptureRequestOption(
                CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
                CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
            ).build()
        )
    }
}

Jetpack Preview Classes (4:26~)

  • Jetpack には2つのプレビュークラスがある
    • CameraX : PreviewView
    • Camera2 CameraViewfinder
  • どちらのプレビュークラスにも6つのスケールタイプが提供されている

CameraX の PreviewView の設定コードは以下。

// CameraX: Using PreviewView
val previewView: PreviewView = viewBinding.viewFinder
cameraController = LifecycleCameraController(baseContext)
cameraController.bindToLifecycle(myActivity)
previewView.controller = cameraController

CameraX は高度なプレビュー機能も提供している。

// Add an image analyzer (including our built-in MlKitAnalyzer),
// with each image's coordinates mapped to the preview.
cameraController.setImageAnalysisAnalyzer(executor, analyzer)

// Add Graphics Library effects to the preview, images, and video.
cameraController.setEffects(effects)

Camera2 の場合。

// Camera2: Using CameraViewFinder
val previewResolution = Size(width, height)
val viewfinderSurfaceRequest = SurfaceRequest(previewResolution, characteristics)
val cameraViewfinder = CameraViewfinder(baseContext)
val surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

Future.addCallback(surfaceListenableFuture,
                   object: FutureCallback<Surface> {
    override fun onSuccess(surface: Surface) {
        // create a CaptureSession using this surface as usual
    }
    override fun onFailure(t: Throwable) { /* something went wrong */ }
}, ContextCompat.getMainExecuter(context))

Large Screen Previews (8:33~)

  • Jetpack のプレビュークラスは大きいスクリーンサイズを考慮している

Premium visual quality (9:41~)

HDR Video (10:02~)

  • Android 13 で HDR のビデオキャプチャが導入された
    • 10-Bit Color : 1億以上の色が表示できる

10-Bit がサポートされているかのコードは以下。

// Assure Camera has 10-Bit Capabilities
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun isTenBitSupported(manager: CameraManager, cameraId: String): Boolean {
    val capabilities = manager
        .getCameraCharacteristics(cameraId)
        .get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
        
    return capabilities?.contains(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) ?: false
}

HDR がサポートされているかのコードは以下。

// Assure Camera supports HLG10
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun isHDRSupported(manager: CameraManager, cameraId: String): Boolean {
    val supportedProfiles = manager
        .getCameraCharacteristics(cameraId)
        .get(CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)
        ?.supportedProfiles
    
    return supportedProfiles?.contains(DynamicRangeProfiles.HDR10_PLUS) ?: false
}

OutputConfiguration の dynamicRangeProfile を設定する。

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun configureSession(device: CameraDevice, targets: List<Surface>, exe: Executor, cb: CameraCaptureSession.StateCallback) {
    val configs = targets.map {
        val config = OutputConfiguration(it)
        config.dynamicRangeProfile = DynamicRangeProfile.HLG10
        config
    }
    
    val session = SessionConfiguration(
        SessionConfiguration.SESSION_REGULAR, configs, exe, cb
    )
    
    device.createCaptureSession(session)
}

最後に MediaFormat を設定する。

@RequiresApi(Build.VERSION_CODES.N)
fun configureEncoder(surface: Surface, w: Int, h: Int) {
    val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, w, h)
    
    // Other Setup
    // ...
    
    // Set media format properties
    format.setInteger(...)
}

setInteger の部分には以下のパラメータを設定する。

/// Color Format
(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)

/// HEVC (H.265)
(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10)

/// HLG Color Transfer
(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_HLG)

/// BT2020 Color Standard
(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020)

Stream Use Cases (12:23~)

  • 以下は事前定義された Stream Use Cases
    • DEFAULT
      • 全ての既存の動作をカバーする設定
    • PREVIEW
      • Viewfinder やアプリ内の画像解析の用途にオススメ
    • STILL_CAPTURE
    • VIDEO_RECORD
    • VIDEO_CALL
      • 長時間カメラを使うような電力消費に関わる場合にオススメ
    • PREVIEW_VIDEO_STILL
      • ソーシャルメディアアプリや単一の stream use case に推奨される多目的の stream
    • VENDOR_START
      • OEM が定義した use cases

13:15 から以下の use case のデモ動画が見れる。

  • PREVIEW
    • Viewfinder としてのパフォーマンスと利便性を最適化
    • 画像の質は必ずしも最適化されない
  • STILL_CAPTURE
    • 高品質な画像キャプチャを最適化することが期待される
    • プレビューのようにフレームレートを維持することは想定されない

以下は特定の stream use case がサポートされているかどうかをチェックするコード例。

// Check if camera supports the stream use case.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun isStreamUseCaseAvailable(manager: CameraManager, cameraId: String, streamUseCase: Long): Boolean {
    val characteristics = manager
        .getCameraCharacteristics(id)
        .get(CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES)
        
    return characteristics?.contains(streamUseCase) ?: false
}

以下は stream use case を設定する例。

// Setup Stream Use Case while setting up your Output Configuration.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun configureSession(device: CameraDevice, targets: List<Surface>) {
    val configs = mutableListOf<OutputConfiguration>()
    val streamUseCase = CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL
    
    targets.forEach {
        val config = OutputConfiguration(it)
        config.streamUseCase = streamUseCase.toLong()
        configs.add(config)
    }
    ...
    device.createCaptureSession(session)
}

Extensions (13:56~)

  • 端末の製造メーカーはカメラの拡張機能を通じて、ナイトモードやボケ効果などの特別な機能を開発者に提供することができる
  • Snapchat はナイトモードの拡張機能で見た目の品質を改善
  • 以下が現在利用できる拡張機能
    • EXTENSION_NIGHT
    • EXTENSION_HDR
    • EXTENSION_AUTO
    • EXTENSION_BOKEH
    • EXTENSION_FACE_RETOUCH

拡張機能がサポートされているかチェックするコードは以下。

@RequireApi(Build.VERSION_CODE.S)
fun isExtensionSupported(manager: CameraManager, id: String, extension: Int): Boolean {
    return manger
        .getCameraExtensionCharacteristics(id)
        .supportedExtensions
        .contains(extension)
}

ボケ効果の拡張機能付きのセッションを作成する例は以下。

@RequireApi(Build.VERSION_CODE.S)
private fun createCaptureSession(
    device: CameraDevice,
    configs: List<OutputConfiguraion>,
    extension: Int = CameraExtensionCharacteristics.EXTENSION_BOKEH,
    exe: Executor
) {
    if (!isExtensionSupported(manager, device.id, extension)) return
    
    // Implement callbacks
    val cb = object: CameraExtensionSession.StateCallback() {
        // Implement onConfigured & onConfiguredFailed
    }
    
    val config = ExtensionSessionConfiguration(extension, configs, exe, cb)
    device.createExtensionSession(config)
}

これらの機能は CameraX の 1.3.0-alpha から使用可能。

Android 14 (15:50~)

  • Ultra HDR still capture
    • AppCompat で後方互換性のある JPEG フォーマットを設計中
  • P3 color space
  • ズームの最適化