IM腾讯Trtc与vod云点播:实现合流录制并上传,根据参数返回视频地址

发布于:2025-03-27 ⋅ 阅读:(66) ⋅ 点赞:(0)


1.前言简介

腾讯trtc合流模式使用 如果时单流 可在控制台直接设置

1.1 专栏传送门

1.1.1 文档传送门

===> 传送门: api文档-官网地址

2. java基础使用

2.1 准备工作

2.1.1 云控制台获取(密钥和密钥secret)

===> 腾讯云控制台登录 传送门<===
如图所示在这里插入图片描述

2.1.2 找到trtc控制台

===> im控制台 传送门 <===

创建sdkAppId 开通需要的服务
在这里插入图片描述

2.1.3 vod云点播控制台

===> vod云点播控制台地址

  1. 创建视频生成模板
  2. 创建任务流绑定视频模板

--------> 如图所示

在这里插入图片描述

2.2 使用准备的数据进行操作

2.2.0 引入依赖

trtc包和vod包
源码位置: 源码gitee地址

     <!--   # 版本在maven生效需要时间,如获取不到对应的版本,可以调低版本号-->
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java-trtc</artifactId>
            <version>3.1.1218</version>
        </dependency>
      <!--  # 版本在maven生效需要时间,如获取不到对应的版本,可以调低版本号-->
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java-vod</artifactId>
            <version>3.1.1212</version>
        </dependency>

2.2.1 创建TrtcUtils工具类

缺少的包可以忽略 删除都可以

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import com.google.common.base.Objects;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.trtc.v20190722.TrtcClient;
import com.tencentcloudapi.trtc.v20190722.models.*;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * trtc工具类
 * 这里的常量就不大写了
 * @author pzy
 * @version 0.1.1
 */
@Slf4j
public class TrtcUtils {

    /**
     * 密钥key
     */
    private static final String secretId = "";

    /**
     * 密钥secret
     */
    private static final String secretKey = "";

    /**
     * 请求节点
     */
    private static final String trtcEndPoint = "trtc.tencentcloudapi.com";

    /**
     * sdk 的 appid
     */
    public static final String SDKAppID = "";

    //vod-------------------------------------------------------->

    private static final String vodEndPoint = "vod.tencentcloudapi.com";
    /**
     * vod任务模板名称
     */
    public static final String vodTaskTemplateName = "";

    /**
     * vod的subAppId
     */
    public static final Long vodSubAppId = ;

    /**
     * 0. 获取trtc客户端对象(不对外)
     */
    private static TrtcClient getTrtcClient() {
        // 实例化一个client选项,可选的,没有特殊需求可以跳过
        ClientProfile clientProfile = new ClientProfile();

        // 实例化一个http选项,可选的,没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(trtcEndPoint);
        clientProfile.setHttpProfile(httpProfile);

        // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
        // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
        // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
        Credential cred = new Credential(secretId, secretKey);
        return new TrtcClient(cred, "ap-beijing", clientProfile);
    }

    /**
     * 1. 开启TRTC云录制功能
     */
    @SneakyThrows
    public static CreateCloudRecordingResponse sendTrtcRecord(CreateCloudRecordingRequest req) {

        //获取trtc客户端对象
        TrtcClient trtcClient = getTrtcClient();

        // 返回的resp是一个CreateCloudRecordingResponse的实例,与请求对象对应
        CreateCloudRecordingResponse resp = trtcClient.CreateCloudRecording(req);

        // 输出json格式的字符串回包
        log.info("===> 开启TRTC云录制功能-响应信息: {}" + AbstractModel.toJsonString(resp));

        return resp;
    }

