Browse Source

feat(screenshare): Audio only screenshare (#8922)

* audio only screen share implementation

* clean up

* handle stop screen share from chrome window

* update icon
j8
Andrei Gavrilescu 4 years ago
parent
commit
6d3d65da03
No account linked to committer's email address

+ 45
- 3
conference.js View File

6
 
6
 
7
 import { openConnection } from './connection';
7
 import { openConnection } from './connection';
8
 import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
8
 import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
9
+import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
9
 import AuthHandler from './modules/UI/authentication/AuthHandler';
10
 import AuthHandler from './modules/UI/authentication/AuthHandler';
10
 import UIUtil from './modules/UI/util/UIUtil';
11
 import UIUtil from './modules/UI/util/UIUtil';
11
 import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
12
 import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
126
     makePrecallTest
127
     makePrecallTest
127
 } from './react/features/prejoin';
128
 } from './react/features/prejoin';
128
 import { disableReceiver, stopReceiver } from './react/features/remote-control';
129
 import { disableReceiver, stopReceiver } from './react/features/remote-control';
130
+import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
129
 import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
131
 import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
130
 import { setSharedVideoStatus } from './react/features/shared-video/actions';
132
 import { setSharedVideoStatus } from './react/features/shared-video/actions';
131
 import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
133
 import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
1546
             this._desktopAudioStream = undefined;
1548
             this._desktopAudioStream = undefined;
1547
         }
1549
         }
1548
 
1550
 
