1484179251
- Fix deviceId being read from wrong storage (making it not detected as already sharing) - Remove added by mistake profile filtered by metadata - Fix background and brightness selector being show when sharing camera that is being presented
254 lines
7.5 KiB
JavaScript
Executable File
254 lines
7.5 KiB
JavaScript
Executable File
import Storage from '/imports/ui/services/storage/session';
|
|
import { getStorageSingletonInstance } from '/imports/ui/services/storage';
|
|
import getFromUserSettings from '/imports/ui/services/users-settings';
|
|
import MediaStreamUtils from '/imports/utils/media-stream-utils';
|
|
import VideoService from '/imports/ui/components/video-provider/service';
|
|
import BBBVideoStream from '/imports/ui/services/webrtc-base/bbb-video-stream';
|
|
import browserInfo from '/imports/utils/browserInfo';
|
|
|
|
// GUM retry + delay params (Chrome only for now)
|
|
const GUM_MAX_RETRIES = 5;
|
|
const GUM_RETRY_DELAY = 200;
|
|
const CAMERA_AS_CONTENT_PROFILE_ID = 'fhd';
|
|
|
|
const getDefaultProfile = () => {
|
|
const BBBStorage = getStorageSingletonInstance();
|
|
// Unfiltered, includes hidden profiles
|
|
const CAMERA_PROFILES = window.meetingClientSettings.public.kurento.cameraProfiles || [];
|
|
|
|
return CAMERA_PROFILES.find(profile => profile.id === BBBStorage.getItem('WebcamProfileId'))
|
|
|| CAMERA_PROFILES.find(profile => profile.id === VideoService.getUserParameterProfile())
|
|
|| CAMERA_PROFILES.find(profile => profile.default)
|
|
|| CAMERA_PROFILES[0];
|
|
};
|
|
|
|
const getCameraAsContentProfile = () => {
|
|
// Unfiltered, includes hidden profiles
|
|
const CAMERA_PROFILES = window.meetingClientSettings.public.kurento.cameraProfiles || [];
|
|
|
|
return CAMERA_PROFILES.find(profile => profile.id == CAMERA_AS_CONTENT_PROFILE_ID)
|
|
|| CAMERA_PROFILES.find(profile => profile.default)
|
|
};
|
|
|
|
const getCameraProfile = (id) => {
|
|
// Unfiltered, includes hidden profiles
|
|
const CAMERA_PROFILES = window.meetingClientSettings.public.kurento.cameraProfiles || [];
|
|
return CAMERA_PROFILES.find(profile => profile.id === id);
|
|
};
|
|
|
|
// VIDEO_STREAM_STORAGE: Map<deviceId, MediaStream>. Registers WEBCAM streams.
|
|
// Easier to keep track of them. Easier to centralize their referencing.
|
|
// Easier to shuffle them around.
|
|
const VIDEO_STREAM_STORAGE = new Map();
|
|
|
|
const storeStream = (deviceId, stream) => {
|
|
if (!stream) return false;
|
|
|
|
// Check if there's a dangling stream. If there's one and it's active, cool,
|
|
// return false as it's already stored. Otherwised, clean the derelict stream
|
|
// and store the new one
|
|
if (hasStream(deviceId)) {
|
|
const existingStream = getStream(deviceId);
|
|
if (existingStream.active) return false;
|
|
deleteStream(deviceId);
|
|
}
|
|
|
|
VIDEO_STREAM_STORAGE.set(deviceId, stream);
|
|
|
|
// Stream insurance: clean it up if it ends (see the events being listened to below)
|
|
stream.once('inactive', () => {
|
|
deleteStream(deviceId);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
const getStream = (deviceId) => {
|
|
return VIDEO_STREAM_STORAGE.get(deviceId);
|
|
}
|
|
|
|
const hasStream = (deviceId) => {
|
|
return VIDEO_STREAM_STORAGE.has(deviceId);
|
|
}
|
|
|
|
const deleteStream = (deviceId) => {
|
|
const stream = getStream(deviceId);
|
|
if (stream == null) return false;
|
|
MediaStreamUtils.stopMediaStreamTracks(stream);
|
|
return VIDEO_STREAM_STORAGE.delete(deviceId);
|
|
}
|
|
|
|
const promiseTimeout = (ms, promise) => {
|
|
const timeout = new Promise((resolve, reject) => {
|
|
const id = setTimeout(() => {
|
|
clearTimeout(id);
|
|
|
|
const error = {
|
|
name: 'TimeoutError',
|
|
message: 'Promise did not return',
|
|
};
|
|
|
|
reject(error);
|
|
}, ms);
|
|
});
|
|
|
|
return Promise.race([
|
|
promise,
|
|
timeout,
|
|
]);
|
|
};
|
|
|
|
const getSkipVideoPreview = () => {
|
|
const KURENTO_CONFIG = window.meetingClientSettings.public.kurento;
|
|
|
|
const skipVideoPreviewOnFirstJoin = getFromUserSettings(
|
|
'bbb_skip_video_preview_on_first_join',
|
|
KURENTO_CONFIG.skipVideoPreviewOnFirstJoin,
|
|
);
|
|
const skipVideoPreview = getFromUserSettings(
|
|
'bbb_skip_video_preview',
|
|
KURENTO_CONFIG.skipVideoPreview,
|
|
);
|
|
|
|
return (
|
|
(Storage.getItem('isFirstJoin') !== false && skipVideoPreviewOnFirstJoin)
|
|
|| skipVideoPreview
|
|
);
|
|
};
|
|
|
|
// Takes a raw list of media devices of any media type coming enumerateDevices
|
|
// and a deviceId to be prioritized
|
|
// Outputs an object containing:
|
|
// webcams: videoinput media devices, priorityDevice being the first member of the array (if it exists)
|
|
// areLabelled: whether all videoinput devices are labelled
|
|
// areIdentified: whether all videoinput devices have deviceIds
|
|
const digestVideoDevices = (devices, priorityDevice) => {
|
|
const webcams = [];
|
|
let areLabelled = true;
|
|
let areIdentified = true;
|
|
|
|
devices.forEach((device) => {
|
|
if (device.kind === 'videoinput') {
|
|
if (!webcams.some(d => d.deviceId === device.deviceId)) {
|
|
// We found a priority device. Push it to the beginning of the array so we
|
|
// can use it as the "initial device"
|
|
if (priorityDevice && priorityDevice === device.deviceId) {
|
|
webcams.unshift(device);
|
|
} else {
|
|
webcams.push(device);
|
|
}
|
|
|
|
if (!device.label) { areLabelled = false }
|
|
if (!device.deviceId) { areIdentified = false }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Returns the list of devices and whether they are labelled and identified with deviceId
|
|
return {
|
|
webcams,
|
|
areLabelled,
|
|
areIdentified,
|
|
};
|
|
};
|
|
|
|
const _retry = (foo, opts) => new Promise((resolve, reject) => {
|
|
const {
|
|
retries = 1,
|
|
delay = 0,
|
|
error: bubbledError,
|
|
errorRetryList = [],
|
|
} = opts;
|
|
|
|
if (!retries) return reject(bubbledError);
|
|
|
|
return foo().then(resolve).catch((_error) => {
|
|
if (errorRetryList.length > 0
|
|
&& !errorRetryList.some((eName) => _error.name === eName)) {
|
|
reject(_error);
|
|
return;
|
|
}
|
|
|
|
const newOpts = {
|
|
...opts,
|
|
retries: retries - 1,
|
|
error: _error,
|
|
};
|
|
|
|
setTimeout(() => {
|
|
_retry(foo, newOpts).then(resolve).catch(reject);
|
|
}, delay);
|
|
});
|
|
});
|
|
|
|
// Returns a promise that resolves an instance of BBBVideoStream or rejects an *Error
|
|
const doGUM = (deviceId, profile) => {
|
|
const GUM_TIMEOUT = window.meetingClientSettings.public.kurento.gUMTimeout;
|
|
|
|
// Check if this is an already loaded stream
|
|
if (deviceId && hasStream(deviceId)) {
|
|
return Promise.resolve(getStream(deviceId));
|
|
}
|
|
|
|
const constraints = {
|
|
audio: false,
|
|
video: { ...profile.constraints },
|
|
};
|
|
|
|
if (deviceId) {
|
|
constraints.video.deviceId = { exact: deviceId };
|
|
}
|
|
|
|
const postProcessedgUM = (cts) => {
|
|
const ppGUM = () => navigator.mediaDevices.getUserMedia(cts)
|
|
.then((stream) => new BBBVideoStream(stream));
|
|
|
|
// Chrome/Edge sometimes bork gUM calls when switching camera
|
|
// profiles. This looks like a browser bug. Track release not
|
|
// being done synchronously -> quick subsequent gUM calls for the same
|
|
// device (profile switching) -> device becoming unavailable while previous
|
|
// tracks aren't finished - prlanzarin
|
|
if (browserInfo.isChrome || browserInfo.isEdge) {
|
|
const opts = {
|
|
retries: GUM_MAX_RETRIES,
|
|
errorRetryList: ['NotReadableError'],
|
|
delay: GUM_RETRY_DELAY,
|
|
};
|
|
return _retry(ppGUM, opts);
|
|
}
|
|
|
|
return ppGUM();
|
|
};
|
|
|
|
return promiseTimeout(GUM_TIMEOUT, postProcessedgUM(constraints));
|
|
};
|
|
|
|
const terminateCameraStream = (bbbVideoStream, deviceId) => {
|
|
// Cleanup current stream if it wasn't shared/stored
|
|
if (bbbVideoStream && !hasStream(deviceId)) {
|
|
bbbVideoStream.stop()
|
|
}
|
|
}
|
|
|
|
export default {
|
|
promiseTimeout,
|
|
changeWebcam: (deviceId) => {
|
|
getStorageSingletonInstance().setItem('WebcamDeviceId', deviceId);
|
|
},
|
|
webcamDeviceId: () => getStorageSingletonInstance().getItem('WebcamDeviceId'),
|
|
changeProfile: (profileId) => {
|
|
getStorageSingletonInstance().setItem('WebcamProfileId', profileId);
|
|
},
|
|
getSkipVideoPreview,
|
|
storeStream,
|
|
getStream,
|
|
hasStream,
|
|
deleteStream,
|
|
digestVideoDevices,
|
|
getDefaultProfile,
|
|
getCameraAsContentProfile,
|
|
getCameraProfile,
|
|
doGUM,
|
|
terminateCameraStream,
|
|
};
|