├── Bunny.html ├── LICENSE ├── README.md ├── admin.html ├── apprtc.html ├── apprtc.json ├── help.html ├── htmlmapmarker.js ├── index.html ├── info.png ├── janusconfig.js ├── janustest.html ├── janusvideoroom-element.js ├── janusvideoroom.html ├── janusvideoroom.js ├── jitsi.ico ├── jitsiroom.html ├── jitsitest.html ├── jsdoc ├── JanusVideoRoom.html ├── WebRtcStreamer.html ├── XMPPVideoRoom.html ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.svg │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.svg │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.svg │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.svg │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.svg │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ └── OpenSans-Regular-webfont.woff ├── index.html ├── janusvideoroom.js.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js ├── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── webrtcstreamer.js.html └── xmppvideoroom.js.html ├── libs ├── EventEmitter.min.js ├── adapter.min.js ├── ag-grid.min.js ├── blazeface ├── body-pix ├── coco-ssd ├── deeplab ├── jquery-3.5.1.min.js ├── lib-jitsi-meet.min.js ├── posenet ├── strophe.disco.min.js ├── strophe.jingle.sdp.js ├── strophe.min.js ├── strophe.muc.min.js ├── tf-backend-wasm.js ├── tfjs ├── tfjs-backend-wasm-simd.wasm ├── tfjs-backend-wasm.wasm └── whep-video.component.js ├── map.html ├── nav.css ├── sendstream.html ├── styles.css ├── tensorflow.js ├── webrtc-streamer-algo-element.js ├── webrtc-streamer-element.html ├── webrtc-streamer-element.js ├── webrtc-streamer-footer-element.js ├── webrtc-streamer-menu-element.js ├── webrtc-streamer-options-element.js ├── webrtc-streamer-selector-element.js ├── webrtc-streamer.html ├── webrtc.png ├── webrtcconfig.js ├── webrtcstreamer.html ├── webrtcstreamer.js ├── whep.html ├── xmppconfig.js ├── xmppvideoroom.html └── xmppvideoroom.js /Bunny.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webrtc-streamer-html 2 | HTML for webrtc-streamer server 3 | -------------------------------------------------------------------------------- /admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebRTC Streamer Info 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 |
21 | log 28 |
29 |
30 | 31 | 32 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /apprtc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AppRTC Url:
4 | Room id:
5 | WebRTC stream:
6 | 7 | 8 | 9 | 10 | 131 | 132 | -------------------------------------------------------------------------------- /apprtc.json: -------------------------------------------------------------------------------- 1 | var apprtcConfig = { 2 | url: "https://appr.tc", 3 | roomId: 051196040 4 | } 5 | -------------------------------------------------------------------------------- /help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Help

