客户端实现

大约 12 分钟

客户端实现

场景描述

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

技术原理

房间管理

语聊房 Demo 内部依赖环信 IM 聊天室与 Agora RTC 频道。

以下为房间管理流程图。

img

Demo 中创建或加入语聊房的流程如下:

  1. 启动 Demo。在 application 中调用 ChatroomConfigManager.getInstance.initRoomConfig 方法初始化环信 IM SDK。
  2. 利用手机号和验证码登录环信 IM。
  3. 登录 IM 后,通过 App Server 创建语聊房。成功后,房间创建者成为房主自动加入语聊房并跳转至语聊房页面。
  4. 进入房间后加入环信 IM 聊天室和 RTC 频道。 首先检查是否具有 RTC 所需权限。获取到所需权限并得到授权后执行 onPermissionGrant 方法,调用 initSdkJoin 进行 RTC 初始化(初始化 RTC 加入频道需要的参数由登录时由 App Server 返回)。
  5. 检查是否成功加入环信 IM 聊天室 和 RTC 频道。
    • 若成功加入,房主可以发送本地音频流和消息,观众可以发消息。
    • 若未成功加入,则退出房间,返回房间列表页面。
  6. 退出房间。

语聊房的用户角色如下表所示:

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

语聊房的相关操作如下表所示:

操作描述
创建房间房主调用 App Server 的 ChatroomHttpManager.getInstance.createRoom 方法创建语聊房,创建成功后自动加入语聊房。房主自动上麦成为主播,更新房间内麦位信息。
创建语聊房前,房主调用 ChatroomConfigManager.getInstance.initRoomConfig 方法初始化环信 IM SDK。
加入房间用户调用 App Server 的 ChatroomHttpManager.getInstance.joinRoom 方法加入语聊房。加入语聊房前,首先调用 ChatroomHttpManager.getInstance.loginWithToken 方法登录环信 IM,然后调用 initSdkJoin 进行 RTC 初始化。
离开房间观众或主播可调用 ChatroomHttpManager.getInstance.leaveRoom 方法离开语聊房。房主离开语聊房,语聊房对象自动销毁,其他成员自动离开语聊房。
发送音频流观众或主播可调用 RtcRoomController.get.joinChannel 方法发送音频流。
发送消息语聊房内的用户可调用 ChatroomHelper.getInstance.sendTextMsg 或者 CustomMsgHelper.getInstance.sendCustomMsg 方法发送消息。观众也可以调用 ChatroomHelper.getInstance.sendGiftMsg 方法发送礼物消息。

麦位管理

麦位管理流程图如下所示:

img

麦位相关操作如下表所示:

操作描述
邀请上麦房主调用 ChatroomHttpManager.getInstance.invitationMic 方法邀请观众上麦。观众收到 ChatroomListener.receiveInviteSite 回调,选择是否上麦。
- 观众调用 ChatroomHttpManager.getInstance.agreeMicInvitation 方法同意上麦,成为主播,房间内所有用户会收到 ChatroomListener.roomAttributesDidUpdated,更新房间内麦位信息。
- 观众调用 ChatroomHttpManager.getInstance.rejectMicInvitation 方法拒绝上麦,房主收到 ChatroomListener receiveInviteRefusedSite 回调。
申请上麦观众调用 ChatroomHttpManager.getInstance.submitMic 方法向房主申请上麦。房主收到 ChatroomListener.receiveApplySite 回调,选择同意或拒绝申请。
- 房主调用 ChatroomHttpManager.getInstance.applySubmitMic 方法同意上麦申请,房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新房间内麦位信息。
- 房主调用 ChatroomHttpManager.getInstance.rejectSubmitMic 方法拒绝上麦申请,申请者收到 ChatroomListener receiveDeclineApply 回调。
撤销上麦申请观众向房主申请上麦后,可以调用 ChatroomHttpManager.getInstance.cancelSubmitMic 方法撤销上麦申请。
下麦下麦分为主动和被动两种方式:
- 主动下麦:主播可调用 ChatroomHttpManager.getInstance.leaveMic 方法下麦成为观众。
- 被踢下麦:房主调用 ChatroomHttpManager.getInstance.kickMic 方法对所在麦位主播发起下麦指令。
对于这两种下麦方式,房间内所有用户都会收到 ChatroomListener.roomAttributesDidUpdated 回调,更新房间内麦位信息。
开麦房主和主播调用 ChatroomHttpManager.getInstance.cancelCloseMic 方法开麦,发言时房间内其他用户可听到。房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新麦位状态。
关麦房主和主播调用 ChatroomHttpManager.getInstance.closeMic 方法关麦,关闭自己的声音。房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新麦位状态。
禁麦房主调用 ChatroomHttpManager.getInstance.muteMic 方法禁麦,不允许指定连麦主播发言,该主播的音频流将关闭。包括主播在内的房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新麦位状态。
解禁麦位房主调用 ChatroomHttpManager.getInstance.cancelMuteMic 方法解禁麦位,恢复连麦主播的发言权限,即恢复该麦位主播的音频流。包括主播在内的房间内所有用户会收到 ChatroomListener.roomAttributesDidUpdated 回调,更新麦位状态。
锁麦房主调用 ChatroomHttpManager.getInstance.lockMic 方法锁麦,不允许任何人上该麦位。锁麦时,若该麦位有主播连麦,该主播收到 ChatroomListener.roomAttributesDidUpdated 回调被踢下来成为普通观众。房间内所有用户也会收到该回调,更新房间内麦位信息。
解锁麦位房主调用 ChatroomHttpManager.getInstance.cancelLockMic 方法解锁麦位,使指定麦位恢复空闲状态,观众可申请该麦位上麦。房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新麦位状态。
换麦主播调用 ChatroomHttpManager.getInstance.exChangeMic 方法换麦,即从当前麦位切换到另一个空闲麦位。包括主播在内的房间内所有用户会收到 ChatroomListener roomAttributesDidUpdated 回调,更新房间内麦位信息。

