实现语聊房(基于NERoom)

更新时间: 2023/11/22 03:29:42

本文档为您展示通过 NERoom房间组件实现语聊房场景的相关步骤,帮助您在业务中实现麦位管理、低延时语音互动、文字聊天等能力。

开发环境要求

开发环境要求如下:

环境要求 说明
iOS 版本 11.0 及以上的 iPhone 或者 iPad 真机
CPU 架构 ARM64、ARMV7
IDE XCode
其他 安装 CocoaPods

前提条件

方案架构

基于NERoom的方案架构.png

方案架构说明如下:

  • 主播、连麦者和观众加入同一个音视频房间。
  • 主播和连麦者在音视频房间内进行音频流的实时发布和订阅。
  • 观众在音视频房间内仅订阅音频流,不发布音频流。

API调用时序图

sequenceDiagram
  participant hostClientA as 主播A client
  participant NERoomA as NERoom
  participant app_server as VoiceRoom_Server
  participant NERoomB as NERoom
  participant audienceB as 观众B client


  note over hostClientA, audienceB: 主播开播
  hostClientA ->> NERoomA: createRoom
  NERoomA ->> app_server: createRoom
  app_server -->> hostClientA: 房间信息
  hostClientA ->> NERoomA: joinRoom
  NERoomA ->> app_server: joinRoom
  app_server -->> hostClientA: 加入成功
  hostClientA ->> NERoomA: unmuteMyAudio
  NERoomA ->> app_server: unmuteMyAudio

  note over hostClientA, audienceB: 观众进入房间
  audienceB ->> NERoomB: joinRoom
  NERoomB ->> app_server: joinRoom
  app_server -->> audienceB: 房间信息
  audienceB ->> NERoomB: unmuteMyAudio
  NERoomB ->> app_server: unmuteMyAudio


sequenceDiagram
participant hostClientA as 主播A client
participant NERoomA as NERoom
participant app_server as VoiceRoom_Server
participant NERoomB as NERoom
participant audienceB as 观众B client

note over hostClientA, audienceB: 抱观众上麦
hostClientA ->> NERoomA: sendSeatInvitation <br>(邀请上麦)
NERoomA ->> app_server: sendSeatInvitation <br>(邀请上麦)
app_server -->> NERoomB: onSeatInvitationAccepted <br>(自动同意上麦的回调)
NERoomB -->> audienceB: onSeatInvitationAccepted <br>(自动同意上麦的回调)
app_server -->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB -->> audienceB: onSeatListChanged <br>(麦位变更的回调)
app_server -->> NERoomA: onSeatInvitationAccepted <br>(自动同意上麦的回调)
NERoomA -->> hostClientA: onSeatInvitationAccepted <br>(自动同意上麦的回调)
app_server -->> NERoomA: onSeatListChanged <br> (麦位变更的回调)
NERoomA -->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI

note over hostClientA, audienceB: 将观众移出麦位
hostClientA ->> NERoomA: kickSeat <br>(主播踢观众B下麦)
NERoomA ->> app_server: kickSeat <br>(主播踢观众B下麦)
app_server -->> NERoomB: onSeatKicked <br>(主播踢观众B下麦的回调)
NERoomB -->> audienceB: onSeatKicked <br>(主播踢观众B下麦的回调)
app_server -->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB -->> audienceB: onSeatListChanged <br>(麦位变更的回调)
app_server -->> NERoomA: onSeatKicked <br>(主播踢观众B下麦的回调)
NERoomA -->> hostClientA: onSeatKicked <br>(主播踢观众B下麦的回调)
app_server -->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA -->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI


sequenceDiagram
participant hostClientA as 主播A client
participant NERoomA as NERoom
participant app_server as VoiceRoom_Server
participant NERoomB as NERoom
participant audienceB as 观众B client