4 |
HTML sample
5 | Select WebRTC streams to display
6 | Select WebRTC streams to display in a 3x3 matrix
7 | Display the WebRTC stream given in the video parameter of the url
8 | Display the Bunny WebRTC stream using webcomponent
9 | Select a WebRTC stream using webcomponent
10 | Display WebRTC stream using webcomponent over googleMap
11 | Publish WebRTC streams to a Janus Video Room
12 | Publish WebRTC streams to XMPP Video Room (Jitsi)
13 |
Config
14 | Configuration for WebRtcStreamer
15 | Configuration for JanusVideoRoom
16 | Configuration for XMPPVideoRoom
17 |
API
18 | API list
19 | Version
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /htmlmapmarker.js: -------------------------------------------------------------------------------- 1 | class HTMLMapMarker extends google.maps.OverlayView { 2 | constructor(args) { 3 | super(); 4 | this._latlng = args.latlng; 5 | this._html = args.html; 6 | this._map = args.map; 7 | this._width = args.width; 8 | this._height = args.height; 9 | this.setMap(args.map); 10 | } 11 | 12 | createDiv() { 13 | this.div = document.createElement('div'); 14 | this.div.style.position = 'absolute'; 15 | if (this._width && this._height) { 16 | this.div.style.width = this._width; 17 | this.div.style.height = this._height; 18 | } 19 | if (this._html) { 20 | this.div.innerHTML = this._html; 21 | } 22 | 23 | google.maps.event.addDomListener(this.div, 'click', (event) => { 24 | event.stopPropagation(); 25 | 26 | google.maps.event.trigger(this, 'click', event); 27 | }); 28 | } 29 | 30 | positionDiv() { 31 | const point = this.getProjection().fromLatLngToDivPixel(this._latlng); 32 | if (point) { 33 | const left = point.x - this.div.clientWidth/2; 34 | this.div.style.left = `${left}px`; 35 | const top = point.y - this.div.clientHeight/2; 36 | this.div.style.top = `${top}px`; 37 | } 38 | } 39 | onAdd() { 40 | if (!this.div) { 41 | this.createDiv(); 42 | this.getPanes().overlayMouseTarget.appendChild(this.div); 43 | } 44 | this.positionDiv(); 45 | } 46 | 47 | draw() { 48 | this.positionDiv(); 49 | } 50 | 51 | onRemove() { 52 | if (this.div) { 53 | this.div.parentNode.removeChild(this.div); 54 | this.div = null; 55 | } 56 | } 57 | 58 | getPosition() { 59 | return this._latlng; 60 | } 61 | 62 | detach() { 63 | if (this.getMap()) { 64 | this.setMap(null); 65 | } 66 | }; 67 | 68 | attach() { 69 | if (!this.getMap()) { 70 | this.setMap(this._map); 71 | } 72 | }; 73 | 74 | setSize(width, height) { 75 | this._width = width; 76 | this._height = height; 77 | if (this.div) { 78 | this.div.style.width = this._width; 79 | this.div.style.height = this._height; 80 | } 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebRTC Streamer 4 | 5 | 6 | 7 | 8 | 9 | 251 | 252 | 253 |
254 |
255 | 256 |
257 |
258 |
259 |
260 | 261 | 262 | -------------------------------------------------------------------------------- /info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/info.png -------------------------------------------------------------------------------- /janusconfig.js: -------------------------------------------------------------------------------- 1 | var janusRoomConfig = { 2 | url: "https://janus.conf.meetecho.com/janus", 3 | roomId: 1234 4 | } 5 | -------------------------------------------------------------------------------- /janustest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /janusvideoroom-element.js: -------------------------------------------------------------------------------- 1 | import "./libs/request.min.js"; 2 | import "./janusvideoroom.js"; 3 | import "./libs/EventEmitter.min.js"; 4 | 5 | class WebRTCStreamerJanusElement extends HTMLElement { 6 | static get observedAttributes() { 7 | return ['videostream','audiostream','options', 'webrtcurl']; 8 | } 9 | 10 | constructor() { 11 | super(); 12 | this.shadowDOM = this.attachShadow({mode: 'open'}); 13 | this.shadowDOM.innerHTML = ` 14 | 15 | 16 | `; 17 | } 18 | connectedCallback() { 19 | this.connectStream(); 20 | } 21 | disconnectedCallback() { 22 | this.disconnectStream(); 23 | } 24 | attributeChangedCallback(attrName, oldVal, newVal) { 25 | this.connectStream(); 26 | } 27 | 28 | writeStatus(name, status) { 29 | var textNode = this.shadowDOM.createElement("h3"); 30 | textNode.innerHTML = name + " " + status; 31 | this.shadowDOM.body.appendChild(textNode); 32 | } 33 | 34 | disconnectStream() { 35 | if (this.janus) { 36 | this.janus.leave(); 37 | this.janus = null; 38 | } 39 | } 40 | connectStream() { 41 | this.disconnectStream(); 42 | 43 | let webrtcurl = this.getAttribute("webrtcurl") || webrtcConfig.url; 44 | let videostream = this.getAttribute("videostream") || webrtcConfig.defaultvideostream; 45 | let audiostream = this.getAttribute("audiostream") || webrtcConfig.defaultaudiostream; 46 | let options = this.getAttribute("options") || webrtcConfig.options; 47 | 48 | 49 | var bus = new EventEmitter(); 50 | bus.addListener('state', writeStatus); 51 | 52 | this.janus = new JanusVideoRoom(janusRoomConfig.url, webrtcurl, bus); 53 | this.janus.join(janusRoomConfig.roomId, {video:videostream}, videostream); 54 | } 55 | 56 | 57 | } 58 | 59 | customElements.define('webrtc-streamer-janus', WebRTCStreamerJanusElement); 60 | -------------------------------------------------------------------------------- /janusvideoroom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Janus Url: 12 | Room id: 13 | 14 | 15 | 16 | 17 | 18 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /janusvideoroom.js: -------------------------------------------------------------------------------- 1 | var JanusVideoRoom = (function() { 2 | 3 | 4 | /** 5 | * Interface with Janus Gateway Video Room and WebRTC-streamer API 6 | * @constructor 7 | * @param {string} janusUrl - url of Janus Gateway 8 | * @param {string} srvurl - url of WebRTC-streamer 9 | */ 10 | var JanusVideoRoom = function JanusVideoRoom (janusUrl, srvurl, bus) { 11 | this.janusUrl = janusUrl; 12 | this.handlers = []; 13 | this.srvurl = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port; 14 | this.connection = []; 15 | this.bus = bus; 16 | }; 17 | 18 | JanusVideoRoom.prototype._handleHttpErrors = function (response) { 19 | if (!response.ok) { 20 | throw Error(response.statusText); 21 | } 22 | return response; 23 | } 24 | 25 | /** 26 | * Ask to publish a stream from WebRTC-streamer in a Janus Video Room user 27 | * @param {string} janusroomid - id of the Janus Video Room to join 28 | * @param {string} url - WebRTC stream to publish 29 | * @param {string} name - name in Janus Video Room 30 | */ 31 | JanusVideoRoom.prototype.join = function(janusroomid, url, name) { 32 | // create a session 33 | var createReq = {janus: "create", transaction: Math.random().toString() }; 34 | 35 | fetch(this.janusUrl, { method: "POST", body: JSON.stringify(createReq) }) 36 | .then(this._handleHttpErrors) 37 | .then( (response) => (response.json()) ) 38 | .then( (response) => this.onCreateSession(response, janusroomid, url, name)) 39 | .catch( (error) => this.onError("create " + error )) 40 | 41 | } 42 | 43 | /** 44 | * Ask to unpublish a stream from WebRTC-streamer in a Janus Video Room user 45 | * @param {string} janusroomid - id of the Janus Video Room to join 46 | * @param {string} url - WebRTC stream to publish 47 | * @param {string} name - name in Janus Video Room 48 | */ 49 | JanusVideoRoom.prototype.leave = function(janusroomid, url, name) { 50 | var connection = this.connection[janusroomid + "_" + url + "_" + name]; 51 | if (connection) { 52 | var sessionId = connection.sessionId; 53 | var pluginid = connection.pluginId; 54 | 55 | var leaveReq = { "janus": "message", "body": {"request": "unpublish"}, "transaction": Math.random().toString() }; 56 | 57 | 58 | fetch(this.janusUrl + "/" + sessionId + "/" + pluginid, { method: "POST", body: JSON.stringify(leaveReq) }) 59 | .then(this._handleHttpErrors) 60 | .then( (response) => (response.json()) ) 61 | .then( (response) => console.log("leave janus room answer:" + response) ) 62 | .catch( (error) => this.onError("leave " + error )) 63 | } 64 | } 65 | 66 | 67 | JanusVideoRoom.prototype.emit = function(name, state) { 68 | if (this.bus) { 69 | this.bus.emit('state', name, state); 70 | } 71 | } 72 | 73 | // ------------------------------------------ 74 | // Janus callback for Session Creation 75 | // ------------------------------------------ 76 | JanusVideoRoom.prototype.onCreateSession = function(dataJson, janusroomid, url, name) { 77 | var sessionId = dataJson.data.id; 78 | console.log("onCreateSession sessionId:" + sessionId); 79 | 80 | // attach to video room plugin 81 | var attachReq = { "janus": "attach", "plugin": "janus.plugin.videoroom", "transaction": Math.random().toString() }; 82 | 83 | fetch(this.janusUrl + "/" + sessionId, { method: "POST", body: JSON.stringify(attachReq) }) 84 | .then(this._handleHttpErrors) 85 | .then( (response) => (response.json()) ) 86 | .then( (response) => this.onPluginsAttached(response, janusroomid, url, name, sessionId) ) 87 | .catch( (error) => this.onError("attach " + error )) 88 | 89 | } 90 | 91 | // ------------------------------------------ 92 | // Janus callback for Video Room Plugins Connection 93 | // ------------------------------------------ 94 | JanusVideoRoom.prototype.onPluginsAttached = function(dataJson, janusroomid, url, name, sessionId) { 95 | var pluginid = dataJson.data.id; 96 | console.log("onPluginsAttached pluginid:" + pluginid); 97 | 98 | this.emit(name, "joining"); 99 | 100 | var joinReq = {"janus":"message","body":{"request":"join","room":janusroomid,"ptype":"publisher","display":name},"transaction":Math.random().toString()}; 101 | 102 | fetch(this.janusUrl + "/" + sessionId + "/" + pluginid, { method: "POST", body: JSON.stringify(joinReq) }) 103 | .then(this._handleHttpErrors) 104 | .then( (response) => (response.json()) ) 105 | .then( (response) => this.onJoinRoom(response, janusroomid, url, name, sessionId, pluginid) ) 106 | .catch( (error) => this.onError("join " + error )) 107 | 108 | } 109 | 110 | // ------------------------------------------ 111 | // Janus callback for Video Room Joined 112 | // ------------------------------------------ 113 | JanusVideoRoom.prototype.onJoinRoom = function(dataJson,janusroomid,url,name,sessionId,pluginid) { 114 | console.log("onJoinRoom:" + JSON.stringify(dataJson)); 115 | 116 | fetch(this.janusUrl + "/" + sessionId + "?rid=" + new Date().getTime() + "&maxev=1") 117 | .then(this._handleHttpErrors) 118 | .then( (response) => (response.json()) ) 119 | .then( (response) => this.onJoinRoomResult(response, janusroomid, url, name, sessionId, pluginid) ) 120 | .catch( (error) => this.onError("join anwser " + error )) 121 | } 122 | 123 | // ------------------------------------------ 124 | // Janus callback for Video Room Joined 125 | // ------------------------------------------ 126 | JanusVideoRoom.prototype.onJoinRoomResult = function(dataJson,janusroomid,url,name,sessionId,pluginid) { 127 | console.log("onJoinRoomResult:" + JSON.stringify(dataJson)); 128 | 129 | if (dataJson.plugindata.data.videoroom === "joined") { 130 | // register connection 131 | this.connection[janusroomid + "_" + url + "_" + name] = {"sessionId":sessionId, "pluginId": pluginid }; 132 | 133 | // member of the room 134 | var publishers = dataJson.plugindata.data.publishers; 135 | for (var i=0; i (response.json()) ) 183 | .then( (response) => this.onPublishStream(response, name, sessionId, pluginid, peerid) ) 184 | .catch( (error) => this.onError("publish " + error )) 185 | 186 | } 187 | 188 | // ------------------------------------------ 189 | // Janus callback for WebRTC stream is published 190 | // ------------------------------------------ 191 | JanusVideoRoom.prototype.onPublishStream = function(dataJson,name,sessionId,pluginid,peerid) { 192 | console.log("onPublishStream:" + JSON.stringify(dataJson)); 193 | 194 | fetch(this.janusUrl + "/" + sessionId + "?rid=" + new Date().getTime() + "&maxev=1") 195 | .then(this._handleHttpErrors) 196 | .then( (response) => (response.json()) ) 197 | .then( (response) => this.onPublishStreamResult(response, name, sessionId, pluginid, peerid) ) 198 | .catch( (error) => this.onError("publish anwser " + error )) 199 | 200 | } 201 | 202 | // ------------------------------------------ 203 | // Janus callback for WebRTC stream is published 204 | // ------------------------------------------ 205 | JanusVideoRoom.prototype.onPublishStreamResult = function(dataJson,name,sessionId,pluginid,peerid) { 206 | console.log("onPublishStreamResult:" + JSON.stringify(dataJson)); 207 | 208 | if (dataJson.jsep) { 209 | fetch(this.srvurl + "/api/setAnswer?peerid="+ peerid, { method: "POST", body: JSON.stringify(dataJson.jsep) }) 210 | .then(this._handleHttpErrors) 211 | .then( (response) => (response.json()) ) 212 | .then( (response) => this.onSetAnswer(response, name, sessionId, pluginid, peerid) ) 213 | .catch( (error) => this.onError("setAnswer " + error )) 214 | 215 | } else { 216 | this.emit(name, "publishing failed (no SDP)"); 217 | } 218 | } 219 | 220 | // ------------------------------------------ 221 | // WebRTC streamer callback for Answer 222 | // ------------------------------------------ 223 | JanusVideoRoom.prototype.onSetAnswer = function(dataJson,name,sessionId,pluginid,peerid) { 224 | console.log("onSetAnswer:" + JSON.stringify(dataJson)); 225 | 226 | fetch(this.srvurl + "/api/getIceCandidate?peerid="+peerid) 227 | .then(this._handleHttpErrors) 228 | .then( (response) => (response.json()) ) 229 | .then( (response) => this.onReceiveCandidate(response, name, sessionId, pluginid) ) 230 | .catch( (error) => this.onError("getIceCandidate " + error )) 231 | 232 | } 233 | 234 | // ------------------------------------------ 235 | // WebRTC streamer callback for ICE candidate 236 | // ------------------------------------------ 237 | JanusVideoRoom.prototype.onReceiveCandidate = function(dataJson,name,sessionId,pluginid) { 238 | console.log("onReceiveCandidate answer:" + JSON.stringify(dataJson)); 239 | 240 | for (var i=0; i (response.json()) ) 247 | .then( (response) => console.log("onReceiveCandidate janus answer:" + JSON.stringify(response)) ) 248 | .catch( (error) => this.onError("setAnswer " + error )) 249 | 250 | } 251 | 252 | // start long polling 253 | this.longpoll(null, name, sessionId); 254 | } 255 | 256 | // ------------------------------------------ 257 | // Janus callback for keepAlive Session 258 | // ------------------------------------------ 259 | JanusVideoRoom.prototype.keepAlive = function(sessionId) { 260 | var keepAliveReq = { "janus": "keepalive", "session_id": sessionId, "transaction": Math.random().toString() }; 261 | 262 | fetch(this.janusUrl + "/" + sessionId, { method: "POST", body: JSON.stringify(keepAliveReq) }) 263 | .then(this._handleHttpErrors) 264 | .then( (response) => (response.json()) ) 265 | .then( (response) => console.log("keepAlive answer:" + JSON.stringify(response)) ) 266 | .catch( (error) => this.onError("keepAlive " + error )) 267 | 268 | } 269 | 270 | // ------------------------------------------ 271 | // Janus callback for Long Polling 272 | // ------------------------------------------ 273 | JanusVideoRoom.prototype.longpoll = function(dataJson, name, sessionId) { 274 | if (dataJson) { 275 | console.log("poll evt:" + JSON.stringify(dataJson)); 276 | 277 | if (dataJson.janus === "webrtcup") { 278 | // notify connection 279 | this.emit(name, "up"); 280 | 281 | // start keep alive 282 | var bind = this; 283 | setInterval( function() { bind.keepAlive(sessionId); }, 10000); 284 | } 285 | else if (dataJson.janus === "hangup") { 286 | // notify connection 287 | this.emit(name, "down"); 288 | } 289 | else if (dataJson.janus === "event") { 290 | // member of the room 291 | var publishers = dataJson.plugindata.data.publishers; 292 | if (publishers) { 293 | for (var i=0; i (response.json()) ) 304 | .then( (response) => this.longpoll(response, name, sessionId) ) 305 | .catch( (error) => this.onError("longpoll anwser " + error )) 306 | 307 | } 308 | 309 | // ------------------------------------------ 310 | // Janus callback for Error 311 | // ------------------------------------------ 312 | JanusVideoRoom.prototype.onError = function(status) { 313 | console.log("onError:" + status); 314 | } 315 | 316 | 317 | return JanusVideoRoom; 318 | })(); 319 | 320 | if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { 321 | window.JanusVideoRoom = JanusVideoRoom; 322 | } 323 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { 324 | module.exports = JanusVideoRoom; 325 | } 326 | -------------------------------------------------------------------------------- /jitsi.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jitsi.ico -------------------------------------------------------------------------------- /jitsiroom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jitsi Room 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | XMPP Url: 14 | Room id: 15 | Load 16 | Unload 17 |
18 | 19 | 144 | 145 | -------------------------------------------------------------------------------- /jitsitest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /jsdoc/JanusVideoRoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: JanusVideoRoom 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: JanusVideoRoom

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

JanusVideoRoom

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 |

new JanusVideoRoom(janusUrl, srvurl)

44 | 45 | 46 | 47 | 48 | 49 |
50 | Interface with Janus Gateway Video Room and WebRTC-streamer API 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Parameters:
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
NameTypeDescription
janusUrl 90 | 91 | 92 | string 93 | 94 | 95 | 96 | url of Janus Gateway
srvurl 113 | 114 | 115 | string 116 | 117 | 118 | 119 | url of WebRTC-streamer
131 | 132 | 133 | 134 | 135 | 136 | 137 |
138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
Source:
165 |
168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |

Methods

209 | 210 | 211 | 212 | 213 | 214 | 215 |

join(janusroomid, url, name)

216 | 217 | 218 | 219 | 220 | 221 |
222 | Ask to publish a stream from WebRTC-streamer in a Janus Video Room user 223 |
224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
Parameters:
234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 |
NameTypeDescription
janusroomid 262 | 263 | 264 | string 265 | 266 | 267 | 268 | id of the Janus Video Room to join
url 285 | 286 | 287 | string 288 | 289 | 290 | 291 | WebRTC stream to publish
name 308 | 309 | 310 | string 311 | 312 | 313 | 314 | name in Janus Video Room
326 | 327 | 328 | 329 | 330 | 331 | 332 |
333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 |
Source:
360 |
363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 |
371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 |

leave(janusroomid, url, name)

393 | 394 | 395 | 396 | 397 | 398 |
399 | Ask to unpublish a stream from WebRTC-streamer in a Janus Video Room user 400 |
401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 |
Parameters:
411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 |
NameTypeDescription
janusroomid 439 | 440 | 441 | string 442 | 443 | 444 | 445 | id of the Janus Video Room to join
url 462 | 463 | 464 | string 465 | 466 | 467 | 468 | WebRTC stream to publish
name 485 | 486 | 487 | string 488 | 489 | 490 | 491 | name in Janus Video Room
503 | 504 | 505 | 506 | 507 | 508 | 509 |
510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 |
Source:
537 |
540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 |
548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 |

subscribeEvents(fn)

570 | 571 | 572 | 573 | 574 | 575 |
576 | subscribeEvents 577 |
578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 |
Parameters:
588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 |
NameTypeDescription
fn 616 | 617 | 618 | string 619 | 620 | 621 | 622 | funtcion to call
634 | 635 | 636 | 637 | 638 | 639 | 640 |
641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 |
Source:
668 |
671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 |
679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 |
702 | 703 |
704 | 705 | 706 | 707 | 708 |
709 | 710 | 713 | 714 |
715 | 716 |
717 | Documentation generated by JSDoc 3.4.3 on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) 718 |
719 | 720 | 721 | 722 | 723 | -------------------------------------------------------------------------------- /jsdoc/WebRtcStreamer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: WebRtcStreamer 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: WebRtcStreamer

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

WebRtcStreamer

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 |

new WebRtcStreamer(videoElement, srvurl)

44 | 45 | 46 | 47 | 48 | 49 |
50 | Interface with WebRTC-streamer API 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Parameters:
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
NameTypeDescription
videoElement 90 | 91 | 92 | string 93 | 94 | 95 | 96 | id of the video element tag
srvurl 113 | 114 | 115 | string 116 | 117 | 118 | 119 | url of webrtc-streamer (default is current location)
131 | 132 | 133 | 134 | 135 | 136 | 137 |
138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
Source:
165 |
168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |

Methods

209 | 210 | 211 | 212 | 213 | 214 | 215 |

connect(videourl, audiourl, options, stream)

216 | 217 | 218 | 219 | 220 | 221 |
222 | Connect a WebRTC Stream to videoElement 223 |
224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
Parameters:
234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 |
NameTypeDescription
videourl 262 | 263 | 264 | string 265 | 266 | 267 | 268 | id of WebRTC video stream
audiourl 285 | 286 | 287 | string 288 | 289 | 290 | 291 | id of WebRTC audio stream
options 308 | 309 | 310 | string 311 | 312 | 313 | 314 | options of WebRTC call
stream 331 | 332 | 333 | string 334 | 335 | 336 | 337 | local stream to send
349 | 350 | 351 | 352 | 353 | 354 | 355 |
356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 |
Source:
383 |
386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 |
394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 |

disconnect()

416 | 417 | 418 | 419 | 420 | 421 |
422 | Disconnect a WebRTC Stream and clear videoElement source 423 |
424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 |
438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 |
Source:
465 |
468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 |
476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 |
499 | 500 |
501 | 502 | 503 | 504 | 505 |
506 | 507 | 510 | 511 |
512 | 513 |
514 | Documentation generated by JSDoc 3.4.3 on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) 515 |
516 | 517 | 518 | 519 | 520 | -------------------------------------------------------------------------------- /jsdoc/XMPPVideoRoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: XMPPVideoRoom 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: XMPPVideoRoom

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

XMPPVideoRoom

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 |

new XMPPVideoRoom(xmppUrl, srvurl)

44 | 45 | 46 | 47 | 48 | 49 |
50 | Interface with Jitsi Video Room and WebRTC-streamer API 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Parameters:
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
NameTypeDescription
xmppUrl 90 | 91 | 92 | string 93 | 94 | 95 | 96 | url of XMPP server
srvurl 113 | 114 | 115 | string 116 | 117 | 118 | 119 | url of WebRTC-streamer
131 | 132 | 133 | 134 | 135 | 136 | 137 |
138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
Source:
165 |
168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |

Methods

209 | 210 | 211 | 212 | 213 | 214 | 215 |

join(roomid, url, name)

216 | 217 | 218 | 219 | 220 | 221 |
222 | Ask to publish a stream from WebRTC-streamer in a XMPP Video Room user 223 |
224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
Parameters:
234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 |
NameTypeDescription
roomid 262 | 263 | 264 | string 265 | 266 | 267 | 268 | id of the XMPP Video Room to join
url 285 | 286 | 287 | string 288 | 289 | 290 | 291 | WebRTC stream to publish
name 308 | 309 | 310 | string 311 | 312 | 313 | 314 | name in Video Room
326 | 327 | 328 | 329 | 330 | 331 | 332 |
333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 |
Source:
360 |
363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 |
371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 |

leave(roomid, name)

393 | 394 | 395 | 396 | 397 | 398 |
399 | Ask to leave a XMPP Video Room user 400 |
401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 |
Parameters:
411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 |
NameTypeDescription
roomid 439 | 440 | 441 | string 442 | 443 | 444 | 445 | id of the XMPP Video Room to leave
name 462 | 463 | 464 | string 465 | 466 | 467 | 468 | name in Video Room
480 | 481 | 482 | 483 | 484 | 485 | 486 |
487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 |
Source:
514 |
517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 |
525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 |

leaveAll()

547 | 548 | 549 | 550 | 551 | 552 |
553 | Ask to leave all XMPP Video Room 554 |
555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 |
569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 |
Source:
596 |
599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 |
607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 |
630 | 631 |
632 | 633 | 634 | 635 | 636 |
637 | 638 | 641 | 642 |
643 | 644 |
645 | Documentation generated by JSDoc 3.4.3 on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) 646 |
647 | 648 | 649 | 650 | 651 | -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /jsdoc/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/jsdoc/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /jsdoc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 55 | 56 |
57 | 58 |
59 | Documentation generated by JSDoc 3.4.3 on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) 60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /jsdoc/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /jsdoc/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /jsdoc/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /jsdoc/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p th:last-child { border-right: 1px solid #ddd; } 220 | 221 | .ancestors { color: #999; } 222 | .ancestors a 223 | { 224 | color: #999 !important; 225 | text-decoration: none; 226 | } 227 | 228 | .clear 229 | { 230 | clear: both; 231 | } 232 | 233 | .important 234 | { 235 | font-weight: bold; 236 | color: #950B02; 237 | } 238 | 239 | .yes-def { 240 | text-indent: -1000px; 241 | } 242 | 243 | .type-signature { 244 | color: #aaa; 245 | } 246 | 247 | .name, .signature { 248 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 249 | } 250 | 251 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 252 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 253 | .details dd { margin-left: 70px; } 254 | .details ul { margin: 0; } 255 | .details ul { list-style-type: none; } 256 | .details li { margin-left: 30px; padding-top: 6px; } 257 | .details pre.prettyprint { margin: 0 } 258 | .details .object-value { padding-top: 0; } 259 | 260 | .description { 261 | margin-bottom: 1em; 262 | margin-top: 1em; 263 | } 264 | 265 | .code-caption 266 | { 267 | font-style: italic; 268 | font-size: 107%; 269 | margin: 0; 270 | } 271 | 272 | .prettyprint 273 | { 274 | border: 1px solid #ddd; 275 | width: 80%; 276 | overflow: auto; 277 | } 278 | 279 | .prettyprint.source { 280 | width: inherit; 281 | } 282 | 283 | .prettyprint code 284 | { 285 | font-size: 100%; 286 | line-height: 18px; 287 | display: block; 288 | padding: 4px 12px; 289 | margin: 0; 290 | background-color: #fff; 291 | color: #4D4E53; 292 | } 293 | 294 | .prettyprint code span.line 295 | { 296 | display: inline-block; 297 | } 298 | 299 | .prettyprint.linenums 300 | { 301 | padding-left: 70px; 302 | -webkit-user-select: none; 303 | -moz-user-select: none; 304 | -ms-user-select: none; 305 | user-select: none; 306 | } 307 | 308 | .prettyprint.linenums ol 309 | { 310 | padding-left: 0; 311 | } 312 | 313 | .prettyprint.linenums li 314 | { 315 | border-left: 3px #ddd solid; 316 | } 317 | 318 | .prettyprint.linenums li.selected, 319 | .prettyprint.linenums li.selected * 320 | { 321 | background-color: lightyellow; 322 | } 323 | 324 | .prettyprint.linenums li * 325 | { 326 | -webkit-user-select: text; 327 | -moz-user-select: text; 328 | -ms-user-select: text; 329 | user-select: text; 330 | } 331 | 332 | .params .name, .props .name, .name code { 333 | color: #4D4E53; 334 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 335 | font-size: 100%; 336 | } 337 | 338 | .params td.description > p:first-child, 339 | .props td.description > p:first-child 340 | { 341 | margin-top: 0; 342 | padding-top: 0; 343 | } 344 | 345 | .params td.description > p:last-child, 346 | .props td.description > p:last-child 347 | { 348 | margin-bottom: 0; 349 | padding-bottom: 0; 350 | } 351 | 352 | .disabled { 353 | color: #454545; 354 | } 355 | -------------------------------------------------------------------------------- /jsdoc/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /jsdoc/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /jsdoc/webrtcstreamer.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: webrtcstreamer.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: webrtcstreamer.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |

 30 | /** 
 31 |  * Interface with WebRTC-streamer API
 32 |  * @constructor
 33 |  * @param {string} videoElement - id of the video element tag
 34 |  * @param {string} srvurl -  url of webrtc-streamer (default is current location)
 35 | */
 36 | function WebRtcStreamer (videoElement, srvurl) {
 37 | 	this.videoElement     = videoElement;	
 38 | 	this.srvurl           = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port;
 39 | 	this.pc               = null;    
 40 | 
 41 | 	this.pcOptions        = { "optional": [{"DtlsSrtpKeyAgreement": true} ] };
 42 | 
 43 | 	this.mediaConstraints = { offerToReceiveAudio: true, offerToReceiveVideo: true };
 44 | 
 45 | 	this.iceServers = null;
 46 | 	this.earlyCandidates = [];
 47 | }
 48 | 
 49 | /** 
 50 |  * Connect a WebRTC Stream to videoElement 
 51 |  * @param {string} videourl - id of WebRTC video stream
 52 |  * @param {string} audiourl - id of WebRTC audio stream
 53 |  * @param {string} options -  options of WebRTC call
 54 |  * @param {string} stream  -  local stream to send
 55 | */
 56 | WebRtcStreamer.prototype.connect = function(videourl, audiourl, options, localstream) {
 57 | 	this.disconnect();
 58 | 	
 59 | 	// getIceServers is not already received
 60 | 	if (!this.iceServers) {
 61 | 		console.log("Get IceServers");
 62 | 		
 63 | 		var bind = this;
 64 | 		request("GET" , this.srvurl + "/api/getIceServers")
 65 | 			.done( function (response) { 
 66 | 				if (response.statusCode === 200) {
 67 | 					bind.onReceiveGetIceServers.call(bind,JSON.parse(response.body), videourl, audiourl, options, localstream);
 68 | 				}
 69 | 				else {
 70 | 					bind.onError(response.statusCode);
 71 | 				}
 72 | 			}
 73 | 		);		
 74 | 	} else {
 75 | 		this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream);
 76 | 	}
 77 | }
 78 | 
 79 | /** 
 80 |  * Disconnect a WebRTC Stream and clear videoElement source
 81 | */
 82 | WebRtcStreamer.prototype.disconnect = function() {		
 83 | 	var videoElement = document.getElementById(this.videoElement);
 84 | 	if (videoElement) {
 85 | 		videoElement.src = "";
 86 | 	}
 87 | 	if (this.pc) {
 88 | 		request("GET" , this.srvurl + "/api/hangup?peerid="+this.pc.peerid);
 89 | 		
 90 | 		try {
 91 | 			this.pc.close();
 92 | 		}
 93 | 		catch (e) {
 94 | 			console.log ("Failure close peer connection:" + e);
 95 | 		}
 96 | 		this.pc = null;
 97 | 	}
 98 | }    
 99 | 
100 | /*
101 | * GetIceServers callback
102 | */
103 | WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl, audiourl, options, stream) {
104 | 	this.iceServers       = iceServers;
105 | 	this.pcConfig         = iceServers || {"iceServers": [] };
106 | 	try {            
107 | 		this.pc = this.createPeerConnection();
108 | 
109 | 		var peerid = Math.random();			
110 | 		this.pc.peerid = peerid;
111 | 		
112 | 		var callurl = this.srvurl + "/api/call?peerid="+ peerid+"&url="+encodeURIComponent(videourl);
113 | 		if (audiourl) {
114 | 			callurl += "&audiourl="+encodeURIComponent(audiourl);
115 | 		}
116 | 		if (options) {
117 | 			callurl += "&options="+encodeURIComponent(options);
118 | 		}
119 | 		
120 | 		if (stream) {
121 | 			this.pc.addStream(stream);
122 | 		}
123 | 
124 |                 // clear early candidates
125 | 		this.earlyCandidates.length = 0;
126 | 		
127 | 		// create Offer
128 | 		var bind = this;
129 | 		this.pc.createOffer(this.mediaConstraints).then(function(sessionDescription) {
130 | 			console.log("Create offer:" + JSON.stringify(sessionDescription));
131 | 			
132 | 			bind.pc.setLocalDescription(sessionDescription
133 | 				, function() {
134 | 					request("POST" , callurl, { body: JSON.stringify(sessionDescription) })
135 | 						.done( function (response) { 
136 | 							if (response.statusCode === 200) {
137 | 								bind.onReceiveCall.call(bind,JSON.parse(response.body));
138 | 							}
139 | 							else {
140 | 								bind.onError(response.statusCode);
141 | 							}
142 | 						}
143 | 					);					
144 | 				}
145 | 				, function() {} );
146 | 			
147 | 		}, function(error) { 
148 | 			alert("Create offer error:" + JSON.stringify(error));
149 | 		});
150 | 
151 | 	} catch (e) {
152 | 		this.disconnect();
153 | 		alert("connect error: " + e);
154 | 	}	    
155 | }
156 | 
157 | /*
158 | * create RTCPeerConnection 
159 | */
160 | WebRtcStreamer.prototype.createPeerConnection = function() {
161 | 	console.log("createPeerConnection  config: " + JSON.stringify(this.pcConfig) + " option:"+  JSON.stringify(this.pcOptions));
162 | 	var pc = new RTCPeerConnection(this.pcConfig, this.pcOptions);
163 | 	var streamer = this;
164 | 	pc.onicecandidate = function(evt) { streamer.onIceCandidate.call(streamer, evt); };
165 | 	pc.onaddstream    = function(evt) { streamer.onAddStream.call(streamer,evt); };
166 | 	pc.oniceconnectionstatechange = function(evt) {  
167 | 		console.log("oniceconnectionstatechange  state: " + pc.iceConnectionState);
168 | 		var videoElement = document.getElementById(streamer.videoElement);
169 | 		if (videoElement) {
170 | 			if (pc.iceConnectionState === "connected") {
171 | 				videoElement.style.opacity = "1.0";
172 | 			}			
173 | 			else if (pc.iceConnectionState === "disconnected") {
174 | 				videoElement.style.opacity = "0.25";
175 | 			}			
176 | 			else if ( (pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed") )  {
177 | 				videoElement.style.opacity = "0.5";
178 | 			}			
179 | 		}
180 | 	}
181 | 	pc.ondatachannel = function(evt) {  
182 | 		console.log("remote datachannel created:"+JSON.stringify(evt));
183 | 		
184 | 		evt.channel.onopen = function () {
185 | 			console.log("remote datachannel open");
186 | 			this.send("remote channel openned");
187 | 		}
188 | 		evt.channel.onmessage = function (event) {
189 | 			console.log("remote datachannel recv:"+JSON.stringify(event.data));
190 | 		}
191 | 	}
192 | 
193 | 	try {
194 | 		var dataChannel = pc.createDataChannel("ClientDataChannel");
195 | 		dataChannel.onopen = function() {
196 | 			console.log("local datachannel open");
197 | 			this.send("local channel openned");
198 | 		}
199 | 		dataChannel.onmessage = function(evt) {
200 | 			console.log("local datachannel recv:"+JSON.stringify(evt.data));
201 | 		}
202 | 	} catch (e) {
203 | 		console.log("Cannor create datachannel error: " + e);
204 | 	}	
205 | 	
206 | 	console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig) + "option:"+  JSON.stringify(this.pcOptions) );
207 | 	return pc;
208 | }
209 | 
210 | 
211 | /*
212 | * RTCPeerConnection IceCandidate callback
213 | */
214 | WebRtcStreamer.prototype.onIceCandidate = function (event) {
215 | 	if (event.candidate) {
216 |                 if (this.pc.currentRemoteDescription)  {
217 | 			var bind = this;
218 | 			request("POST" , this.srvurl + "/api/addIceCandidate?peerid="+this.pc.peerid, { body: JSON.stringify(event.candidate) })
219 | 				.done( function (response) { 
220 | 					if (response.statusCode === 200) {
221 | 						console.log("addIceCandidate ok:" + response.body);
222 | 					}
223 | 					else {
224 | 						bind.onError(response.statusCode);
225 | 					}
226 | 				}
227 | 			);					
228 | 		} else {
229 | 			this.earlyCandidates.push(event.candidate);
230 | 		}
231 | 	} 
232 | 	else {
233 | 		console.log("End of candidates.");
234 | 	}
235 | }
236 | 
237 | /*
238 | * RTCPeerConnection AddTrack callback
239 | */
240 | WebRtcStreamer.prototype.onAddStream = function(event) {
241 | 	console.log("Remote track added:" +  JSON.stringify(event));
242 | 	
243 | 	var videoElement = document.getElementById(this.videoElement);
244 | 	videoElement.srcObject = event.stream;
245 | 	videoElement.setAttribute("playsinline", true);
246 | 	videoElement.play();
247 | }
248 | 		
249 | /*
250 | * AJAX /call callback
251 | */
252 | WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {
253 | 	var bind = this;
254 | 	console.log("offer: " + JSON.stringify(dataJson));
255 | 	this.pc.setRemoteDescription(new RTCSessionDescription(dataJson)
256 | 		, function()      { 
257 |                         console.log ("setRemoteDescription ok");
258 |                         while (bind.earlyCandidates.length) {
259 | 				var candidate = bind.earlyCandidates.shift();
260 | 				
261 | 				request("POST" , bind.srvurl + "/api/addIceCandidate?peerid=" + bind.pc.peerid, { body: JSON.stringify(candidate) })
262 | 					.done( function (response) { 
263 | 						if (response.statusCode === 200) {
264 | 							console.log("addIceCandidate ok:" + response.body);
265 | 						}
266 | 						else {
267 | 							bind.onError(response.statusCode);
268 | 						}
269 | 					}
270 | 				);
271 | 			}
272 | 			
273 | 			request("GET" , bind.srvurl + "/api/getIceCandidate?peerid=" + bind.pc.peerid)
274 | 				.done( function (response) { 
275 | 					if (response.statusCode === 200) {
276 | 						bind.onReceiveCandidate.call(bind,JSON.parse(response.body));
277 | 					}
278 | 					else {
279 | 						bind.onError(response.statusCode);
280 | 					}
281 | 				}
282 | 			);
283 | 				
284 | 		}
285 | 		, function(error) { console.log ("setRemoteDescription error:" + JSON.stringify(error)); });
286 | }	
287 | 
288 | /*
289 | * AJAX /getIceCandidate callback
290 | */
291 | WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) {
292 | 	console.log("candidate: " + JSON.stringify(dataJson));
293 | 	if (dataJson) {
294 | 		for (var i=0; i<dataJson.length; i++) {
295 | 			var candidate = new RTCIceCandidate(dataJson[i]);
296 | 			
297 | 			console.log("Adding ICE candidate :" + JSON.stringify(candidate) );
298 | 			this.pc.addIceCandidate(candidate
299 | 				, function()      { console.log ("addIceCandidate OK"); }
300 | 				, function(error) { console.log ("addIceCandidate error:" + JSON.stringify(error)); } );
301 | 		}
302 | 		this.pc.addIceCandidate();
303 | 	}
304 | }
305 | 
306 | 
307 | /*
308 | * AJAX callback for Error
309 | */
310 | WebRtcStreamer.prototype.onError = function(status) {
311 | 	console.log("onError:" + status);
312 | }
313 |
314 |
315 | 316 | 317 | 318 | 319 |
320 | 321 | 324 | 325 |
326 | 327 |
328 | Documentation generated by JSDoc 3.4.3 on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) 329 |
330 | 331 | 332 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /libs/EventEmitter.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * EventEmitter v5.2.5 - git.io/ee 3 | * Unlicense - http://unlicense.org/ 4 | * Oliver Caldwell - http://oli.me.uk/ 5 | * @preserve 6 | */ 7 | !function(e){"use strict";function t(){}function n(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function r(e){return function(){return this[e].apply(this,arguments)}}function i(e){return"function"==typeof e||e instanceof RegExp||!(!e||"object"!=typeof e)&&i(e.listener)}var s=t.prototype,o=e.EventEmitter;s.getListeners=function(e){var t,n,r=this._getEvents();if(e instanceof RegExp){t={};for(n in r)r.hasOwnProperty(n)&&e.test(n)&&(t[n]=r[n])}else t=r[e]||(r[e]=[]);return t},s.flattenListeners=function(e){var t,n=[];for(t=0;t0&&o[o.length-1])&&(6===i[0]||2===i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&m[m.length-1])||6!==t[0]&&2!==t[0])){d=0;continue}if(3===t[0]&&(!m||t[1]>m[0]&&t[1]d&&(d=e[t*i+o],s=o);n[t]=d,m[t]=s}return[n,m]},e.prototype.detect=function(e,a,i){return void 0===a&&(a=20),void 0===i&&(i=.5),n(this,void 0,void 0,(function(){return m(this,(function(n){return[2,this.infer(e,a,i)]}))}))},e.prototype.dispose=function(){null!=this.model&&this.model.dispose()},e}();e.ObjectDetection=d,e.load=function(e){return void 0===e&&(e={}),n(this,void 0,void 0,(function(){var a,n,t;return m(this,(function(m){switch(m.label){case 0:if(null==i)throw new Error("Cannot find TensorFlow.js. If you are using a 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 95 | 96 | -------------------------------------------------------------------------------- /nav.css: -------------------------------------------------------------------------------- 1 | nav { 2 | background-color: #333; 3 | overflow: hidden; 4 | } 5 | nav a { 6 | float: left; 7 | display: block; 8 | color: #f2f2f2; 9 | text-align: center; 10 | padding: 1rem 1rem; 11 | user-select: none; 12 | } 13 | nav a:hover { 14 | background-color: #ddd; 15 | color: black; 16 | } 17 | nav a.active { 18 | background-color: #4CAF50; 19 | color: white; 20 | } 21 | nav a.active:hover { 22 | background-color: #4CAF50; 23 | color: black; 24 | } 25 | -------------------------------------------------------------------------------- /sendstream.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 36 | 37 | 38 |
39 | 40 |
41 |
42 | 43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | @import url('/nav.css'); 2 | h2 { 3 | text-align: center; 4 | } 5 | 6 | #footer { 7 | text-align: center; 8 | flex-shrink: 0; 9 | } 10 | 11 | #container { 12 | width: 100%; 13 | height: 100%; 14 | display: flex; 15 | flex-direction: column; 16 | flex-wrap: nowrap; 17 | } 18 | 19 | header { 20 | flex-shrink: 0; 21 | } 22 | 23 | #content { 24 | flex-grow: 1; 25 | overflow: auto; 26 | min-height: 2em; 27 | } 28 | 29 | div { 30 | text-align: center; 31 | } 32 | 33 | #video { 34 | width: auto; 35 | height: 100%; 36 | } 37 | 38 | video { 39 | margin: auto; 40 | left: 0; 41 | right: 0; 42 | position: relative; 43 | background: grey; 44 | } 45 | 46 | canvas { 47 | position: absolute; 48 | margin: auto; 49 | left: 0; 50 | right: 0; 51 | z-index:999; 52 | } 53 | -------------------------------------------------------------------------------- /tensorflow.js: -------------------------------------------------------------------------------- 1 | 2 | window.runDetect = (model, video, canvas) => { 3 | 4 | console.time('runDetect'); 5 | // detect objects in the image. 6 | model.detect(video).then(predictions => { 7 | console.timeEnd('runDetect'); 8 | console.log('Predictions: ', predictions); 9 | 10 | const context = canvas.getContext('2d'); 11 | context.clearRect(0, 0, canvas.width, canvas.height); 12 | context.font = '10px Arial'; 13 | 14 | console.log('number of detections: ', predictions.length); 15 | for (let i = 0; i < predictions.length; i++) { 16 | context.beginPath(); 17 | context.rect(...predictions[i].bbox); 18 | context.lineWidth = 2; 19 | context.strokeStyle = 'green'; 20 | context.fillStyle = 'green'; 21 | context.stroke(); 22 | context.fillText( 23 | predictions[i].score.toFixed(3) + ' ' + predictions[i].class, predictions[i].bbox[0], 24 | predictions[i].bbox[1] > 10 ? predictions[i].bbox[1] - 5 : 10); 25 | } 26 | 27 | window.setTimeout(() => { 28 | if (model.run) { 29 | model.run(model, video, canvas); 30 | } 31 | }, 120); 32 | }); 33 | } 34 | 35 | window.runPosenet = (model, video, canvas) => { 36 | 37 | console.time('runPosenet'); 38 | model.estimatePoses(video, { decodingMethod: 'multi-person', maxDetections: 5 }).then(poses => { 39 | console.timeEnd('runPosenet'); 40 | console.log('Predictions: ', poses); 41 | 42 | const ctx = canvas.getContext('2d'); 43 | ctx.clearRect(0, 0, canvas.width, canvas.height); 44 | ctx.font = '10px Arial'; 45 | 46 | poses.forEach(({ score, keypoints }) => { 47 | console.log('keypoints: ', keypoints); 48 | for (let i = 0; i < keypoints.length; i++) { 49 | const keypoint = keypoints[i]; 50 | if (keypoint.score >= 0.5) { 51 | const { y, x } = keypoint.position; 52 | console.log(keypoint.part, ' : ', x, "x", y); 53 | ctx.beginPath(); 54 | ctx.arc(x, y, 3, 0, 2 * Math.PI); 55 | ctx.fillStyle = 'red'; 56 | ctx.fill(); 57 | } 58 | } 59 | const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, 0.5); 60 | console.log('adjacentKeyPoints: ', adjacentKeyPoints); 61 | 62 | adjacentKeyPoints.forEach((keypoints) => { 63 | const org = keypoints[0].position; 64 | const dest = keypoints[1].position; 65 | ctx.beginPath(); 66 | ctx.moveTo(org.x,org.y); 67 | ctx.lineTo(dest.x,dest.y); 68 | ctx.lineWidth = 2; 69 | ctx.strokeStyle = 'green'; 70 | ctx.stroke(); 71 | }); 72 | 73 | }); 74 | 75 | window.setTimeout(() => { 76 | if (model.run) { 77 | model.run(model, video, canvas); 78 | } 79 | }, 120); 80 | }); 81 | } 82 | 83 | window.runDeeplab = (model, video, canvas) => { 84 | 85 | console.time('runDeeplab'); 86 | model.segment(video).then(deeplabOutput => { 87 | console.timeEnd('runDeeplab'); 88 | console.log('deeplabOutput: ', deeplabOutput); 89 | const { legend, height, width, segmentationMap } = deeplabOutput; 90 | 91 | const scalecanvas = document.createElement('canvas') 92 | scalecanvas.width = width; 93 | scalecanvas.height = height; 94 | const segmentationMapData = new ImageData(segmentationMap, width, height) 95 | var imageData = segmentationMapData.data; 96 | for (var i = 0; i < imageData.length; i += 4) { 97 | if ((imageData[i] == 0) && (imageData[i + 1] == 0) && (imageData[i + 2] == 0) && (imageData[i + 3] == 255)) { 98 | imageData[i + 3] = 0; 99 | } else { 100 | imageData[i + 3] = 200; 101 | } 102 | } 103 | scalecanvas.getContext('2d').putImageData(segmentationMapData, 0, 0); 104 | 105 | const ctx = canvas.getContext('2d'); 106 | ctx.font = '16px Arial'; 107 | ctx.clearRect(0, 0, canvas.width, canvas.height); 108 | ctx.drawImage(scalecanvas, 0, 0, width, height, 0, 0, canvas.width, canvas.height); 109 | 110 | let cnt = 0 111 | Object.keys(legend).forEach((label) => { 112 | const [red, green, blue] = legend[label]; 113 | 114 | ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`; 115 | ctx.fillRect(0, cnt * 16 + 16, 32, 16); 116 | ctx.fillText(label, 40, cnt * 16 + 32); 117 | cnt++ 118 | }); 119 | 120 | window.setTimeout(() => { 121 | if (model.run) { 122 | model.run(model, video, canvas); 123 | } 124 | }, 120); 125 | }); 126 | } 127 | 128 | 129 | window.runbodyPix = (model, video, canvas) => { 130 | 131 | console.time('runbodyPix'); 132 | model.segmentMultiPersonParts(video).then(multiPersonPartSegmentation => { 133 | console.timeEnd('runbodyPix'); 134 | 135 | console.log('multiPersonPartSegmentation: ', multiPersonPartSegmentation); 136 | const coloredPartImageData = bodyPix.toColoredPartMask(multiPersonPartSegmentation); 137 | 138 | const ctx = canvas.getContext('2d'); 139 | if (coloredPartImageData) { 140 | ctx.clearRect(0, 0, canvas.width, canvas.height); 141 | bodyPix.drawMask(canvas, video, coloredPartImageData, 0.3); 142 | } 143 | 144 | window.setTimeout(() => { 145 | if (model.run) { 146 | model.run(model, video, canvas); 147 | } 148 | }, 120); 149 | }); 150 | } 151 | 152 | window.runblazeface = (model, video, canvas) => { 153 | 154 | console.time('runblazeface'); 155 | model.estimateFaces(video, false).then(predictions => { 156 | console.timeEnd('runblazeface'); 157 | 158 | console.log('predictions: ', predictions); 159 | 160 | const ctx = canvas.getContext('2d'); 161 | context.clearRect(0, 0, canvas.width, canvas.height); 162 | for (let i = 0; i < predictions.length; i++) { 163 | const start = predictions[i].topLeft; 164 | const end = predictions[i].bottomRight; 165 | const size = [end[0] - start[0], end[1] - start[1]]; 166 | 167 | ctx.rect(start[0], start[1], size[0], size[1]); 168 | } 169 | 170 | window.setTimeout(() => { 171 | if (model.run) { 172 | model.run(model, video, canvas); 173 | } 174 | }, 120); 175 | }); 176 | } 177 | -------------------------------------------------------------------------------- /webrtc-streamer-algo-element.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | class WebRTCStreamerAlgoElement extends HTMLElement { 4 | static get observedAttributes() { 5 | return ['selected']; 6 | } 7 | constructor() { 8 | super(); 9 | this.shadowDOM = this.attachShadow({mode: 'open'}); 10 | this.shadowDOM.innerHTML = ` 11 | 12 |

