Browse Source

add p2p metaRTC ws signaling

m7s
superxxd 2 years ago
parent
commit
bd5424f9a6
  1. 1
      test/adapter-7.4.0.min.js
  2. 53
      test/main.html
  3. 568
      test/srs.sdk.p2p.js
  4. 3
      test/start.js
  5. 12
      test/styles/style.css
  6. 29
      test/video.js
  7. 0
      test/wssignaling.js

1
test/adapter-7.4.0.min.js

File diff suppressed because one or more lines are too long

53
test/main.html

@ -2,43 +2,14 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>WASM TEST</title> <title>Webrtc IPC H265/H264 Player</title>
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-touch-fullscreen" content="yes" /> <meta name="apple-touch-fullscreen" content="yes" />
<meta name="format-detection" content="telephone=no, email=no" /> <meta name="format-detection" content="telephone=no, email=no" />
<link rel="stylesheet" href="styles/style.css"> <link rel="stylesheet" href="styles/style.css">
<text> webrtc datachannel H265 Player Demo 2022</text>
</head> </head>
<body> <body>
<!-- <div style="margin: 20px;">
H264:
<input type="file" onchange="handleVideoFiles(this.files)" onclick="setDecoder(0)">
</div>
<div style="margin: 20px;">
H265:
<input type="file" onchange="handleVideoFiles(this.files)" onclick="setDecoder(1)">
</div>
<div style="margin: 20px;">
AudioContext:
<input type="file" onchange="handleAudioFiles(this.files)" onclick="setAudioDecoder(0)">
</div>
<div style="margin: 20px;">
MSE:
<input type="file" onchange="handleAudioFiles(this.files)" onclick="setAudioDecoder(1)">
</div> -->
<div class="row">
<form action="demo_form.html" method="get">
服务器名称: <input type="text" name="servername" id="serverId" value="rtmpserver1">
拉 流 名 称: <input type="text" name="streamname" id="streamId" value="kvs">
<!-- 设 备 名 称: <input type="text" name="devicename" id="deviceId" value="0A:2A:BE:6C:03:2E"> -->
设 备 名 称: <input type="text" name="devicename" id="deviceId" value="4E:7B:BF:71:D6:B4">
数 据 发 送:<input type="text" name="msgout" id="controlInput" value="msgout"><button type="button" onclick="controlSend()" id="control-send">发送控制消息</button><br>
数 据 接 收:<input type="text" name="msgin" id="control_output" value="msgin"><br>
</form>
</div>
<div class="container" id="videoPlayer"> <div class="container" id="videoPlayer">
<div class="sideBar"> <div class="sideBar">
<span class="no-padding"> <span class="no-padding">
@ -46,11 +17,12 @@
</span> </span>
<div id="input"> <div id="input">
<select id="protocol" onchange="onSelectProto()"> <select id="protocol" onchange="onSelectProto()">
<option value="webrtc">WEBRTC</option>
<option value="http">HTTP</option> <option value="http">HTTP</option>
<option value="ws">WS</option> <option value="ws">WS</option>
<option value="httpFlv">HTTP-FLV</option> <option value="httpFlv">HTTP-FLV</option>
</select> </select>
<input type="text" id="inputUrl" style="width:300px"/> <input type="text" id="inputUrl" value="webrtc://192.168.0.4:1988/live/livestream" style="width:300px"/>
</div> </div>
</div> </div>
<div class="canvasDiv"> <div class="canvasDiv">
@ -90,7 +62,24 @@
</span> </span>
</div> </div>
</div> </div>
<div id="footer">Copyright &copy; 2022 xiangxudong.All rights reserved.</div> <div><style type="text/css">
div{width:800px;height:280px;margin:10px auto;color:rgb(9, 32, 95);font-size:16px;}
.box1{background: #71a879;text-align: center;}
</style>
<div class="row">
<form action="demo_form.html" method="get">
服务器名称: <input type="text" name="servername" id="serverId" value="rtmpserver1">
拉 流 名 称: <input type="text" name="streamname" id="streamId" value="kvs">
<!-- 设 备 名 称: <input type="text" name="devicename" id="deviceId" value="0A:2A:BE:6C:03:2E"> -->
设 备 名 称: <input type="text" name="devicename" id="deviceId" value="4E:7B:BF:71:D6:B4"><br>
数 据 发 送:<input type="text" name="msgout" id="controlInput" value="msgout"><button type="button" onclick="controlSend()" id="control-send">发送控制消息</button><br>
数 据 接 收:<input type="text" name="msgin" id="control_output" value="msgin"><br>
</form>
</div>
</div>
</div>
<div id="footer">Copyright &copy; 2022 xiangxudong.All rights reserved.webrtc datachannel H265 Player Demo 2022</div>
</div> </div>
<!-- <div style="margin: 20px;"> <!-- <div style="margin: 20px;">
<button id="play" onclick="handleVideo()">Start Play</button> <button id="play" onclick="handleVideo()">Start Play</button>

568
test/srs.sdk.p2p.js

@ -0,0 +1,568 @@
//
// 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.
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
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
console.log("Got answer: ", 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;
}
var datachannel=null;
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-await-promise based SRS RTC Player.
function SrsRtcPlayerAsync() {
var self = {};
/**
const config = {
bundlePolicy: 'balanced',
// certificates?: RTCCertificate[];
// iceCandidatePoolSize?: number;
iceTransportPolicy: "relay",// all
rtcpMuxPolicy : 'negotiate',
iceServers: [
{
urls: "turn:192.168.1.102:3478",
username: "metartc",
credential: "metartc"
}
]
};
self.pc = new RTCPeerConnection(config);
* */
self.pc = new RTCPeerConnection(null);
self.pc.onconnectionstatechange=function(event){
console.log("connection state change: ", self.pc.connectionState);
}
self.pc.onicecandidate = async (ev) => {
console.log('=======>' + JSON.stringify(ev.candidate));
};
datachannel=self.pc.createDataChannel('chat');
datachannel.onopen = function(event) {
console.log("datachannel onopen: ", event.data);
}
datachannel.onmessage = function(event) {
console.log("receive message: ", event.data);
$('#datachannel_recv').val(event.data);
}
datachannel.onerror=function(event) {
console.log("datachannel error: ", event.data);
}
datachannel.onclose=function(event) {
console.log("datachannel close: ");
}
// @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);
console.log("conf.apiUrl: ", conf.apiUrl);
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
};
console.log("Generated offer: ", data);
//text/plain application/json
$.ajax({
type: "POST", url: conf.apiUrl, data: offer.sdp+"}",
contentType:'text/plain', dataType: 'json',
crossDomain:true
}).done(function(data) {
if (data.code) {
reject(data); return;
}
console.log("Got sdp: ", data.sdp);
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() {
if(datachannel) {
datachannel.close();
datachannel=null;
}
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);
var schema="http:";
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;
}
}
};
// 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
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(", ");
}

