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 Camera 示例

资源

有关 HDR 播放的更多信息,请参阅以下资源

HDR

媒体