客户端实现

大约 11 分钟

客户端实现

场景描述

本文介绍如何通过环信 IM SDK 和 Agora Audio SDK 在你的 iOS 项目里实现语聊房的主要功能。

技术原理

房间管理

语聊房 SDK 内部依赖环信 IM 聊天室(Chatroom)与 Agora Audio 的音频房间(RTC Channel)。

语聊房内用户角色说明如下:

角色描述
房主语聊房创建者,在 Demo 中占用 0 号麦位,不可修改。房主可以接收、发送音频流。
主播进入语聊房后,通过上麦成为主播,可以接收、发送音频流。
观众进入语聊房后,接收音频流。申请上麦或房主邀请上麦后,可以和主播实时互动。

以下为房间管理流程图。

img

房间管理流程如下:

  1. 用户利用手机号和验证码登录环信 IM。
  2. 房主可创建语聊房,普通用户加入语聊房。
  3. 加入环信 IM 和 RTC 频道。
  4. 进入语聊房,检查是否成功加入环信 IM 和 RTC 频道。
    • 若成功加入,房主可以发送本地音频流和消息,观众可以发消息。
    • 若未成功加入,退出房间,返回房间列表页面。
  5. 退出房间。

房间相关操作如下表所示:

操作描述
创建房间房主通过 App Server 创建语聊房,创建成功后自动加入语聊房。房主自动上麦成为主播,更新房间内麦位信息。加入语聊房前,调用 VoiceRoomIMManager.configIM 方法初始化环信 IM,调用 ASRTCKit.getSharedInstance 方法初始化 Agora Audio。
加入房间用户可调用 joinRoom 方法加入语聊房。加入语聊房前,调用 VoiceRoomIMManager.loginIM 方法登录环信 IM,然后调用 VoiceRoomIMManager.joinChatRoomself.rtcKit.joinChannel 方法加入聊天室和 RTC 频道,成功后再加入语聊房。
离开房间观众或主播可调用 leaveRoom() 方法离开语聊房。房主离开语聊房,语聊房对象自动销毁,其他成员自动离开语聊房。
发送音频流观众或主播可调用 self.rtcKit.joinChannel 方法发送音频流。
发送消息语聊房内的用户可调用 VoiceRoomIMManager.sendMessage 或者 VoiceRoomIMManager.sendCustomMessage 方法发送消息。观众也可以调用 VoiceRoomIMManager.sendGift 方法发送礼物消息。

麦位管理

以下为麦位管理流程图:

img

麦位相关操作如下所示:

操作描述
邀请上麦房主调用 inviteUserToMic RESTful API 邀请观众上麦。观众收到 receiveInviteSite 回调,选择是否同意上麦。
- 观众调用 agreeInvite RESTful API 同意上麦,成为主播,App Server 设置聊天室属性。同时,房间内所有用户会收到该观众连麦的回调 VoiceRoomIMDelegate.chatroomAttributesDidUpdate,更新房间内麦位信息。
申请上麦观众调用 requestSpeak 方法向房主申请上麦。房主收到 VoiceRoomIMManager.receiveApplySite 回调,选择同意或拒绝申请。
- 房主调用 agreeUserApply 方法同意上麦申请,申请者收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调并调用 self.rtcKit.joinChannel 方法成为主播。同时,房间内所有用户会收到该观众连麦的回调 VoiceRoomIMDelegate.chatroomAttributesDidUpdate,并更新房间内麦位信息。
- 房主调用 refuseApply RESTful API 方法拒绝上麦申请。
下麦下麦分为主动和被动两种方式:
- 主动下麦:主播可调用 leaveMic 方法下麦成为观众。
- 被踢下麦:房主调用 kickOff RESTful API 对主播发起下麦指令。该连麦主播收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,然后下麦成为观众。
对于这两种下麦方式,房间内所有用户都会收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,更新房间内麦位信息。
禁麦房主调用 muteMic RESTful API 禁麦,不允许指定连麦主播发言,该主播的音频流将关闭。包括主播在内的房间内所有用户会收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,更新房间内麦位信息。
解禁麦位房主调用 unmuteMic RESTful API 解禁麦位,恢复连麦主播的发言权限,即恢复该麦位主播的音频流。包括主播在内的房间内所有用户会收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,更新房间内麦位信息。
锁麦房主调用 lockMic RESTful API 锁麦,不允许任何人上该麦位。锁麦时,若该麦位有主播连麦,该主播收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,被踢下来成为普通观众。房间内所有用户也会收到该回调,更新房间内麦位信息。
解锁麦位房主调用 unlockMic RESTful API 解锁麦位,使指定麦位恢复空闲状态,观众可以在该麦位申请上麦。房间内所有用户会收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,更新房间内麦位信息。
换麦主播可调用 changeMic 方法换麦,即从当前麦位切换到另一个空闲麦位。包括主播在内的房间内所有用户会收到 VoiceRoomIMDelegate.chatroomAttributesDidUpdate 回调,更新房间内麦位信息。