note over hostClientA, audienceB: 观众申请上麦
audienceB ->> NERoomB: submitSeatRequest <br>(申请上麦)
NERoomB ->> app_server: submitSeatRequest <br>(申请上麦)
app_server ->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB ->> audienceB: onSeatListChanged <br>(麦位变更的回调)
app_server ->> NERoomA: onSeatRequestSubmitted <br>(观众B申请上麦的回调)
NERoomA ->> hostClientA: onSeatRequestSubmitted <br>(观众B申请上麦的回调)
app_server ->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA ->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI

alt 主播同意上麦申请
hostClientA ->> NERoomA: approveSeatRequest <br>(主播同意上麦申请)
NERoomA ->> app_server: approveSeatRequest <br>(主播同意上麦申请)
app_server ->> NERoomA: onSeatRequestApproved <br>(主播同意上麦申请的回调)
NERoomA ->> hostClientA: onSeatRequestApproved <br>(主播同意上麦申请的回调)
app_server ->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA ->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
app_server ->> NERoomB: onSeatRequestApproved <br>(主播同意上麦申请的回调)
NERoomB ->> audienceB: onSeatRequestApproved <br> (主播同意上麦申请的回调)
app_server ->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB ->> audienceB: onSeatListChanged <br> (麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI
else 主播拒绝上麦申请
hostClientA ->> NERoomA: rejectRequestSeat <br>(主播不同意上麦申请)
NERoomA ->> app_server: rejectRequestSeat <br>(主播不同意上麦申请)
app_server -->> NERoomA: onSeatRequestRejected <br>(主播拒绝观众B上麦的回调)
NERoomA -->> hostClientA: onSeatRequestRejected <br>(主播拒绝观众B上麦的回调)
app_server -->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA -->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
app_server -->> NERoomB: onSeatRequestRejected <br>(主播拒绝观众B上麦的回调)
NERoomB -->> audienceB: onSeatRequestRejected <br>(主播拒绝观众B上麦的回调)
app_server -->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB -->> audienceB: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI
end


sequenceDiagram
participant hostClientA as 主播A client
participant NERoomA as NERoom
participant app_server as VoiceRoom_Server
participant NERoomB as NERoom
participant audienceB as 观众B client

note over hostClientA, audienceB: 观众取消申请上麦
audienceB ->> NERoomB: cancelSeatRequest<br> (取消申请上麦)
NERoomB ->> app_server: cancelSeatRequest <br> (取消申请上麦)
app_server -->> NERoomA: onSeatRequestCancelled <br>(观众B取消上麦的回调)
NERoomA -->> hostClientA: onSeatRequestCancelled <br>(观众B取消上麦的回调)
app_server -->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA -->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
app_server -->> NERoomB: onSeatRequestCancelled <br>(观众B取消上麦的回调)
NERoomB -->> audienceB: onSeatRequestCancelled <br> (观众B取消上麦的回调)
app_server -->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB -->> audienceB: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI

note over hostClientA, audienceB: 观众下麦
audienceB ->> NERoomB: leaveSeat (下麦)
NERoomB ->> app_server: leaveSeat (下麦)
app_server -->> NERoomA: onSeatLeave <br>(观众B成功下麦的回调)
NERoomA -->> hostClientA: onSeatLeave <br>(观众B成功下麦的回调)
app_server -->> NERoomA: onSeatListChanged <br>(麦位变更的回调)
NERoomA -->> hostClientA: onSeatListChanged <br>(麦位变更的回调)
app_server -->> NERoomB: onSeatLeave <br>(观众B成功下麦的回调)
NERoomB -->> audienceB: onSeatLeave <br>(观众B成功下麦的回调)
app_server -->> NERoomB: onSeatListChanged <br>(麦位变更的回调)
NERoomB -->> audienceB: onSeatListChanged <br>(麦位变更的回调)
audienceB ->> audienceB: 刷新UI
hostClientA ->> hostClientA: 刷新UI


登录鉴权

调用 NEAuthService 实例中的login接口进行账号登录。

let account:String = "your account"
let token:String = "your token"

  NERoomKit.shared().authService.login(account: account,
                                         token: token) { code, str, _ in
      if code == 0 {
        // 登陆成功
      } else {
        // 登陆失败
      }
    }

示例代码中的your accountyour token是您的应用服务器向 NERoom 服务器申请创建账号时,返回的 userUuiduserToken 的值,具体请参见创建账号

主播开播

  1. 主播调用 createRoom 接口创建房间。

    创建房间麦位 NESeatInitParams 参数说明:

    参数 类型 描述
    seatCount int 麦位数量
    seatRequestApprovalMode int NESeatRequestApprovalMode 内获取麦位申请审批模式。
    • OFF:关闭审批模式,即成员提交申请后不需要管理员同意,可直接上麦。
    • ON:开启审批模式,即成员提交申请后需要管理员同意才能上麦。
    seatInvitationConfirmMode int 麦位邀请确认模式。
    • OFF: 关闭确认模式,即管理员邀请其他成员上麦,不需要被邀请的成员确认,可直接上麦。
    • ON:开启确认模式,即管理员邀请其他成员上麦,需要被邀请的成员确认,接受后方可上麦。

    示例代码如下:

    NECreateRoomParams *param = [[NECreateRoomParams alloc] init];
                  param.roomUuid = uuid;
                  param.roomName = name;
                  param.templateId = config.integerValue;
    
                  NESeatInitParams *seatParams = [[NESeatInitParams alloc] init];
                  seatParams.seatCount = 8;
                  seatParams.seatRequestApprovalMode = NESeatRequestApprovalModeOn;
                  param.seatInitParams = seatParams;
    
                  NECreateRoomOptions *options = [[NECreateRoomOptions alloc] init];
                  options.enableRtc = true;
                  options.enableChatroom = true;
                  options.enableWhiteboard = true;
                  [[[NERoomKit shared] roomService]
                      createRoomWithParams:param
                                   options:options
                                  callback:^(NSInteger code, NSString *_Nullable msg,
                                             id _Nullable obj) {
                                  }];
    
  2. 主播调用 joinRoom接口加入房间。

    示例代码如下:

    NEJoinRoomParams *param = [NEJoinRoomParams new];
    param.roomUuid = roomUuid;
    param.userName = userName;
    param.role = role;
    
    NEJoinRoomOptions *options = [[NEJoinRoomOptions alloc] init];
    [[[NERoomKit shared] roomService]
        joinRoomWithParams:param
                   options:options
                  callback:^(NSInteger code, NSString *_Nullable string,
                             NERoomContext *_Nullable context) {
    
                  }];
    
  3. 调用 joinRtcChannel 接口加入音视频房间。

    示例代码如下:

    [self.context.rtcController
    joinRtcChannel:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    
    }];
    
  4. 调用 addRoomListener 接口监听房间里的事件。

    详细的事件列表请参见监听房间事件

    示例代码如下:

    [self.context addRoomListenerWithListener:self];
    
  5. 收到加入成功的 onMemberJoinRoom 回调后,调用 unmuteMyAudio 方法开启本地音频采集。

    示例代码如下:

    [self.context.rtcController
    unmuteMyAudio:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    }];
    

