实现一起听(基于 NERoom)

更新时间: 2023/08/25 09:17:47

本文介绍如何基于 NERoom 房间组件,在您的 App 中添加一起听场景,实现房主和观众一起听音乐。

方案架构

一起听的架构.png

开发环境要求

开发环境要求如下:

环境要求 说明
JDK 版本 1.8.0 及以上版
Android API 版本 API 21 及以上版本
Android Studio 版本 5.0 及以上版本
CPU架构 ARM 64、ARMV7
IDE Android Studio
其他 依赖 Androidx,不支持 support 库。
Android 系统 5.0 及以上版本的真机。

前提条件

示例项目源码

一起听的示例项目源码地址请参见 语聊房 NEChatroom 的示例项目源码

示例项目的目录结构和跑通示例项目的步骤请参见跑通示例项目

API 时序图

sequenceDiagram
    participant playerA as NERoom SDK
    participant hostClientA as 房主A
    participant app_server as 一起听Server
    participant neroom_server as NERoom_Server
    participant audienceB as 连麦观众B
    participant playerB as NERoom SDK

    Note over playerA, playerB: 单人场景
    hostClientA ->> app_server: 创建房间、加入房间
    hostClientA ->> app_server: 点歌(已下载)
    app_server -->> hostClientA: 点歌信息
    app_server -->> hostClientA: 歌单变化
    hostClientA ->> app_server: 上报歌曲ready
    app_server ->> app_server: 判断房间人数和上报ready人数
    app_server ->> hostClientA: 下发开始播放
    hostClientA ->> playerA: 播放
    hostClientA ->> hostClientA: 刷新歌词
    
    Note over playerA, playerB: 单人场景暂停、恢复
    hostClientA ->> app_server: 暂停
    app_server -->> hostClientA: 下发暂停播放
    hostClientA ->> playerA: 暂停
    hostClientA ->> app_server: 恢复
    app_server -->> hostClientA: 下发开始播放
    hostClientA ->> playerA: 恢复

    Note over playerA, playerB: 单人场景切歌
    hostClientA ->> app_server: 切歌
    app_server -->> hostClientA: 下发切歌,包含下一首歌信息
    hostClientA ->> app_server: 上报下一首歌ready
    app_server -->> hostClientA: 下发开始播放
    hostClientA ->> playerA: 播放

    Note over playerA, playerB: 观众上麦
    audienceB ->> app_server: 上麦(主播抱麦)
    audienceB ->> app_server: 获取当前播放歌曲
    audienceB ->> app_server: 获取已点歌曲列表
    app_server -->> audienceB: 当前播放歌曲信息
    app_server -->> audienceB: 当前已点歌曲列表
    audienceB ->> audienceB: 下载当前播放歌曲
    audienceB ->> audienceB: 当前歌曲下载完成
    audienceB ->> audienceB: 预下载歌曲列表的其他歌曲
    audienceB ->> neroom_server: 获取播放进度
    neroom_server ->> hostClientA: 下发消息通知房主需要同步播放进度给连麦观众
    hostClientA ->> neroom_server: 通知当前播放进度
    neroom_server -->> audienceB: 通知当前播放进度
    audienceB ->> audienceB: 对齐播放进度
    audienceB ->> playerB: seekTo
    audienceB ->> audienceB: 刷新歌词
