这个公众号会路线图式的遍历分享音视频技术:音视频基础(完成) → 音视频工具(完成) → 音视频工程示例(进行中) → 音视频工业实战(准备)。**关注一下成本不高,错过干货损失不小 **
苹果用 EDR 这个词是为了跟 HDR 区分开,因为 HDR 在不同的场景可能对应着不同的理解:
- HDR 显示:更生动的显示亮色和暗色
- HDR 格式:HDR10、Dolby Vision
- HDR 转换函数:PQ、HLG
- 色调映射(Tone Mapping):HDR → SDR
而 EDR(Extended Dynamic Range)是苹果推出的一套渲染管线技术,以支持在不同的屏幕上同时正确显示 SDR 和 HDR 内容。当显示 HDR 的内容时,EDR 并不会直接将 HDR 区域变得更亮,而是识别到 HDR 内容后提高整体屏幕亮度的同时,降低非 HDR 区域的白点值,使得其看起来没有那么亮。
下图展示了 EDR 在 Pro Display XDR 显示器上的预设:
Pro Display XDR preset Pro Display XDR preset一般台式电脑显示器的持续亮度在 350 尼特左右,有些专业显示器的会高一点,但大部分持续不了较长时间。Pro Display XDR 则可以达到 1000 尼特全屏持续亮度,峰值亮度达到 1600 尼特。这样的性能,可以提供持续的高亮度。配合高效的背光控制,出色的明暗对比范围从最亮的白一直跨越到最深的黑,可实现 1000000:1 对比度和格外逼真的 XDR 影像。
1、在 iOS 上探索 EDR
参见:Explore EDR on iOS[1]
这个 Session 的内容包含了下面几点:
1)介绍了 EDR API 的新增特性
- EDR API 开始支持 iOS 和 iPadOS。
- 12.9 英寸 iPad Pro 新增两个新特性:Reference 模式支持 EDR 渲染;Sidecar 支持 EDR 渲染(Sidecar 是苹果的一项技术,支持将 iPad 作为 Mac 的扩展屏)。
其中,Reference 模式对 EDR 的支持如下:
- 修正 SDR 峰值亮度为 100 nits;
- 修正 HDR 峰值亮度为 1000 nits(这样就留出了 10 倍(1000 nits / 100 nits)的 EDR headroom);
- 支持关闭 HDR Tone Mapping;
- 支持关闭环境光自适应(比如,iPhone 的 True Tone、Auto Brightness、Night Shift 等功能);
- 支持白点和亮度的手动校准;
- Reference 模式支持的最常见 5 种 HDR/SDR 视频格式如下图(不在下表中的格式,会使用默认模式):
2)回顾了 EDR 的技术方案
如下图所示,SDR 的像素浮点数表示范围为 [0.0, 1.0]
,其中 0.0 表示黑色,1.0 表示白色。在 EDR 的像素浮点数表示中,SDR 的部分映射到 [0.0, 1.0]
,而大于 1.0 的部分就是比 SDR 更亮的 HDR 部分。
不像其他的 HDR 格式那样,EDR 不会做 Tone Mapping 将像素值都映射到 [0.0, 1.0]
的范围。这就意味着在渲染时,它有一套新的机制。
当渲染时,像素浮点值范围为 [0.0, 1.0]
的 SDR 内容是始终会正常渲染的。(1.0, EDR headroom]
范围的 HDR 内容也是可以渲染的。但是,超过了 EDR headroom 的部分就会被丢掉。
EDR headroom 的存在支持了亮度更高的 HDR 内容,但是它具体是多少呢?其实,EDR headroom 是动态的,它的值受到多种因素的影响,比如:设备的显示技术、当前的显示亮度等等。
我们通常可以使用下面这个公式粗略估计 EDR headroom:
Headroom ≈ Display Peak / SDR
比如,上面提到的 Reference 模式下的 EDR headroom 就是 1000 nits / 100 nits
,即 10 倍。
下表中列出了部分设备的 EDR headroom:
一些设备的 EDR headroom .png3)EDR 内容的读取
对于 HDR 图片文件的处理流程如下:
HDR 图片文件的渲染流程示例代码如下:
HDR 图片文件的渲染示例代码 HDR 图片文件的渲染示例代码4)使用 EDR
要使用 EDR 有下面几个注意点:
- 要使用
CAMetalLayer
; - 设置
wantsExtendedDynamicRangeContent
为 YES; - 内容支持 EDR:
- 支持像素缓冲区格式;
- 支持转换函数;
- 使用扩展的颜色空间。
5)查询 EDR headroom
EDR headroom 的信息可以通过 NSScreen(macOS)或 UIScreen(iOS)来获取。
比如,通过 UIScreen(iOS)可以获取这些信息:
- 颜色的动态值:
- 当前屏幕可能的最大值;
- 当前的最大值;
- Reference 模式的状态;
- Reference 模式状态变化的通知。
示例代码如下:
查询 EDR headroom 监听 Reference 模式变化通知通过查询 EDR headroom 的信息,可以用来做自定义的 Tone Mapping 算法。
6)色调映射(Tone Mapping)
如果你不想去做自己的 Tone Mapping 算法,而是想用 Apple 内置的算法,需要用到这些能力:
- CoreAnimation 库提供了跟 EDR metadata 相关的接口;
- 支持多种 metadata 参数设置,包括 HDR10 和 HLG 格式;
- 注意,内置的 Tone Mapping 算法不是全局支持的,使用之前要检查一下。
如下图所示,在使用之前要检查当前设备是否可以支持 EDR metadata,然后去初始化对应格式的 EDR metadata,最后将其应用到 CAMetalLayer 来渲染时,就用使用系统自带的 Tone Mapping 算法了。
![初始化不同格式的 CAEDRMetadata](https://img.haomeiwen.com/i27498310/dfe0ddf0bd7453ff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)下图展示了如何初始化不同格式的 EDR Metadata:
初始化不同格式的 CAEDRMetadata下图展示了不同颜色空间格式建议的 EDR Metadata 的构造方法:
初始化不同格式的 CAEDRMetadata 22、基于 CoreImage、Metal、SwiftUI 展示 EDR 内容
参见:Display EDR content with Core Image, Metal, and SwiftUI[2]
在这个 Session 中,回顾了一下 EDR 相关的概念和术语,然后展示了一个基于 Core Image 来添加 EDR 支持的 Demo,最后提到了如何用 CIFilter 来创建支持 EDR 的图像去服务于相关内容的生产。
1)EDR 相关的概念和术语
EDR 相关的概念和术语,在前面的内容已经介绍过了,这里就不再赘述了。
不过,这里提到了可以获取到 EDR 图像内容的几种途径,可以了解一下:
- 使用 TIFF、OpenEXR 等支持浮点数存储像素值的图像格式来获取 EDR 图像;
- 通过 AVFoundation 从 HDR 的视频中截帧获得 EDR 图像;
- 使用 Metal API 可以将 EDR 环境渲染到纹理上;
- ProRAW、DNG 等存储原始图像信息的格式,可以用于渲染来还原 EDR 的高亮特性。
2)在 SwiftUI 应用中使用 CoreImage 和 Metal 并为其添加 EDR 支持
相关源码见:Generating an Animation with a Core Image Render Destination[3]
这个 Demo 中绘制了一幅动画的 CIImage 并通过 Metal 来渲染它,这里使用了 MTKView。
Animate a filtered image to a Metal view大致流程如下图所示,即 MetalView 调用其 delegate
来绘制,作为代理的 Renderer 在 draw()
方法中调用 ContentView 来提供图像去绘制:
其中,最核心的代码分别是:
- MetalView 的
makeView()
方法:
- Renderer 的
draw()
方法:
- ContentView 的
provider()
方法:
接下来,则是在这个流程中添加 EDR 支持,包括 3 个步骤:
- 1、初始化 MetalView 时,为其添加 EDR 配置;
- 2、每次渲染动作前,计算 EDR headroom;
- 3、基于 EDR headroom 构造 CIImage。
对应的代码改动如下:
- MetalView 的
makeView()
方法中,当构建 MetalView 时,要设置对应 layer 的几个属性如下:
- Renderer 的
draw()
方法中,每次渲染前需要获取 EDR headroom,并传给 provider:
- ContentView 的
provider()
方法中,要增加 headroom 参数,并使用这个 headroom 参数和 CIFilter 来一起构建 CIImage,这个 CIImage 就可以实现 EDR 渲染展示了:
3)使用内置的 CIFilter 来支持 EDR
CoreImage 中有超过 150 款 内置的 CIFilter 支持 EDR。
CIFilters with EDR在使用一款 CIFilter 时,要想知道它是否支持 EDR,可以用如下的代码来做一下检查:
Check if a filter supports EDR下面展示了如何基于 headroom 编写一个可以生成高亮白色的 shading image,并结合一个 Ripple 效果的 CIFilter 来生成 EDR 的图像。
Ripple CIFilter supports EDR Ripple CIFilter supports EDR 24)基于 CIColorCube filters 编写自定义的 CIFilter 来支持 EDR
其中 CIColorCubeWithColorSpace 是一个比较受欢迎的用于 SDR 图片的滤镜。以前,在使用它时有一个严格的限制:输入和输出的 RGB 颜色值都在 0-1 的范围内。当我们要支持 EDR 时,可以配置 CIColorCubeWithColorSpace 滤镜的颜色空间为 EDR 格式的颜色空间,比如:HLG 或 PQ,这时候就可以突破 RGB 颜色值只在 0-1 范围的限制了,从而生产出最佳效果的 EDR 的内容。但是,需要注意,对应的 cube data 需要在对应颜色空间的有效范围内。同时,还可能需要增加 cube data 的维度。
有时候,你可能想要使用 SDR cube data 用于 EDR 图像,在最新的 API 中,可以设置 extrapolate
属性为 true
来告诉 filter 来对 SDR cube data 进行推断,这时候你就可以给一个 filter 输入一个 EDR 图像,并从输出获得 EDR 图像。
5)编写自定义 CIKernel 的最佳实践
以往对于 RGB 的值必须保持在 0-1 范围内的限制可以去掉了,超过这个范围,CIKernel 也可以正常工作。
CIKernel 最佳实践:RGB 可以大于 1但是,alpha 值仍然需要保持在 0-1 的范围内。
CIKernel 最佳实践:Alpha 必须小于 13、基于 AVFoundation、Metal 展示 HDR 视频
参见:Display HDR video in EDR with AVFoundation and Metal[4]
这个 Session 中介绍了如何基于 AVFoundation 和 Metal 来构建高效的 EDR 渲染管线。其中,包括:
- 介绍如何支持 EDR 视频的播放。不仅是简单的基于黑盒的 AVPlayer 来实现,会深入的介绍如何基于 AVFoundation 来解码 HDR 视频,并在自定义的 EDR layer/view 上渲染。
- 介绍如何支持 EDR 视频的后处理。基于 CoreVideo Display Link 实时访问解码后的的视频帧并送给 CoreImage filter 或 Metal shader 来进行图像或特效处理,最后将处理后的视频帧交给 Metal 来渲染。
整个 Session 的内容从下面几个方面展开:
1)Apple EDR 视频框架
Apple EDR 视频框架如下图所示:
Apple EDR 视频框架首先是 AVKit 框架,我们可以基于 AVKit 来创建支持视频播放的用户界面,完成传输控制、章节导航、画中画播放、字幕、隐藏式字幕显示等功能,AVKit 可以将 HDR 内容在 EDR 管线中播放。这些都可以通过 AVPlayerViewController 来实现。不过,如果想要进一步处理视频帧,那就要使用更底层的媒体框架来控制整个 pipeline。
接下来是 AVFoundation 框架,AVFoundation 是处理基于时间的音视频媒体的全功能框架。使用 AVFoundation 可以轻松的播放、创建和编辑 QuickTime 电影和 MP4 文件,播放 HLS 流,并在应用中构建强大的媒体功能。本次演讲中会探索 AVPlayer 和相关 AVPlayerLayer 接口的使用。
Core Video 是一个为数字视频提供管道模型的框架。它通过将流程划分为离散的步骤来简化处理视频的方式。Core Video 还可以更轻松地访问和操作单个帧,而不必担心数据类型之间的转换或显示同步。我们将演示如何结合使用 DisplayLink、CVPixelBuffer、Core Image,以及如何配合使用 CVMetalTextureCache、Metal。
接着是 Video Toolbox 框架,这是一个底层框架,提供对硬件编码器和解码器的直接访问。Video Toolbox 提供视频压缩和解压缩服务,以及存储在 Core Video 像素缓冲区中的光栅图像格式之间的转换。VTDecompressionSession 是一个强大的底层接口,本次不会讨论,但高级开发人员可以进一步研究。
最后是 Core Media 框架,该框架定义了 AVFoundation 和其他高级媒体框架使用的媒体管道。可以使用 Core Media 的底层数据类型和接口来有效地处理媒体样本、管理媒体数据队列。
对于不同的应用场景,我们可以选择不同的框架来实现:
- 简单的播放 HDR 视频,可以使用 AVKit 和 AVFoundation;
- 在指定的 layer 上播放 HDR 视频,可以使用 AVPlayerLayer;
- 对 HDR 视频帧做后处理,可以使用 AVPlayer、CADisplayLink 将 CVPixelBuffer 发送给 CoreImage 进行图像处理;
- 对 HDR 视频帧做后处理和渲染,可以使用 AVPlayer、CVMetalTextureCache 将 MetalTexture 发送给 Metal 进行图像处理和渲染。
2)使用 AVKit 和 AVFoundation
如果需要简单的播放 HDR 视频媒体渲染到 EDR,可以使用 AVPlayerViewController 渲染 AVPlayer,也可以使用 AVPlayer 和 AVPlayerLayer 在 iOS 或 macOS 上播放自己的视图。
AVPlayer 的用法 .png示例代码如下:
AVPlayerViewController 使用示例 AVPlayer+AVPlayerLayer 使用示例如果需要实现实时视频效果,比如:
- 特效:
- Color grading
- Chroma keying
- 图像处理:
- CoreImage filters
- Metal shaders
大致流程如下:
实时视频效果最终渲染一般都使用 CAMetalLayer,要支持正确的渲染 HDR 视频,还需要为 CAMetalLayer 设置一些属性:
CAMetalLayer 属性设置 .pngCAMetalLayer 属性设置
接下来,继续展示如何结合 AVPlayer 和 CADisplayLink 来实时访问解码的视频帧,大致流程如下:
AVPlayer+CADisplayLink 获取视频帧 .png下面是各个步骤对应的示例代码:
步骤 1 步骤 2 步骤 3 步骤 4接下在 CADisplayLink 的回调中,我们就可以从 AVPlayerItemVideoOutput 中来读取 CVPixelBuffer 图像信息进行后处理了。
一种方式是将 CVPixelBuffer 发送到 Core Image 进行处理。Core Image 可以将一个或多个 CIFilter 串起来,为视频帧提供 GPU 加速的图像处理。但是需要注意,并非所有的 CIFilter 都支持 EDR,可以通过 CIFilter.filterNames(inCategory: kCICategoryHighDynamicRange)
来遍历支持 EDR 的滤镜来使用。在下面的示例代码中,我们来添加一个简单的滤镜效果:
另一种方式是使用 Metal 和自定义的 Metal shader 来处理和渲染 CVPixelBuffer。我们来简单介绍一下将 CVPixelBuffer 转换为 Metal 纹理的过程:
- 从 CVPixelBuffer 中获取 IOSurface 对象;
- 创建一个 MetalTextureDescriptor;
- 把这个 MetalTextureDescriptor 作为参数,使用
newTextureWithDescriptor
从 MetalDevice 创建一个 MetalTexture。
这里要小心可能存在纹理被重复使用和过度绘制的问题,所以要小心的加锁;此外,并非所有的 PixelBuffer 格式都被 MetalTexture 支持,这也是为什么我们在示例中用 half float 格式的原因。同时,实现这种转换以保持最佳性能是一个深入的话题,这里先不探讨了。
正因为这些复杂性,我们才建议从 Core Video Metal 纹理缓存中获取 Metal 纹理,这是我们在下一节内容中要介绍的。
3)使用 CoreVideo 和 Metal
CVMetalTextureCache 是一种将 CVPixelBuffer 与 Metal 结合使用的简单有效的方法。CVMetalTextureCache 很方便,可以直接从缓存中获取 Metal 纹理,无需进一步转换。CVMetalTextureCache 自动桥接 CVPixelBuffer 和 MetalTexture,从而既简化了代码,又保持了高效。通过保持 MTLTexture 到 IOSurface 的映射,CVMetalTextureCache 与 CVPixelBufferPools 配合使用还提供了性能优势。最后,使用 CVMetalTextureCache 消除了手动跟踪 IOSurfaces 的需要。下面的示例将展示如何使用 CVMetalTextureCache 从 Core Video 中直接提取 Metal 纹理,大致流程如下图所示:
CVMetalTextureCache 从 CoreVideo 提取 Metal 纹理下面是各个步骤对应的示例代码:
步骤 1、2 步骤 3、4注意,在用 Objective-C 时需要保证在释放 CVMetalTextureRef 之前要完成对 Metal 纹理的使用,可以借助 Metal 命令缓冲区的 completion 回调来实现。
参考资料
[1]Explore EDR on iOS: https://developer.apple.com/videos/play/wwdc2022/10113/
[2]Display EDR content with Core Image, Metal, and SwiftUI: https://developer.apple.com/videos/play/wwdc2022/10114
[3]Animation with a Core Image Render: https://developer.apple.com/documentation/coreimage/generating_an_animation_with_a_core_image_render_destination
[4]Display HDR video in EDR with AVFoundation and Metal: https://developer.apple.com/videos/play/wwdc2022/110565/
- 完 -
推荐阅读《WWDC 2022 音视频相关 Session 概览(HLS 相关)》《iOS 视频处理框架及重点 API 合集》《iOS 音频处理框架及重点 API 合集》
网友评论