java对接云点播上传视频并且自动触发转码任务回调更新成m3u8格式的视频
前端拿到签名上传文件,签名加转码参数会触发转码任务,转码任务完成会回调设置的回调接口
/**
* @desc:获取上传签名
* @date 2022/12/29 15:57
*/
@Override
public String getSignature() throws Exception {
// 签名有效期:2天
int signValidDuration = vodConfig.getSignValidDuration();
// 设置 App 的云 API 密钥
return UploadSignatureUtil.getUploadSignature(vodConfig.getSecretId(), vodConfig.getSecretKey(), signValidDuration);
}
工具类
package com.icourt.lawtrust.utils;
import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Random;
// 签名工具类
public class UploadSignatureUtil {
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;
}
// 获取签名
public static String getUploadSignature(String secretId, String secretKey, int signValidDuration) throws Exception {
long currentTime = System.currentTimeMillis() / 1000;
int random = new Random().nextInt(java.lang.Integer.MAX_VALUE);
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=" + "m3u8转码";
/*
* 任务流状态变更通知模式(仅当指定了 procedure 时才有效)。
* Finish:只有当任务流全部执行完毕时,才发起一次事件通知。
* Change:只要任务流中每个子任务的状态发生变化,都进行事件通知。
* None:不接受该任务流回调。
* 默认为 Finish。
*/
contextStr += "&taskNotifyMode=" + "Change";
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(CONTENT_CHARSET), mac.getAlgorithm());
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(contextStr.getBytes(CONTENT_CHARSET));
byte[] sigBuf = byteMerger(hash, contextStr.getBytes("utf8"));
strSign = base64Encode(sigBuf);
strSign = strSign.replace(" ", "").replace("\n", "").replace("\r", "");
return strSign;
}
private static String base64Encode(byte[] buffer) {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(buffer);
}
}
回调接口更新转码的url
/**
* @param json {
* "EventType": "ProcedureStateChanged",
* "FileUploadEvent": null,
* "ProcedureStateChangeEvent": {
* "TaskId": "1306822285-procedurev2-67ee78458206943cfc947c39b394a03ctt0",
* "Status": "FINISH",
* "ErrCode": 0,
* "Message": "SUCCESS",
* "FileId": "243791577896846461",
* "FileName": "604a87fc-e7bc-463e-8d56-cde7e661d690",
* "FileUrl": "https://2332222225.vod2.myqcloud.com/644a93a7vodcq1306822285/19516f26243791577896846461/XlaVDSKoc4sA.mp4",
* "MetaData": {
* "AudioDuration": 0,
* "AudioStreamSet": [],
* "Bitrate": 5994988,
* "Container": "mov,mp4,m4a,3gp,3g2,mj2",
* "Duration": 24.933,
* "Height": 1080,
* "Rotate": 0,
* "Size": 18684382,
* "VideoDuration": 24.933,
* "VideoStreamSet": [{
* "Bitrate": 5989089,
* "Codec": "h264",
* "CodecTag": "",
* "Fps": 30,
* "Height": 1080,
* "Width": 1920
* }],
* "Width": 1920 * },
* "AiAnalysisResultSet": [],
* "AiRecognitionResultSet": [],
* "AiContentReviewResultSet": [],
* "MediaProcessResultSet": [{
* "Type": "Transcode",
* "TranscodeTask": {
* "Status": "SUCCESS",
* "ErrCode": 0,
* "ErrCodeExt": "",
* "Message": "SUCCESS",
* "Progress": 100,
* "BeginProcessTime": "2022-12-29T08:14:48Z",
* "FinishTime": "2022-12-29T08:15:07Z",
* "Input": {
* "Definition": 100240,
* "TraceWatermark": {
* "Definition": 0,
* "DefinitionForBStream": 0,
* "Switch": ""
* },
* "WatermarkSet": [],
* "HeadTailSet": [],
* "MosaicSet": [],
* "StartTimeOffset": 0,
* "EndTimeOffset": 0
* },
* "Output": {
* "Url": "https://1303333385.vod2.myqcloud.com/3b34814fvodtranscq1306822285/19516f26243791577896846461/v.f100240.m3u8",
* "Size": 7784349,
* "Container": "hls",
* "Height": 1080,
* "Width": 1920,
* "Bitrate": 2497685,
* "Md5": "68b119f4bcef596c0bd1bf083f3e5047",
* "Duration": 24.933,
* "VideoStreamSet": [{
* "Bitrate": 2494974,
* "Codec": "h264",
* "CodecTag": "avc1",
* "Fps": 25,
* "Height": 1080,
* "Width": 1920
* }],
* "AudioStreamSet": [],
* "Definition": 100240,
* "DigitalWatermarkType": "None"
* }
* },
* "AnimatedGraphicTask": null,
* "SnapshotByTimeOffsetTask": null,
* "SampleSnapshotTask": null,
* "ImageSpriteTask": null,
* "CoverBySnapshotTask": null,
* "AdaptiveDynamicStreamingTask": null
* }],
* "SessionContext": "",
* "SessionId": "",
* "TasksPriority": 0,
* "TasksNotifyMode": "Change",
* "Operator": "",
* "OperationType": ""
* },
* "FileDeleteEvent": null,
* "PullCompleteEvent": null,
* }
* @desc:事件回调通知 回调地址写的生产根据数据判断是否调到测试环境
* @date 2022/12/29 15:57
*/
@Override
public void notifyCallBack(JSONObject json, Map<String, String> headers) {
log.info("打印notifyCallBack参数:{}{}", json, headers);
}
通过fileId删除视频
package com.lawtrust.database.web.common.config;
import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
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.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaRequest;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class VODClient {
private VodClient vodClient;
@Autowired
private VodConfig vodConfig;
public VODClient() {
}
@PostConstruct
public void init() {
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
Credential cred = new Credential(vodConfig.getSecretId(), vodConfig.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("vod.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
this.vodClient = new VodClient(cred, "", clientProfile);
}
public String delete(String fileId) throws TencentCloudSDKException {
// 实例化一个请求对象,每个接口都会对应一个request对象
DeleteMediaRequest req = new DeleteMediaRequest();
req.setFileId(fileId);
// 返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
DeleteMediaResponse resp = this.vodClient.DeleteMedia(req);
String response = DeleteMediaResponse.toJsonString(resp);
return response;
}
}
如果视频地址需要做防盗链和限时需要获取签名的url
/**
* @return java.lang.String
* @desc:生成防盗链播放URL
* @date 2022/9/27 16:49
*/
public String getSignatureUrl(String videoUrl) {
//生成的十位随机字符串
String us = RandomUtil.randomString(10);
//过期时间戳1517400000的十六进制表示结果 3小时后过期
String t = UploadSignatureUtil.longToHex(DateUtil.addHours(new Date(), vodConfig.getUrlExpire()).getTime());
UrlBuilder urlBuild = UrlBuilder.of(videoUrl);
String pathStr = urlBuild.getPathStr();
//原始播放 URL 的 PATH 除去**.m3u8
String substring = pathStr.substring(0, pathStr.lastIndexOf("/") + 1);
urlBuild.addQuery("t", t);
urlBuild.addQuery("us", us);
//生成签名
String sign = SecureUtil.md5(vodConfig.getAntiLeechLink() + substring + t + us);
urlBuild.addQuery("sign", sign);
return urlBuild.build();
}