直播间搭建

直播在生活中可谓是无处不在,越来越多的企业、开发者们正在搭建自己的直播平台。由于直播平台本身所涉及到的如推拉直播流、直播转码、直播截图、直播混流、直播间聊天室、直播间互动(点赞、送礼、连麦)、直播间状态管理等需求往往比较复杂,本文章以腾讯云相关产品(腾讯云即时通信 IM腾讯云直播 CSS )为基础,梳理了在搭建直播间过程中常见的需求的实现方案,以及可能遇到的问题、需要注意的细节点等,希望能帮助开发者们快速的理解业务、实现需求。
您也可以通过我们的 Demo 快速在线体验:





一、准备工作

创建应用

使用腾讯云搭建直播间首先要在 控制台 创建一个即时通信 IM 应用,如下图所示:



添加直播推流及播放域名

搭建直播间,少不了直播功能,直播功能可以通过 云直播 来实现。需要添加推流及播放域名,如下图所示:

操作详情请参见 添加自有域名

完成相关基本配置

准备工作步骤一中创建的应用为开发版,只适合在开发阶段使用,正式环境用户可结合自己业务需求,开通标准版或进阶版。不同版本之间的差异可参见 即时通信 IM 价格文档
在直播场景中,除了创建应用之外,还需要一些额外的配置:

使用密钥计算 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 服务端 API 发送 群自定义消息 通知群内用户。
需要频繁多次获取直播间状态时,相比于将直播间状态存 IM 群资料,直播间状态存业务后台可以减少 IM SDK 的调用频率。提供在未集成 IM SDK 地方提供获取直播间状态的可能。
需要业务后台额外提供读写直播间状态模块。增加获取直播间数据异常概率,直播间数据来自业务后台和 IM 群资料两部分。发送自定义消息有丢失的可能,当消息量特别大时,低优先级消息会优先被丢弃从而影响直播间状态的显示。因此这里建议使用高优先自定义消息。
通过群自定义字段或者群属性存储直播间状态,通过客户端 SDK 群属性更改回调 通知群内用户。
开发者不需要提供额外的读写直播间状态模块。群属性变更回调理论上不存在丢失可能。直播间数据都从群资料中获取,统一数据源,减少异常。
在高曝光模块需要频繁获取群资料,增大 IM 压力。在未集成 IM SDK 模块,需要通过业务后台调用 IM 服务端 SDK 获取群资料。且调用频率有限制。

