Java集成腾讯云音视频录制功能
为什么要实现音视频录制功能
因为我们做的是一个医院的项目,医生和患者可能进行视频通话和语音通话,为了保证通话的质量以及后续的问题,
我们就需要进行音视频录制,以便后续的问题解决
为什么选择使用腾讯云实现音视频录制功能
因为我们做的是微信小程序
- 腾讯云是基于腾讯的技术支撑,大厂技术比较稳定;
- 微信和腾讯云都是腾讯的产品,两者兼容性更好;
- 腾讯云提供的有uni-app的例子,参考资料更充分,可以基于demo和现有系统集成。
注意:userId需要保持在整个房间是唯一的,不管是录制还是音视频通话,进入房间的userId必须唯一,不然就会造成录制不了的情况
1.导入依赖
因为我们是Gradle项目,与我们之前Maven项目导入依赖的方式不一样,Gradle项目导入依赖的方式如下所示
implementation 'com.tencentcloudapi:tencentcloud-sdk-java-common:3.1.691'
implementation 'com.tencentcloudapi:tencentcloud-sdk-java-trtc:3.1.691'
implementation 'com.tencentcloudapi:tencentcloud-sdk-java-vod:3.1.704'
2.实现云端录制
流程图
2.1 添加配置文件
访问秘钥和key
SDKAppID和SDKSecretKey
在application.properties添加一下配置
secretid=访问秘钥ID
secretkey=访问秘钥Key
expiretime=过期时间
sdkappid=SDKAppID
key=SDKSecretKey
2.2 回调地址设置
打开音视频控制台 --》应用管理 --》回调配置
跟着下面配置就可以了
2.3 签名 Sign
/**
* @param key 回调秘钥
* @param body 入参
* @return 签名 Sign 计算公式中 key 为计算签名 Sign 用的加密密钥。
* @throws Exception
*/
private static String getResultSign(String key, String body) throws Exception {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
hmacSha256.init(secret_key);
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(body.getBytes()));
}
2.4 实现房间回调
房间回调API文档地址:https://cloud.tencent.com/document/product/647/51586
使用房间回调事件可以监听进入房间和退出房间时间,分别对应开始录制和退出录制(当我们进入房间开始录制,退出房间就退出录制)
入参是body加上请求头的方式,详细信息大家可以查阅文档,这里我就不一一赘述了
代码实现
//# 功能:第三方回调sign校验
//# 参数:
//# key:控制台配置的密钥key
//# body:腾讯云回调返回的body体
//# sign:腾讯云回调返回的签名值sign
//# 返回值:
//# Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info
//# Info:成功/失败信息
@ApiOperation(value = "音视频房间回调接口")
@PostMapping("/roomCallback")
public void roomCallback(@RequestBody String body, HttpServletRequest request) throws Exception {
String key = "key";
String sdkAppId = request.getHeader("SdkAppId");
String sign = request.getHeader("Sign");
String resultSign = getResultSign(key,body);
ValueOperations ops = redisTemplate.opsForValue();
if (resultSign.equals(sign)) {
JSONObject jsonObject = (JSONObject) JSON.parse(body);
Integer eventType = jsonObject.getInteger("EventType"); // 事件类型
String eventInfo = jsonObject.getString("EventInfo"); // 事件信息
JSONObject jsonObject1 = (JSONObject) JSON.parse(eventInfo);
String roomId = jsonObject1.getString("RoomId"); // 房间号
String userId = jsonObject1.getString("UserId"); // 用户ID
if (!StringUtils.isEmpty(userId)) {
// 执行业务逻辑,判断是医生端还是患者端进入房间,查询出相关的问诊订单
}
// 创建房间事件(创建房间事件只会进入一次房间回调接口,可以防止重复两次调用录制接口)
if (eventType == 101) {
logger.debug("{'Status': 'OK', 'Info': '校验通过'}");
String openCloudRecording = openCloudRecording(userId, roomId); // 开启录制
JSONObject jsonObject2 = (JSONObject) JSON.parse(openCloudRecording);
String taskId = jsonObject2.getString("taskId"); // 任务ID
if (!StringUtils.isEmpty(taskId)) {
// 执行相关业务
}
} else if (eventType == 104) { // 104 退出房间事件
logger.debug("{'Status': 'FAIL', 'Info': '退出房间'}");
closeDeleteCloudRecording("录制任务ID"); // 退出录制
}
} else {
logger.debug("{'Status': 'FAIL', 'Info': '校验失败'}");
}
logger.debug("腾讯云测试成功");
}
2.5 开启音视频录制
开启云端录制API文档地址:https://cloud.tencent.com/document/api/647/73786
当房间回调事件监听到进入房间事件我们就开启语音(视频)通话了,就会调用开启音视频录制接口
输入参数我就不做赘述了(输出参数开启云端录制和关闭云端录制都是一致的),详细信息请看API文档
代码实现
/**
* 开启腾讯云录制
* @param userId
* @param roomId
* @return
* @throws Exception
*/
private String openCloudRecording(String userId, String roomId) throws Exception {
try {
//创建文件对象
Properties properties = new Properties();
//加载文件获取数据 文件带后缀
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream
("application.properties"));
//根据key来获取value
String secretId = properties.getProperty("secretid");
String secretKey = properties.getProperty("secretkey");
String ExpireTime = properties.getProperty("expiretime");
String sdkAppId = properties.getProperty("sdkappid");
String key = properties.getProperty("key");
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("trtc.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
TrtcClient client = new TrtcClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
CreateCloudRecordingRequest req = new CreateCloudRecordingRequest();
// 实例化一个请求对象,每个接口都会对应一个request对象
CreateCloudRecordingRequest req = new CreateCloudRecordingRequest();
req.setSdkAppId(Long.valueOf(sdkAppId)); // SdkAppId – TRTC的[SdkAppId](https://cloud.tencent.com/document/product/647/46351#sdkappid),和录制的房间所对应的SdkAppId相同
req.setRoomId(roomId); // RoomId – TRTC的[RoomId](https://cloud.tencent.com/document/product/647/46351#roomid),录制的TRTC房间所对应的RoomId
req.setRoomIdType(1L);
/**
* 录制机器人用于进入TRTC房间拉流的[UserId](https://cloud.tencent.com/document/product/647/46351#userid),
* 注意这个UserId不能与其他TRTC房间内的主播或者其他录制任务等已经使用的UserId重复,建议可以把房间ID作为userId的标识的一部分,
* 即录制机器人进入房间的userid应保证独立且唯一
*/
req.setUserId(userId);
TLSSigAPIv2 api = new TLSSigAPIv2(Long.valueOf(sdkAppId), key);
String userSign = api.genUserSig(userId, Long.valueOf(ExpireTime));
req.setUserSig(userSign); // 录制机器人用于进入TRTC房间拉流的用户签名,当前 UserId 对应的验证签名,相当于登录密码
RecordParams recordParams = new RecordParams();
// 单流录制
recordParams.setMaxIdleTime(30L); // 30 秒内房间里面没有主播,自动停止录制
recordParams.setStreamType(0L); // 0:录制音频+视频流(默认); 1:仅录制音频流; 2:仅录制视频流
recordParams.setRecordMode(1L); // 1:单流录制,分别录制房间的订阅UserId的音频和视频,将录制文件上传至云存储; 2:混流录制,将房间内订阅UserId的音视频混录成一个音视频文件,将录制文件上传至云存储;
recordParams.setOutputFormat(0L); // 0:(默认)输出文件为hls格式。1:输出文件格式为hls+mp4。2:输出文件格式为hls+aac
StorageParams storageParams1 = new StorageParams();
CloudVod cloudVod1 = new CloudVod();
TencentVod tencentVod1 = new TencentVod();
cloudVod1.setTencentVod(tencentVod1); // 腾讯云点播相关参数。
storageParams1.setCloudVod(cloudVod1); // 必填】腾讯云云点播的账号信息,目前仅支持存储至腾讯云点播VOD。
req.setRecordParams(recordParams); // 云端录制控制参数
req.setStorageParams(storageParams1); // 云端录制文件上传到云存储的参数(目前只支持使用腾讯云点播作为存储)
// 返回的resp是一个CreateCloudRecordingResponse的实例,与请求对象对应
CreateCloudRecordingResponse resp = client.CreateCloudRecording(req);
// 输出json格式的字符串回包
String s = JSON.toJSONString(resp);
return s;
} catch (TencentCloudSDKException | IOException e) {
return e.toString();
} catch (Exception e) {
return e.toString();
}
}
2.6 关闭音视频录制
关闭云端录制API文档地址:https://cloud.tencent.com/document/api/647/73785
当房间回调事件监听到退出房间事件我们就退出语音(视频)通话了,就会调用关闭音视频录制接口
输入参数我就不做赘述了(输出参数开启云端录制和关闭云端录制都是一致的),详细信息请看API文档
代码实现
/**
* 关闭腾讯云录制
* @param taskId 任务ID
* @return
*/
public String closeDeleteCloudRecording(String taskId){
try {
if (taskId != null) {
taskId = taskId.replaceAll(" ", "+");
}
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
//创建文件对象
Properties properties = new Properties();
//加载文件获取数据 文件带后缀
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream
("application.properties"));
//根据key来获取value
String SecretId = properties.getProperty("secretid");
String SecretKey = properties.getProperty("secretkey");
String sdkAppId = properties.getProperty("sdkappid");
Credential cred = new Credential(SecretId, SecretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("trtc.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
TrtcClient client = new TrtcClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
DeleteCloudRecordingRequest req = new DeleteCloudRecordingRequest();
req.setSdkAppId(Long.valueOf(sdkAppId)); // SdkAppId – TRTC的SDKAppId,和录制的房间所对应的SDKAppId相同
req.setTaskId(taskId); // TaskId – 录制任务的唯一Id,在启动录制成功后会返回
// 返回的resp是一个DeleteCloudRecordingResponse的实例,与请求对象对应
DeleteCloudRecordingResponse resp = client.DeleteCloudRecording(req);
// 输出json格式的字符串回包
String s = JSON.toJSONString(resp);
return s;
} catch (TencentCloudSDKException | IOException e) {
return e.toString();
}
}
2.7 云端录制回调
云端录制回调API文档地址:https://cloud.tencent.com/document/product/647/81113
当云端录制回调监听到录制上传成功事件,就会调用下载音视频接口,我们根据我们给我们数据库存储地址的表中标记,
未下载的进行下载。
输入(出)参数我就不做赘述了详细信息请看API文档
//# 功能:第三方回调sign校验
//# 参数:
//# key:控制台配置的密钥key
//# body:腾讯云回调返回的body体
//# sign:腾讯云回调返回的签名值sign
//# 返回值:
//# Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info
//# Info:成功/失败信息
@ApiOperation(value = "音视频录制回调接口")
@PostMapping("/recordingCallback")
public void recordingCallback(@RequestBody String body, HttpServletRequest request) throws Exception {
String key = "key";
String sdkAppId = request.getHeader("SdkAppId");
String sign = request.getHeader("Sign");
logger.info("body:"+body);
logger.info("sdkAppId:"+sdkAppId);
logger.info("sign:"+sign);
String resultSign = getResultSign(key,body);
logger.info("resultSign:"+resultSign);
if (resultSign.equals(sign)) {
JSONObject jsonObject = (JSONObject) JSON.parse(body);
Integer eventType = jsonObject.getInteger("EventType"); // 事件类型
String eventInfo = jsonObject.getString("EventInfo"); // 事件信息
JSONObject jsonObject1 = (JSONObject) JSON.parse(eventInfo);
String taskId = jsonObject1.getString("TaskId"); // 任务ID
String payload = jsonObject1.getString("Payload"); // 根据不同事件类型定义不同
JSONObject jsonObject2 = (JSONObject) JSON.parse(payload);
String tencentVod = jsonObject2.getString("TencentVod"); // 点播平台信息
JSONObject jsonObject3 = (JSONObject) JSON.parse(tencentVod);
if (eventType == 311) { // 录制视频上传成功
String fileId = jsonObject3.getString("FileId"); // 点播平台的唯一 ID
String videoUrl = jsonObject3.getString("VideoUrl"); // 点播平台的播放地址
// 进行相关业务逻辑处理
// 311事件证明视频已成功上传到云点播,
// 建立相关的数据库用来存储音视频录制地址并和相关的业务ID绑定,用于后续下载
/**
* 使用线程池进行音视频下载,控制最大并发数,防止资源抢占导致服务崩溃
*/
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(() -> {
try {
downloadVideo(fileIds);
TimeUnit.SECONDS.sleep(1000 * 60 * 30); // 等待30分钟
} catch (Exception e) {
e.printStackTrace();
}
});
}
} else {
logger.debug("{'Status': 'FAIL', 'Info': '校验失败'}");
}
logger.debug("腾讯云测试成功");
}
2.8 音视频下载
2.8.1 音视频列表查询接口
为了保证数据的安全性,我们需要把云点播的音视频存储到医院的服务器上面,然后将云点播上面的音视频删除掉,然后将存储地址表中的视频路径改成服务器的路径。
音视频下载API文档地址:https://cloud.tencent.com/document/product/266/84093
https://cloud.tencent.com/document/product/266/31763
通过fileIds查询出在云点播上面存储的音视频列表,然后下载,再删除
输入(出)参数我就不做赘述了详细信息请看API文档
/**
* 查询出音视频集合,并下载,在将云点播上面的音视频删除
* @param fileIds 点播平台唯一ID集合
* @throws Exception
*/
private void downloadVideo(String [] fileIds) throws Exception {
try{
//创建文件对象
Properties properties = new Properties();
//加载文件获取数据 文件带后缀
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream
("application.properties"));
//根据key来获取value
String secretId = properties.getProperty("secretid");
String secretKey = properties.getProperty("secretkey");
// 实例化一个认证对象,入参需要传入腾讯云账户 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);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("vod.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
req.setFileIds(fileIds);
String[] basicInfos = {"basicInfo"};
req.setFilters(basicInfos);
// 返回的resp是一个DescribeMediaInfosResponse的实例,与请求对象对应
DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
// 输出json格式的字符串回包
logger.info(DescribeMediaInfosResponse.toJsonString(resp));
String json = DescribeMediaInfosResponse.toJsonString(resp);
JSONObject jsonObject = (JSONObject) JSON.parse(json);
JSONArray jsonArray = jsonObject.getJSONArray("MediaInfoSet"); // 媒体文件信息列表。
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObject1 = jsonArray.getJSONObject(i);
String fileId = jsonObject1.getString("FileId"); // 点播平台的唯一 ID
String basicInfo = jsonObject1.getString("BasicInfo"); // 基础信息
JSONObject jsonObject2 = (JSONObject) JSON.parse(basicInfo);
String mediaUrl = jsonObject2.getString("MediaUrl"); // 文件地址
String downPath = downloadImage(mediaUrl); // 下载音视频(返回本地下载地址)
// 将未下载的音视频列表查询出来,进行下载到服务器上面,并更新数据库数据
// 业务逻辑处理
delVideo(fileId); // 删除音视频
logger.info(downPath); // 本地地址
}
logger.info("下载音视频成功");
} catch (TencentCloudSDKException e) {
logger.debug(e.toString());
} catch (IOException e) {
e.printStackTrace();
}
logger.debug("腾讯云测试成功");
}
2.8.2 音视频下载接口
通过fileIds查询出在云点播上面存储的音视频列表,然后下载,再删除
代码
/**
* 将视频下载到本地
* @param fileUrl 视频路径
* @return
*/
public String downloadImage(String fileUrl) {
long l = 0L;
String path = null;
String staticAndMksDir = null;
if (fileUrl != null) {
//下载时文件名称
String fileName = fileUrl.substring(fileUrl.lastIndexOf("."));
try {
String dataStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uuidName = UUID.randomUUID().toString();
path = "resources/images/"+dataStr+"/"+uuidName+fileName;
staticAndMksDir = Paths.get(ResourceUtils.getURL("classpath:").getPath(),"resources", "images",dataStr).toFile().toString();
HttpUtil.downloadFile(fileUrl, staticAndMksDir + File.separator + uuidName + fileName);
} catch (Exception e) {
e.printStackTrace();
} finally {
logger.debug("下载成功");
}
}
log.info(System.currentTimeMillis()-l);
return path;
}
2.9 删除音视频
为了保证数据的安全性,我们需要把云点播的音视频存储到医院的服务器上面,然后将云点播上面的音视频删除掉,然后将存储地址表中的视频路径改成服务器的路径。
音视频删除API文档地址:https://cloud.tencent.com/document/product/266/31764
输入(出)参数我就不做赘述了详细信息请看API文档
/**
* 删除音视频
* @param fileId 通过点播平台唯一ID删除掉音视频
* @throws Exception
*/
private void delVideo(String fileId) throws Exception {
try{
//创建文件对象
Properties properties = new Properties();
//加载文件获取数据 文件带后缀
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream
("application.properties"));
//根据key来获取value
String secretId = properties.getProperty("secretid");
String secretKey = properties.getProperty("secretkey");
// 实例化一个认证对象,入参需要传入腾讯云账户 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);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("vod.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(cred, "", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
DeleteMediaRequest req = new DeleteMediaRequest();
req.setFileId(fileId);
// 返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
DeleteMediaResponse resp = client.DeleteMedia(req);
// 输出json格式的字符串回包
// 输出json格式的字符串回包
logger.info("删除音视频成功");
logger.info("删除音视频:", DeleteMediaResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
logger.debug(e.toString());
}
}
至此我们集成腾讯音视频就结束了