快速构建社交APP中的语音房

4,183 阅读12分钟

📃 文章须自出机杼,成一家风骨。 —— 魏收

一、需求背景

近年来,在线语音聊天的用户量持续上升。语音可承载的信息密度比文字图片丰富,同时又比视频更简单;不失为一种抓住新时代用户和实现流量变现的有效途径。在陌生人社交领域,为了满足用户的情感需要、娱乐需求等,晓宇公司也在自己的应用中推出了语音房的功能。

二、什么是语音房

  • 什么是社交语音房?

语音房是一款基于一对一的语音电话,语音聊天基础之上的一个多人聊天交流场景;能够同时支撑多人在一个房间的语音互动,这里的语音交互与发语音消息不同,而是类似于多人语音电话,能够实时进行交互沟通。

  • 社交语音房能做什么?

社交中的语音房最核心的目的一定是促进用户之间的交流,快速提升用户间的关系;语音房能够通过多人同时通话的方式;要比文字,语音消息的方式更富有情感,更直接的让用户之间进行情感交流。可以实现:纯语音聊天、游戏互动、情感电台、线上KTV等场景。

三、功能描述

  • 场景化

语音房功能可以比较契合生活中的 KTV 场景,在实际应用中也确实可以实现线上歌房。

  1. KTV 的房间是相互隔绝的(不要钻牛角尖),不同的房间是听不到声音的;语音房也一样,每个房间相互隔离,没有联系。
  2. KTV 的房间中麦克风数量是有限的,语音房中同样提供的上麦数量是有限的。
  3. KTV 的房间中可以容纳的人数是远大于能够拿到麦克风唱歌的人数的;语音房中能够容纳的人数同样是远大于能够上麦说话唱歌的数量。
  4. KTV 的房间中拿着麦克风的时候,可以选择性唱歌;语音房中上麦的用户可以选择不说话,可以进行控制。
  5. KTV 中有限的麦克风是流动性的,不是仅仅某几个人能够唱歌;语音房的上下麦就是对有限的麦位进行流动,麦位数量不变,但是拿着麦的人是可以变化的。
  • 功能描述

    • 创建语音房

首先需要玩语音房,我们需要创建一个房间,创建房间需要设置房间的一些基本信息,如下图所示:

创建房间的时候可以自定义房间的名称,或者根据系统提供的名称进行随机获取。

  • 创建语音房流程图

在进行房间信息填写完成之后,还需要最后一步就可以创建成功了;点击创建,客户端请求服务端接口房间创建完成。服务端内部主要流程如下:

初始化房间流程中,服务端只是进行存储用户设置的数据及初始化房间麦位信息;以及生成对应房间的靓号及对应的 rtc 频道 id 返回给客户端,客户端进行创建频道信息等操作。

  • 房间基础信息

-- ----------------------------

-- Table structure for room

-- ----------------------------

DROP TABLE IF EXISTS `room`;