观众进入房间

  1. 观众调用 joinRoom接口加入房间。

  2. 观众调用 addRoomListener 接口监听房间里的事件。

  3. 观众调用 muteMyAudio 方法关闭本地音频采集。

    等到上麦后,再开启本地音频采集。

麦位管理

添加麦位事件监听

调用 addSeatListener 接口,监听麦位相关的事件。

详细的事件列表请参见NESeatEventListener

示例代码如下:

[self.context.seatController addSeatListener:self];

主播邀请观众上麦

  • 主播调用 sendSeatInvitation 接口邀请观众上麦。

    [self.context.seatController
    sendSeatInvitation:userId
              callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {
              }];
    
  • 主播也可以在邀请观众上麦时指定麦位。

    [self.context.seatController
    sendSeatInvitation:self.index
                  user:self.userId
              callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    
              }];
    

观众申请上麦

观众调用 submitSeatRequest 接口申请上麦。

  • 观众不指定麦位

    [self.context.seatController
    submitSeatRequest:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {
    
    }];
    
  • 观众指定麦位

    
    [self.context.seatController
        submitSeatRequest:index
                  callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    
                  }];
    

主播同意观众上麦

主播调用 approveSeatRequest 接口同意观众上麦。

[self.context.seatController
    approveSeatRequest:userId
              callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {

              }];

