Browse Source

fix(remote-control): when multistream is enabled

factor2
Hristo Terezov 3 years ago
parent
commit
53e4f584f9

+ 0
- 11
conference.js View File

@@ -2660,17 +2660,6 @@ export default {
2660 2660
                 APP.UI.updateLargeVideo(displayedUserId, true);
2661 2661
             }
2662 2662
         });
2663
-
2664
-        // Used in non-multi-stream.
2665
-        APP.UI.addListener(
2666
-            UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly, ignoreDidHaveVideo, desktopStream }) => {
2667
-                this.toggleScreenSharing(enabled,
2668
-                    {
2669
-                        audioOnly,
2670
-                        desktopStream
2671
-                    }, ignoreDidHaveVideo);
2672
-            }
2673
-        );
2674 2663
     },
2675 2664
 
2676 2665
     /**

+ 2
- 1
modules/UI/videolayout/LargeVideoManager.js View File

@@ -36,7 +36,8 @@ import {
36 36
     isTrackStreamingStatusInactive,
37 37
     isTrackStreamingStatusInterrupted
38 38
 } from '../../../react/features/connection-indicator/functions';
39
-import { FILMSTRIP_BREAKPOINT, getVerticalViewMaxWidth, isFilmstripResizable } from '../../../react/features/filmstrip';
39
+import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip/constants';
40
+import { getVerticalViewMaxWidth, isFilmstripResizable } from '../../../react/features/filmstrip/functions';
40 41
 import {
41 42
     updateKnownLargeVideoResolution
42 43
 } from '../../../react/features/large-video/actions';

+ 0
- 77
react/features/base/conference/middleware.native.js View File

@@ -1,78 +1 @@
1
-// @flow
2
-
3
-import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
4
-import { setAudioOnly } from '../audio-only';
5
-import JitsiMeetJS from '../lib-jitsi-meet';
6
-import { MiddlewareRegistry } from '../redux';
7
-import { TOGGLE_SCREENSHARING } from '../tracks/actionTypes';
8
-import { destroyLocalDesktopTrackIfExists, replaceLocalTrack } from '../tracks/actions';
9
-import { getLocalVideoTrack, isLocalVideoTrackDesktop } from '../tracks/functions';
10
-
11 1
 import './middleware.any';
12
-
13
-MiddlewareRegistry.register(store => next => action => {
14
-    switch (action.type) {
15
-    case TOGGLE_SCREENSHARING: {
16
-        _toggleScreenSharing(action.enabled, store);
17
-        break;
18
-    }
19
-    }
20
-
21
-    return next(action);
22
-});
23
-
24
-/**
25
- * Toggles screen sharing.
26
- *
27
- * @private
28
- * @param {boolean} enabled - The state to toggle screen sharing to.
29
- * @param {Store} store - The redux.
30
- * @returns {void}
31
- */
32
-function _toggleScreenSharing(enabled, store) {
33
-    const { dispatch, getState } = store;
34
-    const state = getState();
35
-
36
-    if (enabled) {
37
-        const isSharing = isLocalVideoTrackDesktop(state);
38
-
39
-        if (!isSharing) {
40
-            _startScreenSharing(dispatch, state);
41
-        }
42
-    } else {
43
-        dispatch(destroyLocalDesktopTrackIfExists());
44
-        setPictureInPictureEnabled(true);
45
-    }
46
-}
47
-
48
-/**
49
- * Creates desktop track and replaces the local one.
50
- *
51
- * @private
52
- * @param {Dispatch} dispatch - The redux {@code dispatch} function.
53
- * @param {Object} state - The redux state.
54
- * @returns {void}
55
- */
56
-function _startScreenSharing(dispatch, state) {
57
-    setPictureInPictureEnabled(false);
58
-
59
-    JitsiMeetJS.createLocalTracks({ devices: [ 'desktop' ] })
60
-    .then(tracks => {
61
-        const track = tracks[0];
62
-        const currentLocalTrack = getLocalVideoTrack(state['features/base/tracks']);
63
-        const currentJitsiTrack = currentLocalTrack && currentLocalTrack.jitsiTrack;
64
-
65
-        dispatch(replaceLocalTrack(currentJitsiTrack, track));
66
-
67
-        const { enabled: audioOnly } = state['features/base/audio-only'];
68
-
69
-        if (audioOnly) {
70
-            dispatch(setAudioOnly(false));
71
-        }
72
-    })
73
-    .catch(error => {
74
-        console.log('ERROR creating ScreeSharing stream ', error);
75
-
76
-        setPictureInPictureEnabled(true);
77
-    });
78
-}

+ 1
- 248
react/features/base/conference/middleware.web.js View File

@@ -1,48 +1,14 @@
1
-// @flow
2 1
 
3
-import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from '../../../../modules/UI/UIErrors';
4
-import UIEvents from '../../../../service/UI/UIEvents';
5
-import { showModeratedNotification } from '../../av-moderation/actions';
6
-import { shouldShowModeratedNotification } from '../../av-moderation/functions';
7
-import { setNoiseSuppressionEnabled } from '../../noise-suppression/actions';
8
-import {
9
-    NOTIFICATION_TIMEOUT_TYPE,
10
-    isModerationNotificationDisplayed,
11
-    showNotification
12
-} from '../../notifications';
13 2
 import {
14 3
     setPrejoinPageVisibility,
15 4
     setSkipPrejoinOnReload
16 5
 } from '../../prejoin';
