直播在生活中可谓是无处不在,越来越多的企业、开发者们正在搭建自己的直播平台。由于直播平台本身所涉及到的,例如推拉直播流、直播转码、直播截图、直播混流、直播间聊天室、直播间互动(点赞、送礼、连麦)、直播间状态管理等需求往往比较复杂,本文章以腾讯云相关产品(腾讯云即时通信 IM、腾讯云直播 CSS )为基础,梳理了在搭建直播间过程中常见的需求的实现方案,以及可能遇到的问题、需要注意的细节点等,希望能帮助开发者们快速的理解业务、实现需求。
您也可以通过我们的 Demo 快速在线体验:
一、准备工作
创建应用
添加直播推流及播放域名
完成相关基本配置
在直播场景中,除了创建应用之外,还需要一些额外的配置:
使用密钥计算 UserSig
在 IM 的账号体系中,用户登录需要的密码由用户服务端使用 IM 提供的密钥计算,用户可参见 UserSig计算 文档,在开发阶段,为了不阻塞客户端开发,也可在 控制台计算 UserSig ,如下图所示:
配置管理员账号
在直播过程中,可能需要管理员向直播间发送消息、禁言(踢出)违规用户等,这时就需要使用 即时通信 IM 服务端 API 来进行相应的处理,调用服务端api前需要 创建 IM 管理员账号,IM 默认提供一个 UserID 为 administrator 的账号供开发者使用,开发者也可以根据业务的场景,创建多个管理员账号。需要注意的是,IM 最多创建五个管理员账号。
配置回调地址以及开通回调
在实现直播间弹幕抽奖、消息统计等需求时,需要用到 IM 的回调模块,即 IM 后台在某些特定的场景回调开发者业务后台。开发者只需要提供一个 HTTP 的接口并且配置在 控制台 > 回调配置 模块即可,如下图所示:
集成客户端 SDK
在准备工作都完成好后,需要将即时通信 IM 以及云直播 CSS 的客户端 SDK 集成到用户项目中去。开发者可以根据自己业务需要,选择不同的集成方案。
即时通信 IM 可参见 IM 快速集成系列文档,
云直播 CSS 可参见 直播 SDK 快速集成系列文档。
接下来文章梳理了直播间中常见的功能点,提供实践方案供开发者参见,并附上相关实现代码。
二、直播间各功能开发指引
直播间状态
直播间的状态一般分为如下几种:
序号 | 直播间的状态 |
1 | 直播待开始 |
2 | 直播中 |
3 | 直播暂停 |
4 | 直播结束 |
5 | 直播回放中 |
直播间状态有以下特点:
序号 | 特点 |
1 | 直播间状态的更改需要实时通知直播间用户 |
2 | 新进入直播间用户需要获取当前直播间状态 |
综上,这里提供两种实现方案。并分析两种方案的优缺点。
两种实现方案 | 优点 | 缺点 |
需要频繁多次获取直播间状态时,相比于将直播间状态存 IM 群资料,直播间状态存业务后台可以减少 IM SDK 的调用频率。提供在未集成 IM SDK 地方提供获取直播间状态的可能。 | 需要业务后台额外提供读写直播间状态模块。增加获取直播间数据异常概率,直播间数据来自业务后台和 IM 群资料两部分。发送自定义消息有丢失的可能,当消息量特别大时,低优先级消息会优先被丢弃从而影响直播间状态的显示。因此这里建议使用高优先自定义消息。 | |
开发者不需要提供额外的读写直播间状态模块。群属性变更回调理论上不存在丢失可能。直播间数据都从群资料中获取,统一数据源,减少异常。 | 在高曝光模块需要频繁获取群资料,增大 IM 压力。在未集成 IM SDK 模块,需要通过业务后台调用 IM 服务端 SDK 获取群资料。且调用频率有限制。 |
通过上面的分析,我们建议使用方案一与方案二相结合的方式,维护直播间状态:
1. 在直播间内,使用 方案一获取直播间状态。
2. 在直播间外、使用方案二获取直播间状态。
群类型选择
在直播这个场景,用户聊天区域有以下特点:
1. 用户进出群频繁,且不需要管理该群会话信息(未读、lastMessage等)。
2. 用户自动进群不需要审核。
3. 用户临时发言,不关注群历史聊天记录。
4. 群人数通常比较多。
5. 可以不用存储群成员信息。
即时通信 IM 的直播群(AVChatRoom)有以下特点:
无人数限制,可实现千万级的互动直播场景。
支持向全体在线用户推送消息(群系统通知)。
申请加群后,无需管理员审批,直接加入。
说明
IM Web &小程序 SDK 限制同一用户在同一时间内,只能进入一个 AVChatRoom,在 IM 的多端登录场景,如果用户登录终端一在直播间 A 观看直播,在 控制台配置 允许多端登录情况下,该用户登录终端二进入直播间 B 观看,这时终端一的直播间 A 会被退群。
直播间公告
直播间公告(主题)是每个直播间必备的内容,用户进入直播间可以看到该直播间的基本信息。同时,直播间公告也需要更改后实时通知给直播群中的群成员。与群直播状态类似,可以将直播状态存储在客户业务后台或者 IM 的群资料。区别于直播间状态,直播间公告有一些额外的事项,需要开发者关注。如下:
1. 群资料的 notification 以及 introduction 字段存储长度有限。客户下发不了比较长的通知(群简介最长240字节、群公告最长300字节)。
2. 群资料的 notification 以及 introduction 字段目前仅仅支持 string 类型。如果用户希望图文混排的公告,可通过 json string 形式设置,且图片为可访问的线上图片 url。
3. 如在 Web 端通过 editor工具设置的公告,如包含 html 标签在 native 端可能解析不了。
直播间公告可通过 服务端 API 或者客户端 SDK 设置群属性来设置。客户端通过获取群属性来获取当前群信息,可通过监听 GroupListener 中的 OnGroupInfoChange 来获取更改的群属性。
相关代码示例:
// 客户端获取群资料V2TIMManager.getGroupManager().getGroupsInfo(groupIDList, new V2TIMValueCallback<List<V2TIMGroupInfoResult>>() {@Overridepublic void onSuccess(List<V2TIMGroupInfoResult> v2TIMGroupInfoResults) {// 获取群资料成功}@Overridepublic void onError(int code, String desc) {// 获取群资料失败}});// 客户端修改群资料V2TIMGroupInfo v2TIMGroupInfo = new V2TIMGroupInfo();v2TIMGroupInfo.setGroupID("需要修改的群 ID");v2TIMGroupInfo.setFaceUrl("http://xxxx");V2TIMManager.getGroupManager().setGroupInfo(v2TIMGroupInfo, new V2TIMCallback() {@Overridepublic void onSuccess() {// 修改群资料成功}@Overridepublic void onError(int code, String desc) {// 修改群资料失败}});
[[V2TIMManager sharedInstance] getGroupsInfo:@[@"groupA"] succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {// 获取群资料成功} fail:^(int code, NSString *desc) {// 获取群资料失败}];V2TIMGroupInfo *info = [[V2TIMGroupInfo alloc] init];info.groupID = @"需要修改的群 ID";info.faceURL = @"http://xxxx";[[V2TIMManager sharedInstance] setGroupInfo:info succ:^{// 修改群资料成功} fail:^(int code, NSString *desc) {// 修改群资料失败}];
// 获取群资料V2TimValueCallback<List<V2TimGroupInfoResult>> groupinfos = await groupManager.getGroupsInfo(groupIDList: ['groupid1']);// 修改群资料groupManager.setGroupInfo(info: V2TimGroupInfo.fromJson({"groupAddOpt":GroupAddOptTypeEnum.V2TIM_GROUP_ADD_AUTH// ...其他资料}));// 回调TencentImSDKPlugin.v2TIMManager.addGroupListener(listener: V2TimGroupListener(onGroupInfoChanged: ((groupID, changeInfos) {// 群信息更改回调})));
// 获取群资料let promise = tim.getGroupProfile({ groupID: 'group1', groupCustomFieldFilter: ['key1','key2'] });promise.then(function(imResponse) {console.log(imResponse.data.group);}).catch(function(imError) {console.warn('getGroupProfile error:', imError); // 获取群详细资料失败的相关信息});// 修改群资料let promise = tim.updateGroupProfile({groupID: 'group1',name: 'new name', // 修改群名称introduction: 'this is introduction.', // 修改群简介// v2.6.0 起,群成员能收到群自定义字段变更的群提示消息,且能获取到相关的内容,详见 Message.payload.newGroupProfile.groupCustomFieldgroupCustomField: [{ key: 'group_level', value: 'high'}] // 修改群组维度自定义字段});promise.then(function(imResponse) {console.log(imResponse.data.group) // 修改成功后的群组详细资料}).catch(function(imError) {console.warn('updateGroupProfile error:', imError); // 修改群组资料失败的相关信息});// v2.6.2 起,提供了全体禁言和取消禁言的功能。目前群全体禁言后,不支持下发群提示消息。let promise = tim.updateGroupProfile({groupID: 'group1',muteAllMembers: true, // true 表示全体禁言,false表示取消全体禁言});promise.then(function(imResponse) {console.log(imResponse.data.group) // 修改成功后的群组详细资料}).catch(function(imError) {console.warn('updateGroupProfile error:', imError); // 修改群组资料失败的相关信息});
消息优先级
直播间的特色之一是用户的消息量非常大,每个用户可能都会频繁的刷消息。当上行消息达到 IM 的频控阈值时,IM 后台会丢弃部分消息,来保证系统的稳定运行。其实当消息达到客户端的频率太高,消息的可读性也降低了,所以对直播群消息进行频控是非常有必要的。
群消息分为 3 个优先级,后台会优先下发高优先级的消息。因此用户应根据消息的重要程度,来选择合适的优先级。
3个优先级从高到低,分别如下:
优先级 | 含义 |
High | 高优先级 |
Normal | 正常优先级 |
Low | 低优先级 |
消息丢弃策略如下:
总消息数频控是指单个群每秒最多能发送的消息数限制,默认值为 40条/秒,采用每秒平均限频。消息数量超过限制后,后台优先下发优先级相对较高的消息,同等优先级的消息随机排序。
综上:制定直播间消息优先级规则是很有必要的。
对于直播间消息,我们按对于用户的重要性来进行优先级的划分从高到低依次是:
1. 用户的打赏、送礼消息,这类消息用户期望直播间的所有用户都能看到。
2. 正常的文字、语音、图片等消息。
3. 点赞消息。
说明:
通常来说,C 端用户自己发送的消息,不论消息优先级的高低,都是一定要上屏的。在消息量特别大时,用户能够接受丢失少部分其他用户的消息。
设置消息优先级代码示例:
// 创建自定义消息V2TIMMessage v2TIMMessage = V2TIMManager.getMessageManager().createCustomMessage("单聊自定义消息".getBytes());// 设置消息优先级为高优先级消息v2TIMMessage.setPriority(V2TIMMessage.V2TIM_PRIORITY_HIGH)// 发送消息V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, "receiver_userID", null, V2TIMMessage.V2TIM_PRIORITY_NORMAL, false, null, new V2TIMSendCallback<V2TIMMessage>() {@Overridepublic void onProgress(int progress) {// 自定义消息不会回调进度}@Overridepublic void onSuccess(V2TIMMessage message) {// 发送群聊自定义消息成功}@Overridepublic void onError(int code, String desc) {// 发送群聊自定义消息失败}});
/ 创建文本消息V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:@"content"];// 发送消息[V2TIMManager.sharedInstance sendMessage:messagereceiver:@"userID"groupID:nilpriority:V2TIM_PRIORITY_NORMALonlineUserOnly:NOofflinePushInfo:nilprogress:nilsucc:^{// 文本消息发送成功}fail:^(int code, NSString *desc) {// 文本消息发送失败}];
/ 创建文本消息V2TimValueCallback<V2TimMsgCreateInfoResult> createTextAtMessageRes = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createTextAtMessage(text: "test",atUserList: [],);if(createTextAtMessageRes.code == 0){String id = createTextAtMessageRes.data.id;// 发送文本消息V2TimValueCallback<V2TimMessage> sendMessageRes = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage(id: id, receiver: "", groupID: "groupID");if(sendMessageRes.code == 0){// 发送成功}}
// 发送文本消息,Web 端与小程序端相同// 1. 创建消息实例,接口返回的实例可以上屏let message = tim.createTextMessage({to: 'user1',conversationType: TIM.TYPES.CONV_C2C,// 消息优先级,用于群聊(v2.4.2起支持)。如果某个群的消息超过了频率限制,后台会优先下发高优先级的消息,详细请参见:https://cloud.tencent.com/document/product/269/3663#.E6.B6.88.E6.81.AF.E4.BC.98.E5.85.88.E7.BA.A7.E4.B8.8E.E9.A2.91.E7.8E.87.E6.8E.A7.E5.88.B6)// 支持的枚举值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默认), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST// priority: TIM.TYPES.MSG_PRIORITY_NORMAL,payload: {text: 'Hello world!'},// v2.20.0起支持C2C消息已读回执功能,如果您发消息需要已读回执,需购买旗舰版套餐,并且创建消息时将 needReadReceipt 设置为 trueneedReadReceipt: true// 消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到,v2.10.2起支持)// cloudCustomData: 'your cloud custom data'});// 2. 发送消息let promise = tim.sendMessage(message);promise.then(function(imResponse) {// 发送成功console.log(imResponse);}).catch(function(imError) {// 发送失败console.warn('sendMessage error:', imError);});
礼物与点赞消息实践教程
礼物消息
1. 客户端短连接请求到自己的业务服务器,涉及到计费逻辑。
2. 计费后,发送人直接看到 XXX 送了 XXX 礼物。(以确保发送人自己看到自己发的礼物,消息量大的时候,可能会触发抛弃策略)。
3. 计费结算后,调用服务端接口发送发送自定义消息(礼物)。
4. 如果遇到连刷礼物的场景需要进行消息合并。
4.1 如果直接选择礼物数量的刷礼物,例如:直接选择 99 个礼物,1 条消息发送,参数带入礼物数量 99。
4.2 如果连击的礼物,不确定停留在多少个,可以合并每 20 个(数量自己调整)或者连击超过 1 秒,发送一个。按照上述逻辑,例如:连击 99 个礼物,优化后仅需要发送 5 条。
点赞消息
1. 点赞与礼物略不同,点赞往往不用计费,所以一般采用端上直接发送的方式。
2. 针对与有需要服务端计数的点赞消息,在客户端节流之后,统计客户端点赞次数,将短时间内多次点赞消息合并之后,仅发送一次消息即可。业务方服务端在发消息前回调中拿到点赞次数进行统计。
3. 针对与不需要计数的点赞消息,与步骤2中的逻辑一致,业务要在客户端对点赞消息节流后发送,不需要在发消息前回调中计数。
直播间用户身份、等级
大多直播间都会有用户等级的概念,开发者可能会根据以下几点按照一定的权重来计算用户的等级:
1. 直播间用户的在线时长。
2. 直播间用户成功发送普通直播间消息的数量。
3. 直播间用户点赞、送礼的数量。
4. 直播间用户是否开通直播间会员。
5. ...
统计相关信息的流程图如图:
消息发送后回调数据示例:
{"CallbackCommand": "Group.CallbackAfterSendMsg", // 回调命令"GroupId": "@TGS#2J4SZEAEL", // 群组 ID"Type": "Public", // 群组类型"From_Account": "jared", // 发送者"Operator_Account":"admin", // 请求的发起者"Random": 123456, // 随机数"MsgSeq": 123, // 消息的序列号"MsgTime": 1490686222, // 消息的时间"OnlineOnlyFlag": 1, //在线消息,为1,否则为0;直播群忽略此属性,为默认值0。"MsgBody": [ // 消息体,参见 TIMMessage 消息对象{"MsgType": "TIMTextElem", // 文本"MsgContent": {"Text": "red packet"}}],"CloudCustomData": "your cloud custom data"}
用户在线状态改变回调数据示例:
{"CallbackCommand": "State.StateChange","EventTime": 1629883332497,"Info": {"Action": "Login","To_Account": "testuser316","Reason": "Register"},"KickedDevice": [{"Platform": "Windows"},{"Platform": "Android"}]}
说明:
此外,不少直播间都会展示直播间热度,并且根据直播间热度,将直播间推荐给用户观看,直播间热度的统计方式和用户等级统计方式类似,也可以通过 IM 提供的回调系统来进行实现,这里就不在做过多的讲解。
直播间历史消息
使用 AVChatRoom 默认不存储直播间历史消息,当新用户进入直播间后,只能看到进入直播间后用户发送的消息。为了优化新进群用户的体验,可在控制台配置直播群用户拉取进群前消息条数,如图:
说明:
此功能仅旗舰版用户才可开通,且仅支持拉群24小时内最多20条历史消息。
注意:直播群进群前历史消息通过信息消息回调返回。
// 设置事件监听器V2TIMManager.getMessageManager().addAdvancedMsgListener(v2TIMAdvancedMsgListener);// 接收消息public void onRecvNewMessage(V2TIMMessage msg) {// 解析出 groupID 和 userIDString groupID = msg.getGroupID();String userID = msg.getUserID();// 判断当前是单聊还是群聊:// 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊// 解析出 msg 中的自定义消息if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) {V2TIMCustomElem customElem = msg.getCustomElem();String data = new String(customElem.getData());Log.i("onRecvNewMessage", "customData:" + data);}}
// 设置事件监听器[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];/// 接收消息/// @param msg 消息对象- (void)onRecvNewMessage:(V2TIMMessage *)msg {// 解析出 groupID 和 userIDNSString *groupID = msg.groupID;NSString *userID = msg.userID;// 判断当前是单聊还是群聊:// 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊// 解析出 msg 中的自定义消息if (msg.elemType == V2TIM_ELEM_TYPE_CUSTOM) {V2TIMCustomElem *customElem = msg.customElem;NSData *customData = customElem.data;NSLog(@"onRecvNewMessage, customData: %@", customData);}}
// 拉取单聊历史消息// 首次拉取,lastMsgID 设置为 null// 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的idTencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMessageListener(listener: V2TimAdvancedMessageListener(onRecvedNewMessage:(msg){}),);
let onMessageReceived = function(event) {// event.data - 存储 Message 对象的数组 - [Message]const messageList = event.data;messageList.forEach((message) => {if (message.type === TencentCloudChat.TYPES.MSG_TEXT) {// 文本消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.TextPayload} else if (message.type === TencentCloudChat.TYPES.MSG_IMAGE) {// 图片消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.ImagePayload} else if (message.type === TencentCloudChat.TYPES.MSG_SOUND) {// 音频消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.AudioPayload} else if (message.type === TencentCloudChat.TYPES.MSG_VIDEO) {// 视频消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.VideoPayload} else if (message.type === TencentCloudChat.TYPES.MSG_FILE) {// 文件消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.FilePayload} else if (message.type === TencentCloudChat.TYPES.MSG_CUSTOM) {// 自定义消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.CustomPayload} else if (message.type === TencentCloudChat.TYPES.MSG_MERGER) {// 合并消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.MergerPayload} else if (message.type === TencentCloudChat.TYPES.MSG_LOCATION) {// 地理位置消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.LocationPayload} else if (message.type === TencentCloudChat.TYPES.MSG_GRP_TIP) {// 群提示消息 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.GroupTipPayload} else if (message.type === TencentCloudChat.TYPES.MSG_GRP_SYS_NOTICE) {// 群系统通知 - https://web.sdk.qcloud.com/im/doc/v3/zh-cn/Message.html#.GroupSystemNoticePayloadconst { operationType, userDefinedField } = message.payload;// operationType - 操作类型// userDefinedField - 用户自定义字段(对应 operationType 为 255)// 使用 RestAPI 在群组中发送系统通知时,接入侧可从 userDefinedField 拿到自定义通知的内容。}});};chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, onMessageReceived)
直播间在线人数
直播间实时展示在线人数在直播场景也是一个十分常见的需求,实现方案分为两种,但两种也是各有优劣。
1. 通过客户端SDK提供的 getGroupOnlineMemberCount API 定时轮训的方式拉取群在线人数。
通过客户端 SDK 提供的接口拉取在线人数的方式获取在线人数对于单直播间模式的用户来说基本可以满足用户需求。但对于 App 有多个直播间且需要在大量曝光位置展示直播间在线人数的需求,建议使用第二种方案来统计在线人数。
说明:
开发者服务端在向客户端发送在线人数统计消息时,可采用定时发送的方式,例如每五秒发一次。但这种方式,在直播间人数变化不大时有额外的网络开销。建议开发者按照群人数变化率监测的方式来进行更新。
直播间在线人数的准确性与实时性的优先级,开发者可以根据自身的业务来设置。
获取直播间在线人数代码如下:
2TIMManager.getGroupManager().getGroupOnlineMemberCount("group_avchatroom", new V2TIMValueCallback<Integer>() {@Overridepublic void onSuccess(Integer integer) {// 获取直播群在线人数成功}@Overridepublic void onError(int code, String desc) {// 获取直播群在线人数失败}});
[[V2TIMManager sharedInstance] getGroupOnlineMemberCount:@"group_avchatroom" succ:^(NSInteger count) {// 获取直播群在线人数成功} fail:^(int code, NSString *desc) {// 获取直播群在线人数失败}];
groupManager.getGroupOnlineMemberCount(groupID: '');
// v2.8.0 起,支持查询直播群在线人数let promise = tim.getGroupOnlineMemberCount('group1');promise.then(function(imResponse) {console.log(imResponse.data.memberCount);}).catch(function(imError) {console.warn('getGroupOnlineMemberCount error:', imError); // 获取直播群在线人数失败的相关信息});
直播间禁言
直播间禁言分为两种,一种是整个直播间禁言,另一种是针对某个用户禁言。两种禁言都有其使用的业务场景。
当直播间管理者在后台设置群禁言后,客户端在收到对应的回调事件后,应该把用户的输入框设置为 disable 状态。以免用户在发消息时提示发送消息失败。在解除禁言后也应该将输入框设置为 enable 状态。
客户端对应的回调代码如下:
// 禁言群成员 userB 1分钟V2TIMManager.getGroupManager().muteGroupMember("groupA", "userB", 60, new V2TIMCallback() {@Overridepublic void onSuccess() {// 禁言群成员成功}@Overridepublic void onError(int code, String desc) {// 禁言群成员失败}});// 全员禁言V2TIMGroupInfo info = new V2TIMGroupInfo();info.setGroupID("groupA");info.setAllMuted(true);V2TIMManager.getGroupManager().setGroupInfo(info, new V2TIMCallback() {@Overridepublic void onSuccess() {// 全员禁言成功}@Overridepublic void onError(int code, String desc) {// 全员禁言失败}});V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {@Overridepublic void onMemberInfoChanged(String groupID, List<V2TIMGroupMemberChangeInfo> v2TIMGroupMemberChangeInfoList) {// 禁言群成员监听for (V2TIMGroupMemberChangeInfo memberChangeInfo : v2TIMGroupMemberChangeInfoList) {// 被禁言用户 IDString userID = memberChangeInfo.getUserID();// 禁言时间long muteTime = memberChangeInfo.getMuteTime();}}@Overridepublic void onGroupInfoChanged(String groupID, List<V2TIMGroupChangeInfo> changeInfos) {// 全员禁言监听for (V2TIMGroupChangeInfo groupChangeInfo : changeInfos) {if (groupChangeInfo.getType() == V2TIMGroupChangeInfo.V2TIM_GROUP_INFO_CHANGE_TYPE_SHUT_UP_ALL) {// 是否全员禁言boolean isMuteAll = groupChangeInfo.getBoolValue();}}}});
// 禁言群成员 user1 1分钟[[V2TIMManager sharedInstance] muteGroupMember:@"groupA" member:@"user1" muteTime:60 succ:^{// 禁言群成员成功} fail:^(int code, NSString *desc) {// 禁言群成员失败}];// 全员禁言V2TIMGroupInfo *info = [[V2TIMGroupInfo alloc] init];info.groupID = @"groupA";info.allMuted = YES;[[V2TIMManager sharedInstance] muteGroupMember:@"groupA" member:@"user1" muteTime:60 succ:^{// 全员禁言成功} fail:^(int code, NSString *desc) {// 全员禁言失败}];[[V2TIMManager sharedInstance] addGroupListener:self];- (void)onMemberInfoChanged:(NSString *)groupID changeInfoList:(NSArray <V2TIMGroupMemberChangeInfo *> *)changeInfoList {// 禁言群成员监听for (V2TIMGroupMemberChangeInfo *memberChangeInfo in changeInfoList) {// 被禁言用户 IDNSString *userID = memberChangeInfo.userID;// 禁言时间uint32_t muteTime = memberChangeInfo.muteTime;}}- (void)onGroupInfoChanged:(NSString *)groupID changeInfoList:(NSArray <V2TIMGroupChangeInfo *> *)changeInfoList {// 全员禁言监听for (V2TIMGroupChangeInfo groupChangeInfo in changeInfoList) {if (groupChangeInfo.type == V2TIM_GROUP_INFO_CHANGE_TYPE_SHUT_UP_ALL) {// 是否全员禁言BOOL isMuteAll = groupChangeInfo.boolValue;}}}
// 禁言群成员 userB 10分钟groupManager.muteGroupMember(groupID: '',userID: 'userB',seconds: 10);// 全员禁言groupManager.setGroupInfo(info: V2TimGroupInfo(isAllMuted: true,groupID: '',groupType: 'Public'));TencentImSDKPlugin.v2TIMManager.addGroupListener(listener: V2TimGroupListener(onMemberInfoChanged: (groupID, v2TIMGroupMemberChangeInfoList) {//群成员信息更改},onGroupInfoChanged: (groupID,info){// 群信息修改}));
tim.setGroupMemberMuteTime(options);let promise = tim.setGroupMemberMuteTime({groupID: 'group1',userID: 'user1',muteTime: 600 // 禁言10分钟;设为0,则表示取消禁言});promise.then(function(imResponse) {console.log(imResponse.data.group); // 修改后的群资料console.log(imResponse.data.member); // 修改后的群成员资料}).catch(function(imError) {console.warn('setGroupMemberMuteTime error:', imError); // 禁言失败的相关信息});// 设置群成员在话题中的禁言时间let promise = tim.setGroupMemberMuteTime({groupID: 'topicID',userID: 'user1',muteTime: 600 // 禁言10分钟;设为0,则表示取消禁言});promise.then(function(imResponse) {console.log(imResponse.data.group); // 修改后的群资料console.log(imResponse.data.member); // 修改后的群成员资料}).catch(function(imError) {console.warn('setGroupMemberMuteTime error:', imError); // 禁言失败的相关信息});// v2.6.2 起,提供了全体禁言和取消禁言的功能。目前群全体禁言后,不支持下发群提示消息。let promise = tim.updateGroupProfile({groupID: 'group1',muteAllMembers: true, // true 表示全体禁言,false表示取消全体禁言});promise.then(function(imResponse) {console.log(imResponse.data.group) // 修改成功后的群组详细资料}).catch(function(imError) {console.warn('updateGroupProfile error:', imError); // 修改群组资料失败的相关信息});
直播间封禁
控制台相关配置 如图所示:
注意
在客户端 SDK 6.6.X 及以上版本、Flutter SDK 4.1.1 及以上版本,可以使用直播间踢人接口实现封禁功能。
可参考代码如下:
List<String> userIDList = new ArrayList<>();userIDList.add("userB");V2TIMManager.getGroupManager().kickGroupMember("groupA", userIDList, "", new V2TIMValueCallback<List<V2TIMGroupMemberOperationResult>>() {@Overridepublic void onSuccess(List<V2TIMGroupMemberOperationResult> v2TIMGroupMemberOperationResults) {// 踢人成功}@Overridepublic void onError(int code, String desc) {// 踢人失败}});V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {@Overridepublic void onMemberKicked(String groupID, V2TIMGroupMemberInfo opUser,List<V2TIMGroupMemberInfo> memberList) {// 群成员被踢通知}});
[[V2TIMManager sharedInstance] kickGroupMember:@"groupA" memberList:@[@"user1"] reason:@"" succ:^(NSArray<V2TIMGroupMemberOperationResult *> *resultList) {// 踢人成功} fail:^(int code, NSString *desc) {// 踢人失败}];[[V2TIMManager sharedInstance] addGroupListener:self];- (void)onMemberKicked:(NSString *)groupID opUser:(V2TIMGroupMemberInfo *)opUser memberList:(NSArray<V2TIMGroupMemberInfo *>*)memberList {// 群成员被踢通知}
groupManager.kickGroupMember(groupID: '',memberList: []);
tim.deleteGroupMember(options);
直播间敏感内容过滤
过滤直播间敏感内容也是直播业务非常重要的部分。您可以使用 IM 的内容审核(反垃圾信息)功能,针对不安全、不适宜的内容进行自动识别、处理,为您的产品体验和业务安全保驾护航。目前已提供云端审核服务:
如果您需要对文本、图片、音频、视频内容进行审核,且需要我们为您提供默认审核策略(也可支持自定义配置审核策略),推荐您接入 云端审核功能,在服务端检测由单聊、群聊、资料场景中产生的文本、图片、音频、视频内容,支持针对不同场景的不同内容分别配置审核策略,并对识别出的不安全内容进行拦截。
敏感内容过滤
在直播场景中,用户很可能会发送不合适的内容,特别是与敏感事件/人物相关、黄色不良内容等令人反感的内容,不仅严重损害了用户们的身心健康,更很有可能违法并导致业务被监管部门查封。
即时通信 IM 支持内容审核(反垃圾信息)功能,可针对不安全、不适宜的内容进行自动识别、处理,为您的产品体验和业务安全保驾护航。可以通过云端审核方式来实现:
云端审核功能:在服务端检测由单聊、群聊、资料场景中产生的文本、图片、音频、视频内容,支持针对不同场景的不同内容分别配置审核策略,并对识别出的不安全内容进行拦截。此功能已提供默认预设拦截词库和审核场景,只需在 IM 控制台打开功能开关,即可直接使用。
直播间群成员列表
在需要展示直播间在线群成员列表时,可以通过
getGroupMemberList
接口获取群成员列表,但 AVChatRoom 由于人数众多,不提供拉取全量成员列表的功能。根据旗舰版和非旗舰版区分不同的表现:1. 非旗舰版客户可调用
getGroupMemberList
拉取最近进群的 30 位群成员。2. 旗舰版客户可调用
getGroupMemberList
拉取最近进群的 1000 位群成员。此功能需要在 IM 控制台开启开关,如果不开启,默认跟非旗舰版一样,仅拉取最近进群 30 位群成员。控制台配置如图2.6:
如果是非旗舰版,开发者也可通过
getGroupMemberList
与群监听中的 onGroupMemberEnter 和 onGroupMemberQuit 回调,在客户端维护当前在线群成员列表。但此方案用户退出直播间重新进入后,也只能获取最新的30位群成员。// 通过 filter 参数指定只拉取群主的资料int role = V2TIMGroupMemberFullInfo.V2TIM_GROUP_MEMBER_FILTER_OWNER;V2TIMManager.getGroupManager().getGroupMemberList("testGroup", role, 0,new V2TIMValueCallback<V2TIMGroupMemberInfoResult>() {@Overridepublic void onError(int code, String desc) {// 拉取失败}@Overridepublic void onSuccess(V2TIMGroupMemberInfoResult v2TIMGroupMemberInfoResult) {// 拉取成功}});
[[V2TIMManager sharedInstance] getGroupMemberList:@"groupA" filter:V2TIM_GROUP_MEMBER_FILTER_OWNER nextSeq:0 succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {// 拉取成功} fail:^(int code, NSString *desc) {// 拉取失败}];
// 通过 filter 参数指定只拉取群主的资料 ,可指定ALL拉取全部群成员groupManager.getGroupMemberList(count: 10,filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_ADMIN,nextSeq: '0',offset: 0,groupID: "",);
tim.getGroupMemberList(options);
直播间弹幕抽奖
直播抽奖与消息统计类似,都需要用到发消息后回调,通过检测消息内容,将命中抽奖关键词的用户加入抽奖池。在此不做过多的补充。
直播弹幕
AVChatRoom 支持弹幕、 送礼和点赞等多消息类型,轻松打造良好的直播聊天互动体验。
直播间大喇叭
大喇叭功能相当于一个直播间维度的系统公告功能,但有别于公告,大喇叭功能属于消息。当系统管理员下发大喇叭消息时,SDKAppID 下所有直播间均可收到该大喇叭消息。
大喇叭功能目前属于旗舰版功能,且需要在控制台开通。
业务后台发送大喇叭消息可参见文档:直播群广播消息
说明:
若开发者版本非旗舰版,可在服务端发送群自定义消息进行实现。
三、直播音视频流部分
主播端(推流)
目前腾讯云直播推流有以下几种方式:
1.1 客户端推流可使用 OBS 工具推流
1.2 Web 端可使用 Web 推流
1.3 App 端可使用 直播 SDK 推流
1.4 小程序端可使用 小程序推流
用户端(拉流)
用户端获取直播流的方式也有不同的方式例如:
1.1 PC 端可使用 VLC 工具播放
1.2 Web 端可使用 播放器 SDK 播放
1.3 App 端可使用 播放器 SDK 播放
1.4 小程序端可使用 小程序播放
导播台
1.1 直播水印、文本、转场。
1.2 点播转直播。
1.3 直播画质、码率、分辨率设置。
1.4 直播布局调整。
1.5 直播录制。
1.6 添加直播弹幕。
1.7 直播流监控。
更多高级功能
云直播除了推拉流业务,还有更多高级功能,例如:
1.1 极速高清转码,极速高清转码技术,智能识别场景动态编码,实现直播高清低码和画质提升。
1.2 直播时移,支持直播过程中暂停、回看精彩片段。
1.3 直播录制,可在直播过程中同步录制并存储,便于后续对录制视频的编辑和再传播。
1.4 直播水印,通过直播画面叠加明水印或数字水印实现视频防盗效果。
1.5 云端混流,根据您设定好的混流布局同步的将各路输入源混流成一个新的流,可实现直播互动效果。
1.6 拉流转推,可将其他平台的直播或点播视频通过直播形式分发,提供内容拉取并推送的功能,将已有的直播或视频推送到目标地址上。
四、常见问题
1. 云直播可以测试吗?
新用户开通云直播服务,会免费获得 20GB 的流量套餐包,有效期1年。此时测试产生的流量可以使用该套餐包抵扣,超出套餐部分按照后付费日结计费。同时也欢迎使用直播增值功能,例如:直播水印、转码、录制、截图、鉴黄等,增值功能默认关闭,按需开启使用并计费,详细功能了解可参见 产品概述。
2. 直播的在线人数是否有上限?
腾讯云直播默认不限制观看直播的在线人数,只要网络等条件允许都可以观看直播。如果用户配置了带宽限制,当观看人数过多、超出了限制带宽时新的用户无法观看,此情况下在线人数是有限制的。
3. 自己发消息时,消息发送状态,Message.nick
与 Message.avatar
字段都为空,该怎么处理才能在界面上正常展示昵称和头像?
可通过 getUserInfo 接口,获取自己的用户信息中的 nick 和 avatar 字段作为消息发送发的 nick 和 avatar 字段。
4. 为什么会丢消息?
出现丢消息的可能原因如下:
直播群有40条/秒的频率限制,可通过消息发送前回调与消息发送后回调进行判断,若丢失的消息有收到消息发送前回调,未收到消息发送后回调,则该消息被限频。
可参见 消息相关问题,判断是否因为小程序/Web 端退出时,导致 Android/iOS/PC 同步退出。
如果是小程序/Web 出现问题,请确认您使用的 SDK 版本是否早于V2.7.6,如果是,请升级最新版。
五、联系我们
搜索群号:853084820,我们会提供更加详细的解答。