提交 fba2db77 作者: 翁进城

优化视频组件代码,支持品牌播放器选择

上级 05893c9a
<template>
<div class="flv">
<video controls autoplay="autoplay" muted="muted" id="videoElement">
<video controls autoplay="autoplay" muted="muted" ref="video">
<source type="video/mp4" />
</video>
<!-- <button @click="play">播放</button> -->
......@@ -17,20 +17,20 @@ export default {
return {
player: null,
flvPlayer: null,
videourl: ""
};
},
mounted() {
this.videourl = this.data.vUrl
console.log(this.data.vUrl);
this.ngOnInit();
this.init();
},
beforeDestroy() {
this.destroy();
},
methods: {
//页面初始化
ngOnInit() {
init() {
if (flvjs.default.isSupported()) {
// 获取DOM对象
this.player = document.getElementById("videoElement");
this.player = this.$refs.video;
// 创建flvjs对象
this.flvPlayer = flvjs.default.createPlayer({
type: "flv", // 指定视频类型
......@@ -38,21 +38,12 @@ export default {
hasAudio: false, // 关闭声音
cors: true, // 开启跨域访问
// url: "http://pzlink999bju.powzamedia.com:8000/live/flv/test/mlinkm/channel1?only-video=1", // 指定流链接
url: this.getTarget(this.videourl), // 指定流链接
url: this.data.vUrl, // 指定流链接
});
// 将flvjs对象和DOM对象绑定
this.flvPlayer.attachMediaElement(this.player);
}
this.play()
},
getTarget(url) {
const reg = /^pzsp:\/\/(.*):7788.*\/trans\/(.*)\/mlinkm\/(.*)\?/;
url.match(reg);
const hostname = RegExp.$1;
const groupname = RegExp.$2;
const streamname = RegExp.$3;
return `http://${hostname}:8000/live/flv/${groupname}a/mlinkm/${streamname}?only-video=1`;
this.play();
},
// 播放视频
......@@ -62,7 +53,7 @@ export default {
},
//页面退出销毁和暂停播放
ngOnDestroy() {
destroy() {
this.flvPlayer.pause();
this.flvPlayer.unload();
// 卸载DOM对象
......@@ -70,6 +61,18 @@ export default {
// 销毁flvjs对象
this.flvPlayer.destroy();
},
fullScreen() {
let video = this.$refs.video;
if (video.webkitRequestFullScreen) {
video.webkitRequestFullScreen();
} else if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen)
setTimeout(() => {
video.play();
}, 200);
},
},
};
</script>
......@@ -78,7 +81,7 @@ export default {
.flv {
width: 100%;
height: 100%;
#videoElement {
video {
width: 100%;
height: 100%;
}
......
......@@ -113,7 +113,7 @@ export default {
const image = canvasToDataURL(canvas);
let blob = dataURLToBlob(image);
return blob;
}
},
}
};
</script>
......
<template>
<div class="cpt-player-webrtc">
<video id="rtc_media_player" ref="webrtc" controls autoplay></video>
</div>
</template>
<script>
import $ from "./jquery-1.10.2.min";
import {
SrsRtcPublisherAsync,
SrsRtcPlayerAsync,
SrsRtcFormatSenders,
} from "./srs.sdk";
window.$ = $;
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
data() {
return {
sdk: null,
};
},
watch: {
data: {
handler(value) {
this.$nextTick(() => {
this.init();
});
},
deep: true,
immediate: true,
},
},
beforeDestroy() {
this.sdk.close();
},
methods: {
pause() {
if (this.$refs["webrtc"]) {
this.$refs["webrtc"].pause();
}
},
play() {
if (this.$refs["webrtc"]) {
this.$refs["webrtc"].play();
}
},
init() {
let _this = this;
if (this.$refs["webrtc"]) {
if (this.sdk) {
this.sdk.close();
}
this.sdk = new SrsRtcPlayerAsync();
this.$refs["webrtc"].srcObject = this.sdk.stream;
this.sdk
.play(this.data.vUrl)
.then(function (session) {})
.catch(function (reason) {
console.log('srs err', reason)
_this.sdk.close();
});
}
},
fullScreen() {
let video = this.$refs["webrtc"];
if (video.webkitRequestFullScreen) {
video.webkitRequestFullScreen();
} else if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen)
setTimeout(() => {
video.play();
}, 200);
},
},
};
</script>
<style lang="scss" scoped>
.cpt-player-webrtc {
height: 100%;
width: 100%;
background-color: #000;
video {
width: 100%;
height: 100%;
object-fit: fill;
}
}
</style>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
//
// Copyright (c) 2013-2021 Winlin
//
// SPDX-License-Identifier: MIT
//
'use strict';
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
export function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the publish:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.publish = async function (url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
if (data.code) {
reject(data);
return;
}
resolve(data);
}).fail(function (reason) {
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/publish/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = 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);
// parse the vhost in the params of app, that srs supports.
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("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, 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;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
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 elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-await-promise based SRS RTC Player.
export function SrsRtcPlayerAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the play:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.play = async function(url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function(resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType:'application/json', dataType: 'json'
}).done(function(data) {
if (data.code) {
reject(data); return;
}
resolve(data);
}).fail(function(reason){
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/play/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = 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);
// parse the vhost in the params of app, that srs supports.
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("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, 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;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
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 elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
console.log("RTCPeerConnection");
self.pc = new RTCPeerConnection(null);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
export function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}
<!-- 总览页视频 -->
<template>
<div class="cpt-player-webrtc">
<video ref="webrtc" :controls="controls">
<source />
</video>
</div>
</template>
<script>
import JSWebrtc from "./jswebrtc";
export default {
props: {
controls: {
type: Boolean,
default: () => true,
},
data: {
type: Object,
default: () => ({}),
},
},
data() {
return {
player: null,
value: "",
times: 0,
};
},
watch: {
data: {
handler(value) {
if (value) {
this.$nextTick(() => {
this.player = new JSWebrtc.Player(value.vUrl, {
video: this.$refs["webrtc"],
autoplay: true,
});
});
}
},
deep: true,
immediate: true,
},
},
beforeDestroy(){
this.player?.destroy();
},
methods: {
fullScreen() {
let video = this.$refs["webrtc"];
if (video.webkitRequestFullScreen) {
video.webkitRequestFullScreen();
} else if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen)
setTimeout(() => {
video.play();
}, 200);
},
exit() {
let video = this.$refs["webrtc"];
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
setTimeout(() => {
video.play();
}, 200);
},
},
};
</script>
<style lang="scss" scoped>
.cpt-player-webrtc {
height: 100%;
width: 100%;
background-color: #000;
video {
width: 100%;
height: 100%;
object-fit: fill;
position: relative;
.title {
position: absolute;
font-family: MicrosoftYaHei-Bold;
font-size: 16px;
color: #69ceff;
letter-spacing: 1px;
text-align: center;
font-weight: 700;
height: 42px;
line-height: 42px;
position: relative;
box-sizing: border-box;
position: absolute;
bottom: 0;
}
}
}
</style>
\ No newline at end of file
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;
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
}();
export default JSWebrtc;
\ No newline at end of file
......@@ -17,8 +17,15 @@
</div>
<div v-show="!fullScreen" class="close" @click="onClose(data)">关闭</div>
</div>
<el-tooltip content="模式切换" placement="bottom" v-if="playerCom !== 'QingLiuPlayer' && fpvUrl.vUrl">
<div class="fpv-switch iconfont icon-moshiqiehuan modelStyle pointer" @click="onChangeFPV"></div>
<el-tooltip
content="模式切换"
placement="bottom"
v-if="playerCom !== 'QingLiuPlayer' && fpvUrl.vUrl"
>
<div
class="fpv-switch iconfont icon-moshiqiehuan modelStyle pointer"
@click="onChangeFPV"
></div>
</el-tooltip>
<components
:is="playerCom"
......@@ -98,10 +105,15 @@
<div v-else class="left ml24" style="margin-left: 124px; color: #fff;"></div>
<div class="ctrl-items">
<div class="ctrl-items-selects">
<el-select class="video_type mr24" v-model="streamSelect" @change="fpvSmallWindow = true;" placeholder="切换源">
<el-select
class="video_type mr24"
v-model="streamSelect"
@change="fpvSmallWindow = true;"
placeholder="切换源"
>
<el-option
v-for="item in streamOptions"
:key="item.value"
v-for="(item, i) in _streamOptions"
:key="i"
:label="item.label"
:value="item.value"
></el-option>
......@@ -129,18 +141,24 @@
<script>
// import API from "@/api";
import LiveNVRPlayer from "./components/livenvr/index.vue";
import LiveNVRPlayer from "./components/livenvr";
// import QingLiuPlayer from "./flv/index.vue";
import QingLiuPlayer from "./components/qingliu/index.vue";
import QingLiuPlayer from "./components/qingliu";
import FLVPlayer from "./components/flv";
import SRSPlayer from "./components/srs";
import WebRtcPlayer from './components/webrtc';
import { to_moveMount } from "../../lib/to_moveMount";
import Obstacle from './components/obstacle';
import Obstacle from "./components/obstacle";
export default {
name: "MMCPlayer",
components: {
QingLiuPlayer,
LiveNVRPlayer,
Obstacle
FLVPlayer,
SRSPlayer,
WebRtcPlayer,
Obstacle,
},
props: {
//网络类型
......@@ -148,7 +166,7 @@ export default {
type: String,
default: () => "公网",
},
//流可选项, {value: "播放地址", label: '流名称', fpvUrl: ''}
//流可选项, {value: "播放地址", label: '流名称', fpvUrl: '', brand: 'srs|fly|liveNVR|QingLiu|webrtc'}
streamOptions: {
type: Array,
default: () => [],
......@@ -164,13 +182,13 @@ export default {
},
obstacleData: {
type: Object,
default(){
default() {
return null; /* {
distances: [], //array<int>(36个值每10度一个与障碍物的距离厘米);
obsDistance: 0, //int(避障警告距离米)}
} */
}
}
},
},
},
data() {
return {
......@@ -214,12 +232,43 @@ export default {
* 播放器组件名
*/
playerCom() {
if (this.streamSelect?.includes("pzsp://")) {
return "QingLiuPlayer";
if (this.stream?.brand) {
switch (this.stream.brand.toLowerCase()) {
case "flv":
return "FLVPlayer";
case "srs":
return "SRSPlayer";
case "livenvr":
return "LiveNVRPlayer";
case "qingliu":
return "QingLiuPlayer";
case "webrtc":
return "WebRtcPlayer";
default:
throw "不支持的播放器品牌!";
}
} else {
return "LiveNVRPlayer";
if (this.vUrl.vUrl?.includes("pzsp://")) {
return "QingLiuPlayer";
} else {
return "LiveNVRPlayer";
}
}
},
_streamOptions(){
return this.streamOptions.map((item, i) => {
item.value = i;
return item;
})
},
/**
* 选择流选项
*/
stream() {
let find = this.streamOptions.find((item) => {
return item.value === this.streamSelect;
......@@ -237,7 +286,7 @@ export default {
*/
vUrl() {
return {
vUrl: this.stream?.value || "",
vUrl: this.stream?.url || "",
};
},
fpvUrl() {
......@@ -248,31 +297,23 @@ export default {
},
watch: {
streamOptions: {
handler: function() {
console.log('streamOptions');
handler: function () {
console.log("streamOptions");
//当流选项变化时,如果未选择流类型,则自动选择第一个流类型
if (this.streamOptions.length > 0) {
this.streamSelect = this.streamOptions[0].value;
if (this._streamOptions.length > 0) {
this.streamSelect = this._streamOptions[0].value;
}
},
deep: true,
immediate: true
immediate: true,
},
},
mounted() {
this.wrapCenter();
},
methods: {
test(){
alert('TEST');
},
firstCopySuccess(e) {
console.log("copy arguments e:", e);
alert("复制成功!");
},
firstCopyError(e) {
console.log("copy arguments e:", e);
alert("复制失败!");
test() {
alert("TEST");
},
/**
* 切换展示清流调试信息
......
......@@ -10,7 +10,6 @@
<script src="./js/icontfont.js"></script>
<script type="text/javascript" src="./js/nipplejs.min.js"></script>
<script src="js/PIMediaPlayer_api.js"></script>
<script src="./js/liveplayer-lib.min.js"></script>
<script src="./s3m.js"></script>
<script type="text/javascript" src="js/kbt_api.js"></script>
<script src="liveplayer/liveplayer-lib.min.js"></script>
......
......@@ -3,7 +3,10 @@ export default `
1.清流需要网站响应头中添加
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
2.需要将mmc-stl-vue2/components/MMCPlayer/lib/js和mmc-stl-vue2/components/MMCPlayer/lib/liveplayer复制到项目的public文件中
2.需要将mmc-stl-vue2/components/MMCPlayer/lib/js和mmc-stl-vue2/components/MMCPlayer/lib/liveplayer复制到项目的public文件中, index.html添加以下代码
<script src="js/PIMediaPlayer_api.js"></script>
<script src="js/kbt_api.js"></script>
<script src="liveplayer/liveplayer-lib.min.js"></script>
<template>
<div>
......@@ -22,24 +25,36 @@ export default {
return {
streamOptions: [
{
label: "播放源1",
value:
label: "flv",
brand: "flv", //支持以下品牌播放器"srs|fly|liveNVR|QingLiu|webrtc", 为空则根据url判断使用liveNVR或QingLiu
url:
"https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
fpvUrl:
"http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8",
},
{
label: "播放源2",
value:
label: "liveNVR",
brand: 'liveNVR',
url:
"http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8",
fpvUrl:
fpvUrl: //只有liveNVR支持fpvUrl
"https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
},
{
label: "播放源3",
value:
label: "srs",
brand: 'srs',
url:
"webrtc://live.mmcuav.cn/hls/shouguang01",
},
{
label: "QingLiu",
url:
"pzsp://pzlink999bju.powzamedia.com:7788/live/ld/trans/test/mlinkm/channel1?ndselect=2&linkmode=8&fstusrd=1&us=1&wsinfo=pzlink999bju.powzamedia.com-13000&only-video=1&rct=500",
},
{
label: "WebRtc",
brand: 'webrtc',
url:
"webrtc://live.mmcuav.cn/hls/shouguang01",
},
],
obstacleData: null,
/* {
......
......@@ -15,24 +15,36 @@ export default {
return {
streamOptions: [
{
label: "播放源1",
value:
label: "flv",
brand: "flv", //支持以下品牌播放器"srs|fly|liveNVR|QingLiu|webrtc", 为空则根据url判断使用liveNVR或QingLiu
url:
"https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
fpvUrl:
"http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8",
},
{
label: "播放源2",
value:
"http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8",
fpvUrl:
label: "liveNVR",
brand: 'liveNVR',
url:
"https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
fpvUrl: //只有liveNVR支持fpvUrl
"webrtc://live.mmcuav.cn/hls/shouguang01",
},
{
label: "播放源3",
value:
label: "srs",
brand: 'srs',
url:
"webrtc://live.mmcuav.cn/hls/shouguang01",
},
{
label: "QingLiu",
url:
"pzsp://pzlink999bju.powzamedia.com:7788/live/ld/trans/test/mlinkm/channel1?ndselect=2&linkmode=8&fstusrd=1&us=1&wsinfo=pzlink999bju.powzamedia.com-13000&only-video=1&rct=500",
},
{
label: "WebRtc",
brand: 'webrtc',
url:
"webrtc://live.mmcuav.cn/hls/shouguang01",
},
],
obstacleData: null,
/* {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论