    /**
     * 2. 停止TRTC云端录制任务
     */
    @SneakyThrows
    public static Object functionCloudRecording(TrtcCommonReqDto trtcCommonReqDto) {

        String taskId = trtcCommonReqDto.getTaskId();
        Assert.notNull(taskId, () -> new ServiceException("请传递任务id后重试~"));

        // 实例化要请求产品的client对象,clientProfile是可选的
        TrtcClient client = getTrtcClient();

        /* 功能类型 1 停止录制(默认) 2查询录制状态  */
        Long sdkAppId = Long.valueOf(SDKAppID);
        if (Objects.equal(trtcCommonReqDto.getFunctionType(), "1")) {
            DeleteCloudRecordingRequest req = new DeleteCloudRecordingRequest();
            req.setSdkAppId(sdkAppId);
            req.setTaskId(taskId);
            // 返回的resp是一个DeleteCloudRecordingResponse的实例,与请求对象对应
            DeleteCloudRecordingResponse resp = client.DeleteCloudRecording(req);

            // 输出json格式的字符串回包
            log.info("===> 停止TRTC云端录制任务-响应信息: {}" , AbstractModel.toJsonString(resp));

            return resp;
        }

        /* 功能类型 1 停止录制(默认) 2查询录制状态  */
        if (Objects.equal(trtcCommonReqDto.getFunctionType(), "2")) {

            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeCloudRecordingRequest req = new DescribeCloudRecordingRequest();
            req.setSdkAppId(sdkAppId);
            req.setTaskId(taskId);

            // 返回的resp是一个DescribeCloudRecordingResponse的实例,与请求对象对应
            DescribeCloudRecordingResponse resp = client.DescribeCloudRecording(req);
            log.info("===> 查询TRTC云端录制任务状态-响应信息: {}" + AbstractModel.toJsonString(resp));

            return resp;
        }

        throw new ServiceException("抱歉,类型不存在,请重试呦~");
    }

    /**
     * 3. vod云点播地址获取 通过roomId(暂不优化)
     */
    @SneakyThrows
    public static SearchMediaResponse getVodAddressUrl(TrtcCommonReqDto trtcCommonReqDto) {

        Credential cred = new Credential(secretId, secretKey);
        // 实例化一个http选项,可选的,没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(vodEndPoint);
        // 实例化一个client选项,可选的,没有特殊需求可以跳过
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        // 实例化要请求产品的client对象,clientProfile是可选的
        VodClient client = new VodClient(cred, "ap-guangzhou", clientProfile);
        // 实例化一个请求对象,每个接口都会对应一个request对象
        SearchMediaRequest req = new SearchMediaRequest();
        req.setSubAppId(vodSubAppId);

        List<String> roomIds = trtcCommonReqDto.getRoomIds();

        if (CollectionUtils.isEmpty(roomIds)) {
            return new SearchMediaResponse();
        }

        String[] trtcRoomIds1 = roomIds.toArray(new String[0]);
        req.setTrtcRoomIds(trtcRoomIds1);

        // 返回的resp是一个SearchMediaResponse的实例,与请求对象对应
        SearchMediaResponse resp = client.SearchMedia(req);

        // 输出json格式的字符串回包
        log.info("===> 查询Vod云端视频信息: {}", AbstractModel.toJsonString(resp));

        return resp;
    }

    /**
     * 查询vod云点播地址并封装对象 (根据订单和房间号存入)
     * <p>
     * ps: 失败也存,最好异步或者延时调用, 不是立即生成的
     * -> 优化: 增加重试机制,现在默认好用
     */
    public static List<OrderTrtcVideo> getVodUrlByRoomInOrder(TrtcCommonReqDto trtcCommonReqDto) {
        String orderRegisterId = trtcCommonReqDto.getOrderRegisterId();

        SearchMediaResponse searchMediaResponse = TrtcUtils.getVodAddressUrl(trtcCommonReqDto);

        //处理数据
        List<OrderTrtcVideo> orderTrtcVideoList = Lists.newArrayList();

        /*校验: totalCounts是0 则查询失败*/
        MediaInfo[] mediaInfoSet = searchMediaResponse.getMediaInfoSet();
        if (Objects.equal(searchMediaResponse.getTotalCount(), 0L) || mediaInfoSet.length <= 0) {

            log.error("===> 云点播数据查询失败,获取失败~~~");

            //失败了也存记录
            trtcCommonReqDto.getRoomIds().forEach(roomId ->
                    orderTrtcVideoList.add(
                            new OrderTrtcVideo(orderRegisterId, roomId, trtcCommonReqDto.getTaskId(), null, "0")
                    )
            );
        } else {
            log.error("===> 云点播数据查询成功,获取成功~~~");
            for (MediaInfo mediaInfo : mediaInfoSet) {
                MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
                String mediaUrl = basicInfo.getMediaUrl();

                MediaSourceData sourceInfo = basicInfo.getSourceInfo();
                TrtcRecordInfo trtcRecordInfo = sourceInfo.getTrtcRecordInfo();

                orderTrtcVideoList.add(new OrderTrtcVideo(orderRegisterId,
                        trtcRecordInfo.getRoomId(), trtcRecordInfo.getTaskId(), mediaUrl
                ));
            }
        }

        return orderTrtcVideoList;
    }


