Authorized Playback and Watermark
Screen recording is one of the most difficult video piracy methods to prevent. The anti-recording watermark feature provided by the Polyv player displays text content (usually the viewer's identity ID) irregularly scrolling across the video to deter pirates, thereby protecting video copyright.
Additionally, on the user's website, besides verifying whether a viewer has permission to access the video playback page through login information (cookies), the Polyv player can also verify whether a viewer has permission to play a specific video, thus achieving dual verification of viewer permissions.
Video introduction of the anti-recording watermark: Anti-Recording Tool – Watermark (Video).
Implementation Flow

Implementation Steps
1. Management Console Settings
Log in to the Cloud Video on Demand management console, click 【Settings】 → 【Video Settings】 to enter the video settings page.
In the Authorized Playback and Anti-Recording Watermark Interface settings section, fill in the business party's interface service URL.

When the player requests the authorization interface, it will automatically adapt to the following situations:
- When a complete URL is filled in, the player will request it directly, e.g., http://mywebsite.com/interface/validate。
- When an interface address without a protocol header is filled in, the player will automatically complete it based on the request protocol of the current page. For example, if
//mywebsite.com/interface/validateis filled in and the current page uses the https protocol, the actual request will be: https://mywebsite.com/interface/validate。- When an interface address without a protocol header and HOST is filled in, the player will automatically complete it based on the request protocol and HOST of the current page. For example, if
/interface/validateorinterface/validateis filled in, and the current page's domain is mywebsite2.com using the http protocol, the actual request will be: http://mywebsite2.com/interface/validate。
2. Business Party Server-Side Implementation
1. Player Request
After setting the authorization playback and watermark interface in the management console, the Polyv player will first request the interface set in the background when playing a video. The request method is GET, and it will include the four parameters: vid, code, t, and callback. For example: https://www.mywebsite.com/validate?vid=e2e84a73837363106d8d257f60e55c4c_e&code=&t=1457938821973&callback=polyvObject16209048491895664483_1457938783908&_=1457938784101。
Where code is a parameter in the player embed code, and its value can be customized; t is a random number generated by the player. Example player embed code:
<script src='https://player.polyv.net/script/player.js'></script>
<div id='player'></div>
<script>
var player = polyvPlayer({
wrap: '#player',
width: 800,
height: 533,
vid: '88083abbf5bcf1356e05d39666be527a_8',
code: 'myCodeValue' // 用户可自定义参数值,也可以不设置此参数,那么在请求接口时该参数值为空。参数值为中文时需要做base64URLSafe。
});
</script>
Since the H5 player requests the user's interface via Ajax, it requires cross-origin requests, hence the
callbackparameter is needed. The Flash player achieves cross-origin through a cross-domain file, so it does not need thecallbackparameter and only submits the three parameters vid, code, and t when requesting the interface. For Flash player cross-origin implementation, please refer to Cross-Origin Access Settings.
2. Server-Side Interface Implementation
If the business party's server-side interface only needs to implement authorization verification functionality, it only needs to return the three parameters status, username, and sign to the player for verification.
PHP example for server-side implementation:
// validate.php
<?php
$username = "elvis"; // 用户昵称, 若值为中文需要urlencode('张三'),可从session获取
$secretkey = "secretkey"; // 登录保利威管理后台,点击 【设置】 → 【API接口】获取
$vid=$_GET["vid"];
$t = $_GET["t"];
$code = $_GET["code"];
if($username=="elvis"){
$status = 1; // 业务方可自定义授权验证逻辑
}else {
$status = 2;
}
if(!empty($_GET["callback"])){
$callback = $_GET["callback"];
}else{
$callback = '';
}
$sign=md5("vid=$vid&secretkey=$secretkey&username=$username&code=$code&status=$status&t=$t");
$array=Array("status"=>$status,"username"=>$username,"sign"=>$sign);
$Json = json_encode($array);
if($callback!=''){ //PC H5播放器会提交callback参数
echo $callback."(".$Json.")";
} else{ //Flash播放器不提交callback参数
echo "(".$Json.")";
}
?>
Java SpringMvc example:
@ResponseBody
@RequestMapping("/player-auth")
public String playerAuth(String vid, long t, String code, String callback) {
String username = "elvis";
String secretKey = "secretkey";
int status;
// 业务方可自定义授权验证逻辑
if ("elvis".equals(username)) {
status = 1;
} else {
status = 2;
}
// md5签名,自行选择md5库
String plain = "vid=" + vid + "&secretkey=" + secretKey + "&username=" + username + "&code=" + code + "&status=" + status + "&t=" + t;
String sign = md5Hex(plain);
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("username", username);
resultMap.put("status", status);
resultMap.put("sign", sign);
// json序列化
ObjectMapper objectMapper = new ObjectMapper();
String resultJson = "";
try {
resultJson = objectMapper.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
if (callback != null && !"".equals(callback)) {
return callback + "(" + resultJson + ")";
} else {
return resultJson;
}
}
Where the calculation rule for sign is: concatenate vid, secretkey, username, code, status, t parameters and perform MD5 calculation:
Plain ="vid=" + vid + "&secretkey=" + secretKey + "&username=" + username + "&code=" + code + "&status=" +status + "&t=" + t
sign = MD5.hash(Plain);
Example interface response:
polyvObject16208229674372271079_1478765178186({
"status":1,
"username":"elvis",
"sign":"1cca74bd55c6076091ed84807065e5b7"
})
// 不提交callback参数时
{
"status":1,
"username":"elvis",
"sign":"2c2bfb00314da7d768d50a7d1e93bd9f"
}
If, in addition to authorization verification, the watermark functionality is also required, the interface needs to return parameters related to the watermark.
PHP example for server-side implementation:
// validate.php
<?php
$username = "elvis"; // 用户昵称, 若值为中文需要urlencode('张三'),可从session获取
$secretkey = "secretkey"; // 登录保利威管理后台,点击 【设置】 → 【API接口】获取
$vid=$_GET["vid"];
$t = $_GET["t"];
$code = $_GET["code"];
$fontSize="40";
$fontColor="0xFFE900";
$speed="200";
$filter="on";
$setting="3";
$alpha="1";
$filterAlpha="1";
$filterColor="0x3914AF";
$blurX="2";
$blurY="2";
$tweenTime="1";
$interval="5";
$lifeTime="3";
$strength="4";
$show="on";
$msg="Errormessage!";
if($username=="elvis"){ // 业务方可自定义授权验证逻辑
$status = 1;
}else {
$status = 2;
}
if(!empty($_GET["callback"])){
$callback = $_GET["callback"];
}else{
$callback = '';
}
$sign=md5("vid=$vid&secretkey=$secretkey&username=$username&code=$code&status=$status&t=$t&msg=$msg&fontSize=$fontSize&fontColor=$fontColor&speed=$speed&filter=$filter&setting=$setting&alpha=$alpha&filterAlpha=$filterAlpha&filterColor=$filterColor&blurX=$blurX&blurY=$blurY&interval=$interval&lifeTime=$lifeTime&tweenTime=$tweenTime&strength=$strength&show=$show");
$array = Array("status"=>$status,"username"=>$username,"sign"=>$sign,"msg"=>$msg,"fontSize"=>$fontSize,"fontColor"=>$fontColor,"speed"=>$speed,"filter"=>$filter,"setting"=>$setting,"alpha"=>$alpha,"filterAlpha"=>$filterAlpha,"filterColor"=>$filterColor,"blurX"=>$blurX,"blurY"=>$blurY,"tweenTime"=>$tweenTime,"interval"=>$interval,"lifeTime"=>$lifeTime,"strength"=>$strength,"show"=>$show,);
$Json = json_encode($array);
if($callback!=''){
echo $callback."(".$Json.")";
} else{
echo $Json;
}
?>
Java SpringMvc example:
@ResponseBody
@RequestMapping("/player-auth-marquee")
public String playerAuthMarquee(String vid, long t, String code, String callback) {
String username = "suki";
String secretKey = "AiDQw1mAmi";
String fontSize = "40", fontColor = "0xFFE900", speed = "200",
filter = "on", setting = "3", alpha = "1", filterAlpha = "1",
filterColor = "0x3914AF", blurX = "2", blurY = "2", tweenTime = "1",
interval = "5", lifeTime = "3", strength = "4", show = "on", msg = "Errormessage!";
int status;
// 业务方可自定义授权验证逻辑
if ("suki".equals(username)) {
status = 1;
} else {
status = 2;
}
code = code == null?"":code;
// md5签名,自行选择md5库
String plain = "vid=" + vid + "&secretkey=" + secretKey + "&username=" + username + "&code=" + code + "&status=" + status + "&t=" + t +
"&msg=" + msg + "&fontSize=" + fontSize + "&fontColor=" + fontColor + "&speed=" + speed + "&filter=" + filter + "&setting=" + setting +
"&alpha=" + alpha + "&filterAlpha=" + filterAlpha + "&filterColor=" + filterColor + "&blurX=" + blurX + "&blurY=" + blurY +
"&interval=" + interval + "&lifeTime=" + lifeTime + "&tweenTime=" + tweenTime + "&strength=" + strength + "&show=" + show;
String sign = md5Hex(plain);
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("username", username);
resultMap.put("status", status);
resultMap.put("sign", sign);
resultMap.put("msg", msg);
resultMap.put("fontSize", fontSize);
resultMap.put("fontColor", fontColor);
resultMap.put("speed", speed);
resultMap.put("filter", filter);
resultMap.put("setting", setting);
resultMap.put("alpha", alpha);
resultMap.put("filterAlpha", filterAlpha);
resultMap.put("filterColor", filterColor);
resultMap.put("blurX", blurX);
resultMap.put("blurY", blurY);
resultMap.put("tweenTime", tweenTime);
resultMap.put("interval", interval);
resultMap.put("lifeTime", lifeTime);
resultMap.put("strength", strength);
resultMap.put("show", show);
// json序列化,可自行选择json序列化库
ObjectMapper objectMapper = new ObjectMapper();
String resultJson = "";
try {
resultJson = objectMapper.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
if (callback != null && !"".equals(callback)) {
return callback + "(" + resultJson + ")";
} else {
return resultJson;
}
}
Where the calculation rule for sign is (parameters must be concatenated in the order shown in the example):
Plain = "vid=" + vid + "&secretkey=" + secretKey + "&username=" + username + "&code=" + code + "&status=" + status + "&t=" + t +
"&msg=" + msg + "&fontSize=" + fontSize + "&fontColor=" + fontColor + "&speed=" + speed +"&filter=" +filter + "&setting=" + setting +
"&alpha=" + alpha + "&filterAlpha=" + filterAlpha + "&filterColor=" + filterColor + "&blurX=" + blurX + "&blurY=" + blurY +
"&interval=" + interval + "&lifeTime=" + lifeTime + "&tweenTime=" + tweenTime + "&strength=" + strength + "&show=" +show;
sign = MD5.hash(Plain);
For example: When
vid="8f8482aaab11dd5f45f183a9192a04c5_8",secretkey="AiDQw1mAmi",username="suki",code="abc",status="1",t="143020010115550947",msg="Errormessage!",fontSize="40",fontColor="0xFFE900",speed="200",filter="on",setting="3",alpha="1",filterAlpha="1",filterColor="0x3914AF",blurX="2",blurY="2",interval="5",lifeTime="3",tweenTime="1",strength="4",show="on"
the string concatenated for MD5 calculation is:
vid=8f8482aaab11dd5f45f183a9192a04c5_8&secretkey=AiDQw1mAmi&username=suki&code=abc&status=1&t=143020010115550947&msg=Errormessage!&fontSize=40&fontColor=0xFFE900&speed=200&filter=on&setting=3&alpha=1&filterAlpha=1&filterColor=0x3914AF&blurX=2&blurY=2&interval=5&lifeTime=3&tweenTime=1&strength=4&show=on
Then the sign is the 32-character lowercase MD5 value: 3b07f56f29b7fd728bf20020442338e7
Example interface response:
{
"status":1,
"username":"elvis",
"sign":"6ab63590797e513d1b6c46b407413478",
"msg":"Errormessage!",
"fontSize":"40",
"fontColor":"0xFFE900",
"speed":"200",
"filter":"on",
"setting":"3",
"alpha":"1",
"filterAlpha":"1",
"filterColor":"0x3914AF",
"blurX":"2",
"blurY":"2",
"tweenTime":"1",
"interval":"5",
"lifeTime":"3",
"strength":"4",
"show":"on"
}
3. Interface Return Parameter Description
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| status | Integer | Yes | / | Whether playback is allowed: 1 Allow 2 Deny |
| username | String | Yes | / | Viewer name, also used as the text content displayed in the watermark |
| sign | String | Yes | / | Interface signature, used to verify if the returned content has been tampered with |
| show | String | Yes | off | When the parameter value is "on", the watermark is displayed; default is off |
| setting | Integer | Yes | 1 | Watermark scrolling style: 1 Scroll from right to left 2 Flash at random positions 3 Flash scroll from right to left |
| speed | Integer | Yes | 200 | Time required for the watermark text to move from right to left, unit: 1/10 second, only effective for setting(1, 3) |
| lifeTime | Integer | Yes | 3 | Watermark text display time, unit: seconds, only effective for setting(2) |
| interval | Integer | Yes | 5 | Watermark text hidden interval time, unit: seconds, only effective for setting(2, 3) |
| tweenTime | Integer | Yes | 1 | Watermark text fade in/out time, unit: seconds (Deprecated) |
| fontSize | Integer | Yes | 30 | Font size of the watermark text |
| fontColor | String | Yes | 0x000000 | Watermark text color, represented in hexadecimal color value, e.g., 0xFF0000, default is black |
| alpha | Float | Yes | 1 | Watermark text transparency, range 0.01~1, parameter value cannot be less than 0.01 |
| filter | String | Yes | off | Whether the watermark text has an outline, on outline off no outline |
| filterAlpha | Float | Yes | 1 | Text outline transparency, range 0~1 |
| filterColor | String | Yes | 0x000000 | Text outline color, represented in hexadecimal color value, e.g., 0xFF0000, default is black |
| strength | Integer | Yes | 4 | Outline strength, range 0~255 |
| blurX | Integer | Yes | 2 | Outline horizontal blur amount, range 0~255 |
| blurY | Integer | Yes | 2 | Outline vertical blur amount, range 0~255 |
| msg | String | Yes | / | Custom error message |
- setting(1): Watermark scrolls from right to left, completes scrolling in
speed/10seconds, then waits 2 seconds before the next scroll - setting(2): Watermark alternately displays and hides at random positions, displays for
lifeTimeseconds each time, then hides forintervalseconds, then the next cycle begins - setting(3): Watermark scrolls from right to left, completes scrolling in
speed/10seconds, waitsspeed/10 + intervalseconds before the next scroll, hides every 3 seconds during scrolling, and displays after 3 seconds of hiding
- When the interface is only used for authorization verification, only the three parameters status, username, and sign need to be returned; watermark-related parameters can be omitted.
- Please ensure the interface returns data in utf-8 encoding. Note that the status parameter is an integer type, not a string type.
- Currently, the watermark feature is not supported on mobile H5 players (1.0 does not support it; it is recommended to use 2.0).
- Please try not to modify the player style or the
<video>tag.