发送单向消息流程图

img

邀请用户上麦的流程如下:

  1. 房主调用 RESTFul API 邀请用户上麦。
  2. App Server 收到邀请消息后,利用房主角色向用户发送该消息。
  3. 用户同意邀请后调用 RESTful API 上麦。
  4. App Server 设置聊天室属性通知房间全员刷新房间设置。

用户申请上麦的流程如下:

  1. 用户调用 RESTful 接口申请上麦。
  2. App Server 收到上麦申请消息后以用户角色向房主发送该消息。
  3. 房主收到上麦申请后调用 RESTful API 同意上麦。
  4. App Server 设置聊天室属性通知房间全员刷新房间设置。

发送礼物消息流程图

img

发送礼物消息的流程如下:

  1. 用户 A 发送礼物详情到 App Server。
  2. App Server 统计礼物信息后对用户 A 返回响应。
  3. SDK 将礼物消息发送到语聊房。

前提条件

项目配置

产品SDK 下载集成文档
环信即时通讯 IM SDK环信即时通讯 IM SDK 3.9.8 或以上版本发送、接收消息、聊天室属性 KV
Agora Audio SDK声网 RTM SDK 4.0.1实现音频通话open in new window

基本 API 参考

本节提供环信 IM SDK 和 Agora Audio SDK 的基本 API 参考。

房间管理 API 列表

API实现功能
VoiceRoomBusinessApi.fetchRoomInfoopen in new window获取指定语聊房的信息。
VoiceRoomBusinessApi.createRoomopen in new window创建房间。
VoiceRoomBusinessApi.deleteRoomopen in new window根据房间 ID 删除房间。
VoiceRoomBusinessApi.modifyRoomInfoopen in new window修改语聊房信息。
VoiceRoomBusinessApi.fetchRoomListopen in new window获取房间列表。
VoiceRoomBusinessApi.fetchRoomMembersopen in new window获取房间内的成员。
VoiceRoomBusinessApi.joinRoomopen in new window加入房间。
VoiceRoomBusinessApi.validatePassWordopen in new window校验密码。
VoiceRoomBusinessApi.leaveRoomopen in new window离开房间。
VoiceRoomBusinessApi.kickUseropen in new window踢出房间。

麦位管理 API 列表

API实现功能
VoiceRoomBusinessApi.fetchMicsInfoopen in new window获取麦位信息。
VoiceRoomBusinessApi.fetchApplyMembersopen in new window获取上麦申请列表。
VoiceRoomBusinessApi.inviteUserToMicopen in new window邀请上麦。
VoiceRoomBusinessApi.agreeInviteopen in new window用户同意上麦邀请。
VoiceRoomBusinessApi.refuseInviteopen in new window用户拒绝上麦邀请。
VoiceRoomBusinessApi.submitApplyopen in new window提交上麦申请。
VoiceRoomBusinessApi.cancelApplyopen in new window撤销上麦申请。
VoiceRoomBusinessApi.refuseApplyopen in new window拒绝上麦申请。
VoiceRoomBusinessApi.agreeApplyopen in new window同意上麦申请。
VoiceRoomBusinessApi.closeMicopen in new window关麦。
VoiceRoomBusinessApi.cancelCloseMicopen in new window开麦。
VoiceRoomBusinessApi.leaveMicopen in new window主动下麦。
VoiceRoomBusinessApi.muteMicopen in new window封禁指定麦位。
VoiceRoomBusinessApi.unmuteMicopen in new window解禁指定麦位。
VoiceRoomBusinessApi.exchangeMicopen in new window换麦。
VoiceRoomBusinessApi.kickMicopen in new window踢用户下麦。
VoiceRoomBusinessApi.lockMicopen in new window锁定麦位。
VoiceRoomBusinessApi.unlockMicopen in new window解锁麦位。

麦位相关方法,详见 VoiceRoomViewController+Micopen in new window 文件。

礼物榜单 API 列表

API实现功能
VoiceRoomBusinessApi.fetchGiftContributeopen in new window获取赠送礼物榜单。
VoiceRoomBusinessApi.giftToopen in new window赠送礼物。

对 HyphenateChat IM SDK 进行封装的类中的方法

