腾讯云点播(Video on Demand,VOD)基于腾讯多年技术积累与基础设施建设,为有音视频应用相关需求的客户提供包括音视频存储管理、音视频转码处理、音视频加速播放和音视频通信服务的一站式解决方案。
腾讯云vod 开发文档
pom.xml
<!-- tencentcloud -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.2</version>
</dependency>
获取视频上传签名
@PostMapping("/getSignature")
@ApiOperation("获取视频上传签名")
public Result getVodSignature() {
VodSignatureUtil sign = videoUtil.getVodSignature();
String signature = null;
try {
signature = sign.getUploadSignature();
} catch (Exception e) {
e.printStackTrace();
return Result.error(ResultEnum.SERVICE_UNAVAILABLE, "获取签名失败");
}
return Result.success(signature);
}
超级播放器获取视频播放签名
@GetMapping("/playSignature")
@ApiOperation("超级播放器获取视频播放签名")
public Result playSignature(@RequestParam("fileId") String fileId){
return Result.success(videoUtil.videoToken(fileId));
}
腾讯云回调填值
@PostMapping("/callback")
@ApiOperation(value = "vod回调", hidden = true)
public void callback(@RequestBody String jsonObject){
log.info("video callback log:" + jsonObject);
JsonNode readTree = null;
try {
readTree = jsonMapper.readTree(jsonObject);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
String eventType = readTree.get("EventType").asText();
if ("ProcedureStateChanged".equals(eventType)){
try{
JsonNode changeEvent = readTree.get("ProcedureStateChangeEvent");
TencentVideoCastVo castVos = null;
try {
castVos = jsonMapper.readValue(changeEvent.toString(), TencentVideoCastVo.class);
} catch (JsonProcessingException e) {
log.error("callback json处理异常:{}" ,e.getMessage());
throw new BusinessException(ResultEnum.SERVICE_UNAVAILABLE);
}
if (!"SUCCESS".equals(castVos.getMessage())){
throw new BusinessException(ResultEnum.SERVICE_UNAVAILABLE.getCode(),"腾讯云点播转码出现问题,请及时解决。");
}
// 转码成功后将数据保存在数据库中
log.info("=========转码成功后将数据保存在数据库中===================");
//---
// 将视频发布到微信小程序
VodClient client = videoUtil.getVodClient();
WeChatMiniProgramPublishRequest req = new WeChatMiniProgramPublishRequest();
req.setFileId(castVos.getFileId());
req.setSubAppId(tencentYunProperties.getVod().getAppId());
// 选择转码后的视频发布到微信小程序
req.setSourceDefinition(tencentYunProperties.getVod().getSourceDefinition());
WeChatMiniProgramPublishResponse resp = client.WeChatMiniProgramPublish(req);
log.info("log,视频发布微信小程序:{}",WeChatMiniProgramPublishResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.error(e.toString());
}
}
// 视频发布到小程序 回调
if ("WechatMiniProgramPublishComplete".equals(eventType)){
JsonNode miniProgramPublishEvent = readTree.get("WechatMiniProgramPublishEvent");
String fileId = miniProgramPublishEvent.get("FileId").asText();
if ("SUCCESS".equals(miniProgramPublishEvent.get("Message").asText())){
log.info("视频Id为:{} -->已发布到小程序",fileId);
}
}
}
工具类
package com.also.common.util;
import com.also.common.exception.BusinessException;
import com.also.common.properties.TencentYunProperties;
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.cvm.v20170312.CvmClient;
import com.tencentcloudapi.cvm.v20170312.models.DescribeRegionsRequest;
import com.tencentcloudapi.cvm.v20170312.models.DescribeRegionsResponse;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaRequest;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaResponse;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* @author Joe
*/
@Component
@Slf4j
public class VideoUtil {
@Resource
private TencentYunProperties tencentYunProperties;
private static Map<String, Object> JWT_HEADER = new HashMap<>();
static {
JWT_HEADER.put("typ", "JWT");
JWT_HEADER.put("alg", "HS256");
}
/**
* 获取视频上传签名
* @return
*/
public VodSignatureUtil getVodSignature(){
VodSignatureUtil sign = new VodSignatureUtil();
// 设置 App 的云 API 密钥
sign.setSecretId(tencentYunProperties.getSecretId());
sign.setSecretKey(tencentYunProperties.getSecretKey());
sign.setVodSubAppId(tencentYunProperties.getVod().getAppId());
sign.setCurrentTime(System.currentTimeMillis() / 1000);
sign.setRandom(new Random().nextInt(Integer.MAX_VALUE));
sign.setProcedure(tencentYunProperties.getVod().getProcedure());
// 签名有效期:2天
sign.setSignValidDuration(3600 * 24 * 2);
return sign;
}
/**
* vod视频播放token
* @param fileId
* @return
*/
public String videoToken(String fileId) {
LocalDateTime now = LocalDateTime.now();
long expireTimeStamp = now.plusHours(24L).toEpochSecond(ZoneOffset.of("+8"));
Map<String, Object> claims = new HashMap<>();
claims.put("appId", tencentYunProperties.getVod().getAppId());
claims.put("fileId", fileId);
claims.put("currentTimeStamp", now.toEpochSecond(ZoneOffset.of("+8")));
claims.put("expireTimeStamp", expireTimeStamp);
claims.put("pcfg", "basicDrmPreset");
Map<String, Object> urlAccessInfo = new HashMap<>();
urlAccessInfo.put("t", Long.toHexString(expireTimeStamp));
claims.put("urlAccessInfo", urlAccessInfo);
// 加密生成jwt token
return Jwts.builder()
.setHeader(JWT_HEADER)
.setClaims(claims)
.setExpiration(new Date(expireTimeStamp * 1000))
.signWith(SignatureAlgorithm.HS256, new SecretKeySpec(tencentYunProperties.getVod().getKey().getBytes(), 0, tencentYunProperties.getVod().getKey().getBytes().length, "HS256"))
.compact();
}
public void uploadVideo(){
try{
Credential cred = new Credential(tencentYunProperties.getSecretId(), tencentYunProperties.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("cvm.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
CvmClient client = new CvmClient(cred, "", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
DescribeRegionsRequest req = new DescribeRegionsRequest();
// 返回的resp是一个DescribeRegionsResponse的实例,与请求对象对应
DescribeRegionsResponse resp = client.DescribeRegions(req);
// 输出json格式的字符串回包
log.info(DescribeRegionsResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.error(e.toString());
throw new BusinessException(e.getMessage());
}
}
public void removeTencentCloudVideo(String fileId) {
try {
VodClient client = getVodClient();
// 实例化一个请求对象,每个接口都会对应一个request对象
DeleteMediaRequest req = new DeleteMediaRequest();
req.setFileId(fileId);
// 返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
DeleteMediaResponse resp = client.DeleteMedia(req);
// 输出json格式的字符串回包
log.info(DeleteMediaResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.error(e.toString());
throw new BusinessException(e.getMessage());
}
}
public VodClient getVodClient() {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
Credential cred = new Credential(tencentYunProperties.getSecretId(), tencentYunProperties.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("vod.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
return new VodClient(cred, "", clientProfile);
}
}
腾讯云vod签名
package com.also.common.util;
import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* @author Joe
* @date 2023/08/03 10:42
* 腾讯云vod签名
* @see <a href="https://cloud.tencent.com/document/product/266/10638">官方示例 腾讯云vod上传视频签名生成</a>
*/
public class VodSignatureUtil {
private String secretId;
private String secretKey;
private long currentTime;
private int random;
private int signValidDuration;
private String procedure;
private long vodSubAppId;
/**
* 签名算法
*/
private static final String HMAC_ALGORITHM = "HmacSHA1";
private static final String CONTENT_CHARSET = "UTF-8";
public static byte[] byteMerger(byte[] byte1, byte[] byte2) {
byte[] byte3 = new byte[byte1.length + byte2.length];
System.arraycopy(byte1, 0, byte3, 0, byte1.length);
System.arraycopy(byte2, 0, byte3, byte1.length, byte2.length);
return byte3;
}
/**
* 获取签名
* @return strSign
* @throws Exception
*/
public String getUploadSignature() throws Exception {
String strSign = "";
String contextStr = "";
// 生成原始参数字符串
long endTime = (currentTime + signValidDuration);
contextStr += "secretId=" + java.net.URLEncoder.encode(secretId, "utf8");
contextStr += "¤tTimeStamp=" + currentTime;
contextStr += "&expireTime=" + endTime;
contextStr += "&random=" + random;
contextStr += "&procedure=" + procedure;
contextStr += "&vodSubAppId=" + vodSubAppId;
try {
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
SecretKeySpec secretKey = new SecretKeySpec(this.secretKey.getBytes(CONTENT_CHARSET), mac.getAlgorithm());
mac.init(secretKey);
byte[] hash = mac.doFinal(contextStr.getBytes(CONTENT_CHARSET));
byte[] sigBuf = byteMerger(hash, contextStr.getBytes(StandardCharsets.UTF_8));
strSign = base64Encode(sigBuf);
strSign = strSign.replace(" ", "").replace("\n", "").replace("\r", "");
} catch (Exception e) {
throw e;
}
return strSign;
}
private String base64Encode(byte[] buffer) {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(buffer);
}
public void setSecretId(String secretId) {
this.secretId = secretId;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public void setCurrentTime(long currentTime) {
this.currentTime = currentTime;
}
public void setRandom(int random) {
this.random = random;
}
public String getProcedure() {
return procedure;
}
public void setProcedure(String procedure) {
this.procedure = procedure;
}
public void setSignValidDuration(int signValidDuration) {
this.signValidDuration = signValidDuration;
}
public long getVodSubAppId() {
return vodSubAppId;
}
public void setVodSubAppId(long vodSubAppId) {
this.vodSubAppId = vodSubAppId;
}
}