> 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/product/security_best_demo.md).

# 版权保护最佳实践

## 1. 功能介绍

保利威致力于提升视频播放的安全性，保护您的视频资源。通过自研的视频版权保护方案（VRM）和自有专利的加密算法，保利威能够最大限度保证视频文件的安全。您在对接保利威 PlaySafe 视频保护功能时，建议参考本文的最佳实践，以获得最佳的视频安全保护效果。

## 2. 整体方案

对接视频加密功能，需要您的服务器侧和客户端侧进行一定量的修改，以下流程图展示了播放加密视频功能的整体工作流程：

![播放加密视频工作流程图](https://git.polyv.net/help-center/document-center/-/blob/master/img/播放加密视频工作流程图.jpg)

接下来对流程图中的各个步骤进行详细描述：

1. 您的终端向您的服务器请求Token

在播放视频前，您的终端（包括 Web 端、移动 App 端、pc 播放器等）应使用待播放视频的vid，通过您内部自己开发的接口，向您的服务器请求播放 Token。为了加强 Token 的安全性，您的接口应当使用 HTTPS 协议。

2. 您的服务器向保利威服务器请求Token

您的服务器端应通过 [获取视频播放凭证](https://git.polyv.net/help-center/document-center/-/blob/master/vod/api/playsafe/token/create_token/README.md) 接口向保利威服务器请求加密视频播放所需的 Token。

* 建议您联系保利威技术支持设置[IP白名单](https://git.polyv.net/help-center/document-center/-/blob/master/docs/vod/product/vod/product/manual/playsafe/ipwhitelist/README.md) 限制功能，在设置白名单后，只有您指定的 IP 可以通过该接口获取 Token，其它 IP 均无法请求该接口获得 Token

3. 保利威服务器返回Token给您的服务器

保利威服务器在收到您的获取指定视频播放凭证的请求后，在校验无异常时，会向您的服务器返回一个用于播放加密视频的 Token

4. 您的服务器端返回Token给您的终端

您的服务器在收到保利威返回来的 Token 时，需要向终端返回该 Token。在向终端返回 Token 时，您应当使用自己定义的一套加密规则对 Token 加密，以免该 Token 在网络传输过程中被泄露出去。

5. 您的终端使用点播SDK播放视频

在播放加密视频前，您需要调用点播 SDK 的接口，传递从您的服务器获得的 Token。若您在服务器已经对 Token 进行了加密，您需要在终端先根据对应的规则进行解密，再传递真实的 Token 给点播 SDK

下面将对您需要改动的地方进行说明，并提供示例代码以供参考。

## 3. 服务器侧

您的服务器需要关注的是上图中“客户服务器和保利威服务器的交互”这一部分。

![播放加密视频工作流程图-1](https://git.polyv.net/help-center/document-center/-/blob/master/img/播放加密视频工作流程图-1.jpg)

在服务器侧，您应该提供一个接口实现从保利威服务器申请播放对应视频（指定vid）的 Token 并返回的功能，以便您的终端可以通过服务器获取到播放 Token，具体功能点如下：

1）获取接口请求参数：视频 vid，用户 viewerId，以及其他参数；

2）请求保利威服务器的 [获取视频播放凭证](https://git.polyv.net/help-center/document-center/-/blob/master/vod/api/playsafe/token/create_token/README.md) 接口，移动端请求需对 viewerId 进行 base64 处理；

3）解析接口返回值，获取到 Token；

4）通过自己定义的一套加密规则对 Token 加密，以免泄露；

5）向客户端返回加密后的 Token。

### 3.1 示例代码

```java
/**
 * 获取点播加密视频的播放token
 */
@PostMapping("/getVodToken")
@ResponseBody
public ResponseVO getVodToken(@RequestBody GetTokenRequestVO tokenReq, HttpServletRequest request) throws IOException, NoSuchAlgorithmException {
    // 请求参数处理
    String videoId = tokenReq.getVideoId();
    String viewerId = tokenReq.getViewerId();
    if (StringUtils.isEmpty(videoId) || StringUtils.isEmpty(viewerId)) {
        return ResponseVO.failure("argument is error");
    }
    Map<String, Object> args = new HashMap<>(16);
    args.put("videoId", videoId);
    args.put("viewerId", viewerId);
    args.put("viewerIp", IPUtil.getIPAddress(request));
    args.put("viewerName", tokenReq.getViewerName());
    args.put("expires", tokenReq.getExpires());
    args.put("disposable", tokenReq.getDisposable());
    args.put("iswxa", tokenReq.getIswxa());
    args.put("userId", USER_ID);
    args.put("ts", System.currentTimeMillis());
    // 去除空值参数
    Map<String, String> params = args.entrySet()
            .stream()
            .filter(entry -> entry.getValue() != null)
            .collect(
                    Collectors.toMap(Map.Entry::getKey, entry -> String.valueOf(entry.getValue()))
            );
    // 请求参数签名
    params.put("sign", LiveSignUtil.getSign(params, SECRET_KEY));
    
    // 向保利威服务器请求Token
    TokenVO vo = RequestTokenService.requestTokenFromServer(params);
    if (vo == null || vo.getCode() == null || vo.getCode() != HttpStatus.OK.value()) {
        return ResponseVO.failure("请求数据失败");
    } else {
        // TODO 在返回Token前建议实现自己的加密逻辑，以免在网络传输过程中泄露Token
        return ResponseVO.success(vo.getData());
    }
}
```

其中 requestTokenFromServer 方法实现了调用保利威服务器 [获取视频播放凭证](https://git.polyv.net/help-center/document-center/-/blob/master/vod/api/playsafe/token/create_token/README.md) 接口获取 Token

```kotlin
private const val API_URL = "http://hls.videocc.net/service/v1/token"

/**
 * 向保利威服务器请求Token
 * POST方式，Content-type: application/x-www-form-urlencoded
 */
fun requestTokenFromServer(params: Map<String, String>): TokenVO {
    return httpClient.submitForm(
        url = API_URL,
        formParameters = Parameters.build {
            params.forEach { (key, value) ->
                append(key, value)
            }
        }
    ).bodyAsObject<TokenVO>()
}
```

更详细的代码可以移步 [Gitee地址](https://gitee.com/hufewzk/polyv-vod-player-demo) 进行浏览

### 3.2 GetTokenRequestVO 参数描述

| 参数名         | 必填    | 类型      | 说明                                                                                   |
| ----------- | ----- | ------- | ------------------------------------------------------------------------------------ |
| videoId     | true  | String  | 视频id，例如：e6b23c6f519c5906e54a13b8200d7bb0\_e                                          |
| viewerId    | true  | String  | 观看者id，要求不同的观看者使用不同的id；移动端请求需对viewerId进行base64处理                                      |
| viewerIp    | false | String  | 观看者ip，如果为空，会自动获取调用该接口时的ip                                                            |
| viewerName  | false | String  | 观看者名称                                                                                |
| expires     | false | Long    | token有效时长，单位为秒。为空时默认为10分钟，有效期为最长24小时。                                                |
| disposable  | false | Boolean | <p>token有效期，默认为false<br>true：token仅一次有效（验证一次后，token就失效了）<br>false：在有效期内可以进行多次验证。</p> |
| iswxa       | false | Integer | <p>是否微信小程序播放，默认为0<br>1：是<br>0：否</p>                                                  |
| extraParams | false | String  | 自定义的其它参数                                                                             |

### 3.3 TokenVO 参数描述

| 参数名         | 类型      | 说明                                                                          |
| ----------- | ------- | --------------------------------------------------------------------------- |
| token       | String  | token值                                                                      |
| userId      | String  | 保利威点播账户id                                                                   |
| appId       | String  | 账号appId                                                                     |
| videoId     | String  | 视频id，例如：e6b23c6f519c5906e54a13b8200d7bb0\_e                                 |
| viewerId    | String  | 观看者id                                                                       |
| viewerIp    | String  | 观看者ip                                                                       |
| viewerName  | String  | 观看者名称                                                                       |
| ttl         | Long    | token有效时长，单位为豪秒                                                             |
| disposable  | Boolean | <p>token有效期<br>true：token仅一次有效（验证一次后，token就失效了）<br>false：在有效期内可以进行多次验证。</p> |
| iswxa       | Integer | <p>是否微信小程序播放<br>1：是<br>0：否</p>                                              |
| extraParams | String  | 自定义的其它参数                                                                    |
| createdTime | Long    | token创建时间，13位毫秒级时间戳                                                         |
| expiredTime | Long    | token过期时间，13位毫秒级时间戳                                                         |

## 4. 终端侧

终端侧需要关注的是上图中“客户终端和客户服务器的交互”、“客户终端和保利威点播SDK的交互”这两部分。

![播放加密视频工作流程图-2](https://git.polyv.net/help-center/document-center/-/blob/master/img/播放加密视频工作流程图-2.jpg)

在终端侧，您需要做的事情如下：

1）视频播放前，调用您的服务器自行开发的接口，通过视频vid，获得对应视频的播放 Token。

**注意，移动端（iOS端和android端）向您的服务器请求获取播放 Token 时，请求参数的 viewerId 必须经过 Base64 编码。**

2）若您在服务器侧已经对 Token 进行了加密，您还需要先根据自定义的规则对获取到的 Token 进行解密。

3）通过保利威点播 SDK 所提供的 API 传递解密后的 Token。

上述步骤1和2，属于“客户终端和客户服务器的交互”，需要您自行开发，这里不予细述，以下示例代码主要展示了步骤3——如何调用点播 SDK 的 API 传递 Token，这部分属于“客户终端和保利威点播SDK的交互”。

### 4.1 Web 端

完整的示例代码请参考 [点播 JS SDK 播放加密视频](https://git.polyv.net/help-center/document-center/-/blob/master/vod/js/video_player/function/encryption/README.md)

```js
var player = polyvPlayer({
   wrap: '#player',
   width: 800,
   height: 533,
   vid: '88083abbf5bcf1356e05d39666be527a_8',
   playsafe:'81814fed-bdd0-4506-bec1-ebc8093148c5-hfevwsfxcsbcocx', // 通过 playsafe 传入 token
   ts:'1568131545000',
   sign:'88313661ba7ded642c7b557b0a364b4b'
});
```

### 4.2 iOS 端

完整的示例代码请参考 [点播 iOS SDK 4.视频播放](https://git.polyv.net/help-center/document-center/-/blob/master/vod/ios/4.视频播放) 4.1.2 外部传入播放凭证。

```objective-c
// 通过配置播放器的 requestCustomKeyTokenBlock 属性，播放器内部在开始播放加密视频时，将会自动执行该 block ，并使用 block 返回的 token 对加密视频进行解密。
self.player.requestCustomKeyTokenBlock = ^NSString *(NSString *vid) {
        // 通过参数 vid，向客户自己的服务器请求 token
        NSString *encodeToken = @"xxxxxxxx";
        // 将服务器返回的 token 进行解密
        NSString *decodeToken = @"yyyyyyyy";
    return decodeToken;
};
// 通过 vid 获取在线播放视频模型
[PLVVodVideo requestVideoWithVid:vid completion:^(PLVVodVideo *video, NSError *error) {
    // 播放视频 
    weakSelf.player.video = video;
}];
```

### 4.3 Android 端

#### 4.3.1 视频播放

完整的示例代码请参考 [点播 Android SDK 4.视频播放](https://git.polyv.net/help-center/document-center/-/blob/master/vod/android/4.视频播放) 1.2 外部传入播放凭证

```java
videoView.setVideoTokenRequestListener(new IPLVVideoTokenRequestListener() {
    @Override
    public String onRequestToken(PolyvVideoVO videoVO, String viewerId, String viewerName, String viewerParam) {
        // 返回外部获取的 token
        return token;
    }
});
// 通过vid播放视频
videoView.setVid(vid, bitrate, isMustFromLocal);
```

#### 4.3.2 视频下载

完整的示例代码请参考 [点播 Android SDK 5.视频下载](https://git.polyv.net/help-center/document-center/-/blob/master/vod/android/5.视频下载)

```java
downloader.setDownloaderTokenRequestListener(new IPLVDownloaderTokenRequestListener() {
    @Override
    public String onRequestToken(@NotNull String videoId, int bitRate) {
        // 返回视频下载的token
        return token;
    }
});
```

### 4.4 C++ SDK

C++ SDK 如何传递 Token 并播放视频代码示例：

```C++
// 创建播放器对象，window为播放窗口句柄
auto player = PLVPlayerCreate(window);
// 播放前要先设置vid, videoPath为离线播放的视频存放地址，videoRate为视频的清晰度
PLVPlayerSetVideo(player, vid, videoPath, videoRate);
// 开始在线播放，token通过业务侧获取传入，seekMillisecond开始播放时想要seek的位置，sync是否同步(播放时会请求videoJson，同步时会阻塞等待请求结果，异步时通过回调通知结果)
PLVPlayerPlay(palyer, token, seekMillisecond, sync);

```

### 4.5 APICloud 平台

APICloud 如何调用点播 SDK 的 API 传递 Token 并播放视频代码示例：

```
// 设置 播放外部播放 token
this.polyvVideo.setCustomVideoToken({token:""}, function (ret, err) {
});
// 通过 vid 播放在线视频
this.polyvVideo.setVid({
     vid: ""
 });
```

### 4.6 uniapp 平台

uniapp 如何调用点播 SDK 的 API 传递 Token 并播放视频代码示例：

```
// 设置 播放外部播放 token
this.$refs.vod.setCustomVideoToken({token: ""},(ret) => {
})
// 通过 vid 播放在线视频
this.$refs.vod.setVid({
        vid: "",
    level: 0
},(ret) => {
})
```
