视频点播我去年写过一篇阿里云的,这次写腾讯云。
腾讯云点播(Video on Demand,VOD)基于腾讯多年技术积累与基础设施建设,为有音视频应用相关需求的客户提供包括音视频存储管理、音视频转码处理、音视频加速播放和音视频通信服务的一站式解决方案。
文档中心:https://cloud.tencent.com/document/product/266
上传视频可将视频上传到云点播的存储中,以进行后续的处理和分发等。
前端集成有两种方式,使用“超级播放器预览”与“web播放器预览”,后者代码已经不更新,推荐使用前者,因此“web播放器预览”仅做了解。
1、查看“web播放器预览”;
说明:需要将视频进行转码,才能支持超级播放器播放,转码为:自适应码流
2、查看任务流设置
3、查看详情
当前任务流就是系统默认的“自适应码流”任务流
4、在【音视频管理】重新上传视频
5、查看详情
6、复制代码index.html到项目,即可播放
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>vod_api</artifactId>
<version>2.1.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
(1)上传视频集成方案:https://cloud.tencent.com/document/product/266/10276
(2)删除视频
@Api(tags = "腾讯云点播")
@RestController
@RequestMapping("/admin/vod")
public class VodController {
@Autowired
private VodService vodService;
//返回客户端上传视频的签名
@GetMapping("sign")
public Result sign(){
Signature sign = new Signature();
// 设置 App 的云 API 密钥
sign.setSecretId(ConstantPropertiesUtil.ACCESS_KEY_ID);
sign.setSecretKey(ConstantPropertiesUtil.ACCESS_KEY_SECRET);
sign.setCurrentTime(System.currentTimeMillis() / 1000);
sign.setRandom(new Random().nextInt(java.lang.Integer.MAX_VALUE));
sign.setSignValidDuration(3600 * 24 * 2); // 签名有效期:2天
try {
String signature = sign.getUploadSignature();
System.out.println("signature : " + signature);
return Result.ok(signature);
} catch (Exception e) {
System.out.print("获取签名失败");
e.printStackTrace();
throw new GgktException(20001,"获取签名失败");
}
}
//上传视频接口
@ApiOperation("上传视频接口")
@PostMapping("upload")
public Result upload(){
String fileId=vodService.uploadVideo();
return Result.ok(fileId);
}
//删除腾讯云视频
@ApiOperation("删除腾讯云视频")
@DeleteMapping("remove/{fileId}")
public Result remove(@PathVariable String fileId){
vodService.removeVideo(fileId);
return Result.ok(null);
}
}
(1)VodService定义方法
public interface VodService {
//上传视频接口
String uploadVideo();
//删除腾讯云视频
void removeVideo(String fileId);
}
(2)VodServiceImpl实现方法
@Service
public class VodServiceImpl implements VodService {
//上传视频接口
@Override
public String uploadVideo() {
//指定当前腾讯云账号的id和key
VodUploadClient client = new VodUploadClient(ConstantPropertiesUtil.ACCESS_KEY_ID,
ConstantPropertiesUtil.ACCESS_KEY_SECRET);
//上传请求对象
VodUploadRequest request = new VodUploadRequest();
//设置上传的视频文件在本地路径
request.setMediaFilePath("I:\\temp\\faster.mp4");
//任务流
request.setProcedure("LongVideoPreset");
try {
//调用方法上传视频,指定地域
VodUploadResponse response = client.upload("ap-guangzhou", request);
//获取上传之后的视频id
String fileId = response.getFileId();
return fileId;
} catch (Exception e) {
// 业务方进行异常处理
throw new GgktException(20001,"上传视频失败");
}
}
//删除腾讯云视频
@Override
public void removeVideo(String fileId) {
try{
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
Credential cred = new Credential(ConstantPropertiesUtil.ACCESS_KEY_ID,
ConstantPropertiesUtil.ACCESS_KEY_SECRET);
// 实例化一个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格式的字符串回包
System.out.println(DeleteMediaResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
throw new GgktException(20001,"删除视频失败");
}
}
}
这里暂时路径写死上传的,因为我在文档中并没有找到有关文件流上传方式的API。
不过腾讯云提供了客户端上传的方式,不用服务端上传也是可以的。
Signature类
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Encoder;
public class Signature {
private String secretId;
private String secretKey;
private long currentTime;
private int random;
private int signValidDuration;
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 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=LongVideoPreset";
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("utf8"));
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 void setSignValidDuration(int signValidDuration) {
this.signValidDuration = signValidDuration;
}
}
VodController类
//返回客户端上传视频的签名
@GetMapping("sign")
public Result sign(){
Signature sign = new Signature();
// 设置 App 的云 API 密钥
sign.setSecretId(ConstantPropertiesUtil.ACCESS_KEY_ID);
sign.setSecretKey(ConstantPropertiesUtil.ACCESS_KEY_SECRET);
sign.setCurrentTime(System.currentTimeMillis() / 1000);
sign.setRandom(new Random().nextInt(java.lang.Integer.MAX_VALUE));
sign.setSignValidDuration(3600 * 24 * 2); // 签名有效期:2天
try {
String signature = sign.getUploadSignature();
System.out.println("signature : " + signature);
return Result.ok(signature);
} catch (Exception e) {
System.out.print("获取签名失败");
e.printStackTrace();
throw new GgktException(20001,"获取签名失败");
}
}
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>QCloud VIDEO UGC UPLOAD SDKtitle>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
.text-danger {
color: red;
}
.control-label {
text-align: left !important;
}
#resultBox {
width: 100%;
height: 300px;
border: 1px solid #888;
padding: 5px;
overflow: auto;
margin-bottom: 20px;
}
.uploaderMsgBox {
width: 100%;
border-bottom: 1px solid #888;
}
.cancel-upload {
text-decoration: none;
cursor: pointer;
}
style>
head>
<body>
<div id="content">
<div class="container">
<h1>UGC-Uploaderh1>
div>
div>
<div class="container" id="main-area">
<div class="row" style="padding:10px;">
<p>
示例1点击“直接上传视频”按钮即可上传视频。<br>。
p>
div>
<form ref="vExample">
<input type="file" style="display:none;" ref="vExampleFile" @change="vExampleUpload" />
form>
<div class="row" style="padding:10px;">
<h4>示例1:直接上传视频h4>
<a href="javascript:void(0);" class="btn btn-default" @click="vExampleAdd">直接上传视频a>
div>
<div class="uploaderMsgBox" v-for="uploaderInfo in uploaderInfos">
<div v-if="uploaderInfo.videoInfo">
视频名称:{{uploaderInfo.videoInfo.name + '.' + uploaderInfo.videoInfo.type}};
上传进度:{{Math.floor(uploaderInfo.progress * 100) + '%'}};
fileId:{{uploaderInfo.fileId}};
上传结果:{{uploaderInfo.isVideoUploadCancel ? '已取消' : uploaderInfo.isVideoUploadSuccess ? '上传成功' : '上传中'}};
<br>
地址:{{uploaderInfo.videoUrl}};
<a href="javascript:void(0);" class="cancel-upload" v-if="!uploaderInfo.isVideoUploadSuccess && !uploaderInfo.isVideoUploadCancel" @click="uploaderInfo.cancel()">取消上传a><br>
div>
<div v-if="uploaderInfo.coverInfo">
封面名称:{{uploaderInfo.coverInfo.name}};
上传进度:{{Math.floor(uploaderInfo.coverProgress * 100) + '%'}};
上传结果:{{uploaderInfo.isCoverUploadSuccess ? '上传成功' : '上传中'}};
<br>
地址:{{uploaderInfo.coverUrl}};
<br>
div>
div>
div>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js">script>
<script src="https://cdn-go.cn/cdn/vod-js-sdk-v6/latest/vod-js-sdk-v6.js">script>
<script type="text/javascript">
;(function () {
/**
* 计算签名。调用签名接口获取
**/
function getSignature() {
return axios.get("http://localhost:8333/admin/vod/sign").then(response =>{
return response.data.data
})
};
var app = new Vue({
el: '#main-area',
data: {
uploaderInfos: [],
vcExampleVideoName: '',
vcExampleCoverName: '',
cExampleFileId: '',
},
created: function () {
this.tcVod = new TcVod.default({
getSignature: getSignature
})
},
methods: {
/**
* vExample示例。添加视频
**/
vExampleAdd: function () {
this.$refs.vExampleFile.click()
},
/**
* vExample示例。上传视频过程。
**/
vExampleUpload: function () {
var self = this;
var mediaFile = this.$refs.vExampleFile.files[0]
var uploader = this.tcVod.upload({
mediaFile: mediaFile,
})
uploader.on('media_progress', function (info) {
uploaderInfo.progress = info.percent;
})
uploader.on('media_upload', function (info) {
uploaderInfo.isVideoUploadSuccess = true;
})
console.log(uploader, 'uploader')
var uploaderInfo = {
videoInfo: uploader.videoInfo,
isVideoUploadSuccess: false,
isVideoUploadCancel: false,
progress: 0,
fileId: '',
videoUrl: '',
cancel: function() {
uploaderInfo.isVideoUploadCancel = true;
uploader.cancel()
},
}
this.uploaderInfos.push(uploaderInfo)
uploader.done().then(function(doneResult) {
console.log('doneResult', doneResult)
uploaderInfo.fileId = doneResult.fileId;
return doneResult.video.url;
}).then(function (videoUrl) {
uploaderInfo.videoUrl = videoUrl
self.$refs.vExample.reset();
})
},
// cExample 上传过程
cExampleUpload: function() {
var self = this;
var coverFile = this.$refs.cExampleCover.files[0];
var uploader = this.tcVod.upload({
fileId: this.cExampleFileId,
coverFile: coverFile,
})
uploader.on('cover_progress', function(info) {
uploaderInfo.coverProgress = info.percent;
})
uploader.on('cover_upload', function(info) {
uploaderInfo.isCoverUploadSuccess = true;
})
console.log(uploader, 'uploader')
var uploaderInfo = {
coverInfo: uploader.coverInfo,
isCoverUploadSuccess: false,
coverProgress: 0,
coverUrl: '',
cancel: function () {
uploader.cancel()
},
}
this.uploaderInfos.push(uploaderInfo)
uploader.done().then(function (doneResult) {
console.log('doneResult', doneResult)
uploaderInfo.coverUrl = doneResult.cover.url;
self.$refs.cExample.reset();
})
},
},
})
})();
script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-26476625-7">script>
<script>
// add by alsotang@gmail.com
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-26476625-7');
script>
body>
html>
改下代码中的这里,请求签名接口。
本来应该访问8301端口,但是我用Spring Cloud Gateway配置了网关,nacos配置了服务注册,所以这里就将请求交给网关,由网关负责转发。
运行这个html测试下:
点击上传视频按钮,选择视频
功能基本实现了。