在线KTV
  • iOS : Objective-C
  • Android
  • Windows
  • Linux
  • Web
  • Flutter
  • Electron
  • Unity3D
  • 方案简介
  • 下载
  • 体验 App
  • 快速开始
  • 方案实现
  • ZEGO 内容中心
  • 客户端 API
  • 服务端 API
  • 常见错误码
  • 常见问题
  • 文档中心
  • 在线KTV
  • 正版曲库
  • 点歌(获取和分享歌曲)

点歌

更新时间:2023-12-11 16:18

1 功能简介

ZEGO 版权内容中心,提供了在线 K 歌场景所需歌曲内容。内容由音乐合作的版权方提供,如需进一步了解版权内容中心,请联系 ZEGO 商务人员咨询。

本文将介绍如何快速实现在线 KTV 场景下搜索、下载和播放正版曲库歌曲的流程。

相关概念

  • ZEGO Express SDK:由 ZEGO 提供的实时音视频 SDK,能够为开发者提供便捷接入、高清流畅、多平台互通、低延迟、高并发的音视频服务。
  • 点歌:通过 requestResource 接口获取一首歌曲或伴奏的播放权限。
  • 歌曲相关接口扩展说明:用户在使用 ZEGO Express SDK 的版权音乐功能时,所需遵循的调用接口与返回值的相关扩展说明。相关扩展说明为 发送扩展请求接口说明 以及 获取歌曲与歌词资源接口说明
  • 分享资源:用户在点歌/点伴奏成功后,其他用户可以调用 getSharedResource 获取被分享的音乐资源。

2 示例源码

下载 示例源码,体验 ZEGO 在线 KTV 版权音乐点歌功能。

3 前提条件

在实现基本的版权音乐功能之前,请确保:

  • 已在项目中集成 ZEGO Express SDK(含版权音乐功能),详情请参考 SDK 集成
  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理 中的“项目信息”。
  • 已联系 ZEGO 商务人员为 AppID 开通版权音乐服务。

SDK 支持 Token 鉴权。若您需要升级鉴权方式,可参考 如何从 AppSign 鉴权升级为 Token 鉴权

4 实现流程

用户实现点歌并播放的基本流程如下图:

  1. 调用 createEngineWithProfile 初始化 ZEGO Express SDK,调用 loginRoom 登录房间。
  2. 调用 createCopyrightedMusic 创建版权音乐对象,调用 initCopyrightedMusic 接口,初始化版权音乐对象。
  3. 调用 sendExtendedRequest 接口,传入相应参数获取 songID(songID 是一首歌曲的唯一标识)。例如传入 "/top/song" 可获取到当前热门的歌曲列表。
  4. 调用 requestResource 点歌,获取歌曲的资源信息。歌曲的资源信息包括歌曲名、歌曲时长、resourceID 等。
  5. 调用 download 接口传入 resourceID,将歌曲下载到设备中(歌曲必须先下载完成才能播放)。
  6. 调用 createMediaPlayer 初始化播放器,调用播放器等。loadCopyrightedMusicResourceWithPosition 加载歌曲资源,加载成功后调用 start 开始播放。
  7. 向远端用户推送唱歌音频流。

4.1 初始化 SDK

步骤 1:调用 createEngineWithProfile 初始化 SDK。

初始化 ZEGO Express SDK 时,内部处理的操作较多,建议用户在 App 启动时候进行。详情参考:实时语音 - 快速开始 - 实现流程

/** 引入 ZegoExpressEngine.h 头文件 */
#import <ZegoExpressEngine/ZegoExpressEngine.h>
@property (nonatomic, strong) ZegoExpressEngine *engine;

/** 1. 创建 ZegoExpressEngine 对象 */
ZegoEngineProfile *profile = [ZegoEngineProfile new];
// 请通过官网注册获取,格式为:1234567890
profile.appID = appID; 
// 请通过官网注册获取,格式为:@"0123456789012345678901234567890123456789012345678901234567890123"(共64个字符)
// 如果使用 token 鉴权,不需要配置此参数
profile.appSign = appSign; 
// KTV场景接入
profile.scenario = ZegoScenarioKaraoke; 
// 创建引擎,并注册 self 为 eventHandler 回调。不需要注册回调的话,eventHandler 参数可以传 nil,后续可调用 "-setEventHandler:" 方法设置回调
self.engine = [ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];

