Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable20.1] Stop sending the nick through data channels after some time #4726

Merged
merged 9 commits into from
Dec 9, 2020
4 changes: 2 additions & 2 deletions src/utils/webrtc/models/CallParticipantModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,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 @@ -238,15 +246,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 @@ -499,56 +509,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 @@ -716,8 +684,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 @@ -799,7 +765,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 @@ -974,12 +939,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 @@ -1012,6 +973,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