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.
This commit is contained in:
Saúl Ibarra Corretgé 2019-08-02 19:22:19 +02:00 committed by Saúl Ibarra Corretgé
parent 0f77cf9e0c
commit f3e7952e51
5 changed files with 58 additions and 32 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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 {

View File

@ -185,8 +185,6 @@ class ParticipantView extends Component<Props> {
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<Props> {
&& <VideoTrack
onPress = { onPress }
videoTrack = { videoTrack }
waitForVideoStarted = { waitForVideoStarted }
waitForVideoStarted = { false }
zOrder = { this.props.zOrder }
zoomEnabled = { this.props.zoomEnabled } /> }

View File

@ -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;
}
/**