1、下载 jswebrtc.min.js 文件( / dist / jswebrtc.min.js ):
JSWebrtc – 支持 SRS 的 Webrtc 播放器
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: schema,
server: a.hostname,
port: port,
vhost: vhost,
app: app,
stream: 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);
}
JSWebrtc.VideoElement = (function() {
"use strict";
var VideoElement = function(element) {
var url = element.dataset.url;
if (!url) {
throw "VideoElement has no `data-url` attribute";
}
var addStyles = function(element, styles) {
for (var name in styles) {
element.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() {
"use strict";
var Player = function(url, options) {
this.options = options || {};
if (!url.match(/^webrtc?:\/\//)) {
throw "JSWebrtc just work with webrtc";
}
if (!this.options.video) {
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;
let isFirst = true;
for (var key in _self.urlParams.user_query) {
if (key != "api" && key != "play") {
if (isFirst) {
url += "?" + key + "=" + _self.urlParams.user_query[key];
isFirst = false;
} else {
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;
})();
2、放在 vue 项目 static 文件夹,在 index.html 中引入:
<script src="static/jswebrtc.min.js"></script>
3、完整代码:
<template>
<div>
<video id="webrtc" ref="webrtc" style="width:100%;"></video>
</div>
</template>
<script>
export default {
data() {
return {
player: null
};
},
beforeDestroy() {
if (this.player) {
this.player.destroy();
this.player = null;
}
},
mounted() {
this.$nextTick(() => {
this.initPlayer();
});
},
methods: {
initPlayer() {
let url = "webrtc://" + window.location.hostname + "xxx";
let videoDom = document.getElementById('webrtc');
// url 是一个 webrtc 开头的地址 webrtc://xxx
this.player = new JSWebrtc.Player(url, {
video: videoDom,// 用于播放视频的 HTML Video 元素
autoplay: true,// 是否自动播放,默认 false
onPlay: () => {// onPlay(player),播放后回调
videoDom.addEventListener('canplay', function () {
// .play() – 开始 .pause() – 暂停 .stop() – 停止
// .destroy() – 停止播放并清理相关的播放资源 .paused – 只读,是否暂停播放
videoDom.play();
})
}
// onPause(player),暂停后回调
})
}
},
};
</script>