通过上面的分析,我们建议使用方案一与方案二相结合的方式,维护直播间状态:
1. 在直播间内,使用 方案一获取直播间状态
2. 在直播间外、使用方案二获取直播间状态
3. 业务后台直播间状态更改后及时通过 IM 服务端接口 同步数据到 IM 群资料(群属性群自定义字段

群类型选择

在直播这个场景,用户聊天区域有以下特点:
1. 用户进出群频繁,且不需要管理该群会话信息(未读、lastMessage等)
2. 用户自动进群不需要审核
3. 用户临时发言,不关注群历史聊天记录
4. 群人数通常比较多
5. 可以不用存储群成员信息
所以根据 IM 的 群特性,这里选择 AVChatRoom 作为直播间的群类型。
即时通信 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 来获取更改的群属性。
相关代码示例:
Android
iOS & Mac
Flutter
Web
// 客户端获取群资料
V2TIMManager.getGroupManager().getGroupsInfo(groupIDList, new V2TIMValueCallback<List<V2TIMGroupInfoResult>>() {
@Override
public void onSuccess(List<V2TIMGroupInfoResult> v2TIMGroupInfoResults) {
// 获取群资料成功
}

@Override
public void onError(int code, String desc) {
// 获取群资料失败
}
});
// 客户端修改群资料
V2TIMGroupInfo v2TIMGroupInfo = new V2TIMGroupInfo();
v2TIMGroupInfo.setGroupID("需要修改的群 ID");
v2TIMGroupInfo.setFaceUrl("http://xxxx");
V2TIMManager.getGroupManager().setGroupInfo(v2TIMGroupInfo, new V2TIMCallback() {
@Override
public void onSuccess() {
// 修改群资料成功
}

@Override
public 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.groupCustomField
groupCustomField: [{ 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 端用户自己发送的消息,不论消息优先级的高低,都是一定要上屏的。在消息量特别大时,用户能够接受丢失少部分其他用户的消息。
设置消息优先级代码示例:
Android
iOS&Mac
Flutter
Web
// 创建自定义消息
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>() {
@Override
public void onProgress(int progress) {
// 自定义消息不会回调进度
}

@Override
public void onSuccess(V2TIMMessage message) {
// 发送群聊自定义消息成功
}

@Override
public void onError(int code, String desc) {
// 发送群聊自定义消息失败
}
});
/ 创建文本消息
V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:@"content"];
// 发送消息
[V2TIMManager.sharedInstance sendMessage:message
receiver:@"userID"
groupID:nil
priority:V2TIM_PRIORITY_NORMAL
onlineUserOnly:NO
offlinePushInfo:nil
progress:nil
succ:^{
// 文本消息发送成功
}
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){
// 发送成功
}
}
// 发送文本消息
// 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 设置为 true
needReadReceipt: 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. ...
其中和 IM 相关的如统计在线时长、统计消息量等我们都需要使用 IM 的回调来实现。在第一个准备工作的模块中,有配置回调的相关指引,控制台 中具体的回调如图:


统计相关信息的流程图如图:


消息发送后回调数据示例:
{
"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"
}
开发者可根据数据中的 MsgBody 中的消息类型区分普通消息和点赞送礼消息。全部字段含义可参见 服务端 API 文档。
用户在线状态改变回调数据示例:
{
"CallbackCommand": "State.StateChange",
"EventTime": 1629883332497,
"Info": {
"Action": "Login",
"To_Account": "testuser316",
"Reason": "Register"
},
"KickedDevice": [
{
"Platform": "Windows"
},
{
"Platform": "Android"
}
]
}
开发者可根据 Info 中的字段判断用户的在线状态,从而统计用户的在线时长。全部字段含义可参见 服务端 API
说明
此外,不少直播间都会展示直播间热度,并且根据直播间热度,讲直播间推荐给用户观看,直播间热度的统计方式和用户等级统计方式类似,也可以通过 IM 提供的回调系统来进行实现,这里就不在做过多的讲解。

直播间历史消息

使用 AVChatRoom 默认不存储直播间历史消息,当新用户进入直播间后,只能看到进入直播间后用户发送的消息。为了优化新进群用户的体验,可在控制台配置直播群用户拉取进群前消息条数,如图:


说明
此功能仅进阶版用户才可开通,且仅支持拉群24小时内最多20条历史消息。
直播群用户拉取进群前历史消息与拉起其他群历史消息一样,代码示例:
Android
iOS&Mac
Flutter
Web
V2TIMMessageListGetOption option = new V2TIMMessageListGetOption();
option.setGetType(V2TIMMessageListGetOption.V2TIM_GET_CLOUD_OLDER_MSG); // 拉取云端的更老的消息
option.setGetTimeBegin(1640966400); // 从 2022-01-01 00:00:00 开始
option.setGetTimePeriod(1 * 24 * 60 * 60); // 拉取一整天的消息
option.setCount(Integer.MAX_VALUE); // 返回时间范围内所有的消息
option.setGroupID(#you group id#); // 拉取群聊消息
V2TIMManager.getMessageManager().getHistoryMessageList(option, new V2TIMValueCallback<List<V2TIMMessage>>() {
@Override
public void onSuccess(List<V2TIMMessage> v2TIMMessages) {
Log.i("imsdk", "success");
}

@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
// 拉取单聊历史消息
// 首次拉取,lastMsg 设置为 nil
// 再次拉取时,lastMsg 可以使用返回的消息列表中的最后一条消息
[V2TIMManager.sharedInstance getC2CHistoryMessageList:#your user id# count:20 lastMsg:nil succ:^(NSArray<V2TIMMessage *> *msgs) {
// 记录下次拉取的 lastMsg,用于下次拉取
V2TIMMessage *lastMsg = msgs.lastObject;
NSLog(@"success, %@", msgs);
} fail:^(int code, NSString *desc) {
NSLog(@"fail, %d, %@", code, desc);
}];
// 拉取单聊历史消息
// 首次拉取,lastMsgID 设置为 null
// 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的id
TencentImSDKPlugin.v2TIMManager.getMessageManager().getC2CHistoryMessageList(
userID: "userId",
count: 10,
lastMsgID: null,
);
// 打开某个会话时,第一次拉取消息列表
let promise = tim.getMessageList({conversationID: 'C2Ctest', count: 15});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
});
// 下拉查看更多消息
let promise = tim.getMessageList({conversationID: 'C2Ctest', nextReqMessageID, count: 15});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
});

