Procházet zdrojové kódy

feat(external-api) add command for setting camera facing mode (#13541)

- added command for setting the camera facing mode remotely
- enhanced toggleVideo command to optionally accept the facing mode
- fix(startSilent) do not create audio track when start silent
factor2
Horatiu Muresan před 1 rokem
rodič
revize
c19d91a373
Žádný účet není propojen s e-mailovou adresou tvůrce revize

+ 1
- 1
conference.js Zobrazit soubor

@@ -441,7 +441,7 @@ export default {
441 441
 
442 442
         // Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
443 443
         // "Are you trying to speak?" ) even if the user joins the conference muted.
444
-        const initialDevices = config.disableInitialGUM ? [] : [ MEDIA_TYPE.AUDIO ];
444
+        const initialDevices = config.startSilent || config.disableInitialGUM ? [] : [ MEDIA_TYPE.AUDIO ];
445 445
         const requestedAudio = !config.disableInitialGUM;
446 446
         let requestedVideo = false;
447 447
 

+ 2
- 0
lang/main.json Zobrazit soubor

@@ -269,6 +269,8 @@
269 269
         "addMeetingNote": "Add a note about this meeting",
270 270
         "addOptionalNote": "Add a note (optional):",
271 271
         "allow": "Allow",
272
+        "allowToggleCameraDialog": "Do you allow {{initiatorName}} to toggle your camera facing mode?",
273
+        "allowToggleCameraTitle": "Allow toggle camera?",
272 274
         "alreadySharedVideoMsg": "Another participant is already sharing a video. This conference allows only one shared video at a time.",
273 275
         "alreadySharedVideoTitle": "Only one shared video is allowed at a time",
274 276
         "applicationWindow": "Application window",

+ 16
- 8
modules/API/API.js Zobrazit soubor

@@ -50,8 +50,8 @@ import {
50 50
 } from '../../react/features/base/participants/functions';
51 51
 import { updateSettings } from '../../react/features/base/settings/actions';
52 52
 import { getDisplayName } from '../../react/features/base/settings/functions.web';
53
-import { toggleCamera } from '../../react/features/base/tracks/actions.any';
54
-import { isToggleCameraEnabled } from '../../react/features/base/tracks/functions';
53
+import { setCameraFacingMode } from '../../react/features/base/tracks/actions.web';
54
+import { CAMERA_FACING_MODE_MESSAGE } from '../../react/features/base/tracks/constants';
55 55
 import {
56 56
     autoAssignToBreakoutRooms,
57 57
     closeBreakoutRoom,
@@ -395,12 +395,8 @@ function initCommands() {
395 395
             sendAnalytics(createApiEvent('film.strip.resize'));
396 396
             APP.store.dispatch(resizeFilmStrip(options.width));
397 397
         },
398
-        'toggle-camera': () => {
399
-            if (!isToggleCameraEnabled(APP.store.getState())) {
400
-                return;
401
-            }
402
-
403
-            APP.store.dispatch(toggleCamera());
398
+        'toggle-camera': facingMode => {
399
+            APP.store.dispatch(setCameraFacingMode(facingMode));
404 400
         },
405 401
         'toggle-camera-mirror': () => {
406 402
             const state = APP.store.getState();
@@ -529,6 +525,18 @@ function initCommands() {
529 525
                 logger.error('Failed sending endpoint text message', err);
530 526
             }
531 527
         },
528
+        'send-camera-facing-mode-message': (to, facingMode) => {
529
+            if (!to) {
530
+                logger.warn('Participant id not set');
531
+
532
+                return;
533
+            }
534
+
535
+            APP.conference.sendEndpointMessage(to, {
536
+                name: CAMERA_FACING_MODE_MESSAGE,
537
+                facingMode
538
+            });
539
+        },
532 540
         'overwrite-names': participantList => {
533 541
             logger.debug('Overwrite names command received');
534 542
 

+ 2
- 1
modules/API/external/external_api.js Zobrazit soubor

@@ -54,6 +54,7 @@ const commands = {
54 54
     removeBreakoutRoom: 'remove-breakout-room',
55 55
     resizeFilmStrip: 'resize-film-strip',
56 56
     resizeLargeVideo: 'resize-large-video',
57
+    sendCameraFacingMode: 'send-camera-facing-mode-message',
57 58
     sendChatMessage: 'send-chat-message',
58 59
     sendEndpointTextMessage: 'send-endpoint-text-message',
59 60
     sendParticipantToRoom: 'send-participant-to-room',
@@ -112,6 +113,7 @@ const events = {
112 113
     'data-channel-opened': 'dataChannelOpened',
113 114
     'device-list-changed': 'deviceListChanged',
114 115
     'display-name-change': 'displayNameChange',
116
+    'dominant-speaker-changed': 'dominantSpeakerChanged',
115 117
     'email-change': 'emailChange',
116 118
     'error-occurred': 'errorOccurred',
117 119
     'endpoint-text-message-received': 'endpointTextMessageReceived',
@@ -153,7 +155,6 @@ const events = {
153 155
     'video-mute-status-changed': 'videoMuteStatusChanged',
154 156
     'video-quality-changed': 'videoQualityChanged',
155 157
     'screen-sharing-status-changed': 'screenSharingStatusChanged',
156
-    'dominant-speaker-changed': 'dominantSpeakerChanged',
157 158
     'subject-change': 'subjectChange',
158 159
     'suspend-detected': 'suspendDetected',
159 160
     'tile-view-changed': 'tileViewChanged',

+ 56
- 2
react/features/base/tracks/actions.web.ts Zobrazit soubor

@@ -13,18 +13,23 @@ import { toggleScreenshotCaptureSummary } from '../../screenshot-capture/actions
13 13
 import { isScreenshotCaptureEnabled } from '../../screenshot-capture/functions';
14 14
 import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
15 15
 import { getCurrentConference } from '../conference/functions';
16
+import { openDialog } from '../dialog/actions';
16 17
 import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
17 18
 import { setScreenshareMuted } from '../media/actions';
18 19
 import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
19 20
 
20 21
 import {
21 22
     addLocalTrack,
22
-    replaceLocalTrack
23
+    replaceLocalTrack,
24
+    toggleCamera
23 25
 } from './actions.any';
26
+import AllowToggleCameraDialog from './components/web/AllowToggleCameraDialog';
24 27
 import {
25 28
     createLocalTracksF,
26 29
     getLocalDesktopTrack,
27
-    getLocalJitsiAudioTrack
30
+    getLocalJitsiAudioTrack,
31
+    getLocalVideoTrack,
32
+    isToggleCameraEnabled
28 33
 } from './functions';
29 34
 import { IShareOptions, IToggleScreenSharingOptions } from './types';
30 35
 
@@ -263,3 +268,52 @@ async function _toggleScreenSharing(
263 268
         APP.API.notifyScreenSharingStatusChanged(enable, screensharingDetails);
264 269
     }
265 270
 }
271
+
272
+/**
273
+ * Sets the camera facing mode(environment/user). If facing mode not provided, it will do a toggle.
274
+ *
275
+ * @param {string | undefined} facingMode - The selected facing mode.
276
+ * @returns {void}
277
+ */
278
+export function setCameraFacingMode(facingMode: string | undefined) {
279
+    return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
280
+        const state = getState();
281
+
282
+        if (!isToggleCameraEnabled(state)) {
283
+            return;
284
+        }
285
+
286
+        if (!facingMode) {
287
+            dispatch(toggleCamera());
288
+
289
+            return;
290
+        }
291
+
292
+        const tracks = state['features/base/tracks'];
293
+        const localVideoTrack = getLocalVideoTrack(tracks)?.jitsiTrack;
294
+
295
+        if (!tracks || !localVideoTrack) {
296
+            return;
297
+        }
298
+
299
+        const currentFacingMode = localVideoTrack.getCameraFacingMode();
300
+
301
+        if (currentFacingMode !== facingMode) {
302
+            dispatch(toggleCamera());
303
+        }
304
+    };
305
+}
306
+
307
+/**
308
+ * Signals to open the permission dialog for toggling camera remotely.
309
+ *
310
+ * @param {Function} onAllow - Callback to be executed if permission to toggle camera was granted.
311
+ * @param {string} initiatorId - The participant id of the requester.
312
+ * @returns {Object} - The open dialog action.
313
+ */
314
+export function openAllowToggleCameraDialog(onAllow: Function, initiatorId: string) {
315
+    return openDialog(AllowToggleCameraDialog, {
316
+        onAllow,
317
+        initiatorId
318
+    });
319
+}

+ 44
- 0
react/features/base/tracks/components/web/AllowToggleCameraDialog.tsx Zobrazit soubor

@@ -0,0 +1,44 @@
1
+import React from 'react';
2
+import { WithTranslation } from 'react-i18next';
3
+import { useSelector } from 'react-redux';
4
+
5
+import { IReduxState } from '../../../../app/types';
6
+import { translate } from '../../../i18n/functions';
7
+import { getParticipantDisplayName } from '../../../participants/functions';
8
+import Dialog from '../../../ui/components/web/Dialog';
9
+
10
+
11
+interface IProps extends WithTranslation {
12
+
13
+    /**
14
+     * The participant id of the toggle camera requester.
15
+     */
16
+    initiatorId: string;
17
+
18
+    /**
19
+     * Function to be invoked after permission to toggle camera granted.
20
+     */
21
+    onAllow: () => void;
22
+}
23
+
24
+/**
25
+ * Dialog to allow toggling camera remotely.
26
+ *
27
+ * @returns {JSX.Element} - The allow toggle camera dialog.
28
+ */
29
+const AllowToggleCameraDialog = ({ onAllow, t, initiatorId }: IProps): JSX.Element => {
30
+    const initiatorName = useSelector((state: IReduxState) => getParticipantDisplayName(state, initiatorId));
31
+
32
+    return (
33
+        <Dialog
34
+            ok = {{ translationKey: 'dialog.allow' }}
35
+            onSubmit = { onAllow }
36
+            titleKey = 'dialog.allowToggleCameraTitle'>
37
+            <div>
38
+                { t('dialog.allowToggleCameraDialog', { initiatorName }) }
39
+            </div>
40
+        </Dialog>
41
+    );
42
+};
43
+
44
+export default translate(AllowToggleCameraDialog);

+ 4
- 0
react/features/base/tracks/constants.ts Zobrazit soubor

@@ -0,0 +1,4 @@
1
+/**
2
+ * The payload name for remotely setting the camera facing mode message.
3
+ */
4
+export const CAMERA_FACING_MODE_MESSAGE = 'camera-facing-mode-message';

react/features/conference/middleware.ts → react/features/conference/middleware.any.ts Zobrazit soubor


+ 1
- 0
react/features/conference/middleware.native.ts Zobrazit soubor

@@ -0,0 +1 @@
1
+import './middleware.any';

+ 45
- 0
react/features/conference/middleware.web.ts Zobrazit soubor

@@ -0,0 +1,45 @@
1
+
2
+import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
3
+import { IJitsiConference } from '../base/conference/reducer';
4
+import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
5
+import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
6
+import { openAllowToggleCameraDialog, setCameraFacingMode } from '../base/tracks/actions.web';
7
+import { CAMERA_FACING_MODE_MESSAGE } from '../base/tracks/constants';
8
+
9
+import './middleware.any';
10
+
11
+MiddlewareRegistry.register(_store => next => action => {
12
+    switch (action.type) {
13
+    case CONFERENCE_JOINED: {
14
+        _addSetCameraFacingModeListener(action.conference);
15
+        break;
16
+    }
17
+    }
18
+
19
+    return next(action);
20
+});
21
+
22
+/**
23
+ * Registers listener for {@link JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED} that
24
+ * will perform various chat related activities.
25
+ *
26
+ * @param {IJitsiConference} conference - The conference.
27
+ * @returns {void}
28
+ */
29
+function _addSetCameraFacingModeListener(conference: IJitsiConference) {
30
+    conference.on(
31
+        JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
32
+        (...args: any) => {
33
+            if (args && args.length >= 2) {
34
+                const [ sender, eventData ] = args;
35
+
36
+                if (eventData.name === CAMERA_FACING_MODE_MESSAGE) {
37
+                    APP.store.dispatch(openAllowToggleCameraDialog(
38
+                        /* onAllow */ () => APP.store.dispatch(setCameraFacingMode(eventData.facingMode)),
39
+                        /* initiatorId */ sender._id
40
+                    ));
41
+                }
42
+            }
43
+        }
44
+    );
45
+}

Načítá se…
Zrušit
Uložit