13 | 21 |

22 | `; 23 | } 24 | connectedCallback() { 25 | this.fillList(); 26 | } 27 | 28 | attributeChangedCallback(attrName, oldVal, newVal) { 29 | if (attrName === "selected") { 30 | this.selected = newVal; 31 | let mediaList = this.shadowDOM.getElementById("algoList"); 32 | for (const option of mediaList.getElementsByTagName('option')) { 33 | if (option.value === newVal) { 34 | option.selected = true; 35 | } 36 | } 37 | } 38 | } 39 | 40 | fillList() { 41 | let algoList = this.shadowDOM.getElementById("algoList"); 42 | algoList.onchange = (event) => { 43 | this.dispatchEvent(new CustomEvent('change', { 44 | detail: { 45 | algo: algoList.selectedOptions[0].value, 46 | } 47 | })); 48 | } 49 | } 50 | } 51 | 52 | customElements.define('webrtc-streamer-algo', WebRTCStreamerAlgoElement); 53 | -------------------------------------------------------------------------------- /webrtc-streamer-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /webrtc-streamer-element.js: -------------------------------------------------------------------------------- 1 | import "./libs/adapter.min.js"; 2 | import "./tensorflow.js"; 3 | import "./webrtcstreamer.js"; 4 | 5 | class WebRTCStreamerElement extends HTMLElement { 6 | static get observedAttributes() { 7 | return ['url', 'options', 'webrtcurl', 'notitle', 'width', 'height', 'algo']; 8 | } 9 | 10 | constructor() { 11 | super(); 12 | this.shadowDOM = this.attachShadow({mode: 'open'}); 13 | this.shadowDOM.innerHTML = ` 14 | 15 |

