项目集成腾讯移动直播总结--后端

 

最近项目新增直播带货需求,指定使用腾讯移动直播SDK,集成的过程也算是磕磕绊绊,因为以前自己没接触过,同事也没做过这一块,所以很多不清不楚的地方,而且是是多端集成,主播端在App直播,用户端是在小程序观看,小程序又是集成的腾讯IM直播带货SDK。


一、开播前准备

1、生成推拉流地址

移动端在开启直播时需要设置推流地址,小程序端需要通过拉流地址获取直播内容,所以在开播前需要把推拉流地址生成好,主播端推流成功后算开启直播成功,这时候后端需要把拉流地址保存起来,小程序端获取直播列表时返回拉流地址。推拉流地址也可以在前端生成,但是最好在推拉流地址上生成防盗链,这样可以防止攻击者伪造您的后台生成播放 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,计算方法在后面。

1.1、 计算防盗链签名

  • 第一步:交换密钥
    首先,您需要在官网的控制台配置一个加密密钥,这个加密密钥用于在您的服务器上生成防盗链签名,由于腾讯云跟您持有同样的密钥,所以您生成的防盗链签名,腾讯云是可以进行解密确认的。

    加密密钥分为推流防盗链 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<String, String> 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<String, String> 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;
    }
}

 2、生成直播间ID和IM群组ID

         直播间id就是移动直播需要一个RoomId,IM群组ID则是即时通讯需要用到的,就类似于生成一个聊天室,然后可以设置管理员,设置群信息,设置禁言等功能。

        这个比较好处理,都可以使用自己平台的用户id。

        如果有需求是需要每次创建新的群组,那么可以在用户id上加个时间戳之类,但是每次直播结束的时候需要把本次的群组解散,不然下次创建会提示群组已经存在。

3、生成用户签名(UserSig)

  • UserSig 介绍

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 用的密钥,方法如下:

  1. 选择一个应用并进入详情页面,如果还没有应用就创建一个。

  2. 进入应用管理页面,单击【查看密钥】按钮即可获得加密密钥。

  • 服务端计算

采用服务端计算 UserSig 的方案,可以最大限度地保障计算 UserSig 用的密钥不被泄露,因为攻破一台服务器的难度要高于逆向一款 App。具体的做法如下:

  1. 您的 App 在调用 SDK 的初始化函数之前,首先要向您的服务器请求 UserSig。

  2. 您的服务器根据 SDKAppID 和 UserID 计算 UserSig,计算源码见文档前半部分。

  3. 服务器将计算好的 UserSig 返回给您的 App。

  4. 您的 App 将获得的 UserSig 通过特定 API 传递给 SDK。

  5. SDK 将 SDKAppID + UserID + UserSig 提交给腾讯云服务器进行校验。

  6. 腾讯云校验 UserSig,确认合法性。

  7. 校验通过后,会向 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文件里面引入依赖

        <dependency>
            <groupId>com.github.tencentyun</groupId>
            <artifactId>tls-sig-api-v2</artifactId>
            <version>1.1</version>
        </dependency>

然后直接调用: 

/**
 * @BelongsProject: 
 * @BelongsPackage: 
 * @Author: hef
 * @CreateTime: 2020-06-16 17:33
 * @Description: //UserSig 计算公式,其中 secretkey 为计算 usersig 用的加密密钥
 * <p>
 * usersig = hmacsha256(secretkey, (userid + sdkappid + currtime + expire +
 * base64(userid + sdkappid + currtime + expire)))
 */