主播拒绝观众上麦

主播调用 rejectSeatRequest 接口拒绝观众上麦。

[self.context.seatController
    rejectSeatRequest:self.userId
              callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {

              }];

主播将观众移出麦位

主播调用 kickSeat 接口将观众移出麦位。

[self.context.seatController
    kickSeat:userId
    callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {

    }];

观众取消申请上麦

观众调用 cancelSeatRequest 接口取消申请上麦。

[self.context.seatController
    cancelSeatRequest:^(NSInteger code, NSString *_Nullable msg, id _Nullable objc) {

    }];

观众主动下麦

观众调用 leaveSeat 接口下麦。

[self.context.seatController leaveSeat:^(NSInteger code, NSString *_Nullable msg,
                                          id _Nullable objc) {

}];

离开房间

  1. 调用 leaveRoom 接口离开房间。

    示例代码如下:

      [self.context leaveRoomWithCallback:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    
      }];
    
  2. 离开房间成功后,会触发 NERoomListener.onMemberLeaveRoom 回调,通知房间内所有成员。

  3. 主播调用 endRoom 接口删除房间。

    示例代码如下:

      [self.context
      endRoomWithIsForce:true
                callback:^(NSInteger code, NSString *_Nullable msg, id _Nullable obj) {
    
        }];
    
  4. 删除房间成功后,会触发 NERoomListener 协议中的 onRoomEnded 回调方法。

进阶功能

伴音

  1. 调用 RtcControllerstartAudioMixing 方法播放伴音。

    NERoomCreateAudioMixingOption 参数说明如下表所示。

    参数 类型 描述
    path String 待播放的音乐文件的绝对路径或 URL 地址,支持本地 SD 卡中的绝对路径或 URL 地址
    loopCount int 伴音循环播放的次数。1:(默认)播放一次。≤ 0:无限循环播放
    sendEnabled boolean 是否将伴音发送远端,默认为 true,即远端用户可以听到该伴音。
    sendVolume int 音乐文件的发送音量,取值范围为 0~200。默认为 100,表示使用文件的原始音量。
    playbackEnabled boolean 是否本地播放伴音。默认为 true,即本地用户可以听到该伴音。
    playbackVolume int 音乐文件的播放音量,取值范围为 0~200。默认为 100,表示使用文件的原始音量。
    startTimeStamp long 音乐文件开始播放的时间,UTC 时间戳,即从1970 年 1 月 1 日 0 点 0 分 0 秒开始到事件发生时的毫秒数。默认值为 0,表示立即播放。
    sendWithAudioType NERoomAudioStreamType 伴音跟随音频主流还是辅流,默认跟随主流。
    • NERoomAudioStreamTypeMain:伴音跟随主流
    • NERoomAudioStreamTypeSub:伴音跟随辅流
  2. 设置伴音的音量。

  3. 在离开房间前调用 stopAudioMixing 结束伴音。

示例代码如下:

  // 开始伴音
  NERoomCreateAudioMixingOption *option = [[NERoomCreateAudioMixingOption alloc] init];
  option.path = @"";
  option.loopCount = -1;
  option.sendEnabled = true;
  option.sendVolume = 100;
  option.playbackEnabled = true;
  option.playbackVolume = 100;
  option.startTimeStamp = 0;
  option.sendWithAudioType = NERoomAudioStreamTypeMain;
  [self.context.rtcController startAudioMixingWithOption:option];
  
  // 设置伴音音量
  [self.context.rtcController setAudioMixingSendVolumeWithVolume:90];
  [self.context.rtcController setAudioMixingPlaybackVolumeWithVolume:90];
  
  // 结束伴音
  [self.context.rtcController stopAudioMixing];