16 |
17 | 18 | 19 |
20 | `; 21 | this.initialized = false; 22 | this.titleElement = this.shadowDOM.getElementById("title"); 23 | this.videoElement = this.shadowDOM.getElementById("video"); 24 | this.canvasElement = this.shadowDOM.getElementById("canvas"); 25 | this.modelLoaded = {}; 26 | } 27 | connectedCallback() { 28 | this.connectStream(true); 29 | this.initialized = true; 30 | } 31 | disconnectedCallback() { 32 | this.disconnectStream(); 33 | this.initialized = false; 34 | } 35 | attributeChangedCallback(attrName, oldVal, newVal) { 36 | if (attrName === "notitle") { 37 | this.titleElement.style.visibility = "hidden"; 38 | } else if (attrName === "width") { 39 | this.videoElement.style.width = newVal; 40 | } else if (attrName === "height") { 41 | this.videoElement.style.height = newVal; 42 | } if (this.initialized) { 43 | this.connectStream((attrName !== "algo")); 44 | } 45 | } 46 | 47 | disconnectStream() { 48 | if (this.webRtcServer) { 49 | this.webRtcServer.disconnect(); 50 | this.webRtcServer = null; 51 | } 52 | } 53 | 54 | connectStream(reconnect) { 55 | 56 | const webrtcurl = this.getAttribute("webrtcurl"); 57 | 58 | let videostream; 59 | let audiostream; 60 | 61 | const url = this.getAttribute("url"); 62 | if (url) { 63 | try { 64 | let urljson = JSON.parse(url); 65 | videostream = urljson.video; 66 | audiostream = urljson.audio; 67 | } catch (e) { 68 | videostream = url; 69 | } 70 | 71 | const notitle = this.getAttribute("notitle"); 72 | if (notitle === null) { 73 | this.titleElement.innerHTML = videostream; 74 | } 75 | this.videoElement.title = videostream; 76 | 77 | // stop running algo 78 | Object.values(this.modelLoaded).forEach( promise => { 79 | if (promise.model) { 80 | promise.model.run = null; 81 | } 82 | }); 83 | 84 | let imgLoaded; 85 | if (reconnect) { 86 | this.disconnectStream(); 87 | 88 | imgLoaded = new Promise( (resolve,rejet) => { 89 | this.videoElement.addEventListener('loadedmetadata', () => resolve(), { once: true }); 90 | } ); 91 | 92 | this.webRtcServer = new WebRtcStreamer(this.videoElement, webrtcurl); 93 | this.webRtcServer.connect(videostream, audiostream, this.getAttribute("options")); 94 | 95 | } else { 96 | imgLoaded = new Promise( (resolve) => resolve() ); 97 | } 98 | 99 | const algo = this.getAttribute("algo") 100 | let modelLoaded = this.getModelPromise(algo); 101 | 102 | Promise.all([imgLoaded, modelLoaded]).then(([event,model]) => { 103 | this.setVideoSize(this.videoElement.videoWidth, this.videoElement.videoHeight) 104 | 105 | if (model) { 106 | model.run = this.getModelRunFunction(algo); 107 | if (model.run) { 108 | model.run(model, this.videoElement, this.canvasElement) 109 | modelLoaded.model = model; 110 | } 111 | } 112 | }); 113 | } 114 | } 115 | 116 | setVideoSize(width, height) { 117 | this.videoElement.width = width; 118 | this.videoElement.height = height; 119 | 120 | this.canvasElement.width = width; 121 | this.canvasElement.height = height; 122 | } 123 | getModelPromise(algo) { 124 | let modelLoaded; 125 | if (this.modelLoaded[algo]) { 126 | modelLoaded = this.modelLoaded[algo]; 127 | } 128 | else { 129 | if (algo === "posenet") { 130 | modelLoaded = posenet.load(); 131 | } else if (algo === "deeplab") { 132 | modelLoaded = deeplab.load() 133 | } else if (algo === "cocossd") { 134 | modelLoaded = cocoSsd.load(); 135 | } else if (algo === "bodyPix") { 136 | modelLoaded = bodyPix.load(); 137 | } else if (algo === "blazeface") { 138 | modelLoaded = blazeface.load(); 139 | } else { 140 | modelLoaded = new Promise( (resolve) => resolve() ); 141 | } 142 | this.modelLoaded[algo] = modelLoaded; 143 | } 144 | return modelLoaded; 145 | } 146 | 147 | getModelRunFunction(algo) { 148 | let modelFunction; 149 | if (algo === "posenet") { 150 | modelFunction = runPosenet; 151 | } else if (algo === "deeplab") { 152 | modelFunction = runDeeplab; 153 | } else if (algo === "cocossd") { 154 | modelFunction = runDetect; 155 | } else if (algo === "bodyPix") { 156 | modelFunction = runbodyPix; 157 | } else if (algo === "blazeface") { 158 | modelFunction = runblazeface; 159 | } 160 | return modelFunction; 161 | } 162 | } 163 | 164 | customElements.define('webrtc-streamer', WebRTCStreamerElement); 165 | -------------------------------------------------------------------------------- /webrtc-streamer-footer-element.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | class WebRTCStreamerFooterElement extends HTMLElement { 4 | constructor() { 5 | super(); 6 | this.shadowDOM = this.attachShadow({mode: 'open'}); 7 | this.shadowDOM.innerHTML = ` 8 | 9 |
10 | `; 11 | } 12 | 13 | static get observedAttributes() { 14 | return ['webrtcurl']; 15 | } 16 | 17 | attributeChangedCallback(attrName, oldVal, newVal) { 18 | if (attrName === "webrtcurl") { 19 | this.fillFooter(); 20 | } 21 | } 22 | 23 | connectedCallback() { 24 | this.fillFooter(); 25 | } 26 | 27 | fillFooter() { 28 | let footerElement = this.shadowDOM.getElementById("footer"); 29 | const webrtcurl = this.getAttribute("webrtcurl") || ""; 30 | fetch(webrtcurl + "/api/version").then(r => r.text()).then( function (response) { 31 | footerElement.innerHTML = "

WebRTC-Streamer " + response.split(" ")[0] + "

"; 32 | }); 33 | 34 | } 35 | 36 | } 37 | 38 | customElements.define('webrtc-streamer-footer', WebRTCStreamerFooterElement); 39 | -------------------------------------------------------------------------------- /webrtc-streamer-menu-element.js: -------------------------------------------------------------------------------- 1 | 2 | class WebRTCStreamerMenuElement extends HTMLElement { 3 | static get observedAttributes() { 4 | return ['selected', 'webrtcurl']; 5 | } 6 | constructor() { 7 | super(); 8 | this.shadowDOM = this.attachShadow({ mode: 'open' }); 9 | this.shadowDOM.innerHTML = ` 10 | 11 | 12 | `; 13 | } 14 | connectedCallback() { 15 | this.fillList(); 16 | } 17 | 18 | attributeChangedCallback(attrName, oldVal, newVal) { 19 | if (attrName === "selected") { 20 | this.selected = newVal; 21 | let mediaList = this.shadowDOM.getElementById("mediaList"); 22 | let newValjson = JSON.parse(newVal); 23 | for (const option of mediaList.getElementsByTagName('a')) { 24 | let optionjson = JSON.parse(option.value); 25 | if (optionjson.video === newValjson.video) { 26 | option.selected = true; 27 | option.className = "active"; 28 | } 29 | } 30 | 31 | } else if (attrName === "webrtcurl") { 32 | this.fillList(); 33 | } 34 | } 35 | 36 | fillList() { 37 | let mediaList = this.shadowDOM.getElementById("mediaList"); 38 | const webrtcurl = this.getAttribute("webrtcurl") || ""; 39 | fetch(webrtcurl + "/api/getMediaList").then(r => r.json()).then((response) => { 40 | response.forEach((media) => { 41 | var option = document.createElement("a"); 42 | option.text = media.video; 43 | option.value = JSON.stringify(media); 44 | if (this.selected && (this.selected === option.text)) { 45 | option.className = "active"; 46 | } 47 | option.onclick = () => { 48 | if (option.className === "active") { 49 | option.className = ""; 50 | this.dispatchEvent(new CustomEvent('change', { 51 | detail: { url: "" } 52 | })); 53 | } else { 54 | for (const opt of mediaList.getElementsByTagName('a')) { 55 | opt.className = ""; 56 | } 57 | this.dispatchEvent(new CustomEvent('change', { 58 | detail: { url: option.value } 59 | })); 60 | option.className = "active"; 61 | } 62 | } 63 | mediaList.appendChild(option); 64 | }); 65 | this.dispatchEvent(new CustomEvent('init', { 66 | detail: response 67 | })); 68 | 69 | 70 | var settings = document.createElement("a"); 71 | settings.onclick = () => { 72 | if (settings.className === "active") { 73 | settings.className = ""; 74 | this.dispatchEvent(new CustomEvent('settings', { 75 | detail: "off" 76 | })); 77 | } else { 78 | settings.className = "active"; 79 | this.dispatchEvent(new CustomEvent('settings', { 80 | detail: "on" 81 | })); 82 | } 83 | } 84 | var img = document.createElement("img"); 85 | img.src = "webrtc.png" 86 | settings.appendChild(img); 87 | mediaList.appendChild(settings); 88 | 89 | 90 | for (const option of mediaList.getElementsByTagName('a')) { 91 | if (option.className === "active") { 92 | this.dispatchEvent(new CustomEvent('change', { 93 | detail: { url: option.value } 94 | })); 95 | } 96 | } 97 | }); 98 | } 99 | } 100 | 101 | customElements.define('webrtc-streamer-menu', WebRTCStreamerMenuElement); 102 | -------------------------------------------------------------------------------- /webrtc-streamer-options-element.js: -------------------------------------------------------------------------------- 1 | 2 | class WebRTCStreamerOptionsElement extends HTMLElement { 3 | constructor() { 4 | super(); 5 | this.shadowDOM = this.attachShadow({mode: 'open'}); 6 | this.shadowDOM.innerHTML = ` 7 | 8 |

