最近项目新增直播带货需求,指定使用腾讯移动直播SDK,集成的过程也算是磕磕绊绊,因为以前自己没接触过,同事也没做过这一块,所以很多不清不楚的地方,而且是是多端集成,主播端在App直播,用户端是在小程序观看,小程序又是集成的腾讯IM直播带货SDK。
移动端在开启直播时需要设置推流地址,小程序端需要通过拉流地址获取直播内容,所以在开播前需要把推拉流地址生成好,主播端推流成功后算开启直播成功,这时候后端需要把拉流地址保存起来,小程序端获取直播列表时返回拉流地址。推拉流地址也可以在前端生成,但是最好在推拉流地址上生成防盗链,这样可以防止攻击者伪造您的后台生成播放 URL
Domain
推流域名,可使用腾讯云直播提供的默认推流域名,也可以用自有已备案且 CNAME 配置成功的推流域名。
AppName
直播的应用名称,默认为 live,可自定义。
StreamName(流 ID)
自定义的流名称,每路直播流的唯一标识符,推荐用随机数字或数字与字母组合。
鉴权 Key(非必需)
包含 txSecret 和 txTime 两部分:txSecret=Md5(key+StreamName+hex(time))&txTime=hex(time)。
开启推流鉴权后需使用包含鉴权 Key 的 URL 进行推流。若未开启推流鉴权,则推流地址中无需 “?” 及其后内容。
txTime(地址有效期)
表示何时该 URL 会过期,格式支持十六进制的 UNIX 时间戳。
说明:
例如5867D600代表2017年1月1日0时0点0分过期,我们的客户一般会将 txTime 设置为当前时间24小时以后过期,过期时间不要太短也不要太长,当主播在直播过程中遭遇网络闪断时会重新恢复推流,如果过期时间太短,主播会因为推流 URL 过期而无法恢复推流。
txSecret(防盗链签名)
用以防止攻击者伪造您的后台生成推流 URL,计算方法在后面。
第一步:交换密钥
首先,您需要在官网的控制台配置一个加密密钥,这个加密密钥用于在您的服务器上生成防盗链签名,由于腾讯云跟您持有同样的密钥,所以您生成的防盗链签名,腾讯云是可以进行解密确认的。
加密密钥分为推流防盗链 KEY 和播放防盗链 KEY,前者用于生成推流防盗链 URL,后者用于生成播放防盗 URL。进入【云直播控制台】>【域名管理】中单击对应的域名或【管理】,选择【推流配置】即可以自助配置推流防盗链 KEY,如图所示:
第二步 :生成 txTime
签名中明文部分为 txTime,含义是该链接的有效期,例如当前的时间是2018-12-29 11:13:45,而且期望新生成的 URL 是在3小时后即作废,那么 txTime 就可以设置为2018-12-29 14:13:45。
不过这么长一串时间字符串放在 URL 里显然不太合适,实际使用中我们是把2018-12-29 14:13:45转换成 UNIX 时间戳,也就是1546064025(可通过不同编程语言直接调用时间函数进行转换处理),然后转换成十六进制以进一步压缩字符长度,也就是 txTime = 1546064025(十进制) = 5C271099(十六进制),当然,使用十进制也是支持的。
注意:
我们建议您设置的 txTime 过期时间不要过长或过短:
- 如果过期时间过短,当主播在直播过程中遭遇网络闪断时,会因为推流 URL 过期而无法恢复推流。
- 如果过期时间过长,存在被盗推的风险。
第三步:生成 txSecret
txSecret 的生成方法是 = MD5(KEY + StreamName + txTime),这里的 KEY 就是您在第一步中配置的加密 KEY, StreamName(也叫流 ID,推荐用随机数字或者用户 ID)在本例中为 test,txTime 为刚才计算的 5C271099,MD5 即标准的 MD5 单向不可逆哈希算法。
例如:
KEY 为 e12c46f2612d5106e2034781ab261ca3
则 txSecret = MD5(e12c46f2612d5106e2034781ab261ca3test5C271099) = f85a2ab363fe4deaffef9754d79da6fe
第四步:合成防盗链地址
符合腾讯云标准的推流 URL,它由下面四个部分组成:
现在我们有推流(或播放)可以用来告知腾讯云该 URL 过期时间的 txTime,只有腾讯云才能解密并且验证的 txSecret,StreamName 以及推流域名(假设为 livepush.tcloud.com),那么我们就可以合作一条标准的 URL。在本文档的例子中,推流 URL 为:
rtmp://livepush.tcloud.com/live/test?txSecret=f85a2ab363fe4deaffef9754d79da6fe&txTime=5C27
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-16 14:28
* @Description: 直播防盗链计算
*/
public class LiveAntiTheftChain {
/**
* 推流密文
*/
private static final String txSecret = "TXSECRET";
/**
* 有效时间 16进制
*/
private static final String txTime = "TXTIME";
/**
* 流 ID,推荐用随机数字或者用户 ID
*/
private static final String streamName = "STREAMNAME";
/**
* 推流地址 域名+应用名
*/
private static String pushStreamUrl = LiveConstant.PUSH_STREAM_URL;
/**
* 拉流地址 域名+应用名
*/
private static String getStreamUrl = LiveConstant.GET_STREAM_URL;
/**
* 推拉流共同的参数
*/
private static String auth_key = streamName + "?txSecret=" + txSecret + "&txTime=" + txTime;
/**
* 生成推流地址
*
* @param userId
* @return
*/
public static Map generateEncodeUrl(String userId) {
//1.生成 txTime
//我们建议您设置的 txTime 过期时间不要过长或过短:
//如果过期时间过短,当主播在直播过程中遭遇网络闪断时,会因为推流 URL 过期而无法恢复推流。
//如果过期时间过长,存在被盗推的风险。
//暂时设置五小时
Long second = LocalDateTime.now().plusHours(5).toEpochSecond(ZoneOffset.of("+8"));
//获取秒数
String effectiveTime = Long.toHexString(second);
//2.生成 txSecret
//生成推流密文 txSecret 的生成方法是 = MD5(KEY + StreamName + txTime)
String md5Code = Md5Utils.getMd5Code(LiveConstant.PUSH_STREAM_KEY + userId + effectiveTime);
//3.合成防盗链地址
String authKey = auth_key
.replace(streamName, userId)
.replace(txSecret, md5Code)
.replace(txTime, effectiveTime);
HashMap map = Maps.newHashMap();
map.put("pushStreamUrl", pushStreamUrl + authKey);
map.put("getStreamUrl", getStreamUrl + authKey);
//生成IM房间号Id
// map.put("ImGroupId", Md5Utils.getMd5Code(userId + System.currentTimeMillis()));
map.put("ImGroupId", userId);
return map;
}
}
直播间id就是移动直播需要一个RoomId,IM群组ID则是即时通讯需要用到的,就类似于生成一个聊天室,然后可以设置管理员,设置群信息,设置禁言等功能。
这个比较好处理,都可以使用自己平台的用户id。
如果有需求是需要每次创建新的群组,那么可以在用户id上加个时间戳之类,但是每次直播结束的时候需要把本次的群组解散,不然下次创建会提示群组已经存在。
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权。
目前,腾讯云的移动直播(MLVB)、实时音视频(TRTC)以及即时通信(IM)等服务都采用了该套安全保护机制。要使用这些服务,您都需要在相应 SDK 的初始化或登录函数中提供 SDKAppID、UserID 和 UserSig 三个关键信息。
其中 SDKAppID 用于标识您的应用,UserID 用于标识您的用户,而 UserSig 则是基于前两者计算出的安全签名,它由 HMAC SHA256 加密算法计算得出。只要攻击者不能伪造 UserSig,就无法盗用您的云服务流量。
UserSig 的计算原理如下所示,其本质就是对 SDKAppID、UserID 和 ExpireTime 等关键信息进行了一次哈希加密:
//UserSig 计算公式,其中 secretkey 为计算 usersig 用的加密密钥
usersig = hmacsha256(secretkey, (userid + sdkappid + currtime + expire +
base64(userid + sdkappid + currtime + expire)))
访问云直播 应用管理 可以查询计算 UserSig 用的密钥,方法如下:
选择一个应用并进入详情页面,如果还没有应用就创建一个。
进入应用管理页面,单击【查看密钥】按钮即可获得加密密钥。
采用服务端计算 UserSig 的方案,可以最大限度地保障计算 UserSig 用的密钥不被泄露,因为攻破一台服务器的难度要高于逆向一款 App。具体的做法如下:
您的 App 在调用 SDK 的初始化函数之前,首先要向您的服务器请求 UserSig。
您的服务器根据 SDKAppID 和 UserID 计算 UserSig,计算源码见文档前半部分。
服务器将计算好的 UserSig 返回给您的 App。
您的 App 将获得的 UserSig 通过特定 API 传递给 SDK。
SDK 将 SDKAppID + UserID + UserSig 提交给腾讯云服务器进行校验。
腾讯云校验 UserSig,确认合法性。
校验通过后,会向 TRTCSDK 提供实时音视频服务。
官方DEMO:
语言版本 |
签名算法 |
关键函数 |
下载链接 |
---|---|---|---|
Java |
HMAC-SHA256 |
genSig |
Github |
GO |
HMAC-SHA256 |
GenSig |
Github |
PHP |
HMAC-SHA256 |
genSig |
Github |
Nodejs |
HMAC-SHA256 |
genSig |
Github |
Python |
HMAC-SHA256 |
gen_sig |
Github |
C# |
HMAC-SHA256 |
GenSig |
Github |
如果是Java,可以直接在Pom文件里面引入依赖
com.github.tencentyun
tls-sig-api-v2
1.1
然后直接调用:
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-16 17:33
* @Description: //UserSig 计算公式,其中 secretkey 为计算 usersig 用的加密密钥
*
* usersig = hmacsha256(secretkey, (userid + sdkappid + currtime + expire +
* base64(userid + sdkappid + currtime + expire)))
*/
public class LiveUtil {
/**
* 签名过期时间,建议不要设置的过短
*
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
private static final int EXPIRETIME = 604800;
/**
* 获取用户签名
*
* @return
*/
public static String getUserSig(String userId) {
return new TLSSigAPIv2(LiveConstant.SDK_APPID, LiveConstant.SDK_SECRETKEY).genSig(userId, EXPIRETIME);
}
}
前端需要先登陆直播SDK(登陆直播后会将当前直播账号和即时通信 IM 应用绑定起来,所以IM不需要再登陆),登陆时需要的参数需要从后端获取,所以后端需要提供这个接口。
参数 | 类型 | 填写方案 |
---|---|---|
sdkAppID | 数字 | 当前应用的 AppID,在 Step4 中可以获取到。 |
userID | 字符串 | 当前用户在您的帐号系统中的 ID。 |
userName | 字符串 | 用户名(昵称)。 |
userAvatar | 字符串 | 用户头像的 URL 地址。 |
userSig | 字符串 | 登录签名 |
/**
* 获取登陆SDK配置信息
* @param userId
* @param type 0-App用户,1-小程序用户
* @return
*/
public Map getLoginConfig(String userId, String type) {
String userNname;
String userAvatar;
if (type.equals("0")) {
//获取App用户信息
Register register = registerService.getRegisterById(userId);
Assert.isTrue(register != null, "App用户信息不存在");
userNname = register.getName();
userAvatar = register.getImage();
} else {
//获取小程序用户信息
MiniProgramUser user = miniProgramUserService.getById(userId);
Assert.isTrue(user != null, "小程序用户信息不存在");
userNname = user.getNickname();
userAvatar = user.getAvatar();
}
//获取用户登录签名
String userSig = LiveUtil.getUserSig(userId);
HashMap map = Maps.newHashMap();
map.put("sdkAppID", LiveConstant.SDK_APPID);
map.put("userID", userId);
map.put("userName", userNname);
map.put("userAvatar", userAvatar);
map.put("userSig", userSig);
return map;
}
这是Service层的代码,Controller直接返回这些信息给前端就好了。
既然是带货直播,那么自然涉及商品,比如说介绍中的商品,商品列表等。这个时候是需要在直播间实时变化的,通常的HTTP请求肯定是实现不了,不可能说让前端一直轮询去请求接口看介绍中的商品是否有变化。这个在小程序直播带货SDK里面有解决示例。
在直播带货场景中,主播推荐某款产品时,屏幕下方的商品位需要立即更新为当前产品,并需要通知所有在直播中的用户有新的商品上线。
从技术角度来看,触发商品上新消息有两种方案:
我这边采用的是管理员修改群自定义字段的方式实现商品上新消息通知,具体操作步骤如下:
添加群自定义字段
说明:
- 字段名称只能由字母、数字以及下划线(_)组成,不能以数字开头,且长度不能超过16个字符。
- 群自定义字段名称不允许与群成员自定义字段名称一致。
请求参数:(注意这个是拼接在URL后面,每个接口都必须要)
参数 | 说明 |
---|---|
sdkappid | 创建应用时即时通信 IM 控制台分配的 SDKAppID |
identifier | 必须为 App 管理员帐号 |
usersig | App 管理员帐号生成的签名 |
random | 请输入随机的32位无符号整数,取值范围0 - 4294967295 |
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-19 14:40
* @Description: 描述
*/
@Data
public class ImReqCommonParam {
/**
* 创建应用时即时通信 IM 控制台分配的 SDKAppID
*/
private Long sdkappid = LiveConstant.SDK_APPID;
/**
* 必须为 App 管理员帐号
*/
private String identifier = "admin";
/**
* App 管理员帐号生成的签名
*/
private String usersig = LiveUtil.getUserSig(identifier);
/**
* 请输入随机的32位无符号整数,取值范围0 - 4294967295
*/
private Long random = RandomUtils.nextLong(0, 4294967295L);
/**
* 固定json
*/
private String contenttype = "json";
}
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-19 14:38
* @Description: 描述
*/
@AllArgsConstructor
public enum ImReqUrl {
/**
* 禁言
*/
FORBIDDEN("/v4/group_open_http_svc/forbid_send_msg"),
/**
* 解散群组
*/
DESTROY_GROUP("/v4/group_open_http_svc/destroy_group"),
/**
* 修改群基础资料
*/
MODIFY_GROUP_BASE_INFO("/v4/group_open_http_svc/modify_group_base_info"),
/**
* 修改群成员资料(包括设置管理员等)
*/
MODIFY_GROUP_MEMBER_INFO("/v4/group_open_http_svc/modify_group_member_info"),
;
private String url;
/**
* IM接口域名
*/
private static final String HOST = "https://console.tim.qq.com";
public String getUrl() {
String s = HOST + url + "?" + UrlUtil.getGetParams(new ImReqCommonParam());
return s;
}
}
/**
* 发起请求
*
* @param reqUrl
* @param param
* @return
*/
private static ImRepBody makeARequest(ImReqUrl reqUrl, Object param) {
//获取请求地址
String url = reqUrl.getUrl();
//发起请求
String result = HttpRequestUtil.doPost(url, JSON.toJSONString(param));
return JSON.parseObject(result, ImRepBody.class);
}
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-19 15:16
* @Description: 描述
*/
@Data
public class ImRepBody {
/**
* 请求处理的结果,OK 表示处理成功,FAIL 表示失败
*/
private String ActionStatus;
/**
* 错误信息
*/
private String ErrorInfo;
/**
* 错误码,0表示成功,非0表示失败
*/
private Integer ErrorCode;
}
在IM服务端API里面群组管理中有修改群基础资料的接口,这个接口就可以修改群自定义字
接口地址:https://console.tim.qq.com/v4/group_open_http_svc/modify_group_base_info
Body消息体参数:
{
"GroupId": "@TGS#2J4SZEAEL", // 要修改哪个群的基础资料(必填)
"Name": "NewName", // 群名称(选填)
"Introduction": "NewIntroduction", // 群简介(选填)
"Notification": "NewNotification", // 群公告(选填)
"FaceUrl": "http://this.is.new.face.url", // 群头像(选填)
"MaxMemberNum": 500, // 最大群成员数量(选填)
"ApplyJoinOption": "NeedPermission", // 申请加群方式(选填)
"ShutUpAllMember": "On", // 设置全员禁言(选填):"On"开启,"Off"关闭
"AppDefinedData": [ // 自定义字段(选填)
{
"Key": "GroupTestData1", // 需要修改的自定义字段 key
"Value": "NewData" // 自定义字段的新值
},
{
"Key": "GroupTestData2",
"Value": "" // 设置为空表示删除该项自定义字段
}
]
}
示例:
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-19 17:13
* @Description: 修改群基础资料
*/
@Data
public class ModifyGroupBaseInfoReq {
/**
* 需要修改基础信息的群组的 ID
*/
@JSONField(name = "GroupId")
private String groupId;
/**
* 默认情况是没有的。开通群组维度的自定义字段详情请参见 自定义字段
*/
@JSONField(name = "AppDefinedData")
private List appDefinedData;
//暂时只定义了这两个字段,因为还没涉及到要修改其他群信息
public ModifyGroupBaseInfoReq(String groupId) {
this.groupId = groupId;
}
public ModifyGroupBaseInfoReq addDefinedData(DefinedDataKey definedDataKey,String value){
if (appDefinedData == null) {
appDefinedData = Lists.newArrayList();
}
appDefinedData.add(new DefinedData(definedDataKey.name(), value));
return this;
}
@Data
@AllArgsConstructor
protected class DefinedData {
@JSONField(name = "Key")
private String Key;
@JSONField(name = "Value")
private String Value;
}
}
/**
* 更新介绍中商品
*
* @param liveGoodsListVo
* @return
*/
public static ImRepBody updateIntroduceGoods(String groupId, LiveGoodsListVo liveGoodsListVo) {
//设置请求参数
ModifyGroupBaseInfoReq modifyGroupBaseInfoReq = new ModifyGroupBaseInfoReq(groupId);
modifyGroupBaseInfoReq.addDefinedData(DefinedDataKey.introduce_goods, Objects.nonNull(liveGoodsListVo) ? JSON.toJSONString(liveGoodsListVo) : "");
//发起请求
ImRepBody imRepBody = makeARequest(ImReqUrl.MODIFY_GROUP_BASE_INFO, modifyGroupBaseInfoReq);
return imRepBody;
}
/**
* 修改直播间状态
* @param groupId 直播间id
* @param status 0-未开播,1-已开播,2-屏蔽
* @return
*/
public static ImRepBody updateRoomStatus(String groupId,String status){
//设置请求参数
ModifyGroupBaseInfoReq modifyGroupBaseInfoReq = new ModifyGroupBaseInfoReq(groupId);
modifyGroupBaseInfoReq.addDefinedData(DefinedDataKey.room_status, status);
//发起请求
ImRepBody imRepBody = makeARequest(ImReqUrl.MODIFY_GROUP_BASE_INFO, modifyGroupBaseInfoReq);
return imRepBody;
}
https://console.tim.qq.com/v4/group_open_http_svc/forbid_send_msg
{
"GroupId": "@TGS#2C5SZEAEF",
"Members_Account": [ // 最多支持500个
"peter",
"leckie"
],
"ShutUpTime": 60 // 禁言时间,单位为秒,0=取消禁言
}
示例
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-19 15:13
* @Description: 群组禁言请求实体
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ForbiddenReq {
/**
* 需要查询的群组 ID
*/
@JSONField(name = "GroupId")
private String GroupId;
/**
* 需要禁言的用户帐号,最多支持500个帐号
*/
@JSONField(name = "Members_Account")
private List Members_Account;
/**
* 需禁言时间,单位为秒,为0时表示取消禁言
*/
@JSONField(name = "ShutUpTime")
private Long ShutUpTime;
}
/**
* 设置禁言
*
* @param groupId
* @param userId
* @param shutUpTime 禁言时间 0=取消禁言
* @return
*/
public static ImRepBody groupForbidden(String groupId, String[] userId, Long shutUpTime) {
//设置请求参数
ForbiddenReq forbiddenReq = new ForbiddenReq(groupId, Arrays.asList(userId), shutUpTime);
ImRepBody imRepBody = makeARequest(ImReqUrl.FORBIDDEN, forbiddenReq);
return imRepBody;
}
https://console.tim.qq.com/v4/group_open_http_svc/modify_group_member_info
{
"GroupId": "@TGS#2CLUZEAEJ", // 要操作的群组(必填)
"Member_Account": "bob", // 要操作的群成员(必填)
"Role": "Admin" // 设置管理员 Member-取消管理员
}
示例:
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-20 14:40
* @Description: 描述
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ModifyGroupMemberInfoReq {
/**
* 操作的群 ID
*/
@JSONField(name = "GroupId")
private String GroupId;
/**
* 要操作的群成员
*/
@JSONField(name = "Member_Account")
private String Member_Account;
/**
* 成员身份,Admin/Member 分别为设置/取消管理员
*/
@JSONField(name = "Role")
private Role Role;
public enum Role{
Admin,Member
}
}
/**
* 设置管理员
*
* @param groupId 群组id
* @param userId 用户id
* @param b 设置管理员/取消管理员
* @return
*/
public static ImRepBody settingAdmin(String groupId, String userId, boolean b) {
//设置请求参数
ModifyGroupMemberInfoReq req = new ModifyGroupMemberInfoReq(groupId, userId,
b ? ModifyGroupMemberInfoReq.Role.Admin : ModifyGroupMemberInfoReq.Role.Member);
//发起请求
ImRepBody imRepBody = makeARequest(ImReqUrl.MODIFY_GROUP_MEMBER_INFO, req);
return imRepBody;
}
直播结束后需要获取到直播录像,这个是在控制台配置录制模板和回调模板,服务端编写接口获取回调。
录制回看功能依托于腾讯云的云点播服务支撑,如果您想要对接这个功能,首先需要在腾讯云的管理控制台 开通云点播服务。服务开通之后,新录制的文件就可以在云点播控制台的 视频管理 里找到它们。
开启录制的方法如下:
说明:
如需通过 API 对直播频道进行录制,详细请参见 创建录制任务。
录制转点播后,文件自动存放于点播平台,故用户需在使用录制功能前,提前开通点播服务并购买相关空间和流量用于存放和播放录制后的视频文件,详细请参见 点播快速入门。
基本步骤:
在云直播菜单栏内选择【功能模板】>【录制配置】,单击"+"进行设置。设置完基本信息后,单击【保存】。
规格说明:
录制视频针对直播原始码率录制,输出格式有 HLS、MP4、FLV 和 ACC 四种,其中 ACC 为纯音频录制。
录制 MP4 和 FLV 格式最长单个文件时长为120分钟,录制 HLS 格式最长单个文件时长无限制,如果超出则新建文件继续录制。
单个录制文件保存最大时长均为1080天。
直播过程中预计在录制结束5分钟左右可获取对应文件。例如,某直播从12:00开始录制,12:30结束录制,则12:35左右可获取12:00 - 12:30的对应片段。
受限于音视频文件格式(FLV/MP4/HLS)对编码类型的支持,视频编码类型支持 H.264,音频编码类型支持 AAC。
关联域名
创建好录制模板后,需通在【域名管理】中,选择对应的推流域名,进入【模板配置】栏,单击【编辑】为该域名指定录制模板后,单击【保存】即可。
一个新的录制视频文件生成后,会相应的生成一个观看地址,您可以按照自己的业务需求对其进行处理。在小直播中,我们直接将录制的文件 URL 和房间列表拼在了一起,以弥补在线主播不足的窘境。
但您可以结合自己的业务场景实现很多的扩展功能,例如:您可以将其追加到主播的资料信息里,作为该主播曾经直播的节目而存在;或者将其放入回放列表中,经过专门的人工筛选,将优质的视频推荐给您的 App 用户。
如何才能拿到文件的地址,有如下两种解决方案:
1. 被动监听通知
您可以使用腾讯云的 回调配置:您的服务器注册一个自己的回调 URL 给腾讯云,腾讯云会在一个新的录制文件生成时通过这个 URL 通知给您。
在云直播菜单栏内选择【功能模板】>【回调配置】,单击"+"创建回调模板。在回调设置弹框中填写完成回调信息,单击【保存】即可。
关联域名
创建好回调模板后,需通在【域名管理】中,选择对应的推流域名,进入【模板配置】栏,单击【编辑】为该域名指定回调模板后,单击【保存】即可。
如下是一个典型的通知消息,它的含义是:一个 ID 为9192487266581821586的新的 flv 录制文件已经生成,播放地址为:http://200025724.vod.myqcloud.com/200025724_ac92b781a22c4a3e937c9e61c2624af7.f0.flv
。
{
"channel_id": "2121_15919131751",
"end_time": 1473125627,
"event_type": 100,
"file_format": "flv",
"file_id": "9192487266581821586",
"file_size": 9749353,
"sign": "fef79a097458ed80b5f5574cbc13e1fd",
"start_time": 1473135647,
"stream_id": "2121_15919131751",
"t": 1473126233,
"video_id": "200025724_ac92b781a22c4a3e937c9e61c2624af7",
"video_url": "http://200025724.vod.myqcloud.com/200025724_ac92b781a22c4a3e937c9e61c2624af7.f0.flv"
}
/**
* @BelongsProject:
* @BelongsPackage:
* @Author: hef
* @CreateTime: 2020-06-17 16:20
* @Description: 腾讯移动直播录像回调结构
*/
@Data
public class LiveVideoNotifyDTO extends NotifyDTO {
/**
* 用户 APPID
*/
private Integer appid;
/**
* 直播流名称
*/
private String stream_id;
/**
* 同直播流名称
*/
private String channel_id;
/**
* 点播 file ID,在点播平台可以唯一定位一个点播视频文件
*/
private String file_id;
/**
* 文件格式:flv,hls,mp4,aac
*/
private String file_format;
/**
*录制文件起始时间戳
*/
private Long start_time;
/**
*录制文件结束时间戳
*/
private Long end_time;
/**
*录制文件时长,单位秒
*/
private Long duration;
/**
* 录制文件大小,单位字节
*/
private Long file_size;
/**
* 用户推流 URL 所带参数
*/
private String stream_param;
/**
* 录制文件文件下载 URL
*/
private String video_url;
}
public boolean liveVideoNotify(LiveVideoNotifyDTO liveVideoNotifyDTO) {
//校验是否属于录制事件回调
if (!liveVideoNotifyDTO.getEvent_type().equals(100)) {
throw new RuntimeException("回调不属于录制事件");
}
//校验回调事件是否已经过期
Long t = liveVideoNotifyDTO.getT();
Date date = new Date(t * 1000);
if (new Date().after(date)) {
throw new RuntimeException("回调事件已过期");
}
//校验秘钥是否正确 主要用来保证数据信息安全
String sign = Md5Utils.getMd5Code(LiveConstant.SDK_CALLBACK_KEY + t);
if (!sign.equals(liveVideoNotifyDTO.getSign())) {
throw new RuntimeException("签名校验失败");
}
....
//自己的业务代码
}
未完待续......