1551
+        APP.store.dispatch(setScreenAudioShareState(false));
1552
+
1549
         if (didHaveVideo) {
1553
         if (didHaveVideo) {
1550
             promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
1554
             promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
1551
                 .then(([ stream ]) => {
1555
                 .then(([ stream ]) => {
1662
                 = this._turnScreenSharingOff.bind(this, didHaveVideo);
1666
                 = this._turnScreenSharingOff.bind(this, didHaveVideo);
1663
 
1667
 
1664
             const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
1668
             const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
1669
+            const dekstopAudioStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
1670
+
1671
+            if (dekstopAudioStream) {
1672
+                dekstopAudioStream.on(
1673
+                    JitsiTrackEvents.LOCAL_TRACK_STOPPED,
1674
+                    () => {
1675
+                        logger.debug(`Local screensharing audio track stopped. ${this.isSharingScreen}`);
1676
+
1677
+                        // Handle case where screen share was stopped from  the browsers 'screen share in progress'
1678
+                        // window. If audio screen sharing is stopped via the normal UX flow this point shouldn't
1679
+                        // be reached.
1680
+                        isScreenAudioShared(APP.store.getState())
1681
+                            && this._untoggleScreenSharing
1682
+                            && this._untoggleScreenSharing();
1683
+                    }
1684
+                );
1685
+            }
1665
 
1686
 
1666
             if (desktopVideoStream) {
1687
             if (desktopVideoStream) {
1667
                 desktopVideoStream.on(
1688
                 desktopVideoStream.on(
1830
 
1851
 
1831
         return this._createDesktopTrack(options)
1852
         return this._createDesktopTrack(options)
1832
             .then(async streams => {
1853
             .then(async streams => {
1833
-                const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
1854
+                let desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
1855
+
1856
+                this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
1857
+
1858
+                const { audioOnly = false } = options;
1859
+
1860
+                // If we're in audio only mode dispose of the video track otherwise the screensharing state will be
1861
+                // inconsistent.
1862
+                if (audioOnly) {
1863
+                    desktopVideoStream.dispose();
1864
+                    desktopVideoStream = undefined;
1865
+
1866
+                    if (!this._desktopAudioStream) {
1867
+                        return Promise.reject(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
1868
+                    }
1869
+                }
1834
 
1870
 
1835
                 if (desktopVideoStream) {
1871
                 if (desktopVideoStream) {
1836
                     logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
1872
                     logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
1837
                     await this.useVideoStream(desktopVideoStream);
1873
                     await this.useVideoStream(desktopVideoStream);
1838
                 }
1874
                 }
1839
 
1875
 
1840
-                this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
1841
 
1876
 
1842
                 if (this._desktopAudioStream) {
1877
                 if (this._desktopAudioStream) {
1843
                     // If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
1878
                     // If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
1850
                         // If no local stream is present ( i.e. no input audio devices) we use the screen share audio
1885
                         // If no local stream is present ( i.e. no input audio devices) we use the screen share audio
1851
                         // stream as we would use a regular stream.
1886
                         // stream as we would use a regular stream.
1852
                         await this.useAudioStream(this._desktopAudioStream);
1887
                         await this.useAudioStream(this._desktopAudioStream);
1888
+
1853
                     }
1889
                     }
1890
+                    APP.store.dispatch(setScreenAudioShareState(true));
1854
                 }
1891
                 }
1855
             })
1892
             })
1856
             .then(() => {
1893
             .then(() => {
1918
         } else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
1955
         } else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
1919
             descriptionKey = 'dialog.screenSharingFailed';
1956
             descriptionKey = 'dialog.screenSharingFailed';
1920
             titleKey = 'dialog.screenSharingFailedTitle';
1957
             titleKey = 'dialog.screenSharingFailedTitle';
1958
+        } else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
1959
+            descriptionKey = 'notify.screenShareNoAudio';
1960
+            titleKey = 'notify.screenShareNoAudioTitle';
1921
         }
1961
         }
1922
 
1962
 
1923
         APP.UI.messageHandler.showError({
1963
         APP.UI.messageHandler.showError({
2409
         });
2449
         });
2410
 
2450
 
2411
         APP.UI.addListener(
2451
         APP.UI.addListener(
2412
-            UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
2452
+            UIEvents.TOGGLE_SCREENSHARING, audioOnly => {
2453
+                this.toggleScreenSharing(undefined, { audioOnly });
2454
+            }
2413
         );
2455
         );
2414
 
2456
 
2415
         /* eslint-disable max-params */
2457
         /* eslint-disable max-params */

+ 1
- 1
config.js View File

428
     // toolbarButtons: [
428
     // toolbarButtons: [
429
     //    'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
429
     //    'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
430
     //    'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
430
     //    'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
431
-    //    'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
431
+    //    'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
432
     //    'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
432
     //    'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
433
     //    'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
433
     //    'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
434
     // ],
434
     // ],

+ 4
- 0
lang/main.json View File

511
         "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
511
         "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
512
         "passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
512
         "passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
513
         "raisedHand": "{{name}} would like to speak.",
513
         "raisedHand": "{{name}} would like to speak.",
514
+        "screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
515
+        "screenShareNoAudioTitle": "Share audio was not chcked",
514
         "somebody": "Somebody",
516
         "somebody": "Somebody",
515
         "startSilentTitle": "You joined with no audio output!",
517
         "startSilentTitle": "You joined with no audio output!",
516
         "startSilentDescription": "Rejoin the meeting to enable audio",
518
         "startSilentDescription": "Rejoin the meeting to enable audio",
752
             "remoteVideoMute": "Disable camera of participant",
754
             "remoteVideoMute": "Disable camera of participant",
753
             "security": "Security options",
755
             "security": "Security options",
754
             "Settings": "Toggle settings",
756
             "Settings": "Toggle settings",
757
+            "shareaudio": "Share audio",
755
             "sharedvideo": "Toggle YouTube video sharing",
758
             "sharedvideo": "Toggle YouTube video sharing",
756
             "shareRoom": "Invite someone",
759
             "shareRoom": "Invite someone",
757
             "shareYourScreen": "Toggle screenshare",
760
             "shareYourScreen": "Toggle screenshare",
811
         "raiseYourHand": "Raise your hand",
814
         "raiseYourHand": "Raise your hand",
812
         "security": "Security options",
815
         "security": "Security options",
813
         "Settings": "Settings",
816
         "Settings": "Settings",
817
+        "shareaudio": "Share audio",
814
         "sharedvideo": "Share a YouTube video",
818
         "sharedvideo": "Share a YouTube video",
815
         "shareRoom": "Invite someone",
819
         "shareRoom": "Invite someone",
816
         "shortcuts": "View shortcuts",
820
         "shortcuts": "View shortcuts",

+ 7
- 0
modules/UI/UIErrors.js View File

8
  * @type {{FEEDBACK_REQUEST_IN_PROGRESS: string}}
8
  * @type {{FEEDBACK_REQUEST_IN_PROGRESS: string}}
9
  */
9
  */
10
 export const FEEDBACK_REQUEST_IN_PROGRESS = 'FeedbackRequestInProgress';
10
 export const FEEDBACK_REQUEST_IN_PROGRESS = 'FeedbackRequestInProgress';
11
+
12
+/**
13
+ * Indicated an attempted audio only screen share session with no audio track present
14
+ *
15
+ * @type {{AUDIO_ONLY_SCREEN_SHARE_NO_TRACK: string}}
16
+ */
17
+export const AUDIO_ONLY_SCREEN_SHARE_NO_TRACK = 'AudioOnlyScreenShareNoTrack';

+ 1
- 0
react/features/app/reducers.any.js View File

44
 import '../recording/reducer';
44
 import '../recording/reducer';
45
 import '../settings/reducer';
45
 import '../settings/reducer';
46
 import '../subtitles/reducer';
46
 import '../subtitles/reducer';
47
+import '../screen-share/reducer';
47
 import '../toolbox/reducer';
48
 import '../toolbox/reducer';
48
 import '../transcribing/reducer';
49
 import '../transcribing/reducer';
49
 import '../video-layout/reducer';
50
 import '../video-layout/reducer';

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

32
     }
32
     }
33
     case TOGGLE_SCREENSHARING: {
33
     case TOGGLE_SCREENSHARING: {
34
         if (typeof APP === 'object') {
34
         if (typeof APP === 'object') {
35
-            APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
35
+            APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING, action.audioOnly);
36
         }
36
         }
37
 
37
 
38
         break;
38
         break;

+ 1
- 1
react/features/base/config/constants.js View File

16
 export const TOOLBAR_BUTTONS = [
16
 export const TOOLBAR_BUTTONS = [
17
     'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
17
     'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
18
     'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
18
     'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
19
-    'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
19
+    'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
20
     'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
20
     'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
21
     'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
21
     'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
22
     'security', 'toggle-camera'
22
     'security', 'toggle-camera'

+ 9
- 0
react/features/base/environment/environment.js View File

41
         .includes(browserName);
41
         .includes(browserName);
42
 }
42
 }
43
 
43
 
44
+/**
45
+ * Returns whether or not the current OS is Mac.
46
+ *
47
+ * @returns {boolean}
48
+ */
49
+export function isMacOS() {
50
+    return Platform.OS === 'macos';
51
+}
52
+
44
 /**
53
 /**
45
  * Returns whether or not the current browser or the list of passed in browsers
54
  * Returns whether or not the current browser or the list of passed in browsers
46
  * is considered suboptimal. Suboptimal means it is a supported browser but has
55
  * is considered suboptimal. Suboptimal means it is a supported browser but has

+ 1
- 0
react/features/base/icons/svg/index.js View File

99
 export { default as IconSignalLevel1 } from './signal_cellular_1.svg';
99
 export { default as IconSignalLevel1 } from './signal_cellular_1.svg';
100
 export { default as IconSignalLevel2 } from './signal_cellular_2.svg';
100
 export { default as IconSignalLevel2 } from './signal_cellular_2.svg';
101
 export { default as IconShare } from './share.svg';
101
 export { default as IconShare } from './share.svg';
102
+export { default as IconShareAudio } from './share-audio.svg';
102
 export { default as IconShareDesktop } from './share-desktop.svg';
103
 export { default as IconShareDesktop } from './share-desktop.svg';
103
 export { default as IconShareDoc } from './share-doc.svg';
104
 export { default as IconShareDoc } from './share-doc.svg';
104
 export { default as IconShareVideo } from './shared-video.svg';
105
 export { default as IconShareVideo } from './shared-video.svg';

+ 3
- 0
react/features/base/icons/svg/share-audio.svg View File

1
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.31658 3.06949L4.99999 6.66664H2.49999C2.03975 6.66664 1.66666 7.03974 1.66666 7.49998V12.5C1.66666 12.9602 2.03975 13.3333 2.49999 13.3333H4.99999L9.31658 16.9305C9.39146 16.9929 9.48585 17.027 9.58332 17.027C9.81344 17.027 9.99999 16.8405 9.99999 16.6104V3.38958C9.99999 3.2921 9.96582 3.19772 9.90341 3.12283C9.7561 2.94605 9.49336 2.92217 9.31658 3.06949ZM3.33332 8.33331H5.60341L8.33332 6.05838V13.9416L5.60341 11.6666H3.33332V8.33331ZM11.6667 6.66664C13.5076 6.66664 15 8.15903 15 9.99998C15 11.8409 13.5076 13.3333 11.6667 13.3333V11.6666C12.5871 11.6666 13.3333 10.9205 13.3333 9.99998C13.3333 9.0795 12.5871 8.33331 11.6667 8.33331V6.66664ZM11.6667 3.33331C15.3486 3.33331 18.3333 6.31808 18.3333 9.99998C18.3333 13.6819 15.3486 16.6666 11.6667 16.6666V15C14.4281 15 16.6667 12.7614 16.6667 9.99998C16.6667 7.23855 14.4281 4.99998 11.6667 4.99998V3.33331Z" fill="white"/>
3
+</svg>

+ 5
- 2
react/features/base/tracks/actions.js View File

257
  * Signals that the local participant is ending screensharing or beginning the
257
  * Signals that the local participant is ending screensharing or beginning the
258
  * screensharing flow.
258
  * screensharing flow.
259
  *
259
  *
260
+ * @param {boolean} audioOnly - Only share system audio.
260
  * @returns {{
261
  * @returns {{
261
  *     type: TOGGLE_SCREENSHARING,
262
  *     type: TOGGLE_SCREENSHARING,
263
+ *     audioOnly: boolean
262
  * }}
264
  * }}
263
  */
265
  */
264
-export function toggleScreensharing() {
266
+export function toggleScreensharing(audioOnly = false) {
265
     return {
267
     return {
266
-        type: TOGGLE_SCREENSHARING
268
+        type: TOGGLE_SCREENSHARING,
269
+        audioOnly
267
     };
270
     };
268
 }
271
 }
269
 
272
 

+ 1
- 1
react/features/base/tracks/middleware.js View File

135
 
135
 
136
     case TOGGLE_SCREENSHARING:
136
     case TOGGLE_SCREENSHARING:
137
         if (typeof APP === 'object') {
137
         if (typeof APP === 'object') {
138
-            APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
138
+            APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING, action.audioOnly);
139
         }
139
         }
140
         break;
140
         break;
141
 
141
 

+ 12
- 0
react/features/screen-share/actionTypes.js View File

1
+// @flow
2
+
3
+/**
4
+ * Type of action which sets the current state of screen audio sharing.
5
+ *
6
+ * {
7
+ *     type: SET_SCREEN_AUDIO_SHARE_STATE,
8
+ *     isSharingAudio: boolean
9
+ * }
10
+ */
11
+export const SET_SCREEN_AUDIO_SHARE_STATE = 'SET_SCREEN_AUDIO_SHARE_STATE';
12
+

+ 19
- 0
react/features/screen-share/actions.js View File

1
+// @flow
2
+
3
+import { SET_SCREEN_AUDIO_SHARE_STATE } from './actionTypes';
4
+
5
+/**
6
+ * Updates the current known status of the shared video.
7
+ *
8
+ * @param {boolean} isSharingAudio - Is audio currently being shared or not.
9
+ * @returns {{
10
+ *     type: SET_SCREEN_AUDIO_SHARE_STATE,
11
+ *     isSharingAudio: boolean
12
+ * }}
13
+ */
14
+export function setScreenAudioShareState(isSharingAudio: boolean) {
15
+    return {
16
+        type: SET_SCREEN_AUDIO_SHARE_STATE,
17
+        isSharingAudio
18
+    };
19
+}

+ 25
- 0
react/features/screen-share/functions.js View File

1
+// @flow
2
+
3
+import { isMacOS } from '../base/environment';
4
+import { browser } from '../base/lib-jitsi-meet';
5
+
6
+
7
+/**
8
+ * State of audio sharing.
9
+ *
10
+ * @param {Object} state - The state of the application.
11
+ * @returns {boolean}
12
+ */
13
+export function isScreenAudioShared(state: Object) {
14
+    return state['features/screen-share'].isSharingAudio;
15
+}
16
+
17
+/**
18
+ * Returns the visibility of the audio only screen share button. Currently electron on mac os doesn't
19
+ * have support for this functionality.
20
+ *
21
+ * @returns {boolean}
22
+ */
23
+export function isScreenAudioSupported() {
24
+    return !(browser.isElectron() && isMacOS());
25
+}

+ 4
- 0
react/features/screen-share/index.js View File

1
+export * from './actions';
2
+export * from './actionTypes';
3
+export * from './functions';
4
+

+ 22
- 0
react/features/screen-share/reducer.js View File

1
+
2
+import { ReducerRegistry } from '../base/redux';
3
+
4
+import { SET_SCREEN_AUDIO_SHARE_STATE } from './actionTypes';
5
+
6
+/**
7
+ * Reduces the Redux actions of the feature features/screen-share.
8
+ */
9
+ReducerRegistry.register('features/screen-share', (state = {}, action) => {
10
+    const { isSharingAudio } = action;
11
+
12
+    switch (action.type) {
13
+    case SET_SCREEN_AUDIO_SHARE_STATE:
14
+        return {
15
+            ...state,
16
+            isSharingAudio
17
+        };
18
+
19
+    default:
20
+        return state;
21
+    }
22
+});

+ 23
- 3
react/features/stream-effects/audio-mixer/AudioMixerEffect.js View File

14
      */
14
      */
15
     _mixAudio: Object;
15
     _mixAudio: Object;
16
 
16
 
17
+    /**
18
+     * MediaStream resulted from mixing.
19
+     */
20
+    _mixedMediaStream: Object;
21
+
22
+    /**
23
+     * MediaStreamTrack obtained from mixed stream.
24
+     */
25
+    _mixedMediaTrack: Object;
26
+
17
     /**
27
     /**
18
      * Original MediaStream from the JitsiLocalTrack that uses this effect.
28
      * Original MediaStream from the JitsiLocalTrack that uses this effect.
19
      */
29
      */
68
         this._audioMixer.addMediaStream(this._mixAudio.getOriginalStream());
78
         this._audioMixer.addMediaStream(this._mixAudio.getOriginalStream());
69
         this._audioMixer.addMediaStream(this._originalStream);
79
         this._audioMixer.addMediaStream(this._originalStream);
70
 
80
 
71
-        return this._audioMixer.start();
81
+        this._mixedMediaStream = this._audioMixer.start();
82
+        this._mixedMediaTrack = this._mixedMediaStream.getTracks()[0];
83
+
84
+        // Sync the resulting mixed track enabled state with that of the track using the effect.
85
+        this.setMuted(!this._originalTrack.enabled);
86
+        this._originalTrack.enabled = true;
87
+
88
+        return this._mixedMediaStream;
72
     }
89
     }
73
 
90
 
74
     /**
91
     /**
77
      * @returns {void}
94
      * @returns {void}
78
      */
95
      */
79
     stopEffect() {
96
     stopEffect() {
97
+        // Match state of the original track with that of the mixer track, not doing so can
98
+        // result in an inconsistent state e.g. redux state is muted yet track is enabled.
99
+        this._originalTrack.enabled = this._mixedMediaTrack.enabled;
80
         this._audioMixer.reset();
100
         this._audioMixer.reset();
81
     }
101
     }
82
 
102
 
87
      * @returns {void}
107
      * @returns {void}
88
      */
108
      */
89
     setMuted(muted: boolean) {
109
     setMuted(muted: boolean) {
90
-        this._originalTrack.enabled = !muted;
110
+        this._mixedMediaTrack.enabled = !muted;
91
     }
111
     }
92
 
112
 
93
     /**
113
     /**
96
      * @returns {boolean}
116
      * @returns {boolean}
97
      */
117
      */
98
     isMuted() {
118
     isMuted() {
99
-        return !this._originalTrack.enabled;
119
+        return !this._mixedMediaTrack.enabled;
100
     }
120
     }
101
 }
121
 }

+ 26
- 3
react/features/toolbox/components/web/Toolbox.js View File

23
     IconPresentation,
23
     IconPresentation,
24
     IconRaisedHand,
24
     IconRaisedHand,
25
     IconRec,
25
     IconRec,
26
+    IconShareAudio,
26
     IconShareDesktop
27
     IconShareDesktop
27
 } from '../../../base/icons';
28
 } from '../../../base/icons';