Options

9 |
10 | 11 | `; 12 | this.button = this.shadowDOM.getElementById("apply"); 13 | this.button.onclick = () => this.notifyOptions(); 14 | this.params = new URLSearchParams(); 15 | } 16 | 17 | static get observedAttributes() { 18 | return ['options']; 19 | } 20 | 21 | attributeChangedCallback(attrName, oldVal, newVal) { 22 | if (attrName === "options") { 23 | this.fillOptions(); 24 | } 25 | } 26 | 27 | connectedCallback() { 28 | this.fillOptions(); 29 | } 30 | 31 | fillOptions() { 32 | const options = this.getAttribute("options") || ""; 33 | this.params = new URLSearchParams(options); 34 | 35 | let optElement = this.shadowDOM.getElementById("options"); 36 | optElement.innerHTML = ""; 37 | 38 | for (let [k,v] of this.params.entries()) { 39 | let label = document.createTextNode(k+":"); 40 | optElement.appendChild(label); 41 | let input = document.createElement("input"); 42 | input.type = "text"; 43 | input.value = v; 44 | input.onchange = () => this.params.set(k, input.value); 45 | optElement.appendChild(input); 46 | optElement.appendChild(document.createElement("br")); 47 | } 48 | } 49 | 50 | notifyOptions() 51 | { 52 | this.setAttribute("options", this.params.toString()); 53 | this.dispatchEvent(new CustomEvent('change', { 54 | detail: { 55 | options: this.params.toString(), 56 | } 57 | })); 58 | } 59 | } 60 | 61 | customElements.define('webrtc-streamer-options', WebRTCStreamerOptionsElement); 62 | -------------------------------------------------------------------------------- /webrtc-streamer-selector-element.js: -------------------------------------------------------------------------------- 1 | class WebRTCStreamerSelectorElement extends HTMLElement { 2 | static get observedAttributes() { 3 | return ['selected', 'webrtcurl']; 4 | } 5 | constructor() { 6 | super(); 7 | this.shadowDOM = this.attachShadow({mode: 'open'}); 8 | this.shadowDOM.innerHTML = ` 9 | 10 |