音效

  1. 调用 RtcControllerplayEffect 方法播放音效。

    NERoomCreateAudioEffectOption 参数说明如下表所示。

    参数 类型 描述
    path String 待播放的音乐文件的绝对路径或 URL 地址,支持本地 SD 卡中的绝对路径或 URL 地址
    loopCount int 音效循环播放的次数。1:(默认)播放一次。≤ 0:无限循环播放
    sendEnabled boolean 是否将音效发送远端,默认为 true,即远端用户可以听到该音效。
    sendVolume int 音效文件的发送音量,取值范围为 0~200。默认为 100,表示使用文件的原始音量。
    playbackEnabled boolean 是否本地播放音效。默认为 true,即本地用户可以听到该音效。
    playbackVolume int 音乐文件的播放音量,取值范围为 0~200。默认为 100,表示使用文件的原始音量。
    startTimeStamp long 音乐文件开始播放的时间,UTC 时间戳,即从1970 年 1 月 1 日 0 点 0 分 0 秒开始到事件发生时的毫秒数。默认值为 0,表示立即播放。
    sendWithAudioType NERoomAudioStreamType 伴音跟随音频主流还是辅流,默认跟随主流。
    • NERoomAudioStreamTypeMain:伴音跟随主
    • NERoomAudioStreamTypeSub:伴音跟随辅流
  2. 设置音效的音量。

  3. 在离开房间前,调用 stopAllEffects 方法停止播放所有音效文件。

示例代码如下:

  int effectId = 1;
  // 开始音效
  NERoomCreateAudioEffectOption *option = [[NERoomCreateAudioEffectOption alloc] init];
  option.path = @"";
  option.loopCount = -1;
  option.sendEnabled = true;
  option.sendVolume = 100;
  option.playbackEnabled = true;
  option.playbackVolume = 100;
  option.startTimeStamp = 0;
  option.sendWithAudioType = NERoomAudioStreamTypeMain;
  [self.context.rtcController playEffectWithEffectId:effectId option:option];
  
  // 设置音效音量
  [self.context.rtcController setEffectPlaybackVolumeWithEffectId:effectId volume:90];
  [self.context.rtcController setEffectSendVolumeWithEffectId:effectId volume:90];
  
  // 停止指定音效
  [self.context.rtcController stopEffectWithEffectId:effectId];
  // 停止所有音效
  [self.context.rtcController stopAllEffects];

耳返

  1. 通过 onRtcAudioOutputDeviceChanged 监听播放设备的变化,当监听到播放设备切换为耳机时才开启耳返。
    - (void)onRtcAudioOutputDeviceChangedWithDevice:(enum NEAudioOutputDevice)device {
    switch (device) {
    case NEAudioOutputDeviceSpeakerPhone:
      // 扬声器
      break;
    case NEAudioOutputDeviceWiredHeadset:
      // 有线耳机,可开启耳返
      break;
    case NEAudioOutputDeviceEarpiece:
      // 听筒
      break;
    case NEAudioOutputDeviceBluetoothHeadset:
      // 无线耳机,可开启耳返
      break;
    default:
      break;
        }
    }
    
  2. 调用 enableEarBack 方法开启耳返。
    [self.context.rtcController enableEarbackWithVolume:100];
    
  3. 调用 disableEarBack 方法关闭耳返。
    [self.context.rtcController disableEarback];
    
此文档是否对你有帮助?
有帮助
去反馈
  • 开发环境要求
  • 前提条件
  • 方案架构
  • API调用时序图
  • 登录鉴权
  • 主播开播
  • 观众进入房间
  • 麦位管理
  • 添加麦位事件监听
  • 主播邀请观众上麦
  • 观众申请上麦
  • 主播同意观众上麦
  • 主播拒绝观众上麦
  • 主播将观众移出麦位
  • 观众取消申请上麦
  • 观众主动下麦
  • 离开房间
  • 进阶功能
  • 伴音
  • 音效
  • 耳返