Discover rolling clips with ReplayKit
Media & Web 进阶 18m

探索 ReplayKit 滚动录制片段

Discover rolling clips with ReplayKit

2021年6月11日

在 Apple 官方观看视频

一句话判断

Rolling Clips 是 ReplayKit 的”回溯录制”功能 —— 游戏不需要一直录屏,只在精彩瞬间发生时回捞过去几秒的画面,存储和性能开销都降了一个数量级,但实现细节比想象中复杂。

这场 Session 讲了什么

Session 介绍了 ReplayKit 在 iOS 15 中的新功能:Rolling Clips(滚动片段录制)。传统 ReplayKit 需要用户手动开始/停止录制,整个过程中视频数据持续写入磁盘。Rolling Clips 改变了这个模型 —— 它始终在后台缓冲最近 N 秒的画面(不写入磁盘),当用户触发”保存”时,把缓冲区的内容导出为视频片段。

工作流程:app 启动 Rolling Clips -> 系统在内存中保持一个环形缓冲区(默认 15 秒)-> 精彩时刻发生 -> 用户点击保存或 app 自动触发 -> 缓冲区内容被编码为视频文件 -> 保存到相册或分享。

这个功能特别适合游戏场景:击杀、进球、Boss 击败等瞬间,玩家不需要提前录制,事后回捞即可。

值得深挖的点

环形缓冲区的内存管理。Rolling Clips 把视频帧保持在内存中,不写入磁盘。缓冲区大小取决于分辨率和帧率:1080p 60fps 的 15 秒片段大约需要 150-200MB 内存。在设备内存紧张时(如打开了很多 app),系统可能会静默停止 Rolling Clips 而不通知你。你需要监听 RPScreenRecorder 的状态变化来处理这种情况。

自动触发的时机选择。Session 建议两种触发模式:手动触发(用户按按钮保存)和自动触发(游戏逻辑检测到精彩瞬间)。自动触发的挑战在于”精彩”的定义 —— 击杀数、连击、Boss 血量变化等游戏事件都可以作为触发条件。但要注意误触发:如果每次小事件都保存,用户相册里会堆满无意义的短片段。

代码片段

启动 Rolling Clips 录制

import ReplayKit

class ClipRecorder: NSObject, RPScreenRecorderDelegate {
    let recorder = RPScreenRecorder.shared()

    func startRollingClip() {
        guard recorder.isAvailable else {
            print("ReplayKit 不可用")
            return
        }
        recorder.delegate = self

        // 启动 Rolling Clips 模式
        recorder.startClipBuffering(
            captureHandler: { sampleBuffer, bufferType, error in
                if let error = error {
                    print("缓冲错误: \(error)")
                    return
                }
                // sampleBuffer 会自动进入环形缓冲区
            },
            completionHandler: { error in
                if let error = error {
                    print("启动 Rolling Clips 失败: \(error)")
                } else {
                    print("Rolling Clips 已启动,持续缓冲中...")
                }
            }
        )
    }
}

保存缓冲区内容为视频片段

import ReplayKit

extension ClipRecorder {
    // 用户触发或游戏自动触发保存
    func saveClip() {
        // 导出最近 15 秒的缓冲内容
        recorder.exportClip(
            to: getOutputURL(),
            completionHandler: { error in
                if let error = error {
                    print("导出失败: \(error)")
                    return
                }
                print("精彩片段已保存")
                // 可选:保存到相册
                self.saveToPhotoLibrary()
            }
        )
    }

    private func getOutputURL() -> URL {
        let tempDir = FileManager.default.temporaryDirectory
        let fileName = "clip_\(Int(Date().timeIntervalSince1970)).mp4"
        return tempDir.appendingPathComponent(fileName)
    }
}

游戏事件触发的自动保存

// 在游戏逻辑中检测精彩时刻
func onPlayerAchievement(_ achievement: GameAchievement) {
    switch achievement.type {
    case .killStreak where achievement.count >= 5:
        // 五连杀!自动保存最近片段
        clipRecorder.saveClip()
        showClipSavedNotification("五连击杀已保存!")
    case .bossDefeated:
        clipRecorder.saveClip()
        showClipSavedNotification("Boss 击败片段已保存!")
    case .levelCompleted where achievement.time < 60:
        // 速通成就
        clipRecorder.saveClip()
        showClipSavedNotification("速通记录已保存!")
    default:
        break
    }
}

最佳实践

  1. 缓冲时长根据游戏类型调整。格斗/射击游戏 15 秒足够(击杀瞬间很短);MOBA/RPG 建议延长到 30 秒(团战/ Boss 战时间更长)。
  2. 在低端设备上主动禁用 Rolling Clips。如果可用内存低于 1GB,启动缓冲可能导致系统杀 app。用 ProcessInfo.processInfo.physicalMemory 检查设备能力。
  3. 给用户一个明显的”正在录制”指示器。虽然 Rolling Clips 不写磁盘,但用户需要知道”随时可以回捞”这个功能的存在。在 HUD 上放一个小图标,比埋在设置菜单里有效。

还有什么值得关注

  • Rolling Clips 支持同时录制麦克风和系统音频。
  • 导出的视频片段可以添加 app 的水印或 Logo overlay。
  • RPScreenRecorderisClipBufferingAvailable 属性可以检测设备是否支持此功能。
WWDC 2021