1/** 2 * Class for unification and work with WebRTC commands 3 * @param params 4 * @constructor 5 */ 6 7;(function (window) 8{ 9 if (window.BX.webrtc) return; 10 11 var BX = window.BX; 12 13 // this is constructor, you must inherit 14 BX.webrtc = function () 15 { 16 this.debug = false; 17 this.audioMuted = false; 18 this.videoMuted = false; 19 this.enabled = false; 20 this.detectedBrowser = 'none'; 21 this.pcConfig = {}; 22 this.oneway = false; 23 this.lastUserMediaParams = {}; 24 this.pcConstraints = {}; 25 this.sdpConstraints = {'mandatory': { 'OfferToReceiveAudio':true, 'OfferToReceiveVideo':true }}; 26 this.defaultMicrophone = null; 27 this.defaultCamera = null; 28 this.defaultSpeaker = null; 29 this.enableMicAutoParameters = true; 30 this.useFallbackConstraints = false; 31 32 this.configVideo = { 33 aspectRatio: 1.7777777778 34 }; 35 this.configVideoGroup = this.configVideo; 36 this.configVideoMobile = this.configVideo; 37 this.configVideoAfterError = { }; 38 39 this.callStreamSelf = null; 40 this.callStreamMain = null; 41 this.callStreamUsers = {}; 42 43 this.pc = {}; 44 this.pcStart = {}; 45 this.connected = {}; 46 47 this.iceCandidates = {}; 48 49 this.initiator = false; 50 this.callUserId = 0; 51 this.callChatId = 0; 52 this.callToGroup = false; 53 this.callGroupUsers = []; 54 this.callInit = false; 55 this.callInitUserId = 0; 56 this.callActive = false; 57 this.callVideo = false; 58 this.callRequestUserMedia = {}; 59 this.needPeerConnection = true; 60 61 this.createAnswerTimeout = {}; 62 this.initPeerConnectionTimeout = {}; 63 this.iceCandidateTimeout = {}; 64 this.pcConnectTimeout = {}; 65 66 this.init(); 67 this.setTurnServer(); 68 }; 69 70 // this function needed to execute, after constructor 71 BX.inheritWebrtc = function(child) 72 { 73 child.prototype = Object.create(BX.webrtc.prototype); 74 child.prototype.constructor = child; 75 child.prototype.parent = BX.webrtc.prototype; 76 }; 77 78 BX.webrtc.prototype.init = function() 79 { 80 if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) 81 this.enabled = true; 82 else 83 return; 84
85 if (navigator.userAgent.substr(navigator.userAgent.indexOf('Firefox/')+8, 2) >= 27) 86 this.detectedBrowser = 'firefox'; 87 else if (navigator.appVersion.substr(navigator.appVersion.indexOf('Chrome/')+7, 2) >= 29) 88 this.detectedBrowser = 'chrome'; 89 }; 90 91 BX.webrtc.prototype.attachMediaStream = function(element, stream) 92 { 93 element.srcObject = stream; 94 if(BX.type.isNotEmptyString(this.defaultSpeaker) && element.setSinkId) 95 { 96 this.log('Trying to set output device: ' + this.defaultSpeaker); 97 element.setSinkId(this.defaultSpeaker); 98 } 99 }; 100 101 // this is public function, don't overwrite 102 BX.webrtc.prototype.ready = function () 103 { 104 return this.enabled; 105 }; 106 107 // this is public function, don't overwrite 108 BX.webrtc.prototype.setTurnServer = function (params) 109 { 110 if (!this.ready()) return false; 111 112 params = params || {}; 113 114 this.turnServer = params.turnServer || 'turn.calls.bitrix24.com'; 115 this.turnServerFirefox = params.turnServerFirefox || '54.217.240.163'; 116 this.turnServerLogin = params.turnServerLogin || 'bitrix'; 117 this.turnServerPassword = params.turnServerPassword || 'bitrix'; 118 119 if (this.detectedBrowser == 'firefox') 120 { 121 this.pcConfig = { "iceServers": [ { url:"stun:"+this.turnServerFirefox}, { url:"turn:"+this.turnServerFirefox, credential:this.turnServerPassword, username: this.turnServerLogin} ] }; 122 this.pcConstraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]}; 123 } 124 else if (this.detectedBrowser == 'chrome') 125 { 126 this.pcConfig = { "iceServers": [ { url:"stun:"+this.turnServer}, { url:"turn:"+this.turnServerLogin+"@"+this.turnServer, username: this.turnServerLogin, credential:this.turnServerPassword} ] }; 127 this.pcConstraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]}; 128 } 129 130 return true; 131 }; 132 133 // this is public function, don't overwrite 134 BX.webrtc.prototype.toggleAudio = function(changeVariable) 135 { 136 if (!this.ready()) return false; 137 138 changeVariable = typeof(changeVariable) != 'undefined'? changeVariable: true; 139 if (changeVariable) 140 this.audioMuted = this.audioMuted? false: true; 141 142 if (this.callStreamSelf && this.callStreamSelf.getAudioTracks() && this.callStreamSelf.getAudioTracks().length>0) 143 { 144 for (var i = 0; i < this.callStreamSelf.getAudioTracks().length; i++) 145 { 146 this.callStreamSelf.getAudioTracks()[i].enabled = !this.audioMuted; 147 } 148 } 149 150 return true; 151 }; 152 153 // this is public function, don't overwrite 154 BX.webrtc.prototype.toggleVideo = function(changeVariable) 155 { 156 if (!this.ready()) return false; 157 158 changeVariable = typeof(changeVariable) != 'undefined'? changeVariable: true; 159 if (changeVariable) 160 this.videoMuted = this.videoMuted? false: true; 161 162 if (this.callStreamSelf && this.callStreamSelf.getVideoTracks() && this.callStreamSelf.getVideoTracks().length>0) 163 { 164 for (var i = 0; i < this.callStreamSelf.getVideoTracks().length; i++) 165 { 166 this.callStreamSelf.getVideoTracks()[i].enabled = !this.videoMuted; 167 } 168 } 169 170 return true; 171 }; 172 173 // this is public function, don't overwrite 174 BX.webrtc.prototype.signalingReady = function() 175 { 176 return (this.ready() && BX.PULL && BX.PULL.getPullServerStatus()); 177 }; 178 179 // this is public function, don't overwrite 180 BX.webrtc.prototype.log = function() 181 { 182 var text = ''; 183 if (BX.desktop && BX.desktop.ready()) 184 { 185 for (var i = 0; i < arguments.length; i++) 186 { 187 try 188 { 189 text = text+' | '+(typeof(arguments[i]) == 'object'? JSON.stringify(arguments[i]): arguments[i]); 190 } 191 catch (e) 192 { 193 text = text+' | (circular structure)'; 194 } 195 196 } 197 BX.desktop.log(BX.message('USER_ID')+'.video.log', text.substr(3)); 198 } 199 if (this.debug) 200 { 201 if (console) console.log('WebRTC Log', arguments); 202 } 203 }; 204 205 /* UserMedia API */ 206 207 // this is public function, you can inherit 208 BX.webrtc.prototype.startGetUserMedia = function(video, audio) 209 { 210 if (this.oneway && !this.initiator) 211 { 212 this.onUserMediaSuccess(); 213 return true; 214 } 215 216 this.lastUserMediaParams = {video: video, audio: audio}; 217 218 if (this.callRequestUserMedia[this.callVideo? 'video': 'audio']) 219 return false; 220 221 if(typeof(video) != 'undefined' && video !== true) 222 { 223 //nop 224 } 225 else 226 { 227 if(this.useFallbackConstraints) 228 video = this.configVideoAfterError; 229 else if(this.callToGroup) 230 video = this.configVideoGroup; 231 else 232 video = this.configVideo; 233 } 234 235 if(video && this.defaultCamera) 236 { 237 video.deviceId = {ideal: this.defaultCamera}; 238 } 239 240 audio = typeof(audio) != 'undefined' && audio !== true? audio: {}; 241 if(audio && this.defaultMicrophone) 242 { 243 audio.deviceId = {ideal: this.defaultMicrophone}; 244 } 245 246 /* for unknown reason, these constraints do not work in chrome, when paired with video resolution constraints */ 247 if(false && this.enableMicAutoParameters === false) 248 { 249 audio.optional = [ 250 {echoCancellation:false}, 251 {googEchoCancellation:false}, 252 {googEchoCancellation2:false}, 253 {googDAEchoCancellation:false}, 254 {googAutoGainControl: false}, 255 {googAutoGainControl2: false}, 256 {mozAutoGainControl: false}, 257 {googNoiseSuppression: false}, 258 {googNoiseSuppression2: false}, 259 {googHighpassFilter: false}, 260 {googTypingNoiseDetection: false}, 261 {googAudioMirroring: false} 262 ]; 263 } 264 265 var constraints = { 266 "audio": audio, 267 "video": video 268 }; 269 270 this.log("Requested access to local media with constraints: \"" + JSON.stringify(constraints) + "\""); 271 272 try { 273 this.callRequestUserMedia[this.callVideo? 'video': 'audio'] = true; 274 navigator.mediaDevices.getUserMedia(constraints).then(BX.delegate(this.onUserMediaSuccess, this), BX.delegate(this.onUserMediaError, this)); 275 } catch (e) { 276 this.debug = true; 277 this.log("Method getUserMedia failed with exception: " + e.message); 278 } 279 280 return true; 281 } 282 283 // this is protected function, you must inherit 284 BX.webrtc.prototype.onUserMediaSuccess = function(stream) 285 { 286 this.log('Media stream received'); 287 if(stream && stream.getTracks) 288 {
289 stream.getTracks().forEach(function(track) 290 { 291 this.log(BX.webrtc.mediaStreamTrackToString(track)); 292 }.bind(this)); 293 } 294 295 if (!this.oneway || this.initiator) 296 { 297 this.callRequestUserMedia[this.callVideo? 'video': 'audio'] = false; 298 if (this.callStreamSelf) 299 return false; 300 301 if (!this.callActive && stream) 302 { 303 BX.webrtc.stopMediaStream(stream); 304 return false; 305 } 306 307 this.log("User has granted access to local media."); 308 309 this.callStreamSelf = stream; 310 this.toggleAudio(false); 311 } 312 313 if (!this.needPeerConnection) 314 return true; 315 316 if (this.initiator) 317 { 318 if (this.callToGroup) 319 { 320 for (var i = 0; i < this.callGroupUsers.length; i++) 321 { 322 var userId = this.callGroupUsers[i]; 323 if (userId != BX.message('USER_ID')) 324 { 325 clearTimeout(this.initPeerConnectionTimeout[userId]); 326 this.initPeerConnection(userId); 327 } 328 } 329 } 330 else 331 { 332 clearTimeout(this.initPeerConnectionTimeout[this.callUserId]); 333 this.initPeerConnection(this.callUserId); 334 } 335 } 336 else 337 { 338 if (this.callToGroup) 339 { 340 for (var i = 0; i < this.callGroupUsers.length; i++) 341 { 342 var userId = this.callGroupUsers[i]; 343 if (userId != BX.message('USER_ID') && userId != this.callInitUserId && !this.callStreamUsers[userId]) 344 { 345 clearTimeout(this.initPeerConnectionTimeout[userId]); 346 this.initPeerConnection(userId, true); 347 } 348 } 349 } 350 } 351 return true; 352 } 353 354 // this is protected function, you can inherit 355 BX.webrtc.prototype.onUserMediaError = function(error) 356 { 357 this.debug = true; 358 this.callRequestUserMedia[this.callVideo? 'video': 'audio'] = false; 359 if (!this.callActive) 360 return false; 361 362 this.log("Failed to get access to local media. Error code was " + JSON.stringify(error)); 363 364 return true; 365 } 366 367 /* PeerConnection API */ 368 369 // this is public function, don't overwrite or inherit 370 BX.webrtc.prototype.initPeerConnection = function(userId, initiator) 371 { 372 if (!this.callActive) 373 return false; 374 375 initiator = initiator === true; 376 if (this.debug) console.log(userId, 'wait initPeerConnection') 377 if (!this.pcStart[userId] && (!this.oneway && this.callStreamSelf || this.oneway && this.initiator && this.callStreamSelf || this.oneway && !this.initiator)) 378 { 379 if (this.connected[userId]) 380 { 381 this.log("Creating PeerConnection.", userId); 382 this.createPeerConnection(userId); 383 if (!this.pc[userId]) 384 { 385 clearTimeout(this.initPeerConnectionTimeout[userId]); 386 this.initPeerConnectionTimeout[userId] = setTimeout(BX.delegate(function(){ 387 this.initPeerConnection(userId, initiator); 388 }, this), 2000); 389 return false; 390 } 391 392 this.log("Adding local stream.", userId, this.pc[userId]); 393 394 this.pcStart[userId] = true; 395 396 if (this.initiator || initiator) 397 this.sendOfferToPeer(userId); 398 } 399 else 400 { 401 clearTimeout(this.initPeerConnectionTimeout[userId]); 402 this.initPeerConnectionTimeout[userId] = setTimeout(BX.delegate(function(){ 403 this.initPeerConnection(userId, initiator); 404 }, this), 2000); 405 } 406 } 407 else if (!this.connected[userId]) 408 { 409 clearTimeout(this.initPeerConnectionTimeout[userId]); 410 this.initPeerConnectionTimeout[userId] = setTimeout(BX.delegate(function(){ 411 this.initPeerConnection(userId, initiator); 412 }, this), 2000); 413 } 414 415 return true; 416 } 417 418 // this protected function, you must inherit 419 BX.webrtc.prototype.setLocalAndSend = function(userId, desc) 420 { 421 if (this.pc[userId] == null || !this.callActive) 422 return false; 423 424 this.log('set local description and send', userId, JSON.stringify(desc)); 425 this.pc[userId].setLocalDescription(desc, BX.delegate(function(a){this.log('setLocalDescription', a)}, this), BX.delegate(function(a){this.log('setLocalDescription', a)}, this)); 426 427 // need to send local description to "userId" peer 428 429 return true; 430 } 431 432 // this is blank function, you must overwrite 433 BX.webrtc.prototype.onRemoteStreamAdded = function(userId, event, setMainVideo) 434 { 435 } 436 437 // this is blank function, you can overwrite 438 BX.webrtc.prototype.onRemoteStreamRemoved = function(userId, event) 439 { 440 } 441
442 // this is blank function, you must overwrite 443 BX.webrtc.prototype.onIceCandidate = function (userId, candidates) 444 { 445 // need to send IceCandidate to "userId" peer 446 } 447 448 // this is blank function, you can overwrite 449 BX.webrtc.prototype.onIceConnectionStateChange = function(userId, event) 450 { 451 } 452 453 // this is blank function, you can overwrite 454 BX.webrtc.prototype.onSignalingStateChange = function(userId, event) 455 { 456 } 457 458 // this is blank function, you can overwrite 459 BX.webrtc.prototype.peerConnectionError = function (userId, event) 460 { 461 } 462 463 // this is public function, you must inherit 464 BX.webrtc.prototype.peerConnectionReconnect = function (userId) 465 { 466 if (!this.pc[userId]) 467 return false; 468 469 if ((this.pc[userId].iceConnectionState == 'connected' || this.pc[userId].iceConnectionState == 'completed') && this.pc[userId].signalingState == 'stable') 470 return false; 471 472 this.log('peerConnectionReconnect', this.pc[userId].iceConnectionState, this.pc[userId].signalingState) 473 this.pc[userId].close(); 474 475 delete this.pc[userId]; 476 delete this.pcStart[userId]; 477 478 if (this.callStreamMain == this.callStreamUsers[userId]) 479 this.callStreamMain = null; 480 this.callStreamUsers[userId] = null; 481 482 clearTimeout(this.initPeerConnectionTimeout[userId]); 483 484 // need to send reconnect command to "userId" peer in your class 485 return true; 486 } 487 488 // this is public function, you can inherit 489 BX.webrtc.prototype.deleteEvents = function() 490 { 491 this.initiator = false; 492 this.callUserId = 0; 493 this.callChatId = 0; 494 this.callToGroup = false; 495 this.callGroupUsers = []; 496 this.callInit = false; 497 this.callInitUserId = 0; 498 this.callActive = false; 499 this.callVideo = false; 500 this.callRequestUserMedia = {}; 501 502 this.audioMuted = false; 503 this.videoMuted = false; 504 this.needPeerConnection = true; 505 506 var i = 0; 507 for (i in this.pc) 508 { 509 if(this.pc.hasOwnProperty(i)) 510 { 511 this.pc[i].close(); 512 delete this.pc[i]; 513 } 514 } 515 516 this.pc = {}; 517 this.pcStart = {}; 518 this.connected = {}; 519 this.iceCandidates = {}; 520 521 for (i in this.iceCandidateTimeout) 522 { 523 if (this.iceCandidateTimeout.hasOwnProperty(i)) 524 { 525 clearTimeout(this.iceCandidateTimeout[i]); 526 } 527 } 528 this.iceCandidateTimeout = {}; 529 530 for (i in this.pcConnectTimeout) 531 { 532 if (this.pcConnectTimeout.hasOwnProperty(i)) 533 { 534 clearTimeout(this.pcConnectTimeout[i]); 535 } 536 } 537 this.pcConnectTimeout = {}; 538 539 for (i in this.createAnswerTimeout) 540 { 541 if (this.createAnswerTimeout.hasOwnProperty(i)) 542 { 543 clearTimeout(this.createAnswerTimeout[i]); 544 } 545 } 546 this.createAnswerTimeout = {}; 547 548 for (i in this.initPeerConnectionTimeout) 549 { 550 if (this.initPeerConnectionTimeout.hasOwnProperty(i)) 551 { 552 clearTimeout(this.initPeerConnectionTimeout[i]); 553 } 554 } 555 this.initPeerConnectionTimeout = {}; 556 557 if (this.callStreamSelf) 558 { 559 BX.webrtc.stopMediaStream(this.callStreamSelf); 560 this.callStreamSelf = null; 561 } 562 if (this.callStreamMain) 563 { 564 BX.webrtc.stopMediaStream(this.callStreamMain); 565 this.callStreamMain = null; 566 } 567 for (i in this.callStreamUsers) 568 { 569 if (this.callStreamUsers.hasOwnProperty(i)) 570 { 571 if (this.callStreamUsers[i]) 572 BX.webrtc.stopMediaStream(this.callStreamUsers[i]); 573 delete this.callStreamUsers[i]; 574 } 575 } 576 } 577 578 /* Private function */ 579 580 // this is private function, don't use or overwrite or inherit 581 BX.webrtc.prototype.mergeConstraints = function (cons1, cons2) 582 { 583 if (!this.ready()) return false; 584 585 var merged = cons1; 586 for (var name in cons2.mandatory) 587 { 588 if (cons2.mandatory.hasOwnProperty(name)) 589 { 590 merged.mandatory[name] = cons2.mandatory[name]; 591 } 592 } 593 merged.optional.concat(cons2.optional); 594 return merged; 595 }; 596 597 // this is private function, don't use or overwrite or inherit 598 BX.webrtc.prototype.sendOfferToPeer = function(userId) 599 { 600 if (!this.pc[userId]) 601 return false; 602 603 var constraints = {"optional": [], "mandatory": {"MozDontOfferDataChannel": true}}; 604 605 if (this.detectedBrowser === "chrome") 606 { 607 for (var prop in constraints.mandatory) 608 { 609 if (constraints.mandatory.hasOwnProperty(prop)) 610 { 611 if (prop.indexOf("Moz") != -1) 612 delete constraints.mandatory[prop]; 613 } 614 } 615 } 616 this.log('Constraints', constraints); 617 constraints = this.mergeConstraints(constraints, this.sdpConstraints); 618 619 this.log("Sending offer to peer, with constraints:", userId, "\"" + JSON.stringify(constraints) + "\"."); 620 this.pc[userId].createOffer(BX.delegate(function(desc){this.setLocalAndSend(userId, desc)}, this), BX.delegate(function(a){this.log('createOffer failure', a)}, this), constraints); 621 622 return true; 623 } 624 625 // this is private function, don't use or overwrite or inherit 626 BX.webrtc.prototype.createPeerConnection = function(userId) 627 { 628 try 629 { 630 this.pc[userId] = new RTCPeerConnection(this.pcConfig, this.pcConstraints); 631 this.pc[userId].onicecandidate = BX.delegate(function(event) { this.onIceCandidateEvent(userId, event) }, this); 632 this.pc[userId].onaddstream = BX.delegate(function(event) { this.onRemoteStreamAddedEvent(userId, event) }, this); 633 this.pc[userId].onremovestream = BX.delegate(function(event) { this.onRemoteStreamRemovedEvent(userId, event)}, this); 634 this.pc[userId].oniceconnectionstatechange = BX.delegate(function(event) { this.onIceConnectionStateChangeEvent(userId, event)}, this); 635 this.pc[userId].onsignalingstatechange = BX.delegate(function(event) { this.onSignalingStateChangeEvent(userId, event)}, this); 636 if (!this.oneway || this.initiator) 637 { 638 this.pc[userId].addStream(this.callStreamSelf); 639 } 640 641 this.log("Created RTCPeerConnnection for "+userId+" with:\n" + 642 " config: \"" + JSON.stringify(this.pcConfig) + "\";\n" + 643 " constraints: \"" + JSON.stringify(this.pcConstraints) + "\"."); 644 } 645 catch (e) 646 { 647 if (this.callToGroup && this.callStreamUsers[userId]) 648 return false;
649 650 this.log('PeerConnection: ', JSON.stringify(this.pcConfig), JSON.stringify(this.pcConstraints)); 651 this.log("Failed to create PeerConnection, exception: " + e.message); 652 653 this.peerConnectionError(userId, e); 654 } 655 return true; 656 } 657 658 // this is private function, don't use or overwrite or inherit 659 BX.webrtc.prototype.signalingPeerData = function(userId, peerData) 660 { 661 var signal = JSON.parse(peerData); 662 if (signal.type === 'offer') 663 { 664 if (!this.pcStart[userId]) 665 this.initPeerConnection(userId); 666 667 this.createAnswer(userId, signal); 668 } 669 else if (signal.type === 'answer' && this.pcStart[userId]) 670 { 671 if (this.pc[userId] == null) 672 return false; 673 674 this.pc[userId].setRemoteDescription(new RTCSessionDescription(signal), function(){}, function(){}); 675 } 676 else if (signal.type === 'candidate' && this.pcStart[userId]) 677 { 678 if (!this.pc[userId] || this.pc[userId] == null) 679 return false; 680 681 for (var i = 0; i < signal.candidates.length; i++) 682 { 683 var candidate = new RTCIceCandidate({sdpMLineIndex:signal.candidates[i].label, candidate:signal.candidates[i].candidate}); 684 try{ 685 this.pc[userId].addIceCandidate(candidate); 686 } 687 catch(e) 688 { 689 this.log('Error addIceCandidate', JSON.stringify(e)); 690 this.peerConnectionReconnect(userId); 691 return false; 692 } 693 } 694 } 695 else 696 { 697 this.log('Signaling command "'+(signal && signal.type? signal.type: 'undefined')+'" from user "'+userId+'" skip'); 698 } 699 700 return true; 701 } 702 703 // this is private function, don't use or overwrite or inherit 704 BX.webrtc.prototype.onRemoteStreamAddedEvent = function (userId, event) 705 { 706 if (!this.pc[userId]) return false; 707 708 this.log('Remote stream added', userId, JSON.stringify(event)); 709 710 var setMainVideo = false; 711 if (!this.callStreamMain) 712 { 713 this.callStreamMain = event.stream; 714 setMainVideo = true; 715 } 716 this.callStreamUsers[userId] = event.stream; 717 this.onRemoteStreamAdded(userId, event, setMainVideo) 718 } 719 720 // this is private function, don't use or overwrite or inherit 721 BX.webrtc.prototype.onRemoteStreamRemovedEvent = function(userId, event) 722 { 723 if (!this.pc[userId]) return false; 724 725 this.callStreamUsers[userId] = null; 726 this.onRemoteStreamRemoved(userId, event); 727 } 728 729 // this is private function, don't use or overwrite or inherit 730 BX.webrtc.prototype.onIceCandidateEvent = function (userId, event) 731 { 732 if (!this.pc[userId]) return false; 733 734 if (!this.iceCandidates[userId]) 735 this.iceCandidates[userId] = []; 736 737 if (event.candidate) 738 { 739 this.log("New ICE candidate: ", event.candidate); 740 this.iceCandidates[userId].push({type: 'candidate', label: event.candidate.sdpMLineIndex, id: event.candidate.sdpMid, candidate: event.candidate.candidate}); 741 742 clearTimeout(this.iceCandidateTimeout[userId]); 743 this.iceCandidateTimeout[userId] = setTimeout(BX.delegate(function(){ 744 if (this.iceCandidates[userId].length === 0) 745 return false; 746 747 this.onIceCandidate(userId, {'type': 'candidate', 'candidates': this.iceCandidates[userId]}); 748 this.iceCandidates[userId] = []; 749 }, this), 250); 750 } 751 else 752 { 753 this.log("End of candidates of "+userId); 754 } 755 } 756 757 // this is private function, don't use or overwrite or inherit 758 BX.webrtc.prototype.onIceConnectionStateChangeEvent = function(userId, event) 759 { 760 clearTimeout(this.pcConnectTimeout[userId]); 761 if (!this.pc[userId] || this.pc[userId].iceConnectionState == "close") 762 return false; 763 764 this.log('iceConnectionStateChange', userId, this.pc[userId].iceConnectionState, this.pc[userId].signalingState); 765 this.pcConnectTimeout[userId] = setTimeout(BX.delegate(function(){ 766 this.peerConnectionReconnect(userId); 767 }, this), 15000); 768 769 this.onIceConnectionStateChange(userId, event); 770 771 return true; 772 } 773 774 // this is private function, don't use or overwrite or inherit 775 BX.webrtc.prototype.onSignalingStateChangeEvent = function(userId, event) 776 { 777 clearTimeout(this.pcConnectTimeout[userId]); 778 if (!this.pc[userId] || this.pc[userId].signalingState == "close") 779 return false; 780 781 this.log('signalingStateChange', userId, this.pc[userId].iceConnectionState, this.pc[userId].signalingState); 782 this.pcConnectTimeout[userId] = setTimeout(BX.delegate(function(){ 783 this.peerConnectionReconnect(userId); 784 }, this), 15000); 785 786 this.onSignalingStateChange(userId, event) 787 } 788 789 // this is private function, don't use or overwrite or inherit 790 BX.webrtc.prototype.createAnswer = function(userId, signal) 791 { 792 if (!this.callActive) 793 return false;
794 795 if (!this.pcStart[userId]) 796 { 797 clearTimeout(this.createAnswerTimeout[userId]); 798 this.createAnswerTimeout[userId] = setTimeout(BX.delegate(function(){ 799 this.createAnswer(userId, signal); 800 }, this), 2000); 801 802 return false; 803 } 804 this.pc[userId].setRemoteDescription(new RTCSessionDescription(signal), BX.delegate(function() 805 { 806 if (!this.pc[userId]) return false; 807 this.pc[userId].createAnswer(BX.delegate(function(desc){this.setLocalAndSend(userId, desc)}, this), BX.delegate(function(a){this.log('createAnswer failure', a)}, this), this.sdpConstraints); 808 }, this), function(){}); 809 810 return true; 811 } 812 813 BX.webrtc.prototype.setDefaultCamera = function(defaultCamera) 814 { 815 this.defaultCamera = defaultCamera; 816 }; 817 818 BX.webrtc.prototype.setDefaultMicrophone = function(defaultMicrophone) 819 { 820 this.defaultMicrophone = defaultMicrophone; 821 }; 822 823 BX.webrtc.prototype.logDevices = function() 824 { 825 var self = this; 826 if(navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) 827 { 828 self.log('Enumerating media devices'); 829 navigator.mediaDevices.enumerateDevices().then(function(devices) 830 { 831 devices.forEach(function(device) 832 { 833 try 834 { 835 self.log(JSON.stringify(device)); 836 } 837 catch (e) 838 { 839 self.log(device); 840 } 841 }); 842 }) 843 } 844 else 845 { 846 self.log('Could not enumerate devices, api is not supported'); 847 } 848 }; 849 850 BX.webrtc.mediaStreamTrackToString = function(track) 851 { 852 var result = ''; 853 for(key in track) 854 { 855 if(!BX.type.isFunction(track[key])) 856 { 857 result = result + ' ' + key + ': ' + track[key] + ';'; 858 } 859 } 860 return result; 861 }; 862 863 BX.webrtc.stopMediaStream = function(mediaStream) 864 { 865 if(!(mediaStream instanceof MediaStream)) 866 return; 867 868 if (typeof mediaStream.getTracks === 'undefined') 869 { 870 // Support for legacy browsers 871 mediaStream.stop(); 872 } 873 else 874 { 875 mediaStream.getTracks().forEach(function(track) 876 { 877 track.stop(); 878 }); 879 } 880 }; 881 882})(window);
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.