    public static void main(String[] args) {

        List<String> list = Lists.newArrayList();
        list.add("2807212226");

        getVodAddressUrl(new TrtcCommonReqDto().setRoomIds(list));
    }
}

2.2.2 TrtcReqDTO 录制请求dto

官方提供了请求对象 但是部分方法没有set方法, 调整如此

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.tencentcloudapi.trtc.v20190722.models.MixLayoutParams;
import com.tencentcloudapi.trtc.v20190722.models.MixTranscodeParams;
import com.tencentcloudapi.trtc.v20190722.models.RecordParams;
import com.tencentcloudapi.trtc.v20190722.models.StorageParams;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * trtc请求dto
 * @author pzy
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TrtcReqDTO {

    /**
     * TRTC的[SdkAppId](https://cloud.tencent.com/document/product/647/46351#sdkappid),和录制的房间所对应的SdkAppId相同。
     */
    private Long SdkAppId;

    /**
     * TRTC的[RoomId](https://cloud.tencent.com/document/product/647/46351#roomid),录制的TRTC房间所对应的RoomId。
     注:房间号类型默认为整型,若房间号类型为字符串,请通过RoomIdType指定。

     */
    private String RoomId;

    /**
     * 录制机器人的UserId,用于进房发起录制任务。
     【*注意】这个UserId不能与当前房间内的主播观众[UserId](https://cloud.tencent.com/document/product/647/46351#userid)重复。如果一个房间发起多个录制任务时,机器人的userid也不能相互重复,否则会中断前一个录制任务。建议可以把房间ID作为UserId的标识的一部分,即录制机器人UserId在房间内唯一。
     */
    private String UserId;

    /**
     * 录制机器人UserId对应的校验签名,即UserId和UserSig相当于录制机器人进房的登录密码,具体计算方法请参考TRTC计算[UserSig](https://cloud.tencent.com/document/product/647/45910#UserSig)的方案。
     */
    private String UserSig;

    /**
     * 云端录制控制参数。
     */
    private RecordParams RecordParams;

    /**
     * 云端录制文件上传到云存储的参数(不支持同时设置云点播VOD和对象存储COS)
     */
    private StorageParams StorageParams;

    /**
     * TRTC房间号的类型。
     【*注意】必须和录制的房间所对应的RoomId类型相同:
     0: 字符串类型的RoomId
     1: 32位整型的RoomId(默认)
     */
    private Long RoomIdType;

    /**
     * 合流的转码参数,录制模式为合流的时候可以设置。
     */
    private MixTranscodeParams MixTranscodeParams;

    /**
     * 合流的布局参数,录制模式为合流的时候可以设置。
     */
    private MixLayoutParams MixLayoutParams;

    /**
     * 接口可以调用的时效性,从成功开启录制并获得任务ID后开始计算,超时后无法调用查询、更新和停止等接口,但是录制任务不会停止。 参数的单位是小时,默认72小时(3天),最大可设置720小时(30天),最小设置6小时。举例说明:如果不设置该参数,那么开始录制成功后,查询、更新和停止录制的调用时效为72个小时。
     */
    private Long ResourceExpiredHour;

    /**
     * TRTC房间权限加密串,只有在TRTC控制台启用了高级权限控制的时候需要携带,在TRTC控制台如果开启高级权限控制后,TRTC 的后台服务系统会校验一个叫做 [PrivateMapKey] 的“权限票据”,权限票据中包含了一个加密后的 RoomId 和一个加密后的“权限位列表”。由于 PrivateMapKey 中包含 RoomId,所以只提供了 UserSig 没有提供 PrivateMapKey 时,并不能进入指定的房间。
     */
    private String PrivateMapKey;

}

2.2.3 TrtcCommonReqDto 请求dto

import com.google.common.collect.Lists;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.List;

