基于腾讯云点播的视频上传和转码功能
在之前的文章中提到过腾讯云储存上传大文件的各种不方便,比如不能转码(要转码得先把mp4文件从云存储上下载下来,然后通过服务器执行ffmpeg操作,最后再把切割的ts文件上传到腾讯云,过程复杂且容易出错),不能获取实时的上传进度等等,所以就发现腾讯云点播这项技术。相对来说通过云点播既能实现上传进度的获取,而且还能上传完成后自动转码,比如自动把mp4转换为m3u8格式的清单文件来进行分片加载,这样子既能大大加快加载速度,还能保护视频链接的url,所以就记录一下这种技术的使用原理。
云点播视频上传Js端的sdk如下http://video.qcloud.com/sdk/upload.html,现在的官方sdk页面做的比较挫,希望腾讯云以后能改进,毕竟寻找一个方法还要点击“查看网页源代码”让人感觉很方。
首先在html头部引入云点播的js,
<script src="http://qzonestyle.gtimg.cn/open/qcloud/js/vod/sdk/uploaderh5.js" charset="utf-8"></script>
然后在页面写上一个button因为云点播是通过绑定一个button的方法来实现input type='file':
<button class="btn btn-default" id="video">选择文件</button>
页面长成这个样子
接着在js里定义一个初始化云点播上传条件的方法initUpload(),绑定后只要在页面上拉取了文件,每隔1s腾讯服务器就会给你一个回调,你可以根据回调里不同的返回码来写上自己的处理方法。另外,当你在页面选择好一个文件后,js代码会去计算其SHA值,在计算完后才能调用qcVideo.uploader.startUpload()进行上传操作,否则会报错。在上传完成后云点播会返回一个已上传文件在腾讯服务器上的唯一标识args.serverFileId,其实现代码如下:
//初始化直播上传 function initUpload() { //检测浏览器是否支持 var $ = qcVideo.get('$'); var Version = qcVideo.get('Version'); if( !qcVideo.uploader.supportBrowser() ) { if(Version.IS_MOBILE) { alert('当前浏览器不支持上传,请升级系统版本或者下载最新的chrome浏览器'); } else { alert('当前浏览器不支持上传,请升级浏览器或者下载最新的chrome浏览器'); } return; } //绑定按钮及回调处理 accountDone('video',‘你的云点播secretId’,1,1,'你的转码成功后得回调url',null); } /** * * @param upBtnId 上传按钮ID * @param secretId 云api secretId * @param isTranscode 是否转码 * @param isWatermark 是否设置水印 * @param [transcodeNotifyUrl] 转码成功后的回调 * @param [classId] 分类ID */ function accountDone(upBtnId,secretId, isTranscode, isWatermark,transcodeNotifyUrl,classId) { var $ = qcVideo.get('$'), ErrorCode = qcVideo.get('ErrorCode'), Log = qcVideo.get('Log'), JSON = qcVideo.get('JSON'), util = qcVideo.get('util'), Code = qcVideo.get('Code'), Version = qcVideo.get('Version'); qcVideo.uploader.init( { web_upload_url: 'http://vod.qcloud.com/v2/index.php', secretId: secretId, // 云api secretId getSignature: function (argStr, done) {//注意:出于安全考虑, 服务端接收argStr这个参数后,需要校验其中的Action参数是否为 "MultipartUploadVodFile",用来证明该参数标识上传请求 $.ajax({ 'dataType': 'json', 'url': '你的签名路径' + encodeURIComponent(argStr), 'success': function (d) { done(d['result']); } }); }, upBtnId: upBtnId, //上传按钮ID(任意页面元素ID) isTranscode: isTranscode,//是否转码 isWatermark: isWatermark,//是否设置水印 after_sha_start_upload: false,//sha计算完成后,开始上传 (默认关闭立即上传) sha1js_path: '/calculator_worker_sha1.js', //计算sha1的位置 disable_multi_selection: false, //禁用多选 ,默认为false transcodeNotifyUrl: transcodeNotifyUrl,//(转码成功后的回调地址)isTranscode==true,时开启; 回调url的返回数据格式参考 http://www.qcloud.com/wiki/v2/MultipartUploadVodFile classId: classId, // mime_types, 默认是常用的视频和音频文件扩展名,如MP4, MKV, MP3等, video_only 默认为false,可允许音频文件上传 filters: {max_file_size: '8gb', mime_types: [], video_only: true} } //2: 回调 , { /** * 更新文件状态和进度 code:1、准备计算SHA 2、计算完SHA,准备上传 3、SHA计算中 4、即将上传 5、上传进度更新 6、上传完成 * @param args { id: 文件ID, size: 文件大小, name: 文件名称, status: 状态, percent: 进度 speed: 速度, errorCode: 错误码,serverFileId: 后端文件ID } */ onFileUpdate: function (args) { if(args.code == 1 || args.code == 3)//计算SHA中 { //你的逻辑,比如显示文件名等信息 } else if(args.code == 2) //计算完SHA { //计算完SHA值,准备开始上传,这步执行完之后才能执行qcVideo.uploader.startUpload()即上传操作
}
elseif(args.code == 5 )//上传中
{
//获取实时进度
var percent=args.percent+'%';
$(".progress-bar").css({'width':percent});
}
elseif(args.code == 6 )//上传完成
{
$(".progress-bar").css({'width':'100%'}); //取得回调的视频serverFileId,用于后面更新字段用
var file_id=args.serverFileId; console.log(params);
}
},
/** * 文件状态发生变化,暂时不用 * @param info { done: 完成数量 , fail: 失败数量 , sha: 计算SHA或者等待计算SHA中的数量 , wait: 等待上传数量 , uploading: 上传中的数量 } */
onFileStatus: function (info)
{
$('#count').text('各状态总数-->' + JSON.stringify(info));
},
/** * 上传时错误文件过滤提示,暂时不用 * @param args {code:{-1: 文件类型异常,-2: 文件名异常} , message: 错误原因 , solution: 解决方法} */
onFilterError: function (args)
{
var msg = 'message:' + args.message + (args.solution ? (';solution==' + args.solution) : ''); console.log(msg);
}
} );
}
在文件上传完成后如果选择了转码,腾讯云点播会去对文件转码,并在成功后给你设置的回调url发请求,根据回调里的$_POST['file_id']来更新DB为转码完成即可。
js端说完了,现在来看php端,要通过后台向云点播发请求相对来说比较复杂,首先你要定义一个方法,方法可从官网上找到实例:
https://www.qcloud.com/doc/api/257/1976,简易修改后方法如下:
public function CreateRequest($HttpUrl, $HttpMethod, $COMMON_PARAMS, $secretKey, $PRIVATE_PARAMS, $isHttps)
{
$FullHttpUrl = $HttpUrl . "/v2/index.php";
/***************对请求参数 按参数名 做字典序升序排列,注意此排序区分大小写*************/
$ReqParaArray = array_merge($COMMON_PARAMS, $PRIVATE_PARAMS);
ksort($ReqParaArray);
/**********************************生成签名原文**********************************
* 将 请求方法, URI地址,及排序好的请求参数 按照下面格式 拼接在一起, 生成签名原文,此请求中的原文为
* GETcvm.api.qcloud.com/v2/index.php?Action=DescribeInstances&Nonce=345122&Region=gz
* &SecretId=AKIDz8krbsJ5yKBZQ ·1pn74WFkmLPx3gnPhESA&Timestamp=1408704141
* &instanceIds.0=qcvm12345&instanceIds.1=qcvm56789
* ****************************************************************************/
$SigTxt = $HttpMethod . $FullHttpUrl . "?";
$isFirst = true;
foreach ($ReqParaArray as $key => $value) {
if (!$isFirst) {
$SigTxt = $SigTxt . "&";
}
$isFirst = false;
/*拼接签名原文时,如果参数名称中携带_,需要替换成.*/
if (strpos($key, '_')) {
$key = str_replace('_', '.', $key);
}
$SigTxt = $SigTxt . $key . "=" . $value;
}
/*********************根据签名原文字符串 $SigTxt,生成签名 Signature******************/
$Signature = base64_encode(hash_hmac('sha1', $SigTxt, $secretKey, true));
/***************拼接请求串,对于请求参数及签名,需要进行urlencode编码********************/
$Req = "Signature=" . urlencode($Signature);
foreach ($ReqParaArray as $key => $value) {
$Req = $Req . "&" . $key . "=" . urlencode($value);
}
/*********************************发送请求********************************/
if ($HttpMethod === 'GET') {
if ($isHttps === true) {
$Req = "https://" . $FullHttpUrl . "?" . $Req;
} else {
$Req = "http://" . $FullHttpUrl . "?" . $Req;
}
$Rsp = file_get_contents($Req);
}
// var_export(json_decode($Rsp,true)) ;
return json_decode($Rsp, true);
}
同时再写一个通用的调用API的方法,把API的名称以及参数传过去即可:
public function videoApi($action, $private_params)
{
/*DescribeInstances 接口的 URL地址为 cvm.api.qcloud.com,可从对应的接口说明 “1.接口描述” 章节获取该接口的地址*/
$HttpUrl = "vod.api.qcloud.com";
/*除非有特殊说明,如MultipartUploadVodFile,其它接口都支持GET及POST*/
$HttpMethod = "GET";
/*是否https协议,大部分接口都必须为https,只有少部分接口除外(如MultipartUploadVodFile)*/
$isHttps = true;
/*需要填写你的密钥,可从 https://console.qcloud.com/capi 获取 SecretId 及 $secretKey*/
$secretKey = '你的SecretKey';
/*下面这五个参数为所有接口的 公共参数;对于某些接口没有地域概念,则不用传递Region(如DescribeDeals)*/
$COMMON_PARAMS = array(
'Nonce' => rand(),
'Timestamp' => time(NULL),
'Action' => $action,
'SecretId' => '你的secretId',
'Region' => 'gz',
);
/*下面这两个参数为 DescribeInstances 接口的私有参数,用于查询特定的虚拟机列表*/
// $PRIVATE_PARAMS = array(
// 'pullset.0.fileId'=> '14651978969383094681',
// 'width'=>200,
// 'height'=>200
// );
$PRIVATE_PARAMS = $private_params;
/***********************************************************************************/
return $this->CreateRequest($HttpUrl, $HttpMethod, $COMMON_PARAMS, $secretKey, $PRIVATE_PARAMS, $isHttps);
}
具体调用如下:
$getParams = array('fileId' => $fileId);
$resultArray = $this->videoApi('DescribeVodPlayUrls', $getParams);
这样就通过后台发了一个请求,并把返回结果写在$resultArray里,最后再根据其返回值作自己需要的处理即可。