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() 메서드를 사용하여 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) Metadata 코덱 비트 심도
HLG10 HLG 아니요 HEVC 10비트
HDR10 실점 정적 HEVC 10비트
HDR10+ 실점 동적 HEVC 10비트
Dolby Vision 8.4 HLG 동적 HEVC 10비트

리소스

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