直播间在线人数

直播间实时展示在线人数在直播场景也是一个十分常见的需求,实现方案分为两种,但两种也是各有优劣。
1. 通过客户端SDK提供的 getGroupOnlineMemberCount API 定时轮训的方式拉取群在线人数。
2. 通过用户进退群的后台回调统计,并通过服务端 API 如 群系统通知群自定义消息 下发给群内所有成员。
通过客户端 SDK 提供的接口拉取在线人数的方式获取在线人数对于单直播间模式的用户来说基本可以满足用户需求。但对于 App 有多个直播间且需要在大量曝光位置展示直播间在线人数的需求,建议使用第二种方案来统计在线人数。
说明
开发者服务端在向客户端发送在线人数统计消息时,可采用定时发送的方式,如每五秒发一次。但这种方式,在直播间人数变化不大时有额外的网络开销。建议开发者按照群人数变化率监测的方式来进行更新。
直播间在线人数的准确性与实时性的优先级,开发者可以根据自身的业务来设置。
获取直播间在线人数代码如下:
Android
iOS&Mac
Flutter
Web
2TIMManager.getGroupManager().getGroupOnlineMemberCount("group_avchatroom", new V2TIMValueCallback<Integer>() {
@Override
public void onSuccess(Integer integer) {
// 获取直播群在线人数成功
}

@Override
public 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); // 获取直播群在线人数失败的相关信息
});

直播间禁言

直播间禁言分为两种,一种是整个直播间禁言,另一种是针对某个用户禁言。两种禁言都有其使用的业务场景。
一般来说我们使用服务端 SDK 来设置禁言。整个直播间禁言,通过 设置群属性 的全员禁言字段来设置。对单个群成员禁言,通过 设置群成员属性 来设置。
当直播间管理者在后台设置群禁言后,客户端在收到对应的回调事件后,应该把用户的输入框设置为 disable 状态。以免用户在发消息时提示发送消息失败。在解除禁言后也应该将输入框设置为 enable 状态。
客户端对应的回调代码如下:
Android
iOS&Mac
Flutter
Web
// 禁言群成员 userB 1分钟
V2TIMManager.getGroupManager().muteGroupMember("groupA", "userB", 60, new V2TIMCallback() {
@Override
public void onSuccess() {
// 禁言群成员成功
}

@Override
public void onError(int code, String desc) {
// 禁言群成员失败
}
});

// 全员禁言
V2TIMGroupInfo info = new V2TIMGroupInfo();
info.setGroupID("groupA");
info.setAllMuted(true);
V2TIMManager.getGroupManager().setGroupInfo(info, new V2TIMCallback() {
@Override
public void onSuccess() {
// 全员禁言成功
}

@Override
public void onError(int code, String desc) {
// 全员禁言失败
}
});

V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
@Override
public void onMemberInfoChanged(String groupID, List<V2TIMGroupMemberChangeInfo> v2TIMGroupMemberChangeInfoList) {
// 禁言群成员监听
for (V2TIMGroupMemberChangeInfo memberChangeInfo : v2TIMGroupMemberChangeInfoList) {
// 被禁言用户 ID
String userID = memberChangeInfo.getUserID();
// 禁言时间
long muteTime = memberChangeInfo.getMuteTime();
}
}

@Override
public 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) {
// 被禁言用户 ID
NSString *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 暂时不支持直播间禁言,可使用服务端 API 进行 封禁 以及 解封

直播间封禁

开发者可以通过服务端 封禁 的接口,来踢出直播间成员,并且封禁一段时间不允许被踢成员再次进群。
注意
在客户端 SDK 6.6.X 及以上版本、Flutter SDK 4.1.1 及以上版本,可以使用直播间踢人接口实现封禁功能。
可参考代码如下:
Android
iOS&Mac
Flutter
Web
List<String> userIDList = new ArrayList<>();
userIDList.add("userB");
V2TIMManager.getGroupManager().kickGroupMember("groupA", userIDList, "", new V2TIMValueCallback<List<V2TIMGroupMemberOperationResult>>() {
@Override
public void onSuccess(List<V2TIMGroupMemberOperationResult> v2TIMGroupMemberOperationResults) {
// 踢人成功
}

@Override
public void onError(int code, String desc) {
// 踢人失败
}
});

V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
@Override
public 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);

直播间敏感内容过滤

