1/* 2 * This code and all components (c) Copyright 2019-2020, Wowza Media Systems, LLC. All rights reserved. 3 * This code is licensed pursuant to the BSD 3-Clause License. 4 */ 5 6let browserDetails = window.adapter.browserDetails; 7 8// munge plug-in 9let mungeSDP = undefined; 10 11// callbacks 12let onconnectionstatechange = undefined; 13let onstop = undefined; 14let onerror = undefined; 15 16// local state 17let localStream = undefined; 18let streamInfo = undefined; 19let mediaInfo = { 20 videoBitrate: "", 21 audioBitrate: "", 22 videoFrameRate: "30", 23 videoCodec: "42e01f", 24 audioCodec: "opus" 25} 26let userData = undefined; 27let statsInterval = undefined; 28 29let wsConnection = undefined; 30let peerConnection = undefined; 31let peerConnectionConfig = { 'iceServers': [] }; 32peerConnectionConfig = null; 33let videoSender = undefined; 34let audioSender = undefined; 35 36 37function gotIceCandidate(event) { 38 if (event.candidate != null) { 39 console.log('WowzaPeerConnectionPublish.gotIceCandidate: ' + JSON.stringify({ 'ice': event.candidate })); 40 } 41} 42 43function gotDescription(description) { 44 console.log("WowzaPeerConnectionPublish.gotDescription: SDP:"); 45 console.log(description.sdp+''); 46 47 let mungeData = new Object(); 48 49 if (mediaInfo.audioBitrate != null) 50 mungeData.audioBitrate = mediaInfo.audioBitrate; 51 if (mediaInfo.videoBitrate != null) 52 mungeData.videoBitrate = mediaInfo.videoBitrate; 53 if (mediaInfo.videoFrameRate != null) 54 mungeData.videoFrameRate = mediaInfo.videoFrameRate; 55 if (mediaInfo.videoCodec != null) 56 mungeData.videoCodec = mediaInfo.videoCodec; 57 if (mediaInfo.audioCodec != null) 58 mungeData.audioCodec = mediaInfo.audioCodec; 59 60 if (mungeSDP != null) 61 { 62 description.sdp = mungeSDP(description.sdp, mungeData); 63 } 64 65 console.log("WowzaPeerConnectionPublish.gotDescription: Setting local description SDP: "); 66 console.log(description.sdp); 67 68 69 peerConnection 70 .setLocalDescription(description) 71 .then(() => wsConnection.send('{"direction":"publish", "command":"sendOffer", "streamInfo":' + JSON.stringify(streamInfo) + ', "sdp":' + JSON.stringify(description) + ', "userData":' + JSON.stringify(userData) + '}')) 72 .catch((error)=>{ 73 let newError = {message:"Peer connection failed",...error}; 74 errorHandler(newError); 75 }); 76} 77 78function createOnStats(onStats) { 79 return () => { 80 if (peerConnection != null) 81 { 82 peerConnection.getStats(null) 83 .then(onStats, err => console.log(err)); 84 } 85 } 86} 87 88function wsConnect(url) { 89 try 90 { 91 wsConnection = new WebSocket(url); 92 } 93 catch(e) 94 { 95 errorHandler(e); 96 return; 97 } 98 99 wsConnection.binaryType = 'arraybuffer'; 100 101 wsConnection.onopen = function () { 102 console.log("WowzaPeerConnectionPublish.wsConnection.onopen"); 103 104 peerConnection = new RTCPeerConnection(peerConnectionConfig); 105 videoSender = undefined;
106 audioSender = undefined; 107 108 peerConnection.onicecandidate = (event) => { 109 if (event.candidate != null) { 110 console.log('WowzaPeerConnectionPublish.gotIceCandidate: ' + JSON.stringify({ 'ice': event.candidate })); 111 } 112 } 113 114 peerConnection.onconnectionstatechange = (event) => { 115 if (onconnectionstatechange != null) 116 { 117 onconnectionstatechange(event); 118 } 119 } 120 121 peerConnection.onnegotiationneeded = (event) => { 122 peerConnection.createOffer(gotDescription, errorHandler); 123 } 124 125 let localTracks = localStream.getTracks(); 126 for (let localTrack in localTracks) { 127 let sender = peerConnection.addTrack(localTracks[localTrack], localStream); 128 if (localTracks[localTrack].type === 'audio') 129 { 130 audioSender = sender; 131 } 132 else if (localTracks[localTrack].type === 'video') 133 { 134 videoSender = sender; 135 } 136 } 137 } 138 139 wsConnection.onmessage = function (evt) { 140 console.log("WowzaPeerConnectionPublish.wsConnection.onmessage: " + evt.data); 141 142 var msgJSON = JSON.parse(evt.data); 143 144 var msgStatus = Number(msgJSON['status']); 145 var msgCommand = msgJSON['command']; 146 147 if (msgStatus != 200) { 148 stop(); 149 errorHandler({message:msgJSON['statusDescription']}); 150 } 151 else { 152 var sdpData = msgJSON['sdp']; 153 if (sdpData !== undefined) { 154 155 var mungeData = new Object(); 156 157 if (mediaInfo.audioBitrate !== undefined) 158 mungeData.audioBitrate = mediaInfo.audioBitrate; 159 if (mediaInfo.videoBitrate !== undefined) 160 mungeData.videoBitrate = mediaInfo.videoBitrate; 161 162 console.log("WowzaPeerConnectionPublish.wsConnection.onmessage: Setting remote description SDP:"); 163 console.log(sdpData.sdp); 164 165 peerConnection 166 .setRemoteDescription(new RTCSessionDescription(sdpData), 167 () => { }, 168 errorHandler 169 ); 170 } 171 172 var iceCandidates = msgJSON['iceCandidates']; 173 if (iceCandidates !== undefined) { 174 for (var index in iceCandidates) { 175 console.log('WowzaPeerConnectionPublish.wsConnection.iceCandidates: ' + iceCandidates[index]); 176 peerConnection.addIceCandidate(new RTCIceCandidate(iceCandidates[index])); 177 } 178 } 179 } 180 } 181 182 wsConnection.onclose = function () { 183 console.log("WowzaPeerConnectionPublish.wsConnection.onclose"); 184 } 185 186 wsConnection.onerror = function (error) { 187 console.log('wsConnection.onerror'); 188 console.log(error); 189 let message = "Websocket connection failed: " + url; 190 console.log(message); 191 let newError = {message:message,...error}; 192 stop(); 193 errorHandler(newError); 194 } 195} 196 197function replaceTrack (type, newTrack) 198{ 199 if (peerConnection != null) 200 { 201 console.log(peerConnection); 202 if (type === 'audio') 203 { 204 if (audioSender != null) 205 { 206 audioSender.replaceTrack(newTrack); 207 } 208 else 209 { 210 audioSender = peerConnection.addTrack(newTrack); 211 } 212 } 213 else if (type === 'video') 214 { 215 if (videoSender != null) 216 { 217 videoSender.replaceTrack(newTrack); 218 } 219 else 220 { 221 videoSender = peerConnection.addTrack(newTrack); 222 } 223 } 224 } 225} 226 227// startProps: 228// wsURL: string 229// localStream: MediaStream 230// streamInfo: { applicationName, streamName } 231// mediaInfo: { videoBitrate, audioBitrate, videoFrameRate, videoCodec, audioCodec } 232// userData: any 233// mungeSDP: function (sdpStr, mungeData) 234// onconnectionstatechange: function 235// onerror: function 236 237function start (props) 238{ 239 let wsURL = props.wsURL; 240 localStream = props.localStream; 241 if (props.streamInfo != null) 242 streamInfo = props.streamInfo; 243 if (props.mediaInfo != null) 244 mediaInfo = props.mediaInfo; 245 if (props.userData != null) 246 userData = props.userData; 247 248 if (props.mungeSDP != null) 249 mungeSDP = props.mungeSDP; 250 251 if (props.onconnectionstatechange != null) 252 onconnectionstatechange = props.onconnectionstatechange; 253 if (props.onstop != null) 254 onstop = props.onstop; 255 if (props.onerror != null) 256 onerror = props.onerror; 257 258 if (props.onstats != null) 259 { 260 statsInterval = setInterval(createOnStats(props.onstats),5000); 261 } 262 263 if (peerConnection == null) 264 { 265 if (wsConnection != null) 266 wsConnection.close(); 267 wsConnection = null; 268 269 console.log("WowzaPeerConnectionPublish.start: wsURL:" + wsURL + " streamInfo:" + JSON.stringify(streamInfo)); 270 wsConnect(wsURL); 271 } 272 else 273 { 274 console.log('WowzaPeerConnectionPublish.start: peerConnection already in use, not starting'); 275 } 276} 277 278function stop () 279{ 280 if (peerConnection != null) 281 peerConnection.close(); 282 peerConnection = undefined; 283 videoSender = undefined;
284 audioSender = undefined; 285 286 if (wsConnection != null) 287 wsConnection.close(); 288 wsConnection = undefined; 289 290 if (statsInterval != null) 291 { 292 clearInterval(statsInterval); 293 statsInterval = undefined; 294 } 295 if (onstop != null) 296 { 297 onstop(); 298 } 299} 300 301function isStarted() 302{ 303 return (peerConnection != null); 304} 305 306function errorHandler(error) { 307 console.trace(); 308 if (onerror != null) 309 { 310 onerror(error); 311 } 312} 313 314export default { 315 connect: wsConnect, 316 start: start, 317 stop: stop, 318 isStarted: isStarted, 319 replaceTrack: replaceTrack 320};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.