Meet Group Activities
Audio & Video 进阶 27m

初识 Group Activities

Meet Group Activities

2021年6月7日

在 Apple 官方观看视频

一句话判断

Group Activities(即 SharePlay)是 Apple 对”一起看视频、一起听歌”场景的系统级解决方案——如果你做媒体类 App,不集成 SharePlay 就等于把社交场景拱手让给竞争对手。

这场 Session 讲了什么

Session 介绍了 Group Activities 框架(对应消费者端的 SharePlay 功能)的技术架构和 API 设计。Group Activities 允许多个用户在 FaceTime 通话中共享 App 内的体验——同步观看视频、同步听音乐、或者共享自定义的交互界面。

框架的核心是三个概念:GroupActivity(定义一个可共享的活动,比如”一起看电影”)、GroupSession(活动的实例,包含参与者和状态信息)、GroupSessionMessenger(参与者之间的消息通道)。Session 展示了完整的 SharePlay 流程:发起者在 FaceTime 通话中启动一个 GroupActivity -> 其他参与者收到邀请 -> 接受后进入 GroupSession -> 所有人的播放状态自动同步。底层通信由 Apple 的 FaceTime 基础设施处理,开发者不需要自己搭建信令服务器。

值得深挖的点

同步机制:谁控制播放进度

GroupSession 中的播放同步是自动的——发起者(发起活动的人)是”主机”,其他人是”从机”。主机播放/暂停/跳转进度时,所有从机自动同步。但如果任何从机做了一次播放操作(比如暂停),这个操作也会广播给所有参与者。Session 建议为你的 GroupActivity 明确一个”leader”角色——在视频场景中通常是发起者,在协作场景中可以动态切换。

GroupSessionMessenger 的消息模型

GroupSessionMessenger 支持两种消息模式:send() 广播给所有人,send(_:to:) 发送给指定参与者。消息类型必须是 Codable。重要细节:消息不保证送达——如果一个参与者网络中断,他恢复后不会收到离线期间的消息。你的 App 需要自己处理”重新同步”逻辑——参与者重连时请求当前的播放状态。

代码片段

// 定义一个 GroupActivity
struct WatchTogether: GroupActivity {
    // 活动的元数据——显示在邀请卡片上
    var metadata: GroupActivityMetadata {
        GroupActivityMetadata()
            .$title.set("一起看电影")
            .$subtitle.set(movie.title)
            .$previewImageUrl.set(movie.posterURL)
            .$type.set(.watchTogether)
    }
    
    let movie: Movie
}

// 发起 SharePlay 会话
class MoviePlayerViewModel: ObservableObject {
    @Published var isEligibleForGroupSession = false
    private var session: GroupSession<WatchTogether>?
    private var messenger: GroupSessionMessenger?
    
    func startSharing(movie: Movie) async {
        let activity = WatchTogether(movie: movie)
        
        do {
            // 发起活动,触发系统邀请流程
            _ = try await activity.activate()
        } catch {
            print("启动 SharePlay 失败: \(error)")
        }
    }
    
    // 监听会话创建
    func configureGroupSession() async {
        for await session in WatchTogether.sessions() {
            self.session = session
            self.messenger = GroupSessionMessenger(session: session)
            
            // 监听参与者的播放状态消息
            Task {
                for await (message, _) in session.messages(of: PlaybackMessage.self) {
                    handlePlaybackMessage(message)
                }
            }
            
            // 监听参与者变化
            session.$activeParticipants.sink { participants in
                print("当前参与者: \(participants.count) 人")
            }
            
            session.join()
        }
    }
}
// 同步播放状态的消息类型
enum PlaybackMessage: Codable {
    case play
    case pause
    case seek(to: Double)
    case changeMovie(movieID: String)
}

// 广播播放操作给所有参与者
func broadcastPlayback(_ action: PlaybackMessage) async {
    guard let messenger = messenger else { return }
    do {
        try await messenger.send(action)
    } catch {
        print("发送消息失败: \(error)")
    }
}

// 处理收到的播放同步消息
func handlePlaybackMessage(_ message: PlaybackMessage) {
    switch message {
    case .play:
        player.play()
    case .pause:
        player.pause()
    case .seek(let time):
        player.seek(to: CMTime(seconds: time, preferredTimescale: 600))
    case .changeMovie(let movieID):
        // 所有人切换到新电影
        loadMovie(id: movieID)
    }
}
// 配置 AVPlayer 支持 Group Activities
func setupPlayerForSharePlay(player: AVPlayer) {
    // AVPlayer 在 GroupSession 中自动处理播放同步
    // 只需要设置 coordinatedPlaybackPolicy
    player.coordinatedPlaybackPolicy = .groupSession
    
    // 监听等待状态——所有人准备就绪后才开始播放
    player.publisher(for: \.reasonForWaitingToPlay)
        .sink { reason in
            if reason == .coordinatedPlaybackNotEstablished {
                print("等待其他参与者准备就绪...")
            }
        }
}

最佳实践

  • 活动元数据(metadata)中的 previewImageUrl 使用高质量的海报图——这是用户决定是否加入活动的唯一视觉线索。
  • 消息频率控制:视频 seek 操作做防抖处理(用户拖动进度条时不要每帧都广播),使用 500ms 的节流(throttle)避免消息风暴。
  • 为不支持 SharePlay 的设备(iOS 14 及以下)提供降级方案——比如”分享链接”让用户在浏览器中观看。

还有什么值得关注

  • Group Activities 框架支持自定义活动(不限于媒体播放),可以用来做协作编辑、白板共享等场景。
  • FaceTime 通话中的屏幕共享(Screen Sharing)和 Group Activities 可以同时使用——参与者可以边看视频边分享自己的屏幕。
  • Group Activities 的 SDK 支持 iOS、iPadOS、macOS 和 tvOS,一个活动可以跨设备(比如 iPhone 和 Apple TV 同时播放)。
WWDC 2021