步骤 2(可选):调用 loginRoom 登录房间。

  1. 此步骤对于使用 AppSign 鉴权的开发者而言,此步骤为 可选 步骤;对于使用 Token 鉴权的开发者,此步骤为 必需 步骤。
  2. 如果用户需要在初始化版权音乐功能前登录房间,请确保 initCopyrightedMusicloginRoom 接口中传入的 userID、userName 是一致且唯一的。
  3. 对于使用 Token 鉴权的开发者,必须保证在初始化版权音乐功能前登录房间。
/** 2. 登录房间*/
// 此步骤对于使用 **appSign** 鉴权的开发者而言,此步骤为 **可选** 步骤;对于使用 **token** 鉴权的开发者,此步骤为 **必需** 步骤。/
- (void)loginRoom {
    // roomID 由您本地生成,需保证 “roomID” 全局唯一。不同用户要登录同一个房间才能进行通话
    NSString *roomID = @"room1";

    // 创建用户对象,ZegoUser 的构造方法 userWithUserID 会将 “userName” 设为与传的参数 “userID” 一样。“userID” 与 “userName” 不能为 “nil”,否则会导致登录房间失败。
    // userID 由您本地生成,需保证 “userID” 全局唯一。
    ZegoUser *user = [ZegoUser userWithUserID:@"user1"];

    // 只有传入 “isUserStatusNotify” 参数取值为 “true” 的 ZegoRoomConfig,才能收到 onRoomUserUpdate 回调。
    ZegoRoomConfig *roomConfig = [[ZegoRoomConfig alloc] init];
    // token 由用户自己的服务端生成,为了更快跑通流程,也可以通过 ZEGO 控制台 https://console.zego.im/dashboard 获取临时的音视频 token
    roomConfig.token = @"<#token#>"; // 如果使用appSign 鉴权,登录房间不需要此参数

    roomConfig.isUserStatusNotify = YES;
    // 登录房间
    [[ZegoExpressEngine sharedEngine] loginRoom:roomID user:user config:roomConfig callback:^(int errorCode, NSDictionary * _Nullable extendedData) {
        // (可选回调) 登录房间结果,如果仅关注登录结果,关注此回调即可
        if (errorCode == 0) {
            NSLog(@"房间登录成功");
        } else {
            // 登录失败,请参考 errorCode 说明 https://doc-zh.zego.im/article/4377
            NSLog(@"房间登录失败");
        }
    }];
}

步骤 3:调用 createCopyrightedMusic 创建版权音乐对象。

/** 定义 CopyrightedMusic 对象 */
@property (nonatomic, strong) ZegoCopyrightedMusic *copyrightedMusic;


/** 3. 创建 CopyrightedMusic 对象 */
self.copyrightedMusic = [[ZegoExpressEngine sharedEngine] createCopyrightedMusic];

/** 设置版权音乐回调 */
[self.copyrightedMusic setEventHandler:self];

步骤 4:调用 initCopyrightedMusic 初始化版权音乐对象。

/** 4. 初始化版权音乐对象 */
ZegoCopyrightedMusicConfig *config = [ZegoCopyrightedMusicConfig new];
// 这里的 userID 和 userName 应该和登录房间的 userID 和 userName 保持一致。 
ZegoUser *user = [ZegoUser userWithUserID:userID userName:userName];
config.user = user;
[self.copyrightedMusic initCopyrightedMusic:config callback:^(int errorCode) {
if (errorCode == 0) {
          NSLog(@"创建的版权音乐对象 initCopyrightedMusic 初始化成功");
      }
      else {
          NSLog(@"创建的版权音乐对象 initCopyrightedMusic 初始化失败,错误码 = %d",errorCode);
      }
}];

4.2 获取歌曲列表

4.2.1 获取方式

用户可用以下三种方式获取歌曲列表:

方法 具体说明
第 1 种:先获取标签列表,然后通过标签获取歌曲列表  
  1. 获取标签列表(/tag/list);
  2. 选择一个标签获取歌曲列表(/tag/song)。
第 2 种:通过搜索获取
通过用户传入关键字搜索歌曲(/search/song)。
第 3 种:通过榜单获取
获取榜单列表(/top/song)。

4.2.2 调用流程