28
 import JitsiMeetJS from '../../../base/lib-jitsi-meet';
29
 import JitsiMeetJS from '../../../base/lib-jitsi-meet';
47
     LiveStreamButton,
48
     LiveStreamButton,
48
     RecordButton
49
     RecordButton
49
 } from '../../../recording';
50
 } from '../../../recording';
51
+import { isScreenAudioShared, isScreenAudioSupported } from '../../../screen-share/';
50
 import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
52
 import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
51
 import {
53
 import {
52
     SETTINGS_TABS,
54
     SETTINGS_TABS,
252
         this._onToolbarToggleProfile = this._onToolbarToggleProfile.bind(this);
254
         this._onToolbarToggleProfile = this._onToolbarToggleProfile.bind(this);
253
         this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
255
         this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
254
         this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
256
         this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
257
+        this._onToolbarToggleShareAudio = this._onToolbarToggleShareAudio.bind(this);
255
         this._onToolbarOpenLocalRecordingInfoDialog = this._onToolbarOpenLocalRecordingInfoDialog.bind(this);
258
         this._onToolbarOpenLocalRecordingInfoDialog = this._onToolbarOpenLocalRecordingInfoDialog.bind(this);
256
         this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
259
         this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
257
     }
260
     }
487
      * Dispatches an action to toggle screensharing.