API实现功能
VoiceRoomIMManager.configIMopen in new window初始化环信 IM。
VoiceRoomIMManager.loginIMopen in new window登录环信 IM。
VoiceRoomIMManager.addChatRoomListeneropen in new window添加 IM 消息以及聊天室回调监听。
VoiceRoomIMManager.removeListeneropen in new window移除 IM 消息以及聊天室回调监听。

Agora Audio iOS SDK 的 API 封装回调

API实现功能
AgoraRtcEngineKit.sharedEngine(withAppId: AgoraConfig.rtcId, delegate: nil)open in new window初始化 RTC。
ASRTCKit.joinVoicRoomWithopen in new window加入 RTC 频道。
ASRTCKit.setClientRoleopen in new window设置 RTC 角色。
ASRTCKit.leaveChannelopen in new window离开 RTC 频道。

初始化设置

加入语聊房前,进行环信 IM SDK 初始化open in new windowAgora Audio 初始化open in new window设置。

加入/离开环信 IM 聊天室或者声网 RTC 频道

API实现功能
joinChatroomopen in new window加入语聊房。房间内的观众会收到 userDidJoinChatroom 回调。用户加入语聊房后才能接收或发布音频流。
leaveChatroomopen in new window离开语聊房。其他成员收到 userDidLeaveChatroom 回调。房主离开语聊房后,房间对象自动销毁,其他成员会自动离开语聊房。
joinChannelbyTokenopen in new window加入 RTC 频道。用户加入语聊房后才能接收或发布音频流。房间内的观众会收到 ASManagerDelegate.didRtcLocalUserJoinedOfUid 回调。
leaveChannelopen in new window离开 RTC 频道。

通过 App Server 加入或离开语聊房的方法的示例代码如下:

// 加入语聊房
    func uploadStatus( status: Bool) {
        guard let roomId = self.roomInfo?.room?.room_id  else { return }
        VoiceRoomBusinessRequest.shared.sendPOSTRequest(api: .joinRoom(roomId: roomId), params: [:]) { dic, error in
            if let result = dic?["result"] as? Bool,error == nil,result {
                self.view.makeToast("Joined successful!".localized(),point: self.toastPoint, title: nil, image: nil, completion: nil)
            } else {
                self.didHeaderAction(with: .back,destroyed: false)
            }
        }
    }
// 离开语聊房
    @objc private func leaveRoom() {
        if let room_id = roomInfo?.room?.room_id {
            VoiceRoomBusinessRequest.shared.sendDELETERequest(api: .leaveRoom(roomId: room_id), params: [:]) { map, err in
                print(map?["result"] as? Bool ?? false)
            }
        }
    }

音频相关

主播调用以下方法设置音频流:

API实现功能
setAudioProfile [2/2]open in new window设置音频编码属性。
muteLocalAudioStreamopen in new window主播可以关闭或开启本地麦克风。
adjustRecordingSignalVolumeopen in new window调节人声音量。

附加功能

最佳音效

调用 setAudioEffectPresetopen in new window 方法,在不改变原声的性别特征的前提下,设置人声音效。设置音效后,频道内所有用户都能听到该效果。

    rtcKit.setChannelProfile(.liveBroadcasting)
    rtcKit.setAudioProfile(.musicHighQuality)
    rtcKit.setAudioScenario(.gameStreaming)

AI 降噪

AI 降噪插件使用声网人工智能噪声消除算法,能够让远程交流和面对面交谈一样实时。

可以开启或关闭 AI 降噪以及设置中级降噪和高级降噪。示例代码如下:

public func setAINS(with level: AINS_STATE) {
        switch level {
        case .high:
        rtcKit.setParameters("{\"che.audio.ains_mode\":2}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerBound\":10}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerMask\":10}")
        rtcKit.setParameters("{\"che.audio.nsng.statisticalbound\":0}")
        rtcKit.setParameters("{\"che.audio.nsng.finallowermask\":8}")
        rtcKit.setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
        case .mid:
        rtcKit.setParameters("{\"che.audio.ains_mode\":2}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerBound\":80}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerMask\":50}")
        rtcKit.setParameters("{\"che.audio.nsng.statisticalbound\":5}")
        rtcKit.setParameters("{\"che.audio.nsng.finallowermask\":30}")
        rtcKit.setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
        case .off:
        rtcKit.setParameters("{\"che.audio.ains_mode\":0}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerBound\":80}")
        rtcKit.setParameters("{\"che.audio.nsng.lowerMask\":50}")
        rtcKit.setParameters("{\"che.audio.nsng.statisticalbound\":5}")
        rtcKit.setParameters("{\"che.audio.nsng.finallowermask\":30}")
        rtcKit.setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
    }
}