HDR 動画キャプチャ

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

Camera2 API はハイ ダイナミック レンジ(HDR)動画キャプチャをサポートしているため、カメラを使用して HDR 動画コンテンツのプレビューと録画を行うことができます。標準ダイナミック レンジ(SDR)と比較して、HDR はより幅広い色を提供し、輝度コンポーネントのダイナミック レンジを(現在の 100 cd/m2 から数百 cd/m2 に)拡大します。これにより、豊かな色彩、明るいハイライト、暗いシャドウが実現し、実生活により近い動画品質が得られます。

HDR 動画で夕日を細部まで鮮明に捉える方法を確認できます。

図 1.SDR(上)と HDR(下)の動画品質の比較。

デバイスの前提条件

すべての Android デバイスが HDR 動画キャプチャをサポートしているわけではありません。アプリで HDR 動画をキャプチャする前に、デバイスが次の前提条件を満たしているかどうかを確認してください。

  • Android 13(API レベル 33)をターゲットとする。
  • 10 ビット以上対応のカメラセンサーが搭載されている。HDR サポートについて詳しくは、HDR サポートを確認するをご覧ください。

すべてのデバイスが前提条件を満たしているとは限らないため、アプリで HDR 動画キャプチャを設定するときに別のコードパスを追加できます。これにより、互換性のないデバイスでアプリは SDR にフォールバックできます。また、SDR 用の UI オプションを追加することも検討してください。ユーザーは録画のニーズに合わせて SDR と HDR を切り替えることができます。

HDR キャプチャ アーキテクチャ

次の図は、HDR キャプチャ アーキテクチャの主なコンポーネントを示しています。

HDR キャプチャのアーキテクチャ図。
図 2. HDR キャプチャのアーキテクチャ図。

カメラデバイスが HDR でフレームをキャプチャすると、Camera2 フレームワークは処理されたカメラセンサー出力を保存するバッファを割り当てます。また、HDR プロファイルで必要な場合は、対応する HDR メタデータもアタッチします。次に Camera2 フレームワークは、図に示すように、CaptureRequest で参照される出力サーフェス(ディスプレイや動画エンコーダなど)の入力バッファをキューに入れます。

HDR サポートを確認する

アプリで HDR 動画をキャプチャする前に、デバイスが目的の HDR プロファイルをサポートしているかどうかを確認します。

CameraManager getCameraCharacteristics() メソッドを使用して CameraCharacteristics インスタンスを取得し、デバイスの HDR 機能をクエリできます。

次の手順では、デバイスが HLG10 に対応しているかどうかを確認します。 HLG10 は、デバイス メーカーが 10 ビット出力のカメラでサポートする必要があるベースライン HDR 標準です。

  1. まず、デバイスが 10 ビット プロファイル(HLG10 のビット深度)をサポートしているかどうかを確認します。

    Kotlin

    private fun isTenBitProfileSupported(cameraId: String): Boolean {
      val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
      val availableCapabilities = cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
      for (capability in availableCapabilities!!) {
          if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) {
              return true
          }
      }
      return false
    }
    
  2. 次に、デバイスが HLG10(または別のサポートされているプロファイル)に対応しているかどうかを確認します。

    Kotlin

    @RequiresApi(api = 33)
    private fun isHLGSupported(cameraId: String): Boolean {
    if (isTenBitProfileSupported(cameraId)) {
      Val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
      val availableProfiles = cameraCharacteristics
      .get(CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)!!
      .getSupportedProfiles()
    
      // Checks for the desired profile, in this case HLG10
      return availableProfiles.contains(DynamicRangeProfiles.HLG10)
    }
    return false;
    }
    

デバイスが HDR をサポートしている場合、isHLGSupported() は常に true を返します。詳細については、CameraCharacteristics のリファレンス ドキュメントをご覧ください。

HDR キャプチャをセットアップする

デバイスが HDR をサポートしていることを確認したら、カメラから未加工の HDR 動画ストリームをキャプチャするようにアプリを設定します。setDynamicRangeProfile() を使用して、ストリームの OutputConfiguration にデバイス対応の HDR プロファイルを指定します。このプロファイルは作成時に CameraCaptureSession に渡されます。サポートされている HDR プロファイルのリストをご覧ください。

次のコードサンプルでは、setupSessionDynamicRangeProfile() はまず、デバイスに Android 13 が搭載されていることを確認します。次に、デバイスでサポートされている HDR プロファイルで CameraCaptureSessionOutputConfiguration として設定します。

