diff --git a/react/features/base/media/actionTypes.js b/react/features/base/media/actionTypes.js index 3d219b81c..c201b774c 100644 --- a/react/features/base/media/actionTypes.js +++ b/react/features/base/media/actionTypes.js @@ -30,3 +30,15 @@ export const SET_CAMERA_FACING_MODE = Symbol('SET_CAMERA_FACING_MODE'); * } */ export const SET_VIDEO_MUTED = Symbol('SET_VIDEO_MUTED'); + +/** + * The type of (redux) action to toggle the local video camera facing mode. In + * contrast to SET_CAMERA_FACING_MODE, allows the toggling to be optimally + * and/or natively implemented without the overhead of separate reads and writes + * of the current/effective camera facing mode. + * + * { + * type: TOGGLE_CAMERA_FACING_MODE + * } + */ +export const TOGGLE_CAMERA_FACING_MODE = Symbol('TOGGLE_CAMERA_FACING_MODE'); diff --git a/react/features/base/media/actions.js b/react/features/base/media/actions.js index c61cc5262..8e4144a22 100644 --- a/react/features/base/media/actions.js +++ b/react/features/base/media/actions.js @@ -5,7 +5,8 @@ import type { Dispatch } from 'redux'; import { SET_AUDIO_MUTED, SET_CAMERA_FACING_MODE, - SET_VIDEO_MUTED + SET_VIDEO_MUTED, + TOGGLE_CAMERA_FACING_MODE } from './actionTypes'; import { CAMERA_FACING_MODE } from './constants'; @@ -73,21 +74,19 @@ export function toggleAudioMuted() { } /** - * Toggles the camera between front and rear (user and environment). + * Toggles the camera facing mode. Most commonly, for example, mobile devices + * such as phones have a front/user-facing and a back/environment-facing + * cameras. In contrast to setCameraFacingMode, allows the toggling to be + * optimally and/or natively implemented without the overhead of separate reads + * and writes of the current/effective camera facing mode. * - * @returns {Function} + * @returns {{ + * type: TOGGLE_CAMERA_FACING_MODE + * }} */ export function toggleCameraFacingMode() { - return (dispatch: Dispatch<*>, getState: Function) => { - let cameraFacingMode - = getState()['features/base/media'].video.facingMode; - - cameraFacingMode - = cameraFacingMode === CAMERA_FACING_MODE.USER - ? CAMERA_FACING_MODE.ENVIRONMENT - : CAMERA_FACING_MODE.USER; - - return dispatch(setCameraFacingMode(cameraFacingMode)); + return { + type: TOGGLE_CAMERA_FACING_MODE }; } diff --git a/react/features/base/media/reducer.js b/react/features/base/media/reducer.js index f9d52fd8d..1e2f3142b 100644 --- a/react/features/base/media/reducer.js +++ b/react/features/base/media/reducer.js @@ -5,7 +5,8 @@ import { ReducerRegistry } from '../redux'; import { SET_AUDIO_MUTED, SET_CAMERA_FACING_MODE, - SET_VIDEO_MUTED + SET_VIDEO_MUTED, + TOGGLE_CAMERA_FACING_MODE } from './actionTypes'; import { CAMERA_FACING_MODE } from './constants'; @@ -88,6 +89,20 @@ function _video(state = VIDEO_INITIAL_MEDIA_STATE, action) { muted: action.muted }; + case TOGGLE_CAMERA_FACING_MODE: { + let cameraFacingMode = state.facingMode; + + cameraFacingMode + = cameraFacingMode === CAMERA_FACING_MODE.USER + ? CAMERA_FACING_MODE.ENVIRONMENT + : CAMERA_FACING_MODE.USER; + + return { + ...state, + facingMode: cameraFacingMode + }; + } + default: return state; } diff --git a/react/features/base/tracks/middleware.js b/react/features/base/tracks/middleware.js index c37bc983c..c8ce1d4db 100644 --- a/react/features/base/tracks/middleware.js +++ b/react/features/base/tracks/middleware.js @@ -6,6 +6,7 @@ import { SET_AUDIO_MUTED, SET_CAMERA_FACING_MODE, SET_VIDEO_MUTED, + TOGGLE_CAMERA_FACING_MODE, setAudioMuted, setVideoMuted } from '../media'; @@ -64,6 +65,38 @@ MiddlewareRegistry.register(store => next => action => { _setMuted(store, action, MEDIA_TYPE.VIDEO); break; + case TOGGLE_CAMERA_FACING_MODE: { + const localTrack = _getLocalTrack(store, MEDIA_TYPE.VIDEO); + let jitsiTrack; + let mediaStreamTrack; + + if (localTrack + && (jitsiTrack = localTrack.jitsiTrack) + && (mediaStreamTrack = jitsiTrack.track)) { + // XXX MediaStreamTrack._switchCamera a custom function implemented + // in react-native-webrtc for video which switches between the + // cameras via a native WebRTC library implementation without making + // any changes to the track. + // FIXME JitsiLocalTrack defines getCameraFacingMode. By calling + // _switchCamera on MediaStreamTrack without the knowledge of + // lib-jitsi-meet we are likely introducing an inconsistency in + // JitsiLocalTrack's state. + mediaStreamTrack._switchCamera(); + + // Don't mirror the video of the back/environment-facing camera. + // FIXME Relies on the fact that we always open the camera in + // user-facing mode first. + store.dispatch({ + type: TRACK_UPDATED, + track: { + jitsiTrack, + mirror: !localTrack.mirror + } + }); + } + break; + } + case TRACK_UPDATED: return _trackUpdated(store, next, action); }