抢唱方案
更新时间:2023-12-11 16:18
1. 抢唱玩法简介
一轮抢唱游戏会播放若干首歌曲的高潮片段。播放一首歌曲高潮片段(原唱),并同时进入抢麦倒计时。倒计时结束后,玩家即可参与抢麦。当玩家抢到麦后,开始播放高潮片段的伴奏,抢麦者根据歌词和音高线进行演唱,其余玩家聆听歌声。当歌曲演唱完毕后,系统根据抢麦者演唱时的音准对本次演唱评分。评分结束后,进入下一首歌曲流程,一直循环直至一轮游戏结束。
2. 方案简介
KTV 抢唱方案是即构科技基于 ZegoExpressSDK 提供的音视频能力,以及 ZIM SDK 提供的可靠消息传输能力,参考业界抢唱流行玩法而形成的通用解决方案。除了基于 ZEGO 自有技术以外,KTV 抢唱还包含与 ZEGO 合作的版权方提供的正版音乐词曲服务,以保证该方案在商业领域不会出现侵权问题。
本方案的核心思想是状态机,由房间状态驱动业务。后台房间创建成功后,房间即进入等待游戏开始的状态。后台通过接收前端的状态切换指令(通常是一些关键业务接口,例如:开始游戏、点击抢唱),进行状态切换,继而通知前端,前端根据最新状态进行业务活动,例如:更新视图、推拉流、开闭麦等。整个状态转移流程随着房间的销毁而终止。
本文将从抢唱方案的整体架构开始介绍,展示客户端和多个服务之间的关系,然后基于抢唱体验 App 源码,简述移动端的状态切换细节并解释关键流程。
3. 方案架构图
角色说明:
- 房主 —— 房主负责创建、销毁房间,控制游戏流程,并且房主默认参与抢唱。
- 麦上用户 —— 在房间麦位被占满前加入房间的用户自动成为麦上用户,可以参与抢唱,暂不支持下麦操作。
- 麦下听众 —— 在房间麦位被占满后加入房间的用户自动成为麦下用户,不能参与抢唱。暂不支持房间内麦下听众上麦成为麦上用户。
房主、麦上用户、麦下听众角色属于业务角色,并非 ZegoExpressSDK 内置功能。
4. 体验 App 源码
ZEGO 针对抢唱玩法提供了 体验 App 源码,以供开发者进一步了解 ZEGO 在线 KTV 抢唱方案。
5. 业务流程图
抢唱游戏中,房间状态转移如下图所示:
共有 10 个状态:
- 等待本轮开始 (round_waiting)
- 准备本轮游戏 (round_preparing)
- 播放原唱并等待抢唱 (grab_waiting)
- 抢唱成功 (grab_successfully)
- 演唱中 (singing_or_listening)
- 分数展示 (song_end)
- 无人抢唱 (grab_unsuccessfully)
- 下一首 (next_song_preparing)
- 抢唱轮次结束 (round_end)
- 房间结束 (room_exit)
抢唱体验 App 源码中,“演唱中(singing_or_listening)”后,存在状态“AI 识别(song_ai)”。该状态为可选非必要状态,开发者可以决定是否在项目中实现。
6. 流程详述
随着游戏进程进行,状态不断切换,推拉流和麦克风的操作会非常频繁。因此,这里列出了 ZegoExpressSDK 对应方法的文档,以供参考。
信令 CMD 编号
本方案的前端主要用于界面展示以及关键流程触发,需要依赖后台传输的 IM 信令(ZPush)才能完成状态转移以及数据同步的功能。后台的信令 cmd 如下:
- 成员状态变更通知;
- 成员列表变更通知;
- 房间状态变更通知;
- 点歌列表变更通知。
Android 端 对 ZPush 的处理请参考抢唱体验 App 源码的 im.zego.ktv.grab.grabroom.help.ZIMRoomMessageHelp 类。
6.1 状态:等待本轮开始
6.1.1 用户逻辑
- 房主端展示“开始抢唱”按钮。
- 房主端点击“开始抢唱”按钮,调用后台接口,通知后台开始新的一轮游戏。
- 麦上用户和麦下用户等待房主开始游戏。
6.2 状态:准备本轮游戏
6.2.1 用户逻辑
- 所有用户得知“游戏开始”。
- 后台切换到 round_preparing 状态的同时会发送新一轮歌单的 push(信令:点歌列表变更通知)。
- 所有用户维持该状态直到房主下载完新一轮歌曲高潮之后,随后通知后台。
- 收到新一轮歌单后,用户下载资源
- 房主和麦上用户下载歌曲高潮片段、歌词及音高线;麦下用户只需下载歌曲高潮片段和歌词。
- 房主下载高潮片段的处理
- 房主下载完该轮所有高潮片段之后,调用后台接口上报下载情况,后台以此切换到下一个状态。
- 房主要上报每首歌的下载情况;如果后台遇到因下架导致的下载失败的歌曲,需要将其从本轮流程中剔除。
- 房主推流的处理
- 为了保证其他用户在下个状态能够顺利拉流,房主需在这个阶段就推流,其他用户能提前收到流变更并建立链接进行拉流。此时并未推实际数据到流上。
6.2.2 体验 App 源码实现
6.2.3 ZegoExpressSDK 相关方法调用
6.3 状态:播放原唱并等待抢唱
6.3.1 用户逻辑
- 在此状态期间,所有人自动闭麦。
- 高潮部分只播放指定长度,这里的指定长度是通过运营或者歌词计算提供。
- 由于下载片段包括高潮前奏部分(通常5秒左右),因此播放高潮前需要跳过前奏部分。
- 抢唱权限
- 状态开始后进入倒计时,倒计时结束后,房主和麦上用户可以调用后台接口参与抢唱,后台收到抢唱请求会切换到下一个状态。
- 如果麦上用户的进房时间晚于状态开始时间,视作无法参与本首歌曲的抢唱。
- 麦下用户不能参与抢唱。
- 高潮片段(原唱)播放来源
- 房主本地播放当前歌曲高潮片段(原唱)并继续推流。
- 如果麦上用户及麦下用户完成下载当前歌曲高潮片段,则本地播放并停止拉流,否则保持拉流收听高潮片段(原唱)。
6.3.2 体验 App 源码实现
- 非房主是否本地播放请参考体验 App 源码的 im.zego.ktv.grab.grabroom.manager.GrabRoomInfoManager 类的 mGrabWaitingShowSong 字段
- 拉流判断请参考体验 App 源码的 im.zego.ktv.grab.grabroom.manager.GrabRoomStreamManager 类的 onRoomStreamUpdate() 方法 和 stopPlayingHostStream() 方法
- 歌曲的播放操作请参考体验 App 源码的 im.zego.ktv.grab.grabroom.manager.GrabRoomStateManager 类的 grabWaiting() 方法
- 歌词的展示请参考体验 App 源码的 im.zego.ktv.grab.grabroom.manager.GrabRoomProgressTimerManager 类
6.3.3 ZegoExpressSDK 相关方法调用
6.4 状态:抢唱成功
6.4.1 房主逻辑
- 所有用户得知“某用户”抢到麦。
- 房主和麦上观众抢麦后,流与麦克风的处理
- 假如自己抢麦成功,则从该状态开始推流,关麦,供拉流方提前建立链接;
- 假如未抢麦或抢麦失败,则关麦,收到 SDK 流新增进行拉流。
6.4.2 体验 App 源码实现
主动拉流请参考体验 App 源码的 im.zego.ktv.grab.grabroom.manager.GrabRoomStateManager 类的 grabSuccessfully() 方法
6.5 状态:演唱中
6.5.1 房主 & 麦上用户逻辑
抢麦成功:
- 收到该状态时,自动开麦,播放伴奏,展示歌词和音高线界面。
- 唱完之后,调用后台接口将本地打分结果发给后台,触发后台状态切换;后台也需要做超时判断,避免因歌者掉线导致状态无法变更。
- 歌唱时将播放器和麦克风声音本地混合推出去,通过 SEI 带上本地伴奏进度,帮助其他人对齐歌词展示进度。
未抢麦或抢麦失败:
- 收到该状态时,自动闭麦。
- 拉取抢麦成功用户推的流,并基于其中的 SEI 来对齐歌词展示进度。
6.5.2 麦下用户
- 拉取抢麦成功用户推的流,并基于其中的 SEI 来对齐歌词展示进度。
6.5.3 体验 App 源码实现
6.6 状态:分数展示
6.6.1 用户逻辑
6.7 状态:无人抢唱
6.7.1 用户逻辑
6.8 状态:下一首
6.8.1 用户逻辑
6.9 状态:抢唱轮次结束
6.9.1 用户逻辑
- 向房主和麦上用户展示本轮抢唱数据,麦下用户可以选择是否退房。
- 房主的权限
- 当游戏还未进行到最后一轮,房主可以调用后台接口开始新的一轮游戏,触发后台切换到状态 round_waiting。
- 当游戏已经处于最后一轮,房主只能选择退出房间。房主退出房间时,房间被销毁,所有人收到状态 room_exit。
6.10 状态:房间结束
6.10.1 用户逻辑
- 收到此状态时,呈现房间结束对话框,点击后离开房间。
- 任何时刻,只要房主退出房间,其他人都会收到此状态。
7. 时序图
7.1 创建(房主)/加入(非房主)抢唱房间
参考文档:
Express - 使用 Token 鉴权
Express - 实现视频通话
ZIM - 实现基本消息收发
功能总览 - 版权音乐
7.2 房主开始新一轮抢唱游戏
7.3 房主和麦上用户进行抢麦
8. 常见问题
请参考 常见问题 - 抢唱方案相关问题。