/**
 * trtc停止请求dto
 * @author pzy
 * @version 0.1.1
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TrtcCommonReqDto {

    /**
     * 录制任务的唯一Id,在启动录制成功后会返回。
     */
    private String TaskId;

    /**
     * 功能类型 1 停止录制(默认) 2查询录制状态
     */
    private String functionType = "1";

    /**
     * roomIds房间号
     */
    private List<String> roomIds = Lists.newLinkedList();


    /** 挂号订单id  */
    private String orderRegisterId;


}

2.2.4 OrderTrtcVideo 数据库存储实体类

视频对应订单号对应房间号 对应taskId 进行存储
如果失败可以重试(也存数据库)

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;


/**
 * 订单trtc视频对象 order_trtc_video
 *
 * @author pzy
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
//@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@TableName("order_trtc_video")
@Schema(title = "订单trtc视频对象 order_trtc_video")
public class OrderTrtcVideo {

    private static final long serialVersionUID=1L;

    @Schema(description = "主键id")
    @TableId(value = "id")
    private Long id;

    @Schema(description = "订单id")
    private String orderRegisterId;

    @Schema(description = "房间id")
    private String roomId;

    @Schema(description = "任务id")
    private String taskId;

    @Schema(description = "云点播视频url")
    private String vodVideoUrl;

    @Schema(description = "云点播状态(0失败 1成功)")
    private String vodStatus;

    public OrderTrtcVideo(String orderRegisterId, String roomId, String taskId, String vodVideoUrl) {
        this.orderRegisterId = orderRegisterId;
        this.roomId = roomId;
        this.taskId = taskId;
        this.vodVideoUrl = vodVideoUrl;
    }


    public OrderTrtcVideo(String orderRegisterId, String roomId, String taskId, String vodVideoUrl, String vodStatus) {
        this.orderRegisterId = orderRegisterId;
        this.roomId = roomId;
        this.taskId = taskId;
        this.vodVideoUrl = vodVideoUrl;
        this.vodStatus = vodStatus;
    }
}

2.2.5 获取用户usersig(旧版)

:> 依赖如下

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

import com.tencentyun.TLSSigAPIv2;

public static String getUserSig(Long userId){
TLSSigAPIv2 api = new TLSSigAPIv2(sdkAppId, secretKey);
   return api.genSig(userId, expire);
}

2.3 业务使用方式

2.3.0 json传递数据

{
    "userId": "123456",
    "userSig": "eJwtzF0LgjAYhuH-stNC5tpHCh0tsKgOIiM9nGzFS8xNE*mD-ntLPXyuG54PyvenqDctShGJMJoPG7SpO7jCwDFZUMan8tB35T1olMYcYyzIkvOxmKeH1gRnjJGQRu3A-k1QQgRNsJhe4BaOE36*OCurfrdWjUxw5qQtsk3JlXaMinxW*uL9OjT1cbtC3x*78zBl",
    "roomId": "123",
    "mixLayoutParams": {
        "backGroundColor": "#FF0000",
        "backgroundImageRenderMode": 1,
        "maxResolutionUserId": "user_1",
        "mediaId": 0,
        "mixLayoutList": [
            {
                "alpha": 100,
                "height": 100,
                "imageLayer": 2,
                "left": 100,
                "mediaId": 1,
                "renderMode": 1,
                "top": 100,
                "userId": "user_1",
                "width": 100
            }
        ],
        "mixLayoutMode": 3,
        "renderMode": 1
    },
    "recordParams": {
        "maxIdleTime": 60,
        "maxMediaFileDuration": 1440,
        "recordMode": 2,
        "streamType": 0
    },
    "storageParams": {
        "cloudVod": {
            "tencentVod": {
                "classId": 0,
                "mediaType": 0,
                "sessionContext": "任务流上下文,任务完成回调时透传",
                "sourceContext": "上传上下文,上传完成回调时透传"
            }
        }
    }
}

2.3.1 创建service方法

R是统一返回值 可根据自己业务自行修改

/**
 * trtc
 *
 * @author pzy
 * @version 0.1.1
 */
public interface TrtcService {

    R createCloudRecording(TrtcReqDTO trtcReqDTO);

    R functionCloudRecording(TrtcCommonReqDto trtcCommonReqDto);

    R getVodAddressUrl(TrtcCommonReqDto trtcCommonReqDto);
}

2.3.2 TrtcServiceImpl 实现类

