@ -26,6 +26,18 @@ const VIDEO_RESOLUTION_POLL_INTERVAL = 2000;
* Manager for all Large containers .
* Manager for all Large containers .
* /
* /
export default class LargeVideoManager {
export default class LargeVideoManager {
/ * *
* Checks whether given container is a { @ link VIDEO _CONTAINER _TYPE } .
* FIXME currently this is a workaround for the problem where video type is
* mixed up with container type .
* @ param { string } containerType
* @ return { boolean }
* /
static isVideoContainer ( containerType ) {
return containerType === VIDEO _CONTAINER _TYPE
|| containerType === DESKTOP _CONTAINER _TYPE ;
}
constructor ( emitter ) {
constructor ( emitter ) {
/ * *
/ * *
* The map of < tt > LargeContainer < / t t > s w h e r e t h e k e y i s t h e v i d e o
* The map of < tt > LargeContainer < / t t > s w h e r e t h e k e y i s t h e v i d e o
@ -116,7 +128,8 @@ export default class LargeVideoManager {
this . enableLocalConnectionProblemFilter ( true ) ;
this . enableLocalConnectionProblemFilter ( true ) ;
this . _setLocalConnectionMessage ( "connection.RECONNECTING" ) ;
this . _setLocalConnectionMessage ( "connection.RECONNECTING" ) ;
// Show the message only if the video is currently being displayed
// Show the message only if the video is currently being displayed
this . showLocalConnectionMessage ( this . state === VIDEO _CONTAINER _TYPE ) ;
this . showLocalConnectionMessage (
LargeVideoManager . isVideoContainer ( this . state ) ) ;
}
}
/ * *
/ * *
@ -146,7 +159,12 @@ export default class LargeVideoManager {
preUpdate . then ( ( ) => {
preUpdate . then ( ( ) => {
const { id , stream , videoType , resolve } = this . newStreamData ;
const { id , stream , videoType , resolve } = this . newStreamData ;
const isVideoFromCamera = videoType === VIDEO _CONTAINER _TYPE ;
// FIXME this does not really make sense, because the videoType
// (camera or desktop) is a completely different thing than
// the video container type (Etherpad, SharedVideo, VideoContainer).
const isVideoContainer
= LargeVideoManager . isVideoContainer ( videoType ) ;
this . newStreamData = null ;
this . newStreamData = null ;
@ -158,34 +176,26 @@ export default class LargeVideoManager {
// change the avatar url on large
// change the avatar url on large
this . updateAvatar ( Avatar . getAvatarUrl ( id ) ) ;
this . updateAvatar ( Avatar . getAvatarUrl ( id ) ) ;
// FIXME that does not really make sense, because the videoType
// (camera or desktop) is a completely different thing than
// the video container type (Etherpad, SharedVideo, VideoContainer).
// ----------------------------------------------------------------
// If the container is VIDEO_CONTAINER_TYPE, we need to check
// its stream whether exist and is muted to set isVideoMuted
// in rest of the cases it is false
let showAvatar = isVideoFromCamera && ( ! stream || stream . isMuted ( ) ) ;
// If the user's connection is disrupted then the avatar will be
// If the user's connection is disrupted then the avatar will be
// displayed in case we have no video image cached. That is if
// displayed in case we have no video image cached. That is if
// there was a user switch (image is lost on stream detach) or if
// there was a user switch (image is lost on stream detach) or if
// the video was not rendered, before the connection has failed.
// the video was not rendered, before the connection has failed.
const isConnectionActive = this . _isConnectionActive ( id ) ;
const wasUsersImageCached
= ! isUserSwitch && container . wasVideoRendered ;
if ( isVideoFromCamera
const isVideoMuted = ! stream || stream . isMuted ( ) ;
&& ! isConnectionActive
&& ( isUserSwitch || ! container . wasVideoRendered ) ) {
const connectionStatus
showAvatar = true ;
= APP . conference . getParticipantConnectionStatus ( id ) ;
}
const isVideoRenderable
= ! isVideoMuted
// If audio only mode is enabled, always show the avatar for
&& ( APP . conference . isLocalId ( id )
// videos from another participant.
|| connectionStatus
if ( APP . conference . isAudioOnly ( )
=== ParticipantConnectionStatus . ACTIVE
&& ( isVideoFromCamera
|| wasUsersImageCached ) ;
|| videoType === DESKTOP _CONTAINER _TYPE ) ) {
showAvatar = true ;
let showAvatar
}
= isVideoContainer
&& ( APP . conference . isAudioOnly ( ) || ! isVideoRenderable ) ;
let promise ;
let promise ;
@ -208,28 +218,31 @@ export default class LargeVideoManager {
this . updateLargeVideoAudioLevel ( 0 ) ;
this . updateLargeVideoAudioLevel ( 0 ) ;
}
}
const isConnectionInterrupted
= APP . conference . getParticipantConnectionStatus ( id )
=== ParticipantConnectionStatus . INTERRUPTED ;
let messageKey = null ;
if ( isConnectionInterrupted ) {
messageKey = "connection.USER_CONNECTION_INTERRUPTED" ;
} else if ( connectionStatus
=== ParticipantConnectionStatus . INACTIVE ) {
messageKey = "connection.LOW_BANDWIDTH" ;
}
// Make sure no notification about remote failure is shown as
// Make sure no notification about remote failure is shown as
// its UI conflicts with the one for local connection interrupted.
// its UI conflicts with the one for local connection interrupted.
// For the purposes of UI indicators, audio only is considered as
// For the purposes of UI indicators, audio only is considered as
// an "active" connection.
// an "active" connection.
const isConnected
const overrideAndHide
= APP . conference . isAudioOnly ( )
= APP . conference . isAudioOnly ( )
|| APP . conference . isConnectionInterrupted ( )
|| APP . conference . isConnectionInterrupted ( ) ;
|| isConnectionActive ;
// when isHavingConnectivityIssues, state can be inactive,
// interrupted or restoring. We show different message for
// interrupted and the rest.
const isConnectionInterrupted =
APP . conference . getParticipantConnectionStatus ( id )
=== ParticipantConnectionStatus . INTERRUPTED ;
this . updateParticipantConnStatusIndication (
this . updateParticipantConnStatusIndication (
id ,
id ,
isConnected ,
! overrideAndHide && isConnectionInterrupted ,
( isConnectionInterrupted )
! overrideAndHide && messageKey !== null ,
? "connection.USER_CONNECTION_INTERRUPTED"
messageKey ) ;
: "connection.LOW_BANDWIDTH" ) ;
// resolve updateLargeVideo promise after everything is done
// resolve updateLargeVideo promise after everything is done
promise . then ( resolve ) ;
promise . then ( resolve ) ;
@ -265,18 +278,20 @@ export default class LargeVideoManager {
* shown on the large video area .
* shown on the large video area .
*
*
* @ param { string } id the id of remote participant ( MUC nickname )
* @ param { string } id the id of remote participant ( MUC nickname )
* @ param { boolean } isConnected true if the connection is active or false
* @ param { boolean } showProblemsIndication
* when the user is having connectivity issues .
* @ param { boolean } showMessage
* @ param { string } messageKey the i18n key of the message
* @ param { string } messageKey the i18n key of the message
*
*
* @ private
* @ private
* /
* /
updateParticipantConnStatusIndication ( id , isConnected , messageKey ) {
updateParticipantConnStatusIndication (
id , showProblemsIndication , showMessage , messageKey ) {
// Apply grey filter on the large video
// Apply grey filter on the large video
this . videoContainer . showRemoteConnectionProblemIndicator ( ! isConnected ) ;
this . videoContainer . showRemoteConnectionProblemIndicator (
showProblemsIndication ) ;
if ( isConnected ) {
if ( ! showMessage ) {
// Hide the message
// Hide the message
this . showRemoteConnectionMessage ( false ) ;
this . showRemoteConnectionMessage ( false ) ;
} else {
} else {
@ -289,7 +304,7 @@ export default class LargeVideoManager {
// Show it now only if the VideoContainer is on top
// Show it now only if the VideoContainer is on top
this . showRemoteConnectionMessage (
this . showRemoteConnectionMessage (
this . state === VIDEO _CONTAINER _TYPE ) ;
LargeVideoManager . isVideoContainer ( this . state ) ) ;
}
}
}
}
@ -412,15 +427,20 @@ export default class LargeVideoManager {
* Shows hides the "avatar" message which is to be displayed either in
* Shows hides the "avatar" message which is to be displayed either in
* the middle of the screen or below the avatar image .
* the middle of the screen or below the avatar image .
*
*
* @ param { null | boolean } show ( optional ) < tt > true < / t t > t o s h o w t h e a v a t a r
* @ param { null | boolean } [ show = null ] < tt > true < / t t > t o s h o w t h e a v a t a r
* message or < tt > false < / t t > t o h i d e i t . I f n o t p r o v i d e d t h e n t h e c o n n e c t i o n
* message or < tt > false < / t t > t o h i d e i t . I f n o t p r o v i d e d t h e n t h e c o n n e c t i o n
* status of the user currently on the large video will be obtained form
* status of the user currently on the large video will be obtained form
* "APP.conference" and the message will be displayed if the user ' s
* "APP.conference" and the message will be displayed if the user ' s
* connection is interrupted .
* connection is either interrupted or inactive .
* /
* /
showRemoteConnectionMessage ( show ) {
showRemoteConnectionMessage ( show ) {
if ( typeof show !== 'boolean' ) {
if ( typeof show !== 'boolean' ) {
show = ! this . _isConnectionActive ( this . id ) ;
const connStatus
= APP . conference . getParticipantConnectionStatus ( this . id ) ;
show = ! APP . conference . isLocalId ( this . id )
&& ( connStatus === ParticipantConnectionStatus . INTERRUPTED
|| connStatus === ParticipantConnectionStatus . INACTIVE ) ;
}
}
if ( show ) {
if ( show ) {
@ -526,7 +546,7 @@ export default class LargeVideoManager {
// FIXME when video is being replaced with other content we need to hide
// FIXME when video is being replaced with other content we need to hide
// companion icons/messages. It would be best if the container would
// companion icons/messages. It would be best if the container would
// be taking care of it by itself, but that is a bigger refactoring
// be taking care of it by itself, but that is a bigger refactoring
if ( this . state === VIDEO _CONTAINER _TYPE ) {
if ( LargeVideoManager . isVideoContainer ( this . state ) ) {
this . showWatermark ( false ) ;
this . showWatermark ( false ) ;
this . showLocalConnectionMessage ( false ) ;
this . showLocalConnectionMessage ( false ) ;
this . showRemoteConnectionMessage ( false ) ;
this . showRemoteConnectionMessage ( false ) ;
@ -537,7 +557,7 @@ export default class LargeVideoManager {
let container = this . getContainer ( type ) ;
let container = this . getContainer ( type ) ;
return container . show ( ) . then ( ( ) => {
return container . show ( ) . then ( ( ) => {
if ( type === VIDEO _CONTAINER _TYPE ) {
if ( LargeVideoManager . isVideoContainer ( type ) ) {
// FIXME when video appears on top of other content we need to
// FIXME when video appears on top of other content we need to
// show companion icons/messages. It would be best if
// show companion icons/messages. It would be best if
// the container would be taking care of it by itself, but that
// the container would be taking care of it by itself, but that