3
test/start.js

@ -16,6 +16,9 @@ importScripts("./datachannel.js")
importScripts("./audiodc.js") importScripts("./audiodc.js")
importScripts("./h265dc.js") importScripts("./h265dc.js")
importScripts("https://cdn.bootcdn.net/ajax/libs/mqtt/2.18.8/mqtt.min.js") importScripts("https://cdn.bootcdn.net/ajax/libs/mqtt/2.18.8/mqtt.min.js")
importScripts("adapter-7.4.0.min.js")
importScripts("srs.sdk.p2p.js")
// importScripts("./adapter-latest.js") // importScripts("./adapter-latest.js")
function log(msg) { function log(msg) {

12
test/styles/style.css

@ -20,7 +20,17 @@
#btnFullscreen:hover { #btnFullscreen:hover {
transform: scale(1.3); transform: scale(1.3);
} }
#PlayerParameter {
width: 852px;
height: 300px;
background-color: rgb(105, 100, 100);
border: 2px solid rgba(0, 0, 0, 0.9);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
z-index: 10;
}
#videoPlayer { #videoPlayer {
width: 852px; width: 852px;
height: 536px; height: 536px;

29
test/video.js

@ -1,9 +1,38 @@
var webglPlayer, canvas, videoWidth, videoHeight, yLength, uvLength; var webglPlayer, canvas, videoWidth, videoHeight, yLength, uvLength;
var player=null; var player=null;
var sdk = null; // Global handler to do cleanup when replaying.
// var DECODER_TYPE = kDecoder_decodeer_js; // var DECODER_TYPE = kDecoder_decodeer_js;
// var DECODER_TYPE = kDecoder_prod_h265_wasm_combine_js; // var DECODER_TYPE = kDecoder_prod_h265_wasm_combine_js;
function startPlay(url) {
if(url===undefined){
return;
}
// Close PC when user replay.
if (sdk) {
sdk.close();
}
sdk = new SrsRtcPlayerAsync();
// https://webrtc.org/getting-started/remote-streams
// $('#rtc_media_player').prop('srcObject', sdk.stream);
// var url = $("#txt_url").val();
// // parse_webrtc(url);
sdk.play(url).then(function(session){
// console.log("play url ",$("#txt_url").val());
// $('#datachannel_form').show();
}).catch(function (reason) {
sdk.close();
});
};
function handleVideo() { function handleVideo() {
var porotocol=document.getElementById("protocol");
if(porotocol.val==="webrtc"){
var url=document.getElementById("inputUrl");
startPlay(url);
rtrun;
}
player = new Worker("Player.js"); player = new Worker("Player.js");
// H265transferworker = new Worker ("") // H265transferworker = new Worker ("")
var el = document.getElementById("btnPlayVideo"); var el = document.getElementById("btnPlayVideo");

0
test/wssignaling.js

Loading…
Cancel
Save