依赖包没用特殊的 本地环境没有百度搜就行

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.tencentcloudapi.trtc.v20190722.models.*;
import com.tencentcloudapi.vod.v20180717.models.*;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class TrtcServiceImpl implements TrtcService {


    @DubboReference
    private final A a;

    /**
     * 创建TRTC云录制功能(数据处理)
     */
    @Override
    public R createCloudRecording(TrtcReqDTO trtcReqDTO) {

        log.info("TRTC 前端入参请求数据===> {}", JSON.toJSONString(trtcReqDTO));

        //不得使用其他数据业务
        trtcReqDTO.setSdkAppId(Long.valueOf(TrtcUtils.SDKAppID));
        trtcReqDTO.setResourceExpiredHour(72L);

        /*如果是空 默认合流模式 使用默认数据*/
        RecordParams recordParams = trtcReqDTO.getRecordParams() != null ? trtcReqDTO.getRecordParams() : new RecordParams();
        recordParams.setRecordMode(2L);
        recordParams.setMaxIdleTime(30L);
        recordParams.setStreamType(0L);
        recordParams.setMaxMediaFileDuration(1440L);
        trtcReqDTO.setRecordParams(recordParams);


        //云点播 自动上传模板默认值-------------------->
        StorageParams storageParams = trtcReqDTO.getStorageParams() != null ? trtcReqDTO.getStorageParams() : new StorageParams();
        CloudVod cloudVod = storageParams.getCloudVod() != null ? storageParams.getCloudVod() : new CloudVod();

        TencentVod tencentVod = cloudVod.getTencentVod() != null ? cloudVod.getTencentVod() : new TencentVod();
        tencentVod.setSubAppId(TrtcUtils.vodSubAppId);
        tencentVod.setProcedure(TrtcUtils.vodTaskTemplateName);
        tencentVod.setExpireTime(0L);
        tencentVod.setClassId(0L);
        tencentVod.setMediaType(0L);

        cloudVod.setTencentVod(tencentVod);
        storageParams.setCloudVod(cloudVod);
        trtcReqDTO.setStorageParams(storageParams);

        //--------------------------------------------->

        log.info("TRTC 后端固定参数覆盖后请求入参数据===> {}", JSON.toJSONString(trtcReqDTO));
        //数据赋值
        CreateCloudRecordingRequest req = new CreateCloudRecordingRequest();
        BeanUtil.copyProperties(trtcReqDTO, req);

        log.info("TRTC 标准参数<官方要求>请求格式===> {}", JSON.toJSONString(req));

        return R.ok(TrtcUtils.sendTrtcRecord(req));
    }

    @Override
    public R functionCloudRecording(TrtcCommonReqDto trtcCommonReqDto) {

        Object resp = TrtcUtils.functionCloudRecording(trtcCommonReqDto);

//        /*校验: 如果是停止调用*/
        if (Objects.equal(trtcCommonReqDto.getFunctionType(), "1")) {

            // 添加trtc云点播视频url
            addOrderTrtcVodUrl(trtcCommonReqDto);
        }

        return R.ok(resp);
    }

    /**
     * 添加trtc云点播视频url
     */
    @SneakyThrows
    @Async
    protected void addOrderTrtcVodUrl(TrtcCommonReqDto trtcCommonReqDto) {

        Thread.sleep(15000);//模拟延时队列

        List<OrderTrtcVideo> orderTrtcVideoList = TrtcUtils.getVodUrlByRoomInOrder(trtcCommonReqDto);

        //添加Trtc视频
        log.info("===> 批量添加trtc视频记录中 =====> ");
		//TODO
    }


    @Override
    public R getVodAddressUrl(TrtcCommonReqDto trtcCommonReqDto) {

        SearchMediaResponse resp = TrtcUtils.getVodAddressUrl(trtcCommonReqDto);

        return R.ok(resp);
    }
}

2.3.4 上传后视频效果

在这里插入图片描述

3. 文章的总结与预告

3.1 本文总结

trtc录制并上传到vod , 录制结束时根据房间号进行查询 存入数据库 设置重试机制

3.2 代码指正

如有遗漏, 代码错误,流程错误,更优方案等 欢迎评论区指正 谢谢



@author: pingzhuyan
@description: ok
@year: 2024


网站公告

今日签到

点亮在社区的每一天
去签到