diff --git a/coffee/gum.coffee b/coffee/gum.coffee
index 7e8f64f..d2f230d 100644
--- a/coffee/gum.coffee
+++ b/coffee/gum.coffee
@@ -5,7 +5,9 @@ palava = @palava
class palava.Gum extends @EventEmitter
constructor: (config) ->
@config = config || { video: true, audio: true }
- @stream = null
+ @stream = null # this stream switches from localStream to displayStream on request
+ @localStream = null
+ @displayStream = null
changeConfig: (config) =>
@config = config
@@ -17,6 +19,7 @@ class palava.Gum extends @EventEmitter
@config
).then(
(stream) =>
+ @localStream = stream.clone()
@stream = stream
@emit 'stream_ready', stream
).catch(
@@ -24,9 +27,36 @@ class palava.Gum extends @EventEmitter
@emit 'stream_error', error
)
+ requestDisplaySharing: =>
+ navigator.mediaDevices.getDisplayMedia(
+ {video:true}
+ ).then(
+ (stream) =>
+ # add audio track to the display stream (if any)
+ if @localStream.getAudioTracks().length > 0
+ stream.addTrack(@localStream.getAudioTracks()[0], @localStream)
+ @displayStream = stream.clone()
+ @stream = stream
+ @emit 'display_stream_ready', stream
+ ).catch(
+ (error) =>
+ @emit 'display_stream_error', error
+ )
+
+ stopDisplaySharing: =>
+ @stream = @localStream
+ @emit 'display_stream_stop', @displayStream
+ @displayStream = null
+
getStream: =>
@stream
+ getLocalStream: =>
+ @localStream
+
+ getDisplayStream: =>
+ @displayStream
+
releaseStream: =>
if @stream
@stream.getAudioTracks().forEach( (track) => track.stop() )
diff --git a/coffee/local_peer.coffee b/coffee/local_peer.coffee
index b4a524f..b9fee7e 100644
--- a/coffee/local_peer.coffee
+++ b/coffee/local_peer.coffee
@@ -33,6 +33,10 @@ class palava.LocalPeer extends palava.Peer
@emit 'stream_ready', e
@userMedia.on 'stream_error', (e) =>
@emit 'stream_error', e
+ @userMedia.on 'display_stream_ready', (e) =>
+ @emit 'display_stream_ready', e
+ @userMedia.on 'display_stream_error', (e) =>
+ @emit 'display_stream_error', e
if @getStream()
@ready = true
@emit 'stream_ready'
@@ -54,6 +58,9 @@ class palava.LocalPeer extends palava.Peer
getStream: =>
@userMedia.getStream()
+ requestDisplaySharing: =>
+ @userMedia.requestDisplaySharing()
+
# Updates the status of the local peer. The status is extended or updated with the given items.
#
# @param status [Object] Object containing the new items
diff --git a/coffee/remote_peer.coffee b/coffee/remote_peer.coffee
index 5de671f..b9e2d28 100644
--- a/coffee/remote_peer.coffee
+++ b/coffee/remote_peer.coffee
@@ -28,10 +28,14 @@ class palava.RemotePeer extends palava.Peer
@dataChannels = {}
+ @offers = offers
+ @makingOffer = false
+ @ignoreOffer = false
+ @setRemoteAnswerPending = false
+
@setupRoom()
- @setupPeerConnection(offers)
@setupDistributor()
-
+ @setupPeerConnection(offers)
if offers
@sendOffer()
@@ -62,8 +66,7 @@ class palava.RemotePeer extends palava.Peer
credential: @turnCredentials.password
{iceServers: options}
- # Sets up the peer connection and its events
- #
+ # Sets up the peer connection and its events #
# @nodoc
#
setupPeerConnection: (offers) =>
@@ -87,6 +90,9 @@ class palava.RemotePeer extends palava.Peer
@ready = false
@emit 'stream_removed'
+ @peerConnection.onnegotiationneeded = () =>
+ @sendOffer()
+
@peerConnection.oniceconnectionstatechange = (event) =>
connectionState = event.target.iceConnectionState
@@ -107,12 +113,33 @@ class palava.RemotePeer extends palava.Peer
@error = "connection_closed"
@emit 'connection_closed'
- # TODO onsignalingstatechange
-
if @room.localPeer.getStream()
- @peerConnection.addStream @room.localPeer.getStream()
- else
- # not suppored yet
+ for track in @room.localPeer.getStream().getTracks()
+ @peerConnection.addTrack(track, @room.localPeer.getStream())
+
+ @room.localPeer.on 'display_stream_ready', (stream) =>
+ videoSender = @peerConnection.getSenders().find(
+ (sender) => sender.track.kind == "video"
+ )
+ if videoSender
+ # if there is a local video track then replace it because that's faster
+ # and does not need renegotiation
+ videoSender.replaceTrack(track)
+ else
+ @peerConnection.addTrack(stream.getVideoTracks()[0],
+ @room.localPeer.getStream())
+
+ @room.localPeer.on 'display_stream_stop', (stream) =>
+ localVideoTracks = @room.localPeer.getLocalStream().getVideoTracks()
+ if localVideoTracks.length > 0
+ # if there was a local video track then reuse the display video track
+ # because it's faster and does not need renegotiation
+ videoSender = @peerConnection.getSenders().find(
+ (sender) => sender.track.kind == "video"
+ )
+ videoSender.replaceTrack(localVideoTracks[0])
+ else
+ @peerConnection.removeTrack(stream.getVideoTracks()[0])
# data channel setup
@@ -140,8 +167,6 @@ class palava.RemotePeer extends palava.Peer
# @nodoc
#
setupDistributor: =>
- # TODO _ in events also in rtc-server
- # TODO consistent protocol naming
@distributor = new palava.Distributor(@room.channel, @id)
@distributor.on 'peer_left', (msg) =>
@@ -157,16 +182,22 @@ class palava.RemotePeer extends palava.Peer
return if msg.candidate == ""
candidate = new RTCIceCandidate({candidate: msg.candidate, sdpMLineIndex: msg.sdpmlineindex, sdpMid: msg.sdpmid})
unless @room.options.filterIceCandidateTypes.includes(candidate.type)
- @peerConnection.addIceCandidate(candidate)
+ await @peerConnection.addIceCandidate(candidate)
@distributor.on 'offer', (msg) =>
- @peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
- @emit 'offer' # ignored so far
+ # we sent an offer already and are the preferred offerer, so we don't back down
+ return if @pendingOffer && @offers
+
+ # we backed down, so we drop the pending offer and choose the other peer's offer
+ @pendingOffer = null
+
+ await @peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
@sendAnswer()
@distributor.on 'answer', (msg) =>
- @peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
- @emit 'answer' # ignored so far
+ await @peerConnection.setLocalDescription(@pendingOffer) if @pendingOffer
+ @pendingOffer = null
+ await @peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
@distributor.on 'peer_updated_status', (msg) =>
@status = msg.status
@@ -199,30 +230,32 @@ class palava.RemotePeer extends palava.Peer
@on 'oaerror', (e) => @room.emit('peer_oaerror', @, e)
@on 'channel_ready', (n, c) => @room.emit('peer_channel_ready', @, n, c)
- # Sends the offer for a peer connection
- #
- # @nodoc
+ sendMessage: (data) =>
+ @distributor.send
+ event: 'message'
+ data: data
+
+ # Sends the offer to create a peer connection
#
sendOffer: =>
- @peerConnection.createOffer @sdpSender('offer'), @oaError, palava.browser.getConstraints()
+ @peerConnection.createOffer @sdpSender('offer'), @oaError, palava.browser.getConstraints() if @peerConnection.signalingState == "stable" && !@pendingOffer
# Sends the answer to create a peer connection
#
sendAnswer: =>
@peerConnection.createAnswer @sdpSender('answer'), @oaError, palava.browser.getConstraints()
- sendMessage: (data) =>
- @distributor.send
- event: 'message'
- data: data
- # Helper for sending sdp
+ # Send offer/answer
#
# @nodoc
#
sdpSender: (event) =>
(sdp) =>
- @peerConnection.setLocalDescription(sdp)
+ if event == 'offer'
+ @pendingOffer = sdp
+ else
+ await @peerConnection.setLocalDescription(sdp)
@distributor.send
event: event
sdp: sdp
diff --git a/coffee/room.coffee b/coffee/room.coffee
index a4fd1c3..10d49bb 100644
--- a/coffee/room.coffee
+++ b/coffee/room.coffee
@@ -85,6 +85,7 @@ class palava.Room extends @EventEmitter
@options.ownStatus[key] = status[key] for key in status
@options.ownStatus.user_agent ||= palava.browser.getUserAgent()
+ @options.ownStatus.user_agent_version ||= palava.browser.getUserAgentVersion()
@distributor.send
event: 'join_room'
diff --git a/palava.bundle.js b/palava.bundle.js
index 89e6869..3621890 100644
--- a/palava.bundle.js
+++ b/palava.bundle.js
@@ -3547,4 +3547,4 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
-(function(){}).call(this),function(){this.palava={browser:{}}}.call(this),function(){"object"==typeof module&&"object"==typeof module.exports&&(module.exports=this.palava),"object"!=typeof EventEmitter&&"function"==typeof require?this.EventEmitter=require("wolfy87-eventemitter"):this.EventEmitter=EventEmitter,"object"!=typeof adapter&&"function"==typeof require?this.adapter=require("webrtc-adapter/out/adapter_no_edge"):this.adapter=adapter}.call(this),function(){var t,r;r=this.palava,t=this.adapter,r.browser.isMozilla=function(){return"firefox"===t.browserDetails.browser},r.browser.isChrome=function(){return"chrome"===t.browserDetails.browser},r.browser.getUserAgent=function(){return t.browserDetails.browser},r.browser.getUserAgentVersion=function(){return t.browserDetails.version},r.browser.checkForWebrtcError=function(){try{new window.RTCPeerConnection({iceServers:[]})}catch(t){return t}return!(window.RTCPeerConnection&&window.RTCIceCandidate&&window.RTCSessionDescription&&navigator.mediaDevices&&navigator.mediaDevices.getUserMedia)},r.browser.getConstraints=function(){return{optional:[],mandatory:{OfferToReceiveAudio:!0,OfferToReceiveVideo:!0}}},r.browser.getPeerConnectionOptions=function(){return r.browser.isChrome()?{optional:[{DtlsSrtpKeyAgreement:!0}]}:{}},r.browser.registerFullscreen=function(t,e){return console.log("DEPRECATED: palava.browser.registerFullscreen will be removed from the palava library in early 2021"),t.requestFullscreen?t.addEventListener(e,function(){return this.requestFullscreen()}):t.mozRequestFullScreen?t.addEventListener(e,function(){return this.mozRequestFullScreen()}):t.webkitRequestFullscreen?t.addEventListener(e,function(){return this.webkitRequestFullscreen()}):void 0},r.browser.attachMediaStream=function(t,e){return e?t.srcObject=e:(t.pause(),t.srcObject=null)},r.browser.attachPeer=function(t,e){var n;return n=function(){return r.browser.attachMediaStream(t,e.getStream()),e.isLocal()&&t.setAttribute("muted",!0),t.play()},e.getStream()?n():e.on("stream_ready",function(){return n()})}}.call(this),function(){var n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function n(){this.constructor=t}for(var r in e)i.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},i={}.hasOwnProperty;this.palava.Gum=function(t){function e(t){this.releaseStream=n(this.releaseStream,this),this.getStream=n(this.getStream,this),this.requestStream=n(this.requestStream,this),this.changeConfig=n(this.changeConfig,this),this.config=t||{video:!0,audio:!0},this.stream=null}return r(e,t),e.prototype.changeConfig=function(t){return this.config=t,this.releaseStream(),this.requestStream()},e.prototype.requestStream=function(){return navigator.mediaDevices.getUserMedia(this.config).then((n=this,function(t){return n.stream=t,n.emit("stream_ready",t)}))["catch"]((e=this,function(t){return e.emit("stream_error",t)}));var e,n},e.prototype.getStream=function(){return this.stream},e.prototype.releaseStream=function(){return!!this.stream&&(this.stream.getAudioTracks().forEach(function(t){return t.stop()}),this.stream.getVideoTracks().forEach(function(t){return t.stop()}),this.stream=null,this.emit("stream_released",this),!0)},e}(this.EventEmitter)}.call(this),function(){var e,n=function(t,e){return function(){return t.apply(e,arguments)}};(e=this.palava).Identity=function(){function t(t){this.getStatus=n(this.getStatus,this),this.getName=n(this.getName,this),this.userMediaConfig=t.userMediaConfig,this.status=t.status||{},this.status.name=t.name}return t.prototype.newUserMedia=function(){return new e.Gum(this.userMediaConfig)},t.prototype.getName=function(){return this.name},t.prototype.getStatus=function(){return this.status},t}()}.call(this),function(){var r,i=function(t,e){return function(){return t.apply(e,arguments)}},n=function(t,e){function n(){this.constructor=t}for(var r in e)o.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},o={}.hasOwnProperty;(r=this.palava).Peer=function(t){function e(t,e){var n;this.isRemote=i(this.isRemote,this),this.isLocal=i(this.isLocal,this),this.isReady=i(this.isReady,this),this.isMuted=i(this.isMuted,this),this.getError=i(this.getError,this),this.hasError=i(this.hasError,this),this.hasVideo=i(this.hasVideo,this),this.transmitsVideo=i(this.transmitsVideo,this),this.hasAudio=i(this.hasAudio,this),this.transmitsAudio=i(this.transmitsAudio,this),this.id=t,this.status=e||{},(n=this.status).user_agent||(n.user_agent=r.browser.getUserAgent()),this.joinTime=(new Date).getTime(),this.ready=!1,this.error=null}return n(e,t),e.prototype.transmitsAudio=function(){var t,e,n;return!!(null!=(t=this.getStream())&&null!=(e=t.getAudioTracks())&&null!=(n=e[0])?n.enabled:void 0)},e.prototype.hasAudio=function(){var t,e;return!!(null!=(t=this.getStream())&&null!=(e=t.getAudioTracks())?e[0]:void 0)},e.prototype.transmitsVideo=function(){var t,e,n;return!!(null!=(t=this.getStream())&&null!=(e=t.getVideoTracks())&&null!=(n=e[0])?n.enabled:void 0)},e.prototype.hasVideo=function(){var t,e;return!!(null!=(t=this.getStream())&&null!=(e=t.getVideoTracks())?e[0]:void 0)},e.prototype.hasError=function(){return!!this.error},e.prototype.getError=function(){return this.error},e.prototype.isMuted=function(){return!!this.muted},e.prototype.isReady=function(){return!!this.ready},e.prototype.isLocal=function(){return!!this.local},e.prototype.isRemote=function(){return!this.local},e}(this.EventEmitter)}.call(this),function(){var i,o=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function n(){this.constructor=t}for(var r in e)s.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},s={}.hasOwnProperty;(i=this.palava).LocalPeer=function(t){function r(t,e,n){this.leave=o(this.leave,this),this.enableVideo=o(this.enableVideo,this),this.disableVideo=o(this.disableVideo,this),this.enableAudio=o(this.enableAudio,this),this.disableAudio=o(this.disableAudio,this),this.updateStatus=o(this.updateStatus,this),this.getStream=o(this.getStream,this),this.setupRoom=o(this.setupRoom,this),this.setupUserMedia=o(this.setupUserMedia,this),this.muted=!0,this.local=!0,r.__super__.constructor.call(this,t,e),this.room=n,this.userMedia=n.userMedia,this.setupRoom(),this.setupUserMedia()}return e(r,t),r.prototype.setupUserMedia=function(){var t,e,n;if(this.userMedia.on("stream_released",(t=this,function(){return t.ready=!1,t.emit("stream_removed")})),this.userMedia.on("stream_ready",(e=this,function(t){return e.ready=!0,e.emit("stream_ready",t)})),this.userMedia.on("stream_error",(n=this,function(t){return n.emit("stream_error",t)})),this.getStream())return this.ready=!0,this.emit("stream_ready")},r.prototype.setupRoom=function(){var t,e,n;return this.room.peers[this.id]=this.room.localPeer=this,this.on("update",(t=this,function(){return t.room.emit("peer_update",t)})),this.on("stream_ready",(e=this,function(){return e.room.emit("peer_stream_ready",e)})),this.on("stream_removed",(n=this,function(){return n.room.emit("peer_stream_removed",n)}))},r.prototype.getStream=function(){return this.userMedia.getStream()},r.prototype.updateStatus=function(t){var e,n;if(!(t&&t instanceof Object&&0!==Object.keys(t).length))return t;for(n in t)this.status[n]=t[n];return(e=this.status).user_agent||(e.user_agent=i.browser.getUserAgent()),this.room.channel.send({event:"update_status",status:this.status}),this.status},r.prototype.disableAudio=function(){var t,e,n,r,i;if(this.ready){for(r=[],t=0,e=(n=this.getStream().getAudioTracks()).length;tthis.MAX_BUFFER)return void setTimeout(this.actualSend.bind(this),1);e=(r=this.sendBuffer[0])[0],t=r[1];try{this.channel.send(e)}catch(i){return n=i,void setTimeout(this.actualSend.bind(this),1)}try{"function"==typeof t&&t()}catch(i){n=i,console.log("Exception in write callback:",n)}this.sendBuffer.shift()}else console.log("Not sending when not open!")},e}(this.EventEmitter)}.call(this),function(){var c,s=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function n(){this.constructor=t}for(var r in e)i.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},i={}.hasOwnProperty;(c=this.palava).RemotePeer=function(t){function o(t,e,n,r,i){this.closePeerConnection=s(this.closePeerConnection,this),this.oaError=s(this.oaError,this),this.sdpSender=s(this.sdpSender,this),this.sendMessage=s(this.sendMessage,this),this.sendAnswer=s(this.sendAnswer,this),this.sendOffer=s(this.sendOffer,this),this.setupRoom=s(this.setupRoom,this),this.setupDistributor=s(this.setupDistributor,this),this.setupPeerConnection=s(this.setupPeerConnection,this),this.generateIceOptions=s(this.generateIceOptions,this),this.toggleMute=s(this.toggleMute,this),this.getStream=s(this.getStream,this),this.muted=!1,this.local=!1,o.__super__.constructor.call(this,t,e),this.room=n,this.remoteStream=null,this.turnCredentials=i,this.dataChannels={},this.setupRoom(),this.setupPeerConnection(r),this.setupDistributor(),r&&this.sendOffer()}return e(o,t),o.prototype.getStream=function(){return this.remoteStream},o.prototype.toggleMute=function(){return this.muted=!this.muted},o.prototype.generateIceOptions=function(){var t;return t=[],this.room.options.stun&&t.push({urls:[this.room.options.stun]}),this.room.options.turnUrls&&this.turnCredentials&&t.push({urls:this.room.options.turnUrls,username:this.turnCredentials.user,credential:this.turnCredentials.password}),{iceServers:t}},o.prototype.setupPeerConnection=function(t){var e,n,r,i,o,s,a,u,h;if(this.peerConnection=new RTCPeerConnection(this.generateIceOptions(),c.browser.getPeerConnectionOptions()),this.peerConnection.onicecandidate=(o=this,function(t){if(t.candidate)return o.distributor.send({event:"ice_candidate",sdpmlineindex:t.candidate.sdpMLineIndex,sdpmid:t.candidate.sdpMid,candidate:t.candidate.candidate})}),this.peerConnection.ontrack=(s=this,function(t){return s.remoteStream=t.streams[0],s.ready=!0,s.emit("stream_ready")}),this.peerConnection.onremovestream=(a=this,function(){return a.remoteStream=null,a.ready=!1,a.emit("stream_removed")}),this.peerConnection.oniceconnectionstatechange=(u=this,function(t){switch(t.target.iceConnectionState){case"connecting":return u.error=null,u.emit("connection_pending");case"connected":return u.error=null,u.emit("connection_established");case"failed":return u.error="connection_failed",u.emit("connection_failed");case"disconnected":return u.error="connection_disconnected",u.emit("connection_disconnected");case"closed":return u.error="connection_closed",u.emit("connection_closed")}}),this.room.localPeer.getStream()&&this.peerConnection.addStream(this.room.localPeer.getStream()),null!=this.room.options.dataChannels)if(h=this,i=function(t){var e,n;return e=t.label,n=new c.DataChannel(t),h.dataChannels[e]=n,h.emit("channel_ready",e,n)},t)for(e in r=this.room.options.dataChannels)n=r[e],this.peerConnection.createDataChannel(e,n).onopen=function(){return i(this)};else this.peerConnection.ondatachannel=function(t){return i(t.channel)};return this.peerConnection},o.prototype.setupDistributor=function(){var t,n,e,r,i,o;return this.distributor=new c.Distributor(this.room.channel,this.id),this.distributor.on("peer_left",(t=this,function(){return t.ready&&(t.remoteStream=null,t.emit("stream_removed"),t.ready=!1),t.peerConnection.close(),t.emit("left")})),this.distributor.on("ice_candidate",(n=this,function(t){var e;if(""!==t.candidate)return e=new RTCIceCandidate({candidate:t.candidate,sdpMLineIndex:t.sdpmlineindex,sdpMid:t.sdpmid}),n.room.options.filterIceCandidateTypes.includes(e.type)?void 0:n.peerConnection.addIceCandidate(e)})),this.distributor.on("offer",(e=this,function(t){return e.peerConnection.setRemoteDescription(new RTCSessionDescription(t.sdp)),e.emit("offer"),e.sendAnswer()})),this.distributor.on("answer",(r=this,function(t){return r.peerConnection.setRemoteDescription(new RTCSessionDescription(t.sdp)),r.emit("answer")})),this.distributor.on("peer_updated_status",(i=this,function(t){return i.status=t.status,i.emit("update")})),this.distributor.on("message",(o=this,function(t){return o.emit("message",t.data)})),this.distributor},o.prototype.setupRoom=function(){var t,e,n,r,i,o,s,a,u,h,c,p,d;return(this.room.peers[this.id]=this).on("left",(t=this,function(){return delete t.room.peers[t.id],t.room.emit("peer_left",t)})),this.on("offer",(e=this,function(){return e.room.emit("peer_offer",e)})),this.on("answer",(n=this,function(){return n.room.emit("peer_answer",n)})),this.on("update",(r=this,function(){return r.room.emit("peer_update",r)})),this.on("stream_ready",(i=this,function(){return i.room.emit("peer_stream_ready",i)})),this.on("stream_removed",(o=this,function(){return o.room.emit("peer_stream_removed",o)})),this.on("connection_pending",(s=this,function(){return s.room.emit("peer_connection_pending",s)})),this.on("connection_established",(a=this,function(){return a.room.emit("peer_connection_established",a)})),this.on("connection_failed",(u=this,function(){return u.room.emit("peer_connection_failed",u)})),this.on("connection_disconnected",(h=this,function(){return h.room.emit("peer_connection_disconnected",h)})),this.on("connection_closed",(c=this,function(){return c.room.emit("peer_connection_closed",c)})),this.on("oaerror",(p=this,function(t){return p.room.emit("peer_oaerror",p,t)})),this.on("channel_ready",(d=this,function(t,e){return d.room.emit("peer_channel_ready",d,t,e)}))},o.prototype.sendOffer=function(){return this.peerConnection.createOffer(this.sdpSender("offer"),this.oaError,c.browser.getConstraints())},o.prototype.sendAnswer=function(){return this.peerConnection.createAnswer(this.sdpSender("answer"),this.oaError,c.browser.getConstraints())},o.prototype.sendMessage=function(t){return this.distributor.send({event:"message",data:t})},o.prototype.sdpSender=function(e){return n=this,function(t){return n.peerConnection.setLocalDescription(t),n.distributor.send({event:e,sdp:t})};var n},o.prototype.oaError=function(t){return this.emit("oaerror",t)},o.prototype.closePeerConnection=function(){var t;return null!=(t=this.peerConnection)&&t.close(),this.peerConnection=null},o}(c.Peer)}.call(this),function(){var u,i=function(t,e){return function(){return t.apply(e,arguments)}},n=function(t,e){function n(){this.constructor=t}for(var r in e)o.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},o={}.hasOwnProperty;(u=this.palava).Room=function(t){function e(t,e,n,r){null==r&&(r={}),this.getAllPeers=i(this.getAllPeers,this),this.getRemotePeers=i(this.getRemotePeers,this),this.getLocalPeer=i(this.getLocalPeer,this),this.getPeerById=i(this.getPeerById,this),this.destroy=i(this.destroy,this),this.leave=i(this.leave,this),this.join=i(this.join,this),this.setupDistributor=i(this.setupDistributor,this),this.setupOptions=i(this.setupOptions,this),this.setupUserMedia=i(this.setupUserMedia,this),this.id=t,this.userMedia=n,this.channel=e,this.peers={},this.options=r,this.setupUserMedia(),this.setupDistributor(),this.setupOptions()}return n(e,t),e.prototype.setupUserMedia=function(){var e,n,t;return this.userMedia.on("stream_ready",(e=this,function(t){return e.emit("local_stream_ready",t)})),this.userMedia.on("stream_error",(n=this,function(t){return n.emit("local_stream_error",t)})),this.userMedia.on("stream_released",(t=this,function(){return t.emit("local_stream_removed")}))},e.prototype.setupOptions=function(){var t,e,n;return(t=this.options).joinTimeout||(t.joinTimeout=1e3),(e=this.options).ownStatus||(e.ownStatus={}),(n=this.options).filterIceCandidateTypes||(n.filterIceCandidateTypes=[])},e.prototype.setupDistributor=function(){var a,r,e,n;return this.distributor=new u.Distributor(this.channel),this.distributor.on("joined_room",(a=this,function(t){var e,n,r,i,o,s;for(clearTimeout(a.joinCheckTimeout),s=t.turn_user?{user:t.turn_user,password:t.turn_password}:null,new u.LocalPeer(t.own_id,a.options.ownStatus,a),e=0,n=(o=t.peers).length;ethis.MAX_BUFFER)return void setTimeout(this.actualSend.bind(this),1);e=(n=this.sendBuffer[0])[0],t=n[1];try{this.channel.send(e)}catch(i){return r=i,void setTimeout(this.actualSend.bind(this),1)}try{"function"==typeof t&&t()}catch(i){r=i,console.log("Exception in write callback:",r)}this.sendBuffer.shift()}else console.log("Not sending when not open!")},e}(this.EventEmitter)}.call(this),function(){var _,s=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function r(){this.constructor=t}for(var n in e)i.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},i={}.hasOwnProperty;(_=this.palava).RemotePeer=function(t){function o(t,e,r,n,i){this.closePeerConnection=s(this.closePeerConnection,this),this.oaError=s(this.oaError,this),this.sdpSender=s(this.sdpSender,this),this.sendMessage=s(this.sendMessage,this),this.sendAnswer=s(this.sendAnswer,this),this.sendOffer=s(this.sendOffer,this),this.setupRoom=s(this.setupRoom,this),this.setupDistributor=s(this.setupDistributor,this),this.setupPeerConnection=s(this.setupPeerConnection,this),this.generateIceOptions=s(this.generateIceOptions,this),this.toggleMute=s(this.toggleMute,this),this.getStream=s(this.getStream,this),this.muted=!1,this.local=!1,o.__super__.constructor.call(this,t,e),this.room=r,this.remoteStream=null,this.turnCredentials=i,this.dataChannels={},this.setupRoom(),this.setupPeerConnection(n),this.setupDistributor(),this.offers=n}return e(o,t),o.prototype.getStream=function(){return this.remoteStream},o.prototype.toggleMute=function(){return this.muted=!this.muted},o.prototype.generateIceOptions=function(){var t;return t=[],this.room.options.stun&&t.push({urls:[this.room.options.stun]}),this.room.options.turnUrls&&this.turnCredentials&&t.push({urls:this.room.options.turnUrls,username:this.turnCredentials.user,credential:this.turnCredentials.password}),{iceServers:t}},o.prototype.setupPeerConnection=function(t){var e,r,n,i,o,s,a,u,h,c,p,l,d,m,f,g;if(this.peerConnection=new RTCPeerConnection(this.generateIceOptions(),_.browser.getPeerConnectionOptions()),this.peerConnection.onicecandidate=(h=this,function(t){if(t.candidate)return h.distributor.send({event:"ice_candidate",sdpmlineindex:t.candidate.sdpMLineIndex,sdpmid:t.candidate.sdpMid,candidate:t.candidate.candidate})}),this.peerConnection.ontrack=(c=this,function(t){return c.remoteStream=t.streams[0],c.ready=!0,c.emit("stream_ready")}),this.peerConnection.onremovestream=(p=this,function(){return p.remoteStream=null,p.ready=!1,p.emit("stream_removed")}),this.peerConnection.onnegotiationneeded=(l=this,function(){return l.sendOffer()}),this.peerConnection.oniceconnectionstatechange=(d=this,function(t){switch(t.target.iceConnectionState){case"connecting":return d.error=null,d.emit("connection_pending");case"connected":return d.error=null,d.emit("connection_established");case"failed":return d.error="connection_failed",d.emit("connection_failed");case"disconnected":return d.error="connection_disconnected",d.emit("connection_disconnected");case"closed":return d.error="connection_closed",d.emit("connection_closed")}}),this.room.localPeer.getStream())for(e=0,n=(o=this.room.localPeer.getStream().getTracks()).length;e.
function Gum(config) {
this.releaseStream = bind(this.releaseStream, this);
+ this.getDisplayStream = bind(this.getDisplayStream, this);
+ this.getLocalStream = bind(this.getLocalStream, this);
this.getStream = bind(this.getStream, this);
+ this.stopDisplaySharing = bind(this.stopDisplaySharing, this);
+ this.requestDisplaySharing = bind(this.requestDisplaySharing, this);
this.requestStream = bind(this.requestStream, this);
this.changeConfig = bind(this.changeConfig, this);
this.config = config || {
@@ -179,6 +183,8 @@ along with this program. If not, see .
audio: true
};
this.stream = null;
+ this.localStream = null;
+ this.displayStream = null;
}
Gum.prototype.changeConfig = function(config) {
@@ -190,6 +196,7 @@ along with this program. If not, see .
Gum.prototype.requestStream = function() {
return navigator.mediaDevices.getUserMedia(this.config).then((function(_this) {
return function(stream) {
+ _this.localStream = stream.clone();
_this.stream = stream;
return _this.emit('stream_ready', stream);
};
@@ -200,10 +207,43 @@ along with this program. If not, see .
})(this));
};
+ Gum.prototype.requestDisplaySharing = function() {
+ return navigator.mediaDevices.getDisplayMedia({
+ video: true
+ }).then((function(_this) {
+ return function(stream) {
+ if (_this.localStream.getAudioTracks().length > 0) {
+ stream.addTrack(_this.localStream.getAudioTracks()[0], _this.localStream);
+ }
+ _this.displayStream = stream.clone();
+ _this.stream = stream;
+ return _this.emit('display_stream_ready', stream);
+ };
+ })(this))["catch"]((function(_this) {
+ return function(error) {
+ return _this.emit('display_stream_error', error);
+ };
+ })(this));
+ };
+
+ Gum.prototype.stopDisplaySharing = function() {
+ this.stream = this.localStream;
+ this.emit('display_stream_stop', this.displayStream);
+ return this.displayStream = null;
+ };
+
Gum.prototype.getStream = function() {
return this.stream;
};
+ Gum.prototype.getLocalStream = function() {
+ return this.localStream;
+ };
+
+ Gum.prototype.getDisplayStream = function() {
+ return this.displayStream;
+ };
+
Gum.prototype.releaseStream = function() {
if (this.stream) {
this.stream.getAudioTracks().forEach((function(_this) {
@@ -379,6 +419,7 @@ along with this program. If not, see .
this.enableAudio = bind(this.enableAudio, this);
this.disableAudio = bind(this.disableAudio, this);
this.updateStatus = bind(this.updateStatus, this);
+ this.requestDisplaySharing = bind(this.requestDisplaySharing, this);
this.getStream = bind(this.getStream, this);
this.setupRoom = bind(this.setupRoom, this);
this.setupUserMedia = bind(this.setupUserMedia, this);
@@ -409,6 +450,16 @@ along with this program. If not, see .
return _this.emit('stream_error', e);
};
})(this));
+ this.userMedia.on('display_stream_ready', (function(_this) {
+ return function(e) {
+ return _this.emit('display_stream_ready', e);
+ };
+ })(this));
+ this.userMedia.on('display_stream_error', (function(_this) {
+ return function(e) {
+ return _this.emit('display_stream_error', e);
+ };
+ })(this));
if (this.getStream()) {
this.ready = true;
return this.emit('stream_ready');
@@ -438,6 +489,10 @@ along with this program. If not, see .
return this.userMedia.getStream();
};
+ LocalPeer.prototype.requestDisplaySharing = function() {
+ return this.userMedia.requestDisplaySharing();
+ };
+
LocalPeer.prototype.updateStatus = function(status) {
var base, key;
if (!status || !(status instanceof Object) || Object.keys(status).length === 0) {
@@ -681,9 +736,7 @@ along with this program. If not, see .
this.setupRoom();
this.setupPeerConnection(offers);
this.setupDistributor();
- if (offers) {
- this.sendOffer();
- }
+ this.offers = offers;
}
RemotePeer.prototype.getStream = function() {
@@ -715,7 +768,7 @@ along with this program. If not, see .
};
RemotePeer.prototype.setupPeerConnection = function(offers) {
- var channel, label, options, ref, registerChannel;
+ var channel, i, label, len, options, ref, ref1, registerChannel, track;
this.peerConnection = new RTCPeerConnection(this.generateIceOptions(), palava.browser.getPeerConnectionOptions());
this.peerConnection.onicecandidate = (function(_this) {
return function(event) {
@@ -743,6 +796,11 @@ along with this program. If not, see .
return _this.emit('stream_removed');
};
})(this);
+ this.peerConnection.onnegotiationneeded = (function(_this) {
+ return function() {
+ return _this.sendOffer();
+ };
+ })(this);
this.peerConnection.oniceconnectionstatechange = (function(_this) {
return function(event) {
var connectionState;
@@ -767,10 +825,39 @@ along with this program. If not, see .
};
})(this);
if (this.room.localPeer.getStream()) {
- this.peerConnection.addStream(this.room.localPeer.getStream());
- } else {
-
+ ref = this.room.localPeer.getStream().getTracks();
+ for (i = 0, len = ref.length; i < len; i++) {
+ track = ref[i];
+ this.peerConnection.addTrack(track, this.room.localPeer.getStream());
+ }
}
+ this.room.localPeer.on('display_stream_ready', (function(_this) {
+ return function(stream) {
+ var videoSender;
+ videoSender = _this.peerConnection.getSenders().find(function(sender) {
+ return sender.track.kind === "video";
+ });
+ if (videoSender) {
+ return videoSender.replaceTrack(track);
+ } else {
+ return _this.peerConnection.addTrack(stream.getVideoTracks()[0], _this.room.localPeer.getStream());
+ }
+ };
+ })(this));
+ this.room.localPeer.on('display_stream_stop', (function(_this) {
+ return function(stream) {
+ var localVideoTracks, videoSender;
+ localVideoTracks = _this.room.localPeer.getLocalStream().getVideoTracks();
+ if (localVideoTracks.length > 0) {
+ videoSender = _this.peerConnection.getSenders().find(function(sender) {
+ return sender.track.kind === "video";
+ });
+ return videoSender.replaceTrack(localVideoTracks[0]);
+ } else {
+ return _this.peerConnection.removeTrack(stream.getVideoTracks()[0]);
+ }
+ };
+ })(this));
if (this.room.options.dataChannels != null) {
registerChannel = (function(_this) {
return function(channel) {
@@ -782,9 +869,9 @@ along with this program. If not, see .
};
})(this);
if (offers) {
- ref = this.room.options.dataChannels;
- for (label in ref) {
- options = ref[label];
+ ref1 = this.room.options.dataChannels;
+ for (label in ref1) {
+ options = ref1[label];
channel = this.peerConnection.createDataChannel(label, options);
channel.onopen = function() {
return registerChannel(this);
@@ -832,6 +919,14 @@ along with this program. If not, see .
})(this));
this.distributor.on('offer', (function(_this) {
return function(msg) {
+ if (_this.peerConnection.signalingState !== "stable") {
+ if (_this.offers) {
+ return;
+ }
+ _this.peerConnection.setLocalDescription({
+ type: "rollback"
+ });
+ }
_this.peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp));
_this.emit('offer');
return _this.sendAnswer();
@@ -945,6 +1040,9 @@ along with this program. If not, see .
RemotePeer.prototype.sdpSender = function(event) {
return (function(_this) {
return function(sdp) {
+ if (event === 'offer' && _this.peerConnection.signalingState !== 'stable') {
+ return;
+ }
_this.peerConnection.setLocalDescription(sdp);
return _this.distributor.send({
event: event,
@@ -1598,7 +1696,7 @@ along with this program. If not, see .
palava.LIB_VERSION = '2.2.0';
- palava.LIB_COMMIT = 'v2.1.0-12-g412482867a-dirty';
+ palava.LIB_COMMIT = 'v2.2.0-1-gb4ef48ea59-dirty';
palava.protocol_identifier = function() {
return palava.PROTOCOL_NAME = "palava.1.0";
diff --git a/palava.min.js b/palava.min.js
index cd3dfa8..3a0efc4 100644
--- a/palava.min.js
+++ b/palava.min.js
@@ -21,4 +21,4 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
-(function(){}).call(this),function(){this.palava={browser:{}}}.call(this),function(){"object"==typeof module&&"object"==typeof module.exports&&(module.exports=this.palava),"object"!=typeof EventEmitter&&"function"==typeof require?this.EventEmitter=require("wolfy87-eventemitter"):this.EventEmitter=EventEmitter,"object"!=typeof adapter&&"function"==typeof require?this.adapter=require("webrtc-adapter/out/adapter_no_edge"):this.adapter=adapter}.call(this),function(){var t,r;r=this.palava,t=this.adapter,r.browser.isMozilla=function(){return"firefox"===t.browserDetails.browser},r.browser.isChrome=function(){return"chrome"===t.browserDetails.browser},r.browser.getUserAgent=function(){return t.browserDetails.browser},r.browser.getUserAgentVersion=function(){return t.browserDetails.version},r.browser.checkForWebrtcError=function(){try{new window.RTCPeerConnection({iceServers:[]})}catch(t){return t}return!(window.RTCPeerConnection&&window.RTCIceCandidate&&window.RTCSessionDescription&&navigator.mediaDevices&&navigator.mediaDevices.getUserMedia)},r.browser.getConstraints=function(){return{optional:[],mandatory:{OfferToReceiveAudio:!0,OfferToReceiveVideo:!0}}},r.browser.getPeerConnectionOptions=function(){return r.browser.isChrome()?{optional:[{DtlsSrtpKeyAgreement:!0}]}:{}},r.browser.registerFullscreen=function(t,e){return console.log("DEPRECATED: palava.browser.registerFullscreen will be removed from the palava library in early 2021"),t.requestFullscreen?t.addEventListener(e,function(){return this.requestFullscreen()}):t.mozRequestFullScreen?t.addEventListener(e,function(){return this.mozRequestFullScreen()}):t.webkitRequestFullscreen?t.addEventListener(e,function(){return this.webkitRequestFullscreen()}):void 0},r.browser.attachMediaStream=function(t,e){return e?t.srcObject=e:(t.pause(),t.srcObject=null)},r.browser.attachPeer=function(t,e){var n;return n=function(){return r.browser.attachMediaStream(t,e.getStream()),e.isLocal()&&t.setAttribute("muted",!0),t.play()},e.getStream()?n():e.on("stream_ready",function(){return n()})}}.call(this),function(){var n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function n(){this.constructor=t}for(var r in e)i.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},i={}.hasOwnProperty;this.palava.Gum=function(t){function e(t){this.releaseStream=n(this.releaseStream,this),this.getStream=n(this.getStream,this),this.requestStream=n(this.requestStream,this),this.changeConfig=n(this.changeConfig,this),this.config=t||{video:!0,audio:!0},this.stream=null}return r(e,t),e.prototype.changeConfig=function(t){return this.config=t,this.releaseStream(),this.requestStream()},e.prototype.requestStream=function(){return navigator.mediaDevices.getUserMedia(this.config).then((n=this,function(t){return n.stream=t,n.emit("stream_ready",t)}))["catch"]((e=this,function(t){return e.emit("stream_error",t)}));var e,n},e.prototype.getStream=function(){return this.stream},e.prototype.releaseStream=function(){return!!this.stream&&(this.stream.getAudioTracks().forEach(function(t){return t.stop()}),this.stream.getVideoTracks().forEach(function(t){return t.stop()}),this.stream=null,this.emit("stream_released",this),!0)},e}(this.EventEmitter)}.call(this),function(){var e,n=function(t,e){return function(){return t.apply(e,arguments)}};(e=this.palava).Identity=function(){function t(t){this.getStatus=n(this.getStatus,this),this.getName=n(this.getName,this),this.userMediaConfig=t.userMediaConfig,this.status=t.status||{},this.status.name=t.name}return t.prototype.newUserMedia=function(){return new e.Gum(this.userMediaConfig)},t.prototype.getName=function(){return this.name},t.prototype.getStatus=function(){return this.status},t}()}.call(this),function(){var r,i=function(t,e){return function(){return t.apply(e,arguments)}},n=function(t,e){function n(){this.constructor=t}for(var r in e)o.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},o={}.hasOwnProperty;(r=this.palava).Peer=function(t){function e(t,e){var n;this.isRemote=i(this.isRemote,this),this.isLocal=i(this.isLocal,this),this.isReady=i(this.isReady,this),this.isMuted=i(this.isMuted,this),this.getError=i(this.getError,this),this.hasError=i(this.hasError,this),this.hasVideo=i(this.hasVideo,this),this.transmitsVideo=i(this.transmitsVideo,this),this.hasAudio=i(this.hasAudio,this),this.transmitsAudio=i(this.transmitsAudio,this),this.id=t,this.status=e||{},(n=this.status).user_agent||(n.user_agent=r.browser.getUserAgent()),this.joinTime=(new Date).getTime(),this.ready=!1,this.error=null}return n(e,t),e.prototype.transmitsAudio=function(){var t,e,n;return!!(null!=(t=this.getStream())&&null!=(e=t.getAudioTracks())&&null!=(n=e[0])?n.enabled:void 0)},e.prototype.hasAudio=function(){var t,e;return!!(null!=(t=this.getStream())&&null!=(e=t.getAudioTracks())?e[0]:void 0)},e.prototype.transmitsVideo=function(){var t,e,n;return!!(null!=(t=this.getStream())&&null!=(e=t.getVideoTracks())&&null!=(n=e[0])?n.enabled:void 0)},e.prototype.hasVideo=function(){var t,e;return!!(null!=(t=this.getStream())&&null!=(e=t.getVideoTracks())?e[0]:void 0)},e.prototype.hasError=function(){return!!this.error},e.prototype.getError=function(){return this.error},e.prototype.isMuted=function(){return!!this.muted},e.prototype.isReady=function(){return!!this.ready},e.prototype.isLocal=function(){return!!this.local},e.prototype.isRemote=function(){return!this.local},e}(this.EventEmitter)}.call(this),function(){var i,o=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function n(){this.constructor=t}for(var r in e)s.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},s={}.hasOwnProperty;(i=this.palava).LocalPeer=function(t){function r(t,e,n){this.leave=o(this.leave,this),this.enableVideo=o(this.enableVideo,this),this.disableVideo=o(this.disableVideo,this),this.enableAudio=o(this.enableAudio,this),this.disableAudio=o(this.disableAudio,this),this.updateStatus=o(this.updateStatus,this),this.getStream=o(this.getStream,this),this.setupRoom=o(this.setupRoom,this),this.setupUserMedia=o(this.setupUserMedia,this),this.muted=!0,this.local=!0,r.__super__.constructor.call(this,t,e),this.room=n,this.userMedia=n.userMedia,this.setupRoom(),this.setupUserMedia()}return e(r,t),r.prototype.setupUserMedia=function(){var t,e,n;if(this.userMedia.on("stream_released",(t=this,function(){return t.ready=!1,t.emit("stream_removed")})),this.userMedia.on("stream_ready",(e=this,function(t){return e.ready=!0,e.emit("stream_ready",t)})),this.userMedia.on("stream_error",(n=this,function(t){return n.emit("stream_error",t)})),this.getStream())return this.ready=!0,this.emit("stream_ready")},r.prototype.setupRoom=function(){var t,e,n;return this.room.peers[this.id]=this.room.localPeer=this,this.on("update",(t=this,function(){return t.room.emit("peer_update",t)})),this.on("stream_ready",(e=this,function(){return e.room.emit("peer_stream_ready",e)})),this.on("stream_removed",(n=this,function(){return n.room.emit("peer_stream_removed",n)}))},r.prototype.getStream=function(){return this.userMedia.getStream()},r.prototype.updateStatus=function(t){var e,n;if(!(t&&t instanceof Object&&0!==Object.keys(t).length))return t;for(n in t)this.status[n]=t[n];return(e=this.status).user_agent||(e.user_agent=i.browser.getUserAgent()),this.room.channel.send({event:"update_status",status:this.status}),this.status},r.prototype.disableAudio=function(){var t,e,n,r,i;if(this.ready){for(r=[],t=0,e=(n=this.getStream().getAudioTracks()).length;tthis.MAX_BUFFER)return void setTimeout(this.actualSend.bind(this),1);e=(r=this.sendBuffer[0])[0],t=r[1];try{this.channel.send(e)}catch(i){return n=i,void setTimeout(this.actualSend.bind(this),1)}try{"function"==typeof t&&t()}catch(i){n=i,console.log("Exception in write callback:",n)}this.sendBuffer.shift()}else console.log("Not sending when not open!")},e}(this.EventEmitter)}.call(this),function(){var c,s=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function n(){this.constructor=t}for(var r in e)i.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},i={}.hasOwnProperty;(c=this.palava).RemotePeer=function(t){function o(t,e,n,r,i){this.closePeerConnection=s(this.closePeerConnection,this),this.oaError=s(this.oaError,this),this.sdpSender=s(this.sdpSender,this),this.sendMessage=s(this.sendMessage,this),this.sendAnswer=s(this.sendAnswer,this),this.sendOffer=s(this.sendOffer,this),this.setupRoom=s(this.setupRoom,this),this.setupDistributor=s(this.setupDistributor,this),this.setupPeerConnection=s(this.setupPeerConnection,this),this.generateIceOptions=s(this.generateIceOptions,this),this.toggleMute=s(this.toggleMute,this),this.getStream=s(this.getStream,this),this.muted=!1,this.local=!1,o.__super__.constructor.call(this,t,e),this.room=n,this.remoteStream=null,this.turnCredentials=i,this.dataChannels={},this.setupRoom(),this.setupPeerConnection(r),this.setupDistributor(),r&&this.sendOffer()}return e(o,t),o.prototype.getStream=function(){return this.remoteStream},o.prototype.toggleMute=function(){return this.muted=!this.muted},o.prototype.generateIceOptions=function(){var t;return t=[],this.room.options.stun&&t.push({urls:[this.room.options.stun]}),this.room.options.turnUrls&&this.turnCredentials&&t.push({urls:this.room.options.turnUrls,username:this.turnCredentials.user,credential:this.turnCredentials.password}),{iceServers:t}},o.prototype.setupPeerConnection=function(t){var e,n,r,i,o,s,a,u,h;if(this.peerConnection=new RTCPeerConnection(this.generateIceOptions(),c.browser.getPeerConnectionOptions()),this.peerConnection.onicecandidate=(o=this,function(t){if(t.candidate)return o.distributor.send({event:"ice_candidate",sdpmlineindex:t.candidate.sdpMLineIndex,sdpmid:t.candidate.sdpMid,candidate:t.candidate.candidate})}),this.peerConnection.ontrack=(s=this,function(t){return s.remoteStream=t.streams[0],s.ready=!0,s.emit("stream_ready")}),this.peerConnection.onremovestream=(a=this,function(){return a.remoteStream=null,a.ready=!1,a.emit("stream_removed")}),this.peerConnection.oniceconnectionstatechange=(u=this,function(t){switch(t.target.iceConnectionState){case"connecting":return u.error=null,u.emit("connection_pending");case"connected":return u.error=null,u.emit("connection_established");case"failed":return u.error="connection_failed",u.emit("connection_failed");case"disconnected":return u.error="connection_disconnected",u.emit("connection_disconnected");case"closed":return u.error="connection_closed",u.emit("connection_closed")}}),this.room.localPeer.getStream()&&this.peerConnection.addStream(this.room.localPeer.getStream()),null!=this.room.options.dataChannels)if(h=this,i=function(t){var e,n;return e=t.label,n=new c.DataChannel(t),h.dataChannels[e]=n,h.emit("channel_ready",e,n)},t)for(e in r=this.room.options.dataChannels)n=r[e],this.peerConnection.createDataChannel(e,n).onopen=function(){return i(this)};else this.peerConnection.ondatachannel=function(t){return i(t.channel)};return this.peerConnection},o.prototype.setupDistributor=function(){var t,n,e,r,i,o;return this.distributor=new c.Distributor(this.room.channel,this.id),this.distributor.on("peer_left",(t=this,function(){return t.ready&&(t.remoteStream=null,t.emit("stream_removed"),t.ready=!1),t.peerConnection.close(),t.emit("left")})),this.distributor.on("ice_candidate",(n=this,function(t){var e;if(""!==t.candidate)return e=new RTCIceCandidate({candidate:t.candidate,sdpMLineIndex:t.sdpmlineindex,sdpMid:t.sdpmid}),n.room.options.filterIceCandidateTypes.includes(e.type)?void 0:n.peerConnection.addIceCandidate(e)})),this.distributor.on("offer",(e=this,function(t){return e.peerConnection.setRemoteDescription(new RTCSessionDescription(t.sdp)),e.emit("offer"),e.sendAnswer()})),this.distributor.on("answer",(r=this,function(t){return r.peerConnection.setRemoteDescription(new RTCSessionDescription(t.sdp)),r.emit("answer")})),this.distributor.on("peer_updated_status",(i=this,function(t){return i.status=t.status,i.emit("update")})),this.distributor.on("message",(o=this,function(t){return o.emit("message",t.data)})),this.distributor},o.prototype.setupRoom=function(){var t,e,n,r,i,o,s,a,u,h,c,p,d;return(this.room.peers[this.id]=this).on("left",(t=this,function(){return delete t.room.peers[t.id],t.room.emit("peer_left",t)})),this.on("offer",(e=this,function(){return e.room.emit("peer_offer",e)})),this.on("answer",(n=this,function(){return n.room.emit("peer_answer",n)})),this.on("update",(r=this,function(){return r.room.emit("peer_update",r)})),this.on("stream_ready",(i=this,function(){return i.room.emit("peer_stream_ready",i)})),this.on("stream_removed",(o=this,function(){return o.room.emit("peer_stream_removed",o)})),this.on("connection_pending",(s=this,function(){return s.room.emit("peer_connection_pending",s)})),this.on("connection_established",(a=this,function(){return a.room.emit("peer_connection_established",a)})),this.on("connection_failed",(u=this,function(){return u.room.emit("peer_connection_failed",u)})),this.on("connection_disconnected",(h=this,function(){return h.room.emit("peer_connection_disconnected",h)})),this.on("connection_closed",(c=this,function(){return c.room.emit("peer_connection_closed",c)})),this.on("oaerror",(p=this,function(t){return p.room.emit("peer_oaerror",p,t)})),this.on("channel_ready",(d=this,function(t,e){return d.room.emit("peer_channel_ready",d,t,e)}))},o.prototype.sendOffer=function(){return this.peerConnection.createOffer(this.sdpSender("offer"),this.oaError,c.browser.getConstraints())},o.prototype.sendAnswer=function(){return this.peerConnection.createAnswer(this.sdpSender("answer"),this.oaError,c.browser.getConstraints())},o.prototype.sendMessage=function(t){return this.distributor.send({event:"message",data:t})},o.prototype.sdpSender=function(e){return n=this,function(t){return n.peerConnection.setLocalDescription(t),n.distributor.send({event:e,sdp:t})};var n},o.prototype.oaError=function(t){return this.emit("oaerror",t)},o.prototype.closePeerConnection=function(){var t;return null!=(t=this.peerConnection)&&t.close(),this.peerConnection=null},o}(c.Peer)}.call(this),function(){var u,i=function(t,e){return function(){return t.apply(e,arguments)}},n=function(t,e){function n(){this.constructor=t}for(var r in e)o.call(e,r)&&(t[r]=e[r]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},o={}.hasOwnProperty;(u=this.palava).Room=function(t){function e(t,e,n,r){null==r&&(r={}),this.getAllPeers=i(this.getAllPeers,this),this.getRemotePeers=i(this.getRemotePeers,this),this.getLocalPeer=i(this.getLocalPeer,this),this.getPeerById=i(this.getPeerById,this),this.destroy=i(this.destroy,this),this.leave=i(this.leave,this),this.join=i(this.join,this),this.setupDistributor=i(this.setupDistributor,this),this.setupOptions=i(this.setupOptions,this),this.setupUserMedia=i(this.setupUserMedia,this),this.id=t,this.userMedia=n,this.channel=e,this.peers={},this.options=r,this.setupUserMedia(),this.setupDistributor(),this.setupOptions()}return n(e,t),e.prototype.setupUserMedia=function(){var e,n,t;return this.userMedia.on("stream_ready",(e=this,function(t){return e.emit("local_stream_ready",t)})),this.userMedia.on("stream_error",(n=this,function(t){return n.emit("local_stream_error",t)})),this.userMedia.on("stream_released",(t=this,function(){return t.emit("local_stream_removed")}))},e.prototype.setupOptions=function(){var t,e,n;return(t=this.options).joinTimeout||(t.joinTimeout=1e3),(e=this.options).ownStatus||(e.ownStatus={}),(n=this.options).filterIceCandidateTypes||(n.filterIceCandidateTypes=[])},e.prototype.setupDistributor=function(){var a,r,e,n;return this.distributor=new u.Distributor(this.channel),this.distributor.on("joined_room",(a=this,function(t){var e,n,r,i,o,s;for(clearTimeout(a.joinCheckTimeout),s=t.turn_user?{user:t.turn_user,password:t.turn_password}:null,new u.LocalPeer(t.own_id,a.options.ownStatus,a),e=0,n=(o=t.peers).length;ethis.MAX_BUFFER)return void setTimeout(this.actualSend.bind(this),1);e=(n=this.sendBuffer[0])[0],t=n[1];try{this.channel.send(e)}catch(i){return r=i,void setTimeout(this.actualSend.bind(this),1)}try{"function"==typeof t&&t()}catch(i){r=i,console.log("Exception in write callback:",r)}this.sendBuffer.shift()}else console.log("Not sending when not open!")},e}(this.EventEmitter)}.call(this),function(){var _,s=function(t,e){return function(){return t.apply(e,arguments)}},e=function(t,e){function r(){this.constructor=t}for(var n in e)i.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},i={}.hasOwnProperty;(_=this.palava).RemotePeer=function(t){function o(t,e,r,n,i){this.closePeerConnection=s(this.closePeerConnection,this),this.oaError=s(this.oaError,this),this.sdpSender=s(this.sdpSender,this),this.sendMessage=s(this.sendMessage,this),this.sendAnswer=s(this.sendAnswer,this),this.sendOffer=s(this.sendOffer,this),this.setupRoom=s(this.setupRoom,this),this.setupDistributor=s(this.setupDistributor,this),this.setupPeerConnection=s(this.setupPeerConnection,this),this.generateIceOptions=s(this.generateIceOptions,this),this.toggleMute=s(this.toggleMute,this),this.getStream=s(this.getStream,this),this.muted=!1,this.local=!1,o.__super__.constructor.call(this,t,e),this.room=r,this.remoteStream=null,this.turnCredentials=i,this.dataChannels={},this.setupRoom(),this.setupPeerConnection(n),this.setupDistributor(),this.offers=n}return e(o,t),o.prototype.getStream=function(){return this.remoteStream},o.prototype.toggleMute=function(){return this.muted=!this.muted},o.prototype.generateIceOptions=function(){var t;return t=[],this.room.options.stun&&t.push({urls:[this.room.options.stun]}),this.room.options.turnUrls&&this.turnCredentials&&t.push({urls:this.room.options.turnUrls,username:this.turnCredentials.user,credential:this.turnCredentials.password}),{iceServers:t}},o.prototype.setupPeerConnection=function(t){var e,r,n,i,o,s,a,u,h,c,p,l,d,m,f,g;if(this.peerConnection=new RTCPeerConnection(this.generateIceOptions(),_.browser.getPeerConnectionOptions()),this.peerConnection.onicecandidate=(h=this,function(t){if(t.candidate)return h.distributor.send({event:"ice_candidate",sdpmlineindex:t.candidate.sdpMLineIndex,sdpmid:t.candidate.sdpMid,candidate:t.candidate.candidate})}),this.peerConnection.ontrack=(c=this,function(t){return c.remoteStream=t.streams[0],c.ready=!0,c.emit("stream_ready")}),this.peerConnection.onremovestream=(p=this,function(){return p.remoteStream=null,p.ready=!1,p.emit("stream_removed")}),this.peerConnection.onnegotiationneeded=(l=this,function(){return l.sendOffer()}),this.peerConnection.oniceconnectionstatechange=(d=this,function(t){switch(t.target.iceConnectionState){case"connecting":return d.error=null,d.emit("connection_pending");case"connected":return d.error=null,d.emit("connection_established");case"failed":return d.error="connection_failed",d.emit("connection_failed");case"disconnected":return d.error="connection_disconnected",d.emit("connection_disconnected");case"closed":return d.error="connection_closed",d.emit("connection_closed")}}),this.room.localPeer.getStream())for(e=0,n=(o=this.room.localPeer.getStream().getTracks()).length;e