- Overview
- Name meaning
- Features
- download and install
- User Guide
- Basic Usage
- Vue framework usage
- Parameter Configuration
- method
- event
- Detailed explanation of source address URL parameters
- Logo settings
- Chapter annotation settings
- Progress bar preview image settings
- Subtitle Settings
- Bullet Chat Settings
- Description of Barrage Server
- Play RTSP stream
- autoplay
- shortcut key
- demo
- Basic Usage
- logoSettings
- Chapter annotation settings
- Progress bar preview image settings
- Subtitle Settings
- bullet chat
- Support protocol format
- comprehensive
comprehensive
1. Comprehensive Demo
2. Operation Instructions
1. Open, Close, Destroy buttons: Correspond to opening, closing, and destroying the player respectively
2. URL input box: The video URL to play
3. Live option: Indicates whether it is a live stream
4. Use Flv option: Indicates whether to prioritize FLV format playback
5. DANMU input box: Enter danmu content
6. Send Danmu, Enable Danmu, Disable Danmu buttons: Correspond to sending danmu, enabling danmu, and disabling danmu respectively. Note: This is only for local testing and is not sent to the danmu server.
7. Load Chapters button: Load video chapters
8. Load Subtitle, Remove Subtitle buttons: Correspond to loading subtitles and removing subtitles respectively
9. MStreams button: Click to open an input popup where you can enter multiple video sources. Supports multiple formats. See Source URL Parameter Description for details:
{ "HD1": "https://example.com/stream-hd.flv", "SD1": "https://example.com/stream-ld.flv ", "SD2": "https://example.com/stream-sd.flv" }2. JSON list Method 1: Each list item corresponds to a JSON object containing two properties: name and url. Name represents the video source name, and url represents the video source URL.
[ { "name": "HD1", "url": "https://example.com/media/stream-hd.flv" }, { "name": "SD1", "url": "https://example.com/media/stream-ld.flv", "default": true }, { "name": "SD2", "url": "https://example.com/media/stream-sd.flv" } ]3. JSON list Method 2: Each list item corresponds to a list: Position 0 is the URL; Position 1 is the stream name (optional); Position 2 is the MIME type (usually omitted).
[ [ "https://example.com/stream-uhd.flv", "HD1" ], [ "https://example.com/stream-hd.flv", "SD1" ], [ "https://example.com/stream-ld.flv", "SD2" ] ]
1. JSON object
10. stream_type dropdown: Select the video source format. Optional values: httpflv, hls, dash, webrtc, mpegts
11. CurrentTime button: Get current time
12. Start, End, diffTime display: Represents start time, end time, and time difference (milliseconds). Shows the time required from opening a source to starting playback.
3. Important Notes
1. To test RTSP protocol, you need to enable the media gateway server. See Playing RTSP Streams for details.
2. To send danmu using the built-in danmu UI, you need to enable the danmu server. See Danmu Setup Instructions for details.
3. To test RTSP protocol or danmu features, please add WeChat: zwplayerX to apply.
4. Progress bar thumbnails, subtitles, and chapter markers are for the test video. Replace them with actual progress bar thumbnails, subtitles, and chapter markers when using.
4. Example Code
Example code is for reference only. Modify according to actual requirements when using.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="x5-fullscreen" content="true">
<meta name="full-screen" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="format-detection" content="telephone=no">
<title>ZWPlayer Comprehensive Demo | ZWPlayer Official</title>
<meta name="keywords"
content="ZWPlayer, player feature demo, streaming playback test, HTTP-FLV live, HLS playback, DASH video, WebRTC live, video danmu feature, external subtitles, chapter markers, multi-bitrate switching, developer integration">
<meta name="description"
content="ZWPlayer one-stop feature demonstration and testing platform. Full support for HTTP-FLV, HLS, DASH, WebRTC and other streaming protocols, with interactive testing of advanced features like danmu, subtitle loading, chapter markers, and multi-bitrate switching. Includes detailed code examples to help developers integrate and debug quickly.">
<script type="text/javascript" charset="utf-8" src="https://cdn.zwplayer.cn/v3/zwplayer/zwplayer.js"></script>
<style>
/* ===== Design Variables ===== */
:root {
--primary: #00c6ff;
--primary-dark: #0084cc;
--bg: #ffffff;
--surface: #f5f7fa;
--text: #222;
--text2: #555;
--border: #e2e8f0;
--radius: 12px;
--shadow: 0 4px 12px rgba(0, 0, 0, .08);
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--safe-top: env(safe-area-inset-top, 0);
--safe-bottom: env(safe-area-inset-bottom, 0);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #121212;
--surface: #1e1e1e;
--text: #eee;
--text2: #bbb;
--border: #333;
--shadow: 0 4px 12px rgba(0, 0, 0, .3);
}
}
/* ===== Global Reset ===== */
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-tap-highlight-color: transparent;
}
body {
margin: 0;
font-family: var(--font);
background: var(--bg);
color: var(--text);
line-height: 1.5;
padding-top: var(--safe-top);
padding-bottom: var(--safe-bottom);
}
/* ===== Player Container ===== */
.player-wrap {
max-width: 960px;
margin: 0 auto;
padding: .75rem;
display: flex;
flex-direction: column;
/* gap: .75rem;*/
}
/* 16:9 Adaptive Video Area */
#player-holder {
position: relative;
width: 100%;
/* border-radius: var(--radius);*/
overflow: hidden;
background: #000;
box-shadow: var(--shadow);
aspect-ratio: 16 / 9;
}
.main_bar {
max-width: 960px;
margin: 0 auto;
padding: .75rem;
display: flex;
flex-direction: column;
gap: .75rem;
}
/* ===== Toolbar General ===== */
.danmubar .btn {
border: 1px solid #a0a0a0;
border-radius: 5px;
min-width: 60px;
height: 26px;
box-sizing: border-box;
margin: 0 5px;
float: left;
}
.danmubar .btn:hover {
background: #b8e1ee;
border-color: #89baf2;
color: #003566;
}
.danmubar .btn:hover {
background: #83aab7;
color: #016789;
border-color: #2c5c81;
}
.danmubar {
border: 1px solid #c0c0c0;
width: 100%;
margin-left: auto;
margin-right: auto;
padding: 6px 10px;
box-sizing: border-box;
position: relative;
height: 48px;
background-color: #474343;
padding-left: 200px;
margin-top: 0;
border-top: 0;
border-color: #000;
}
.vxplayer-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: .75rem 1rem;
box-shadow: var(--shadow);
}
/* Button */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 44px; /* iOS recommended touch height */
padding: .5rem 1rem;
font-size: .95rem;
font-weight: 500;
color: #fff;
background: var(--primary);
border: none;
border-radius: 8px;
cursor: pointer;
transition: background .2s;
}
.btn:hover {
background: var(--primary-dark);
}
@media (hover: none) {
.btn:hover {
background: var(--primary);
}
}
.btn:active {
transform: scale(.97);
}
/* Input Box */
input[type="text"],
textarea,
.cbx {
flex: 1 1 140px;
min-width: 0;
padding: .5rem .75rem;
font-size: .95rem;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--bg);
color: var(--text);
}
input[type="text"]:focus,
textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px var(--primary-dark);
}
/* Checkbox */
.opt-panel {
display: flex;
align-items: center;
gap: .25rem .75rem;
flex-wrap: wrap;
}
.opt-panel input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--primary);
}
/* ===== Danmu Bar ===== */
#player-dammubar {
gap: .5rem;
}
#player-dammubar .url-inputbox {
flex: 1 1 180px;
display: flex;
align-items: center;
gap: .5rem;
}
#player-dammubar .label {
font-weight: 500;
color: var(--text2);
white-space: nowrap;
}
/* ===== Multi-Stream Popup ===== */
.popup_box {
position: fixed;
inset: 50% auto auto 50%;
transform: translate(-50%, -50%);
z-index: 999;
width: 90vw;
max-width: 480px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 1rem;
display: none;
flex-direction: column;
gap: 1rem;
}
.popup_box.active {
display: flex;
}
.m-urls-box {
width: 100%;
min-height: 120px;
resize: vertical;
}
.popup-btoolbar {
display: flex;
justify-content: flex-end;
gap: .5rem;
}
/* ===== Bottom Time Info ===== */
.time-info {
font-size: .8rem;
color: var(--text2);
justify-content: space-between;
}
.time-item {
display: flex;
align-items: center;
gap: .25rem;
}
.time-value {
font-weight: 600;
color: var(--primary);
}
/* ===== Narrow Screen Adjustments ===== */
@media (max-width: 600px) {
.vxplayer-toolbar {
gap: .5rem;
}
.btn {
font-size: .9rem;
padding: .5rem .75rem;
}
.time-info {
flex-direction: column;
align-items: flex-start;
gap: .25rem;
}
}
</style>
<script language="javascript">
var thumbnails = {
"url": "https://cdn.zwplayer.cn/media/b44c43c90be3521bc352aad1e80f9cd0_thumb.jpg",
"width": 160,
"height": 90,
"row": 9,
"col": 9,
"total": 74
};
function formatTimeShortWithMs(timestamp) {
// Short format accurate to milliseconds: HH:MM:SS.mmm using local time
var date = new Date(timestamp);
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
var milliseconds = date.getMilliseconds();
seconds = seconds % 60;
minutes = minutes % 60;
hours = hours % 24;
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0')
].join(':') + '.' + milliseconds.toString().padStart(3, '0');
}
function onOpenUrl(urlObj) {
var url;
var streamtype = "";
if (urlObj) {
url = urlObj;
} else {
var urlbox = document.getElementById('url-box');
url = urlbox.value;
if (!url) {
alert('Please enter url.');
urlbox.focus();
return;
}
streamtype = document.getElementById('stream_type_cbx').value;
}
var isLive = document.getElementById('isLive-flag').checked;
var isUseFlv = document.getElementById('isUseFlv-flag').checked;
var timer = new Date();
var timeStart = timer.getTime();
document.getElementById('time-start').innerHTML = formatTimeShortWithMs(timeStart);
if (!window.zwplayer) {
//var playerDom = document.querySelector('#player-holder');
window.zwplayer = new ZWPlayer({
url: url,
playerElm: '#player-holder', //player element ID, can also be direct DOM object playerDom
videoElm: '#videoview', //videoElm element
// videostyle: "width:100%;height:100%;",
reconnect: true,
autoplay: false,
nativecontrols: false,
isLive: isLive,
useOldFlv: false,
useFlv: isUseFlv,
streamtype: streamtype,
// hasAudio: false,
xmc_url: 'https://xmcdemo.zwplayer.cn:11683/', //Please fill in the actual address
rtsp_over_tcp: true, // Use TCP transmission
//rtsp_bypass: true, // Enable encoding bypass
controlbar: true,
infoButton: true,
speedButton: true,
optionButton: true,
snapshotButton: true,
chapterButton: true,
enableDanmu: true,
//poster: 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.jpg',
useProgressTooltip: true,
fixedControlbar: true,
hidePlayBtn: false,
disablePlayBtn: false,
disableSeek: false,
disableFullscreenWin: false,
disablePicInPic: false,
disableVolumeControl: false,
thumbnails: thumbnails, //Use actual thumbnail configuration
disableMutedConfirm: true,
fluid: true,
ratio: "16:9",
// chapters: 'http://192.168.1.202/chapters.json?v=3',
muted: false,
onready: function () {
console.log("Player onready");
},
onfirstframe: function () {
console.log("Player onfirstframe");
},
onnetclose: function () {
console.log("Player onnetclose");
},
onneterror: function () {
console.log("Player onneterror");
},
onmediaevent: function (event) {
if (['play', 'pause', 'seeked', 'ended', 'error'].includes(event.type)) {
console.log("player video Event:", event);
if (event.type == 'seeked') {
var currentTime = event.srcElement.currentTime;
console.log("player currentTime:", currentTime);
}
if (event.type === 'play') {
var timeEnd = (new Date()).getTime();
var timeTotal = timeEnd - timeStart;
document.getElementById('time-end').innerHTML = formatTimeShortWithMs(timeEnd);
document.getElementById('time-total').innerHTML = timeTotal;
}
}
},
sendDanmu: function (text) {
//alert(text);
if (typeof window.ws_send === 'function') {
window.ws_send(text);
}
}
});
window.zwplayer.buildDanmuControlbar('player-dammu-controlbar');
} else {
window.zwplayer.play(url, isLive, false);
}
}
function onClose() {
if (window.zwplayer) {
window.zwplayer.stop();
}
}
function ondestroy() {
if (window.zwplayer) {
window.zwplayer.destroy();
delete window.zwplayer;
}
}
//multistream-urls multistream-urls-input
function onMultiStreams() {
var popupbox = document.getElementById('multistream-urls-box');
var display = popupbox.style.display;
display = display !== 'block' ? 'block' : 'none';
popupbox.style.display = display;
}
function onGetCurrentTime() {
if (window.zwplayer) {
var curTime = window.zwplayer.CurrentTime;
document.getElementById('current-time-view').innerHTML = curTime;
}
}
function onMultiStreamsOpen() {
var inputBox = document.getElementById('multistream-urls-input');
var urlText = inputBox.value;
urlText = urlText.trim();
if (urlText.startsWith('{') || urlText.startsWith('[')) {
try {
var urls = JSON.parse(urlText);
if (urls) {
var urlInfo = {
murls: urls,
multistream: 1
};
onOpenUrl(urlInfo);
document.getElementById('multistream-urls-box').style.display = 'none';
}
} catch (err) {
console.error(err);
}
}
}
function onMultiStreamsCancel() {
document.getElementById('multistream-urls-box').style.display = 'none';
}
function onload() {
url = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4';
document.getElementById('url-box').value = url;
setTimeout(function () {
onOpenUrl();
}, 100);
//new VConsole();
}
function onSendDanmu() {
var danmuText = document.getElementById('danmu-box').value;
if (!danmuText)
return;
var danmu = {
//outlineColor: '#f00',
border: '1px solid #ccc',
text: danmuText
};
if (window.zwplayer) {
window.zwplayer.appendDanmu(danmu);
}
}
function onDisableDanmu() {
if (window.zwplayer) {
window.zwplayer.setEnableDanmu(false);
}
}
function onEnableDanmu() {
if (window.zwplayer) {
window.zwplayer.setEnableDanmu(true);
}
}
function onLoadChapters() {
if (window.zwplayer) {
window.zwplayer.setChapters('https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185_chapter.json');
}
}
function onLoadSubtitle() {
if (window.zwplayer) {
var subtitleURL_korean = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggtFawR3lNR7hQ2_kore.srt';
var subtitleURL_japan = 'https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggtFawR3lNR7hQ2_jpan.srt';
window.zwplayer.addSubtitle(subtitleURL_korean, '1');
window.zwplayer.addSubtitle(subtitleURL_japan, '2');
}
}
function onRemoveSubtitle() {
if (window.zwplayer) {
window.zwplayer.removeSubtitle();
}
}
window.channelinfo = {
id: '001'
};
(function (root) {
var wsChat = null;
var logView = null;
var wschat_server = "ws://10.234.1.106:3000/"; //Replace with actual danmu server address
var reconnect = false;
var msg_queue = [];
var force_close = false;
var userCurrent = {};
function chat_log(str) {
console.log(str);
}
function toast(type, msg) {
}
root.ws_init = function chatSocketInit() {
if (wsChat) return wsChat;
// Connect to Web Socket.
// Change host/port here to your own Web Socket server.
try {
wsChat = new WebSocket(wschat_server);
} catch (e) {
return false;
reconnect = false;
}
// Set event handlers.
wsChat.onopen = function () {
chat_log("wsChat onopen");
if (window.enableBalance == '1' && !window.mediaserver) {
root.ws_getmediaserver();
}
window.setTimeout(function () {
delete userCurrent.loginChat;
if (!wsChat) return;
wsChat.send('{type:"hello"}');
if (reconnect) {
toast('showToast', {
text: 'Successfully reconnected to interactive server.',
sticky: false,
stayTime: 3000,
position: 'top-center',
type: 'notice'
});
reconnect = false;
if (msg_queue['userlogin']) {
wsChat.send(msg_queue['userlogin']);
delete msg_queue['userlogin'];
}
}
}, 40);
};
wsChat.onmessage = function (e) {
// e.data contains received string.
chat_log("wsChat onmessage: " + e.data);
if (e.data.length > 0) {
if (e.data.charAt(0) === '{') {
var msgContent;
var msgObj = JSON.parse(e.data);
if (typeof (msgObj) === 'object') {
if (msgObj.text)
msgContent = msgObj.text;
else
msgContent = '';
if (msgObj.type === 'danmu') {
if (window.zwplayer) {
if (msgContent == '') return;
// msgObj.text = msgContent;
window.zwplayer.appendDanmu(msgObj);
}
} else if (msgObj.type === 'hello') {
window.joinRoomOk = true;
//Get assigned UID
window.current_uid = msgObj.uid;
var roomname = 'videoroom_' + window.channelinfo.id;
window.ws_send('{"type":"join","room":"' + roomname + '"}');
} else if (msgObj.type === 'login') {
if (!msgObj.result || msgObj.result != 'success') {
toast('showNoticeToast', 'Login to interactive server was unsuccessful.<br/>Chat interaction may be affected!');
} else {
userCurrent.loginChat = true;
}
} else if (msgObj.type === 'logout') {
//Feedback for user logout
delete userCurrent.loginChat;
} else if (msgObj.type === 'event') {
if (msgObj.event === 'joinroom') {
//Other users and self connecting to chat server
if (msgObj.uid) {
if (msgObj.uid === window.current_uid) {
userCurrent.joinroom = true;
}
}
} else if (msgObj.event === 'leaveroom' || msgObj.event === 'exitroom') {
//Other users leaving chat server
if (msgObj.uid) {
if (msgObj.uid === window.current_uid) {
userCurrent.joinroom = false;
}
}
} else if (msgObj.event === 'login') {
//Other users connecting to chat server
if (msgObj.uid) {
}
} else if (msgObj.event === 'logout') {
//Other users leaving chat server
if (msgObj.uid) {
}
}
} else if (msgObj.type == 'userlist') {
//After connecting to chat server, server immediately pushes online user list, excluding self
if (msgObj.users.length > 1) {
}
} else if (msgObj.type === 'setuserid') {
//Received this event after connection established
window.current_uid = msgObj.uid;
userCurrent.userid = msgObj.uid;
}
}
}
}
};
wsChat.onclose = function () {
chat_log("wsChat onclose");
wsChat = null;
toast('showToast', {
text: 'Connection to interactive server has been disconnected.',
sticky: false,
stayTime: 3000,
position: 'top-center',
type: 'notice'
});
if (force_close) {
} else if (!reconnect) { //If not currently reconnecting, try to reconnect
reconnect = true; //Set to reconnecting state
var on_reconnect = function () {
if (force_close) return;
wschat = root.wschat = root.ws_init();
if (!wschat) {
reconnect = true;
window.setTimeout(on_reconnect, 10000);
}
}
window.setTimeout(on_reconnect, 10000);
}
};
wsChat.onerror = function () {
chat_log("wsChat onerror");
wsChat = null;
};
return wsChat;
}
root.ws_send = function wsChatSend(data) {
chat_log("wsChat send: " + data);
if (wsChat) {
wsChat.send(data);
return true;
} else {
toast('showToast', {
text: 'Chat interaction service failed to connect or has been interrupted!<br/>Trying to reconnect, please try again later...',
sticky: false,
stayTime: 3000,
position: 'top-center',
type: 'warning'
});
reconnect = true;
wschat = root.wschat = root.ws_init();
return false;
}
}
root.ws_queue = function wsQueue(type, msg) {
msg_queue[type] = msg;
}
root.ws_close = function wsChatClose() {
if (wsChat) {
force_close = true;
wsChat.close();
wsChat = null;
} else {
}
}
root.wschat = wsChat;
})(window);
window.ws_init();
// 2. Automatically called after page load completes
document.addEventListener('DOMContentLoaded', onload);
</script>
</head>
<body>
<div class="player-wrap">
<div class="vxplayer" id="player-holder">
<!-- video id="videoview"> </video //-->
</div>
<div class="danmubar" id="player-dammu-controlbar"></div>
<div class="main_bar">
<div class="vxplayer-toolbar" id="player-toolbar">
<button class="btn" onclick="onOpenUrl();">Open</button>
<button class="btn" onclick="onClose();">Close</button>
<button class="btn" onclick="ondestroy();">Destroy</button>
<div class="url-inputbox">
<span class="label">URL:</span>
<input type="text" id="url-box"
value="https://cdn.zwplayer.cn/media/VMAP9lxJvRpgn5sP3lV6rQ9qkzQmh5psggso3185.mp4">
</div>
<div class="opt-panel">
<input type="checkbox" id="isLive-flag" value="1">
<label for="isLive-flag">LIVE</label>
<input type="checkbox" id="isUseFlv-flag" value="1" title="use flv player">
<label for="isUseFlv-flag">Use Flv</label>
</div>
<div class="popup_box" id="multistream-urls-box">
<textarea class="m-urls-box" id="multistream-urls-input"></textarea>
<div class="popup-btoolbar">
<button class="btn" onclick="onMultiStreamsOpen();">Open</button>
<button class="btn" onclick="onMultiStreamsCancel();">Cancel</button>
</div>
</div>
</div>
<div class="vxplayer-toolbar" id="player-dammubar">
<div class="url-inputbox">
<span class="label">DANMU:</span>
<input type="text" id="danmu-box" value="">
</div>
<button class="btn" onclick="onSendDanmu();">Send</button>
<button class="btn" onclick="onEnableDanmu();">Enable Danmu</button>
<button class="btn" onclick="onDisableDanmu();">Disable Danmu</button>
</div>
<div class="vxplayer-toolbar" id="player-other">
<button class="btn" onclick="onLoadChapters();">Load Chapters</button>
<button class="btn" onclick="onLoadSubtitle();">Load Subtitle</button>
<button class="btn" onclick="onRemoveSubtitle();">Remove Subtitle</button>
<button class="btn" onclick="onMultiStreams();">MStreams</button>
<select class="cbx" id="stream_type_cbx">
<option value="">stream_type No specified</option>
<option value="httpflv">httpflv</option>
<option value="hls">hls</option>
<option value="dash">dash</option>
<option value="webrtc">webrtc</option>
<option value="mpegts">mpegts</option>
</select>
<button class="btn" onclick="onGetCurrentTime();">CurrentTime</button>
<span id="current-time-view" class="current-time-view"></span>
</div>
<div class="vxplayer-toolbar time-info">
<div class="time-item"><span>start:</span><span class="time-value" id="time-start"></span></div>
<div class="time-item"><span>end:</span><span class="time-value" id="time-end"></span></div>
<div class="time-item"><span>diffTime:</span><span class="time-value" id="time-total"></span>ms</div>
</div>
<div>
<span>Note: Chrome browser is recommended. If cross-domain issues prevent playback, launch Chrome with the --disable-web-security parameter.</span>
</div>
</div>
</div>
</body>
</html>