การจับภาพวิดีโอ HDR

หมายเหตุ: หน้านี้อ้างอิงถึงแพ็กเกจ Camera2 เราขอแนะนำให้ใช้ CameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

Camera2 API รองรับการจับภาพวิดีโอ High Dynamic Range (HDR) ซึ่งช่วยให้คุณดูตัวอย่างและบันทึกเนื้อหาวิดีโอ HDR โดยใช้กล้องได้ เมื่อเทียบกับ Standard Dynamic Range (SDR) แล้ว HDR จะมีช่วงสีที่กว้างกว่าและเพิ่มช่วงไดนามิกของคอมโพเนนต์ความสว่าง (จากปัจจุบัน 100 cd/m2 เป็น 1,000 cd/m2) ซึ่งส่งผลให้คุณภาพวิดีโอใกล้เคียงกับภาพจริงมากขึ้น โดยมีสีสันที่สมบูรณ์ยิ่งขึ้น ไฮไลต์ที่สว่างขึ้น และเงาที่มืดลง

ดูว่าวิดีโอ HDR บันทึกภาพพระอาทิตย์ตกได้สดใสและมีรายละเอียดมากขึ้นอย่างไร

ภาพพระอาทิตย์ตกที่ถ่ายด้วยคุณภาพวิดีโอแบบ SDR ภาพพระอาทิตย์ตกที่ถ่ายด้วยคุณภาพวิดีโอ HDR
รูปที่ 1 การเปรียบเทียบคุณภาพวิดีโอ SDR (บน) กับ HDR (ล่าง)

ข้อกำหนดเบื้องต้นของอุปกรณ์

อุปกรณ์ Android บางรุ่นไม่รองรับการจับภาพวิดีโอ HDR ก่อนจับภาพวิดีโอ HDR ในแอป ให้ตรวจสอบว่าอุปกรณ์ของคุณมีคุณสมบัติตรงตามข้อกำหนดเบื้องต้นต่อไปนี้หรือไม่

  • กำหนดเป้าหมายเป็น Android 13 (ระดับ API 33)
  • มีเซ็นเซอร์กล้องที่รองรับ 10 บิตขึ้นไป ดูข้อมูลเพิ่มเติมเกี่ยวกับการรองรับ HDR ได้ที่ตรวจสอบการรองรับ HDR

เนื่องจากอุปกรณ์บางรุ่นไม่เป็นไปตามข้อกำหนดเบื้องต้น คุณจึงเพิ่มเส้นทางโค้ดแยกต่างหากได้เมื่อตั้งค่าการบันทึกวิดีโอ HDR ในแอป ซึ่งจะช่วยให้แอปเปลี่ยนไปใช้ SDR ในอุปกรณ์ที่เข้ากันไม่ได้ นอกจากนี้ ให้พิจารณาเพิ่มตัวเลือก UI สำหรับ SDR ด้วย จากนั้นผู้ใช้จะสลับระหว่าง SDR และ HDR ได้ตามความต้องการในการบันทึกวิดีโอ

สถาปัตยกรรมการจับภาพ HDR

แผนภาพต่อไปนี้แสดงองค์ประกอบหลักของสถาปัตยกรรมการจับภาพ HDR

แผนภาพสถาปัตยกรรมการจับภาพ HDR
รูปที่ 2 แผนภาพสถาปัตยกรรมการจับภาพ HDR

เมื่ออุปกรณ์กล้องจับภาพเฟรมใน HDR เฟรมเวิร์ก Camera2 จะจัดสรรบัฟเฟอร์ที่จัดเก็บเอาต์พุตเซ็นเซอร์กล้องที่ประมวลผลแล้ว นอกจากนี้ ยังแนบข้อมูลเมตา HDR ที่เกี่ยวข้องด้วยหากโปรไฟล์ HDR กำหนดไว้ จากนั้นเฟรมเวิร์ก Camera2 จะจัดคิวบัฟเฟอร์ที่สร้างขึ้นสำหรับพื้นผิวเอาต์พุตที่อ้างอิงใน CaptureRequest เช่น จอแสดงผลหรือโปรแกรมเปลี่ยนไฟล์วิดีโอ ดังที่แสดงในแผนภาพ

ตรวจสอบการรองรับ HDR

ก่อนจับภาพวิดีโอ HDR ในแอป ให้ตรวจสอบว่าอุปกรณ์รองรับโปรไฟล์ HDR ที่คุณเลือกหรือไม่

ใช้เมธอด CameraManager getCameraCharacteristics() เพื่อรับอินสแตนซ์ CameraCharacteristics ซึ่งคุณสามารถค้นหาความสามารถ HDR ของอุปกรณ์ได้

ขั้นตอนต่อไปนี้จะตรวจสอบว่าอุปกรณ์รองรับ HLG10 หรือไม่ HLG10 เป็นมาตรฐาน HDR พื้นฐานที่ผู้ผลิตอุปกรณ์ต้องรองรับในกล้องที่มีเอาต์พุต 10 บิต

  1. ก่อนอื่น ให้ตรวจสอบว่าอุปกรณ์รองรับโปรไฟล์ 10 บิต (ความลึกของบิตสำหรับ HLG10) หรือไม่ โดยทำดังนี้

    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 (หรือโปรไฟล์อื่นที่รองรับ) หรือไม่ โดยทำดังนี้

    @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 chosen 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 ดังนี้

  /**
   * Creates a [CameraCaptureSession] with a dynamic range profile.
   */
  private fun setupSessionWithDynamicRangeProfile(
      dynamicRange: Long,
      device: CameraDevice,
      targets: List<Surface>,
      handler: Handler? = null,
      stateCallback: CameraCaptureSession.StateCallback
  ): Boolean {
      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
          val outputConfigs = mutableListOf<OutputConfiguration>()
          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 ซ้ำๆ เพื่อแสดงตัวอย่างการบันทึก ดังนี้

session.setRepeatingRequest(previewRequest, null, cameraHandler)

และหากต้องการเริ่มบันทึกวิดีโอ ให้ทำดังนี้

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

ก่อนอื่น ให้รับเอาต์พุต Surface ซึ่งแมปกับบัฟเฟอร์ที่จัดเก็บข้อมูลวิดีโอดิบ สำหรับ MediaCodec ให้ใช้ createInputSurface()

หากต้องการเริ่มต้น MediaCodec แอปต้องสร้าง MediaFormat ที่มีโปรไฟล์ตัวแปลงรหัส ขอบเขตสี ช่วงสี และฟังก์ชันการโอนที่ระบุ ดังนี้

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)

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการติดตั้งใช้งานได้ที่ EncoderWrapper.kt ของแอปตัวอย่าง Camera2Video

รูปแบบ HDR

ตั้งแต่ Android 13 เป็นต้นไป อุปกรณ์กล้องที่มีความสามารถในการแสดงผล 10 บิตต้องรองรับ HLG10 สำหรับการจับภาพและการเล่น HDR นอกจากนี้ ผู้ผลิตอุปกรณ์ยังสามารถเปิดใช้รูปแบบ 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 ได้ที่ตัวอย่าง Camera2Video ใน GitHub