123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import { IStore } from '../../app/types';
- import { IStateful } from '../app/types';
- import { isMobileBrowser } from '../environment/utils';
- import JitsiMeetJS from '../lib-jitsi-meet';
- import { setAudioMuted } from '../media/actions';
- import { toState } from '../redux/functions';
- import {
- getUserSelectedCameraDeviceId,
- getUserSelectedMicDeviceId
- } from '../settings/functions.web';
-
- import loadEffects from './loadEffects';
- import logger from './logger';
- import { ITrackOptions } from './types';
-
- export * from './functions.any';
-
- /**
- * Create local tracks of specific types.
- *
- * @param {Object} options - The options with which the local tracks are to be
- * created.
- * @param {string|null} [options.cameraDeviceId] - Camera device id or
- * {@code undefined} to use app's settings.
- * @param {string[]} options.devices - Required track types such as 'audio'
- * and/or 'video'.
- * @param {string|null} [options.micDeviceId] - Microphone device id or
- * {@code undefined} to use app's settings.
- * @param {number|undefined} [oprions.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
- * @param {boolean} [options.firePermissionPromptIsShownEvent] - Whether lib-jitsi-meet
- * should check for a {@code getUserMedia} permission prompt and fire a
- * corresponding event.
- * @param {IStore} store - The redux store in the context of which the function
- * is to execute and from which state such as {@code config} is to be retrieved.
- * @returns {Promise<JitsiLocalTrack[]>}
- */
- export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore) {
- let { cameraDeviceId, micDeviceId } = options;
- const {
- desktopSharingSourceDevice,
- desktopSharingSources,
- firePermissionPromptIsShownEvent,
- timeout
- } = options;
-
- // TODO The app's settings should go in the redux store and then the
- // reliance on the global variable APP will go away.
- store = store || APP.store; // eslint-disable-line no-param-reassign
-
- const state = store.getState();
-
- if (typeof cameraDeviceId === 'undefined' || cameraDeviceId === null) {
- cameraDeviceId = getUserSelectedCameraDeviceId(state);
- }
- if (typeof micDeviceId === 'undefined' || micDeviceId === null) {
- micDeviceId = getUserSelectedMicDeviceId(state);
- }
-
- const {
- desktopSharingFrameRate,
- firefox_fake_device, // eslint-disable-line camelcase
- resolution
- } = state['features/base/config'];
- const constraints = options.constraints ?? state['features/base/config'].constraints;
-
- return (
- loadEffects(store).then((effectsArray: Object[]) => {
- // Filter any undefined values returned by Promise.resolve().
- const effects = effectsArray.filter(effect => Boolean(effect));
-
- return JitsiMeetJS.createLocalTracks(
- {
- cameraDeviceId,
- constraints,
- desktopSharingFrameRate,
- desktopSharingSourceDevice,
- desktopSharingSources,
-
- // Copy array to avoid mutations inside library.
- devices: options.devices?.slice(0),
- effects,
- firefox_fake_device, // eslint-disable-line camelcase
- firePermissionPromptIsShownEvent,
- micDeviceId,
- resolution,
- timeout
- })
- .catch((err: Error) => {
- logger.error('Failed to create local tracks', options.devices, err);
-
- return Promise.reject(err);
- });
- }));
- }
-
- /**
- * Returns an object containing a promise which resolves with the created tracks &
- * the errors resulting from that process.
- *
- * @returns {Promise<JitsiLocalTrack>}
- *
- * @todo Refactor to not use APP.
- */
- export function createPrejoinTracks() {
- const errors: any = {};
- const initialDevices = [ 'audio' ];
- const requestedAudio = true;
- let requestedVideo = false;
- const { startAudioOnly, startWithAudioMuted, startWithVideoMuted } = APP.store.getState()['features/base/settings'];
-
- // Always get a handle on the audio input device so that we have statistics even if the user joins the
- // conference muted. Previous implementation would only acquire the handle when the user first unmuted,
- // which would results in statistics ( such as "No audio input" or "Are you trying to speak?") being available
- // only after that point.
- if (startWithAudioMuted) {
- APP.store.dispatch(setAudioMuted(true));
- }
-
- if (!startWithVideoMuted && !startAudioOnly) {
- initialDevices.push('video');
- requestedVideo = true;
- }
-
- let tryCreateLocalTracks;
-
- if (!requestedAudio && !requestedVideo) {
- // Resolve with no tracks
- tryCreateLocalTracks = Promise.resolve([]);
- } else {
- tryCreateLocalTracks = createLocalTracksF({
- devices: initialDevices,
- firePermissionPromptIsShownEvent: true
- }, APP.store)
- .catch((err: Error) => {
- if (requestedAudio && requestedVideo) {
-
- // Try audio only...
- errors.audioAndVideoError = err;
-
- return (
- createLocalTracksF({
- devices: [ 'audio' ],
- firePermissionPromptIsShownEvent: true
- }));
- } else if (requestedAudio && !requestedVideo) {
- errors.audioOnlyError = err;
-
- return [];
- } else if (requestedVideo && !requestedAudio) {
- errors.videoOnlyError = err;
-
- return [];
- }
- logger.error('Should never happen');
- })
- .catch((err: Error) => {
- // Log this just in case...
- if (!requestedAudio) {
- logger.error('The impossible just happened', err);
- }
- errors.audioOnlyError = err;
-
- // Try video only...
- return requestedVideo
- ? createLocalTracksF({
- devices: [ 'video' ],
- firePermissionPromptIsShownEvent: true
- })
- : [];
- })
- .catch((err: Error) => {
- // Log this just in case...
- if (!requestedVideo) {
- logger.error('The impossible just happened', err);
- }
- errors.videoOnlyError = err;
-
- return [];
- });
- }
-
- return {
- tryCreateLocalTracks,
- errors
- };
- }
-
- /**
- * Determines whether toggle camera should be enabled or not.
- *
- * @param {Function|Object} stateful - The redux store or {@code getState} function.
- * @returns {boolean} - Whether toggle camera should be enabled.
- */
- export function isToggleCameraEnabled(stateful: IStateful) {
- const state = toState(stateful);
- const { videoInput } = state['features/base/devices'].availableDevices;
-
- return isMobileBrowser() && Number(videoInput?.length) > 1;
- }
|