17
-import {
18
-    isAudioOnlySharing,
19
-    isScreenVideoShared,
20
-    setScreenAudioShareState,
21
-    setScreenshareAudioTrack
22
-} from '../../screen-share';
23
-import { isScreenshotCaptureEnabled, toggleScreenshotCaptureSummary } from '../../screenshot-capture';
24
-import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
25
-import { setAudioOnly } from '../audio-only';
26
-import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
27
-import { JitsiConferenceErrors, JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
28
-import { MEDIA_TYPE, VIDEO_TYPE, setScreenshareMuted } from '../media';
6
+import { JitsiConferenceErrors } from '../lib-jitsi-meet';
29 7
 import { MiddlewareRegistry } from '../redux';
30
-import {
31
-    TOGGLE_SCREENSHARING,
32
-    addLocalTrack,
33
-    createLocalTracksF,
34
-    getLocalDesktopTrack,
35
-    getLocalJitsiAudioTrack,
36
-    replaceLocalTrack,
37
-    toggleScreensharing
38
-} from '../tracks';
39 8
 
40 9
 import { CONFERENCE_FAILED, CONFERENCE_JOINED, CONFERENCE_JOIN_IN_PROGRESS } from './actionTypes';
41
-import { getCurrentConference } from './functions';
42 10
 import './middleware.any';
43 11
 
44
-declare var APP: Object;
45
-
46 12
 MiddlewareRegistry.register(store => next => action => {
47 13
     const { dispatch, getState } = store;
48 14
     const { enableForcedReload } = getState()['features/base/config'];
@@ -69,220 +35,7 @@ MiddlewareRegistry.register(store => next => action => {
69 35
 
70 36
         break;
71 37
     }
72
-    case TOGGLE_SCREENSHARING:
73
-        if (typeof APP === 'object') {
74
-            // check for A/V Moderation when trying to start screen sharing
75
-            if ((action.enabled || action.enabled === undefined)
76
-                && shouldShowModeratedNotification(MEDIA_TYPE.VIDEO, store.getState())) {
77
-                if (!isModerationNotificationDisplayed(MEDIA_TYPE.PRESENTER, store.getState())) {
78
-                    store.dispatch(showModeratedNotification(MEDIA_TYPE.PRESENTER));
79
-                }
80
-
81
-                return;
82
-            }
83
-
84
-            const { enabled, audioOnly, ignoreDidHaveVideo, shareOptions } = action;
85
-
86
-            if (getMultipleVideoSendingSupportFeatureFlag(store.getState())) {
87
-                _toggleScreenSharing(action, store);
88
-            } else {
89
-                APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING,
90
-                    {
91
-                        enabled,
92
-                        audioOnly,
93
-                        ignoreDidHaveVideo,
94
-                        desktopStream: shareOptions?.desktopStream
95
-                    });
96
-            }
97
-        }
98
-        break;
99 38
     }
100 39
 
101 40
     return next(action);
102 41
 });