如下介绍如何使用第 1 种获取歌曲列表的方法:

  1. 调用 sendExtendedRequest 接口,command 传入 "/tag/list",params 传入 "{}",在回调中获取到标签列表。
  2. 选择某个标签,调用 sendExtendedRequest 接口,command 传入 "/tag/song",params 传入 JSON 格式的字符串(其中 tag_id 从步骤 1 中获取):
{
    "tag_id": "0",
    "page": 1,
    "filter": [1],
    "vendor_id": 0
}

在回调中获取到歌曲列表。

sendExtendedRequest 接口完整参数与返回结果的说明请参考 发送扩展请求接口说明

调用示例:

// 下面以通过标签获取歌曲列表为例
NSString *command = @"/tag/song"; // command 参数代表您请求的业务指令,该示例为通过标签获取歌曲
NSString *params = @"{ \"tag_id\": \"587\",  \"page\": 1,  \"filter\": [1]}"; // params 参数,是 JSON 格式的字符串,需要您传入的额外参数
[self.copyrightedMusic sendExtendedRequest:command params:params callback:^(int errorCode, NSString * _Nonnull command, NSString * _Nonnull result) {
    // command 参数代表您请求的业务指令
    // result 回调结果,JSON 格式字符串
}];

4.2.3 版权信息

在获得的歌曲列表中,每首歌曲都有对应的 copyright 字段,表示这个歌曲的版权信息。

copyright 的结构如下:

//该字段位于 result.data.songs[i].copyright 
{
    "song_lyric": 0,
    "recording": 1,
    "channel": 0
}

具体字段说明如下:

字段 说明
song_lyric
  • 若 song_lyric 为 1,表示有词曲伴奏版权,通过 requestResource 获取到伴奏资源,可在获取歌单歌曲、搜索歌曲、获取榜单歌曲时配置 filter 参数,过滤无法点唱的词曲伴奏。
  • 若 song_lyric 为 0,表示无词曲伴奏版权。
recording
  • 若 recording 为 1,表示有歌曲录音版权,通过 requestResource 获取到歌曲资源后,可以播放收听歌曲。
  • 若 recording 为 0,表示无歌曲版权,无法收听和点唱歌曲。
channel
歌曲渠道。
  • 若 channel 为 其他值,表示其他渠道歌曲。
  • 若 channel 为 0,需要在 UI 界面展示 logo 标志。开发者可通过 本链接 下载相关 logo 资源。UI 界面示例如下:
  • 当 recording 为 1,而 song_lyric 为 0 时,说明无词曲伴奏版权,歌曲只能收听不能点唱。
  • 由于歌曲版权是动态变化的,在实时获取 songID 过程中,无版权的资源会被屏蔽掉。但如果用户在业务逻辑侧设置了歌单管理或其他逻辑缓存过 songID,则该场景下可能会出现该 songID 对应资源无版权的情况。

4.3 获取歌曲资源

  • 不同版权方对应的歌曲资源不同、资源的有效时长也不同。

  • ZEGO 目前仅支持按点歌次数计费,即每调用一次 requestResource 接口,都会计一次费。不同版权方的计费方式不同:

    • 版权方 1:用户包月计费、房间包月计费
    • 版权方 2:按次计费
    • 版权方 3:按次计费

版权方的详细信息(歌曲资源、资源有效时长、计费方式等),请联系 ZEGO 商务咨询。

4.3.1 获取包含人声的歌曲资源

如果包含人声的歌曲资源有版权,可以调用 requestResource 接口获取到歌曲的资源信息,包括歌曲名、歌曲时长、resourceID 等,接口具体回调的信息参考 获取歌曲与歌词资源接口说明