发送单向消息

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发送和接收消息
Agora RTC SDKAgora RTC SDK 4.0.1实现音频通话open in new window

基础 API 参考

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

初始设置

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

App Server 相关

以下表格为 App Server 的基础 API。

房间管理 API 如下表所示:

API实现功能
ChatroomHttpManager.getInstance.getRoomDetailsopen in new window获取指定语聊房的信息。
ChatroomHttpManager.getInstance.createRoomopen in new window创建房间。
ChatroomHttpManager.getInstance.deleteRoomopen in new window根据房间 ID 删除房间。
ChatroomHttpManager.getInstance.updateRoomInfoopen in new window修改语聊房信息。
ChatroomHttpManager.getInstance.getRoomFromServeropen in new window获取房间列表。
ChatroomHttpManager.getInstance.getRoomMembersopen in new window获取房间内的成员。
ChatroomHttpManager.getInstance.joinRoomopen in new window加入房间。
ChatroomHttpManager.getInstance.checkPasswordopen in new window校验密码。
ChatroomHttpManager.getInstance.leaveRoomopen in new window离开房间。
ChatroomHttpManager.getInstance.kickRoomMemberopen in new window踢出房间。

麦位管理 API 如下表所示:

API实现功能
ChatroomHttpManager.getInstance.getMicInfoopen in new window获取麦位信息。
ChatroomHttpManager.getInstance.getApplyMicListopen in new window获取上麦申请列表。
ChatroomHttpManager.getInstance.invitationMicopen in new window邀请上麦。
ChatroomHttpManager.getInstance.agreeMicInvitationopen in new window用户同意上麦邀请。
ChatroomHttpManager.getInstance.rejectMicInvitationopen in new window用户拒绝上麦邀请。
ChatroomHttpManager.getInstance.submitMicopen in new window提交上麦申请。
ChatroomHttpManager.getInstance.cancelSubmitMicopen in new window撤销上麦申请。
ChatroomHttpManager.getInstance.rejectSubmitMicopen in new window拒绝上麦申请。
ChatroomHttpManager.getInstance.applySubmitMicopen in new window同意上麦申请。
ChatroomHttpManager.getInstance.closeMicopen in new window关麦。
ChatroomHttpManager.getInstance.cancelCloseMicopen in new window开麦。
ChatroomHttpManager.getInstance.leaveMicopen in new window主动下麦。
ChatroomHttpManager.getInstance.muteMicopen in new window封禁指定麦位。
ChatroomHttpManager.getInstance.cancelMuteMicopen in new window解禁指定麦位。
ChatroomHttpManager.getInstance.exChangeMicopen in new window换麦。
ChatroomHttpManager.getInstance.kickMicopen in new window踢用户下麦。
ChatroomHttpManager.getInstance.lockMicopen in new window锁定麦位。
ChatroomHttpManager.getInstance.cancelLockMicopen in new window解锁麦位。

礼物榜单 API 如下表所示:

API实现功能
ChatroomHttpManager.getInstance.getGiftListopen in new window获取赠送礼物榜单。
ChatroomHttpManager.getInstance.sendGiftopen in new window赠送礼物。

Agora RTC 相关

API实现功能
initializeAndJoinChannelopen in new window初始化 app 并加入频道。

其他 API,详见声网官网open in new window

Easemob IM SDK 相关

Easemob IM SDK 的 API 如下表所示:

API实现功能
EMClient.getInstance.initopen in new window初始化 IM SDK。
EMClient.getInstance.chatManager.sendMessageopen in new window发送消息。

语聊房监听

