Skip to content

Commit

Permalink
feat(external-api) add command for setting camera facing mode (#13541)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
horymury committed Aug 18, 2023
1 parent 1134634 commit 0b1bdaf
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 12 deletions.
2 changes: 1 addition & 1 deletion conference.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export default {

// Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
// "Are you trying to speak?" ) even if the user joins the conference muted.
const initialDevices = config.disableInitialGUM ? [] : [ MEDIA_TYPE.AUDIO ];
const initialDevices = config.startSilent || config.disableInitialGUM ? [] : [ MEDIA_TYPE.AUDIO ];
const requestedAudio = !config.disableInitialGUM;
let requestedVideo = false;

Expand Down
2 changes: 2 additions & 0 deletions lang/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@
"addMeetingNote": "Add a note about this meeting",
"addOptionalNote": "Add a note (optional):",
"allow": "Allow",
"allowToggleCameraDialog": "Do you allow {{initiatorName}} to toggle your camera facing mode?",
"allowToggleCameraTitle": "Allow toggle camera?",
"alreadySharedVideoMsg": "Another participant is already sharing a video. This conference allows only one shared video at a time.",
"alreadySharedVideoTitle": "Only one shared video is allowed at a time",
"applicationWindow": "Application window",
Expand Down
24 changes: 16 additions & 8 deletions modules/API/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import {
} from '../../react/features/base/participants/functions';
import { updateSettings } from '../../react/features/base/settings/actions';
import { getDisplayName } from '../../react/features/base/settings/functions.web';
import { toggleCamera } from '../../react/features/base/tracks/actions.any';
import { isToggleCameraEnabled } from '../../react/features/base/tracks/functions';
import { setCameraFacingMode } from '../../react/features/base/tracks/actions.web';
import { CAMERA_FACING_MODE_MESSAGE } from '../../react/features/base/tracks/constants';
import {
autoAssignToBreakoutRooms,
closeBreakoutRoom,
Expand Down Expand Up @@ -395,12 +395,8 @@ function initCommands() {
sendAnalytics(createApiEvent('film.strip.resize'));
APP.store.dispatch(resizeFilmStrip(options.width));
},
'toggle-camera': () => {
if (!isToggleCameraEnabled(APP.store.getState())) {
return;
}

APP.store.dispatch(toggleCamera());
'toggle-camera': facingMode => {
APP.store.dispatch(setCameraFacingMode(facingMode));
},
'toggle-camera-mirror': () => {
const state = APP.store.getState();
Expand Down Expand Up @@ -529,6 +525,18 @@ function initCommands() {
logger.error('Failed sending endpoint text message', err);
}
},
'send-camera-facing-mode-message': (to, facingMode) => {
if (!to) {
logger.warn('Participant id not set');

return;
}

APP.conference.sendEndpointMessage(to, {
name: CAMERA_FACING_MODE_MESSAGE,
facingMode
});
},
'overwrite-names': participantList => {
logger.debug('Overwrite names command received');

Expand Down
3 changes: 2 additions & 1 deletion modules/API/external/external_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const commands = {
removeBreakoutRoom: 'remove-breakout-room',
resizeFilmStrip: 'resize-film-strip',
resizeLargeVideo: 'resize-large-video',
sendCameraFacingMode: 'send-camera-facing-mode-message',
sendChatMessage: 'send-chat-message',
sendEndpointTextMessage: 'send-endpoint-text-message',
sendParticipantToRoom: 'send-participant-to-room',
Expand Down Expand Up @@ -111,6 +112,7 @@ const events = {
'data-channel-opened': 'dataChannelOpened',
'device-list-changed': 'deviceListChanged',
'display-name-change': 'displayNameChange',
'dominant-speaker-changed': 'dominantSpeakerChanged',
'email-change': 'emailChange',
'error-occurred': 'errorOccurred',
'endpoint-text-message-received': 'endpointTextMessageReceived',
Expand Down Expand Up @@ -152,7 +154,6 @@ const events = {
'video-mute-status-changed': 'videoMuteStatusChanged',
'video-quality-changed': 'videoQualityChanged',
'screen-sharing-status-changed': 'screenSharingStatusChanged',
'dominant-speaker-changed': 'dominantSpeakerChanged',
'subject-change': 'subjectChange',
'suspend-detected': 'suspendDetected',
'tile-view-changed': 'tileViewChanged',
Expand Down
58 changes: 56 additions & 2 deletions react/features/base/tracks/actions.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ import { toggleScreenshotCaptureSummary } from '../../screenshot-capture/actions
import { isScreenshotCaptureEnabled } from '../../screenshot-capture/functions';
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
import { getCurrentConference } from '../conference/functions';
import { openDialog } from '../dialog/actions';
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
import { setScreenshareMuted } from '../media/actions';
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';

import {
addLocalTrack,
replaceLocalTrack
replaceLocalTrack,
toggleCamera
} from './actions.any';
import AllowToggleCameraDialog from './components/web/AllowToggleCameraDialog';
import {
createLocalTracksF,
getLocalDesktopTrack,
getLocalJitsiAudioTrack
getLocalJitsiAudioTrack,
getLocalVideoTrack,
isToggleCameraEnabled
} from './functions';
import { IShareOptions, IToggleScreenSharingOptions } from './types';

Expand Down Expand Up @@ -263,3 +268,52 @@ async function _toggleScreenSharing(
APP.API.notifyScreenSharingStatusChanged(enable, screensharingDetails);
}
}

/**
* Sets the camera facing mode(environment/user). If facing mode not provided, it will do a toggle.
*
* @param {string | undefined} facingMode - The selected facing mode.
* @returns {void}
*/
export function setCameraFacingMode(facingMode: string | undefined) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();

if (!isToggleCameraEnabled(state)) {
return;
}

if (!facingMode) {
dispatch(toggleCamera());

return;
}

const tracks = state['features/base/tracks'];
const localVideoTrack = getLocalVideoTrack(tracks)?.jitsiTrack;

if (!tracks || !localVideoTrack) {
return;
}

const currentFacingMode = localVideoTrack.getCameraFacingMode();

if (currentFacingMode !== facingMode) {
dispatch(toggleCamera());
}
};
}

/**
* Signals to open the permission dialog for toggling camera remotely.
*
* @param {Function} onAllow - Callback to be executed if permission to toggle camera was granted.
* @param {string} initiatorId - The participant id of the requester.
* @returns {Object} - The open dialog action.
*/
export function openAllowToggleCameraDialog(onAllow: Function, initiatorId: string) {
return openDialog(AllowToggleCameraDialog, {
onAllow,
initiatorId
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { WithTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import { IReduxState } from '../../../../app/types';
import { translate } from '../../../i18n/functions';
import { getParticipantDisplayName } from '../../../participants/functions';
import Dialog from '../../../ui/components/web/Dialog';


interface IProps extends WithTranslation {

/**
* The participant id of the toggle camera requester.
*/
initiatorId: string;

/**
* Function to be invoked after permission to toggle camera granted.
*/
onAllow: () => void;
}

/**
* Dialog to allow toggling camera remotely.
*
* @returns {JSX.Element} - The allow toggle camera dialog.
*/
const AllowToggleCameraDialog = ({ onAllow, t, initiatorId }: IProps): JSX.Element => {
const initiatorName = useSelector((state: IReduxState) => getParticipantDisplayName(state, initiatorId));

return (
<Dialog
ok = {{ translationKey: 'dialog.allow' }}
onSubmit = { onAllow }
titleKey = 'dialog.allowToggleCameraTitle'>
<div>
{ t('dialog.allowToggleCameraDialog', { initiatorName }) }
</div>
</Dialog>
);
};

export default translate(AllowToggleCameraDialog);
4 changes: 4 additions & 0 deletions react/features/base/tracks/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* The payload name for remotely setting the camera facing mode message.
*/
export const CAMERA_FACING_MODE_MESSAGE = 'camera-facing-mode-message';
File renamed without changes.
1 change: 1 addition & 0 deletions react/features/conference/middleware.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './middleware.any';
45 changes: 45 additions & 0 deletions react/features/conference/middleware.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
import { IJitsiConference } from '../base/conference/reducer';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { openAllowToggleCameraDialog, setCameraFacingMode } from '../base/tracks/actions.web';
import { CAMERA_FACING_MODE_MESSAGE } from '../base/tracks/constants';

import './middleware.any';

MiddlewareRegistry.register(_store => next => action => {
switch (action.type) {
case CONFERENCE_JOINED: {
_addSetCameraFacingModeListener(action.conference);
break;
}
}

return next(action);
});

/**
* Registers listener for {@link JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED} that
* will perform various chat related activities.
*
* @param {IJitsiConference} conference - The conference.
* @returns {void}
*/
function _addSetCameraFacingModeListener(conference: IJitsiConference) {
conference.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args: any) => {
if (args && args.length >= 2) {
const [ sender, eventData ] = args;

if (eventData.name === CAMERA_FACING_MODE_MESSAGE) {
APP.store.dispatch(openAllowToggleCameraDialog(
/* onAllow */ () => APP.store.dispatch(setCameraFacingMode(eventData.facingMode)),
/* initiatorId */ sender._id
));
}
}
}
);
}

0 comments on commit 0b1bdaf

Please sign in to comment.