refactor(multi-stream) refactor virtual screenshare creation and support plan-b clients (#11445)
* fix(multi-stream) update selector to find ss track by videoType or mediaType * ref(multi-stream) move fake ss creation logic and support video type changed * refactor(multi-stream) decouple sending and receiving multiple screenshare streams * fix(multi-stream) fix receiver constraints with signaling and without multi-stream * fix(mutli-stream) ensure plan b original SS thumbnail displays avatar * fix(multi-stream) show fake SS for plan b sender * refactor(multi-stream) poc for moving SS creation to state listener * remove reference to fake SS creation * fix lint errors * rename to virtual screenshare participants * fix minor bugs * rename participant subscriber to specify web support only
This commit is contained in:
parent
b9c4d28dac
commit
d3fe246f61
|
@ -56,8 +56,7 @@ import {
|
|||
} from './react/features/base/conference';
|
||||
import {
|
||||
getReplaceParticipant,
|
||||
getMultipleVideoSupportFeatureFlag,
|
||||
getSourceNameSignalingFeatureFlag
|
||||
getMultipleVideoSendingSupportFeatureFlag
|
||||
} from './react/features/base/config/functions';
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
|
@ -97,7 +96,7 @@ import {
|
|||
dominantSpeakerChanged,
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
getScreenshareParticipantByOwnerId,
|
||||
getVirtualScreenshareParticipantByOwnerId,
|
||||
localParticipantAudioLevelChanged,
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
|
@ -1472,7 +1471,7 @@ export default {
|
|||
|
||||
// In the multi-stream mode, add the track to the conference if there is no existing track, replace it
|
||||
// otherwise.
|
||||
if (getMultipleVideoSupportFeatureFlag(state)) {
|
||||
if (getMultipleVideoSendingSupportFeatureFlag(state)) {
|
||||
const trackAction = oldTrack
|
||||
? replaceLocalTrack(oldTrack, newTrack, room)
|
||||
: addLocalTrack(newTrack);
|
||||
|
@ -2265,14 +2264,12 @@ export default {
|
|||
name: formattedDisplayName
|
||||
}));
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
const screenshareParticipantId = getScreenshareParticipantByOwnerId(state, id)?.id;
|
||||
const virtualScreenshareParticipantId = getVirtualScreenshareParticipantByOwnerId(state, id)?.id;
|
||||
|
||||
if (screenshareParticipantId) {
|
||||
APP.store.dispatch(
|
||||
screenshareParticipantDisplayNameChanged(screenshareParticipantId, formattedDisplayName)
|
||||
);
|
||||
}
|
||||
if (virtualScreenshareParticipantId) {
|
||||
APP.store.dispatch(
|
||||
screenshareParticipantDisplayNameChanged(virtualScreenshareParticipantId, formattedDisplayName)
|
||||
);
|
||||
}
|
||||
|
||||
APP.API.notifyDisplayNameChanged(id, {
|
||||
|
|
|
@ -9,7 +9,10 @@ import { Provider } from 'react-redux';
|
|||
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
|
||||
import { Avatar } from '../../../react/features/base/avatar';
|
||||
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../react/features/base/config';
|
||||
import {
|
||||
getMultipleVideoSupportFeatureFlag,
|
||||
getSourceNameSignalingFeatureFlag
|
||||
} from '../../../react/features/base/config';
|
||||
import { i18next } from '../../../react/features/base/i18n';
|
||||
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
|
@ -283,9 +286,18 @@ export default class LargeVideoManager {
|
|||
}
|
||||
|
||||
const isAudioOnly = APP.conference.isAudioOnly();
|
||||
|
||||
// Multi-stream is not supported on plan-b endpoints even if its is enabled via config.js. A virtual
|
||||
// screenshare tile is still created when a remote endpoint starts screenshare to keep the behavior
|
||||
// consistent and an avatar is displayed on the original participant thumbnail as long as screenshare is in
|
||||
// progress.
|
||||
const legacyScreenshare = getMultipleVideoSupportFeatureFlag(state)
|
||||
&& videoType === VIDEO_TYPE.DESKTOP
|
||||
&& !participant.isVirtualScreenshareParticipant;
|
||||
|
||||
const showAvatar
|
||||
= isVideoContainer
|
||||
&& ((isAudioOnly && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable);
|
||||
&& ((isAudioOnly && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable || legacyScreenshare);
|
||||
|
||||
let promise;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from '../../../react/features/base/participants';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant,
|
||||
getFakeScreenshareParticipantTrack
|
||||
getVirtualScreenshareParticipantTrack
|
||||
} from '../../../react/features/base/tracks';
|
||||
|
||||
import LargeVideoManager from './LargeVideoManager';
|
||||
|
@ -95,7 +95,7 @@ const VideoLayout = {
|
|||
return VIDEO_TYPE.CAMERA;
|
||||
}
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state) && participant?.isFakeScreenShareParticipant) {
|
||||
if (getSourceNameSignalingFeatureFlag(state) && participant?.isVirtualScreenshareParticipant) {
|
||||
return VIDEO_TYPE.DESKTOP;
|
||||
}
|
||||
|
||||
|
@ -190,8 +190,8 @@ const VideoLayout = {
|
|||
|
||||
let videoTrack;
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state) && participant?.isFakeScreenShareParticipant) {
|
||||
videoTrack = getFakeScreenshareParticipantTrack(tracks, id);
|
||||
if (getSourceNameSignalingFeatureFlag(state) && participant?.isVirtualScreenshareParticipant) {
|
||||
videoTrack = getVirtualScreenshareParticipantTrack(tracks, id);
|
||||
} else {
|
||||
videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen-share';
|
||||
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { setAudioOnly } from '../audio-only';
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../config/functions.any';
|
||||
import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
|
||||
import { JitsiConferenceErrors, JitsiTrackErrors } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, setScreenshareMuted, VIDEO_TYPE } from '../media';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
|
@ -51,7 +51,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
break;
|
||||
}
|
||||
case TOGGLE_SCREENSHARING: {
|
||||
getMultipleVideoSupportFeatureFlag(getState()) && _toggleScreenSharing(action, store);
|
||||
getMultipleVideoSendingSupportFeatureFlag(getState()) && _toggleScreenSharing(action, store);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -51,15 +51,24 @@ export function getMeetingRegion(state: Object) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Selector used to get the sendMultipleVideoStreams feature flag.
|
||||
* Selector for determining if receiving multiple stream support is enabled.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getMultipleVideoSupportFeatureFlag(state: Object) {
|
||||
return getFeatureFlag(state, FEATURE_FLAGS.MULTIPLE_VIDEO_STREAMS_SUPPORT)
|
||||
&& getSourceNameSignalingFeatureFlag(state)
|
||||
&& isUnifiedPlanEnabled(state);
|
||||
&& getSourceNameSignalingFeatureFlag(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for determining if sending multiple stream support is enabled.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getMultipleVideoSendingSupportFeatureFlag(state: Object) {
|
||||
return getMultipleVideoSupportFeatureFlag(state) && isUnifiedPlanEnabled(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,9 +6,9 @@ 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 {
|
||||
FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SET_TILE_VIEW
|
||||
SET_TILE_VIEW,
|
||||
VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||
} from '../../video-layout/actionTypes';
|
||||
import { SET_AUDIO_ONLY } from '../audio-only/actionTypes';
|
||||
import { CONFERENCE_JOINED } from '../conference/actionTypes';
|
||||
|
@ -95,7 +95,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
switch (action.type) {
|
||||
case APP_STATE_CHANGED:
|
||||
case CONFERENCE_JOINED:
|
||||
case FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED:
|
||||
case PARTICIPANT_JOINED:
|
||||
case PARTICIPANT_KICKED:
|
||||
case PARTICIPANT_LEFT:
|
||||
|
@ -104,6 +103,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case SET_AUDIO_ONLY:
|
||||
case SET_FILMSTRIP_ENABLED:
|
||||
case SET_TILE_VIEW:
|
||||
case VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED:
|
||||
_updateLastN(store);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import { isForceMuted } from '../../participants-pane/functions';
|
|||
import { isScreenMediaShared } from '../../screen-share/functions';
|
||||
import { SET_AUDIO_ONLY, setAudioOnly } from '../audio-only';
|
||||
import { isRoomValid, SET_ROOM } from '../conference';
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../config';
|
||||
import { getMultipleVideoSendingSupportFeatureFlag } from '../config';
|
||||
import { getLocalParticipant } from '../participants';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
import { getPropertyValue } from '../settings';
|
||||
|
@ -192,7 +192,7 @@ function _setAudioOnly({ dispatch, getState }, next, action) {
|
|||
|
||||
// Make sure we mute both the desktop and video tracks.
|
||||
dispatch(setVideoMuted(audioOnly, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY, ensureVideoTrack));
|
||||
if (getMultipleVideoSupportFeatureFlag(state)) {
|
||||
if (getMultipleVideoSendingSupportFeatureFlag(state)) {
|
||||
dispatch(setScreenshareMuted(audioOnly, MEDIA_TYPE.SCREENSHARE, SCREENSHARE_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
} else if (navigator.product !== 'ReactNative') {
|
||||
dispatch(setVideoMuted(audioOnly, MEDIA_TYPE.PRESENTER, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY, ensureVideoTrack));
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
} from './constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getVirtualScreenshareParticipantOwnerId,
|
||||
getNormalizedDisplayName,
|
||||
getParticipantDisplayName,
|
||||
getParticipantById
|
||||
|
@ -503,6 +504,36 @@ export function participantMutedUs(participant, track) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to create a virtual screenshare participant.
|
||||
*
|
||||
* @param {(string)} sourceName - JitsiTrack instance.
|
||||
* @param {(boolean)} local - JitsiTrack instance.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function createVirtualScreenshareParticipant(sourceName, local) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(sourceName);
|
||||
const owner = getParticipantById(state, ownerId);
|
||||
const ownerName = owner.name;
|
||||
|
||||
if (!ownerName) {
|
||||
logger.error(`Failed to create a screenshare participant for sourceName: ${sourceName}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(participantJoined({
|
||||
conference: state['features/base/conference'].conference,
|
||||
id: sourceName,
|
||||
isVirtualScreenshareParticipant: true,
|
||||
isLocalScreenShare: local,
|
||||
name: ownerName
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to signal that a participant had been kicked.
|
||||
*
|
||||
|
|
|
@ -6,21 +6,16 @@ import type { Store } from 'redux';
|
|||
import { i18next } from '../../base/i18n';
|
||||
import { isStageFilmstripAvailable } from '../../filmstrip/functions';
|
||||
import { GRAVATAR_BASE_URL, isCORSAvatarURL } from '../avatar';
|
||||
import { getSourceNameSignalingFeatureFlag } from '../config';
|
||||
import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../config';
|
||||
import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, shouldRenderVideoTrack } from '../media';
|
||||
import { toState } from '../redux';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../tracks';
|
||||
import { getScreenShareTrack, getTrackByMediaTypeAndParticipant } from '../tracks';
|
||||
import { createDeferred } from '../util';
|
||||
|
||||
import {
|
||||
JIGASI_PARTICIPANT_ICON,
|
||||
MAX_DISPLAY_NAME_LENGTH,
|
||||
PARTICIPANT_ROLE
|
||||
} from './constants';
|
||||
import { JIGASI_PARTICIPANT_ICON, MAX_DISPLAY_NAME_LENGTH, PARTICIPANT_ROLE } from './constants';
|
||||
import { preloadImage } from './preloadImage';
|
||||
|
||||
|
||||
/**
|
||||
* Temp structures for avatar urls to be checked/preloaded.
|
||||
*/
|
||||
|
@ -114,12 +109,16 @@ export function getLocalScreenShareParticipant(stateful: Object | Function) {
|
|||
* @param {string} id - The owner ID of the screenshare participant to retrieve.
|
||||
* @returns {(Participant|undefined)}
|
||||
*/
|
||||
export function getScreenshareParticipantByOwnerId(stateful: Object | Function, id: string) {
|
||||
const track = getTrackByMediaTypeAndParticipant(
|
||||
toState(stateful)['features/base/tracks'], MEDIA_TYPE.SCREENSHARE, id
|
||||
);
|
||||
export function getVirtualScreenshareParticipantByOwnerId(stateful: Object | Function, id: string) {
|
||||
const state = toState(stateful);
|
||||
|
||||
return getParticipantById(stateful, track?.jitsiTrack.getSourceName());
|
||||
if (getMultipleVideoSupportFeatureFlag(state)) {
|
||||
const track = getScreenShareTrack(state['features/base/tracks'], id);
|
||||
|
||||
return getParticipantById(stateful, track?.jitsiTrack.getSourceName());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,11 +185,11 @@ export function getParticipantCount(stateful: Object | Function) {
|
|||
local,
|
||||
remote,
|
||||
fakeParticipants,
|
||||
sortedRemoteFakeScreenShareParticipants
|
||||
sortedRemoteVirtualScreenshareParticipants
|
||||
} = state['features/base/participants'];
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
return remote.size - fakeParticipants.size - sortedRemoteFakeScreenShareParticipants.size + (local ? 1 : 0);
|
||||
return remote.size - fakeParticipants.size - sortedRemoteVirtualScreenshareParticipants.size + (local ? 1 : 0);
|
||||
}
|
||||
|
||||
return remote.size - fakeParticipants.size + (local ? 1 : 0);
|
||||
|
@ -198,13 +197,13 @@ export function getParticipantCount(stateful: Object | Function) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns participant ID of the owner of a fake screenshare participant.
|
||||
* Returns participant ID of the owner of a virtual screenshare participant.
|
||||
*
|
||||
* @param {string} id - The ID of the fake screenshare participant.
|
||||
* @param {string} id - The ID of the virtual screenshare participant.
|
||||
* @private
|
||||
* @returns {(string|undefined)}
|
||||
*/
|
||||
export function getFakeScreenShareParticipantOwnerId(id: string) {
|
||||
export function getVirtualScreenshareParticipantOwnerId(id: string) {
|
||||
return id.split('-')[0];
|
||||
}
|
||||
|
||||
|
@ -232,7 +231,7 @@ export function getRemoteParticipantCount(stateful: Object | Function) {
|
|||
const state = toState(stateful)['features/base/participants'];
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
return state.remote.size - state.sortedRemoteFakeScreenShareParticipants.size;
|
||||
return state.remote.size - state.sortedRemoteVirtualScreenshareParticipants.size;
|
||||
}
|
||||
|
||||
return state.remote.size;
|
||||
|
@ -274,7 +273,7 @@ export function getParticipantDisplayName(stateful: Object | Function, id: strin
|
|||
} = toState(stateful)['features/base/config'];
|
||||
|
||||
if (participant) {
|
||||
if (participant.isFakeScreenShareParticipant) {
|
||||
if (participant.isVirtualScreenshareParticipant) {
|
||||
return getScreenshareParticipantDisplayName(stateful, id);
|
||||
}
|
||||
|
||||
|
@ -299,7 +298,7 @@ export function getParticipantDisplayName(stateful: Object | Function, id: strin
|
|||
* @returns {string}
|
||||
*/
|
||||
export function getScreenshareParticipantDisplayName(stateful: Object | Function, id: string) {
|
||||
const owner = getParticipantById(stateful, getFakeScreenShareParticipantOwnerId(id));
|
||||
const owner = getParticipantById(stateful, getVirtualScreenshareParticipantOwnerId(id));
|
||||
const name = owner.name;
|
||||
|
||||
return i18next.t('screenshareDisplayName', { name });
|
||||
|
|
|
@ -69,6 +69,7 @@ import {
|
|||
isLocalParticipantModerator
|
||||
} from './functions';
|
||||
import { PARTICIPANT_JOINED_FILE, PARTICIPANT_LEFT_FILE } from './sounds';
|
||||
import './subscriber';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
|
@ -210,19 +211,19 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
|
||||
case PARTICIPANT_JOINED: {
|
||||
const { isFakeScreenShareParticipant } = action.participant;
|
||||
const { isVirtualScreenshareParticipant } = action.participant;
|
||||
|
||||
// Do not play sounds when a fake participant tile is created for screenshare.
|
||||
!isFakeScreenShareParticipant && _maybePlaySounds(store, action);
|
||||
// Do not play sounds when a virtual participant tile is created for screenshare.
|
||||
!isVirtualScreenshareParticipant && _maybePlaySounds(store, action);
|
||||
|
||||
return _participantJoinedOrUpdated(store, next, action);
|
||||
}
|
||||
|
||||
case PARTICIPANT_LEFT: {
|
||||
const { isFakeScreenShareParticipant } = action.participant;
|
||||
const { isVirtualScreenshareParticipant } = action.participant;
|
||||
|
||||
// Do not play sounds when a tile for screenshare is removed.
|
||||
!isFakeScreenShareParticipant && _maybePlaySounds(store, action);
|
||||
!isVirtualScreenshareParticipant && _maybePlaySounds(store, action);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ const DEFAULT_STATE = {
|
|||
pinnedParticipant: undefined,
|
||||
raisedHandsQueue: [],
|
||||
remote: new Map(),
|
||||
sortedRemoteFakeScreenShareParticipants: new Map(),
|
||||
sortedRemoteVirtualScreenshareParticipants: new Map(),
|
||||
sortedRemoteParticipants: new Map(),
|
||||
sortedRemoteScreenshares: new Map(),
|
||||
speakersList: new Map()
|
||||
|
@ -213,15 +213,15 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
|||
case SCREENSHARE_PARTICIPANT_NAME_CHANGED: {
|
||||
const { id, name } = action;
|
||||
|
||||
if (state.sortedRemoteFakeScreenShareParticipants.has(id)) {
|
||||
state.sortedRemoteFakeScreenShareParticipants.delete(id);
|
||||
if (state.sortedRemoteVirtualScreenshareParticipants.has(id)) {
|
||||
state.sortedRemoteVirtualScreenshareParticipants.delete(id);
|
||||
|
||||
const sortedRemoteFakeScreenShareParticipants = [ ...state.sortedRemoteFakeScreenShareParticipants ];
|
||||
const sortedRemoteVirtualScreenshareParticipants = [ ...state.sortedRemoteVirtualScreenshareParticipants ];
|
||||
|
||||
sortedRemoteFakeScreenShareParticipants.push([ id, name ]);
|
||||
sortedRemoteFakeScreenShareParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
sortedRemoteVirtualScreenshareParticipants.push([ id, name ]);
|
||||
sortedRemoteVirtualScreenshareParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
state.sortedRemoteFakeScreenShareParticipants = new Map(sortedRemoteFakeScreenShareParticipants);
|
||||
state.sortedRemoteVirtualScreenshareParticipants = new Map(sortedRemoteVirtualScreenshareParticipants);
|
||||
}
|
||||
|
||||
return { ...state };
|
||||
|
@ -229,7 +229,14 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
|||
|
||||
case PARTICIPANT_JOINED: {
|
||||
const participant = _participantJoined(action);
|
||||
const { id, isFakeParticipant, isFakeScreenShareParticipant, isLocalScreenShare, name, pinned } = participant;
|
||||
const {
|
||||
id,
|
||||
isFakeParticipant,
|
||||
isLocalScreenShare,
|
||||
isVirtualScreenshareParticipant,
|
||||
name,
|
||||
pinned
|
||||
} = participant;
|
||||
const { pinnedParticipant, dominantSpeaker } = state;
|
||||
|
||||
if (pinned) {
|
||||
|
@ -282,13 +289,13 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
|||
// The sort order of participants is preserved since Map remembers the original insertion order of the keys.
|
||||
state.sortedRemoteParticipants = new Map(sortedRemoteParticipants);
|
||||
|
||||
if (isFakeScreenShareParticipant) {
|
||||
const sortedRemoteFakeScreenShareParticipants = [ ...state.sortedRemoteFakeScreenShareParticipants ];
|
||||
if (isVirtualScreenshareParticipant) {
|
||||
const sortedRemoteVirtualScreenshareParticipants = [ ...state.sortedRemoteVirtualScreenshareParticipants ];
|
||||
|
||||
sortedRemoteFakeScreenShareParticipants.push([ id, name ]);
|
||||
sortedRemoteFakeScreenShareParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
sortedRemoteVirtualScreenshareParticipants.push([ id, name ]);
|
||||
sortedRemoteVirtualScreenshareParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
state.sortedRemoteFakeScreenShareParticipants = new Map(sortedRemoteFakeScreenShareParticipants);
|
||||
state.sortedRemoteVirtualScreenshareParticipants = new Map(sortedRemoteVirtualScreenshareParticipants);
|
||||
}
|
||||
if (isFakeParticipant) {
|
||||
state.fakeParticipants.set(id, participant);
|
||||
|
@ -306,7 +313,7 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
|||
const { conference, id } = action.participant;
|
||||
const {
|
||||
fakeParticipants,
|
||||
sortedRemoteFakeScreenShareParticipants,
|
||||
sortedRemoteVirtualScreenshareParticipants,
|
||||
remote,
|
||||
local,
|
||||
localScreenShare,
|
||||
|
@ -372,9 +379,9 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
|||
fakeParticipants.delete(id);
|
||||
}
|
||||
|
||||
if (sortedRemoteFakeScreenShareParticipants.has(id)) {
|
||||
sortedRemoteFakeScreenShareParticipants.delete(id);
|
||||
state.sortedRemoteFakeScreenShareParticipants = new Map(sortedRemoteFakeScreenShareParticipants);
|
||||
if (sortedRemoteVirtualScreenshareParticipants.has(id)) {
|
||||
sortedRemoteVirtualScreenshareParticipants.delete(id);
|
||||
state.sortedRemoteVirtualScreenshareParticipants = new Map(sortedRemoteVirtualScreenshareParticipants);
|
||||
}
|
||||
|
||||
return { ...state };
|
||||
|
@ -500,7 +507,7 @@ function _participantJoined({ participant }) {
|
|||
dominantSpeaker,
|
||||
email,
|
||||
isFakeParticipant,
|
||||
isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant,
|
||||
isLocalScreenShare,
|
||||
isReplacing,
|
||||
isJigasi,
|
||||
|
@ -534,7 +541,7 @@ function _participantJoined({ participant }) {
|
|||
email,
|
||||
id,
|
||||
isFakeParticipant,
|
||||
isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant,
|
||||
isLocalScreenShare,
|
||||
isReplacing,
|
||||
isJigasi,
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getCurrentConference } from '../conference';
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../config';
|
||||
import { StateListenerRegistry } from '../redux';
|
||||
|
||||
import { createVirtualScreenshareParticipant, participantLeft } from './actions';
|
||||
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/tracks'],
|
||||
/* listener */(tracks, store) => _updateScreenshareParticipants(store)
|
||||
);
|
||||
|
||||
/**
|
||||
* Handles creating and removing virtual screenshare participants.
|
||||
*
|
||||
* @param {*} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _updateScreenshareParticipants({ getState, dispatch }) {
|
||||
const state = getState();
|
||||
|
||||
if (!getMultipleVideoSupportFeatureFlag(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conference = getCurrentConference(state);
|
||||
const tracks = state['features/base/tracks'];
|
||||
const { sortedRemoteVirtualScreenshareParticipants, localScreenShare } = state['features/base/participants'];
|
||||
const previousScreenshareSourceNames = [ ...sortedRemoteVirtualScreenshareParticipants.keys() ];
|
||||
|
||||
let newLocalSceenshareSourceName;
|
||||
|
||||
const currentScreenshareSourceNames = tracks.reduce((acc, track) => {
|
||||
if (track.videoType === 'desktop' && !track.jitsiTrack.isMuted()) {
|
||||
const sourceName = track.jitsiTrack.getSourceName();
|
||||
|
||||
if (track.local) {
|
||||
newLocalSceenshareSourceName = sourceName;
|
||||
} else {
|
||||
acc.push(sourceName);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (!localScreenShare && newLocalSceenshareSourceName) {
|
||||
dispatch(createVirtualScreenshareParticipant(newLocalSceenshareSourceName, true));
|
||||
}
|
||||
|
||||
if (localScreenShare && !newLocalSceenshareSourceName) {
|
||||
dispatch(participantLeft(localScreenShare.id, conference));
|
||||
}
|
||||
|
||||
const removedScreenshareSourceNames = _.difference(previousScreenshareSourceNames, currentScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = _.difference(currentScreenshareSourceNames, previousScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference)));
|
||||
}
|
||||
|
||||
if (addedScreenshareSourceNames.length) {
|
||||
addedScreenshareSourceNames.forEach(id => dispatch(createVirtualScreenshareParticipant(id, false)));
|
||||
|
||||
}
|
||||
}
|
|
@ -107,18 +107,6 @@ export const TRACK_STOPPED = 'TRACK_STOPPED';
|
|||
*/
|
||||
export const TRACK_UPDATED = 'TRACK_UPDATED';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when a screenshare track's muted property were updated.
|
||||
*
|
||||
* {
|
||||
* type: SCREENSHARE_TRACK_MUTED_UPDATED,
|
||||
* track: Track,
|
||||
* muted: Boolean
|
||||
*
|
||||
* }
|
||||
*/
|
||||
export const SCREENSHARE_TRACK_MUTED_UPDATED = 'SCREENSHARE_TRACK_MUTED_UPDATED';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when a local track starts being created
|
||||
* via a WebRTC {@code getUserMedia} call. The action's payload includes an
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from '../../analytics';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification, showNotification } from '../../notifications';
|
||||
import { getCurrentConference } from '../conference';
|
||||
import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../config';
|
||||
import { getMultipleVideoSendingSupportFeatureFlag } from '../config';
|
||||
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
|
||||
import { createLocalTrack } from '../lib-jitsi-meet/functions';
|
||||
import {
|
||||
|
@ -22,7 +22,6 @@ import { getLocalParticipant } from '../participants';
|
|||
import { updateSettings } from '../settings';
|
||||
|
||||
import {
|
||||
SCREENSHARE_TRACK_MUTED_UPDATED,
|
||||
SET_NO_SRC_DATA_NOTIFICATION_UID,
|
||||
TOGGLE_SCREENSHARING,
|
||||
TRACK_ADDED,
|
||||
|
@ -60,7 +59,7 @@ export function addLocalTrack(newTrack) {
|
|||
}
|
||||
|
||||
const setMuted = newTrack.isVideoTrack()
|
||||
? getMultipleVideoSupportFeatureFlag(getState())
|
||||
? getMultipleVideoSendingSupportFeatureFlag(getState())
|
||||
&& newTrack.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
? setScreenshareMuted
|
||||
: setVideoMuted
|
||||
|
@ -371,7 +370,8 @@ function replaceStoredTracks(oldTrack, newTrack) {
|
|||
// state. If this is not done, the current mute state of the app will be reflected on the track,
|
||||
// not vice-versa.
|
||||
const setMuted = newTrack.isVideoTrack()
|
||||
? getMultipleVideoSupportFeatureFlag(getState()) && newTrack.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
? getMultipleVideoSendingSupportFeatureFlag(getState())
|
||||
&& newTrack.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
? setScreenshareMuted
|
||||
: setVideoMuted
|
||||
: setAudioMuted;
|
||||
|
@ -397,19 +397,15 @@ export function trackAdded(track) {
|
|||
return async (dispatch, getState) => {
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_MUTE_CHANGED,
|
||||
() => {
|
||||
if (getSourceNameSignalingFeatureFlag(getState()) && track.getVideoType() === VIDEO_TYPE.DESKTOP) {
|
||||
dispatch(screenshareTrackMutedChanged(track));
|
||||
}
|
||||
dispatch(trackMutedChanged(track));
|
||||
});
|
||||
() => dispatch(trackMutedChanged(track)));
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,
|
||||
type => dispatch(trackVideoTypeChanged(track, type)));
|
||||
|
||||
// participantId
|
||||
const local = track.isLocal();
|
||||
const mediaType = getMultipleVideoSupportFeatureFlag(getState()) && track.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
const mediaType = getMultipleVideoSendingSupportFeatureFlag(getState())
|
||||
&& track.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
? MEDIA_TYPE.SCREENSHARE
|
||||
: track.getType();
|
||||
let isReceivingData, noDataFromSourceNotificationInfo, participantId;
|
||||
|
@ -498,24 +494,6 @@ export function trackMutedChanged(track) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action for when a screenshare track's muted state has been signaled to be changed.
|
||||
*
|
||||
* @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
|
||||
* @returns {{
|
||||
* type: TRACK_UPDATED,
|
||||
* track: Track,
|
||||
* muted: boolean
|
||||
* }}
|
||||
*/
|
||||
export function screenshareTrackMutedChanged(track) {
|
||||
return {
|
||||
type: SCREENSHARE_TRACK_MUTED_UPDATED,
|
||||
track: { jitsiTrack: track },
|
||||
muted: track.isMuted()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action for when a track's muted state change action has failed. This could happen because of
|
||||
* {@code getUserMedia} errors during unmute or replace track errors at the peerconnection level.
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
/* global APP */
|
||||
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../config/functions.any';
|
||||
import {
|
||||
getMultipleVideoSendingSupportFeatureFlag,
|
||||
getMultipleVideoSupportFeatureFlag
|
||||
} from '../config/functions.any';
|
||||
import { isMobileBrowser } from '../environment/utils';
|
||||
import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE, setAudioMuted } from '../media';
|
||||
import { getFakeScreenShareParticipantOwnerId } from '../participants';
|
||||
import { getVirtualScreenshareParticipantOwnerId } from '../participants';
|
||||
import { toState } from '../redux';
|
||||
import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
|
@ -426,18 +429,11 @@ export function getVideoTrackByParticipant(
|
|||
return;
|
||||
}
|
||||
|
||||
let participantId;
|
||||
let mediaType;
|
||||
|
||||
if (participant?.isFakeScreenShareParticipant) {
|
||||
participantId = getFakeScreenShareParticipantOwnerId(participant.id);
|
||||
mediaType = MEDIA_TYPE.SCREENSHARE;
|
||||
} else {
|
||||
participantId = participant.id;
|
||||
mediaType = MEDIA_TYPE.VIDEO;
|
||||
if (participant?.isVirtualScreenshareParticipant) {
|
||||
return getVirtualScreenshareParticipantTrack(tracks, participant.id);
|
||||
}
|
||||
|
||||
return getTrackByMediaTypeAndParticipant(tracks, mediaType, participantId);
|
||||
return getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participant.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -458,16 +454,54 @@ export function getTrackByMediaTypeAndParticipant(
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns track of given fakeScreenshareParticipantId.
|
||||
* Returns screenshare track of given virtualScreenshareParticipantId.
|
||||
*
|
||||
* @param {Track[]} tracks - List of all tracks.
|
||||
* @param {string} fakeScreenshareParticipantId - Fake Screenshare Participant ID.
|
||||
* @param {string} virtualScreenshareParticipantId - Virtual Screenshare Participant ID.
|
||||
* @returns {(Track|undefined)}
|
||||
*/
|
||||
export function getFakeScreenshareParticipantTrack(tracks, fakeScreenshareParticipantId) {
|
||||
const participantId = getFakeScreenShareParticipantOwnerId(fakeScreenshareParticipantId);
|
||||
export function getVirtualScreenshareParticipantTrack(tracks, virtualScreenshareParticipantId) {
|
||||
const ownderId = getVirtualScreenshareParticipantOwnerId(virtualScreenshareParticipantId);
|
||||
|
||||
return getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.SCREENSHARE, participantId);
|
||||
return getScreenShareTrack(tracks, ownderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns track source names of given screen share participant ids.
|
||||
*
|
||||
* @param {Object} state - The entire redux state.
|
||||
* @param {string[]} screenShareParticipantIds - Participant ID.
|
||||
* @returns {(string[])}
|
||||
*/
|
||||
export function getRemoteScreenSharesSourceNames(state, screenShareParticipantIds = []) {
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
return getMultipleVideoSupportFeatureFlag(state)
|
||||
? screenShareParticipantIds
|
||||
: screenShareParticipantIds.reduce((acc, id) => {
|
||||
const sourceName = getScreenShareTrack(tracks, id)?.jitsiTrack.getSourceName();
|
||||
|
||||
if (sourceName) {
|
||||
acc.push(sourceName);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns screenshare track of given owner ID.
|
||||
*
|
||||
* @param {Track[]} tracks - List of all tracks.
|
||||
* @param {string} ownerId - Screenshare track owner ID.
|
||||
* @returns {(Track|undefined)}
|
||||
*/
|
||||
export function getScreenShareTrack(tracks, ownerId) {
|
||||
return tracks.find(
|
||||
t => Boolean(t.jitsiTrack)
|
||||
&& t.participantId === ownerId
|
||||
&& (t.mediaType === MEDIA_TYPE.SCREENSHARE || t.videoType === VIDEO_TYPE.DESKTOP)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -610,7 +644,7 @@ export function setTrackMuted(track, muted, state) {
|
|||
// browser's 'Stop sharing' button, the local stream is stopped before the inactive stream handler is fired.
|
||||
// We still need to proceed here and remove the track from the peerconnection.
|
||||
if (track.isMuted() === muted
|
||||
&& !(track.getVideoType() === VIDEO_TYPE.DESKTOP && getMultipleVideoSupportFeatureFlag(state))) {
|
||||
&& !(track.getVideoType() === VIDEO_TYPE.DESKTOP && getMultipleVideoSendingSupportFeatureFlag(state))) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { _RESET_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes';
|
|||
import { hideNotification, isModerationNotificationDisplayed } from '../../notifications';
|
||||
import { isPrejoinPageVisible } from '../../prejoin/functions';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../config';
|
||||
import { getMultipleVideoSendingSupportFeatureFlag } from '../config';
|
||||
import { getAvailableDevices } from '../devices/actions';
|
||||
import {
|
||||
CAMERA_FACING_MODE,
|
||||
|
@ -25,11 +25,9 @@ import {
|
|||
setScreenshareMuted,
|
||||
SCREENSHARE_MUTISM_AUTHORITY
|
||||
} from '../media';
|
||||
import { participantLeft, participantJoined, getParticipantById } from '../participants';
|
||||
import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
|
||||
|
||||
import {
|
||||
SCREENSHARE_TRACK_MUTED_UPDATED,
|
||||
TOGGLE_SCREENSHARING,
|
||||
TRACK_ADDED,
|
||||
TRACK_MUTE_UNMUTE_FAILED,
|
||||
|
@ -53,7 +51,6 @@ import {
|
|||
isUserInteractionRequiredForUnmute,
|
||||
setTrackMuted
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
import './subscriber';
|
||||
|
||||
|
@ -70,8 +67,7 @@ declare var APP: Object;
|
|||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case TRACK_ADDED: {
|
||||
const state = store.getState();
|
||||
const { jitsiTrack, local } = action.track;
|
||||
const { local } = action.track;
|
||||
|
||||
// The devices list needs to be refreshed when no initial video permissions
|
||||
// were granted and a local video track is added by umuting the video.
|
||||
|
@ -79,23 +75,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
store.dispatch(getAvailableDevices());
|
||||
}
|
||||
|
||||
// Call next before the creation of a fake screenshare participant to ensure a video track is available when
|
||||
// the participant is auto pinned.
|
||||
const result = next(action);
|
||||
|
||||
// The TRACK_ADDED action is dispatched when a presenter starts a screenshare. Do not create a local fake
|
||||
// screenshare participant when multiple stream is not enabled.
|
||||
const skipCreateFakeScreenShareParticipant = local && !getMultipleVideoSupportFeatureFlag(state);
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)
|
||||
&& jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
&& !jitsiTrack.isMuted()
|
||||
&& !skipCreateFakeScreenShareParticipant
|
||||
) {
|
||||
createFakeScreenShareParticipant(store, action);
|
||||
}
|
||||
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
case TRACK_NO_DATA_FROM_SOURCE: {
|
||||
const result = next(action);
|
||||
|
@ -105,39 +85,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
return result;
|
||||
}
|
||||
|
||||
case SCREENSHARE_TRACK_MUTED_UPDATED: {
|
||||
const state = store.getState();
|
||||
|
||||
if (!getSourceNameSignalingFeatureFlag(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { track, muted } = action;
|
||||
|
||||
if (muted) {
|
||||
const conference = getCurrentConference(state);
|
||||
const participantId = track?.jitsiTrack.getSourceName();
|
||||
|
||||
store.dispatch(participantLeft(participantId, conference));
|
||||
}
|
||||
|
||||
if (!muted) {
|
||||
createFakeScreenShareParticipant(store, action);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TRACK_REMOVED: {
|
||||
const state = store.getState();
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state) && action.track.jitsiTrack.videoType === VIDEO_TYPE.DESKTOP) {
|
||||
const conference = getCurrentConference(state);
|
||||
const participantId = action.track.jitsiTrack.getSourceName();
|
||||
|
||||
store.dispatch(participantLeft(participantId, conference));
|
||||
}
|
||||
|
||||
_removeNoDataFromSourceNotification(store, action.track);
|
||||
break;
|
||||
}
|
||||
|
@ -223,7 +171,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
const { enabled, audioOnly, ignoreDidHaveVideo } = action;
|
||||
|
||||
if (!getMultipleVideoSupportFeatureFlag(store.getState())) {
|
||||
if (!getMultipleVideoSendingSupportFeatureFlag(store.getState())) {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING,
|
||||
{
|
||||
enabled,
|
||||
|
@ -241,7 +189,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
if (typeof APP !== 'undefined') {
|
||||
if (isVideoTrack && jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
&& getMultipleVideoSupportFeatureFlag(store.getState())) {
|
||||
&& getMultipleVideoSendingSupportFeatureFlag(store.getState())) {
|
||||
store.dispatch(setScreenshareMuted(!muted));
|
||||
} else if (isVideoTrack) {
|
||||
APP.conference.setVideoMuteStatus();
|
||||
|
@ -256,7 +204,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
const { jitsiTrack } = action.track;
|
||||
|
||||
if (typeof APP !== 'undefined'
|
||||
&& getMultipleVideoSupportFeatureFlag(store.getState())
|
||||
&& getMultipleVideoSendingSupportFeatureFlag(store.getState())
|
||||
&& jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP) {
|
||||
store.dispatch(toggleScreensharing(false));
|
||||
}
|
||||
|
@ -286,7 +234,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
} else if (jitsiTrack.isLocal() && !(jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP)) {
|
||||
APP.conference.setVideoMuteStatus();
|
||||
} else if (jitsiTrack.isLocal() && muted && jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP) {
|
||||
!getMultipleVideoSupportFeatureFlag(state)
|
||||
!getMultipleVideoSendingSupportFeatureFlag(state)
|
||||
&& store.dispatch(toggleScreensharing(false, false, true));
|
||||
} else {
|
||||
APP.UI.setVideoMuted(participantID);
|
||||
|
@ -384,32 +332,6 @@ function _handleNoDataFromSourceErrors(store, action) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fake participant for screen share using the track's source name as the participant id.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified action is dispatched.
|
||||
* @param {Action} action - The redux action dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function createFakeScreenShareParticipant({ dispatch, getState }, { track }) {
|
||||
const state = getState();
|
||||
const participantId = track.jitsiTrack?.getParticipantId?.();
|
||||
const participant = getParticipantById(state, participantId);
|
||||
|
||||
if (participant.name) {
|
||||
dispatch(participantJoined({
|
||||
conference: state['features/base/conference'].conference,
|
||||
id: track.jitsiTrack.getSourceName(),
|
||||
isFakeScreenShareParticipant: true,
|
||||
isLocalScreenShare: track?.jitsiTrack.isLocal(),
|
||||
name: participant.name
|
||||
}));
|
||||
} else {
|
||||
logger.error(`Failed to create a screenshare participant for participantId: ${participantId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local track associated with a specific {@code MEDIA_TYPE} in a
|
||||
* specific redux store.
|
||||
|
@ -472,7 +394,7 @@ async function _setMuted(store, { ensureTrack, authority, muted }, mediaType: ME
|
|||
const state = getState();
|
||||
|
||||
if (mediaType === MEDIA_TYPE.SCREENSHARE
|
||||
&& getMultipleVideoSupportFeatureFlag(state)
|
||||
&& getMultipleVideoSendingSupportFeatureFlag(state)
|
||||
&& !muted) {
|
||||
return;
|
||||
}
|
||||
|
@ -487,8 +409,9 @@ async function _setMuted(store, { ensureTrack, authority, muted }, mediaType: ME
|
|||
|
||||
// Screenshare cannot be unmuted using the video mute button unless it is muted by audioOnly in the legacy
|
||||
// screensharing mode.
|
||||
if (jitsiTrack
|
||||
&& (jitsiTrack.videoType !== 'desktop' || isAudioOnly || getMultipleVideoSupportFeatureFlag(state))) {
|
||||
if (jitsiTrack && (
|
||||
jitsiTrack.videoType !== 'desktop' || isAudioOnly || getMultipleVideoSendingSupportFeatureFlag(state))
|
||||
) {
|
||||
setTrackMuted(jitsiTrack, muted, state).catch(() => dispatch(trackMuteUnmuteFailed(localTrack, muted)));
|
||||
}
|
||||
} else if (!muted && ensureTrack && (typeof APP === 'undefined' || isPrejoinPageVisible(state))) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { getLocalParticipant, getParticipantById } from '../../../base/participa
|
|||
import { Popover } from '../../../base/popover';
|
||||
import { connect } from '../../../base/redux';
|
||||
import {
|
||||
getFakeScreenshareParticipantTrack,
|
||||
getVirtualScreenshareParticipantTrack,
|
||||
getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
|
||||
import {
|
||||
isParticipantConnectionStatusInactive,
|
||||
|
@ -374,8 +374,8 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
|
||||
let firstVideoTrack;
|
||||
|
||||
if (sourceNameSignalingEnabled && participant?.isFakeScreenShareParticipant) {
|
||||
firstVideoTrack = getFakeScreenshareParticipantTrack(tracks, participantId);
|
||||
if (sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant) {
|
||||
firstVideoTrack = getVirtualScreenshareParticipantTrack(tracks, participantId);
|
||||
} else {
|
||||
firstVideoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ type Props = AbstractProps & {
|
|||
* Whether or not the displays stats are for screen share. This prop is behind the sourceNameSignaling feature
|
||||
* flag.
|
||||
*/
|
||||
_isFakeScreenShareParticipant: Boolean,
|
||||
_isVirtualScreenshareParticipant: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the displays stats are for local video.
|
||||
|
@ -205,8 +205,8 @@ class ConnectionIndicatorContent extends AbstractConnectionIndicator<Props, Stat
|
|||
e2eRtt = { e2eRtt }
|
||||
enableSaveLogs = { this.props._enableSaveLogs }
|
||||
framerate = { framerate }
|
||||
isFakeScreenShareParticipant = { this.props._isFakeScreenShareParticipant }
|
||||
isLocalVideo = { this.props._isLocalVideo }
|
||||
isVirtualScreenshareParticipant = { this.props._isVirtualScreenshareParticipant }
|
||||
maxEnabledResolution = { maxEnabledResolution }
|
||||
onSaveLogs = { this.props._onSaveLogs }
|
||||
onShowMore = { this._onToggleShowMore }
|
||||
|
@ -343,7 +343,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
_disableShowMoreStats: state['features/base/config'].disableShowMoreStats,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_isFakeScreenShareParticipant: sourceNameSignalingEnabled && participant?.isFakeScreenShareParticipant,
|
||||
_isVirtualScreenshareParticipant: sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant,
|
||||
_isLocalVideo: participant?.local,
|
||||
_region: participant?.region
|
||||
};
|
||||
|
|
|
@ -94,7 +94,7 @@ type Props = {
|
|||
/**
|
||||
* Whether or not the statistics are for screen share.
|
||||
*/
|
||||
isFakeScreenShareParticipant: boolean,
|
||||
isVirtualScreenshareParticipant: boolean,
|
||||
|
||||
/**
|
||||
* The send-side max enabled resolution (aka the highest layer that is not
|
||||
|
@ -240,12 +240,12 @@ class ConnectionStatsTable extends Component<Props> {
|
|||
classes,
|
||||
disableShowMoreStats,
|
||||
enableSaveLogs,
|
||||
isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant,
|
||||
isLocalVideo
|
||||
} = this.props;
|
||||
const className = clsx(classes.connectionStatsTable, { [classes.mobile]: isMobileBrowser() });
|
||||
|
||||
if (isFakeScreenShareParticipant) {
|
||||
if (isVirtualScreenshareParticipant) {
|
||||
return this._renderScreenShareStatus();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import React, { Component } from 'react';
|
|||
|
||||
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../analytics';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
|
||||
import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../../../base/config';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, VideoTrack } from '../../../base/media';
|
||||
|
@ -24,7 +24,7 @@ import {
|
|||
getLocalAudioTrack,
|
||||
getLocalVideoTrack,
|
||||
getTrackByMediaTypeAndParticipant,
|
||||
getFakeScreenshareParticipantTrack,
|
||||
getVirtualScreenshareParticipantTrack,
|
||||
updateLastTrackVideoMediaEvent,
|
||||
trackStreamingStatusChanged
|
||||
} from '../../../base/tracks';
|
||||
|
@ -51,10 +51,10 @@ import {
|
|||
showGridInVerticalView
|
||||
} from '../../functions';
|
||||
|
||||
import FakeScreenShareParticipant from './FakeScreenShareParticipant';
|
||||
import ThumbnailAudioIndicator from './ThumbnailAudioIndicator';
|
||||
import ThumbnailBottomIndicators from './ThumbnailBottomIndicators';
|
||||
import ThumbnailTopIndicators from './ThumbnailTopIndicators';
|
||||
import VirtualScreenshareParticipant from './VirtualScreenshareParticipant';
|
||||
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
@ -137,10 +137,10 @@ export type Props = {|
|
|||
_isCurrentlyOnLargeVideo: boolean,
|
||||
|
||||
/**
|
||||
* Indicates whether the participant is a fake screen share participant. This prop is behind the
|
||||
* Indicates whether the participant is a virtual screen share participant. This prop is behind the
|
||||
* sourceNameSignaling feature flag.
|
||||
*/
|
||||
_isFakeScreenShareParticipant: boolean,
|
||||
_isVirtualScreenshareParticipant: boolean,
|
||||
|
||||
/**
|
||||
* Whether we are currently running in a mobile browser.
|
||||
|
@ -626,7 +626,7 @@ class Thumbnail extends Component<Props, State> {
|
|||
const {
|
||||
_disableTileEnlargement,
|
||||
_height,
|
||||
_isFakeScreenShareParticipant,
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isHidden,
|
||||
_isScreenSharing,
|
||||
_participant,
|
||||
|
@ -665,7 +665,7 @@ class Thumbnail extends Component<Props, State> {
|
|||
|| _disableTileEnlargement
|
||||
|| _isScreenSharing;
|
||||
|
||||
if (canPlayEventReceived || _participant.local || _isFakeScreenShareParticipant) {
|
||||
if (canPlayEventReceived || _participant.local || _isVirtualScreenshareParticipant) {
|
||||
videoStyles = {
|
||||
objectFit: doNotStretchVideo ? 'contain' : 'cover'
|
||||
};
|
||||
|
@ -1107,7 +1107,7 @@ class Thumbnail extends Component<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _participant, _isFakeScreenShareParticipant } = this.props;
|
||||
const { _participant, _isVirtualScreenshareParticipant } = this.props;
|
||||
|
||||
if (!_participant) {
|
||||
return null;
|
||||
|
@ -1123,12 +1123,12 @@ class Thumbnail extends Component<Props, State> {
|
|||
return this._renderFakeParticipant();
|
||||
}
|
||||
|
||||
if (_isFakeScreenShareParticipant) {
|
||||
if (_isVirtualScreenshareParticipant) {
|
||||
const { isHovered } = this.state;
|
||||
const { _videoTrack, _isMobile, classes, _thumbnailType } = this.props;
|
||||
|
||||
return (
|
||||
<FakeScreenShareParticipant
|
||||
<VirtualScreenshareParticipant
|
||||
classes = { classes }
|
||||
containerClassName = { this._getContainerClassName() }
|
||||
isHovered = { isHovered }
|
||||
|
@ -1170,8 +1170,8 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
|
||||
let _videoTrack;
|
||||
|
||||
if (sourceNameSignalingEnabled && participant?.isFakeScreenShareParticipant) {
|
||||
_videoTrack = getFakeScreenshareParticipantTrack(tracks, id);
|
||||
if (sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant) {
|
||||
_videoTrack = getVirtualScreenshareParticipantTrack(tracks, id);
|
||||
} else {
|
||||
_videoTrack = isLocal
|
||||
? getLocalVideoTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantID);
|
||||
|
@ -1193,7 +1193,6 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
const activeParticipants = getActiveParticipantsIds(state);
|
||||
const tileType = getThumbnailTypeFromLayout(_currentLayout, stageFilmstrip);
|
||||
|
||||
|
||||
switch (tileType) {
|
||||
case THUMBNAIL_TYPE.VERTICAL:
|
||||
case THUMBNAIL_TYPE.HORIZONTAL: {
|
||||
|
@ -1263,6 +1262,7 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
|
||||
return {
|
||||
_audioTrack,
|
||||
_currentLayout,
|
||||
_defaultLocalDisplayName: defaultLocalDisplayName,
|
||||
_disableLocalVideoFlip: Boolean(disableLocalVideoFlip),
|
||||
_disableTileEnlargement: Boolean(disableTileEnlargement),
|
||||
|
@ -1271,13 +1271,14 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
_isAudioOnly: Boolean(state['features/base/audio-only'].enabled),
|
||||
_isCurrentlyOnLargeVideo: state['features/large-video']?.participantId === id,
|
||||
_isDominantSpeakerDisabled: interfaceConfig.DISABLE_DOMINANT_SPEAKER_INDICATOR,
|
||||
_isFakeScreenShareParticipant: sourceNameSignalingEnabled && participant?.isFakeScreenShareParticipant,
|
||||
_isMobile,
|
||||
_isMobilePortrait,
|
||||
_isScreenSharing: _videoTrack?.videoType === 'desktop',
|
||||
_isTestModeEnabled: isTestModeEnabled(state),
|
||||
_isVideoPlayable: id && isVideoPlayable(state, id),
|
||||
_isVirtualScreenshareParticipant: sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant,
|
||||
_localFlipX: Boolean(localFlipX),
|
||||
_multipleVideoSupport: getMultipleVideoSupportFeatureFlag(state),
|
||||
_participant: participant,
|
||||
_raisedHand: hasRaisedHand(participant),
|
||||
_stageFilmstripLayout: isStageFilmstripAvailable(state),
|
||||
|
|
|
@ -36,9 +36,9 @@ type Props = {
|
|||
isHovered: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the thumbnail is a fake screen share participant.
|
||||
* Whether or not the thumbnail is a virtual screen share participant.
|
||||
*/
|
||||
isFakeScreenShareParticipant: boolean,
|
||||
isVirtualScreenshareParticipant: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the indicators are for the local participant.
|
||||
|
@ -81,7 +81,7 @@ const useStyles = makeStyles(() => {
|
|||
const ThumbnailTopIndicators = ({
|
||||
hidePopover,
|
||||
indicatorsClassName,
|
||||
isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant,
|
||||
isHovered,
|
||||
local,
|
||||
participantId,
|
||||
|
@ -101,7 +101,7 @@ const ThumbnailTopIndicators = ({
|
|||
const sourceNameSignalingEnabled = useSelector(getSourceNameSignalingFeatureFlag);
|
||||
const showConnectionIndicator = isHovered || !_connectionIndicatorAutoHideEnabled;
|
||||
|
||||
if (sourceNameSignalingEnabled && isFakeScreenShareParticipant) {
|
||||
if (sourceNameSignalingEnabled && isVirtualScreenshareParticipant) {
|
||||
return (
|
||||
<div className = { styles.container }>
|
||||
{!_connectionIndicatorDisabled
|
||||
|
|
|
@ -68,7 +68,7 @@ type Props = {
|
|||
onTouchStart: Function,
|
||||
|
||||
/**
|
||||
* The ID of the fake screen share participant.
|
||||
* The ID of the virtual screen share participant.
|
||||
*/
|
||||
participantId: string,
|
||||
|
||||
|
@ -88,7 +88,7 @@ type Props = {
|
|||
videoTrack: Object
|
||||
}
|
||||
|
||||
const FakeScreenShareParticipant = ({
|
||||
const VirtualScreenshareParticipant = ({
|
||||
classes,
|
||||
containerClassName,
|
||||
isHovered,
|
||||
|
@ -141,8 +141,8 @@ const FakeScreenShareParticipant = ({
|
|||
) }>
|
||||
<ThumbnailTopIndicators
|
||||
currentLayout = { currentLayout }
|
||||
isFakeScreenShareParticipant = { true }
|
||||
isHovered = { isHovered }
|
||||
isVirtualScreenshareParticipant = { true }
|
||||
participantId = { participantId }
|
||||
thumbnailType = { thumbnailType } />
|
||||
</div>
|
||||
|
@ -161,4 +161,4 @@ const FakeScreenShareParticipant = ({
|
|||
</span>);
|
||||
};
|
||||
|
||||
export default FakeScreenShareParticipant;
|
||||
export default VirtualScreenshareParticipant;
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
||||
import { getFakeScreenShareParticipantOwnerId } from '../base/participants';
|
||||
import { getVirtualScreenshareParticipantOwnerId } from '../base/participants';
|
||||
|
||||
import { setRemoteParticipants } from './actions';
|
||||
import { isReorderingEnabled } from './functions';
|
||||
|
@ -18,9 +18,9 @@ export function updateRemoteParticipants(store: Object, participantId: ?number)
|
|||
const state = store.getState();
|
||||
let reorderedParticipants = [];
|
||||
|
||||
const { sortedRemoteFakeScreenShareParticipants } = state['features/base/participants'];
|
||||
const { sortedRemoteVirtualScreenshareParticipants } = state['features/base/participants'];
|
||||
|
||||
if (!isReorderingEnabled(state) && !sortedRemoteFakeScreenShareParticipants.size) {
|
||||
if (!isReorderingEnabled(state) && !sortedRemoteVirtualScreenshareParticipants.size) {
|
||||
if (participantId) {
|
||||
const { remoteParticipants } = state['features/filmstrip'];
|
||||
|
||||
|
@ -39,14 +39,14 @@ export function updateRemoteParticipants(store: Object, participantId: ?number)
|
|||
} = state['features/base/participants'];
|
||||
const remoteParticipants = new Map(sortedRemoteParticipants);
|
||||
const screenShares = new Map(sortedRemoteScreenshares);
|
||||
const screenShareParticipants = sortedRemoteFakeScreenShareParticipants
|
||||
? [ ...sortedRemoteFakeScreenShareParticipants.keys() ] : [];
|
||||
const screenShareParticipants = sortedRemoteVirtualScreenshareParticipants
|
||||
? [ ...sortedRemoteVirtualScreenshareParticipants.keys() ] : [];
|
||||
const sharedVideos = fakeParticipants ? Array.from(fakeParticipants.keys()) : [];
|
||||
const speakers = new Map(speakersList);
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
for (const screenshare of screenShareParticipants) {
|
||||
const ownerId = getFakeScreenShareParticipantOwnerId(screenshare);
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);
|
||||
|
||||
remoteParticipants.delete(ownerId);
|
||||
remoteParticipants.delete(screenshare);
|
||||
|
@ -72,7 +72,7 @@ export function updateRemoteParticipants(store: Object, participantId: ?number)
|
|||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
// Always update the order of the thumnails.
|
||||
const participantsWithScreenShare = screenShareParticipants.reduce((acc, screenshare) => {
|
||||
const ownerId = getFakeScreenShareParticipantOwnerId(screenshare);
|
||||
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);
|
||||
|
||||
acc.push(ownerId);
|
||||
acc.push(screenshare);
|
||||
|
|
|
@ -509,18 +509,29 @@ export function computeDisplayModeFromInput(input: Object) {
|
|||
isActiveParticipant,
|
||||
isAudioOnly,
|
||||
isCurrentlyOnLargeVideo,
|
||||
isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant,
|
||||
isScreenSharing,
|
||||
canPlayEventReceived,
|
||||
isRemoteParticipant,
|
||||
multipleVideoSupport,
|
||||
stageParticipantsVisible,
|
||||
stageFilmstrip,
|
||||
tileViewActive
|
||||
} = input;
|
||||
const adjustedIsVideoPlayable = input.isVideoPlayable && (!isRemoteParticipant || canPlayEventReceived);
|
||||
|
||||
if (isFakeScreenShareParticipant) {
|
||||
return DISPLAY_VIDEO;
|
||||
if (multipleVideoSupport) {
|
||||
// Display video for virtual screen share participants in all layouts.
|
||||
if (isVirtualScreenshareParticipant) {
|
||||
return DISPLAY_VIDEO;
|
||||
}
|
||||
|
||||
// Multi-stream is not supported on plan-b endpoints even if its is enabled via config.js. A virtual
|
||||
// screenshare tile is still created when a remote endpoint starts screenshare to keep the behavior consistent
|
||||
// and an avatar is displayed on the original participant thumbnail as long as screenshare is in progress.
|
||||
if (isScreenSharing) {
|
||||
return DISPLAY_AVATAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tileViewActive && ((isScreenSharing && isRemoteParticipant)
|
||||
|
@ -551,9 +562,10 @@ export function getDisplayModeInput(props: Object, state: Object) {
|
|||
_isActiveParticipant,
|
||||
_isAudioOnly,
|
||||
_isCurrentlyOnLargeVideo,
|
||||
_isFakeScreenShareParticipant,
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isScreenSharing,
|
||||
_isVideoPlayable,
|
||||
_multipleVideoSupport,
|
||||
_participant,
|
||||
_stageParticipantsVisible,
|
||||
_videoTrack,
|
||||
|
@ -573,7 +585,8 @@ export function getDisplayModeInput(props: Object, state: Object) {
|
|||
videoStream: Boolean(_videoTrack),
|
||||
isRemoteParticipant: !_participant?.isFakeParticipant && !_participant?.local,
|
||||
isScreenSharing: _isScreenSharing,
|
||||
isFakeScreenShareParticipant: _isFakeScreenShareParticipant,
|
||||
isVirtualScreenshareParticipant: _isVirtualScreenshareParticipant,
|
||||
multipleVideoSupport: _multipleVideoSupport,
|
||||
stageParticipantsVisible: _stageParticipantsVisible,
|
||||
stageFilmstrip,
|
||||
videoStreamMuted: _videoTrack ? _videoTrack.muted : 'no stream'
|
||||
|
|
|
@ -16,8 +16,8 @@ StateListenerRegistry.register(
|
|||
* Listens for changes to the remote screenshare participants to recompute the reordered list of the remote endpoints.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].sortedRemoteFakeScreenShareParticipants,
|
||||
/* listener */ (sortedRemoteFakeScreenShareParticipants, store) => updateRemoteParticipants(store));
|
||||
/* selector */ state => state['features/base/participants'].sortedRemoteVirtualScreenshareParticipants,
|
||||
/* listener */ (sortedRemoteVirtualScreenshareParticipants, store) => updateRemoteParticipants(store));
|
||||
|
||||
/**
|
||||
* Listens for changes to the dominant speaker to recompute the reordered list of the remote endpoints.
|
||||
|
|
|
@ -128,10 +128,10 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
const { participant: p } = action;
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
// Do not display notifications for the fake screenshare tiles.
|
||||
// Do not display notifications for the virtual screenshare tiles.
|
||||
if (conference
|
||||
&& !p.local
|
||||
&& !p.isFakeScreenShareParticipant
|
||||
&& !p.isVirtualScreenshareParticipant
|
||||
&& !joinLeaveNotificationsDisabled()
|
||||
&& !p.isReplacing) {
|
||||
dispatch(showParticipantJoinedNotification(
|
||||
|
@ -148,10 +148,10 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
action.participant.id
|
||||
);
|
||||
|
||||
// Do not display notifications for the fake screenshare tiles.
|
||||
// Do not display notifications for the virtual screenshare tiles.
|
||||
if (participant
|
||||
&& !participant.local
|
||||
&& !participant.isFakeScreenShareParticipant
|
||||
&& !participant.isVirtualScreenshareParticipant
|
||||
&& !action.participant.isReplaced) {
|
||||
dispatch(showParticipantLeftNotification(
|
||||
getParticipantDisplayName(state, participant.id)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../base/config/functions.any';
|
||||
import { getMultipleVideoSendingSupportFeatureFlag } from '../base/config/functions.any';
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
import { browser } from '../base/lib-jitsi-meet';
|
||||
import { shouldHideShareAudioHelper } from '../base/settings';
|
||||
|
@ -86,7 +86,7 @@ export function startAudioScreenShareFlow() {
|
|||
// available for audio screen sharing, namely full window audio.
|
||||
// If we're already sharing audio, toggle off.
|
||||
if (shouldHideShareAudioHelper(state) || browser.isElectron() || audioOnlySharing) {
|
||||
if (getMultipleVideoSupportFeatureFlag(state)) {
|
||||
if (getMultipleVideoSendingSupportFeatureFlag(state)) {
|
||||
dispatch(toggleScreensharing(!audioOnlySharing, true));
|
||||
|
||||
return;
|
||||
|
|
|
@ -11,14 +11,14 @@ export const SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED
|
|||
= 'SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED';
|
||||
|
||||
/**
|
||||
* The type of the action which sets the list of known remote fake screen share participant IDs.
|
||||
* The type of the action which sets the list of known remote virtual screen share participant IDs.
|
||||
*
|
||||
* @returns {{
|
||||
* type: FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
* type: VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
* participantIds: Array<string>
|
||||
* }}
|
||||
*/
|
||||
export const FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED = 'FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED';
|
||||
export const VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED = 'VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED';
|
||||
|
||||
/**
|
||||
* The type of the action which enables or disables the feature for showing
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SET_TILE_VIEW
|
||||
SET_TILE_VIEW,
|
||||
VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||
} from './actionTypes';
|
||||
import { shouldDisplayTileView } from './functions';
|
||||
|
||||
|
@ -28,17 +28,18 @@ export function setRemoteParticipantsWithScreenShare(participantIds: Array<strin
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a (redux) action which signals that the list of known remote fake screen share participant ids has changed.
|
||||
* Creates a (redux) action which signals that the list of known remote virtual screen share participant ids has
|
||||
* changed.
|
||||
*
|
||||
* @param {string} participantIds - The remote fake screen share participants.
|
||||
* @param {string} participantIds - The remote virtual screen share participants.
|
||||
* @returns {{
|
||||
* type: FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
* type: VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
* participantIds: Array<string>
|
||||
* }}
|
||||
*/
|
||||
export function fakeScreenshareParticipantsUpdated(participantIds: Array<string>) {
|
||||
export function virtualScreenshareParticipantsUpdated(participantIds: Array<string>) {
|
||||
return {
|
||||
type: FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
type: VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
participantIds
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SET_TILE_VIEW
|
||||
SET_TILE_VIEW,
|
||||
VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||
} from './actionTypes';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
|
@ -28,8 +28,8 @@ const STORE_NAME = 'features/video-layout';
|
|||
|
||||
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case FAKE_SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED:
|
||||
case SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED: {
|
||||
case SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED:
|
||||
case VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED: {
|
||||
return {
|
||||
...state,
|
||||
remoteScreenShares: action.participantIds
|
||||
|
|
|
@ -2,22 +2,22 @@
|
|||
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
||||
import { getMultipleVideoSupportFeatureFlag } from '../base/config';
|
||||
import { StateListenerRegistry, equals } from '../base/redux';
|
||||
import { isFollowMeActive } from '../follow-me';
|
||||
|
||||
import { setRemoteParticipantsWithScreenShare, fakeScreenshareParticipantsUpdated } from './actions';
|
||||
import { setRemoteParticipantsWithScreenShare, virtualScreenshareParticipantsUpdated } from './actions';
|
||||
import { getAutoPinSetting, updateAutoPinnedParticipant } from './functions';
|
||||
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].sortedRemoteFakeScreenShareParticipants,
|
||||
/* listener */ (sortedRemoteFakeScreenShareParticipants, store) => {
|
||||
if (!getAutoPinSetting() || isFollowMeActive(store) || !getSourceNameSignalingFeatureFlag(store.getState())) {
|
||||
/* selector */ state => state['features/base/participants'].sortedRemoteVirtualScreenshareParticipants,
|
||||
/* listener */ (sortedRemoteVirtualScreenshareParticipants, store) => {
|
||||
if (!getAutoPinSetting() || isFollowMeActive(store) || !getMultipleVideoSupportFeatureFlag(store.getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldScreenSharesOrder = store.getState()['features/video-layout'].remoteScreenShares || [];
|
||||
const knownSharingParticipantIds = [ ...sortedRemoteFakeScreenShareParticipants.keys() ];
|
||||
const knownSharingParticipantIds = [ ...sortedRemoteVirtualScreenshareParticipants.keys() ];
|
||||
|
||||
// Filter out any participants which are no longer screen sharing
|
||||
// by looping through the known sharing participants and removing any
|
||||
|
@ -34,7 +34,7 @@ StateListenerRegistry.register(
|
|||
});
|
||||
|
||||
if (!equals(oldScreenSharesOrder, newScreenSharesOrder)) {
|
||||
store.dispatch(fakeScreenshareParticipantsUpdated(newScreenSharesOrder));
|
||||
store.dispatch(virtualScreenshareParticipantsUpdated(newScreenSharesOrder));
|
||||
|
||||
updateAutoPinnedParticipant(oldScreenSharesOrder, store);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ StateListenerRegistry.register(
|
|||
// possible to have screen sharing participant that has already left in the remoteScreenShares array.
|
||||
// This can lead to rendering a thumbnails for already left participants since the remoteScreenShares
|
||||
// array is used for building the ordered list of remote participants.
|
||||
if (!getAutoPinSetting() || isFollowMeActive(store) || getSourceNameSignalingFeatureFlag(store.getState())) {
|
||||
if (!getAutoPinSetting() || isFollowMeActive(store) || getMultipleVideoSupportFeatureFlag(store.getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
|||
import { MEDIA_TYPE } from '../base/media';
|
||||
import { getLocalParticipant, getParticipantCount } from '../base/participants';
|
||||
import { StateListenerRegistry } from '../base/redux';
|
||||
import { getTrackSourceNameByMediaTypeAndParticipant } from '../base/tracks';
|
||||
import { getRemoteScreenSharesSourceNames, getTrackSourceNameByMediaTypeAndParticipant } from '../base/tracks';
|
||||
import { reportError } from '../base/util';
|
||||
import { getActiveParticipantsIds } from '../filmstrip/functions.web';
|
||||
import {
|
||||
|
@ -238,6 +238,8 @@ function _updateReceiverVideoConstraints({ getState }) {
|
|||
let receiverConstraints;
|
||||
|
||||
if (sourceNameSignaling) {
|
||||
const remoteScreenSharesSourceNames = getRemoteScreenSharesSourceNames(state, remoteScreenShares);
|
||||
|
||||
receiverConstraints = {
|
||||
constraints: {},
|
||||
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.NONE },
|
||||
|
@ -253,7 +255,7 @@ function _updateReceiverVideoConstraints({ getState }) {
|
|||
visibleRemoteParticipants.forEach(participantId => {
|
||||
let sourceName;
|
||||
|
||||
if (remoteScreenShares.includes(participantId)) {
|
||||
if (remoteScreenSharesSourceNames.includes(participantId)) {
|
||||
sourceName = participantId;
|
||||
} else {
|
||||
sourceName = getTrackSourceNameByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
|
@ -269,12 +271,11 @@ function _updateReceiverVideoConstraints({ getState }) {
|
|||
}
|
||||
|
||||
if (localParticipantId !== largeVideoParticipantId) {
|
||||
if (remoteScreenShares.includes(largeVideoParticipantId)) {
|
||||
if (remoteScreenSharesSourceNames.includes(largeVideoParticipantId)) {
|
||||
largeVideoSourceName = largeVideoParticipantId;
|
||||
} else {
|
||||
largeVideoSourceName = getTrackSourceNameByMediaTypeAndParticipant(
|
||||
tracks, MEDIA_TYPE.VIDEO,
|
||||
largeVideoParticipantId
|
||||
tracks, MEDIA_TYPE.VIDEO, largeVideoParticipantId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -290,8 +291,8 @@ function _updateReceiverVideoConstraints({ getState }) {
|
|||
});
|
||||
|
||||
// Prioritize screenshare in tile view.
|
||||
if (remoteScreenShares?.length) {
|
||||
receiverConstraints.selectedSources = remoteScreenShares;
|
||||
if (remoteScreenSharesSourceNames?.length) {
|
||||
receiverConstraints.selectedSources = remoteScreenSharesSourceNames;
|
||||
}
|
||||
|
||||
// Stage view.
|
||||
|
@ -325,8 +326,8 @@ function _updateReceiverVideoConstraints({ getState }) {
|
|||
}
|
||||
}
|
||||
|
||||
if (remoteScreenShares?.length) {
|
||||
remoteScreenShares.forEach(sourceName => {
|
||||
if (remoteScreenSharesSourceNames?.length) {
|
||||
remoteScreenSharesSourceNames.forEach(sourceName => {
|
||||
receiverConstraints.constraints[sourceName] = { 'maxHeight': VIDEO_QUALITY_LEVELS.ULTRA };
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue