UNPKG

@zhsz/cool-design-dv

Version:

362 lines (361 loc) 11.2 kB
"use strict"; var JSWebrtc = { Player: null, VideoElement: null, CreateVideoElements: function() { var elements = document.querySelectorAll(".jswebrtc"); for (var i = 0; i < elements.length; i++) { new JSWebrtc.VideoElement(elements[i]); } }, FillQuery: function(query_string, obj) { obj.user_query = {}; if (query_string.length == 0) return; if (query_string.indexOf("?") >= 0) query_string = query_string.split("?")[1]; var queries = query_string.split("&"); for (var i = 0; i < queries.length; i++) { var query = queries[i].split("="); obj[query[0]] = query[1]; obj.user_query[query[0]] = query[1]; } if (obj.domain) obj.vhost = obj.domain; }, ParseUrl: function(rtmp_url) { var a = document.createElement("a"); a.href = rtmp_url.replace("rtmp://", "http://").replace("webrtc://", "http://").replace("rtc://", "http://"); var vhost = a.hostname; var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1); var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1); app = app.replace("...vhost...", "?vhost="); if (app.indexOf("?") >= 0) { var params = app.substr(app.indexOf("?")); app = app.substr(0, app.indexOf("?")); if (params.indexOf("vhost=") > 0) { vhost = params.substr(params.indexOf("vhost=") + "vhost=".length); if (vhost.indexOf("&") > 0) { vhost = vhost.substr(0, vhost.indexOf("&")); } } } if (a.hostname == vhost) { var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/; if (re.test(a.hostname)) vhost = "__defaultVhost__"; } var schema = "rtmp"; if (rtmp_url.indexOf("://") > 0) schema = rtmp_url.substr(0, rtmp_url.indexOf("://")); var port = a.port; if (!port) { if (schema === "http") { port = 80; } else if (schema === "https") { port = 443; } else if (schema === "rtmp") { port = 1935; } else if (schema === "webrtc" || schema === "rtc") { port = 1985; } } var ret = { url: rtmp_url, schema, server: a.hostname, port, vhost, app, stream }; JSWebrtc.FillQuery(a.search, ret); return ret; }, HttpPost: function(url, data) { return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) { var respone = JSON.parse(xhr.responseText); xhr.onreadystatechange = new Function(); xhr = null; resolve(respone); } }; xhr.open("POST", url, true); xhr.timeout = 5e3; xhr.responseType = "text"; xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(data); }); } }; if (document.readyState === "complete") { JSWebrtc.CreateVideoElements(); } else { document.addEventListener("DOMContentLoaded", JSWebrtc.CreateVideoElements); window.JSWebrtc = JSWebrtc; } JSWebrtc.VideoElement = function() { var VideoElement = function(element) { var url = element.dataset.url; if (!url) { throw "VideoElement has no `data-url` attribute"; } var addStyles = function(element2, styles) { for (var name in styles) { element2.style[name] = styles[name]; } }; this.container = element; addStyles(this.container, { display: "inline-block", position: "relative", minWidth: "80px", minHeight: "80px" }); this.video = document.createElement("video"); this.video.width = 960; this.video.height = 540; addStyles(this.video, { display: "block", width: "100%" }); this.container.appendChild(this.video); this.playButton = document.createElement("div"); this.playButton.innerHTML = VideoElement.PLAY_BUTTON; addStyles(this.playButton, { zIndex: 2, position: "absolute", top: "0", bottom: "0", left: "0", right: "0", maxWidth: "75px", maxHeight: "75px", margin: "auto", opacity: "0.7", cursor: "pointer" }); this.container.appendChild(this.playButton); var options = { video: this.video }; for (var option in element.dataset) { try { options[option] = JSON.parse(element.dataset[option]); } catch (err) { options[option] = element.dataset[option]; } } this.player = new JSWebrtc.Player(url, options); element.playerInstance = this.player; if (options.poster && !options.autoplay) { options.decodeFirstFrame = false; this.poster = new Image(); this.poster.src = options.poster; this.poster.addEventListener("load", this.posterLoaded); addStyles(this.poster, { display: "block", zIndex: 1, position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }); this.container.appendChild(this.poster); } if (!this.player.options.streaming) { this.container.addEventListener("click", this.onClick.bind(this)); } if (options.autoplay) { this.playButton.style.display = "none"; } if (this.player.audioOut && !this.player.audioOut.unlocked) { var unlockAudioElement = this.container; if (options.autoplay) { this.unmuteButton = document.createElement("div"); this.unmuteButton.innerHTML = VideoElement.UNMUTE_BUTTON; addStyles(this.unmuteButton, { zIndex: 2, position: "absolute", bottom: "10px", right: "20px", width: "75px", height: "75px", margin: "auto", opacity: "0.7", cursor: "pointer" }); this.container.appendChild(this.unmuteButton); unlockAudioElement = this.unmuteButton; } this.unlockAudioBound = this.onUnlockAudio.bind(this, unlockAudioElement); unlockAudioElement.addEventListener( "touchstart", this.unlockAudioBound, false ); unlockAudioElement.addEventListener("click", this.unlockAudioBound, true); } }; VideoElement.prototype.onUnlockAudio = function(element, ev) { if (this.unmuteButton) { ev.preventDefault(); ev.stopPropagation(); } this.player.audioOut.unlock( (function() { if (this.unmuteButton) { this.unmuteButton.style.display = "none"; } element.removeEventListener("touchstart", this.unlockAudioBound); element.removeEventListener("click", this.unlockAudioBound); }).bind(this) ); }; VideoElement.prototype.onClick = function(ev) { if (this.player.isPlaying) { this.player.pause(); this.playButton.style.display = "block"; } else { this.player.play(); this.playButton.style.display = "none"; if (this.poster) { this.poster.style.display = "none"; } } }; VideoElement.PLAY_BUTTON = '<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 200 200" alt="Play video"><circle cx="100" cy="100" r="90" fill="none" stroke-width="15" stroke="#fff"/><polygon points="70, 55 70, 145 145, 100" fill="#fff"/></svg>'; VideoElement.UNMUTE_BUTTON = '<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 75 75"><polygon class="audio-speaker" stroke="none" fill="#fff" points="39,13 22,28 6,28 6,47 21,47 39,62 39,13"/><g stroke="#fff" stroke-width="5"><path d="M 49,50 69,26"/><path d="M 69,50 49,26"/></g></svg>'; return VideoElement; }(); JSWebrtc.Player = function() { var Player = function(url, options) { this.options = options || {}; if (!url.match(/^webrtc?:\/\//)) { if (this.options.onError) { this.options.onError("JSWebrtc just work with webrtc"); } throw "JSWebrtc just work with webrtc"; } if (!this.options.video) { if (this.options.onError) { this.options.onError("VideoElement is null"); } throw "VideoElement is null"; } this.urlParams = JSWebrtc.ParseUrl(url); this.pc = null; this.autoplay = !!options.autoplay || false; this.paused = true; if (this.autoplay) this.options.video.muted = true; this.startLoading(); }; Player.prototype.startLoading = function() { var _self = this; if (_self.pc) { _self.pc.close(); } _self.pc = new RTCPeerConnection(null); _self.pc.ontrack = function(event) { _self.options.video["srcObject"] = event.streams[0]; }; _self.pc.addTransceiver("audio", { direction: "recvonly" }); _self.pc.addTransceiver("video", { direction: "recvonly" }); _self.pc.createOffer().then(function(offer) { return _self.pc.setLocalDescription(offer).then(function() { return offer; }); }).then(function(offer) { return new Promise(function(resolve, reject) { var port = _self.urlParams.port || 1985; var api = _self.urlParams.user_query.play || "/rtc/v1/play/"; if (api.lastIndexOf("/") != api.length - 1) { api += "/"; } var url = "http://" + _self.urlParams.server + ":" + port + api; for (var key in _self.urlParams.user_query) { if (key != "api" && key != "play") { url += "&" + key + "=" + _self.urlParams.user_query[key]; } } var data = { api: url, streamurl: _self.urlParams.url, clientip: null, sdp: offer.sdp }; JSWebrtc.HttpPost(url, JSON.stringify(data)).then( function(res) { resolve(res.sdp); }, function(rej) { reject(rej); } ); }); }).then(function(answer) { return _self.pc.setRemoteDescription( new RTCSessionDescription({ type: "answer", sdp: answer }) ); }).catch(function(reason) { throw reason; }); if (this.autoplay) { this.play(); } }; Player.prototype.play = function(ev) { if (this.animationId) { return; } this.animationId = requestAnimationFrame(this.update.bind(this)); this.paused = false; }; Player.prototype.pause = function(ev) { if (this.paused) { return; } cancelAnimationFrame(this.animationId); this.animationId = null; this.isPlaying = false; this.paused = true; this.options.video.pause(); if (this.options.onPause) { this.options.onPause(this); } }; Player.prototype.stop = function(ev) { this.pause(); }; Player.prototype.destroy = function() { this.pause(); this.pc && this.pc.close() && this.pc.destroy(); this.audioOut && this.audioOut.destroy(); }; Player.prototype.update = function() { this.animationId = requestAnimationFrame(this.update.bind(this)); if (this.options.video.readyState < 4) { return; } if (!this.isPlaying) { this.isPlaying = true; this.options.video.play(); if (this.options.onPlay) { this.options.onPlay(this); } } }; return Player; }();