播放加密视频
保利威视频加密采用私有的加密算法,提供包括加密转码、安全传输、解密播放等一体化的视频安全方案。通过对视频数据本身进行高强度的加密,视频文件即使被下载到本地也无法直接播放和传播,可有效防止视频泄露和盗版问题,详见:视频加密。
为了方便开发者快速集成,我们将解密播放相关逻辑都封装到了播放器内部,与播放普通视频相比,在播放器端,开发者只需多传入播放凭证参数即可实现加密视频的播放。
开发指引
加密视频,需使用播放凭证方式进行播放,播放器需要传递的参数如下:
名称 | 类型 | 说明 |
---|---|---|
playsafe | String/Function | PC端播放加密视频所需的授权凭证,业务方服务端通过创建 Playsafe Token接口获取并返回给播放器。 |
playsafeUrl | String | 获取PC端播放加密视频凭证的接口URL(与playsafe参数二选一) |
ts | Number | 移动端H5播放加密视频需传入的13位毫秒级时间戳 |
sign | String | 移动端H5播放加密视频所需的签名,生成规则为点播账号的secretkey、vid、ts的值按顺序拼接后进行md5计算后的值,由业务方服务端生成并返回给播放器。 注:sign签名不需要转大写。 签名示例: 若secretkey为abc,vid为123,ts为1672829071000,此时签名sign为md5("abc1231672829071000") |
注:playsafe或playsafeUrl参数用于PC端解密播放,ts、sign用于移动端H5播放器解密。如果PC和移动端使用同一份播放器代码,则三个参数都需要传值。
播放器代码示例:
<div id="player"></div>
<script src="//player.polyv.net/resp/vod-player/latest/player.js"></script>
<script>
var player = polyvPlayer({
wrap: '#player',
width: 800,
height: 533,
vid: '88083abbf5bcf1356e05d39666be527a_8',
playsafe:'81814fed-bdd0-4506-bec1-ebc8093148c5-hfevwsfxcsbcocx',
//playsafeUrl:'https://myDomain.com/token', // 业务方自定义的获取播放凭证接口URL,与playsafe参数二选一
ts:'1568131545000',
sign:'88313661ba7ded642c7b557b0a364b4b'
});
//切换加密视频时,需要重新获取播放凭证。如果初始化播放器时使用了playsafeUrl参数,则播放器会自动获取新的凭证,无需传playsafe参数。
player.changeVid({
vid: '88083abbf5bcf1356e05d39666be527a_9', //需要切换的视频vid
playsafe: '81814fed-bdd0-4506-bec1-ebc8093148c6-hfevwsfxcsbcocx', //新获取的playsafe token
sign: '88313661ba7ded642c7b557b0a364b4c', //新获取的sign和ts参数
ts: '1568131545001'
});
</script>
Web页面播放加密视频前,需要先访问业务方自己的服务端授权验证接口(可以在这里加上自有业务的授权验证逻辑,例如是否登录、是否购买课程等, 建议使用HTTPS)。如果业务上允许播放,则通过创建 Playsafe Token接口获取播放凭证(或者在服务端生成sign、ts参数),并返回给Web端播放器。
服务端生成播放凭证代码示例:
PHP 代码:
// 接口中应附带自有业务的授权验证逻辑,如判断是否登录、是否购买课程等
// 以下为生成播放凭证的代码示例
function get_client_ip() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ipaddress = $_SERVER['REMOTE_ADDR'];
}
return $ipaddress;
}
$userId = 'your userId'; // 保利威点播账号的userId
$secretkey = 'your secretkey'; // 保利威点播账号的secretkey
$videoId = '88083abbf5bcf1356e05d39666be527a_8'; // 视频vid
$ts = time() * 1000; // 时间戳
$viewerIp = get_client_ip(); // 观众ip
$viewerId = '12345'; // 观众id
$viewerName = 'testUser'; // 观众昵称, 若值为中文需要urlencode('张三')
$extraParams = 'HTML5'; // 自定义扩展参数
$disposable = false // true 表示 token 仅一次有效。false 则表示在有效期内可以多次验证。默认为 false。
/* 将参数 $userId、$secretkey、$videoId、$ts、$viewerIp、$viewerIp、$viewerId、$viewerName、$extraParams按照ASCKII升序 key + value + key + value ... +value 拼接
*/
$concated = 'extraParams'.$extraParams.'ts'.$ts.'userId'.$userId.'videoId'.$videoId.'viewerId'.$viewerId.'viewerIp'.$viewerIp.'viewerName'.$viewerName;
// 首尾加上secretkey值
$plain = $secretkey.$concated.$secretkey;
// 取大写MD5
$sign = strtoupper(md5($plain));
// 然后将下列参数用post请求 https://hls.videocc.net/service/v1/token 获取 token
$url = 'https://hls.videocc.net/service/v1/token';
$data = array('userId' => $userId, 'videoId' => $videoId, 'ts' => $ts, 'viewerIp' => $viewerIp, 'viewerName' => $viewerName, 'extraParams' => $extraParams, 'viewerId' => $viewerId, 'sign' => $sign);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
// 获取接口返回结果中的token值, 并传给播放器播放加密视频
$token = json_decode($result)->data->token;
echo $token;
Java SpringMvc代码:
@ResponseBody
@RequestMapping("/playerSafe")
public String playerSafe(HttpServletRequest request) {
String userId = "your userId"; // 保利威点播账号的userId
String secretkey = "your secretkey"; // 保利威点播账号的secretkey
String videoId = "88083abbf5bcf1356e05d39666be527a_8"; // 视频vid
long ts = System.currentTimeMillis(); // 时间戳
String viewerIp = getClientIp(request); // 观众ip
String viewerId = "12345"; // 观众id
String viewerName = "testUser"; // 观众昵称, 若值为中文需要urlencode('张三')
String extraParams = "HTML5"; // 自定义扩展参数
boolean disposable = false; // true 表示 token 仅一次有效。false 则表示在有效期内可以多次验证。默认为 false。
/* 将参数 userId、secretkey、videoId、ts、viewerIp、viewerIp、viewerId、viewerName、extraParams按照ASCKII升序 key + value + key + value ... +value 拼接
*/
String concated = "extraParams" + extraParams + "ts" + ts + "userId" + userId + "videoId" + videoId + "viewerId" + viewerId + "viewerIp" + viewerIp + "viewerName" + viewerName;
// 首尾加上secretkey值
String plain = secretkey + concated + secretkey;
// 取大写MD5,可自行选择md5库
String sign = md5Hex(plain).toUpperCase();
// 然后将下列参数用post请求 https://hls.videocc.net/service/v1/token 获取 token
String url = "https://hls.videocc.net/service/v1/token";
Map<String, String> params = new HashMap<>();
params.put("userId", userId);
params.put("videoId", videoId);
params.put("ts", String.valueOf(ts));
params.put("viewerIp", viewerIp);
params.put("viewerName", viewerName);
params.put("extraParams", extraParams);
params.put("viewerId", viewerId);
params.put("sign", sign);
// 可自行选择http客户端
String response = HttpClientUtil.getInstance().sendHttpPost(url, params);
try {
//解析json
ObjectMapper objectMapper = new ObjectMapper();
TokenResponse tokenResponse = objectMapper.readValue(response, TokenResponse.class);
// 响应代码,200为成功,403为ts过期或签名错误,400为参数错误(例如缺少 userId 或 videoId)
if (tokenResponse.getCode() == 200) {
Map data = (Map) tokenResponse.getData();
return data.get("token").toString();
}
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
static class TokenResponse {
int code;
String status;
String message;
Object data;
//省略getter、setter...
}
public String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
如果播放器使用playsafeUrl参数的方式,则需输出json格式:
PHP 代码
// 已省略获取token的代码...
$token = json_decode($result)->data->token;
$code = json_decode($result)->code; // 响应代码,200为成功,403为ts过期或签名错误,400为参数错误(例如缺少 userId 或 videoId)
$message=json_decode($result)->message;
$status=json_decode($result)->status;
if($code == 200){
$array = Array("code"=>$code,'status'=>$status,'message'=>$message,"data"=>$token);
}else{
$array = Array("code"=>$code,'status'=>$status,'message'=>$message,"data"=>json_decode($result)->data);
}
$Json = json_encode($array);
echo $Json; //输出json
Java SpringMvc代码
@ResponseBody
@RequestMapping("/playerSafeUrl")
public Map<String, Object> playerSafeUrl(HttpServletRequest request) {
// 已省略获取token的代码...
Map<String, Object> resultMap = new LinkedHashMap<>();
try {
//解析json
ObjectMapper objectMapper = new ObjectMapper();
TokenResponse tokenResponse = objectMapper.readValue(response, TokenResponse.class);
resultMap.put("code", tokenResponse.getCode());
resultMap.put("status", tokenResponse.getStatus());
resultMap.put("message", tokenResponse.getMessage());
// 响应代码,200为成功,403为ts过期或签名错误,400为参数错误(例如缺少 userId 或 videoId)
if (tokenResponse.getCode() == 200) {
Map data = (Map) tokenResponse.getData();
resultMap.put("data", data.get("token"));
} else {
resultMap.put("data", tokenResponse.getData());
}
} catch (IOException e) {
e.printStackTrace();
}
return resultMap;
}
playsafeUrl返回结果示例:
// 请求成功
{
"code": 200,
"status": "success",
"message": "",
"data": "973d7731803940a1b14fdc93941f493c"
}
// 请求失败(签名错误)
{
"code": 403,
"status": "error",
"message": "sign_invalid",
"data": "sign parameter invalid."
}
playsafeUrl可能存在跨域问题,解决方案请参考:跨域访问设置。
如果需要在移动端H5播放Web加密视频,则需返回sign和ts参数给播放器:
// php
$vid = "88083abbf5bcf1356e05d39666be527a_8"; // 视频vid
$secretkey= "your secretkey"; // 保利威点播账号的secretkey
$ts=time()*1000; // 10位的秒级时间戳,后面加多3个0,最后为13位的数值
$sign = md5($secretkey.$vid.$ts);
Last updated