490
      * Dispatches an action to toggle screensharing.
488
      *
491
      *
489
      * @private
492
      * @private
493
+     * @param {boolean} audioOnly - Only share system audio.
490
      * @returns {void}
494
      * @returns {void}
491
      */
495
      */
492
-    _doToggleScreenshare() {
496
+    _doToggleScreenshare(audioOnly = false) {
493
         if (this.props._desktopSharingEnabled) {
497
         if (this.props._desktopSharingEnabled) {
494
-            this.props.dispatch(toggleScreensharing());
498
+            this.props.dispatch(toggleScreensharing(audioOnly));
495
         }
499
         }
496
     }
500
     }
497
 
501
 
857
         this._doToggleScreenshare();
861
         this._doToggleScreenshare();
858
     }
862
     }
859
 
863
 
864
+    _onToolbarToggleShareAudio: () => void;
865
+
866
+    /**
867
+     * Handles toggle share audio action.
868
+     *
869
+     * @returns {void}
870
+     */
871
+    _onToolbarToggleShareAudio() {
872
+        this._closeOverflowMenuIfOpen();
873
+        this._doToggleScreenshare(true);
874
+    }
875
+
860
     _onToolbarOpenLocalRecordingInfoDialog: () => void;
876
     _onToolbarOpenLocalRecordingInfoDialog: () => void;
