综合
1. 综合演示
2. 操作说明
1、open、close、destroy 按钮: 分别对应打开、关闭、销毁播放器
2、URL 输入框: 对应播放的视频URL
3、Live 选项:表示是否是直播流
4、Use Flv 选项:表示是否优先使用flv格式播放
5、DANMU 输入框: 输入弹幕内容,
6、Send Danmu、Enable Danmu、Disable Danmu 按钮: 分别对应发送弹幕、允许弹幕、禁止弹幕,注意:只是本地测试,并未发送到弹幕服务器。
7、Load Chapters 按钮: 加载视频章节
8、Load Subtitle、Remove Subtitle 按钮: 分别对应加载字幕、移除字幕
9、MStreams 按钮:点开后弹出输入框,可输入多个视频源,支持多种格式,具体参看源地址url参数说明:
1、json对象
{ "HD1": "https://example.com/stream-hd.flv", "SD1": "https://example.com/stream-ld.flv ", "SD2": "https://example.com/stream-sd.flv" }
2、json列表 方式一: 每个列表项对应一个json对象,包含name和url两个属性,name表示视频源的名称,url表示视频源的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列表 方式二: 每个列表项对应一个列表:第0个位置为url;第1个位置为流的名称name,可以缺省;第2个位置为mime类型,通常缺省。
[ [ "https://example.com/stream-uhd.flv", "HD1" ], [ "https://example.com/stream-hd.flv", "SD1" ], [ "https://example.com/stream-ld.flv", "SD2" ] ]
10、stream_type 列表框: 选择视频源的格式,可选值有: httpflv、hls、dash、webrtc、mpegts
11、CurrentTime 按钮: 获得当前时间
12、Start、End、diffTime 显示:表示开始时间、结束时间、时间差(毫秒),可看到一个输入源从打开到开始播放需要的耗时
3. 注意事项
1、测试rtsp协议,需要开启媒体网关服务器,具体参看播放rtsp流,
2、自带弹幕UI发送弹幕,需要开启弹幕服务器,具体参看弹幕设置说明
3、如需测试rtsp协议、弹幕功能,请加微信号: chenfanyu42 申请加入(请备注"
zwplayer
")4、进度条预览图、字幕、章节标记针对的是测试视频,使用时需要替换为实际的进度条预览图、字幕、章节标记。
4. 示例代码
示例代码仅供参考,实际使用时需要根据实际情况进行修改。
<!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>综合演示</title> <script type="text/javascript" charset="utf-8" src="https://cdn.zwplayer.cn/v3/zwplayer/zwplayer.js"></script> <style> html, body { width: 100%; height: 100%; margin: 0; padding: 0; } .player-wrap { position: relative; padding: 20px; width: 80%; margin-left: auto; margin-right: auto; min-width: 1024px; } .vxplayer { width: 1024px; height: 576px; margin: 0 auto; background-color: #222; } .vxplayer-toolbar { height: 40px; border: 1px solid #c0c0c0; margin-top: 20px; width: 100%; margin-left: auto; margin-right: auto; padding: 6px 10px; box-sizing: border-box; position: relative; } .vxplayer-toolbar .btn { border: 1px solid #a0a0a0; border-radius: 5px; min-width: 60px; height: 26px; box-sizing: border-box; margin: 0 5px; float: left; } .vxplayer-toolbar .btn:hover { background: #b8e1ee; border-color: #89baf2; color: #003566; } .vxplayer-toolbar .btn:hover { background: #83aab7; color: #016789; border-color: #2c5c81; } .url-inputbox { height: 26px; display: inline-block; padding: 0px 10px; float: left; font-size: 14px; } .url-inputbox .label { display: inline-block; width: 60px; height: 26px; line-height: 26px; padding-right: 6px; text-align: right; } .url-inputbox input[type="text"] { height: 26px; width: 500px; border: 1px solid #ccc; border-radius: 3px; line-height: 22px; box-sizing: border-box; padding-left: 6px; padding-right: 6px; } .opt-panel { width: 160px; float: left; padding: 3px 5px; } .time-info { padding: 10px; line-height: 20px; } .time-info .time-item { display: inline-block; min-width: 100px; padding: 0 5px; } .time-info .time-item SPAN.time-value { display: inline-block; padding-left: 6px; width: 120px; font-weight: 600; } .vxplayer-toolbar.danmubar { height: 48px; background-color: #474343; padding-left: 200px; margin-top: 0; border-top: 0; border-color: #000; } .popup_box { position: absolute; width: 800px; height: 300px; left: 100px; bottom: 0px; padding: 6px; background-color: #f0f0f0; border: 1px solid #ccc; display: none; z-index: 10000; } .popup_box .m-urls-box { display: block; width: 100%; height: 260px; border-radius: 5px; box-sizing: border-box; padding: 6px; white-space: nowrap; } .popup_box .popup-btoolbar { overflow: hidden; padding: 8px 0; } .cbx { height: 24px; border: 1px solid #888; border-radius: 3px; float: left; } .current-time-view { } </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) { // 精确到毫秒的简短格式:HH:MM:SS.mmm 使用本地时间 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 元素ID ,也可以直接的DOM对象 playerDom videoElm: '#videoview', //videoElm 元素 videostyle: "width:100%;height:100%;", reconnect: true, autoplay: true, nativecontrols: false, isLive: isLive, useOldFlv: false, useFlv: isUseFlv, streamtype: streamtype, // hasAudio: false, xmc_url: 'https://xmc.zwplayer.cn:8138/', //请填写实际的地址 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: true, disableVolumeControl: false, thumbnails: thumbnails, fluid: true, // 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/"; //替换为实际弹幕服务器地址 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: '与互动服务器的重新建立连接成功。', 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; //获得授予的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', '登录到互动服务器返回不成功。<br/>可能影响聊天互动!'); } else { userCurrent.loginChat = true; } } else if (msgObj.type === 'logout') { //用户自己登出的回馈 delete userCurrent.loginChat; } else if (msgObj.type === 'event') { if (msgObj.event === 'joinroom') { //别的用户与自己连接到聊天服务器 if (msgObj.uid) { if (msgObj.uid === window.current_uid) { userCurrent.joinroom = true; } } } else if (msgObj.event === 'leaveroom' || msgObj.event === 'exitroom') { //别的用户离开聊天服务器 if (msgObj.uid) { if (msgObj.uid === window.current_uid) { userCurrent.joinroom = false; } } } else if (msgObj.event === 'login') { //别的用户连接到聊天服务器 if (msgObj.uid) { } } else if (msgObj.event === 'logout') { //别的用户离开聊天服务器 if (msgObj.uid) { } } } else if (msgObj.type == 'userlist') { //连接到聊天服务器后服务器马上推送在线用户列表,该列表不包括自己 if (msgObj.users.length > 1) { } } else if (msgObj.type === 'setuserid') { //连接建立后将收到这个事件 window.current_uid = msgObj.uid; userCurrent.userid = msgObj.uid; } } } } }; wsChat.onclose = function () { chat_log("wsChat onclose"); wsChat = null; toast('showToast', { text: '与互动服务器的连接已经断开。', sticky: false, stayTime: 3000, position: 'top-center', type: 'notice' }); if (force_close) { } else if (!reconnect) { //当前不是重连状态,则尝试重连 reconnect = true; //设置为正在重连状态 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: '聊天交互服务没有连接成功或已经中断!<br/>正在尝试重连,请稍候再试...', 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(); </script> </head> <body> <div class="player-wrap"> <div class="vxplayer" id="player-holder"> <!-- video id="videoview"> </video //--> </div> <div class="vxplayer-toolbar danmubar" id="player-dammu-controlbar"></div> <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>注意:建议使用chrome浏览器,如果遇到跨域问题不能播放,可加--disable-web-security 参数启动chrome浏览器。</span> </div> </div> </body> </html>