初始化 SDK(ChatroomConfigManager.getInstance.initRoomConfig)时已注册了连接监听、消息监听、聊天室监听,可以实现 ChatroomListener 接口。

API实现功能
ChatroomHttpManager.getInstance.receiveTextMessageopen in new window收到普通文本消息。
ChatroomHttpManager.getInstance.receiveGiftopen in new window收到礼物消息。
ChatroomHttpManager.getInstance.receiveApplySiteopen in new window接收申请消息。
ChatroomHttpManager.getInstance.receiveCancelApplySiteopen in new window接收取消申请消息。
ChatroomHttpManager.getInstance.receiveInviteSiteopen in new window接收邀请消息。
ChatroomHttpManager.getInstance.receiveInviteRefusedSiteopen in new window接收拒绝邀请消息。
ChatroomHttpManager.getInstance.receiveDeclineApplyopen in new window接收拒绝申请消息。
ChatroomHttpManager.getInstance.onMemberExitedopen in new window用户离开房间。
ChatroomHttpManager.getInstance.announcementChangedopen in new window聊天室公告更新。
ChatroomHttpManager.getInstance.userBeKickedopen in new window聊天室成员被踢出房间。
ChatroomHttpManager.getInstance.roomAttributesDidUpdatedopen in new window聊天室属性变更。
ChatroomHttpManager.getInstance.roomAttributesDidRemovedopen in new window聊天室属性移除。
ChatroomHttpManager.getInstance.onTokenWillExpireopen in new windowToken 即将过期。
ChatroomHttpManager.getInstance.onTokenExpiredopen in new windowToken 已过期。
ChatroomHttpManager.getInstance.receiveSystemopen in new window收到系统消息(目前用来实现成员加入)。
ChatroomHttpManager.getInstance.voiceRoomUpdateRobotVolumeopen in new window机器人音量更新。
ChatroomHttpManager.getInstance.onRoomDestroyedopen in new window聊天室被销毁。

音频相关

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

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

附加功能

最佳音效

设置最佳音效,示例代码如下:

annotation class SoundSelection {
                    companion object {
                        const val SocialChat = 0        //社交语聊

                        const val Karaoke = 1           //在线 K 歌

                        const val GamingBuddy = 2       //游戏陪玩

                        const val SoundCardHQ = 3       //专业主播
                    }
                }

                 rtcEngine?.apply {
            when (config.soundType) {
                SoundSelection.SocialChat, SoundSelection.Karaoke -> { // 社交语聊,KTV
                    setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING)
                    setAudioProfile(Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY)
                    setAudioScenario(Constants.AUDIO_SCENARIO_GAME_STREAMING)
                }
                SoundSelection.GamingBuddy -> { // 游戏陪玩
                    setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION)
                }
                else -> { //专业主播
                    setAudioProfile(Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY)
                    setAudioScenario(Constants.AUDIO_SCENARIO_GAME_STREAMING)
                    setParameters("{\"che.audio.custom_payload_type\":73}")
                    setParameters("{\"che.audio.custom_bitrate\":128000}")
                    // setRecordingDeviceVolume(128) 4.0.1 上才支持
                    setParameters("{\"che.audio.input_channels\":2}")
                }
            }
        }

AI 降噪

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

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

internal class AgoraRtcDeNoiseEngine : RtcBaseDeNoiseEngine<RtcEngineEx>() {

    override fun closeDeNoise(): Boolean {
        engine?.apply {
            setParameters("{\"che.audio.ains_mode\":0}")
            setParameters("{\"che.audio.nsng.lowerBound\":80}")
            setParameters("{\"che.audio.nsng.lowerMask\":50}")
            setParameters("{\"che.audio.nsng.statisticalbound\":5}")
            setParameters("{\"che.audio.nsng.finallowermask\":30}")
            setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
        }
        return true
    }

    override fun openMediumDeNoise(): Boolean {
        engine?.apply {
            setParameters("{\"che.audio.ains_mode\":2}")
            setParameters("{\"che.audio.nsng.lowerBound\":80}")
            setParameters("{\"che.audio.nsng.lowerMask\":50}")
            setParameters("{\"che.audio.nsng.statisticalbound\":5}")
            setParameters("{\"che.audio.nsng.finallowermask\":30}")
            setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
        }
        return true
    }

    override fun openHeightDeNoise(): Boolean {
        engine?.apply {
            setParameters("{\"che.audio.ains_mode\":2}")
            setParameters("{\"che.audio.nsng.lowerBound\":10}")
            setParameters("{\"che.audio.nsng.lowerMask\":10}")
            setParameters("{\"che.audio.nsng.statisticalbound\":0}")
            setParameters("{\"che.audio.nsng.finallowermask\":8}")
            setParameters("{\"che.audio.nsng.enhfactorstastical\":200}")
        }
        return true
    }
}