861
 
877
 
862
     /**
878
     /**
983
                 && <SharedVideoButton
999
                 && <SharedVideoButton
984
                     key = 'sharedvideo'
1000
                     key = 'sharedvideo'
985
                     showLabel = { true } />,
1001
                     showLabel = { true } />,
1002
+            this._shouldShowButton('shareaudio') && isScreenAudioSupported()
1003
+                && <OverflowMenuItem
1004
+                    accessibilityLabel = { t('toolbar.accessibilityLabel.shareaudio') }
1005
+                    icon = { IconShareAudio }
1006
+                    key = 'shareaudio'
1007
+                    onClick = { this._onToolbarToggleShareAudio }
1008
+                    text = { t('toolbar.shareaudio') } />,
986
             this._shouldShowButton('etherpad')
1009
             this._shouldShowButton('etherpad')
987
                 && <SharedDocumentButton
1010
                 && <SharedDocumentButton
988
                     key = 'etherpad'
1011
                     key = 'etherpad'
1322
         _locked: locked,
1345
         _locked: locked,
1323
         _overflowMenuVisible: overflowMenuVisible,
1346
         _overflowMenuVisible: overflowMenuVisible,
1324
         _raisedHand: localParticipant.raisedHand,
1347
         _raisedHand: localParticipant.raisedHand,
1325
-        _screensharing: localVideo && localVideo.videoType === 'desktop',
1348
+        _screensharing: (localVideo && localVideo.videoType === 'desktop') || isScreenAudioShared(state),
1326
         _visible: isToolboxVisible(state),
1349
         _visible: isToolboxVisible(state),
1327
         _visibleButtons: getToolbarButtons(state)
1350
         _visibleButtons: getToolbarButtons(state)
1328
     };
1351
     };

Loading…
Cancel
Save