Kotlin

  /**
  * Creates a [CameraCaptureSession] with a dynamic range profile.
  */
  private fun setupSessionWithDynamicRangeProfile(
          dynamicRange: Long,
          device: CameraDevice,
          targets: List,
          handler: Handler? = null,
          stateCallback: CameraCaptureSession.StateCallback
  ): Boolean {
      if (android.os.Build.VERSION.SDK_INT >=
              android.os.Build.VERSION_CODES.TIRAMISU) {
          val outputConfigs = mutableListOf()
              for (target in targets) {
                  val outputConfig = OutputConfiguration(target)
                  //sets the dynamic range profile, for example DynamicRangeProfiles.HLG10
                  outputConfig.setDynamicRangeProfile(dynamicRange)
                  outputConfigs.add(outputConfig)
              }

          device.createCaptureSessionByOutputConfigurations(
                  outputConfigs, stateCallback, handler)
          return true
      } else {
          device.createCaptureSession(targets, stateCallback, handler)
          return false
      }
  }

}

カメラアプリはカメラを初期化するときに、繰り返しの CaptureRequest を送信して録画をプレビューします。

Kotlin

session.setRepeatingRequest(previewRequest, null, cameraHandler)

動画の録画を開始するには:

Kotlin

// Start recording repeating requests, which stops the ongoing preview
//  repeating requests without having to explicitly call
//  `session.stopRepeating`
session.setRepeatingRequest(recordRequest,
        object : CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(session: CameraCaptureSession,
            request: CaptureRequest, result: TotalCaptureResult) {
        if (currentlyRecording) {
            encoder.frameAvailable()
        }
    }
}, cameraHandler)

HDR カメラ ストリームをエンコードする

HDR カメラ ストリームをエンコードしてファイルをディスクに書き込むには、MediaCodec を使用します。

まず、OutputSurface を取得します。これは、未加工の動画データを格納するバッファにマッピングされます。MediaCodec には、createInputSurface() を使用します。

MediaCodec を初期化するには、アプリは指定されたコーデック プロファイル、色空間、色範囲、転送関数を使用して MediaFormat を作成する必要があります。

Kotlin

val mimeType = when {
    dynamicRange == DynamicRangeProfiles.STANDARD -> MediaFormat.MIMETYPE_VIDEO_AVC
    dynamicRange < DynamicRangeProfiles.PUBLIC_MAX ->
            MediaFormat.MIMETYPE_VIDEO_HEVC
    else -> throw IllegalArgumentException("Unknown dynamic range format")
}

val codecProfile = when {
    dynamicRange == DynamicRangeProfiles.HLG10 ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10
    dynamicRange == DynamicRangeProfiles.HDR10 ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10
    dynamicRange == DynamicRangeProfiles.HDR10_PLUS ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus
    else -> -1
}
// Failing to correctly set color transfer causes quality issues
// for example, washout and color clipping
val transferFunction = when (codecProfile) {
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10 ->
            MediaFormat.COLOR_TRANSFER_HLG
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10 ->
            MediaFormat.COLOR_TRANSFER_ST2084
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus ->
            MediaFormat.COLOR_TRANSFER_ST2084
    else -> MediaFormat.COLOR_TRANSFER_SDR_VIDEO
}

val format = MediaFormat.createVideoFormat(mimeType, width, height)

// Set some properties.  Failing to specify some of these can cause the MediaCodec
// configure() call to throw an exception.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
        MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL)

if (codecProfile != -1) {
    format.setInteger(MediaFormat.KEY_PROFILE, codecProfile)
    format.setInteger(MediaFormat.KEY_COLOR_STANDARD,
            MediaFormat.COLOR_STANDARD_BT2020)
    format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED)
    format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, transferFunction)
    format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing,
            true)
}

mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

実装について詳しくは、Camera2Video サンプルアプリの EncoderWrapper.kt をご覧ください。

HDR 形式

Android 13 以降、10 ビット出力機能を備えたカメラデバイスは、HDR キャプチャと再生のために HLG10 をサポートする必要があります。また、デバイス メーカーは HDR キャプチャ アーキテクチャを使用して、任意の HDR 形式を有効にすることができます。

次の表は、HDR 動画キャプチャで使用可能な HDR 形式と機能をまとめたものです。

形式 伝達関数(TF) メタデータ コーデック ビット深度
HLG10 HLG × HEVC 10 ビット
HDR10 前四半期 静的 HEVC 10 ビット
HDR10+ 前四半期 動的 HEVC 10 ビット
ドルビー ビジョン 8.4 HLG 動的 HEVC 10 ビット

参考資料

HDR 動画キャプチャ機能を備えた実用的なアプリについては、GitHub の Camera2Video サンプルをご覧ください。