Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2021-06-18 14:08:32 +0300
committerGitHub <noreply@github.com>2021-06-18 14:08:32 +0300
commit3c9e0e144fe47b27efdae22e62b8576024041adc (patch)
tree2d9e82dbd01465b6d85708b82d7b5697091f18f2
parentec9fd95d8e507113158ed1a5aa62d9db6f084bc9 (diff)
parent9e7967532b24bda5d6e0811fa0de211cae22167c (diff)
Merge pull request #5779 from nextcloud/add-ui-feedback-when-local-participant-is-not-connected
Add UI feedback when local participant is not connected
-rw-r--r--src/components/CallView/shared/LocalVideo.vue63
-rw-r--r--src/utils/webrtc/models/LocalCallParticipantModel.js51
-rw-r--r--src/utils/webrtc/webrtc.js39
3 files changed, 142 insertions, 11 deletions
diff --git a/src/components/CallView/shared/LocalVideo.vue b/src/components/CallView/shared/LocalVideo.vue
index 3e85038bd..aa6d40402 100644
--- a/src/components/CallView/shared/LocalVideo.vue
+++ b/src/components/CallView/shared/LocalVideo.vue
@@ -25,12 +25,16 @@
@mouseover="showShadow"
@mouseleave="hideShadow"
@click="handleClickVideo">
- <video v-show="localMediaModel.attributes.videoEnabled"
- id="localVideo"
- ref="video"
- disablePictureInPicture="true"
- :class="videoClass"
- class="video" />
+ <div v-show="localMediaModel.attributes.videoEnabled"
+ :class="videoWrapperClass"
+ class="videoWrapper">
+ <video
+ id="localVideo"
+ ref="video"
+ disablePictureInPicture="true"
+ :class="videoClass"
+ class="video" />
+ </div>
<div v-if="!localMediaModel.attributes.videoEnabled && !isSidebar" class="avatar-container">
<VideoBackground
v-if="isGrid || isStripe"
@@ -42,9 +46,10 @@
:disable-tooltip="true"
:show-user-status="false"
:user="userId"
- :display-name="displayName" />
+ :display-name="displayName"
+ :class="avatarClass" />
<div v-if="!userId"
- :class="avatarSizeClass"
+ :class="guestAvatarClass"
class="avatar guest">
{{ firstLetterOfGuestName }}
</div>
@@ -84,6 +89,7 @@ import {
} from '@nextcloud/dialogs'
import video from '../../../mixins/video.js'
import VideoBackground from './VideoBackground'
+import { ConnectionState } from '../../../utils/webrtc/models/CallParticipantModel'
export default {
@@ -139,8 +145,17 @@ export default {
return t('spreed', 'Back')
},
+ isNotConnected() {
+ // When there is no sender participant (when the MCU is not used, or
+ // if it is used but no peer object has been set yet) the local
+ // video is shown as connected.
+ return this.localCallParticipantModel.attributes.connectionState !== null
+ && this.localCallParticipantModel.attributes.connectionState !== ConnectionState.CONNECTED && this.localCallParticipantModel.attributes.connectionState !== ConnectionState.COMPLETED
+ },
+
videoContainerClass() {
return {
+ 'not-connected': this.isNotConnected,
speaking: this.localMediaModel.attributes.speaking,
'video-container-grid': this.isGrid,
'video-container-stripe': this.isStripe,
@@ -172,12 +187,26 @@ export default {
)
},
+ videoWrapperClass() {
+ return {
+ 'icon-loading': this.isNotConnected,
+ }
+ },
+
avatarSize() {
return this.useConstrainedLayout ? 64 : 128
},
- avatarSizeClass() {
- return 'avatar-' + this.avatarSize + 'px'
+ avatarClass() {
+ return {
+ 'icon-loading': this.isNotConnected,
+ }
+ },
+
+ guestAvatarClass() {
+ return Object.assign(this.avatarClass, {
+ ['avatar-' + this.avatarSize + 'px']: true,
+ })
},
localStreamVideoError() {
@@ -296,6 +325,13 @@ export default {
@include avatar-mixin(64px);
@include avatar-mixin(128px);
+.not-connected {
+ video,
+ .avatar-container {
+ opacity: 0.5;
+ }
+}
+
.video-container-grid {
position:relative;
height: 100%;
@@ -314,11 +350,18 @@ export default {
flex-direction: column;
}
+.videoWrapper,
.video {
height: 100%;
width: 100%;
}
+.videoWrapper.icon-loading:after {
+ height: 60px;
+ width: 60px;
+ margin: -32px 0 0 -32px;
+}
+
.video--fit {
/* Fit the frame */
object-fit: contain;
diff --git a/src/utils/webrtc/models/LocalCallParticipantModel.js b/src/utils/webrtc/models/LocalCallParticipantModel.js
index db28e32f0..90ff133f9 100644
--- a/src/utils/webrtc/models/LocalCallParticipantModel.js
+++ b/src/utils/webrtc/models/LocalCallParticipantModel.js
@@ -21,6 +21,8 @@
import store from '../../../store/index.js'
+import { ConnectionState } from './CallParticipantModel'
+
export default function LocalCallParticipantModel() {
this.attributes = {
@@ -28,11 +30,13 @@ export default function LocalCallParticipantModel() {
peer: null,
screenPeer: null,
guestName: null,
+ connectionState: null,
}
this._handlers = []
this._handleForcedMuteBound = this._handleForcedMute.bind(this)
+ this._handleExtendedIceConnectionStateChangeBound = this._handleExtendedIceConnectionStateChange.bind(this)
}
@@ -107,7 +111,22 @@ LocalCallParticipantModel.prototype = {
console.warn('Mismatch between stored peer ID and ID of given peer: ', this.get('peerId'), peer.id)
}
+ if (this.get('peer')) {
+ this.get('peer').off('extendedIceConnectionStateChange', this._handleExtendedIceConnectionStateChangeBound)
+ }
+
this.set('peer', peer)
+
+ if (!this.get('peer')) {
+ this.set('connectionState', null)
+
+ return
+ }
+
+ // Reset state that depends on the Peer object.
+ this._handleExtendedIceConnectionStateChange(this.get('peer').pc.iceConnectionState)
+
+ this.get('peer').on('extendedIceConnectionStateChange', this._handleExtendedIceConnectionStateChangeBound)
},
setScreenPeer(screenPeer) {
@@ -132,4 +151,36 @@ LocalCallParticipantModel.prototype = {
this._trigger('forcedMute')
},
+ _handleExtendedIceConnectionStateChange(extendedIceConnectionState) {
+ switch (extendedIceConnectionState) {
+ case 'new':
+ this.set('connectionState', ConnectionState.NEW)
+ break
+ case 'checking':
+ this.set('connectionState', ConnectionState.CHECKING)
+ break
+ case 'connected':
+ this.set('connectionState', ConnectionState.CONNECTED)
+ break
+ case 'completed':
+ this.set('connectionState', ConnectionState.COMPLETED)
+ break
+ case 'disconnected':
+ this.set('connectionState', ConnectionState.DISCONNECTED)
+ break
+ case 'disconnected-long':
+ this.set('connectionState', ConnectionState.DISCONNECTED_LONG)
+ break
+ case 'failed':
+ this.set('connectionState', ConnectionState.FAILED)
+ break
+ // 'failed-no-restart' is not emitted by own peer
+ case 'closed':
+ this.set('connectionState', ConnectionState.CLOSED)
+ break
+ default:
+ console.error('Unexpected (extended) ICE connection state: ', extendedIceConnectionState)
+ }
+ },
+
}
diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js
index 3e092ae29..399e45fdc 100644
--- a/src/utils/webrtc/webrtc.js
+++ b/src/utils/webrtc/webrtc.js
@@ -691,6 +691,43 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
})
}
+ function setHandlerForOwnIceConnectionStateChange(peer) {
+ peer.pc.addEventListener('iceconnectionstatechange', function() {
+ peer.emit('extendedIceConnectionStateChange', peer.pc.iceConnectionState)
+
+ switch (peer.pc.iceConnectionState) {
+ case 'checking':
+ console.debug('Connecting own peer...', peer)
+
+ break
+ case 'connected':
+ case 'completed':
+ console.debug('Connection established (own peer).', peer)
+
+ break
+ case 'disconnected':
+ console.debug('Disconnected (own peer).', peer)
+
+ setTimeout(function() {
+ if (peer.pc.iceConnectionState !== 'disconnected') {
+ return
+ }
+
+ peer.emit('extendedIceConnectionStateChange', 'disconnected-long')
+ }, 5000)
+ break
+ case 'failed':
+ console.debug('Connection failed (own peer).', peer)
+
+ break
+ case 'closed':
+ console.debug('Connection closed (own peer).', peer)
+
+ break
+ }
+ })
+ }
+
const forceReconnect = function(signaling, flags) {
if (ownPeer) {
webrtc.removePeers(ownPeer.id)
@@ -856,7 +893,7 @@ 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)
+ setHandlerForOwnIceConnectionStateChange(peer)
} else {
setHandlerForIceConnectionStateChange(peer)
}