Nagrywanie filmów HDR

Uwaga: ta strona dotyczy pakietu Camera2. Jeśli Twoja aplikacja nie wymaga konkretnych funkcji niskiego poziomu z Camera2, zalecamy używanie CameraX. Zarówno CameraX, jak i Camera2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.

Interfejsy API Camera2 obsługują przechwytywanie filmów w trybie High Dynamic Range (HDR), co umożliwia podgląd i nagrywanie treści wideo HDR za pomocą aparatu. W porównaniu ze standardowym zakresem dynamiki (SDR) HDR oferuje szerszą gamę kolorów i zwiększa zakres dynamiki komponentu luminancji (z obecnych 100 cd/m2 do tysięcy cd/m2). Dzięki temu jakość wideo jest bardziej zbliżona do rzeczywistości, z bogatszymi kolorami, jaśniejszymi podświetleniami i ciemniejszymi cieniami.

Zobacz, jak film HDR rejestruje zachód słońca z większą ilością szczegółów.

Rysunek 1. Porównanie jakości wideo SDR (u góry) i HDR (u dołu).

Wymagania wstępne dotyczące urządzenia

Nie wszystkie urządzenia z Androidem obsługują przechwytywanie filmów HDR. Zanim zaczniesz przechwytywać filmy HDR w aplikacji, sprawdź, czy Twoje urządzenie spełnia te wymagania wstępne:

  • Docelowy poziom interfejsu API to Android 13 (poziom 33).
  • Ma czujnik aparatu o głębi bitowej co najmniej 10 bitów. Więcej informacji o obsłudze HDR znajdziesz w sekcji Sprawdzanie obsługi HDR.

Ponieważ nie wszystkie urządzenia spełniają wymagania wstępne, podczas konfigurowania przechwytywania filmów HDR w aplikacji możesz dodać oddzielną ścieżkę kodu. Dzięki temu aplikacja będzie mogła wrócić do SDR na niekompatybilnych urządzeniach. Rozważ też dodanie opcji interfejsu użytkownika dla SDR. Użytkownik może wtedy przełączać się między SDR a HDR w zależności od potrzeb związanych z nagrywaniem filmów.

Architektura przechwytywania HDR

Ten schemat przedstawia główne komponenty architektury przechwytywania HDR.

Schemat architektury rejestrowania HDR.
Rysunek 2. Schemat architektury przechwytywania HDR.

Gdy urządzenie z aparatem przechwytuje klatkę w HDR, platforma Camera2 przydziela bufor, w którym są przechowywane przetworzone dane wyjściowe czujnika aparatu. W razie potrzeby dołącza też odpowiednie metadane HDR. Następnie platforma Camera2 umieszcza wypełniony bufor w kolejce do powierzchni wyjściowej , do której odwołuje się CaptureRequest, np. do wyświetlacza lub kodera wideo, jak pokazano na schemacie.

Sprawdzanie obsługi HDR

Zanim zaczniesz przechwytywać filmy HDR w aplikacji, sprawdź, czy urządzenie obsługuje wybrany profil HDR.

Użyj metody CameraManager getCameraCharacteristics(), aby uzyskać instancję CameraCharacteristics , którą możesz sprawdzić pod kątem możliwości HDR urządzenia.

Te czynności pozwalają sprawdzić, czy urządzenie obsługuje HLG10. HLG10 to podstawowy standard HDR, który producenci urządzeń muszą obsługiwać w aparatach z 10-bitowym wyjściem.

  1. Najpierw sprawdź, czy urządzenie obsługuje profile 10-bitowe (głębia bitowa 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. Następnie sprawdź, czy urządzenie obsługuje HLG10 (lub inny obsługiwany profil):

    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;
    }

Jeśli urządzenie obsługuje HDR, isHLGSupported() zawsze zwraca wartość true. Więcej informacji znajdziesz w CameraCharacteristics dokumentacji referencyjnej.

Konfigurowanie przechwytywania HDR

Po upewnieniu się, że urządzenie obsługuje HDR, skonfiguruj aplikację tak, aby przechwytywała surowy strumień wideo HDR z aparatu. Użyj setDynamicRangeProfile(), aby podać OutputConfiguration strumienia z profilem HDR obsługiwanym przez urządzenie, który jest następnie przekazywany do CameraCaptureSession podczas tworzenia. Zobacz listę obsługiwanych profili HDR.

W tym przykładzie kodu setupSessionDynamicRangeProfile() najpierw sprawdza, czy urządzenie działa w Androidzie 13. Następnie konfiguruje CameraCaptureSession z profilem HDR obsługiwanym przez urządzenie jako OutputConfiguration:

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
      }
  }
  

Gdy aplikacja aparatu inicjuje aparat, wysyła powtarzające się CaptureRequest aby wyświetlić podgląd nagrania:

Kotlin

session.setRepeatingRequest(previewRequest, null, cameraHandler)

A także aby rozpocząć nagrywanie wideo:

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)

Kodowanie strumienia z kamery HDR

Aby zakodować strumień z kamery HDR i zapisać plik na dysku, użyj MediaCodec.

Najpierw pobierz OutputSurface, który mapuje się na bufor przechowujący surowe dane wideo. W przypadku MediaCodec, użyj createInputSurface().

Aby zainicjować MediaCodec, aplikacja musi utworzyć MediaFormat z określonym profilem kodeka, przestrzenią kolorów, zakresem kolorów i funkcją transferu:

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)

Więcej informacji o implementacji znajdziesz w przykładowej aplikacji Camera2Video EncoderWrapper.kt.

Formaty HDR

Od Androida 13 urządzenia z aparatem, które mają 10-bitowe wyjście, muszą obsługiwać HLG10 do przechwytywania i odtwarzania HDR. Ponadto producenci urządzeń mogą włączyć dowolny format HDR, korzystając z architektury przechwytywania HDR.

W tej tabeli podsumowano dostępne formaty HDR i ich możliwości w zakresie przechwytywania filmów HDR.

Format Funkcja transferu (TF) Metadane Kodek Głębia bitowa
HLG10 HLG Nie HEVC 10-bitowy
HDR10 PQ Statyczne HEVC 10-bitowy
HDR10+ PQ Dynamiczne HEVC 10-bitowy
Dolby Vision 8.4 HLG Dynamiczne HEVC 10-bitowy

Zasoby

Aby zobaczyć działającą aplikację z funkcją przechwytywania filmów HDR, zapoznaj się z przykładem Camera2Video w GitHubie.