sequenceDiagram

    participant playerA as NERoom SDK
    participant hostClientA as 房主A
    participant app_server as 一起听Server
    participant neroom_server as NERoom_Server
    participant audienceB as 连麦观众B
    participant playerB as NERoom SDK
    
    Note over playerA, playerB: 同步一起听歌曲状态(暂停、恢复)
    hostClientA ->> app_server: 歌曲播放控制,暂停or恢复
    audienceB ->> app_server: 歌曲播放控制,暂停or恢复
    app_server -->> hostClientA: 下发当前播放歌曲状态
    app_server -->> audienceB: 下发当前播放歌曲状态
    hostClientA ->> playerA: 暂停or恢复
    audienceB ->> playerB: 暂停or恢复

    Note over playerA, playerB: 同步一起听歌曲状态(换歌)
    hostClientA ->> app_server: 上报换歌动作
    app_server -->> hostClientA: 下发换歌消息
    app_server -->> audienceB: 下发换歌消息
    alt AB均已下载待播放歌曲
        hostClientA ->> app_server: 上报待播歌曲ready
        audienceB ->> app_server: 上报待播歌曲ready
        app_server ->> app_server: 判断房间人数和上报ready人数
        app_server -->> hostClientA: 下发开始播放
        app_server -->> audienceB: 下发开始播放
    else AB至少有一人未下载待播歌曲
        audienceB ->> audienceB: 先切到待播歌曲状态,同时下载歌曲,状态为下载中
        hostClientA ->> hostClientA: 先切到待播歌曲状态,同时下载歌曲,状态为下载中
        hostClientA ->> app_server: 上报待播歌曲ready
        audienceB ->> app_server: 上报待播歌曲ready
        app_server -->> hostClientA: 下发开始播放
        app_server -->> audienceB: 下发开始播放
        hostClientA ->> playerA: 播放新歌
        audienceB ->> playerB: 播放新歌
    end

    Note over playerA, playerB: 同步一起听歌曲进度
    hostClientA ->> hostClientA: 拖动进度条
    hostClientA ->> playerA: seekTo
    hostClientA ->> hostClientA: 刷新歌词
    hostClientA ->> neroom_server: 通知播放进度 <br> (NERoom点对点自定义消息)
    neroom_server ->> audienceB: 通知播放进度 <br> (NERoom点对点自定义消息)
    audienceB ->> audienceB: 对齐播放进度
    audienceB ->> playerB: seekTo
    audienceB ->> audienceB:刷新歌词

房间管理

  1. 房主创建一起听房间。

    您可以通过自己的业务服务器调用 NERoom Server 的接口去创建房间,客户端通过业务服务器提供的restful api创建房间。

  2. 房主和连麦观众分别调用 joinRoom 接口加入一起听房间。加入房间成功后,房间内用户触发 onMemberJoinRoom 回调。

    一起听房间只允许两个用户同时在线,如果房间中已经存在两个用户,则第三个用户无法加入。

  3. 房主和连麦观众分别调用 setChannelProfile 接口,设置房间场景为直播场景(LIVE_BROADCASTING)。

  4. 房主和连麦观众分别调用 setLocalAudioProfile 接口,设置音频 profile 类型为 HighQualityStereo,设置 scenarioMUSIC

  5. 连麦观众调用 leaveRoom 接口离开一起听房间。

  6. 房主调用 endRoom 接口结束一起听房间。

示例代码

// 房主调用自己的业务服务器restful api创建房间。
// 房主或者观众加入房间:
         String roomUuid="123";
            String userName="userName";
            String avatar="your avatar";
            String role="host";// host为房主,audience为观众
            NERoomKit.getInstance().getRoomService().joinRoom(new NEJoinRoomParams(roomUuid,userName,avatar,role,null,new HashMap<>()), new NEJoinRoomOptions(), new NECallback2<NERoomContext>() {
              @Override
              public void onSuccess(@Nullable NERoomContext roomContext) {
                // 设置为直播场景
                  roomContext.getRtcController().setChannelProfile(
                        NERoomRtcChannelProfile.liveBroadcasting
                );
                // 设置音频类型
                roomContext.getRtcController().setLocalAudioProfile(
                        NERoomRtcAudioProfile.HIGH_QUALITY_STEREO,
                        NERoomRtcAudioScenario.MUSIC
                );
                // 加入rtc
                roomContext.getRtcController().joinRtcChannel(null);
                // 加入聊天室
                roomContext.getChatController().joinChatroom(null);
                
              }

              @Override
              public void onError(int code, @Nullable String message) {
                super.onError(code, message);
              }
            });
 // 观众离开房间
 roomContext.leaveRoom(null);
 // 房主结束房间
 roomContext.endRoom(false,null);

