צילום סרטונים באיכות HDR

הערה: הדף הזה מתייחס לחבילה camera2. מומלץ להשתמש ב-CameraX, אלא אם לאפליקציה שלך נדרשים תכונות ספציפיות ברמה נמוכה. גם CameraX וגם Camera2 תומכים ב-Android 5.0 (רמת API 21) ואילך.

תמיכה בממשקי API של Camera2 צילום וידאו עם טווח דינמי גבוה (HDR), שמאפשר לראות תצוגה מקדימה לצלם תוכן של סרטוני HDR באמצעות המצלמה. בהשוואה לדינמיקה רגילה Range (SDR), באיכות HDR יש מגוון רחב יותר של צבעים ומגבירים את הדינמיקה. הטווח של רכיב הבהירות (החל מ- 100 cd/m2 עד 1,000s של cd/m2). התוצאה היא איכות וידאו שקרובה יותר לחיים האמיתיים, צבעים עשירים יותר, אזורים בהירים יותר וצלליות כהות יותר.

אפשר לראות איך סרטון HDR מצלמים את השקיעה בצורה מרהיבה יותר.

איור 1. השוואה בין איכות הסרטונים באיכות SDR (למעלה) לעומת HDR (למטה).

דרישות מוקדמות למכשירים

לא כל מכשירי Android תומכים בצילום סרטונים באיכות HDR. לפני צילום סרטון HDR באפליקציה, כדאי לבדוק אם המכשיר עומד בדרישות הדרישות המוקדמות הבאות:

  • מוגדר טירגוט ל-Android 13 (רמת API 33).
  • כולל חיישן מצלמה עם יכולות של 10 ביט ומעלה. למידע נוסף על HDR כאן תוכלו לקרוא איך בודקים אם יש תמיכה ב-HDR.

לא כל המכשירים עומדים בדרישות המוקדמות, ולכן ניתן להוסיף קוד נפרד נתיב בזמן ההגדרה של צילום וידאו באיכות HDR באפליקציה. כך האפליקציה תוכל לחזור ל-SDR במכשירים לא תואמים. כדאי גם להוסיף אפשרות בממשק המשתמש ל-SDR. המשתמש יכול להחליף מצב בין SDR ל-HDR בהתאם לצרכים שלהם לגבי הקלטת וידאו.

ארכיטקטורת צילום HDR

בתרשים הבא מוצגים הרכיבים העיקריים בארכיטקטורת צילום HDR.

תרשים ארכיטקטורה של צילום HDR.
איור 2. תרשים ארכיטקטורה של צילום HDR.

כשמכשיר מצלמה מצלם פריים ב-HDR, ה-framework של Camera2 מקצה מאגר נתונים זמני שמאחסן את הפלט של חיישן המצלמה המעובד. הוא גם מצרף את המטא-נתונים המתאימים של HDR, אם הדבר נדרש על ידי פרופיל ה-HDR. לאחר מכן, ה-framework של מצלמה2 מוסיף את מאגר הנתונים הזמני המאוכלס למשטח הפלט. שיש הפניה אליו בCaptureRequest, למשל תצוגה או את מקודד הווידאו שלכם, כפי שמוצג בדיאגרמה.

איך בודקים אם יש תמיכה ב-HDR

לפני צילום סרטון HDR באפליקציה, צריך לבדוק אם המכשיר תומך פרופיל ה-HDR הרצוי.

משתמשים בשיטה CameraManager getCameraCharacteristics() כדי לקבל CameraCharacteristics שבה אפשר לשלוח שאילתה ליכולות ה-HDR של המכשיר.

תוכלו לבצע את הפעולות הבאות כדי לבדוק אם המכשיר תומך ב-HLG10. HLG10 הוא התקן הבסיסי ל-HDR שיצרני המכשירים חייבים לתמוך בו במצלמות עם פלט של 10 ביט.

  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. לאחר מכן, הוא מגדיר את CameraCaptureSession עם המכשיר שנתמך פרופיל HDR כ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
      }
  }

}

כשאפליקציית המצלמה מפעילה את המצלמה, היא שולחת חוזר 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 ביט חייבת לתמוך ב-HLG10 לצילום HDR הפעלה. בנוסף, יצרני המכשירים יכולים להפעיל כל פורמט HDR לפי בחירתם באמצעות ארכיטקטורת צילום HDR.

הטבלה הבאה מסכמת את הפורמטים הזמינים של HDR והיכולות שלהם לצילום סרטונים באיכות HDR.

פורמט שיטת העברה (TF) Metadata קודק עומק ביט
HLG10 איכות חיים (HLG) לא מק"ט HEVC 10-ביט
HDR10 PQ סטטי מק"ט HEVC 10-ביט
HDR10+‎ PQ דינמי מק"ט HEVC 10-ביט
Dolby Vision 8.4 איכות חיים (HLG) דינמי מק"ט HEVC 10-ביט

משאבים

לאפליקציה פעילה עם פונקציית צילום וידאו ב-HDR: דוגמה של Camera2Video ב-GitHub.