Coordinate media experiences with Group Activities
Audio & Video 进阶 30m

用 Group Activities 协调媒体体验

Coordinate media experiences with Group Activities

2021年6月8日

在 Apple 官方观看视频

一句话判断

Group Activities 框架是 SharePlay 的底层引擎——它让多个用户通过 FaceTime 同步播放媒体内容,你的 App 负责提供内容,系统负责同步和通信。

这场 Session 讲了什么

Session 详细介绍了 Group Activities 框架的技术实现,这是 iOS 15 中 SharePlay 功能的开发者接口。SharePlay 允许用户在 FaceTime 通话中一起看视频、听音乐、或共享屏幕。Group Activities 是支撑这个功能的消息同步层。

架构上,Group Activities 通过 Group Session 维护参与者状态,通过 $.playbackState 等同步属性协调所有参与者的播放位置和状态。当一个参与者暂停视频,所有其他参与者的视频也会暂停。当主持人跳转到某个时间点,所有人的播放位置都会同步跳转。

Session 覆盖了三种场景:自定义媒体同步(视频和音频)、通用数据同步(白板、游戏等),以及如何处理参与者的加入和离开。还讨论了延迟补偿策略——不同参与者的网络延迟不同,框架会自动调整缓冲来保证体验同步。

值得深挖的点

延迟补偿与播放同步

多个参与者通过 FaceTime 连接,每个人的网络延迟不同。如果只是简单地广播”在 3:45 播放”,网络慢的人会晚几秒开始看。Group Activities 使用了延迟补偿算法——它会估算所有参与者的最大延迟,让所有人等待这个延迟后再开始播放。这保证了真正的同时观看体验,代价是所有人都有一定的初始延迟。

Group Session 的生命周期

Group Session 有明确的生命周期:创建 -> 等待参与者 -> 活跃 -> 结束。参与者可以在任何阶段加入或离开。你的 App 需要监听 .$participants 的变化来更新 UI(比如显示当前参与者列表)。当 FaceTime 通话结束时,Group Session 自动进入结束状态,你的 App 应该保存当前状态并恢复正常播放模式。

代码片段

创建和加入 Group Session

import GroupActivities

// 定义一个媒体 Group Activity
struct MovieWatchingActivity: GroupActivity {
    var metadata: GroupActivityMetadata {
        GroupActivityMetadata(title: "一起看电影")
    }
}

// 发起者:创建并开始 activity
let activity = MovieWatchingActivity()
do {
    let session = try await activity.activate()
    // 监听 session 事件
    configureSession(session)
} catch {
    print("无法启动 Group Activity: \(error)")
}

同步播放状态

func configureSession(_ session: GroupSession<MovieWatchingActivity>) {
    // 监听播放状态的同步
    Task {
        for await state in session.$playbackState.removeDuplicates() {
            switch state {
            case .playing:
                player.play()
            case .paused:
                player.pause()
            @unknown default:
                break
            }
        }
    }
    
    // 当本地播放状态变化时,广播给其他参与者
    Task {
        for await reason in player.publisher(reason: .playbackState) {
            session.playbackState = player.timeControlStatus == .playing ? .playing : .paused
        }
    }
}

处理参与者变化

// 监听参与者加入和离开
Task {
    for await participants in session.$participants.removeDuplicates() {
        // 更新 UI 显示参与者列表
        participantCount = participants.count
        
        // 参与者列表包含每个参与者的唯一标识
        for participant in participants {
            print("参与者: \(participant.id)")
        }
    }
}

// 当参与者离开到只剩一人时,自动结束 session
Task {
    let count = await session.$participants.first { $0.count <= 1 }
    if count != nil {
        session.end()
        // 切换回单人播放模式
    }
}

最佳实践

新项目: 如果你的 App 有视频或音频播放功能,强烈建议集成 Group Activities。这是 iOS 15 的核心新特性之一,Apple 会在系统中大力推广 SharePlay。先实现基本的播放同步,再逐步添加自定义交互。

已有项目: 评估你的媒体播放架构是否使用了 AVPlayer。Group Activities 与 AVPlayer 的集成最简单——只需要几行代码就能启用同步播放。如果使用自定义播放器,需要手动实现状态同步逻辑,工作量会大很多。

还有什么值得关注

  • Group Activities 支持”准备阶段”——发起者可以先选好内容,等参与者到齐后再开始播放。
  • 在模拟器中无法测试 Group Activities,需要两台真机或使用 Xcode 的 Network Link Conditioner 模拟。
  • 用户可以选择”仅同步不同时观看”,即只保持播放进度一致但不强制完全同步。
WWDC 2021