高動態範圍影片播放

HDR 或高動態範圍可提供更廣泛的色彩,並在最亮的白色和最暗的陰影之間提供更強烈的對比,讓影片品質更貼近肉眼所見。

您可以在應用程式中設定 HDR 影片播放功能,以便預覽及播放 HDR 影片內容。

本文假設您已在應用程式中新增基本影片播放支援功能。如要進一步瞭解播放功能,請參閱 ExoPlayer 說明文件。

裝置先決條件

並非所有 Android 裝置都支援 HDR 播放功能。在應用程式中播放 HDR 影片內容前,請先確認裝置是否符合下列必要條件:

  • 指定 Android 7.0 以上版本 (API 層級 24)。
  • 具備 HDR 解碼器,且可存取 HDR 螢幕。

確認 HDR 播放功能是否支援

使用 Display.getHdrCapabilities() 查詢螢幕的 HDR 功能。這個方法會傳回螢幕支援的 HDR 設定檔和亮度範圍相關資訊。

以下程式碼會檢查裝置是否支援 HLG10 播放功能。從 Android 13 開始,如果裝置支援 HDR 播放功能,則裝置製造商必須支援 HLG10 這個最低標準:

Kotlin

// Check if display supports the HDR type
val capabilities = display?.hdrCapabilities?.supportedHdrTypes ?: intArrayOf()
if (!capabilities.contains(HDR_TYPE_HLG)) {
  throw RuntimeException("Display does not support desired HDR type");
}

Java

// Check if display supports the HDR type
int[] list = getDisplay().getHdrCapabilities().getSupportedHdrTypes();
List capabilities = Arrays.stream(list).boxed().collect(Collectors.toList());
if (!capabilities.contains(HDR_TYPE_HLG)) {
 throw new RuntimeException("Display does not support desired HDR type");
}

在應用程式中設定 HDR 播放功能

如果應用程式使用 ExoPlayer,則預設會支援 HDR 播放。如要瞭解後續步驟,請參閱「確認 HDR 播放支援情形」。

如果應用程式未使用 ExoPlayer,請透過 SurfaceView 使用 MediaCodec 設定 HDR 播放功能。

使用 SurfaceView 設定 MediaCodec

使用 SurfaceView 設定標準的 MediaCodec 播放流程。這樣一來,您就能在播放 HDR 內容時,不必特別處理 HDR 內容:

  • MediaCodec:解碼 HDR 影片內容。
  • SurfaceView:顯示 HDR 影片內容。

下列程式碼會檢查編碼器是否支援 HDR 設定檔,然後使用 SurfaceView 設定 MediaCodec

Kotlin

// Check if there's a codec that supports the specific HDR profile
val list = MediaCodecList(MediaCodecList.REGULAR_CODECS) var format = MediaFormat() /* media format from the container */;
format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10)
val codecName = list.findDecoderForFormat (format) ?: throw RuntimeException ("No codec supports the format")

// Here is a standard MediaCodec playback flow
val codec: MediaCodec = MediaCodec.createByCodecName(codecName);
val surface: Surface = surfaceView.holder.surface
val callback: MediaCodec.Callback = (object : MediaCodec.Callback() {
   override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {
      queue.offer(index)
   }

   override fun onOutputBufferAvailable(
      codec: MediaCodec,
      index: Int,
      info: MediaCodec.BufferInfo
   ) {
      codec.releaseOutputBuffer(index, timestamp)
   }

   override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
      // handle error
   }

   override fun onOutputFormatChanged(
      codec: MediaCodec, format: MediaFormat
   ) {
      // handle format change
   }
})

codec.setCallback(callback)
codec.configure(format, surface, crypto, 0 /* flags */)
codec.start()
while (/* until EOS */) {
   val index = queue.poll()
   val buffer = codec.getInputBuffer(index)
   buffer?.put(/* write bitstream */)
   codec.queueInputBuffer(index, offset, size, timestamp, flags)
}
codec.stop()
codec.release()

Java

// Check if there's a codec that supports the specific HDR profile
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaFormat format = /* media format from the container */;
format.setInteger(
    MediaFormat.KEY_PROFILE, CodecProfileLevel.AV1ProfileMain10);
String codecName = list.findDecoderForFormat(format);
if (codecName == null) {
    throw new RuntimeException("No codec supports the format");
}

// Below is a standard MediaCodec playback flow
MediaCodec codec = MediaCodec.getCodecByName(codecName);
Surface surface = surfaceView.getHolder().getSurface();
MediaCodec.Callback callback = new MediaCodec.Callback() {
    @Override
    void onInputBufferAvailable(MediaCodec codec, int index) {
        queue.offer(index);
    }

    @Override
    void onOutputBufferAvailable(MediaCodec codec, int index) {
        // release the buffer for render
        codec.releaseOutputBuffer(index, timestamp);
    }

    @Override
    void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
        // handle format change
    }

    @Override
    void onError(MediaCodec codec, MediaCodec.CodecException ex) {
        // handle error
    }

};
codec.setCallback(callback);
codec.configure(format, surface, crypto, 0 /* flags */);
codec.start();
while (/* until EOS */) {
    int index = queue.poll();
    ByteBuffer buffer = codec.getInputBuffer(index);
    buffer.put(/* write bitstream */);
    codec.queueInputBuffer(index, offset, size, timestamp, flags);
}
codec.stop();
codec.release();

如要進一步瞭解使用 SurfaceView 的其他 MediaCodec 實作方式,請參閱 Android 相機範例

資源

如要進一步瞭解 HDR 播放功能,請參閱下列資源:

高動態範圍

媒體