实现一起听

步骤1 初始化曲库 SDK

  1. 调用 NECopyrightedMedia.getInstance() 接口创建版权音乐对象。 示例代码如下:

    NECopyrightedMedia copyRight = NECopyrightedMedia.getInstance();
    
  2. 调用 NECopyrightedMedia.initialize 接口初始化组件。在调用 SDK 初始化接口之前,需要先为应用下的每一位用户生成user账号(account),同时获取对应的 Token。Token 鉴权相关步骤,请参考 曲库动态 Token 鉴权 即可,下文不再赘述。

    示例代码如下:

    NECopyrightedMedia copyRight = NECopyrightedMedia.getInstance();
    HashMap<String, Object> extras = new HashMap<>();
    copyRight.initialize(
        context,
        appkey,
        token,
        user,     //请传入您的应用账号体系中的账号ID,用于数据统计
        extras,
        new NECopyrightedMedia.Callback<Unit>() {
            @Override
            public void success(@Nullable Unit info) {
            Toast.makeText(
                    context,
                    "init CopyrightedMedia success",
                    Toast.LENGTH_LONG)
                .show();
            }
    
            @Override
            public void error(int code, @Nullable String msg) {
            Toast.makeText(
                    context, "init CopyrightedMedia fail", Toast.LENGTH_LONG)
                .show();
            }
        }
    );
    
  3. 调用 NECopyrightedMedia.setSongScene 接口,指定音乐场景为听歌的场景。

    版权曲库支持听歌场景和 K 歌场景,您需要在初始化曲库 SDK 后指定对应的场景。

    // 设置听歌场景
    NECopyrightedMedia.getInstance().setSongScene(SongScene.TYPE_LISTENING_TO_MUSIC);
    

4.调用 NECopyrightedMedia.setEventHandler 接口注册事件通知回调。

当您的曲库 Token 过期时,会触发 onTokenExpired 回调。此时,您需要参见曲库动态 Token 鉴权生成新的 Token,并调用 renewToken 更新 Token 后才能继续调用 NECopyrightedMedia SDK 的API。示例代码如下:

NECopyrightedMedia.getInstance().setEventHandler(new NECopyrightedEventHandler() {
    @Override
    public void onTokenExpired() {
        // token过期监听回调
    }
});
  1. 调用 NECopyrightedMedia.renewToken 接口更新Token。

    示例代码如下:

    NECopyrightedMedia.getInstance().renewToken(token);
    

    初始化操作完毕,如果能正常调用以下接口,表示初始化成功。

步骤2 获取歌曲列表

房主和连麦观众可以通过搜索、请求歌曲列表、榜单三种方式获取歌曲。

  • 通过搜索获取歌曲

    调用 NECopyrightedMedia.searchSong 接口获取搜索的歌曲列表和歌曲的song ID。

    参数 类型 描述
    keyword String 搜索的关键字。
    channel Int 版权渠道,默认不传则包含所有签约渠道。
    • 1:网易云音乐
    • 2: 咪咕
    • 3: HIFIVE
    pageNum Int 页码。 默认值为 0。
    pageSize Int 每页显示的行数,默认值为 20。
    callback Callback 回调
  • 通过请求歌曲列表获得歌曲

    调用 NECopyrightedMedia.getSongList 接口获取歌曲列表和歌曲的song ID。

  • 通过榜单获取歌曲

    调用 getHotSongList 接口获取推荐歌单的歌曲 song ID。

示例代码如下:

// 搜索歌曲
NECopyrightedMedia.getInstance().searchSong("keyword", null, 1, 20, new NECopyrightedMedia.Callback<List<NECopyrightedSong>>() {
@Override
public void success(@Nullable List<NECopyrightedSong> info) {

}

@Override
public void error(int code, @Nullable String msg) {

}
});