CREATE TABLE `room` (

  `room_id` bigint(20) NOT NULL,

  `app_id` bigint(20) NOT NULL,

  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',

  `administrators` varchar(1000) DEFAULT NULL COMMENT '房间管理员信息',

  `last_close_time` bigint(20) DEFAULT NULL COMMENT 'unix时间戳, 上次打烊时间',

  `last_open_time` bigint(20) DEFAULT NULL COMMENT 'unix时间戳, 上次营业时间',

  `mic_mode` varchar(255) NOT NULL COMMENT '上麦模式 (自由上麦/申请上麦模式)',

  `roomIcon` varchar(255) DEFAULT NULL COMMENT '房间外部展示图片',

  `room_desc` varchar(1000) DEFAULT NULL COMMENT '房间公告',

  `room_layout` int(11) DEFAULT 8 COMMENT '房间麦位数量',

  `room_name` varchar(255) DEFAULT NULL COMMENT '房间名字',

  `room_number` int(11) NOT NULL COMMENT '房间号(过滤靓号)',

  `room_source` varchar(255) NOT NULL COMMENT '房间创建的渠道, 也可以认为是房间的归属主体',

  `room_type` varchar(255) NOT NULL COMMENT '文字房: ROOM_TYPE_TEXT, 语音房: ROOM_TYPE_VOICE',

  `room_status` varchar(255) NOT NULL COMMENT '开启: ROOM_STATUS_OPEN, 关闭: ROOM_STATUS_CLOSE, 被禁: ROOM_STATUS_BANNED',

  `room_tag` varchar(255) DEFAULT NULL COMMENT '房间标识',

  `room_owner_user_id` bigint(20) DEFAULT NULL COMMENT '房主id',

  `room_owner_organization_id` bigint(20) DEFAULT NULL COMMENT '组织id',

  `channel_id` bigint(20) DEFAULT NULL COMMENT '房间对应第三方的唯一频道信息',

  PRIMARY KEY (`room_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;

  • 表中将房间的基本信息及管理员也都放在该表中,导致表的字段较多,以及修改管理员的时候也会更改到房间的基础信息,其实可以将管理员相关信息拆出去。
  1. mic_mode:控制房间用户的上麦模式,例如:申请上麦模式,需要由用户进行申请上麦,管理员/房主同意。
  2. room_layout:用于控制房间能够进行连麦的数量,能够在指定范围内修改房间麦位的数量。
  3. room_source:房间创建的渠道,每个用户可以在不同的渠道创建一个房间;这样子的好处是可以在一个应用支持不同的场景使用聊天室。
  4. room_number:房间编号,过滤掉靓号,可以后续进行靓号的运营。
  5. room_owner_organization_id:创建渠道(room_source)如果是组织类型的,需要存储用户对应的组织id。
  6. room_game:在房间基础之上可以增加新的游戏玩法,例如:拍卖,婚礼等。
  • 创建语音房接口

api: /api/room/room-message  //优先根据房间id获取房间信息 (无房间信息,返回默认参数值)

 /* 传入参数

 (根据调用者id以及房间类型获取是否创建过房间以及房间信息,如果没有创建过该类型房间返回默认参数(图片为创建者头像、房间展示类型为普通房、标签默认为相亲交友房) )

  房间没有头像默认使用房主在应用中头像

  默认展示方式位普通房 

  默认房间类型为相亲交友 */

Long roomId



api: /api/room/operation-room   //操作房间(创建/编辑)

/* 传入参数 

房间id(不传为创建,传参为打开) */

Long roomId

/* (RoomTag 房间标签 ROOM_TAG_FRIEND 相亲交友 ROOM_TAG_TALK 闲聊唠嗑 ROOM_TAG_AUCTION 心动拍卖)*/

String tag

/* 房间名称 */

String name

/* 房间公告 */

String announcement

/* 房间背景图 */

String coverUrl

/* 排麦模式  MIC_MODE_FREEDOM 自由麦    MIC_MODE_QUEUE 排麦 */

String mode

/* 房间展示类型  SECRET_ROOM 私密房    GENERAL_ROOM 普通房间 */

String subType

/* 卡座位置数量 默认8位 范围[4,12] */

Integer boothSize
  • 上麦

首先,如下图所示,上麦的模式分为自由模式上麦,以及排队模式上麦;自由模式上麦顾名思义就是只要房间的麦位上没有用户占有,点击之后即可上麦;排队模式上麦则是针对普通用户(管理员/房主除外),无法直接上麦,点击上麦按钮之后会进入到排麦队列中,等待管理员/房主的同意(拍麦队列中只有管理员/房主才有同意的权限)才能够按照上麦的规则进行上麦。

其次,上麦类型分为主动上麦和被动上麦,该概念是从开发角度来区分出来的;用户主动点击上麦操作称为主动上麦;用户被管理员/房主邀请上麦,再由用户点击确认上麦动作为被动上麦。被动上麦的图如下所示:

  • 上麦流程图

上麦流程中需要针对用户的权限进行校验,及根据当前房间的模式及业务方选择的策略方式(根据角色位置上麦/根据位置倒序上麦等)进行上麦或者排队操作;最后需要发送房间麦位上的数据发送 rtm到客户端进行渲染。

  • 麦位信息

-- ----------------------------

-- Table structure for room_booth

-- ----------------------------

DROP TABLE IF EXISTS `room_booth`;

CREATE TABLE `room_booth` (

  `room_booth_id` bigint(20) NOT NULL,

  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',

  `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',

  `booth_abilities` varchar(255) NOT NULL COMMENT '主持麦位: BOOTH_ABILITIES_HOST, 普通麦位: BOOTH_ABILITIES_NORMAL',

  `booth_index` int(11) NOT NULL COMMENT '卡座位置',

  `room_id` bigint(20) NOT NULL,

  `user_id` bigint(20) DEFAULT NULL COMMENT '当前在卡座的用户',

  `booth_status` varchar(50) DEFAULT NULL COMMENT '卡座声音状态',

  `status` varchar(50) DEFAULT NULL COMMENT '数据状态',

  PRIMARY KEY (`room_booth_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;

麦位的信息是以房间维度,不是用户的维度,业务方调用初始化房间的时候,房间初始化多少个麦为数据一般由业务方来控制。

  1. booth_index:当前位置的下标位置。
  2. booth_abilities:当前麦位的类型,由业务方来控制麦位的数据以及对应麦位的类型;与上麦的策略相关。
  • 上麦接口

api: /api/room/set-seat //上麦/排麦(上麦相关的都调用该接口,根据当前房间上麦模式,进行对应逻辑操作)

/* 传入参数 

 语音房id */

Long roomId

/* 所上的麦位 如果不传 默认按照倒叙上麦,序号从0开始,0表示第一个位置 */

Integer seatNum



api: /api/room/invite-set-seat   将用户抱上麦(该接口由房主/管理员调用用户邀请用户上麦,被邀请的用户将弹出是否同意上麦的弹框)

/* 传入参数 

  被抱上麦用户id*/

Long userId

/* 语音房id */

Long roomId
  • 相关概念

  • 语音房

允许单人/多人在一个房间频道内进行语音聊天或者进行一些娱乐活动的语音交流玩法。

  • RTM 消息

通过类似于房间内打字互动的方式,服务端与客户端采用一种约定好的格式,实现对应指令的互通,从而达到客户端根据具体指令实现对应的动作。

  • RTC

可以理解为一个语音房对应一个 RTC 频道,在语音房内需要听到声音、发出声音,首先都需要加入到该频道中;类似于双方互相打电话

  • 上/下麦

根据语音房内提供的麦位数量进行加入聊天、退出聊天;没有上麦的用户无法将声音传输入该频道内,使得语音房内其他用户听见。

  • 开/闭麦

上麦后的用户提供了开启/关闭自己的麦克风,选择是否将声音传入频道内。

  • 主动行为/被动行为

主动行为可以理解为,触发某个动作,用户自己进行处罚即可完成;例如:自由上下麦模式下的上麦/下麦、开麦/闭麦、加入/离开频道等。

被动行为可以理解为,该动作用户无法自己完成,需要通知到相应用户,由另一用户自己去触发才可以完成的动作;例如:抱上麦/抱下麦、禁麦/解除禁麦等。

四、技术方案

  • 目的

本次建设语音房功能并非只是简单的实现该能力,而是需要将语音房功能做成一个新的中台模块,便于后续新产品的快速且服务端 0 工作量的接入,从而降低新产品功能上线的成本及提高功能接入的效率。

  • 设计

    • 用户角色

  1. 房主:该语音房的创建者,即房间的拥有者,拥有最高的权限;房间跟用户绑定,一个用户在一种类型下只能创建一个直播间。
  2. 管理员:房间的管理员由房主来控制,一个房间可以有多个管理员,一个用户也可以是多个房间的管理员。
  3. 麦上用户:加入了 rtc 频道,并且在房间内能够说话交流的用户。
  4. 观众:加入了 rtc 频道,只能听到房间的声音但是不能说话的用户。
  • 交互

    • 交互流程图

架构流程客户端需要先调用到业务方,由业务方再调用到语音房服务,语音房服务返回相关的结果以及发送指定模版化的 rtm 通知给客户端;图中涉及到送礼相关功能由金币送礼相关的服务处理。

  • 问题及方案

    • 幽灵麦问题

    原因:由于整套逻辑涉及到,客户端、服务端、声网三方,会涉及到服务端的数据与声网的数据不同步问题;幽灵麦问题出现的几个原因主要有:

    1. 服务端将用户a的上麦操作更新成功,但是客户端调用声网的 SDK 操作中出现了问题,导致该麦上有用户a头像,但其实是一个无法说话的麦位;
    2. 麦上的用户a正常上麦了,将 app 进程直接杀掉,通过声网的回调,服务端由于服务异常或者其他原因导致没有收到该回调信息,导致用户a在麦上,但是实际已经离开了app,是一个幽灵麦。

    方案:整体下来,由于麦位上有假用户数据;当房间管理员或者房主发现用户为幽灵麦位的时候,会进行抱下麦,抱下麦会有频率相关的策略,服务端根据策略与声网校验用户与当前频道的关系,不在当前频道进行清理。

五、总结

目前市面上的各种社交软件有非常多的都具有多人语音聊天的功能,体现出了该功能在陌生人社交领域是比较能够让陌生人之间进行更深体验交流并且快速升温的功能;在完成该功能所具有的基本能力之上,可以继续提升丰富语音房的玩法,可以是游戏,可以是各种具有流程化的互动;相信在未来的一段时间之内,多人语音房间的玩法会得到更多用户的认可。后续,我们也会不断推出语音房的玩法,来丰富用户的体验。