HDR 동영상 캡처

참고: 이 페이지에서는 Camera2 패키지를 다룹니다. 앱에 Camera2의 특정 하위 수준 기능이 필요하지 않은 경우 CameraX를 사용하는 것이 좋습니다. CameraX와 Camera2는 모두 Android 5.0 (API 수준 21) 이상을 지원합니다.

Camera2 API는 카메라를 사용하여 HDR 동영상 콘텐츠를 미리 보고 녹화할 수 있는 HDR (High Dynamic Range) 동영상 캡처를 지원합니다. SDR(Standard Dynamic Range)에 비해 HDR은 더 넓은 범위의 색상을 제공하고 휘도 구성요소의 동적 범위를 현재 100cd/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() 메서드를 사용하여 기기의 HDR 기능을 쿼리할 수 있는 CameraCharacteristics 인스턴스를 가져옵니다.

다음 단계에서는 기기가 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()을 사용하여 기기에서 지원하는 HDR 프로필을 스트림의 OutputConfiguration 에 제공합니다. 그러면 생성 시 CameraCaptureSession 에 전달됩니다. 지원되는 HDR 프로필 목록을 참고하세요.

다음 코드 샘플에서 setupSessionDynamicRangeProfile()은 먼저 기기가 Android 13을 실행하는지 확인합니다. 그런 다음 기기에서 지원하는 HDR 프로필을 OutputConfiguration으로 사용하여 CameraCaptureSession을 설정합니다.

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 PQ 정적 HEVC 10비트
HDR10+ PQ 동적 HEVC 10비트
Dolby Vision 8.4 HLG 동적 HEVC 10비트

리소스

HDR 동영상 캡처 기능이 있는 작동하는 앱은 GitHub의 Camera2Video 샘플 을 참고하세요.