过滤直播间敏感内容也是直播业务非常重要的功能,实现方案如下:
1. 绑定用户群发消息前回调
2. 判断通过回调数据判断消息类型,将消息数据传递给天御或者其他第三方检测服务
3. 如消息为普通文本消息,可等天御检测同步返回,在将是否下发消息的数据包返回给 IM 后台
4. 如消息为多媒体消息,可以直接返回下发消息的数据包给 IM 后台。并且等待天御异步返回的结果返回后,如果发现消息不合规,可通过消息编辑接口或群自定义消息接口下发消息变更通知。客户端收到通知后,在屏蔽掉不合规的消息。
发送消息前回调数据示例:
{
"CallbackCommand": "Group.CallbackBeforeSendMsg", // 回调命令
"GroupId": "@TGS#2J4SZEAEL", // 群组 ID
"Type": "Public", // 群组类型
"From_Account": "jared", // 发送者
"Operator_Account":"admin", // 请求的发起者
"Random": 123456, // 随机数
"OnlineOnlyFlag": 1, //在线消息,为1,否则为0;直播群忽略此属性,为默认值0。
"MsgBody": [ // 消息体,参见 TIMMessage 消息对象
{
"MsgType": "TIMTextElem", // 文本
"MsgContent": {
"Text": "red packet"
}
}
],
"CloudCustomData": "your cloud custom data"
}
开发者可通过 MsgBody 中 MsgType 字段判断消息类型。完整字段说明可参见 回调文档
开发者可以选择对不合法的消息进行不一样的处理,可在回调中回包给 IM 后台来控制。
{
"ActionStatus": "OK",
"ErrorInfo": "",
"ErrorCode": 0 // 不同的ErrorCode有不同的含义
}
ErrorCode
含义
0
为允许发言,正常下发消息
1
为拒绝发言,客户端返回10016
2
为静默丢弃,客户端返回正常
开发者可根据自身业务进行选择使用
敏感内容检测流程图如图所示:




直播间群成员列表

在需要展示直播间在线群成员列表时,可以通过getGroupMemberList接口获取群成员列表,但 AVChatRoom 由于人数众多,不提供拉取全量成员列表的功能。根据进阶版和非进阶版区分不同的表现:
1. 非进阶版客户可调用 getGroupMemberList 拉取最近进群的 30 位群成员。
2. 进阶版客户可调用 getGroupMemberList 拉取最近进群的 1000 位群成员。此功能需要在 IM 控制台开启开关,如果不开启,默认跟非进阶版一样,仅拉取最近进群 30 位群成员。
控制台配置如图2.6:

img


如果是非进阶版,开发者也可通过getGroupMemberList与群监听中的 onGroupMemberEnter 和 onGroupMemberQuit 回调,在客户端维护当前在线群成员列表。但此方案用户退出直播间重新进入后,也只能获取最新的30位群成员。
Android
iOS&Mac
Flutter
Web
// 通过 filter 参数指定只拉取群主的资料
int role = V2TIMGroupMemberFullInfo.V2TIM_GROUP_MEMBER_FILTER_OWNER;
V2TIMManager.getGroupManager().getGroupMemberList("testGroup", role, 0,
new V2TIMValueCallback<V2TIMGroupMemberInfoResult>() {
@Override
public void onError(int code, String desc) {
// 拉取失败
}

@Override
public 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.1 PC 端可使用 VLC 工具播放 2.App 端可使用 播放器 SDK 播放
说明
在拉流前也需要配置播放地址,请参见在 播放配置地址生成器 生成地址,SDK接入过程详情可参见 直播播放

导播台

为了满足直播场景更多的需求,开发者可以使用云 导播台 对直播流进行处理,目前导播台支持如下功能:
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.nickMessage.avatar 字段都为空,该怎么处理才能在界面上正常展示昵称和头像?

可通过 getUserInfo 接口,获取自己的用户信息中的 nick 和 avatar 字段作为消息发送发的 nick 和 avatar 字段。

4. 为什么会丢消息?

出现丢消息的可能原因如下:
直播群有40条/秒的频率限制,可通过消息发送前回调与消息发送后回调进行判断,若丢失的消息有收到消息发送前回调,未收到消息发送后回调,则该消息被限频。
可参见 消息相关问题,判断是否因为Web 端退出时,导致 Android/iOS/PC 同步退出。
如果是Web 出现问题,请确认您使用的 SDK 版本是否早于V2.7.6,如果是,请升级最新版。
如果您已排除以上可能性,您可以 提交工单 联系我们。