diff --git a/react/features/base/tracks/actions.js b/react/features/base/tracks/actions.js index dce735b16..98dbb363c 100644 --- a/react/features/base/tracks/actions.js +++ b/react/features/base/tracks/actions.js @@ -20,7 +20,7 @@ import { TRACK_UPDATED, TRACK_WILL_CREATE } from './actionTypes'; -import { createLocalTracksF } from './functions'; +import { createLocalTracksF, getLocalTrack, getLocalTracks } from './functions'; const logger = require('jitsi-meet-logger').getLogger(__filename); @@ -45,8 +45,9 @@ export function createDesiredLocalTracks(...desiredTypes) { } const availableTypes - = state['features/base/tracks'] - .filter(t => t.local) + = getLocalTracks( + state['features/base/tracks'], + /* includePending */ true) .map(t => t.mediaType); // We need to create the desired tracks which are not already available. @@ -85,8 +86,10 @@ export function createLocalTracksA(options = {}) { // to implement them) and the right thing to do is to ask for each // device separately. for (const device of devices) { - if (getState()['features/base/tracks'] - .find(t => t.local && t.mediaType === device)) { + if (getLocalTrack( + getState()['features/base/tracks'], + device, + /* includePending */ true)) { throw new Error(`Local track for ${device} already exists`); } diff --git a/react/features/base/tracks/functions.js b/react/features/base/tracks/functions.js index 8d81633f7..df18f3d1d 100644 --- a/react/features/base/tracks/functions.js +++ b/react/features/base/tracks/functions.js @@ -106,21 +106,32 @@ export function getLocalAudioTrack(tracks) { * * @param {Track[]} tracks - List of all tracks. * @param {MEDIA_TYPE} mediaType - Media type. + * @param {boolean} [includePending] - Indicates whether a local track is to be + * returned if it is still pending. A local track is pending if + * {@code getUserMedia} is still executing to create it and, consequently, its + * {@code jitsiTrack} property is {@code undefined}. By default a pending local + * track is not returned. * @returns {(Track|undefined)} */ -export function getLocalTrack(tracks, mediaType) { - return getLocalTracks(tracks).find(t => t.mediaType === mediaType); +export function getLocalTrack(tracks, mediaType, includePending = false) { + return ( + getLocalTracks(tracks, includePending) + .find(t => t.mediaType === mediaType)); } /** - * Returns an array containing the local tracks with a (valid) + * Returns an array containing the local tracks with or without a (valid) * {@code JitsiTrack}. * * @param {Track[]} tracks - An array containing all local tracks. + * @param {boolean} [includePending] - Indicates whether a local track is to be + * returned if it is still pending. A local track is pending if + * {@code getUserMedia} is still executing to create it and, consequently, its + * {@code jitsiTrack} property is {@code undefined}. By default a pending local + * track is not returned. * @returns {Track[]} */ -export function getLocalTracks(tracks) { - +export function getLocalTracks(tracks, includePending = false) { // XXX A local track is considered ready only once it has its `jitsiTrack` // property set by the `TRACK_ADDED` action. Until then there is a stub // added just before the `getUserMedia` call with a cancellable @@ -128,7 +139,7 @@ export function getLocalTracks(tracks) { // has not yet been added to the redux store. Once GUM is cancelled, it will // never make it to the store nor there will be any // `TRACK_ADDED`/`TRACK_REMOVED` actions dispatched for it. - return tracks.filter(t => t.local && t.jitsiTrack); + return tracks.filter(t => t.local && (t.jitsiTrack || includePending)); } /** diff --git a/react/features/base/tracks/middleware.js b/react/features/base/tracks/middleware.js index 1da33a3e7..d5d503157 100644 --- a/react/features/base/tracks/middleware.js +++ b/react/features/base/tracks/middleware.js @@ -147,12 +147,24 @@ MiddlewareRegistry.register(store => next => action => { * with the specified {@code mediaType} is to be retrieved. * @param {MEDIA_TYPE} mediaType - The {@code MEDIA_TYPE} of the local track to * be retrieved from the specified {@code store}. + * @param {boolean} [includePending] - Indicates whether a local track is to be + * returned if it is still pending. A local track is pending if + * {@code getUserMedia} is still executing to create it and, consequently, its + * {@code jitsiTrack} property is {@code undefined}. By default a pending local + * track is not returned. * @private * @returns {Track} The local {@code Track} associated with the specified * {@code mediaType} in the specified {@code store}. */ -function _getLocalTrack({ getState }, mediaType: MEDIA_TYPE) { - return getLocalTrack(getState()['features/base/tracks'], mediaType); +function _getLocalTrack( + { getState }: { getState: Function }, + mediaType: MEDIA_TYPE, + includePending: boolean = false) { + return ( + getLocalTrack( + getState()['features/base/tracks'], + mediaType, + includePending)); } /** @@ -167,10 +179,17 @@ function _getLocalTrack({ getState }, mediaType: MEDIA_TYPE) { * @returns {void} */ function _setMuted(store, { ensureTrack, muted }, mediaType: MEDIA_TYPE) { - const localTrack = _getLocalTrack(store, mediaType); + const localTrack + = _getLocalTrack(store, mediaType, /* includePending */ true); if (localTrack) { - setTrackMuted(localTrack.jitsiTrack, muted); + // The `jitsiTrack` property will have a value only for a localTrack for + // which `getUserMedia` has already completed. If there's no + // `jitsiTrack`, then the `muted` state will be applied once the + // `jitsiTrack` is created. + const { jitsiTrack } = localTrack; + + jitsiTrack && setTrackMuted(jitsiTrack, muted); } else if (!muted && ensureTrack && typeof APP === 'undefined') { // FIXME: This only runs on mobile now because web has its own way of // creating local tracks. Adjust the check once they are unified.