> For the complete documentation index, see [llms.txt](https://polyv.gitbook.io/document/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://polyv.gitbook.io/document/docs/vod/js/video-player/function/encryption.md).

# 播放加密视频

保利威视频加密采用私有的加密算法，提供包括加密转码、安全传输、解密播放等一体化的视频安全方案。通过对视频数据本身进行高强度的加密，视频文件即使被下载到本地也无法直接播放和传播，可有效防止视频泄露和盗版问题，详见：[视频加密](https://git.polyv.net/help-center/document-center/-/blob/master/vod/product/manual/playsafe/encryption.md)。

为了方便开发者快速集成，我们将解密播放相关逻辑都封装到了播放器内部，与播放普通视频相比，在播放器端，开发者只需多传入播放凭证参数即可实现加密视频的播放。

### 开发指引

加密视频，需使用播放凭证方式进行播放，播放器需要传递的参数如下：

| 名称          | 类型              | 说明                                                                                                                                                                                                |
| ----------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| playsafe    | String/Function | PC端播放加密视频所需的授权凭证，业务方服务端通过[创建 Playsafe Token](https://git.polyv.net/help-center/document-center/-/blob/master/vod/api/playsafe/token/create_token.md)接口获取并返回给播放器。                                  |
| playsafeUrl | String          | 获取PC端播放加密视频凭证的接口URL（***与playsafe参数二选一***）                                                                                                                                                         |
| ts          | Number          | 移动端H5播放加密视频需传入的13位毫秒级时间戳                                                                                                                                                                          |
| sign        | String          | <p>移动端H5播放加密视频所需的签名，生成规则为点播账号的secretkey、vid、ts的值按顺序拼接后进行md5计算后的值，由业务方服务端生成并返回给播放器。<br>注：sign签名不需要转大写。<br>签名示例：<br>若secretkey为abc，vid为123，ts为1672829071000，此时签名sign为md5("abc1231672829071000")</p> |

**注：playsafe或playsafeUrl参数用于PC端解密播放，ts、sign用于移动端H5播放器解密。如果PC和移动端使用同一份播放器代码，则三个参数都需要传值。**

**播放器代码示例：**

```javascript
<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](https://git.polyv.net/help-center/document-center/-/blob/master/vod/api/playsafe/token/create_token.md)接口获取播放凭证（或者在服务端生成sign、ts参数），并返回给Web端播放器。

**服务端生成播放凭证代码示例：**

**PHP 代码：**

```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代码：**

```java
@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 代码**

```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代码**

```java
@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返回结果示例：

```json
// 请求成功
{
    "code": 200,
    "status": "success",
    "message": "",
    "data": "973d7731803940a1b14fdc93941f493c"
}
// 请求失败（签名错误）
{
    "code": 403,
    "status": "error",
    "message": "sign_invalid",
    "data": "sign parameter invalid."
}
```

playsafeUrl可能存在跨域问题，解决方案请参考：[跨域访问设置](https://git.polyv.net/help-center/document-center/-/blob/master/vod/js/video_player/faq/cross_domain.md)。

如果需要在移动端H5播放Web加密视频，则需返回sign和ts参数给播放器：

```php
// php
$vid = "88083abbf5bcf1356e05d39666be527a_8"; // 视频vid
$secretkey= "your secretkey"; // 保利威点播账号的secretkey
$ts=time()*1000;  // 10位的秒级时间戳，后面加多3个0，最后为13位的数值
$sign = md5($secretkey.$vid.$ts); 
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://polyv.gitbook.io/document/docs/vod/js/video-player/function/encryption.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