字段 功能 该字段在回调结果中的获取方式
resource_id
一个 songID 对应的歌曲可能对应有多个不同的音质资源(如标清、高清、无损),因此需要定义 resource_id 来唯一对应每一个资源。可用于下载歌曲(调用 download 接口)。
result.resources[0].resource_id
// recording 为 1 代表该首歌有包含人声的歌曲的版权
if (recording == 1) {
    /** 配置 */
    ZegoCopyrightedMusicRequestConfig *config = [ZegoCopyrightedMusicRequestConfig new];
    /** 音乐 songID,从歌曲列表中获取*/
    config.songID = songID;
    /** 计费模式,请联系 ZEGO 商务咨询 */
    config.mode = ZegoCopyrightedMusicBillingModeCount;
    /** 版权方,其对应的枚举值信息,请联系 ZEGO 商务咨询*/
    config.vendorID = ZegoCopyrightedMusicVendorDefault;
    /** 场景 ID:1 语聊房、2 直播间在线 K 歌、3 直播间播放背景音乐、4 抢唱*/
    /** 请联系 ZEGO 商务开通使用场景,使用过程请传入正确的场景 ID。*/
    config.sceneID = 0;
    /** 
      * 资源类型包含:
      * ZegoCopyrightedMusicResourceSong:包含人声的原曲
      * ZegoCopyrightedMusicResourceAccompaniment:伴奏
      * ZegoCopyrightedMusicResourceAccompanimentClip:伴奏高潮片段
      */
    ZegoCopyrightedMusicResourceType resourceType = ZegoCopyrightedMusicResourceSong;

    /** 点歌 */
    [self.copyrightedMusic requestResource:config type:resourceType callback:^(int errorCode, NSString * _Nonnull resource) {

    }];
} else {
 // 该首歌没有包含人声的歌曲的版权
}

4.3.2 获取歌曲伴奏/伴奏高潮片段资源

通过调用 requestResource 接口,还可以获取歌曲对应的 “伴奏资源”、“长分片高潮片段资源”、“短分片高潮片段资源”,其实现流程与 获取包含人声的歌曲资源 一致。

获取歌曲对应伴奏、长/短分片高潮片段、抢唱片段的资源时,回调会多出了一些字段,比如 krc_token 等,详情请参考 获取歌曲与歌词资源接口说明

4.4 下载歌曲

不同版权方对应的歌曲资源有效时长不同(详情请咨询 ZEGO 商务人员),调用 download 接口下载资源时,如果返回了 1017019 错误码,表示资源已失效,请重新调用 requestResource 接口获取资源。

通过 4.3 获取歌曲资源 中点歌获取 resourceID 后,调用 download 接口下载歌曲,下载成功的结果通过 download 接口本身的回调获取。

如果需要获取下载进度,可通过 onDownloadProgressUpdate 回调获取。

resourceID 是开发者在调用 requestResourcegetSharedResource时生成的,只在 SDK 的生命周期里有效,SDK 反初始化时就会销毁,因此不能储存到磁盘或者分发给其他用户使用。

/** resourceID */
NSString *resourceID = @"";

[self.copyrightedMusic download:resourceID callback:^(int errorCode) {
    if (errorCode == 0) {
        // 歌曲下载成功,可以进行播放等操作

    } else {
        // 歌曲下载失败
    }
}];

4.5 播放歌曲

4.5.1 开始播放

歌曲下载完成后(收到 download 接口的成功回调),使用 ZegoMediaPlayer 并调用 start 接口开始播放歌曲。

/** 音乐资源 ID,点歌(requestResource)时可获取到 */
NSString *resourceID = @"";

// play 开始播放时的进度
unsigned long long startPosition = 0;

// 1. 通过 ZegoExpressEngine 的 createMediaPlayer 方法 创建 ZegoMediaPlayer
ZegoMediaPlayer *mediaPlayer = [[ZegoExpressEngine sharedEngine] createMediaPlayer];

// 2. 加载歌曲对应的 resourceID,从 startPosition 处开始加载。注意,只有该歌曲资源下载完成了,这里才能加载成功
[mediaPlayer loadCopyrightedMusicResourceWithPosition:resourceID startPosition: startPosition callback:^(int errorCode) {

}];

// start 开始播放
[mediaPlayer start];

4.5.2 暂停/恢复/跳转/停止

调用 download 接口下载完资源后,可以调用 pauseresumeseekTostop 等接口控制播放状态。

// pause 暂停播放
[mediaPlayer pause];

// resume 恢复播放
[mediaPlayer resume];

// seekTo 调整播放进度到 position 位置,position 为毫秒数
long position = 1000;
[mediaPlayer seekTo:position callback:^(int errorCode) {

}];

// stop 停止播放
[mediaPlayer stop];

4.6 推送唱歌音频流

点播歌曲完成后,需要推送歌声音频流,以供远端用户听到演唱用户的歌声。请根据实际需求,参考 独唱方案实时合唱方案串行合唱方案抢唱方案 实现推流。

5 API 调用时序

本篇目录