// 获取歌曲列表
NECopyrightedMedia.getInstance().getSongList(null, null, 1, 20, new NECopyrightedMedia.Callback<List<NECopyrightedSong>>() {
@Override
public void success(@Nullable List<NECopyrightedSong> info) {

}

@Override
public void error(int code, @Nullable String msg) {

}
});

// 获取热门歌曲列表
NECopyrightedMedia.getInstance().getHotSongList(NECopyrightedHotType.HOTTYPE_DEFAULT, NECopyrightedHotDimension.HOTDIMENSION_APPLICATION, null, 1, 20, new NECopyrightedMedia.Callback<List<NECopyrightedHotSong>>() {
@Override
public void success(@Nullable List<NECopyrightedHotSong> info) {

}

@Override
public void error(int code, @Nullable String msg) {

}
});

步骤3 点歌并下载歌曲

调用 NECopyrightedMedia.preloadSong 接口预加载歌曲,包括原唱、歌词和MIDI。

示例代码如下:

 NECopyrightedMedia.getInstance().preloadSong(songId, channel, new NESongPreloadCallback() {
      @Override
      public void onPreloadStart(String songId, int channel) {
        
      }

      @Override
      public void onPreloadProgress(String songId, int channel, float progress) {

      }

      @Override
      public void onPreloadComplete(String songId, int channel, int errorCode, String msg) {

      }
    });

步骤4 房主播放歌曲

调用 NERoomRtcController.playEffect 播放歌曲。

示例代码如下:

 String path="";// 音乐文件路径
int loopCount=1;
boolean sendEnabled=false;
int sendVolume=100;
boolean playbackEnabled=true;
int playbackVolume=100;
long startTimestamp= 0;
long progressInterval=100;
NERoomRtcAudioStreamType sendWithAudioType=NERoomRtcAudioStreamType.NERtcAudioStreamTypeSub;
roomContext.getRtcController().playEffect(effectId,new NERoomCreateAudioEffectOption(path,loopCount,sendEnabled,sendVolume,playbackEnabled,playbackVolume,startTimestamp,progressInterval,sendWithAudioType));

步骤5 观众上麦

观众加入房间后,自动上麦并获取歌曲播放信息。

  1. 观众加入房间后,主播调用 NESeatController.sendSeatInvitation 接口邀请观众上麦。上麦成功后,房间内所有成员收到 onSeatInvitationAccepted 的回调和onSeatListChanged的回调。
  2. 观众调用业务服务器接口获取当前播放歌曲列表。
  3. 观众调用 NECopyrightedMedia.preloadSong 接口预加载歌曲。
