HDR 视频播放

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,请使用 MediaCodec 通过 SurfaceView 设置 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 播放的更多信息,请参阅以下资源。

HDR

媒体

  • 媒体 API 参考:了解有关媒体 API 的更多信息。
  • ExoPlayer:了解如何使用 ExoPlayer 库设置您的应用程序。