时间线效果层,为什么不能只复用一个处理器?

问题与范围

做时间线预览或离线导出时,效果层很容易被设计成一个"可复用处理器":时间线保存一个实例,预览调用一次,导出再调用一次。短片或单效果场景可能暂时看不出问题;加入多个效果、时间范围、关键帧和重复运行后,预览与导出却可能出现不同结果。

常见表现包括:预览有效果而导出没有,导出正确而播放器没有接线;效果本应在结束时刻停止,却仍处理相邻片段;或者一次运行留下的缓存、计数器、临时资源影响下一次运行。本文只讨论时间线效果的编排合同,不讨论编解码、完整编辑器能力、真机 HDR 视觉表现或长视频性能。

先给结论

  • 时间范围应是半开区间 [start, end):开始时生效,结束时不再参与处理。
  • 多效果顺序必须先按 layerLevel,同层再按插入顺序;不能依赖数组的偶然排列。
  • 预览和导出应共享同一个已编译的编辑快照,而不是各自临时拼装逻辑。
  • "共享快照"不等于共享有状态的处理器实例;独立运行要得到独立实例。
  • 调用方显式给出的非空导出处理器数组,需要有明确覆盖语义,不能暗中与时间线效果叠加。

效果的时间范围不是一个 if

效果在素材时间线里不是"有帧就处理"。它属于一个 composition-time 区间:如果效果从 1 秒开始、持续 1 秒,那么 1 秒和 1.99 秒的帧应进入处理,0.5 秒和 2 秒的帧则应原样通过。

这里采用 [start, end) 的原因是相邻片段可以无重叠地拼接:前一个效果在 end 停止,后一个效果若从同一时间开始则接管该帧。若把结束点也包含进去,同一帧可能被两条相邻规则同时解释,边界就不再可推导。

时间判断还必须以帧的 presentation time 为依据,而不能用处理器收到第几帧、已经运行多久来推算。预览会拖动、暂停和重新建链,离线导出也可能重试;帧序号或实例内部计数都不是稳定的 composition-time 语义。

叠层顺序要成为合同

多个效果叠加时,"先执行谁"会直接改变输出。可靠的规则至少需要两个排序键:先按 layerLevel 升序,同一层级保留添加顺序。这样相同编辑描述在预览、导出和重编译后仍有可解释的顺序。

强度和关键帧也应从同一份 composition-time 描述求值。当前实现会把强度约束在 0 到 1,并为中间强度走单独的混合路径;源码选择了线性工作色彩空间。这里尤其要区分"实现意图"和"已验证结果":本轮在 macOS arm64e 跑定向测试时,时间窗、顺序、快照和实例隔离相关 8 项通过,但半强度混合用例未通过。因此,不能把当前本机结果扩大为完整的颜色一致性或设备级视觉保证。

共享的是快照,不是处理器对象

一个时间线在用户修改效果、强度或图层后,旧的预览和导出任务不应该被反向改写。较稳妥的模型是先编译出 edit snapshot:它冻结这次任务的层、时间范围和参数;预览与导出都从该快照取得一致的描述。

不过,运行对象仍应隔离。EffectSource(makeProcessor:) 用工厂在每次编译后构造处理器,而不是把同一个实例塞给所有执行路径。这样预览拖动、播放器重建、导出重试或并行任务不会共享处理器内部的缓存、临时纹理、计数器或取消状态。

可以把边界概括为:

text 复制代码
Timeline snapshot = 冻结"何时、以何种顺序、用什么参数"
Processor instance = 执行某一次预览或导出的短生命周期对象

如果调用方在导出时明确传入非空的 videoProcessors,这份显式配置会覆盖时间线效果。覆盖优先级必须写进合同;否则"替换"还是"叠加"会取决于调用顺序,排查结果会非常困难。

用测试守住可迁移的边界

Kakapos 的 TimelineEffectContractTests 直接覆盖了半开时间窗、不同 layer level 的顺序、同层插入顺序、重新编译后的快照隔离、显式导出处理器覆盖,以及工厂生成不同实例。这些测试并非只验证一个效果能运行,而是在验证时间线描述、运行实例和输出之间能否保持可预测关系。

本轮实际命令为:

text 复制代码
swift test --filter TimelineEffectContractTests

在 macOS arm64e 上共执行 9 项:上述 8 项通过;半强度线性混合用例失败。这个结果说明时间线合同的主体有定向覆盖,也说明混合路径仍需要在可复现的图像环境中继续定位。它不等同于真机 HDR、长视频性能或完整视频编辑器验收。

Harbeth 与 Kakapos 的职责边界

Kakapos 在这里负责媒体生命周期与编排:何时处理、怎样排序、如何冻结编辑描述、预览与导出如何共享语义,以及每次运行如何隔离。若应用把 Harbeth 注入 FrameProcessor,Harbeth 负责单帧 GPU 图像处理,例如滤镜、纹理变换或渲染;它不决定时间线效果在哪个时刻生效,也不拥有预览或导出的生命周期。

这种划分让底层渲染后端可以替换,而时间线合同仍由媒体层保持。单帧能力越复杂,越需要让上层明确时间窗、顺序、快照和运行实例这四件事。

小结

时间线效果不是"把一个 Filter 放进数组"就结束了。先固定 [start, end),再固定叠层排序;用编译快照让预览与导出消费同一份编辑描述;最后为每个运行创建独立处理器。这样即便底层帧处理后端更换,时间线语义仍能被测试和复现。

你的时间线效果是由同一个处理器同时服务预览和导出,还是在编译阶段生成独立的运行实例?欢迎分享你遇到的状态泄漏、边界帧或预览导出不一致问题。

源码与参考实现

本文基于本轮本机源码、README 和定向测试;它不代表已经完成真机视觉、HDR 或性能验收。

相关推荐
ZZH_AI项目交付2 小时前
实时字幕负责快,本地录音负责不丢:一个 iOS 录音功能为什么需要两条链路
ios·ai编程
Cc.Y2 小时前
Swift深度链接的极简之道
ios·cocoa·swift
AI砖家4 小时前
iPhone Duo 上架新规:旧 App 怎么兼容,还能不能继续更新?
ios·cocoa·iphone·ios上架·ios上架新规
黑科技iOS上架4 小时前
小蟹iOS混淆:Objective-C项目实战
ios·审核·ios混淆·执行程序差异化
茶底世界之下4 小时前
照片边缘太抢眼?用 Awaking 做一次 1:1 裁切
ios
Digitally5 小时前
如何在iPhone上永久删除文件而无需恢复
ios·iphone
黑科技iOS上架5 小时前
iOS二进制混淆实战:工程级指纹防护彻底解决4.3同质化拒审
ios·审核·ios混淆·执行程序差异化
传奇开心果编程14 小时前
【SwiftUI提高练中学】第9课 安全与隐私
swiftui·移动开发·swift·编程语言理论·开发范式·编程框架·声明式ui
CocoaKier16 小时前
苹果商店详情顶部头图已面向所有开发者开放!
ios·apple