public class LiveUtil {
    /**
     * 签名过期时间,建议不要设置的过短
     * <p>
     * 时间单位:秒
     * 默认时间: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);
    }
}

 4、获取登陆配置

前端需要先登陆直播SDK(登陆直播后会将当前直播账号和即时通信 IM 应用绑定起来,所以IM不需要再登陆),登陆时需要的参数需要从后端获取,所以后端需要提供这个接口。

  • 登陆SDK需要参数:
参数类型填写方案
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<String, Object> 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直接返回这些信息给前端就好了。

二、直播中

 1、使用群自定义字段实现直播间上新商品、直播状态改变的消息通知

          既然是带货直播,那么自然涉及商品,比如说介绍中的商品,商品列表等。这个时候是需要在直播间实时变化的,通常的HTTP请求肯定是实现不了,不可能说让前端一直轮询去请求接口看介绍中的商品是否有变化。这个在小程序直播带货SDK里面有解决示例。

在直播带货场景中,主播推荐某款产品时,屏幕下方的商品位需要立即更新为当前产品,并需要通知所有在直播中的用户有新的商品上线。
从技术角度来看,触发商品上新消息有两种方案:

  • 修改群资料
    • 虽然默认的群资料字段已有既定含义,但 IM 还提供了群维度的自定义字段,您可以自定义指定字段含义和读/写权限等。
  • 发送自定义消息
    • IM里面有自定义消息类型,可以通过发送自定义消息通知直播间的用户信息有变更。

我这边采用的是管理员修改群自定义字段的方式实现商品上新消息通知,具体操作步骤如下:

添加群自定义字段

  1. 登录 即时通信 IM 控制台 ,单击目标应用卡片,在左侧导航栏选择【功能配置】>【群自定义字段】。
  2. 在【群自定义字段】页面,单击【添加群维度自定义字段】。
  3. 在弹出的群维度自定义字段对话框中,输入字段名称,设置群组形态及其对应的读写权限。

    说明:

     

    • 字段名称只能由字母、数字以及下划线(_)组成,不能以数字开头,且长度不能超过16个字符。
    • 群自定义字段名称不允许与群成员自定义字段名称一致。
  4. 勾选【我已经知道自定义字段和群组形态添加后,除了形态的读写权限可修改,无法删除。】,单击【确定】保存设置。
    群维度自定义字段配置后大约10分钟左右生效。

1.1、调用服务端API。

请求参数:(注意这个是拼接在URL后面,每个接口都必须要)

参数说明
sdkappid创建应用时即时通信 IM 控制台分配的 SDKAppID
identifier必须为 App 管理员帐号
usersigApp 管理员帐号生成的签名
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;
}

1.1.1、修改群基础资料(修改群自定义字段)

在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<DefinedData> 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;
    }

1.1.2、设置禁言

  • 接口地址 
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<String> 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;
    }

1.1.3、修改群成员资料(设置管理员)

  • 请求地址:
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 对直播频道进行录制,详细请参见 创建录制任务

  • 录制转点播后,文件自动存放于点播平台,故用户需在使用录制功能前,提前开通点播服务并购买相关空间和流量用于存放和播放录制后的视频文件,详细请参见 点播快速入门

基本步骤:
在云直播菜单栏内选择【功能模板】>【录制配置】,单击"+"进行设置。设置完基本信息后,单击【保存】。

规格说明:

  1. 录制视频针对直播原始码率录制,输出格式有 HLS、MP4、FLV 和 ACC 四种,其中 ACC 为纯音频录制。

  2. 录制 MP4 和 FLV 格式最长单个文件时长为120分钟,录制 HLS 格式最长单个文件时长无限制,如果超出则新建文件继续录制。

  3. 单个录制文件保存最大时长均为1080天。

  4. 直播过程中预计在录制结束5分钟左右可获取对应文件。例如,某直播从12:00开始录制,12:30结束录制,则12:35左右可获取12:00 - 12:30的对应片段。

  5. 受限于音视频文件格式(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;

}
  • 处理回调 (Service层代码,Controller写一个接口接收参数,然后传到服务层就好了)
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("签名校验失败");
        }
        ....
        //自己的业务代码
    }

 

四、总结

  1. 到这里就基本完成了后端的功能(暂时的业务上面),如果有什么遗漏的或者后续版本迭代新功能后会补到blog上面。
  2. 总的来算,通过这次集成腾讯移动直播后对直播的流程有了个基本的概念,如果后续再喷到类似的需求,不至于摸不着头脑。
  3. 另外小程序方面来讲,在自定义消息上面花了比较长的时间,就是App端发送消息后小程序接收不到,应该小程序默认发的是text类型的消息,而App默认是发送的自定义消息类型,而且小程序官方SDK DEMO里面是没有封装接收自定义消息的方法,这个需要自己去实现。

 

 

 

 

 

未完待续......

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值