1.前言简介
腾讯trtc
合流模式
使用 如果时单流 可在控制台直接设置
1.1 专栏传送门
1.1.1 文档传送门
2. java基础使用
2.1 准备工作
2.1.1 云控制台获取(密钥和密钥secret)
2.1.2 找到trtc控制台
创建sdkAppId 开通需要的服务
2.1.3 vod云点播控制台
- 创建视频生成模板
- 创建任务流绑定视频模板
--------> 如图所示
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