103
-
104
-/**
105
- * Displays a UI notification for screensharing failure based on the error passed.
106
- *
107
- * @private
108
- * @param {Object} error - The error.
109
- * @param {Object} store - The redux store.
110
- * @returns {void}
111
- */
112
-function _handleScreensharingError(error, { dispatch }) {
113
-    if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
114
-        return;
115
-    }
116
-    let descriptionKey, titleKey;
117
-
118
-    if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
119
-        descriptionKey = 'dialog.screenSharingPermissionDeniedError';
120
-        titleKey = 'dialog.screenSharingFailedTitle';
121
-    } else if (error.name === JitsiTrackErrors.CONSTRAINT_FAILED) {
122
-        descriptionKey = 'dialog.cameraConstraintFailedError';
123
-        titleKey = 'deviceError.cameraError';
124
-    } else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
125
-        descriptionKey = 'dialog.screenSharingFailed';
126
-        titleKey = 'dialog.screenSharingFailedTitle';
127
-    } else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
128
-        descriptionKey = 'notify.screenShareNoAudio';
129
-        titleKey = 'notify.screenShareNoAudioTitle';
130
-    }
131
-
132
-    dispatch(showNotification({
133
-        titleKey,
134
-        descriptionKey
135
-    }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
136
-}
137
-
138
-/**
139
- * Applies the AudioMixer effect on the local audio track if applicable. If there is no local audio track, the desktop
140
- * audio track is added to the conference.
141
- *
142
- * @private
143
- * @param {JitsiLocalTrack} desktopAudioTrack - The audio track to be added to the conference.
144
- * @param {*} state - The redux state.
145
- * @returns {void}
146
- */
147
-async function _maybeApplyAudioMixerEffect(desktopAudioTrack, state) {
148
-    const localAudio = getLocalJitsiAudioTrack(state);
149
-    const conference = getCurrentConference(state);
150
-
151
-    if (localAudio) {
152
-        // If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing API.
153
-        const mixerEffect = new AudioMixerEffect(desktopAudioTrack);
154
-
155
-        await localAudio.setEffect(mixerEffect);
156
-    } else {
157
-        // If no local stream is present ( i.e. no input audio devices) we use the screen share audio
158
-        // stream as we would use a regular stream.
159
-        await conference.replaceTrack(null, desktopAudioTrack);
160
-    }
161
-}
162
-
163
-/**
164
- * Toggles screen sharing.
165
- *
166
- * @private
167
- * @param {boolean} enabled - The state to toggle screen sharing to.
168
- * @param {Store} store - The redux store.
169
- * @returns {void}
170
- */
171
-async function _toggleScreenSharing({ enabled, audioOnly = false, shareOptions = {} }, store) {
172
-    const { dispatch, getState } = store;
173
-    const state = getState();
174
-    const audioOnlySharing = isAudioOnlySharing(state);
175
-    const screenSharing = isScreenVideoShared(state);
176
-    const conference = getCurrentConference(state);
177
-    const localAudio = getLocalJitsiAudioTrack(state);
178
-    const localScreenshare = getLocalDesktopTrack(state['features/base/tracks']);
179
-
180
-    // Toggle screenshare or audio-only share if the new state is not passed. Happens in the following two cases.
181
-    // 1. ShareAudioDialog passes undefined when the user hits continue in the share audio demo modal.
182
-    // 2. Toggle screenshare called from the external API.
183
-    const enable = audioOnly
184
-        ? enabled ?? !audioOnlySharing
185
-        : enabled ?? !screenSharing;
186
-    const screensharingDetails = {};
187
-
188
-    if (enable) {
189
-        let tracks;
190
-
191
-        // Spot proxy stream.
192
-        if (shareOptions.desktopStream) {
193
-            tracks = [ shareOptions.desktopStream ];
194
-        } else {
195
-            const { _desktopSharingSourceDevice } = state['features/base/config'];
196
-
197
-            if (!shareOptions.desktopSharingSources && _desktopSharingSourceDevice) {
198
-                shareOptions.desktopSharingSourceDevice = _desktopSharingSourceDevice;
199
-            }
200
-            const options = {
201
-                devices: [ VIDEO_TYPE.DESKTOP ],
202
-                ...shareOptions
203
-            };
204
-
205
-            try {
206
-                tracks = await createLocalTracksF(options);
207
-            } catch (error) {
208
-                _handleScreensharingError(error, store);
209
-
210
-                return;
211
-            }
212
-        }
213
-
214
-        const desktopAudioTrack = tracks.find(track => track.getType() === MEDIA_TYPE.AUDIO);
215
-        const desktopVideoTrack = tracks.find(track => track.getType() === MEDIA_TYPE.VIDEO);
216
-
217
-        if (audioOnly) {
218
-            // Dispose the desktop track for audio-only screensharing.
219
-            desktopVideoTrack.dispose();
220
-
221
-            if (!desktopAudioTrack) {
222
-                _handleScreensharingError(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK, store);
223
-
224
-                return;
225
-            }
226
-        } else if (desktopVideoTrack) {
227
-            if (localScreenshare) {
228
-                await dispatch(replaceLocalTrack(localScreenshare.jitsiTrack, desktopVideoTrack, conference));
229
-            } else {
230
-                await dispatch(addLocalTrack(desktopVideoTrack));
231
-            }
232
-            if (isScreenshotCaptureEnabled(state, false, true)) {
233
-                dispatch(toggleScreenshotCaptureSummary(true));
234
-            }
235
-            screensharingDetails.sourceType = desktopVideoTrack.sourceType;
236
-        }
237
-
238
-        // Apply the AudioMixer effect if there is a local audio track, add the desktop track to the conference
239
-        // otherwise without unmuting the microphone.
240
-        if (desktopAudioTrack) {
241
-            // Noise suppression doesn't work with desktop audio because we can't chain track effects yet, disable it
242
-            // first. We need to to wait for the effect to clear first or it might interfere with the audio mixer.
243
-            await dispatch(setNoiseSuppressionEnabled(false));
244
-            _maybeApplyAudioMixerEffect(desktopAudioTrack, state);
245
-            dispatch(setScreenshareAudioTrack(desktopAudioTrack));
246
-
247
-            // Handle the case where screen share was stopped from the browsers 'screen share in progress' window.
248
-            if (audioOnly) {
249
-                desktopAudioTrack?.on(
250
-                    JitsiTrackEvents.LOCAL_TRACK_STOPPED,
251
-                    () => dispatch(toggleScreensharing(undefined, true)));
252
-            }
253
-        }
254
-
255
-        // Disable audio-only or best performance mode if the user starts screensharing. This doesn't apply to
256
-        // audio-only screensharing.
257
-        const { enabled: bestPerformanceMode } = state['features/base/audio-only'];
258
-
259
-        if (bestPerformanceMode && !audioOnly) {
260
-            dispatch(setAudioOnly(false));
261
-        }
262
-    } else {
263
-        const { desktopAudioTrack } = state['features/screen-share'];
264
-
265
-        dispatch(toggleScreenshotCaptureSummary(false));
266
-
267
-        // Mute the desktop track instead of removing it from the conference since we don't want the client to signal
268
-        // a source-remove to the remote peer for the screenshare track. Later when screenshare is enabled again, the
269
-        // same sender will be re-used without the need for signaling a new ssrc through source-add.
270
-        dispatch(setScreenshareMuted(true));
271
-        if (desktopAudioTrack) {
272
-            if (localAudio) {
273
-                localAudio.setEffect(undefined);
274
-            } else {
275
-                await conference.replaceTrack(desktopAudioTrack, null);
276
-            }
277
-            desktopAudioTrack.dispose();
278
-            dispatch(setScreenshareAudioTrack(null));
279
-        }
280
-    }
281
-
282
-    if (audioOnly) {
283
-        dispatch(setScreenAudioShareState(enable));
284
-    } else {
285
-        // Notify the external API.
286
-        APP.API.notifyScreenSharingStatusChanged(enable, screensharingDetails);
287
-    }
288
-}

+ 0
- 10
react/features/base/tracks/actionTypes.ts View File

@@ -9,16 +9,6 @@
9 9
  */
10 10
 export const SET_NO_SRC_DATA_NOTIFICATION_UID = 'SET_NO_SRC_DATA_NOTIFICATION_UID';
11 11
 
12
-/**
13
- * The type of redux action dispatched to disable screensharing or to start the
14
- * flow for enabling screenshare.
15
- *
16
- * {
17
- *     type: TOGGLE_SCREENSHARING
18
- * }
19
- */
20
-export const TOGGLE_SCREENSHARING = 'TOGGLE_SCREENSHARING';
21
-
22 12
 /**
23 13
  * The type of redux action dispatched when a track has been (locally or
24 14
  * remotely) added to the conference.

react/features/base/tracks/actions.ts → react/features/base/tracks/actions.any.ts View File

@@ -24,7 +24,6 @@ import { updateSettings } from '../settings/actions';
24 24
 
25 25
 import {
26 26
     SET_NO_SRC_DATA_NOTIFICATION_UID,
27
-    TOGGLE_SCREENSHARING,
28 27
     TRACK_ADDED,
29 28
     TRACK_CREATE_CANCELED,
30 29
     TRACK_CREATE_ERROR,
@@ -298,32 +297,6 @@ export function showNoDataFromSourceVideoError(jitsiTrack: any) {
298 297
     };
299 298
 }
300 299
 
301
-/**
302
- * Signals that the local participant is ending screensharing or beginning the screensharing flow.
303
- *
304
- * @param {boolean} enabled - The state to toggle screen sharing to.
305
- * @param {boolean} audioOnly - Only share system audio.
306
- * @param {boolean} ignoreDidHaveVideo - Whether or not to ignore if video was on when sharing started.
307
- * @param {Object} shareOptions - The options to be passed for capturing screenshare.
308
- * @returns {{
309
- *     type: TOGGLE_SCREENSHARING,
310
- *     on: boolean,
311
- *     audioOnly: boolean,
312
- *     ignoreDidHaveVideo: boolean,
313
- *     shareOptions: Object
314
- * }}
315
- */
316
-export function toggleScreensharing(enabled: boolean | undefined, audioOnly = false,
317
-        ignoreDidHaveVideo = false, shareOptions = {}) {
318
-    return {
319
-        type: TOGGLE_SCREENSHARING,
320
-        enabled,
321
-        audioOnly,
322
-        ignoreDidHaveVideo,
323
-        shareOptions
324
-    };
325
-}
326
-
327 300
 /**
328 301
  * Replaces one track with another for one renegotiation instead of invoking
329 302
  * two renegotiations with a separate removeTrack and addTrack. Disposes the
@@ -396,7 +369,7 @@ function replaceStoredTracks(oldTrack: any, newTrack: any) {
396 369
  * conference.
397 370
  *
398 371
  * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
399
- * @returns {{ type: TRACK_ADDED, track: Track }}
372
+ * @returns {Function}
400 373
  */
401 374
 export function trackAdded(track: any) {
402 375
     return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
@@ -490,7 +463,13 @@ export function trackAdded(track: any) {
490 463
  *     track: Track
491 464
  * }}
492 465
  */
493
-export function trackMutedChanged(track: any) {
466
+export function trackMutedChanged(track: any): {
467
+    track: {
468
+        jitsiTrack: any;
469
+        muted: boolean;
470
+    };
471
+    type: 'TRACK_UPDATED';
472
+} {
494 473
     return {
495 474
         type: TRACK_UPDATED,
496 475
         track: {
@@ -511,7 +490,11 @@ export function trackMutedChanged(track: any) {
511 490
  *     track: Track
512 491
  * }}
513 492
  */
514
-export function trackMuteUnmuteFailed(track: any, wasMuting: boolean) {
493
+export function trackMuteUnmuteFailed(track: any, wasMuting: boolean): {
494
+    track: any;
495
+    type: 'TRACK_MUTE_UNMUTE_FAILED';
496
+    wasMuting: boolean;
497
+} {
515 498
     return {
516 499
         type: TRACK_MUTE_UNMUTE_FAILED,
517 500
         track,
@@ -549,7 +532,12 @@ export function trackNoDataFromSourceNotificationInfoChanged(track: any, noDataF
549 532
  *     track: Track
550 533
  * }}
551 534
  */
552
-export function trackRemoved(track: any) {
535
+export function trackRemoved(track: any): {
536
+    track: {
537
+        jitsiTrack: any;
538
+    };
539
+    type: 'TRACK_REMOVED';
540
+} {
553 541
     track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED);
554 542
     track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED);
555 543
     track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
@@ -571,7 +559,13 @@ export function trackRemoved(track: any) {
571 559
  *     track: Track
572 560
  * }}
573 561
  */
574
-export function trackVideoStarted(track: any) {
562
+export function trackVideoStarted(track: any): {
563
+    track: {
564
+        jitsiTrack: any;
565
+        videoStarted: true;
566
+    };
567
+    type: 'TRACK_UPDATED';
568
+} {
575 569
     return {
576 570
         type: TRACK_UPDATED,
577 571
         track: {
@@ -611,7 +605,13 @@ export function trackVideoTypeChanged(track: any, videoType: VideoType) {
611 605
  *     track: Track
612 606
  * }}
613 607
  */
614
-export function trackStreamingStatusChanged(track: any, streamingStatus: string) {
608
+export function trackStreamingStatusChanged(track: any, streamingStatus: string): {
609
+    track: {
610
+        jitsiTrack: any;
611
+        streamingStatus: string;
612
+    };
613
+    type: 'TRACK_UPDATED';
614
+} {
615 615
     return {
616 616
         type: TRACK_UPDATED,
617 617
         track: {
@@ -644,7 +644,7 @@ function _addTracks(tracks: any[]) {
644 644
  * about here is to be sure that the {@code getUserMedia} callbacks have
645 645
  * completed (i.e. Returned from the native side).
646 646
  */
647
-function _cancelGUMProcesses(getState: IStore['getState']) {
647
+function _cancelGUMProcesses(getState: IStore['getState']): Promise<any> {
648 648
     const logError
649 649
         = (error: Error) =>
650 650
             logger.error('gumProcess.cancel failed', JSON.stringify(error));
@@ -678,9 +678,9 @@ export function _disposeAndRemoveTracks(tracks: any[]) {
678 678
  * @returns {Promise} - A Promise resolved once {@link JitsiTrack.dispose()} is
679 679
  * done for every track from the list.
680 680
  */
681
-function _disposeTracks(tracks: any) {
681
+function _disposeTracks(tracks: any[]): Promise<any> {
682 682
     return Promise.all(
683
-        tracks.map((t: any) =>
683
+        tracks.map(t =>
684 684
             t.dispose()
685 685
                 .catch((err: Error) => {
686 686
                     // Track might be already disposed so ignore such an error.
@@ -701,7 +701,7 @@ function _disposeTracks(tracks: any) {
701 701
  * @private
702 702
  * @returns {Function}
703 703
  */
704
-function _onCreateLocalTracksRejected(error: Error, device: string) {
704
+function _onCreateLocalTracksRejected(error?: Error, device?: string) {
705 705
     return (dispatch: IStore['dispatch']) => {
706 706
         // If permissions are not allowed, alert the user.
707 707
         dispatch({
@@ -728,7 +728,7 @@ function _onCreateLocalTracksRejected(error: Error, device: string) {
728 728
  * @private
729 729
  * @returns {boolean}
730 730
  */
731
-function _shouldMirror(track: any) {
731
+function _shouldMirror(track: any): boolean {
732 732
     return (
733 733
         track?.isLocal()
734 734
             && track?.isVideoTrack()
@@ -755,7 +755,10 @@ function _shouldMirror(track: any) {
755 755
  *     trackType: MEDIA_TYPE
756 756
  * }}
757 757
  */
758
-function _trackCreateCanceled(mediaType: MediaType) {
758
+function _trackCreateCanceled(mediaType: MediaType): {
759
+    trackType: MediaType;
760
+    type: 'TRACK_CREATE_CANCELED';
761
+} {
759 762
     return {
760 763
         type: TRACK_CREATE_CANCELED,
761 764
         trackType: mediaType
@@ -806,7 +809,11 @@ export function setNoSrcDataNotificationUid(uid?: string) {
806 809
  *     name: string
807 810
  * }}
808 811
  */
809
-export function updateLastTrackVideoMediaEvent(track: any, name: string) {
812
+export function updateLastTrackVideoMediaEvent(track: any, name: string): {
813
+    name: string;
814
+    track: any;
815
+    type: 'TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT';
816
+} {
810 817
     return {
811 818
         type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
812 819
         track,

+ 80
- 0
react/features/base/tracks/actions.native.ts View File

@@ -0,0 +1,80 @@
1
+/* eslint-disable lines-around-comment */
2
+import { IState, IStore } from '../../app/types';
3
+// @ts-ignore
4
+import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
5
+// @ts-ignore
6
+import { setAudioOnly } from '../audio-only';
7
+import JitsiMeetJS from '../lib-jitsi-meet';
8
+
9
+import { destroyLocalDesktopTrackIfExists, replaceLocalTrack } from './actions.any';
10
+// @ts-ignore
11
+import { getLocalVideoTrack, isLocalVideoTrackDesktop } from './functions';
12
+/* eslint-enable lines-around-comment */
13
+
14
+export * from './actions.any';
15
+
16
+/**
17
+ * Signals that the local participant is ending screensharing or beginning the screensharing flow.
18
+ *
19
+ * @param {boolean} enabled - The state to toggle screen sharing to.
20
+ * @returns {Function}
21
+ */
22
+export function toggleScreensharing(enabled: boolean): Function {
23
+    return (store: IStore) => _toggleScreenSharing(enabled, store);
24
+}
25
+
26
+/**
27
+ * Toggles screen sharing.
28
+ *
29
+ * @private
30
+ * @param {boolean} enabled - The state to toggle screen sharing to.
31
+ * @param {Store} store - The redux.
32
+ * @returns {void}
33
+ */
34
+function _toggleScreenSharing(enabled: boolean, store: IStore): void {
35
+    const { dispatch, getState } = store;
36
+    const state = getState();
37
+
38
+    if (enabled) {
39
+        const isSharing = isLocalVideoTrackDesktop(state);
40
+
41
+        if (!isSharing) {
42
+            _startScreenSharing(dispatch, state);
43
+        }
44
+    } else {
45
+        dispatch(destroyLocalDesktopTrackIfExists());
46
+        setPictureInPictureEnabled(true);
47
+    }
48
+}
49
+
50
+/**
51
+ * Creates desktop track and replaces the local one.
52
+ *
53
+ * @private
54
+ * @param {Dispatch} dispatch - The redux {@code dispatch} function.
55
+ * @param {Object} state - The redux state.
56
+ * @returns {void}
57
+ */
58
+function _startScreenSharing(dispatch: Function, state: IState) {
59
+    setPictureInPictureEnabled(false);
60
+
61
+    JitsiMeetJS.createLocalTracks({ devices: [ 'desktop' ] })
62
+    .then((tracks: any[]) => {
63
+        const track = tracks[0];
64
+        const currentLocalTrack = getLocalVideoTrack(state['features/base/tracks']);
65
+        const currentJitsiTrack = currentLocalTrack?.jitsiTrack;
66
+
67
+        dispatch(replaceLocalTrack(currentJitsiTrack, track));
68
+
69
+        const { enabled: audioOnly } = state['features/base/audio-only'];
70
+
71
+        if (audioOnly) {
72
+            dispatch(setAudioOnly(false));
73
+        }
74
+    })
75
+    .catch((error: any) => {
76
+        console.log('ERROR creating ScreeSharing stream ', error);
77
+
78
+        setPictureInPictureEnabled(true);
79
+    });
80
+}

+ 284
- 0
react/features/base/tracks/actions.web.ts View File

@@ -0,0 +1,284 @@
1
+/* eslint-disable lines-around-comment */
2
+// @ts-ignore
3
+import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from '../../../../modules/UI/UIErrors';
4
+import { IState, IStore } from '../../app/types';
5
+import { showModeratedNotification } from '../../av-moderation/actions';
6
+import { shouldShowModeratedNotification } from '../../av-moderation/functions';
7
+import { setNoiseSuppressionEnabled } from '../../noise-suppression/actions';
8
+import { showNotification } from '../../notifications/actions';
9
+import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
10
+import { isModerationNotificationDisplayed } from '../../notifications/functions';
11
+// @ts-ignore
12
+import { stopReceiver } from '../../remote-control/actions';
13
+// @ts-ignore
14
+import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen-share/actions';
15
+import { isAudioOnlySharing, isScreenVideoShared } from '../../screen-share/functions';
16
+// @ts-ignore
17
+import { isScreenshotCaptureEnabled, toggleScreenshotCaptureSummary } from '../../screenshot-capture';
18
+// @ts-ignore
19
+import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
20
+import { setAudioOnly } from '../audio-only/actions';
21
+import { getCurrentConference } from '../conference/functions';
22
+import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
23
+import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
24
+import { setScreenshareMuted } from '../media/actions';
25
+import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
26
+/* eslint-enable lines-around-comment */
27
+
28
+import {
29
+    addLocalTrack,
30
+    replaceLocalTrack
31
+} from './actions.any';
32
+import {
33
+    createLocalTracksF,
34
+    getLocalDesktopTrack,
35
+    getLocalJitsiAudioTrack
36
+} from './functions';
37
+import { ShareOptions, ToggleScreenSharingOptions } from './types';
38
+
39
+export * from './actions.any';
40
+
41
+declare const APP: any;
42
+
43
+/**
44
+ * Signals that the local participant is ending screensharing or beginning the screensharing flow.
45
+ *
46
+ * @param {boolean} enabled - The state to toggle screen sharing to.
47
+ * @param {boolean} audioOnly - Only share system audio.
48
+ * @param {boolean} ignoreDidHaveVideo - Whether or not to ignore if video was on when sharing started.
49
+ * @param {Object} shareOptions - The options to be passed for capturing screenshare.
50
+ * @returns {Function}
51
+ */
52
+export function toggleScreensharing(
53
+        enabled?: boolean,
54
+        audioOnly = false,
55
+        ignoreDidHaveVideo = false,
56
+        shareOptions: ShareOptions = {}) {
57
+    return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
58
+        // check for A/V Moderation when trying to start screen sharing
59
+        if ((enabled || enabled === undefined)
60
+            && shouldShowModeratedNotification(MEDIA_TYPE.VIDEO, getState())) {
61
+            if (!isModerationNotificationDisplayed(MEDIA_TYPE.PRESENTER, getState())) {
62
+                dispatch(showModeratedNotification(MEDIA_TYPE.PRESENTER));
63
+            }
64
+
65
+            return Promise.reject();
66
+        }
67
+
68
+        if (getMultipleVideoSendingSupportFeatureFlag(getState())) {
69
+            return _toggleScreenSharing({
70
+                enabled,
71
+                audioOnly,
72
+                shareOptions
73
+            }, {
74
+                dispatch,
75
+                getState
76
+            });
77
+        }
78
+
79
+        return APP.conference.toggleScreenSharing(enabled, {
80
+            audioOnly,
81
+            desktopStream: shareOptions?.desktopStream
82
+        }, ignoreDidHaveVideo);
83
+    };
84
+}
85
+
86
+/**
87
+ * Displays a UI notification for screensharing failure based on the error passed.
88
+ *
89
+ * @private
90
+ * @param {Object} error - The error.
91
+ * @param {Object} store - The redux store.
92
+ * @returns {void}
93
+ */
94
+function _handleScreensharingError(
95
+        error: Error | AUDIO_ONLY_SCREEN_SHARE_NO_TRACK,
96
+        { dispatch }: IStore): void {
97
+    if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
98
+        return;
99
+    }
100
+    let descriptionKey, titleKey;
101
+
102
+    if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
103
+        descriptionKey = 'dialog.screenSharingPermissionDeniedError';
104
+        titleKey = 'dialog.screenSharingFailedTitle';
105
+    } else if (error.name === JitsiTrackErrors.CONSTRAINT_FAILED) {
106
+        descriptionKey = 'dialog.cameraConstraintFailedError';
107
+        titleKey = 'deviceError.cameraError';
108
+    } else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
109
+        descriptionKey = 'dialog.screenSharingFailed';
110
+        titleKey = 'dialog.screenSharingFailedTitle';
111
+    } else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
112
+        descriptionKey = 'notify.screenShareNoAudio';
113
+        titleKey = 'notify.screenShareNoAudioTitle';
114
+    }
115
+
116
+    dispatch(showNotification({
117
+        titleKey,
118
+        descriptionKey
119
+    }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
120
+}
121
+
122
+
123
+/**
124
+ * Applies the AudioMixer effect on the local audio track if applicable. If there is no local audio track, the desktop
125
+ * audio track is added to the conference.
126
+ *
127
+ * @private
128
+ * @param {JitsiLocalTrack} desktopAudioTrack - The audio track to be added to the conference.
129
+ * @param {*} state - The redux state.
130
+ * @returns {void}
131
+ */
132
+async function _maybeApplyAudioMixerEffect(desktopAudioTrack: any, state: IState): Promise<void> {
133
+    const localAudio = getLocalJitsiAudioTrack(state);
134
+    const conference = getCurrentConference(state);
135
+
136
+    if (localAudio) {
137
+        // If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing API.
138
+        const mixerEffect = new AudioMixerEffect(desktopAudioTrack);
139
+
140
+        await localAudio.setEffect(mixerEffect);
141
+    } else {
142
+        // If no local stream is present ( i.e. no input audio devices) we use the screen share audio
143
+        // stream as we would use a regular stream.
144
+        await conference.replaceTrack(null, desktopAudioTrack);
145
+    }
146
+}
147
+
148
+
149
+/**
150
+ * Toggles screen sharing.
151
+ *
152
+ * @private
153
+ * @param {boolean} enabled - The state to toggle screen sharing to.
154
+ * @param {Store} store - The redux store.
155
+ * @returns {void}
156
+ */
157
+async function _toggleScreenSharing(
158
+        {
159
+            enabled,
160
+            audioOnly = false,
161
+            shareOptions = {}
162
+        }: ToggleScreenSharingOptions,
163
+        store: IStore
164
+): Promise<void> {
165
+    const { dispatch, getState } = store;
166
+    const state = getState();
167
+    const audioOnlySharing = isAudioOnlySharing(state);
168
+    const screenSharing = isScreenVideoShared(state);
169
+    const conference = getCurrentConference(state);
170
+    const localAudio = getLocalJitsiAudioTrack(state);
171
+    const localScreenshare = getLocalDesktopTrack(state['features/base/tracks']);
172
+
173
+    // Toggle screenshare or audio-only share if the new state is not passed. Happens in the following two cases.
174
+    // 1. ShareAudioDialog passes undefined when the user hits continue in the share audio demo modal.
175
+    // 2. Toggle screenshare called from the external API.
176
+    const enable = audioOnly
177
+        ? enabled ?? !audioOnlySharing
178
+        : enabled ?? !screenSharing;
179
+    const screensharingDetails: { sourceType?: string; } = {};
180
+
181
+    if (enable) {
182
+        let tracks;
183
+
184
+        // Spot proxy stream.
185
+        if (shareOptions.desktopStream) {
186
+            tracks = [ shareOptions.desktopStream ];
187
+        } else {
188
+            const { _desktopSharingSourceDevice } = state['features/base/config'];
189
+
190
+            if (!shareOptions.desktopSharingSources && _desktopSharingSourceDevice) {
191
+                shareOptions.desktopSharingSourceDevice = _desktopSharingSourceDevice;
192
+            }
193
+
194
+            const options = {
195
+                devices: [ VIDEO_TYPE.DESKTOP ],
196
+                ...shareOptions
197
+            };
198
+
199
+            try {
200
+                tracks = await createLocalTracksF(options) as any[];
201
+            } catch (error) {
202
+                _handleScreensharingError(error as any, store);
203
+
204
+                throw error;
205
+            }
206
+        }
207
+
208
+        const desktopAudioTrack = tracks.find(track => track.getType() === MEDIA_TYPE.AUDIO);
209
+        const desktopVideoTrack = tracks.find(track => track.getType() === MEDIA_TYPE.VIDEO);
210
+
211
+        if (audioOnly) {
212
+            // Dispose the desktop track for audio-only screensharing.
213
+            desktopVideoTrack.dispose();
214
+
215
+            if (!desktopAudioTrack) {
216
+                _handleScreensharingError(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK, store);
217
+
218
+                throw new Error(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
219
+            }
220
+        } else if (desktopVideoTrack) {
221
+            if (localScreenshare) {
222
+                await dispatch(replaceLocalTrack(localScreenshare.jitsiTrack, desktopVideoTrack, conference));
223
+            } else {
224
+                await dispatch(addLocalTrack(desktopVideoTrack));
225
+            }
226
+            if (isScreenshotCaptureEnabled(state, false, true)) {
227
+                dispatch(toggleScreenshotCaptureSummary(true));
228
+            }
229
+            screensharingDetails.sourceType = desktopVideoTrack.sourceType;
230
+        }
231
+
232
+        // Apply the AudioMixer effect if there is a local audio track, add the desktop track to the conference
233
+        // otherwise without unmuting the microphone.
234
+        if (desktopAudioTrack) {
235
+            // Noise suppression doesn't work with desktop audio because we can't chain track effects yet, disable it
236
+            // first. We need to to wait for the effect to clear first or it might interfere with the audio mixer.
237
+            await dispatch(setNoiseSuppressionEnabled(false));
238
+            _maybeApplyAudioMixerEffect(desktopAudioTrack, state);
239
+            dispatch(setScreenshareAudioTrack(desktopAudioTrack));
240
+
241
+            // Handle the case where screen share was stopped from the browsers 'screen share in progress' window.
242
+            if (audioOnly) {
243
+                desktopAudioTrack?.on(
244
+                    JitsiTrackEvents.LOCAL_TRACK_STOPPED,
245
+                    () => dispatch(toggleScreensharing(undefined, true)));
246
+            }
247
+        }
248
+
249
+        // Disable audio-only or best performance mode if the user starts screensharing. This doesn't apply to
250
+        // audio-only screensharing.
251
+        const { enabled: bestPerformanceMode } = state['features/base/audio-only'];
252
+
253
+        if (bestPerformanceMode && !audioOnly) {
254
+            dispatch(setAudioOnly(false));
255
+        }
256
+    } else {
257
+        const { desktopAudioTrack } = state['features/screen-share'];
258
+
259
+        dispatch(stopReceiver());
260
+
261
+        dispatch(toggleScreenshotCaptureSummary(false));
262
+
263
+        // Mute the desktop track instead of removing it from the conference since we don't want the client to signal
264
+        // a source-remove to the remote peer for the screenshare track. Later when screenshare is enabled again, the
265
+        // same sender will be re-used without the need for signaling a new ssrc through source-add.
266
+        dispatch(setScreenshareMuted(true));
267
+        if (desktopAudioTrack) {
268
+            if (localAudio) {
269
+                localAudio.setEffect(undefined);
270
+            } else {
271
+                await conference.replaceTrack(desktopAudioTrack, null);
272
+            }
273
+            desktopAudioTrack.dispose();
274
+            dispatch(setScreenshareAudioTrack(null));
275
+        }
276
+    }
277
+
278
+    if (audioOnly) {
279
+        dispatch(setScreenAudioShareState(enable));
280
+    } else {
281
+        // Notify the external API.
282
+        APP.API.notifyScreenSharingStatusChanged(enable, screensharingDetails);
283
+    }
284
+}

+ 0
- 2
react/features/base/tracks/logger.ts View File

@@ -1,5 +1,3 @@
1
-// @flow
2
-
3 1
 import { getLogger } from '../logging/functions';
4 2
 
5 3
 export default getLogger('features/base/tracks');

+ 2
- 0
react/features/base/tracks/middleware.ts View File

@@ -42,6 +42,8 @@ import {
42 42
     trackMuteUnmuteFailed,
43 43
     trackNoDataFromSourceNotificationInfoChanged,
44 44
     trackRemoved
45
+
46
+    // @ts-ignore
45 47
 } from './actions';
46 48
 import {
47 49
     getLocalTrack,

+ 12
- 0
react/features/base/tracks/types.ts View File

@@ -17,3 +17,15 @@ export interface TrackOptions {
17 17
     micDeviceId?: string | null;
18 18
     timeout?: number;
19 19
 }
20
+
21
+export interface ToggleScreenSharingOptions {
22
+    audioOnly: boolean;
23
+    enabled?: boolean;
24
+    shareOptions: ShareOptions;
25
+}
26
+
27
+export interface ShareOptions {
28
+    desktopSharingSourceDevice?: string;
29
+    desktopSharingSources?: string[];
30
+    desktopStream?: any;
31
+}

+ 1
- 15
react/features/conference/components/web/Conference.js View File

@@ -21,7 +21,7 @@ import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
21 21
 import { toggleToolboxVisible } from '../../../toolbox/actions.any';
22 22
 import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
23 23
 import { JitsiPortal, Toolbox } from '../../../toolbox/components/web';
24
-import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
24
+import { LAYOUT_CLASSNAMES, getCurrentLayout } from '../../../video-layout';
25 25
 import { maybeShowSuboptimalExperienceNotification } from '../../functions';
26 26
 import {
27 27
     AbstractConference,
@@ -48,20 +48,6 @@ const FULL_SCREEN_EVENTS = [
48 48
     'fullscreenchange'
49 49
 ];
50 50
 
51
-/**
52
- * The CSS class to apply to the root element of the conference so CSS can
53
- * modify the app layout.
54
- *
55
- * @private
56
- * @type {Object}
57
- */
58
-export const LAYOUT_CLASSNAMES = {
59
-    [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip',
60
-    [LAYOUTS.TILE_VIEW]: 'tile-view',
61
-    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip',
62
-    [LAYOUTS.STAGE_FILMSTRIP_VIEW]: 'stage-filmstrip'
63
-};
64
-
65 51
 /**
66 52
  * The type of the React {@code Component} props of {@link Conference}.
67 53
  */

+ 1
- 2
react/features/filmstrip/components/web/ScreenshareFilmstrip.js View File

@@ -2,8 +2,7 @@
2 2
 import React from 'react';
3 3
 
4 4
 import { connect } from '../../../base/redux';
5
-import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference';
6
-import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
5
+import { LAYOUTS, LAYOUT_CLASSNAMES, getCurrentLayout } from '../../../video-layout';
7 6
 import {
8 7
     FILMSTRIP_TYPE
9 8
 } from '../../constants';

+ 1
- 2
react/features/filmstrip/components/web/StageFilmstrip.js View File

@@ -4,8 +4,7 @@ import React from 'react';
4 4
 import { getToolbarButtons } from '../../../base/config';
5 5
 import { isMobileBrowser } from '../../../base/environment/utils';
6 6
 import { connect } from '../../../base/redux';
7
-import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference';
8
-import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
7
+import { LAYOUTS, LAYOUT_CLASSNAMES, getCurrentLayout } from '../../../video-layout';
9 8
 import {
10 9
     ASPECT_RATIO_BREAKPOINT,
11 10
     FILMSTRIP_TYPE,

+ 1
- 0
react/features/filmstrip/components/web/Thumbnail.tsx View File

@@ -32,6 +32,7 @@ import {
32 32
 import { Participant } from '../../../base/participants/types';
33 33
 import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
34 34
 import { isTestModeEnabled } from '../../../base/testing/functions';
35
+// @ts-ignore
35 36
 import { trackStreamingStatusChanged, updateLastTrackVideoMediaEvent } from '../../../base/tracks/actions';
36 37
 import {
37 38
     getLocalAudioTrack,

+ 16
- 3
react/features/remote-control/actions.js View File

@@ -5,9 +5,15 @@ import $ from 'jquery';
5 5
 import { getMultipleVideoSendingSupportFeatureFlag } from '../base/config/functions.any';
6 6
 import { openDialog } from '../base/dialog';
7 7
 import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
8
-import { getParticipantDisplayName, getPinnedParticipant, pinParticipant } from '../base/participants';
8
+import {
9
+    getParticipantDisplayName,
10
+    getPinnedParticipant,
11
+    getVirtualScreenshareParticipantByOwnerId,
12
+    pinParticipant
13
+} from '../base/participants';
9 14
 import { getLocalDesktopTrack, getLocalVideoTrack, toggleScreensharing } from '../base/tracks';
10 15
 import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
16
+import { isScreenVideoShared } from '../screen-share';
11 17
 
12 18
 import {
13 19
     CAPTURE_EVENTS,
@@ -198,9 +204,12 @@ export function processPermissionRequestReply(participantId: string, event: Obje
198 204
                 // the remote control permissions has been granted
199 205
                 // pin the controlled participant
200 206
                 const pinnedParticipant = getPinnedParticipant(state);
207
+                const virtualScreenshareParticipantId = getVirtualScreenshareParticipantByOwnerId(state, participantId);
201 208
                 const pinnedId = pinnedParticipant?.id;
202 209
 
203
-                if (pinnedId !== participantId) {
210
+                if (virtualScreenshareParticipantId && pinnedId !== virtualScreenshareParticipantId) {
211
+                    dispatch(pinParticipant(virtualScreenshareParticipantId));
212
+                } else if (!virtualScreenshareParticipantId && pinnedId !== participantId) {
204 213
                     dispatch(pinParticipant(participantId));
205 214
                 }
206 215
             }
@@ -508,6 +517,10 @@ export function sendStartRequest() {
508 517
         const { sourceId } = track?.jitsiTrack || {};
509 518
         const { transport } = state['features/remote-control'].receiver;
510 519
 
520
+        if (typeof sourceId === 'undefined') {
521
+            return Promise.reject(new Error('Cannot identify screen for the remote control session'));
522
+        }
523
+
511 524
         return transport.sendRequest({
512 525
             name: REMOTE_CONTROL_MESSAGE_NAME,
513 526
             type: REQUESTS.start,
@@ -536,7 +549,7 @@ export function grant(participantId: string) {
536 549
         const tracks = state['features/base/tracks'];
537 550
         const isMultiStreamSupportEnabled = getMultipleVideoSendingSupportFeatureFlag(state);
538 551
         const track = isMultiStreamSupportEnabled ? getLocalDesktopTrack(tracks) : getLocalVideoTrack(tracks);
539
-        const isScreenSharing = track?.videoType === 'desktop';
552
+        const isScreenSharing = isScreenVideoShared(state);
540 553
         const { sourceType } = track?.jitsiTrack || {};
541 554
 
542 555
         if (isScreenSharing && sourceType === 'screen') {

+ 2
- 0
react/features/screen-share/components/ShareAudioDialog.tsx View File

@@ -6,6 +6,8 @@ import { translate } from '../../base/i18n/functions';
6 6
 import { connect } from '../../base/redux/functions';
7 7
 import { updateSettings } from '../../base/settings/actions';
8 8
 import { shouldHideShareAudioHelper } from '../../base/settings/functions.any';
9
+// eslint-disable-next-line lines-around-comment
10
+// @ts-ignore
9 11
 import { toggleScreensharing } from '../../base/tracks/actions';
10 12
 import Checkbox from '../../base/ui/components/web/Checkbox';
11 13
 import Dialog from '../../base/ui/components/web/Dialog';

+ 2
- 0
react/features/screen-share/components/ShareScreenWarningDialog.tsx View File

@@ -4,6 +4,8 @@ import { WithTranslation } from 'react-i18next';
4 4
 import { IStore } from '../../app/types';
5 5
 import { translate } from '../../base/i18n/functions';
6 6
 import { connect } from '../../base/redux/functions';
7
+// eslint-disable-next-line lines-around-comment
8
+// @ts-ignore
7 9
 import { toggleScreensharing } from '../../base/tracks/actions';
8 10
 import Dialog from '../../base/ui/components/web/Dialog';
9 11
 

+ 1
- 1
react/features/screen-share/reducer.ts View File

@@ -9,7 +9,7 @@ import {
9 9
 
10 10
 export interface IScreenShareState {
11 11
     captureFrameRate?: number;
12
-    desktopAudioTrack?: Object;
12
+    desktopAudioTrack?: any;
13 13
     isSharingAudio?: boolean;
14 14
 }
15 15
 

+ 13
- 0
react/features/video-layout/constants.ts View File

@@ -9,3 +9,16 @@ export const LAYOUTS = {
9 9
     VERTICAL_FILMSTRIP_VIEW: 'vertical-filmstrip-view',
10 10
     STAGE_FILMSTRIP_VIEW: 'stage-filmstrip-view'
11 11
 };
12
+
13
+
14
+/**
15
+ * The CSS class to apply so CSS can modify the app layout.
16
+ *
17
+ * @private
18
+ */
19
+export const LAYOUT_CLASSNAMES = {
20
+    [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip',
21
+    [LAYOUTS.TILE_VIEW]: 'tile-view',
22
+    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip',
23
+    [LAYOUTS.STAGE_FILMSTRIP_VIEW]: 'stage-filmstrip'
24
+};

+ 0
- 1
service/UI/UIEvents.js View File

@@ -40,7 +40,6 @@ export default {
40 40
      */
41 41
     TOGGLE_FILMSTRIP: 'UI.toggle_filmstrip',
42 42
 
43
-    TOGGLE_SCREENSHARING: 'UI.toggle_screensharing',
44 43
     HANGUP: 'UI.hangup',
45 44
     LOGOUT: 'UI.logout',
46 45
     VIDEO_DEVICE_CHANGED: 'UI.video_device_changed',

Loading…
Cancel
Save