2025/11/17 18:27:18
录制与回放
声网 Fastboard SDK 提供录制与回放功能,支持将实时房间中的所有互动行为录制下来,并支持高质量回放。相较于传统的录屏模式,白板录制和回放具有以下优势:
- 高效录制:采用信令记录方式,相比传统录屏模式占用带宽更少、存储空间更小
- 高质量回放:回放时还原现场效果,画质清晰、动画流畅
- 服务端录制:录制在服务端自动进行,无需客户端额外处理
- 灵活回放:支持指定时间范围回放、播放控制、视角跟随等功能
- 完整记录:录制白板操作、自定义事件、视角变化、多窗口应用等所有互动行为
- 音视频同步:支持白板回放与音视频播放同步
本文介绍如何开启白板录制功能以及如何使用 Fastboard SDK 实现白板内容回放。
技术原理
白板录制
声网互动白板录制不是屏幕录制,而是记录信令与数据增量构成的私有二进制数据。录制过程完全在服务端进行,具有以下优势:
- 自动录制:只需在创建房间时开启录制功能,服务端会自动录制房间内的所有互动行为
- 智能管理:只有在房间活跃时才会生成录制信息,房间无活动时录制会自动停止
- 数据紧凑:录制生成的数据相比传统音视频格式更紧凑,占用存储空间更小
- 多窗口支持:完整记录 Fastboard 多窗口模式下的所有应用和操作
白板回放
Fastboard iOS SDK 本身专注于实时互动白板房间的快速创建和管理,不提供回放功能的封装。回放功能完全由底层的 Whiteboard SDK 提供。
回放时,SDK 会依照录制的信令与数据增量还原现场,得到的效果和实时互动时一模一样。回放支持:
- 指定时间范围回放
- 播放、暂停、跳转等播放控制
- 播放速度调节
- 视角跟随和自由视角切换
- 自定义事件和全局状态变化的回放
- 多窗口应用的完整回放
- 音视频与白板同步回放
前提条件
开始前,请确保:
- 已在项目中集成声网 Fastboard SDK。详见快速开始。
- 已获取有效的 App Identifier 和 Room Token。详见获取互动白板项目的安全密钥。
- 如需在业务服务器上创建房间,请参考房间管理服务端 API。
实现录制功能
开启录制
要开启白板录制功能,需要在创建房间时设置 isRecord: true 参数。通过服务端 API POST 创建房间创建支持录制的房间。
以下示例展示如何通过服务端 API 创建支持录制的房间:
Swift
// 注意:建议在业务服务器上执行创建房间操作,不要在客户端执行
let url = URL(string: "https://api.netless.link/v5/rooms")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("NETLESSSDK_YWs9xxxxxxM2MjRi", forHTTPHeaderField: "token") // 替换为你的 SDK Token
request.setValue("cn-hz", forHTTPHeaderField: "region") // 替换为你的数据中心
let parameters: [String: Any] = [
"name": "我的 Fastboard 录制房间",
"isRecord": true // 开启录制功能
]
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("创建房间失败:", error)
return
}
if let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
print("房间 UUID:", json["uuid"] ?? "")
print("房间已开启录制功能")
}
}
task.resume()
注意
- SDK Token 是重要的安全凭证,建议在业务服务器中使用,不要写死在客户端代码中。
- 开启录制功能后,该房间内的所有互动行为都会被自动录制。
录制内容
开启录制功能后,以下内容会被自动录制:
- 白板绘制操作(画笔、图形、文字等)
- 场景切换和内容变化
- 图片、音视频、课件(PPT、DOC)的展示和操作
- 多窗口应用的所有操作和状态变化
- 用户视角变化
- 自定义事件(通过
dispatchMagixEvent发送的事件) - 全局状态变化(
GlobalState的修改)
实现回放功能
由于 Fastboard iOS SDK 不提供回放功能的封装,你需要通过 FastRoom 对象的 whiteSDK 属性访问底层 Whiteboard SDK 来实现回放功能。
创建回放播放器
使用 WhiteSDK 的 createReplayerWithConfig 方法创建回放播放器:
Swift
import UIKit
import Whiteboard
class ReplayViewController: UIViewController {
var boardView: WhiteBoardView!
var whiteSDK: WhiteSDK!
var player: WhitePlayer?
override func viewDidLoad() {
super.viewDidLoad()
// 1. 创建白板视图
boardView = WhiteBoardView()
boardView.frame = view.bounds
view.addSubview(boardView)
// 2. 配置并初始化 WhiteSDK
let config = WhiteSdkConfiguration(app: "RMxxxAQ") // 替换为你的 App Identifier
config.region = .CN // 替换为你的数据中心
whiteSDK = WhiteSDK(whiteBoardView: boardView, config: config)
// 3. 配置回放参数
let playerConfig = WhitePlayerConfig(
room: "a7xxx69", // 房间 UUID
roomToken: "NETLESSROOM_YWxxxjk" // Room Token
)
// 设置回放时间范围
playerConfig.beginTimestamp = NSNumber(value: Date(timeIntervalSince1970: 1609459200).timeIntervalSince1970) // 回放开始时间
playerConfig.duration = NSNumber(value: 45 * 60) // 回放持续时间(秒)
// 4. 创建回放播放器
whiteSDK.createReplayer(with: playerConfig, callbacks: self) { [weak self] success, player, error in
if let error = error {
print("创建回放播放器失败:", error.localizedDescription)
return
}
if success, let player = player {
self?.player = player
print("回放播放器创建成功")
// 开始播放
player.play()
}
}
}
}
// MARK: - WhitePlayerEventDelegate
extension ReplayViewController: WhitePlayerEventDelegate {
func phaseChanged(_ phase: WhitePlayerPhase) {
switch phase {
case .waitingFirstFrame:
print("等待首帧")
case .playing:
print("正在播放")
case .pause:
print("已暂停")
case .stopped:
print("已停止")
case .ended:
print("播放结束")
case .buffering:
print("正在缓冲")
@unknown default:
break
}
}
func loadFirstFrame() {
print("首帧加载完成")
}
func stoppedWithError(_ error: Error) {
print("播放出错:", error.localizedDescription)
}
func scheduleTimeChanged(_ time: TimeInterval) {
print("当前播放进度:", time, "秒")
}
}
播放器控制
WhitePlayer 提供了丰富的播放控制功能:
Swift
// 播放控制
player.play() // 开始播放
player.pause() // 暂停播放
player.stop() // 停止播放
// 跳转到指定位置(单位:秒,相对于回放开始时间)
player.seekToScheduleTime(10 * 60) // 跳转到第 10 分钟
// 带回调的跳转
player.seekToScheduleTime(10 * 60) { result in
switch result {
case .success:
print("跳转成功")
case .fail:
print("跳转失败")
case .outOfRange:
print("跳转位置超出范围")
@unknown default:
break
}
}
// 设置播放倍速
player.playbackSpeed = 1.5 // 1.5 倍速播放
player.playbackSpeed = 1.0 // 恢复正常速度
// 获取播放状态信息
print("当前播放状态:", player.phase)
print("播放速度:", player.playbackSpeed)
print("时间信息:", player.timeInfo)
视角控制
回放时可以控制观看视角:
Swift
// 设置观看模式
player.setObserverMode(.directory) // 跟随模式,跟随录制时的视角变化
player.setObserverMode(.freedom) // 自由模式,可以自由调整视角
// 禁止用户调整视角
player.disableCameraTransform = true // 禁止用户通过手势调整视角
player.disableCameraTransform = false // 允许用户调整视角
音视频同步回放
如果需要同步播放音视频和白板回放,可以使用 WhiteCombinePlayer:
Swift
import UIKit
import AVKit
import Whiteboard
class CombineReplayViewController: UIViewController {
var boardView: WhiteBoardView!
var whiteSDK: WhiteSDK!
var combinePlayer: WhiteCombinePlayer?
var playerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// 创建视频播放视图
playerView = UIView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 300))
view.addSubview(playerView)
// 创建白板视图
boardView = WhiteBoardView(frame: CGRect(x: 0, y: 300, width: view.bounds.width, height: view.bounds.height - 300))
view.addSubview(boardView)
// 初始化 WhiteSDK
let config = WhiteSdkConfiguration(app: "RMxxxAQ")
config.region = .CN
whiteSDK = WhiteSDK(whiteBoardView: boardView, config: config)
// 创建混合播放器
let mediaUrl = URL(string: "https://example.com/video.m3u8")!
combinePlayer = WhiteCombinePlayer(mediaUrl: mediaUrl)
combinePlayer?.delegate = self
// 配置回放参数
let playerConfig = WhitePlayerConfig(room: "a7xxx69", roomToken: "NETLESSROOM_YWxxxjk")
playerConfig.beginTimestamp = NSNumber(value: Date(timeIntervalSince1970: 1609459200).timeIntervalSince1970)
playerConfig.duration = NSNumber(value: 45 * 60)
// 创建白板回放播放器
whiteSDK.createReplayer(with: playerConfig, callbacks: self) { [weak self] success, player, error in
if let error = error {
print("创建回放播放器失败:", error.localizedDescription)
return
}
if success, let player = player {
// 配置混合播放器
self?.combinePlayer?.whitePlayer = player
// 显示视频画面
if let nativePlayer = self?.combinePlayer?.nativePlayer {
let playerLayer = AVPlayerLayer(player: nativePlayer)
playerLayer.frame = self?.playerView.bounds ?? .zero
self?.playerView.layer.addSublayer(playerLayer)
}
// WhitePlayer 需要先手动 seek 到 0 才会触发缓冲行为
player.seekToScheduleTime(0)
print("混合播放器创建成功")
}
}
}
// 播放控制
func playVideo() {
combinePlayer?.play()
}
func pauseVideo() {
combinePlayer?.pause()
}
func seekToTime(_ time: TimeInterval) {
let cmTime = CMTime(seconds: time, preferredTimescale: 1)
combinePlayer?.seek(to: cmTime) { finished in
print("跳转完成:", finished)
}
}
func setPlaybackSpeed(_ speed: CGFloat) {
combinePlayer?.playbackSpeed = speed
}
}
// MARK: - WhitePlayerEventDelegate
extension CombineReplayViewController: WhitePlayerEventDelegate {
func phaseChanged(_ phase: WhitePlayerPhase) {
// 注意!必须完成该操作,WhiteCombinePlayer 才能正确同步状态
combinePlayer?.updateWhitePlayerPhase(phase)
switch phase {
case .playing:
print("正在播放")
case .pause:
print("已暂停")
case .ended:
print("播放结束")
default:
break
}
}
func stoppedWithError(_ error: Error) {
print("播放出错:", error.localizedDescription)
}
}
// MARK: - WhiteCombineDelegate
extension CombineReplayViewController: WhiteCombineDelegate {
func combinePlayerStartBuffering() {
// 任意一端进入缓冲
print("开始缓冲")
}
func combinePlayerEndBuffering() {
// 两端都结束缓冲
print("缓冲结束")
}
}
参考信息
开发注意事项
在使用 Fastboard 录制与回放功能时,需要注意以下几点:
- Fastboard 不提供回放 API:Fastboard iOS SDK 专注于实时互动白板房间的快速创建和管理,回放功能需要直接使用底层 Whiteboard SDK。
- 访问底层 SDK:通过
FastRoom对象的whiteSDK属性访问底层 Whiteboard SDK 实现回放功能。 - 录制延时:录制内容不是实时生成的,新创建的房间可能需要等待一段时间才能获得录制内容。
- 时间范围:建议根据实际使用场景设置合适的回放时间范围,避免回放过长的无效内容。
- 网络缓冲:回放时可能需要网络缓冲,特别是跳转到较远时间点时。
- m3u8 格式:m3u8 格式的音视频可能需要经过一次
combinePlayerEndBuffering调用后,才能进行seek播放。 - 状态同步:使用
WhiteCombinePlayer时,必须在phaseChanged回调中调用updateWhitePlayerPhase方法,否则无法正确同步状态。 - 资源释放:使用完毕后记得调用
player.stop()释放播放器资源。
API 参考
WhiteSDK.createReplayerWithConfig- 创建回放播放器WhitePlayer.play- 开始播放WhitePlayer.pause- 暂停播放WhitePlayer.seekToScheduleTime- 跳转播放位置WhitePlayer.setObserverMode- 设置观看模式- POST 创建房间