大多数浏览器和
Developer App 均支持流媒体播放。
-
优化 Core Image 视频 app 工作流
探索利用 Core Image 的强大处理能力来优化视频 app 性能表现的无限可能。我们将向你展示如何构筑 Core Image 管道,从而将各类效果应用到app内的视频。探索如何在使用 CIContext 的过程中减少你的 app 内存占用,并且学习使用通过 Core Image 滤镜来对 AVPlayView 或 MTKView 视图类型进行视频回放的最佳做法。除此之外,还需探究为何需要通过 Metal Shading 语言编写你的个人定制核心程序,并且了解相关性能提示,从而最大限度上使用 Core Image 管道中 Metal 技术的命令队列。
资源
相关视频
WWDC22
WWDC21
WWDC20
-
下载
(你好 WWDC 2020) 大家好 欢迎参加 WWDC
(为你的视频 app 优化 Core Image 工作流) 大家好 我叫 David Hayward 我将做一个简短的演示 当使用 Core Image 为你的视频 app 添加效果时 如何获得最佳性能 以此为目标 我将讨论以下话题
如何以最好的方式创建 CIContext 如何编写和应用定制的 CIKernels 以及如何让你的视图获得最佳渲染
好的 那我们马上开始 先创建一个 CIContext 创建 CIContext 的第一条建议 是仅在每个视图中创建一个 (创建 CIContext) 上下文很容易创建 但它们初始化时需要时间并占用内存 所以你不会想要经常创建它们 还有 当你创建上下文的时候 有一些选择要设定好 最重要的是让上下文不要缓存中间体 对于视频 每一帧都与之前的不一样 所以禁用此缓存会降低内存使用量
还有 我建议给你的上下文命名 当使用 Core Image 调试技术时 这很有帮助 原理是这样的 Core Image 使用 Metal 但是有些应用想把 Core Image 和 其他 Metal API 结合起来
比如 你想用 Metal texture 作为 Core Image 的输入或输出 在这些情况下 我建议 你创建自己的 CIContext 用 MTLCommandQueue 时间线图是解释原因的最好方法 试想当一个 app 初次 使用 MTLCommandQueue 的场景 将内容渲染成 Metal 材质 涉及到在中央处理器上排队工作 在图形处理器上执行 下一步 app 把材质传递 至 Core Image 用的是自己的内部 Metal 序列 去渲染内容 至另一个 Metal 材质 这也涉及中央处理器上的入队工作 在图形处理器上完成 最后 app 再次使用 Metal 从 Core Image 来渲染输出材质 因为所有的工作 都是在不同的队列上完成 此 app 必须发出等待命令 才能得到正确的结果 这样就由于低效的流程而造成了浪费 解决这个问题的方法是创建 CIContext 具有相同的队列 其他 Metal 渲染所使用的 这允许 app 删除等待
这样 工作就可以被有效地流程化了 从而获得最佳效率 (编写内核) 我们讨接下来要讨论 如何在 Metal 里写 CIKernels 为了在你的视频 app 里获得最佳性能 重要的是效果 在 Metal 中实现 最简单的方式是 尽量使用内置 Core Image 过滤器 因为它们都在 Metal 里实现
为了帮助你使用内置过滤器 我们一直在升级文档 包括参数说明 样本图片 甚至包括示例代码
应用内置过滤器的代码很简约 只需导入 CIFilterBuiltins 创建一个过滤器实例 设置输入属性 并获得输出图像 这是一些你想要在 Metal 中编写 自定义 CIKernels 的其他原因 此外 CIKernels 的通常功能 例如自动平铺和串联 用 Metal 写 你将减少运行时编译时间 通过转移把工作到构建 app 之时 此外 你还将被授予 高性能功能的内核访问权限 例如收集阅读、分组写作和半浮点数学 还能让你的开发者轻松一点 通过键入时语法标亮 构建时进行语法检查 让我们来看一个例子 在 Metal 中编写 CIKernels 的过程 在今天的讲座中 我将用内核演示这一点 在演示中显示 用 AVFoundation 编辑和播放 HDR 视频 为此效果定制的 CIKernel 很容易编写 首先 你将在代码的顶部 键入 CoreImage.h 标头 这样你就可以访问所有普通 Metal 类 以及其他 Core Image 提供的类别 接下来 你将声明该函数 对于必须为外部变量“C”的内核 此示例是 CIColorKernel 因此该函数必须返回一个 float4 像素 并接受一些参数 在这里 第一个参数 是 Core Image sample_t 代表一个输入图像里的像素 这个像素是线性的自左乘 RGBA float4 适用于 SDR 或 HDR 图像
最后一个参数是 Core Image 目的地 提供坐标 要返回的像素数 在执行此内核上 我们使用 dest.coord x 和 y 值来 确定我们在哪条对角线上 然后我们用一些简单的数学来计算 是否应该在斑马条纹上
如果我们在斑马条纹上 并且当前的像素样本 比正常的标准动态范围白色更亮 那么我们将返回一个明亮的红色像素 否则 我们将原样返回输入样本 查看更多细节 参见我在《用 Xcode 构建基于 Metal 的 Core Image 内核》的讲座 (选择视视图类) 最后要讨论的主题是选择 你的视频 app 的最佳视图类 如果你的 app 将效果应用于视频 避免一些视图类很重要 像 UIImageView 和 NSImageView 它们被设计用于显示静态内容 最简单的视图类是 AVPlayerView 因为它会自动完成工作 将过滤后的视频显示到视图中 另一个更加灵活的 提供出色性能的选项是 MetalKitView 我将在下面的内容中讨论 两种试图如何与 Core Image 一起使用
使用 AVPlayerView 非常简单 关键对象是 AVMutableVideoComposition 用视频资产和一个处理程序块初始化 这个区块被视频中的每一帧调用 并传递了一个AVAsynchronous CIImageFilteringRequest 当你的区块被调用时 你需要做的就是创建一个 CIFilter 设置其输入 并获取其输出图像 你只需要把这个图像 递交给所要求的的对象
Xcode 的强大功能是在调试时 你可以将鼠标悬停在 CIImage 变量上 然后会出现一个小弹出窗口 显示对象的地址 但是如果你点击眼睛符号 一个新的窗口将会出现 把组成这个图像的元素 以视觉的形式表现出来 如果你仔细看 你可以看到输入视频帧 是 10 bit HDR 编码 Core Image 已自动对其进行颜色管理 从 HLG 到 Core Image 工作空间 使用 MetalKitView 显示你过滤的视频 是你可以考虑的另一种选择
这是用 MetalKitView 呈现画面时 在 Core Image 中获得最佳性能的方法 在你的 MetalKitView 类中 你要做的第一件事就是 覆盖带框架和设备的初始化 初始化方法是 为视图创建 CIContext 的最佳时机 确保将 framebufferOnly 设置为假 Core Image 就可以使用 Metal Compute 在 macOS 上 如果你的视图支持 HDR 那你应该设定colorPixelFormat 为 rgba16Float 并设置 wantsExtended DynamicRangeContent 为真 接下来在你的 MetalKitView 你需要做的事是 是执行 drawin 视图方法 用这种方法 我们创建 特殊方式的 CIRenderDestination 我们用正确的方式创建目的地的 宽度、高度和像素格式 但是与其传递进 Metal 材质 我们会传递一个返回材质的区块 这允许 CIContext 在等待上一帧完成之前 就开始列队 Metal 的工作 接下来 我们告诉我们的 CIContext 开始将图像渲染到目的地的任务 最后一步是创建一个 CommandBuffer 呈现当前的可绘制对象到视图 以上就是我对于 在你的视频 app 使用 Core Image 获得出色表现的分步讨论 我们回顾了 如何最好地创建 CIContext 应用 CIKernels 并渲染到你的视图 我希望这可以让你为你 app 的视频 添加出色的视觉效果 非常感谢 希望你在 WWDC 2020 接下来的讲座中参会愉快
(你好 WWDC 2020)
-
-
0:52 - Creating CIContext
let context = CIContext(options: [ .cacheIntermediates : false, .name : ”MyAppView” ])
-
1:26 - Creating CIContext 2
let context = CIContext(MTLCommandQueue : queue, options: […])
-
2:59 - Use builtins Whenever possible
import CoreImage.CIFilterBuiltins func motionBlur(inputImage: CIImage) -> CIImage? { let motionBlurFilter = CIFilter.motionBlur() motionBlurFilter.inputImage = inputImage motionBlurFilter.angle = 0 motionBlurFilter.radius = 20 return motionBlurFilter.outputImage }
-
3:56 - Put your kernels in .ci.metal sources
// MyKernels.ci.metal #include <CoreImage/CoreImage.h> // includes CIKernelMetalLib.h using namespace metal; extern "C" float4 HDRZebra (coreimage::sample_t s, float time, coreimage::destination dest) { float diagLine = dest.coord().x + dest.coord().y; float zebra = fract(diagLine/20.0 + time*2.0); if ((zebra > 0.5) && (s.r > 1 || s.g > 1 || s.b > 1)) return float4(2.0, 0.0, 0.0, 1.0); return s; }
-
5:50 - Using AVPlayer View
let videoComposition = AVMutableVideoComposition( asset: asset, applyingCIFiltersWithHandler: { (request: AVAsynchronousCIImageFilteringRequest) -> Void in let filter = HDRZebraFilter() filter.inputImage = request.sourceImage let output = filter.outputImage if (output != nil) { request.finish(with: output, context: myCtx) } else { request.finish(with: err) } } )
-
7:01 - Using MTKView
class MyView : MTKView { var context: CIContext var commandQueue : MTLCommandQueue override init(frame frameRect: CGRect, device: MTLDevice?) { let dev = device ?? MTLCreateSystemDefaultDevice()! context = CIContext(mtlDevice: dev, options: [.cacheIntermediates : false] ) commandQueue = dev.makeCommandQueue()! super.init(frame: frameRect, device: dev) framebufferOnly = false // allow Core Image to use Metal Compute colorPixelFormat = MTLPixelFormat.rgba16Float if let caml = layer as? CAMetalLayer { caml.wantsExtendedDynamicRangeContent = true } }
-
7:29 - Using MTKView 2
func draw(in view: MTKView) { let size = self.convertToBacking(self.bounds.size) let rd = CIRenderDestination(width: Int(size.width), height: Int(size.height), pixelFormat: colorPixelFormat, commandBuffer: nil) { () -> MTLTexture in return view.currentDrawable!.texture } context.startTask(toRender:image, from:rect, to:rd, at:point) // Present the current drawable let cmdBuf = commandQueue.makeCommandBuffer()! cmdBuf.present(view.currentDrawable!) cmdBuf.commit() }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。