2016-10-05 14:36:59 +00:00
|
|
|
import { combineReducers } from 'redux';
|
|
|
|
|
2020-05-28 11:20:37 +00:00
|
|
|
import { CONFERENCE_FAILED, CONFERENCE_LEFT } from '../conference/actionTypes';
|
2022-07-20 15:01:16 +00:00
|
|
|
import ReducerRegistry from '../redux/ReducerRegistry';
|
2020-05-28 11:20:37 +00:00
|
|
|
import { TRACK_REMOVED } from '../tracks/actionTypes';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
import {
|
2017-07-24 13:56:57 +00:00
|
|
|
SET_AUDIO_AVAILABLE,
|
2017-01-11 18:14:00 +00:00
|
|
|
SET_AUDIO_MUTED,
|
2021-11-30 20:08:25 +00:00
|
|
|
SET_AUDIO_UNMUTE_PERMISSIONS,
|
2017-01-11 18:14:00 +00:00
|
|
|
SET_CAMERA_FACING_MODE,
|
2022-03-15 17:24:49 +00:00
|
|
|
SET_SCREENSHARE_MUTED,
|
2017-06-28 14:15:46 +00:00
|
|
|
SET_VIDEO_AVAILABLE,
|
2017-07-16 08:44:07 +00:00
|
|
|
SET_VIDEO_MUTED,
|
2021-11-30 20:08:25 +00:00
|
|
|
SET_VIDEO_UNMUTE_PERMISSIONS,
|
2018-04-07 07:52:38 +00:00
|
|
|
STORE_VIDEO_TRANSFORM,
|
2017-04-03 08:57:01 +00:00
|
|
|
TOGGLE_CAMERA_FACING_MODE
|
2016-10-05 14:36:59 +00:00
|
|
|
} from './actionTypes';
|
2022-03-15 17:24:49 +00:00
|
|
|
import { CAMERA_FACING_MODE, SCREENSHARE_MUTISM_AUTHORITY } from './constants';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Media state object for local audio.
|
|
|
|
*
|
|
|
|
* @typedef {Object} AudioMediaState
|
|
|
|
* @property {boolean} muted=false - Audio muted state.
|
|
|
|
*/
|
|
|
|
|
2018-04-13 16:26:42 +00:00
|
|
|
// FIXME Technically, _AUDIO_INITIAL_MEDIA_STATE is a constant internal to the
|
|
|
|
// feature base/media and used in multiple files so it should be in
|
|
|
|
// constants.js. Practically though, AudioMediaState would then be used in
|
|
|
|
// multiple files as well so I don't know where and how to move it.
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
|
|
|
* Initial state for local audio.
|
|
|
|
*
|
|
|
|
* @type {AudioMediaState}
|
|
|
|
*/
|
2018-04-13 16:26:42 +00:00
|
|
|
export const _AUDIO_INITIAL_MEDIA_STATE = {
|
2017-07-24 13:56:57 +00:00
|
|
|
available: true,
|
2021-12-07 21:48:12 +00:00
|
|
|
unmuteBlocked: false,
|
2016-10-05 14:36:59 +00:00
|
|
|
muted: false
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reducer for audio media state.
|
|
|
|
*
|
|
|
|
* @param {AudioMediaState} state - Media state of local audio.
|
|
|
|
* @param {Object} action - Action object.
|
|
|
|
* @param {string} action.type - Type of action.
|
2017-01-28 23:28:13 +00:00
|
|
|
* @private
|
2016-10-05 14:36:59 +00:00
|
|
|
* @returns {AudioMediaState}
|
|
|
|
*/
|
2022-07-20 15:01:16 +00:00
|
|
|
function _audio(state: IAudioState = _AUDIO_INITIAL_MEDIA_STATE, action: any) {
|
2016-10-05 14:36:59 +00:00
|
|
|
switch (action.type) {
|
2017-07-24 13:56:57 +00:00
|
|
|
case SET_AUDIO_AVAILABLE:
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
available: action.available
|
|
|
|
};
|
|
|
|
|
2017-01-11 18:14:00 +00:00
|
|
|
case SET_AUDIO_MUTED:
|
2016-10-05 14:36:59 +00:00
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
muted: action.muted
|
|
|
|
};
|
|
|
|
|
2021-11-30 20:08:25 +00:00
|
|
|
case SET_AUDIO_UNMUTE_PERMISSIONS:
|
|
|
|
return {
|
|
|
|
...state,
|
2021-12-07 21:48:12 +00:00
|
|
|
unmuteBlocked: action.blocked
|
2021-11-30 20:08:25 +00:00
|
|
|
};
|
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-15 17:24:49 +00:00
|
|
|
/**
|
|
|
|
* Media state object for local screenshare.
|
|
|
|
*
|
|
|
|
* @typedef {Object} ScreenshareMediaState
|
|
|
|
* @property {boolean} available=true - Screenshare available state.
|
|
|
|
* @property {boolean} muted=true - Screenshare muted state.
|
|
|
|
* @property {boolean} unmuteBlocked=false - Screenshare unmute blocked state.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initial state for video.
|
|
|
|
*
|
|
|
|
* @type {ScreenshareMediaState}
|
|
|
|
*/
|
|
|
|
export const _SCREENSHARE_INITIAL_MEDIA_STATE = {
|
|
|
|
available: true,
|
|
|
|
muted: SCREENSHARE_MUTISM_AUTHORITY.USER,
|
|
|
|
unmuteBlocked: false
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reducer for screenshare media state.
|
|
|
|
*
|
|
|
|
* @param {VideoMediaState} state - Media state of local screenshare.
|
|
|
|
* @param {Object} action - Action object.
|
|
|
|
* @param {string} action.type - Type of action.
|
|
|
|
* @private
|
|
|
|
* @returns {ScreenshareMediaState}
|
|
|
|
*/
|
2022-07-20 15:01:16 +00:00
|
|
|
function _screenshare(state: IScreenshareState = _SCREENSHARE_INITIAL_MEDIA_STATE, action: any) {
|
2022-03-15 17:24:49 +00:00
|
|
|
switch (action.type) {
|
|
|
|
case SET_SCREENSHARE_MUTED:
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
muted: action.muted
|
|
|
|
};
|
|
|
|
|
|
|
|
case SET_VIDEO_UNMUTE_PERMISSIONS:
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
unmuteBlocked: action.blocked
|
|
|
|
};
|
|
|
|
|
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
|
|
|
* Media state object for local video.
|
|
|
|
*
|
|
|
|
* @typedef {Object} VideoMediaState
|
|
|
|
* @property {CAMERA_FACING_MODE} facingMode='user' - Camera facing mode.
|
|
|
|
* @property {boolean} muted=false - Video muted state.
|
|
|
|
*/
|
|
|
|
|
2018-04-13 16:26:42 +00:00
|
|
|
// FIXME Technically, _VIDEO_INITIAL_MEDIA_STATE is a constant internal to the
|
|
|
|
// feature base/media and used in multiple files so it should be in
|
|
|
|
// constants.js. Practically though, VideoMediaState would then be used in
|
|
|
|
// multiple files as well so I don't know where and how to move it.
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
|
|
|
* Initial state for video.
|
|
|
|
*
|
|
|
|
* @type {VideoMediaState}
|
|
|
|
*/
|
2018-04-13 16:26:42 +00:00
|
|
|
export const _VIDEO_INITIAL_MEDIA_STATE = {
|
2017-07-16 07:23:59 +00:00
|
|
|
available: true,
|
2021-12-07 21:48:12 +00:00
|
|
|
unmuteBlocked: false,
|
2016-10-05 14:36:59 +00:00
|
|
|
facingMode: CAMERA_FACING_MODE.USER,
|
2018-04-07 07:52:38 +00:00
|
|
|
muted: 0,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The video {@link Transform}s applied to {@code MediaStream}s by
|
|
|
|
* {@code id} i.e. "pinch to zoom".
|
|
|
|
*/
|
|
|
|
transforms: {}
|
2016-10-05 14:36:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reducer for camera media state.
|
|
|
|
*
|
|
|
|
* @param {VideoMediaState} state - Media state of local video.
|
|
|
|
* @param {Object} action - Action object.
|
|
|
|
* @param {string} action.type - Type of action.
|
2017-01-28 23:28:13 +00:00
|
|
|
* @private
|
2016-10-05 14:36:59 +00:00
|
|
|
* @returns {VideoMediaState}
|
|
|
|
*/
|
2022-07-20 15:01:16 +00:00
|
|
|
function _video(state: IVideoState = _VIDEO_INITIAL_MEDIA_STATE, action: any) {
|
2016-10-05 14:36:59 +00:00
|
|
|
switch (action.type) {
|
2018-04-07 07:52:38 +00:00
|
|
|
case CONFERENCE_FAILED:
|
|
|
|
case CONFERENCE_LEFT:
|
|
|
|
return _clearAllVideoTransforms(state);
|
|
|
|
|
2017-07-16 08:44:07 +00:00
|
|
|
case SET_CAMERA_FACING_MODE:
|
2017-06-28 14:15:46 +00:00
|
|
|
return {
|
|
|
|
...state,
|
2017-07-16 08:44:07 +00:00
|
|
|
facingMode: action.cameraFacingMode
|
2017-06-28 14:15:46 +00:00
|
|
|
};
|
|
|
|
|
2017-07-16 08:44:07 +00:00
|
|
|
case SET_VIDEO_AVAILABLE:
|
2016-10-05 14:36:59 +00:00
|
|
|
return {
|
|
|
|
...state,
|
2017-07-16 08:44:07 +00:00
|
|
|
available: action.available
|
2016-10-05 14:36:59 +00:00
|
|
|
};
|
|
|
|
|
2017-01-11 18:14:00 +00:00
|
|
|
case SET_VIDEO_MUTED:
|
2016-10-05 14:36:59 +00:00
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
muted: action.muted
|
|
|
|
};
|
|
|
|
|
2021-11-30 20:08:25 +00:00
|
|
|
case SET_VIDEO_UNMUTE_PERMISSIONS:
|
|
|
|
return {
|
|
|
|
...state,
|
2021-12-07 21:48:12 +00:00
|
|
|
unmuteBlocked: action.blocked
|
2021-11-30 20:08:25 +00:00
|
|
|
};
|
|
|
|
|
2018-04-07 07:52:38 +00:00
|
|
|
case STORE_VIDEO_TRANSFORM:
|
|
|
|
return _storeVideoTransform(state, action);
|
|
|
|
|
2017-04-03 08:57:01 +00:00
|
|
|
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
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-04-07 07:52:38 +00:00
|
|
|
case TRACK_REMOVED:
|
|
|
|
return _trackRemoved(state, action);
|
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
2017-01-17 14:44:50 +00:00
|
|
|
|
2022-07-20 15:01:16 +00:00
|
|
|
interface IAudioState {
|
|
|
|
available: boolean;
|
|
|
|
muted: boolean;
|
|
|
|
unmuteBlocked: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IScreenshareState {
|
|
|
|
available: boolean;
|
|
|
|
muted: number;
|
|
|
|
unmuteBlocked: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IVideoState {
|
|
|
|
available: boolean;
|
|
|
|
facingMode: string;
|
|
|
|
muted: number;
|
|
|
|
transforms: Object;
|
|
|
|
unmuteBlocked: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface IMediaState {
|
|
|
|
audio: IAudioState;
|
|
|
|
screenshare: IScreenshareState;
|
|
|
|
video: IVideoState;
|
|
|
|
}
|
|
|
|
|
2017-01-17 14:44:50 +00:00
|
|
|
/**
|
|
|
|
* Listen for various actions related to media devices.
|
|
|
|
*
|
|
|
|
* @param {Object} state - State of media devices.
|
|
|
|
* @param {Object} action - Action object.
|
|
|
|
* @param {string} action.type - Type of action.
|
|
|
|
* @param {Object} action.media - Information about media devices to be
|
|
|
|
* modified.
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2022-09-05 09:05:07 +00:00
|
|
|
ReducerRegistry.register<IMediaState>('features/base/media', combineReducers({
|
2017-01-28 23:28:13 +00:00
|
|
|
audio: _audio,
|
2022-03-15 17:24:49 +00:00
|
|
|
screenshare: _screenshare,
|
2017-01-28 23:28:13 +00:00
|
|
|
video: _video
|
2017-01-17 14:44:50 +00:00
|
|
|
}));
|
2018-04-07 07:52:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all stored video {@link Transform}s.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The {@code video} state of the feature base/media.
|
|
|
|
* @private
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2022-07-20 15:01:16 +00:00
|
|
|
function _clearAllVideoTransforms(state: IVideoState) {
|
2018-04-07 07:52:38 +00:00
|
|
|
return {
|
|
|
|
...state,
|
2018-04-13 16:26:42 +00:00
|
|
|
transforms: _VIDEO_INITIAL_MEDIA_STATE.transforms
|
2018-04-07 07:52:38 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores the last applied transform to a stream.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The {@code video} state of the feature base/media.
|
|
|
|
* @param {Object} action - The redux action {@link STORE_VIDEO_TRANSFORM}.
|
|
|
|
* @private
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
function _storeVideoTransform(state: IVideoState, { streamId, transform }: { streamId: string; transform: string; }) {
|
2018-04-07 07:52:38 +00:00
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
transforms: {
|
|
|
|
...state.transforms,
|
|
|
|
[streamId]: transform
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the stored video {@link Transform} associated with a
|
|
|
|
* {@code MediaStream} when its respective track is removed.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The {@code video} state of the feature base/media.
|
|
|
|
* @param {Object} action - The redux action {@link TRACK_REMOVED}.
|
|
|
|
* @private
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
function _trackRemoved(state: IVideoState, { track: { jitsiTrack } }: { track: { jitsiTrack: any; }; }) {
|
2018-04-07 07:52:38 +00:00
|
|
|
if (jitsiTrack) {
|
|
|
|
const streamId = jitsiTrack.getStreamId();
|
|
|
|
|
|
|
|
if (streamId && streamId in state.transforms) {
|
2022-07-20 15:01:16 +00:00
|
|
|
const nextTransforms: any = {
|
2018-04-07 07:52:38 +00:00
|
|
|
...state.transforms
|
|
|
|
};
|
|
|
|
|
|
|
|
delete nextTransforms[streamId];
|
|
|
|
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
transforms: nextTransforms
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|