Skip to content

Commit

Permalink
Merge pull request #4726 from nextcloud/backport/4182/stable20.1
Browse files Browse the repository at this point in the history
[stable20.1] Stop sending the nick through data channels after some time
  • Loading branch information
nickvergessen authored Dec 9, 2020
2 parents e8fc6b2 + 77d2daa commit bb9eb6f
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 72 deletions.
4 changes: 2 additions & 2 deletions src/utils/webrtc/models/CallParticipantModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ CallParticipantModel.prototype = {
},

_handleNick: function(data) {
if (!this.get('peer') || this.get('peer').id !== data.id) {
// The nick could be changed even if there is no Peer object.
if (this.get('peerId') !== data.id) {
return
}

this.set('userId', data.userid || null)
this.set('name', data.name || null)
},

Expand Down
2 changes: 1 addition & 1 deletion src/utils/webrtc/models/LocalCallParticipantModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ LocalCallParticipantModel.prototype = {

this.set('guestName', guestName)

this._webRtc.sendDirectlyToAll('status', 'nickChanged', guestName)
this._webRtc.webrtc.emit('nickChanged', guestName)
},

_handleForcedMute: function() {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/webrtc/simplewebrtc/simplewebrtc.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ function SimpleWebRTC(opts) {
self.emit('mute', { id: message.payload.peerId })
}
}
} else if (message.type === 'nickChanged') {
// "nickChanged" can be received from a participant without a Peer
// object if that participant is not sending audio nor video.
self.emit('nick', { id: message.from, name: message.payload.name })
} else if (peers.length) {
peers.forEach(function(peer) {
if (message.sid && !self.connection.hasFeature('mcu')) {
Expand Down
143 changes: 74 additions & 69 deletions src/utils/webrtc/webrtc.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const delayedConnectionToPeer = []
let callParticipantCollection = null
let localCallParticipantModel = null
let showedTURNWarning = false
let sendCurrentMediaStateWithRepetitionTimeout = null
let sendCurrentStateWithRepetitionTimeout = null

function arrayDiff(a, b) {
return a.filter(function(i) {
Expand Down Expand Up @@ -159,15 +159,23 @@ function sendCurrentMediaState() {
}
}

function sendCurrentMediaStateWithRepetition(timeout) {
// TODO The participant name should be got from the participant list, but it is
// not currently possible to associate a Nextcloud ID with a standalone
// signaling ID for guests.
function sendCurrentNick() {
webrtc.webrtc.emit('nickChanged', store.getters.getDisplayName())
}

function sendCurrentStateWithRepetition(timeout) {
if (!timeout) {
timeout = 0

clearTimeout(sendCurrentMediaStateWithRepetitionTimeout)
clearTimeout(sendCurrentStateWithRepetitionTimeout)
}

sendCurrentMediaStateWithRepetitionTimeout = setTimeout(function() {
sendCurrentStateWithRepetitionTimeout = setTimeout(function() {
sendCurrentMediaState()
sendCurrentNick()

if (!timeout) {
timeout = 1000
Expand All @@ -176,11 +184,11 @@ function sendCurrentMediaStateWithRepetition(timeout) {
}

if (timeout > 16000) {
sendCurrentMediaStateWithRepetitionTimeout = null
sendCurrentStateWithRepetitionTimeout = null
return
}

sendCurrentMediaStateWithRepetition(timeout)
sendCurrentStateWithRepetition(timeout)
}, timeout)
}

Expand Down Expand Up @@ -241,15 +249,17 @@ function usersChanged(signaling, newUsers, disconnectedSessionIds) {
|| (!signaling.hasFeature('mcu') && user && !userHasStreams(user) && !webrtc.webrtc.localStreams.length)) {
callParticipantModel.setPeer(null)

// As there is no Peer for the other participant the current media
// state will not be sent once it is connected, so it needs to be
// sent now.
// When there is no MCU this is not needed; as the local participant
// has no streams it will be automatically marked with audio and
// video not available on the other end, so there is no need to send
// the media state.
// As there is no Peer for the other participant the current state
// will not be sent once it is connected, so it needs to be sent
// now.
// When there is no MCU this is only needed for the nick; as the
// local participant has no streams it will be automatically marked
// with audio and video not available on the other end, so there is
// no need to send the media state.
if (signaling.hasFeature('mcu')) {
sendCurrentMediaStateWithRepetition()
sendCurrentStateWithRepetition()
} else {
sendCurrentNick()
}
}

Expand Down Expand Up @@ -516,56 +526,14 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local
webrtc.sendDirectlyToAll(channel, message, payload)
}

// The nick name below the avatar is distributed through the DataChannel
// of the PeerConnection and only sent once during establishment. For
// the MCU case, the sending PeerConnection is created once and then
// never changed when more participants join. For this, we periodically
// send the nick to all other participants through the sending
// PeerConnection.
//
// TODO: The name for the avatar should come from the participant list
// which already has all information and get rid of using the
// DataChannel for this.
function stopSendingNick(peer) {
if (!peer.nickInterval) {
return
}

clearInterval(peer.nickInterval)
peer.nickInterval = null
}
function startSendingNick(peer) {
if (!signaling.hasFeature('mcu')) {
return
}

stopSendingNick(peer)
peer.nickInterval = setInterval(function() {
let payload
if (signaling.settings.userId === null) {
payload = store.getters.getDisplayName()
} else {
payload = {
'name': store.getters.getDisplayName(),
'userid': signaling.settings.userId,
}
}
peer.sendDirectly('status', 'nickChanged', payload)
}, 1000)
}

function handleIceConnectionStateConnected(peer) {
// Send the current information about the video and microphone
// state.
// Send the current information about the state.
if (!signaling.hasFeature('mcu')) {
// Only the media state needs to be sent, the nick was already sent
// in the offer/answer.
sendCurrentMediaState()
} else {
sendCurrentMediaStateWithRepetition()
}

if (signaling.settings.userId === null) {
const currentGuestNick = store.getters.getDisplayName()
sendDataChannelToAll('status', 'nickChanged', currentGuestNick)
sendCurrentStateWithRepetition()
}

// Reset ice restart counter for peer
Expand Down Expand Up @@ -733,8 +701,6 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local
if (peer.type === 'video') {
if (peer.id === signaling.getSessionId()) {
console.debug('Not adding ICE connection state handler for own peer', peer)

startSendingNick(peer)
} else {
setHandlerForIceConnectionStateChange(peer)
}
Expand Down Expand Up @@ -816,7 +782,6 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local
function stopPeerCheckMedia(peer) {
stopPeerCheckAudioMedia(peer)
stopPeerCheckVideoMedia(peer)
stopSendingNick(peer)
}

function startPeerCheckMedia(peer, stream) {
Expand Down Expand Up @@ -991,12 +956,8 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local
} else if (data.type === 'videoOff') {
webrtc.emit('mute', { id: peer.id, name: 'video' })
} else if (data.type === 'nickChanged') {
const payload = data.payload || ''
if (typeof (payload) === 'string') {
webrtc.emit('nick', { id: peer.id, name: data.payload })
} else {
webrtc.emit('nick', { id: peer.id, name: payload.name, userid: payload.userid })
}
const name = typeof (data.payload) === 'string' ? data.payload : data.payload.name
webrtc.emit('nick', { id: peer.id, name: name })
} else if (data.type === 'speaking' || data.type === 'stoppedSpeaking') {
// Valid known messages, but handled elsewhere
} else {
Expand Down Expand Up @@ -1029,6 +990,50 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local
sendDataChannelToAll('status', 'videoOff')
})

// Send the nick changed event via data channel and signaling
//
// The message format is different in each case. Due to historical reasons
// the payload of the data channel message is either a string that contains
// the name (if the participant is a guest) or an object with "name" and
// "userid" string fields (when the participant is a user).
//
// In the newer signaling message, on the other hand, the payload is always
// an object with only a "name" string field.
webrtc.on('nickChanged', function(name) {
let payload
if (signaling.settings.userId === null) {
payload = name
} else {
payload = {
'name': name,
'userid': signaling.settings.userId,
}
}

sendDataChannelToAll('status', 'nickChanged', payload)

// "webrtc.sendToAll" can not be used, as it only sends the signaling
// message to participants for which there is a Peer object, so the
// message may not be sent to participants without audio and video.
for (const sessionId in usersInCallMapping) {
if (!usersInCallMapping[sessionId].inCall) {
continue
} else if (sessionId === signaling.getSessionId()) {
continue
}

const message = {
to: sessionId,
roomType: 'video',
type: 'nickChanged',
payload: {
name: name,
},
}
signaling.emit('message', message)
}
})

// Local screen added.
webrtc.on('localScreenAdded', function(/* video */) {
const currentSessionId = signaling.getSessionId()
Expand Down

0 comments on commit bb9eb6f

Please sign in to comment.