// 观众进入房间,主播抱麦,观众自动上麦
NERoomContext roomContext = NERoomKit.getInstance().getRoomService().getRoomContext(roomUuid);
if (roomContext!=null){
  roomContext.getSeatController().sendSeatInvitation(index, "account", new NECallback2<Unit>() {
    @Override
    public void onSuccess(@Nullable Unit data) {
      super.onSuccess(data);
    }

    @Override
    public void onError(int code, @Nullable String message) {
      super.onError(code, message);
    }
  });
}
// 调用业务服务器获取当前播放歌曲和歌曲列表(需自行实现)
// 预加载当前播放歌曲,在加载完后通过点对点自定义消息向主播询问当前播放进度
String songId="your songId";
int channel=1;
if (NECopyrightedMedia.getInstance().isSongPreloaded(songId,channel)){
  // 向主播询问当前歌曲播放进度
  NERoomKit.instance.getService(NEMessageChannelService.class).sendCustomMessage(roomUuid,anchorUserUuid,10001,"",null);
}else {
  NECopyrightedMedia.getInstance().preloadSong(songId, channel, new NESongPreloadCallback() {
    @Override
    public void onPreloadStart(String songId, int channel) {

    }

    @Override
    public void onPreloadProgress(String songId, int channel, float progress) {

    }

    @Override
    public void onPreloadComplete(String songId, int channel, int errorCode, String msg) {
        // 向主播询问当前歌曲播放进度
  NERoomKit.instance.getService(NEMessageChannelService.class).sendCustomMessage(roomUuid,anchorUserUuid,10001,"",null);
    }
  });

    

步骤6 同步一起听的歌曲状态和进度

  1. 房主通过 NEMessageChannelService.sendCustomMessage 发送定向消息,将播放进度同步给观众。
  2. 观众调用 NEMessageChannelService.sendCustomMessage 接口获取歌曲播放进度。
    
// 监听来自主播的自定义消息
NERoomKit.instance.getService(NEMessageChannelService.class).addMessageChannelListener(new NEMessageChannelListener() {
@Override
public void onReceiveCustomMessage(@NonNull NECustomMessage message) {
if (message.getCommandId()==10002){
  // 接收到来自主播播放进度消息,假设commandId=10002
String data = message.getData(); // 可定义为json格式
// 自定义消息内容,通过解析获取主播的播放进度
String path="";// 音乐文件路径
int loopCount=1;
boolean sendEnabled=false;
int sendVolume=100;
boolean playbackEnabled=true;
int playbackVolume=100;
long startTimestamp= 30;// 主播播放进度
long progressInterval=100;
NERoomRtcAudioStreamType sendWithAudioType=NERoomRtcAudioStreamType.NERtcAudioStreamTypeSub;
roomContext.getRtcController().playEffect(effectId,new NERoomCreateAudioEffectOption(path,loopCount,sendEnabled,sendVolume,playbackEnabled,playbackVolume,startTimestamp,progressInterval,sendWithAudioType));
}else if(commandId=10001){
   // 回复当前播放进度给对方
   String data="d";
NERoomKit.instance.getService(NEMessageChannelService.class).sendCustomMessage(roomUuid,anotherUserUuid,10002,data,null);   
}
}
});
  // 向主播询问当前歌曲播放进度
NERoomKit.instance.getService(NEMessageChannelService.class).sendCustomMessage(roomUuid,anchorUserUuid,10001,"",null);

进阶功能

暂停或恢复歌曲播放

// 暂停歌曲播放
roomContext.getRtcController().pauseEffect(effectId);
// 恢复歌曲播放
roomContext.getRtcController().resumeEffect(effectId);

切歌

// 停止播放上一首歌
roomContext.getRtcController().stopEffect(effectId);
// 播放下一首歌
String path="";// 音乐文件路径
int loopCount=1;
boolean sendEnabled=false;
int sendVolume=100;
boolean playbackEnabled=true;
int playbackVolume=100;
long startTimestamp= 0;
long progressInterval=100;
NERoomRtcAudioStreamType sendWithAudioType=NERoomRtcAudioStreamType.NERtcAudioStreamTypeSub;
roomContext.getRtcController().playEffect(effectId,new NERoomCreateAudioEffectOption(path,loopCount,sendEnabled,sendVolume,playbackEnabled,playbackVolume,startTimestamp,progressInterval,sendWithAudioType));  

相关文档

实现一起听(基于 RTC)

此文档是否对你有帮助?
有帮助
去反馈
  • 方案架构
  • 开发环境要求
  • 前提条件
  • 示例项目源码
  • API 时序图
  • 房间管理
  • 实现一起听
  • 步骤1 初始化曲库 SDK
  • 步骤2 获取歌曲列表
  • 步骤3 点歌并下载歌曲
  • 步骤4 房主播放歌曲
  • 步骤5 观众上麦
  • 步骤6 同步一起听的歌曲状态和进度
  • 进阶功能
  • 暂停或恢复歌曲播放
  • 切歌
  • 相关文档