From f3e7952e517fa0d44b302cc526ab531593d2bc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 2 Aug 2019 19:22:19 +0200 Subject: [PATCH] audio-only: implement initial "low bandwidth mode" It's an evolution of audio-only mode, where we also allow for receiving a remote screen-share. Diving deeper: this basically sets last N to 1 or 0 depending on the availability of a screen-share. --- lang/main.json | 10 ++-- modules/UI/videolayout/LargeVideoManager.js | 3 +- react/features/base/lastn/middleware.js | 25 +++++++++- .../components/ParticipantView.native.js | 4 +- react/features/base/participants/functions.js | 48 +++++++++++-------- 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/lang/main.json b/lang/main.json index 98024611d..39f4dff2e 100644 --- a/lang/main.json +++ b/lang/main.json @@ -24,7 +24,7 @@ "speaker": "Speaker" }, "audioOnly": { - "audioOnly": "Audio only" + "audioOnly": "Low bandwidth" }, "calendarSync": { "addMeetingURL": "Add a meeting link", @@ -584,8 +584,8 @@ "videoblur": "Toggle video blur" }, "addPeople": "Add people to your call", - "audioOnlyOff": "Disable audio only mode", - "audioOnlyOn": "Enable audio only mode", + "audioOnlyOff": "Disable low bandwidth mode", + "audioOnlyOn": "Enable low bandwidth mode", "audioRoute": "Select the sound device", "authenticate": "Authenticate", "callQuality": "Manage video quality", @@ -663,13 +663,13 @@ }, "videoStatus": { "audioOnly": "AUD", - "audioOnlyExpanded": "You are in audio only mode. This mode saves bandwidth but you won't see videos of others.", + "audioOnlyExpanded": "You are in low bandwidth mode. In this mode you will receive only audio and screen sharing.", "callQuality": "Video Quality", "hd": "HD", "hdTooltip": "Viewing high definition video", "highDefinition": "High definition", "labelTooiltipNoVideo": "No video", - "labelTooltipAudioOnly": "Audio-only mode enabled", + "labelTooltipAudioOnly": "Low bandwidth mode enabled", "ld": "LD", "ldTooltip": "Viewing low definition video", "lowDefinition": "Low definition", diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index 9a9171b74..6104e31d3 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -12,6 +12,7 @@ import { PresenceLabel } from '../../../react/features/presence-status'; const logger = require('jitsi-meet-logger').getLogger(__filename); +import { VIDEO_TYPE } from '../../../react/features/base/media'; import { JitsiParticipantConnectionStatus } from '../../../react/features/base/lib-jitsi-meet'; @@ -232,7 +233,7 @@ export default class LargeVideoManager { const showAvatar = isVideoContainer - && (APP.conference.isAudioOnly() || !isVideoRenderable); + && ((APP.conference.isAudioOnly() && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable); let promise; diff --git a/react/features/base/lastn/middleware.js b/react/features/base/lastn/middleware.js index d62c1e500..08d3c1422 100644 --- a/react/features/base/lastn/middleware.js +++ b/react/features/base/lastn/middleware.js @@ -3,10 +3,13 @@ import { getLogger } from 'jitsi-meet-logger'; import { SET_FILMSTRIP_ENABLED } from '../../filmstrip/actionTypes'; +import { SELECT_LARGE_VIDEO_PARTICIPANT } from '../../large-video/actionTypes'; import { APP_STATE_CHANGED } from '../../mobile/background/actionTypes'; +import { SCREEN_SHARE_PARTICIPANTS_UPDATED, SET_TILE_VIEW } from '../../video-layout/actionTypes'; -import { SET_AUDIO_ONLY } from '../audio-only'; +import { SET_AUDIO_ONLY } from '../audio-only/actionTypes'; import { CONFERENCE_JOINED } from '../conference/actionTypes'; +import { getParticipantById } from '../participants/functions'; import { MiddlewareRegistry } from '../redux'; declare var APP: Object; @@ -19,8 +22,11 @@ MiddlewareRegistry.register(store => next => action => { switch (action.type) { case APP_STATE_CHANGED: case CONFERENCE_JOINED: + case SCREEN_SHARE_PARTICIPANTS_UPDATED: + case SELECT_LARGE_VIDEO_PARTICIPANT: case SET_AUDIO_ONLY: case SET_FILMSTRIP_ENABLED: + case SET_TILE_VIEW: _updateLastN(store); break; } @@ -52,12 +58,27 @@ function _updateLastN({ getState }) { const defaultLastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN; let lastN = defaultLastN; - if (audioOnly || appState !== 'active') { + if (appState !== 'active') { lastN = 0; + } else if (audioOnly) { + const { screenShares, tileViewEnabled } = state['features/video-layout']; + const largeVideoParticipantId = state['features/large-video'].participantId; + const largeVideoParticipant + = largeVideoParticipantId ? getParticipantById(state, largeVideoParticipantId) : undefined; + + if (!tileViewEnabled && largeVideoParticipant && !largeVideoParticipant.local) { + lastN = (screenShares || []).includes(largeVideoParticipantId) ? 1 : 0; + } else { + lastN = 0; + } } else if (!filmStripEnabled) { lastN = 1; } + if (conference.getLastN() === lastN) { + return; + } + logger.info(`Setting last N to: ${lastN}`); try { diff --git a/react/features/base/participants/components/ParticipantView.native.js b/react/features/base/participants/components/ParticipantView.native.js index ed21ee3ba..1518ec6e1 100644 --- a/react/features/base/participants/components/ParticipantView.native.js +++ b/react/features/base/participants/components/ParticipantView.native.js @@ -185,8 +185,6 @@ class ParticipantView extends Component { tintStyle } = this.props; - const waitForVideoStarted = false; - // If the connection has problems, we will "tint" the video / avatar. const connectionProblem = connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE; @@ -216,7 +214,7 @@ class ParticipantView extends Component { && } diff --git a/react/features/base/participants/functions.js b/react/features/base/participants/functions.js index 066213979..1d89bd460 100644 --- a/react/features/base/participants/functions.js +++ b/react/features/base/participants/functions.js @@ -299,14 +299,14 @@ export function isLocalParticipantModerator( /** * Returns true if the video of the participant should be rendered. + * NOTE: This is currently only used on mobile. * * @param {Object|Function} stateful - Object or function that can be resolved * to the Redux state. * @param {string} id - The ID of the participant. * @returns {boolean} */ -export function shouldRenderParticipantVideo( - stateful: Object | Function, id: string) { +export function shouldRenderParticipantVideo(stateful: Object | Function, id: string) { const state = toState(stateful); const participant = getParticipantById(state, id); @@ -314,29 +314,35 @@ export function shouldRenderParticipantVideo( return false; } + /* First check if we have an unmuted video track. */ + const videoTrack + = getTrackByMediaTypeAndParticipant(state['features/base/tracks'], MEDIA_TYPE.VIDEO, id); + + if (!shouldRenderVideoTrack(videoTrack, /* waitForVideoStarted */ false)) { + return false; + } + + /* Then check if the participant connection is active. */ + const connectionStatus = participant.connectionStatus || JitsiParticipantConnectionStatus.ACTIVE; + + if (connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE) { + return false; + } + + /* Then check if audio-only mode is not active. */ const audioOnly = state['features/base/audio-only'].enabled; - const connectionStatus = participant.connectionStatus - || JitsiParticipantConnectionStatus.ACTIVE; - const videoTrack = getTrackByMediaTypeAndParticipant( - state['features/base/tracks'], - MEDIA_TYPE.VIDEO, - id); - // Is the video to be rendered? - // FIXME It's currently impossible to have true as the value of - // waitForVideoStarted because videoTrack's state videoStarted will be - // updated only after videoTrack is rendered. - // XXX Note that, unlike on web, we don't render video when the - // connection status is interrupted, this is because the renderer - // doesn't retain the last frame forever, so we would end up with a - // black screen. - const waitForVideoStarted = false; + if (!audioOnly) { + return true; + } - return !audioOnly - && (connectionStatus - === JitsiParticipantConnectionStatus.ACTIVE) - && shouldRenderVideoTrack(videoTrack, waitForVideoStarted); + /* Last, check if the participant is sharing their screen and they are on stage. */ + const screenShares = state['features/video-layout'].screenShares || []; + const largeVideoParticipantId = state['features/large-video'].participantId; + const participantIsInLargeVideoWithScreen + = participant.id === largeVideoParticipantId && screenShares.includes(participant.id); + return participantIsInLargeVideoWithScreen; } /**