From 50c824f3bbc818acca2b28a27469fc986e40c047 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 11 Jan 2022 21:19:58 -0600 Subject: [PATCH] Janus cleanup, adds support to the "watch" view --- misc/janus.jcfg | 50 ++++++++++++++++ misc/janus.plugin.streaming.jcfg | 4 ++ misc/janus.transport.http.jcfg | 25 ++++++++ web/js/MonitorStream.js | 66 --------------------- web/js/adapter.min.js | 1 + web/skins/classic/views/js/montage.js | 2 +- web/skins/classic/views/js/watch.js | 83 ++++++++++++++++++++++++--- web/skins/classic/views/montage.php | 2 +- web/skins/classic/views/watch.php | 8 ++- 9 files changed, 164 insertions(+), 77 deletions(-) create mode 100644 misc/janus.jcfg create mode 100644 misc/janus.plugin.streaming.jcfg create mode 100644 misc/janus.transport.http.jcfg create mode 100644 web/js/adapter.min.js diff --git a/misc/janus.jcfg b/misc/janus.jcfg new file mode 100644 index 000000000..a0adffb88 --- /dev/null +++ b/misc/janus.jcfg @@ -0,0 +1,50 @@ +general: { + configs_folder = "/usr/local/etc/janus" # Configuration files folder + plugins_folder = "/usr/local/lib/janus/plugins" # Plugins folder + transports_folder = "/usr/local/lib/janus/transports" # Transports folder + events_folder = "/usr/local/lib/janus/events" # Event handlers folder + loggers_folder = "/usr/local/lib/janus/loggers" # External loggers folder + debug_level = 4 # Debug/logging level, valid values are 0-7 + admin_secret = "janusoverlord" # String that all Janus requests must contain + protected_folders = [ + "/bin", + "/boot", + "/dev", + "/etc", + "/initrd", + "/lib", + "/lib32", + "/lib64", + "/proc", + "/sbin", + "/sys", + "/usr", + "/var", + "/opt/janus/bin", + "/opt/janus/etc", + "/opt/janus/include", + "/opt/janus/lib", + "/opt/janus/lib32", + "/opt/janus/lib64", + "/opt/janus/sbin" + ] +} +media: { + #ipv6 = true + #ipv6_linklocal = true + rtp_port_range = "20000-40000" +} +nat: { + nice_debug = false + ignore_mdns = true + keep_private_host = true + ice_ignore_list = "vmnet" +} + +plugins: { + disable = "libjanus_audiobridge.so,libjanus_echotest.so,libjanus_recordplay.so,libjanus_sip.so,libjanus_textroom.so,libjanus_videocall.so,libjanus_videoroom.so,libjanus_voicemail.so, + libjanus_nosip.so" +} +transports: { + disable = "libjanus_rabbitmq.so, libjanus_pfunix.so,libjanus_websockets.so" +} diff --git a/misc/janus.plugin.streaming.jcfg b/misc/janus.plugin.streaming.jcfg new file mode 100644 index 000000000..f93b15a3e --- /dev/null +++ b/misc/janus.plugin.streaming.jcfg @@ -0,0 +1,4 @@ +general: { + admin_key = "supersecret" + rtp_port_range = "20000-40000" +} diff --git a/misc/janus.transport.http.jcfg b/misc/janus.transport.http.jcfg new file mode 100644 index 000000000..8ae9171ad --- /dev/null +++ b/misc/janus.transport.http.jcfg @@ -0,0 +1,25 @@ +general: { + json = "indented" # Whether the JSON messages should be indented (default), + base_path = "/janus" # Base path to bind to in the web server (plain HTTP only) + http = true # Whether to enable the plain HTTP interface + port = 8088 # Web server HTTP port + https = false # Whether to enable HTTPS (default=false) +} + +# Janus can also expose an admin/monitor endpoint, to allow you to check +# which sessions are up, which handles they're managing, their current +# status and so on. This provides a useful aid when debugging potential +# issues in Janus. The configuration is pretty much the same as the one +# already presented above for the webserver stuff, as the API is very +# similar: choose the base bath for the admin/monitor endpoint (/admin +# by default), ports, etc. Besides, you can specify +# a secret that must be provided in all requests as a crude form of +# authorization mechanism, and partial or full source IPs if you want to +# limit access basing on IP addresses. For security reasons, this +# endpoint is disabled by default, enable it by setting admin_http=true. +admin: { + admin_base_path = "/admin" # Base path to bind to in the admin/monitor web server (plain HTTP only) + admin_http = false # Whether to enable the plain HTTP interface + admin_port = 7088 # Admin/monitor web server HTTP port + admin_https = false # Whether to enable HTTPS (default=false) +} diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 1bc96d62a..ef4d6db41 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -5,7 +5,6 @@ function MonitorStream(monitorData) { this.auth_relay = auth_relay; this.auth_hash = auth_hash; this.url = monitorData.url; - this.janusEnabled = monitorData.janusEnabled; this.url_to_zms = monitorData.url_to_zms; this.width = monitorData.width; this.height = monitorData.height; @@ -87,71 +86,6 @@ function MonitorStream(monitorData) { console.log(stream); return; } - if (this.janusEnabled) { - var id = parseInt(this.id); - var opaqueId = "streamingtest-"+Janus.randomString(12); - var server = "http://zm-dev:8088/janus"; - Janus.init({debug: "all", callback: function() { - janus = new Janus({ - server: server, - success: function() { - janus.attach({ - plugin: "janus.plugin.streaming", - opaqueId: opaqueId, - success: function(pluginHandle) { - streaming = pluginHandle; - var body = { "request": "watch", "id": id }; - streaming.send({"message": body}); - }, - error: function(error) { - Janus.error(" -- Error attaching plugin... ", error); - }, - onmessage: function(msg, jsep) { - Janus.debug(" ::: Got a message :::"); - Janus.debug(msg); - var result = msg["result"]; - if(result !== null && result !== undefined) { - if(result["status"] !== undefined && result["status"] !== null) { - var status = result["status"]; - } - } else if(msg["error"] !== undefined && msg["error"] !== null) { - alert(msg["error"]); - stopStream(); - return; - } - if(jsep !== undefined && jsep !== null) { - Janus.debug("Handling SDP as well..."); - Janus.debug(jsep); - // Offer from the plugin, let's answer - streaming.createAnswer({ - jsep: jsep, - // We want recvonly audio/video and, if negotiated, datachannels - media: { audioSend: false, videoSend: false, data: true }, - success: function(jsep) { - Janus.debug("Got SDP!"); - Janus.debug(jsep); - var body = { "request": "start"}; - streaming.send({"message": body, "jsep": jsep}); - }, - error: function(error) { - Janus.error("WebRTC error:", error); - alert("WebRTC error... " + JSON.stringify(error)); - } - }); - } - }, //onmessage function - onremotestream: function(ourstream) { - Janus.debug(" ::: Got a remote track :::"); - Janus.debug(ourstream); - Janus.attachMediaStream(stream, ourstream); - stream.play() - } - });// attach - } //Success functio - }); //new Janus -}}); //janus.init callback - return; - } src = stream.src.replace(/mode=single/i, 'mode=jpeg'); if ( -1 == src.search('connkey') ) { src += '&connkey='+this.connKey; diff --git a/web/js/adapter.min.js b/web/js/adapter.min.js new file mode 100644 index 000000000..bcbefc98d --- /dev/null +++ b/web/js/adapter.min.js @@ -0,0 +1 @@ +!function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).adapter=e()}(function(){return function n(i,o,a){function s(t,e){if(!o[t]){if(!i[t]){var r="function"==typeof require&&require;if(!e&&r)return r(t,!0);if(c)return c(t,!0);throw(r=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",r}r=o[t]={exports:{}},i[t][0].call(r.exports,function(e){return s(i[t][1][e]||e)},r,r.exports,n,i,o,a)}return o[t].exports}for(var c="function"==typeof require&&require,e=0;er.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+r.sctp.maxMessageSize+" bytes)");return n.apply(t,arguments)}}e.RTCPeerConnection&&"createDataChannel"in e.RTCPeerConnection.prototype&&(t=e.RTCPeerConnection.prototype.createDataChannel,e.RTCPeerConnection.prototype.createDataChannel=function(){var e=t.apply(this,arguments);return r(e,this),e},s.wrapPeerConnectionEvent(e,"datachannel",function(e){return r(e.channel,e.target),e}))},r.shimConnectionState=function(e){var r;!e.RTCPeerConnection||"connectionState"in e.RTCPeerConnection.prototype||(r=e.RTCPeerConnection.prototype,Object.defineProperty(r,"connectionState",{get:function(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(r,"onconnectionstatechange",{get:function(){return this._onconnectionstatechange||null},set:function(e){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),e&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=e)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach(function(e){var t=r[e];r[e]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=function(e){var t,r=e.target;return r._lastConnectionState!==r.connectionState&&(r._lastConnectionState=r.connectionState,t=new Event("connectionstatechange",e),r.dispatchEvent(t)),e},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),t.apply(this,arguments)}}))},r.removeExtmapAllowMixed=function(r,e){var n;r.RTCPeerConnection&&("chrome"===e.browser&&71<=e.version||"safari"===e.browser&&605<=e.version||(n=r.RTCPeerConnection.prototype.setRemoteDescription,r.RTCPeerConnection.prototype.setRemoteDescription=function(e){var t;return e&&e.sdp&&-1!==e.sdp.indexOf("\na=extmap-allow-mixed")&&(t=e.sdp.split("\n").filter(function(e){return"a=extmap-allow-mixed"!==e.trim()}).join("\n"),r.RTCSessionDescription&&e instanceof r.RTCSessionDescription?arguments[0]=new r.RTCSessionDescription({type:e.type,sdp:t}):e.sdp=t),n.apply(this,arguments)}))},r.shimAddIceCandidateNullOrEmpty=function(e,t){var r;e.RTCPeerConnection&&e.RTCPeerConnection.prototype&&((r=e.RTCPeerConnection.prototype.addIceCandidate)&&0!==r.length&&(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?("chrome"===t.browser&&t.version<78||"firefox"===t.browser&&t.version<68||"safari"===t.browser)&&arguments[0]&&""===arguments[0].candidate?Promise.resolve():r.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())}))},r.shimParameterlessSetLocalDescription=function(e,t){var r;e.RTCPeerConnection&&e.RTCPeerConnection.prototype&&((r=e.RTCPeerConnection.prototype.setLocalDescription)&&0!==r.length&&(e.RTCPeerConnection.prototype.setLocalDescription=function(){var t=this,e=arguments[0]||{};if("object"!==(void 0===e?"undefined":o(e))||e.type&&e.sdp)return r.apply(this,arguments);if(!(e={type:e.type,sdp:e.sdp}).type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":e.type="offer";break;default:e.type="answer"}return e.sdp||"offer"!==e.type&&"answer"!==e.type?r.apply(this,[e]):("offer"===e.type?this.createOffer:this.createAnswer).apply(this).then(function(e){return r.apply(t,[e])})}))};var n,i=e("sdp"),a=(n=i)&&n.__esModule?n:{default:n},s=function(e){{if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}}(e("./utils"))},{"./utils":11,sdp:12}],7:[function(e,t,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.shimGetDisplayMedia=r.shimGetUserMedia=void 0;var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n=e("./getusermedia");Object.defineProperty(r,"shimGetUserMedia",{enumerable:!0,get:function(){return n.shimGetUserMedia}});var i=e("./getdisplaymedia");Object.defineProperty(r,"shimGetDisplayMedia",{enumerable:!0,get:function(){return i.shimGetDisplayMedia}}),r.shimOnTrack=function(e){"object"===(void 0===e?"undefined":a(e))&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get:function(){return{receiver:this.receiver}}})},r.shimPeerConnection=function(n,i){var o,r;"object"===(void 0===n?"undefined":a(n))&&(n.RTCPeerConnection||n.mozRTCPeerConnection)&&(!n.RTCPeerConnection&&n.mozRTCPeerConnection&&(n.RTCPeerConnection=n.mozRTCPeerConnection),i.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(e){var t=n.RTCPeerConnection.prototype[e],r=function(e,t,r){t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r;return e}({},e,function(){return arguments[0]=new("addIceCandidate"===e?n.RTCIceCandidate:n.RTCSessionDescription)(arguments[0]),t.apply(this,arguments)});n.RTCPeerConnection.prototype[e]=r[e]}),o={inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"},r=n.RTCPeerConnection.prototype.getStats,n.RTCPeerConnection.prototype.getStats=function(){var e=Array.prototype.slice.call(arguments),t=e[0],n=e[1],e=e[2];return r.apply(this,[t||null]).then(function(r){if(i.version<53&&!n)try{r.forEach(function(e){e.type=o[e.type]||e.type})}catch(e){if("TypeError"!==e.name)throw e;r.forEach(function(e,t){r.set(t,Object.assign({},e,{type:o[e.type]||e.type}))})}return r}).then(n,e)})},r.shimSenderGetStats=function(e){var r,t;"object"===(void 0===e?"undefined":a(e))&&e.RTCPeerConnection&&e.RTCRtpSender&&(e.RTCRtpSender&&"getStats"in e.RTCRtpSender.prototype||((r=e.RTCPeerConnection.prototype.getSenders)&&(e.RTCPeerConnection.prototype.getSenders=function(){var t=this,e=r.apply(this,[]);return e.forEach(function(e){return e._pc=t}),e}),(t=e.RTCPeerConnection.prototype.addTrack)&&(e.RTCPeerConnection.prototype.addTrack=function(){var e=t.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){return this.track?this._pc.getStats(this.track):Promise.resolve(new Map)}))},r.shimReceiverGetStats=function(e){var r;"object"===(void 0===e?"undefined":a(e))&&e.RTCPeerConnection&&e.RTCRtpSender&&(e.RTCRtpSender&&"getStats"in e.RTCRtpReceiver.prototype||((r=e.RTCPeerConnection.prototype.getReceivers)&&(e.RTCPeerConnection.prototype.getReceivers=function(){var t=this,e=r.apply(this,[]);return e.forEach(function(e){return e._pc=t}),e}),o.wrapPeerConnectionEvent(e,"track",function(e){return e.receiver._pc=e.srcElement,e}),e.RTCRtpReceiver.prototype.getStats=function(){return this._pc.getStats(this.track)}))},r.shimRemoveStream=function(e){!e.RTCPeerConnection||"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(t){var r=this;o.deprecated("removeStream","removeTrack"),this.getSenders().forEach(function(e){e.track&&t.getTracks().includes(e.track)&&r.removeTrack(e)})})},r.shimRTCDataChannel=function(e){e.DataChannel&&!e.RTCDataChannel&&(e.RTCDataChannel=e.DataChannel)},r.shimAddTransceiver=function(e){var i;"object"!==(void 0===e?"undefined":a(e))||!e.RTCPeerConnection||(i=e.RTCPeerConnection.prototype.addTransceiver)&&(e.RTCPeerConnection.prototype.addTransceiver=function(){this.setParametersPromises=[];var e=arguments[1],t=e&&"sendEncodings"in e;t&&e.sendEncodings.forEach(function(e){if("rid"in e)if(!/^[a-z0-9]{0,16}$/i.test(e.rid))throw new TypeError("Invalid RID value provided.");if("scaleResolutionDownBy"in e&&!(1<=parseFloat(e.scaleResolutionDownBy)))throw new RangeError("scale_resolution_down_by must be >= 1.0");if("maxFramerate"in e&&!(0<=parseFloat(e.maxFramerate)))throw new RangeError("max_framerate must be >= 0.0")});var r,n=i.apply(this,arguments);return t&&("encodings"in(t=(r=n.sender).getParameters())&&(1!==t.encodings.length||0!==Object.keys(t.encodings[0]).length)||(t.encodings=e.sendEncodings,r.sendEncodings=e.sendEncodings,this.setParametersPromises.push(r.setParameters(t).then(function(){delete r.sendEncodings}).catch(function(){delete r.sendEncodings})))),n})},r.shimGetParameters=function(e){var t;"object"!==(void 0===e?"undefined":a(e))||!e.RTCRtpSender||(t=e.RTCRtpSender.prototype.getParameters)&&(e.RTCRtpSender.prototype.getParameters=function(){var e=t.apply(this,arguments);return"encodings"in e||(e.encodings=[].concat(this.sendEncodings||[{}])),e})},r.shimCreateOffer=function(e){var r;"object"===(void 0===e?"undefined":a(e))&&e.RTCPeerConnection&&(r=e.RTCPeerConnection.prototype.createOffer,e.RTCPeerConnection.prototype.createOffer=function(){var e=this,t=arguments;return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(function(){return r.apply(e,t)}).finally(function(){e.setParametersPromises=[]}):r.apply(this,arguments)})},r.shimCreateAnswer=function(e){var r;"object"===(void 0===e?"undefined":a(e))&&e.RTCPeerConnection&&(r=e.RTCPeerConnection.prototype.createAnswer,e.RTCPeerConnection.prototype.createAnswer=function(){var e=this,t=arguments;return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(function(){return r.apply(e,t)}).finally(function(){e.setParametersPromises=[]}):r.apply(this,arguments)})};var o=function(e){{if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}}(e("../utils"))},{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[function(e,t,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.shimGetDisplayMedia=function(t,r){t.navigator.mediaDevices&&"getDisplayMedia"in t.navigator.mediaDevices||t.navigator.mediaDevices&&(t.navigator.mediaDevices.getDisplayMedia=function(e){if(e&&e.video)return!0===e.video?e.video={mediaSource:r}:e.video.mediaSource=r,t.navigator.mediaDevices.getUserMedia(e);e=new DOMException("getDisplayMedia without video constraints is undefined");return e.name="NotFoundError",e.code=8,Promise.reject(e)})}},{}],9:[function(e,t,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0});var s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};r.shimGetUserMedia=function(e,t){var n=e&&e.navigator,e=e&&e.MediaStreamTrack;{var r,i,o,a;n.getUserMedia=function(e,t,r){c.deprecated("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(t,r)},55=r&&parseInt(t[r],10)}function c(e){return"[object Object]"===Object.prototype.toString.call(e)}function p(t,r,n){r&&!n.has(r.id)&&(n.set(r.id,r),Object.keys(r).forEach(function(e){e.endsWith("Id")?p(t,t.get(r[e]),n):e.endsWith("Ids")&&r[e].forEach(function(e){p(t,t.get(e),n)})}))}},{}],12:[function(e,t,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p={generateIdentifier:function(){return Math.random().toString(36).substr(2,10)}};p.localCName=p.generateIdentifier(),p.splitLines=function(e){return e.trim().split("\n").map(function(e){return e.trim()})},p.splitSections=function(e){return e.split("\nm=").map(function(e,t){return(0n&&(n=e.maxptime)}),0 - + diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 6acce1b5c..3128f7767 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -135,7 +135,11 @@ if (isset($_REQUEST['height'])) { } $connkey = generateConnKey(); -$streamMode = getStreamMode(); +if ( $monitor->JanusEnabled() ) { + $streamMode = 'janus'; +} else { + $streamMode = getStreamMode(); +} noCacheHeaders(); xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); @@ -407,4 +411,6 @@ if ( ZM_WEB_SOUND_ON_ALARM ) { ?> + +