+
REMOTE
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/media-server-demo-node/www/broadcast/js/broadcast.js b/media-server-demo-node/www/broadcast/js/broadcast.js
new file mode 100644
index 0000000..a27aa12
--- /dev/null
+++ b/media-server-demo-node/www/broadcast/js/broadcast.js
@@ -0,0 +1,121 @@
+const url = "wss://"+window.location.hostname+":"+window.location.port;
+
+const roomId = (new Date()).getTime() + "-" + Math.random();
+
+
+function addVideoForStream(stream,muted)
+{
+ //Create new video element
+ const video = document.querySelector (muted ? "#local" : "#remote");
+ //Set same id
+ video.id = stream.id;
+ //Set src stream
+ video.src = URL.createObjectURL(stream);
+ //Set other properties
+ video.autoplay = true;
+ video.muted = muted;
+}
+function removeVideoForStream(stream)
+{
+ //Get video
+ var video = document.getElementById(stream.id);
+ //Remove it when done
+ video.addEventListener('webkitTransitionEnd',function(){
+ //Delete it
+ video.parentElement.removeChild(video);
+ });
+ //Disable it first
+ video.className = "disabled";
+}
+
+var sdp;
+var pc;
+
+function connect()
+{
+
+ if (window.RTCPeerConnection)
+ pc = new RTCPeerConnection({
+ bundlePolicy: "max-bundle",
+ rtcpMuxPolicy : "require"
+ });
+ else
+ pc = new webkitRTCPeerConnection(null);
+
+ var ws = new WebSocket(url,"broadcast");
+
+ pc.onaddstream = function(event) {
+ var prev = 0;
+ console.debug("onAddStream",event);
+ //Play it
+ addVideoForStream(event.stream);
+
+
+ };
+
+ pc.onremovestream = function(event) {
+ console.debug("onRemoveStream",event);
+ //Play it
+ removeVideoForStream(event.stream);
+ };
+
+ ws.onopen = function(){
+ console.log("opened");
+
+ //Create new offer
+ pc.createOffer({
+ offerToReceiveVideo: true
+ })
+ .then(function(offer){
+ console.debug("createOffer sucess",offer);
+ //We have sdp
+ sdp = offer.sdp;
+ //Set it
+ pc.setLocalDescription(offer);
+ console.log(sdp);
+ //Create room
+ ws.send(JSON.stringify({
+ cmd : "OFFER",
+ offer : sdp
+ }));
+ })
+ .catch(function(error){
+ console.error("Error",error);
+ });
+ };
+
+ ws.onmessage = function(event){
+ console.log(event);
+
+ //Get protocol message
+ const msg = JSON.parse(event.data);
+
+ console.log(msg.answer);
+ pc.setRemoteDescription(new RTCSessionDescription({
+ type:'answer',
+ sdp: msg.answer
+ }), function () {
+ console.log("JOINED");
+ }, function (err) {
+ console.error("Error joining",err);
+ }
+ );
+ };
+}
+
+var dialog = document.querySelector('dialog');
+if (dialog.showModal)
+{
+ dialog.showModal();
+ dialog.querySelector('.ready').addEventListener('click', function() {
+ dialog.close();
+ connect();
+ });
+} else {
+ connect();
+}
+
+
+
+
+
diff --git a/media-server-demo-node/www/datachannels/index.html b/media-server-demo-node/www/datachannels/index.html
new file mode 100644
index 0000000..d778ef8
--- /dev/null
+++ b/media-server-demo-node/www/datachannels/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/media-server-demo-node/www/datachannels/js/datachannels.js b/media-server-demo-node/www/datachannels/js/datachannels.js
new file mode 100644
index 0000000..acfdd41
--- /dev/null
+++ b/media-server-demo-node/www/datachannels/js/datachannels.js
@@ -0,0 +1,50 @@
+const url = "wss://"+window.location.hostname+":"+window.location.port;
+
+
+
+var sdp;
+var pc;
+
+function connect()
+{
+
+ //Create PC
+ pc = new RTCPeerConnection();
+ const dc = pc.createDataChannel("aaaaaaaaaaaaaaaa");
+
+ var ws = new WebSocket(url,"datachannels");
+
+ ws.onopen = async function() {
+ //Create new offer
+ const offer = await pc.createOffer();
+ //We have sdp
+ sdp = offer.sdp;
+ console.log("offer",sdp);
+ //Set it
+ await pc.setLocalDescription(offer);
+ //Create room
+ ws.send(JSON.stringify({
+ cmd : "OFFER",
+ offer : sdp
+ }));
+ };
+
+ ws.onmessage = function(event){
+ //Get protocol message
+ const msg = JSON.parse(event.data);
+ const answer = msg.answer.replace("m=application 9 UDP/TLS/RTP/SAVPF","m=application 9 DTLS/SCTP 5000") + "a=sctpmap:5000 webrtc-datachannel 1024\r\n"
+ console.log("answer",answer);
+ pc.setRemoteDescription(new RTCSessionDescription({
+ type:'answer',
+ sdp: answer
+ }), function () {
+ console.log("JOINED");
+ }, function (err) {
+ console.error("Error joining",err);
+ }
+ );
+ };
+}
+
+connect();
+
diff --git a/media-server-demo-node/www/js/dialog-polyfill.js b/media-server-demo-node/www/js/dialog-polyfill.js
new file mode 100644
index 0000000..be4b43d
--- /dev/null
+++ b/media-server-demo-node/www/js/dialog-polyfill.js
@@ -0,0 +1,738 @@
+(function() {
+
+ // nb. This is for IE10 and lower _only_.
+ var supportCustomEvent = window.CustomEvent;
+ if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
+ supportCustomEvent = function CustomEvent(event, x) {
+ x = x || {};
+ var ev = document.createEvent('CustomEvent');
+ ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
+ return ev;
+ };
+ supportCustomEvent.prototype = window.Event.prototype;
+ }
+
+ /**
+ * @param {Element} el to check for stacking context
+ * @return {boolean} whether this el or its parents creates a stacking context
+ */
+ function createsStackingContext(el) {
+ while (el && el !== document.body) {
+ var s = window.getComputedStyle(el);
+ var invalid = function(k, ok) {
+ return !(s[k] === undefined || s[k] === ok);
+ }
+ if (s.opacity < 1 ||
+ invalid('zIndex', 'auto') ||
+ invalid('transform', 'none') ||
+ invalid('mixBlendMode', 'normal') ||
+ invalid('filter', 'none') ||
+ invalid('perspective', 'none') ||
+ s['isolation'] === 'isolate' ||
+ s.position === 'fixed' ||
+ s.webkitOverflowScrolling === 'touch') {
+ return true;
+ }
+ el = el.parentElement;
+ }
+ return false;
+ }
+
+ /**
+ * Finds the nearest