11 | `; 12 | } 13 | connectedCallback() { 14 | this.fillList(); 15 | } 16 | 17 | attributeChangedCallback(attrName, oldVal, newVal) { 18 | if (attrName === "selected") { 19 | this.selected = newVal; 20 | let mediaList = this.shadowDOM.getElementById("mediaList"); 21 | for (const option of mediaList.getElementsByTagName('option')) { 22 | if (option === newVal) { 23 | option.selected = true; 24 | } 25 | } 26 | 27 | } else if (attrName === "webrtcurl") { 28 | this.fillList(); 29 | } 30 | } 31 | 32 | fillList() { 33 | let mediaList = this.shadowDOM.getElementById("mediaList"); 34 | const webrtcurl = this.getAttribute("webrtcurl") || ""; 35 | fetch(webrtcurl + "/api/getMediaList").then(r => r.json()).then( (response) => { 36 | response.forEach( (media) => { 37 | var newOption = document.createElement("option"); 38 | newOption.text = media.video; 39 | newOption.value = JSON.stringify(media); 40 | if (this.selected && (this.selected === newOption.text) ) { 41 | newOption.selected = true; 42 | } 43 | mediaList.appendChild(newOption); 44 | }); 45 | 46 | if (mediaList.selectedOptions.length > 0) { 47 | this.dispatchEvent(new CustomEvent('change', { 48 | detail: { 49 | url: mediaList.selectedOptions[0].value, 50 | } 51 | })); 52 | } 53 | }); 54 | 55 | mediaList.onchange = (event) => { 56 | this.dispatchEvent(new CustomEvent('change', { 57 | detail: { 58 | url: mediaList.selectedOptions[0].value, 59 | } 60 | })); 61 | } 62 | } 63 | } 64 | 65 | customElements.define('webrtc-streamer-selector', WebRTCStreamerSelectorElement); 66 | -------------------------------------------------------------------------------- /webrtc-streamer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 |

12 |
13 |
14 | 15 |
16 | 17 |
18 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /webrtc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpromonet/webrtc-streamer-html/1b0abe51bc73cce50ff1ef9e5232c3a3fe5d6157/webrtc.png -------------------------------------------------------------------------------- /webrtcconfig.js: -------------------------------------------------------------------------------- 1 | var webrtcConfig = { 2 | url: "", 3 | options: "rtptransport=tcp&timeout=60", 4 | layoutextraoptions: "&width=320&height=0", 5 | defaultvideostream: "Bunny" 6 | } 7 | -------------------------------------------------------------------------------- /webrtcstreamer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 42 | 43 | 44 |
45 |
46 |

47 |
48 |
49 | 50 |
51 |
52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /webrtcstreamer.js: -------------------------------------------------------------------------------- 1 | var WebRtcStreamer = (function() { 2 | 3 | /** 4 | * Interface with WebRTC-streamer API 5 | * @constructor 6 | * @param {string} videoElement - id of the video element tag 7 | * @param {string} srvurl - url of webrtc-streamer (default is current location) 8 | */ 9 | var WebRtcStreamer = function WebRtcStreamer (videoElement, srvurl) { 10 | if (typeof videoElement === "string") { 11 | this.videoElement = document.getElementById(videoElement); 12 | } else { 13 | this.videoElement = videoElement; 14 | } 15 | this.srvurl = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port; 16 | this.pc = null; 17 | 18 | this.mediaConstraints = { offerToReceiveAudio: true, offerToReceiveVideo: true }; 19 | 20 | this.iceServers = null; 21 | this.earlyCandidates = []; 22 | } 23 | 24 | WebRtcStreamer.prototype._handleHttpErrors = function (response) { 25 | if (!response.ok) { 26 | throw Error(response.statusText); 27 | } 28 | return response; 29 | } 30 | 31 | /** 32 | * Connect a WebRTC Stream to videoElement 33 | * @param {string} videourl - id of WebRTC video stream 34 | * @param {string} audiourl - id of WebRTC audio stream 35 | * @param {string} options - options of WebRTC call 36 | * @param {string} stream - local stream to send 37 | * @param {string} prefmime - prefered mime 38 | */ 39 | WebRtcStreamer.prototype.connect = function(videourl, audiourl, options, localstream, prefmime) { 40 | this.disconnect(); 41 | 42 | // getIceServers is not already received 43 | if (!this.iceServers) { 44 | console.log("Get IceServers"); 45 | 46 | fetch(this.srvurl + "/api/getIceServers") 47 | .then(this._handleHttpErrors) 48 | .then( (response) => (response.json()) ) 49 | .then( (response) => this.onReceiveGetIceServers(response, videourl, audiourl, options, localstream, prefmime)) 50 | .catch( (error) => this.onError("getIceServers " + error )) 51 | 52 | } else { 53 | this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream, prefmime); 54 | } 55 | } 56 | 57 | /** 58 | * Disconnect a WebRTC Stream and clear videoElement source 59 | */ 60 | WebRtcStreamer.prototype.disconnect = function() { 61 | if (this.videoElement?.srcObject) { 62 | this.videoElement.srcObject.getTracks().forEach(track => { 63 | track.stop() 64 | this.videoElement.srcObject.removeTrack(track); 65 | }); 66 | } 67 | if (this.pc) { 68 | fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid) 69 | .then(this._handleHttpErrors) 70 | .catch( (error) => this.onError("hangup " + error )) 71 | 72 | 73 | try { 74 | this.pc.close(); 75 | } 76 | catch (e) { 77 | console.log ("Failure close peer connection:" + e); 78 | } 79 | this.pc = null; 80 | } 81 | } 82 | 83 | WebRtcStreamer.prototype.filterPreferredCodec = function(sdp, prefmime) { 84 | const lines = sdp.split('\n'); 85 | const [prefkind, prefcodec] = prefmime.toLowerCase().split('/'); 86 | let currentMediaType = null; 87 | let sdpSections = []; 88 | let currentSection = []; 89 | 90 | // Group lines into sections 91 | lines.forEach(line => { 92 | if (line.startsWith('m=')) { 93 | if (currentSection.length) { 94 | sdpSections.push(currentSection); 95 | } 96 | currentSection = [line]; 97 | } else { 98 | currentSection.push(line); 99 | } 100 | }); 101 | sdpSections.push(currentSection); 102 | 103 | // Process each section 104 | const processedSections = sdpSections.map(section => { 105 | const firstLine = section[0]; 106 | if (!firstLine.startsWith('m=' + prefkind)) { 107 | return section.join('\n'); 108 | } 109 | 110 | // Get payload types for preferred codec 111 | const rtpLines = section.filter(line => line.startsWith('a=rtpmap:')); 112 | const preferredPayloads = rtpLines 113 | .filter(line => line.toLowerCase().includes(prefcodec)) 114 | .map(line => line.split(':')[1].split(' ')[0]); 115 | 116 | if (preferredPayloads.length === 0) { 117 | return section.join('\n'); 118 | } 119 | 120 | // Modify m= line to only include preferred payloads 121 | const mLine = firstLine.split(' '); 122 | const newMLine = [...mLine.slice(0,3), ...preferredPayloads].join(' '); 123 | 124 | // Filter related attributes 125 | const filteredLines = section.filter(line => { 126 | if (line === firstLine) return false; 127 | if (line.startsWith('a=rtpmap:')) { 128 | return preferredPayloads.some(payload => line.startsWith(`a=rtpmap:${payload}`)); 129 | } 130 | if (line.startsWith('a=fmtp:') || line.startsWith('a=rtcp-fb:')) { 131 | return preferredPayloads.some(payload => line.startsWith(`a=${line.split(':')[0].split('a=')[1]}:${payload}`)); 132 | } 133 | return true; 134 | }); 135 | 136 | return [newMLine, ...filteredLines].join('\n'); 137 | }); 138 | 139 | return processedSections.join('\n'); 140 | } 141 | 142 | /* 143 | * GetIceServers callback 144 | */ 145 | WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl, audiourl, options, stream, prefmime) { 146 | this.iceServers = iceServers; 147 | this.pcConfig = iceServers || {"iceServers": [] }; 148 | try { 149 | this.createPeerConnection(); 150 | 151 | let callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl); 152 | if (audiourl) { 153 | callurl += "&audiourl="+encodeURIComponent(audiourl); 154 | } 155 | if (options) { 156 | callurl += "&options="+encodeURIComponent(options); 157 | } 158 | 159 | if (stream) { 160 | this.pc.addStream(stream); 161 | } 162 | 163 | // clear early candidates 164 | this.earlyCandidates.length = 0; 165 | 166 | // create Offer 167 | this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => { 168 | console.log("Create offer:" + JSON.stringify(sessionDescription)); 169 | 170 | console.log(`video codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("video")?.codecs?.map(codec => codec.mimeType)))}`) 171 | console.log(`audio codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("audio")?.codecs?.map(codec => codec.mimeType)))}`) 172 | 173 | if (prefmime != undefined) { 174 | //set prefered codec 175 | let [prefkind] = prefmime.split('/'); 176 | if (prefkind != "video" && prefkind != "audio") { 177 | prefkind = "video"; 178 | prefmime = prefkind + "/" + prefmime; 179 | } 180 | console.log("sdp:" + sessionDescription.sdp); 181 | sessionDescription.sdp = this.filterPreferredCodec(sessionDescription.sdp, prefmime); 182 | console.log("sdp:" + sessionDescription.sdp); 183 | } 184 | 185 | 186 | this.pc.setLocalDescription(sessionDescription) 187 | .then(() => { 188 | fetch(callurl, { method: "POST", body: JSON.stringify(sessionDescription) }) 189 | .then(this._handleHttpErrors) 190 | .then( (response) => (response.json()) ) 191 | .catch( (error) => this.onError("call " + error )) 192 | .then( (response) => this.onReceiveCall(response) ) 193 | .catch( (error) => this.onError("call " + error )) 194 | 195 | }, (error) => { 196 | console.log ("setLocalDescription error:" + JSON.stringify(error)); 197 | }); 198 | 199 | }, (error) => { 200 | alert("Create offer error:" + JSON.stringify(error)); 201 | }); 202 | 203 | } catch (e) { 204 | this.disconnect(); 205 | alert("connect error: " + e); 206 | } 207 | } 208 | 209 | 210 | WebRtcStreamer.prototype.getIceCandidate = function() { 211 | fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid) 212 | .then(this._handleHttpErrors) 213 | .then( (response) => (response.json()) ) 214 | .then( (response) => this.onReceiveCandidate(response)) 215 | .catch( (error) => this.onError("getIceCandidate " + error )) 216 | } 217 | 218 | /* 219 | * create RTCPeerConnection 220 | */ 221 | WebRtcStreamer.prototype.createPeerConnection = function() { 222 | console.log("createPeerConnection config: " + JSON.stringify(this.pcConfig)); 223 | this.pc = new RTCPeerConnection(this.pcConfig); 224 | let pc = this.pc; 225 | pc.peerid = Math.random(); 226 | 227 | pc.onicecandidate = (evt) => this.onIceCandidate(evt); 228 | pc.onaddstream = (evt) => this.onAddStream(evt); 229 | pc.oniceconnectionstatechange = (evt) => { 230 | console.log("oniceconnectionstatechange state: " + pc.iceConnectionState); 231 | if (this.videoElement) { 232 | if (pc.iceConnectionState === "connected") { 233 | this.videoElement.style.opacity = "1.0"; 234 | } 235 | else if (pc.iceConnectionState === "disconnected") { 236 | this.videoElement.style.opacity = "0.25"; 237 | } 238 | else if ( (pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed") ) { 239 | this.videoElement.style.opacity = "0.5"; 240 | } else if (pc.iceConnectionState === "new") { 241 | this.getIceCandidate(); 242 | } 243 | } 244 | } 245 | pc.ondatachannel = function(evt) { 246 | console.log("remote datachannel created:"+JSON.stringify(evt)); 247 | 248 | evt.channel.onopen = function () { 249 | console.log("remote datachannel open"); 250 | this.send("remote channel openned"); 251 | } 252 | evt.channel.onmessage = function (event) { 253 | console.log("remote datachannel recv:"+JSON.stringify(event.data)); 254 | } 255 | } 256 | 257 | try { 258 | let dataChannel = pc.createDataChannel("ClientDataChannel"); 259 | dataChannel.onopen = function() { 260 | console.log("local datachannel open"); 261 | this.send("local channel openned"); 262 | } 263 | dataChannel.onmessage = function(evt) { 264 | console.log("local datachannel recv:"+JSON.stringify(evt.data)); 265 | } 266 | } catch (e) { 267 | console.log("Cannor create datachannel error: " + e); 268 | } 269 | 270 | console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig) ); 271 | return pc; 272 | } 273 | 274 | 275 | /* 276 | * RTCPeerConnection IceCandidate callback 277 | */ 278 | WebRtcStreamer.prototype.onIceCandidate = function (event) { 279 | if (event.candidate) { 280 | if (this.pc.currentRemoteDescription) { 281 | this.addIceCandidate(this.pc.peerid, event.candidate); 282 | } else { 283 | this.earlyCandidates.push(event.candidate); 284 | } 285 | } 286 | else { 287 | console.log("End of candidates."); 288 | } 289 | } 290 | 291 | 292 | WebRtcStreamer.prototype.addIceCandidate = function(peerid, candidate) { 293 | fetch(this.srvurl + "/api/addIceCandidate?peerid="+peerid, { method: "POST", body: JSON.stringify(candidate) }) 294 | .then(this._handleHttpErrors) 295 | .then( (response) => (response.json()) ) 296 | .then( (response) => {console.log("addIceCandidate ok:" + response)}) 297 | .catch( (error) => this.onError("addIceCandidate " + error )) 298 | } 299 | 300 | /* 301 | * RTCPeerConnection AddTrack callback 302 | */ 303 | WebRtcStreamer.prototype.onAddStream = function(event) { 304 | console.log("Remote track added:" + JSON.stringify(event)); 305 | 306 | this.videoElement.srcObject = event.stream; 307 | let promise = this.videoElement.play(); 308 | if (promise !== undefined) { 309 | promise.catch((error) => { 310 | console.warn("error:"+error); 311 | this.videoElement.setAttribute("controls", true); 312 | }); 313 | } 314 | } 315 | 316 | /* 317 | * AJAX /call callback 318 | */ 319 | WebRtcStreamer.prototype.onReceiveCall = function(dataJson) { 320 | 321 | console.log("offer: " + JSON.stringify(dataJson)); 322 | let descr = new RTCSessionDescription(dataJson); 323 | this.pc.setRemoteDescription(descr).then(() => { 324 | console.log ("setRemoteDescription ok"); 325 | while (this.earlyCandidates.length) { 326 | let candidate = this.earlyCandidates.shift(); 327 | this.addIceCandidate(this.pc.peerid, candidate); 328 | } 329 | 330 | this.getIceCandidate() 331 | } 332 | , (error) => { 333 | console.log ("setRemoteDescription error:" + JSON.stringify(error)); 334 | }); 335 | } 336 | 337 | /* 338 | * AJAX /getIceCandidate callback 339 | */ 340 | WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) { 341 | console.log("candidate: " + JSON.stringify(dataJson)); 342 | if (dataJson) { 343 | for (let i=0; i { console.log ("addIceCandidate OK"); } 348 | , (error) => { console.log ("addIceCandidate error:" + JSON.stringify(error)); } ); 349 | } 350 | this.pc.addIceCandidate(); 351 | } 352 | } 353 | 354 | 355 | /* 356 | * AJAX callback for Error 357 | */ 358 | WebRtcStreamer.prototype.onError = function(status) { 359 | console.log("onError:" + status); 360 | } 361 | 362 | return WebRtcStreamer; 363 | })(); 364 | 365 | if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { 366 | window.WebRtcStreamer = WebRtcStreamer; 367 | } 368 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { 369 | module.exports = WebRtcStreamer; 370 | } 371 | -------------------------------------------------------------------------------- /whep.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebRTC Streamer using WHEP 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | 23 | 47 | 48 | -------------------------------------------------------------------------------- /xmppconfig.js: -------------------------------------------------------------------------------- 1 | var xmppRoomConfig = { 2 | url: "meet.jit.si", 3 | roomId: "testroom" 4 | } 5 | -------------------------------------------------------------------------------- /xmppvideoroom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | XMPP Url: 17 | Room id: 18 | 19 | 20 | 21 | 22 | 131 | --------------------------------------------------------------------------------