Merge remote-tracking branch 'remotes/upstream/develop' into tiles-start-positions
This commit is contained in:
commit
7f61e9addd
182 changed files with 17118 additions and 4494 deletions
|
@ -1,13 +1,14 @@
|
|||
import {derived, get, Readable, readable, writable, Writable} from "svelte/store";
|
||||
import {peerStore} from "./PeerStore";
|
||||
import {localUserStore} from "../Connexion/LocalUserStore";
|
||||
import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap";
|
||||
import {userMovingStore} from "./GameStore";
|
||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||
import {BrowserTooOldError} from "./Errors/BrowserTooOldError";
|
||||
import {errorStore} from "./ErrorStore";
|
||||
import {isIOS} from "../WebRtc/DeviceUtils";
|
||||
import {WebviewOnOldIOS} from "./Errors/WebviewOnOldIOS";
|
||||
import { derived, get, Readable, readable, writable, Writable } from "svelte/store";
|
||||
import { localUserStore } from "../Connexion/LocalUserStore";
|
||||
import { userMovingStore } from "./GameStore";
|
||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||
import { BrowserTooOldError } from "./Errors/BrowserTooOldError";
|
||||
import { errorStore } from "./ErrorStore";
|
||||
import { isIOS } from "../WebRtc/DeviceUtils";
|
||||
import { WebviewOnOldIOS } from "./Errors/WebviewOnOldIOS";
|
||||
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility";
|
||||
import { peerStore } from "./PeerStore";
|
||||
import { privacyShutdownStore } from "./PrivacyShutdownStore";
|
||||
|
||||
/**
|
||||
* A store that contains the camera state requested by the user (on or off).
|
||||
|
@ -35,35 +36,6 @@ function createRequestedMicrophoneState() {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A store containing whether the current page is visible or not.
|
||||
*/
|
||||
export const visibilityStore = readable(document.visibilityState === 'visible', function start(set) {
|
||||
const onVisibilityChange = () => {
|
||||
set(document.visibilityState === 'visible');
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||
|
||||
return function stop() {
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* A store that contains whether the game overlay is shown or not.
|
||||
* Typically, the overlay is hidden when entering Jitsi meet.
|
||||
*/
|
||||
function createGameOverlayVisibilityStore() {
|
||||
const { subscribe, set, update } = writable(false);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
showGameOverlay: () => set(true),
|
||||
hideGameOverlay: () => set(false),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A store that contains whether the EnableCameraScene is shown or not.
|
||||
*/
|
||||
|
@ -79,49 +51,13 @@ function createEnableCameraSceneVisibilityStore() {
|
|||
|
||||
export const requestedCameraState = createRequestedCameraState();
|
||||
export const requestedMicrophoneState = createRequestedMicrophoneState();
|
||||
export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore();
|
||||
export const enableCameraSceneVisibilityStore = createEnableCameraSceneVisibilityStore();
|
||||
|
||||
/**
|
||||
* A store that contains "true" if the webcam should be stopped for privacy reasons - i.e. if the the user left the the page while not in a discussion.
|
||||
*/
|
||||
function createPrivacyShutdownStore() {
|
||||
let privacyEnabled = false;
|
||||
|
||||
const { subscribe, set, update } = writable(privacyEnabled);
|
||||
|
||||
visibilityStore.subscribe((isVisible) => {
|
||||
if (!isVisible && get(peerStore).size === 0) {
|
||||
privacyEnabled = true;
|
||||
set(true);
|
||||
}
|
||||
if (isVisible) {
|
||||
privacyEnabled = false;
|
||||
set(false);
|
||||
}
|
||||
});
|
||||
|
||||
peerStore.subscribe((peers) => {
|
||||
if (peers.size === 0 && get(visibilityStore) === false) {
|
||||
privacyEnabled = true;
|
||||
set(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
};
|
||||
}
|
||||
|
||||
export const privacyShutdownStore = createPrivacyShutdownStore();
|
||||
|
||||
|
||||
/**
|
||||
* A store containing whether the webcam was enabled in the last 10 seconds
|
||||
*/
|
||||
const enabledWebCam10secondsAgoStore = readable(false, function start(set) {
|
||||
let timeout: NodeJS.Timeout|null = null;
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
const unsubscribe = requestedCameraState.subscribe((enabled) => {
|
||||
if (enabled === true) {
|
||||
|
@ -135,7 +71,7 @@ const enabledWebCam10secondsAgoStore = readable(false, function start(set) {
|
|||
} else {
|
||||
set(false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return function stop() {
|
||||
unsubscribe();
|
||||
|
@ -146,7 +82,7 @@ const enabledWebCam10secondsAgoStore = readable(false, function start(set) {
|
|||
* A store containing whether the webcam was enabled in the last 5 seconds
|
||||
*/
|
||||
const userMoved5SecondsAgoStore = readable(false, function start(set) {
|
||||
let timeout: NodeJS.Timeout|null = null;
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
const unsubscribe = userMovingStore.subscribe((moving) => {
|
||||
if (moving === true) {
|
||||
|
@ -158,45 +94,51 @@ const userMoved5SecondsAgoStore = readable(false, function start(set) {
|
|||
timeout = setTimeout(() => {
|
||||
set(false);
|
||||
}, 5000);
|
||||
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return function stop() {
|
||||
unsubscribe();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* A store containing whether the mouse is getting close the bottom right corner.
|
||||
*/
|
||||
const mouseInBottomRight = readable(false, function start(set) {
|
||||
let lastInBottomRight = false;
|
||||
const gameDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('game');
|
||||
const gameDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game");
|
||||
|
||||
const detectInBottomRight = (event: MouseEvent) => {
|
||||
const rect = gameDiv.getBoundingClientRect();
|
||||
const inBottomRight = event.x - rect.left > rect.width * 3 / 4 && event.y - rect.top > rect.height * 3 / 4;
|
||||
const inBottomRight = event.x - rect.left > (rect.width * 3) / 4 && event.y - rect.top > (rect.height * 3) / 4;
|
||||
if (inBottomRight !== lastInBottomRight) {
|
||||
lastInBottomRight = inBottomRight;
|
||||
set(inBottomRight);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', detectInBottomRight);
|
||||
document.addEventListener("mousemove", detectInBottomRight);
|
||||
|
||||
return function stop() {
|
||||
document.removeEventListener('mousemove', detectInBottomRight);
|
||||
}
|
||||
document.removeEventListener("mousemove", detectInBottomRight);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* A store that contains "true" if the webcam should be stopped for energy efficiency reason - i.e. we are not moving and not in a conversation.
|
||||
*/
|
||||
export const cameraEnergySavingStore = derived([userMoved5SecondsAgoStore, peerStore, enabledWebCam10secondsAgoStore, mouseInBottomRight], ([$userMoved5SecondsAgoStore,$peerStore, $enabledWebCam10secondsAgoStore, $mouseInBottomRight]) => {
|
||||
return !$mouseInBottomRight && !$userMoved5SecondsAgoStore && $peerStore.size === 0 && !$enabledWebCam10secondsAgoStore;
|
||||
});
|
||||
export const cameraEnergySavingStore = derived(
|
||||
[userMoved5SecondsAgoStore, peerStore, enabledWebCam10secondsAgoStore, mouseInBottomRight],
|
||||
([$userMoved5SecondsAgoStore, $peerStore, $enabledWebCam10secondsAgoStore, $mouseInBottomRight]) => {
|
||||
return (
|
||||
!$mouseInBottomRight &&
|
||||
!$userMoved5SecondsAgoStore &&
|
||||
$peerStore.size === 0 &&
|
||||
!$enabledWebCam10secondsAgoStore
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* A store that contains video constraints.
|
||||
|
@ -207,28 +149,30 @@ function createVideoConstraintStore() {
|
|||
height: { min: 400, ideal: 720 },
|
||||
frameRate: { ideal: localUserStore.getVideoQualityValue() },
|
||||
facingMode: "user",
|
||||
resizeMode: 'crop-and-scale',
|
||||
aspectRatio: 1.777777778
|
||||
resizeMode: "crop-and-scale",
|
||||
aspectRatio: 1.777777778,
|
||||
} as MediaTrackConstraints);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setDeviceId: (deviceId: string|undefined) => update((constraints) => {
|
||||
if (deviceId !== undefined) {
|
||||
constraints.deviceId = {
|
||||
exact: deviceId
|
||||
};
|
||||
} else {
|
||||
delete constraints.deviceId;
|
||||
}
|
||||
setDeviceId: (deviceId: string | undefined) =>
|
||||
update((constraints) => {
|
||||
if (deviceId !== undefined) {
|
||||
constraints.deviceId = {
|
||||
exact: deviceId,
|
||||
};
|
||||
} else {
|
||||
delete constraints.deviceId;
|
||||
}
|
||||
|
||||
return constraints;
|
||||
}),
|
||||
setFrameRate: (frameRate: number) => update((constraints) => {
|
||||
constraints.frameRate = { ideal: frameRate };
|
||||
return constraints;
|
||||
}),
|
||||
setFrameRate: (frameRate: number) =>
|
||||
update((constraints) => {
|
||||
constraints.frameRate = { ideal: frameRate };
|
||||
|
||||
return constraints;
|
||||
})
|
||||
return constraints;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -242,39 +186,39 @@ function createAudioConstraintStore() {
|
|||
//TODO: make these values configurable in the game settings menu and store them in localstorage
|
||||
autoGainControl: false,
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true
|
||||
} as boolean|MediaTrackConstraints);
|
||||
noiseSuppression: true,
|
||||
} as boolean | MediaTrackConstraints);
|
||||
|
||||
let selectedDeviceId = null;
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setDeviceId: (deviceId: string|undefined) => update((constraints) => {
|
||||
selectedDeviceId = deviceId;
|
||||
setDeviceId: (deviceId: string | undefined) =>
|
||||
update((constraints) => {
|
||||
selectedDeviceId = deviceId;
|
||||
|
||||
if (typeof(constraints) === 'boolean') {
|
||||
constraints = {}
|
||||
}
|
||||
if (deviceId !== undefined) {
|
||||
constraints.deviceId = {
|
||||
exact: selectedDeviceId
|
||||
};
|
||||
} else {
|
||||
delete constraints.deviceId;
|
||||
}
|
||||
if (typeof constraints === "boolean") {
|
||||
constraints = {};
|
||||
}
|
||||
if (deviceId !== undefined) {
|
||||
constraints.deviceId = {
|
||||
exact: selectedDeviceId,
|
||||
};
|
||||
} else {
|
||||
delete constraints.deviceId;
|
||||
}
|
||||
|
||||
return constraints;
|
||||
})
|
||||
return constraints;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export const audioConstraintStore = createAudioConstraintStore();
|
||||
|
||||
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
let previousComputedVideoConstraint: boolean|MediaTrackConstraints = false;
|
||||
let previousComputedAudioConstraint: boolean|MediaTrackConstraints = false;
|
||||
let previousComputedVideoConstraint: boolean | MediaTrackConstraints = false;
|
||||
let previousComputedAudioConstraint: boolean | MediaTrackConstraints = false;
|
||||
|
||||
/**
|
||||
* A store containing the media constraints we want to apply.
|
||||
|
@ -289,7 +233,8 @@ export const mediaStreamConstraintsStore = derived(
|
|||
audioConstraintStore,
|
||||
privacyShutdownStore,
|
||||
cameraEnergySavingStore,
|
||||
], (
|
||||
],
|
||||
(
|
||||
[
|
||||
$requestedCameraState,
|
||||
$requestedMicrophoneState,
|
||||
|
@ -299,92 +244,97 @@ export const mediaStreamConstraintsStore = derived(
|
|||
$audioConstraintStore,
|
||||
$privacyShutdownStore,
|
||||
$cameraEnergySavingStore,
|
||||
], set
|
||||
],
|
||||
set
|
||||
) => {
|
||||
let currentVideoConstraint: boolean | MediaTrackConstraints = $videoConstraintStore;
|
||||
let currentAudioConstraint: boolean | MediaTrackConstraints = $audioConstraintStore;
|
||||
|
||||
let currentVideoConstraint: boolean|MediaTrackConstraints = $videoConstraintStore;
|
||||
let currentAudioConstraint: boolean|MediaTrackConstraints = $audioConstraintStore;
|
||||
|
||||
if ($enableCameraSceneVisibilityStore) {
|
||||
set({
|
||||
video: currentVideoConstraint,
|
||||
audio: currentAudioConstraint,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable webcam if the user requested so
|
||||
if ($requestedCameraState === false) {
|
||||
currentVideoConstraint = false;
|
||||
}
|
||||
|
||||
// Disable microphone if the user requested so
|
||||
if ($requestedMicrophoneState === false) {
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam and microphone when in a Jitsi
|
||||
if ($gameOverlayVisibilityStore === false) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam for privacy reasons (the game is not visible and we were talking to noone)
|
||||
if ($privacyShutdownStore === true) {
|
||||
currentVideoConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam for energy reasons (the user is not moving and we are talking to noone)
|
||||
if ($cameraEnergySavingStore === true) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Let's make the changes only if the new value is different from the old one.
|
||||
if (previousComputedVideoConstraint != currentVideoConstraint || previousComputedAudioConstraint != currentAudioConstraint) {
|
||||
previousComputedVideoConstraint = currentVideoConstraint;
|
||||
previousComputedAudioConstraint = currentAudioConstraint;
|
||||
// Let's copy the objects.
|
||||
if (typeof previousComputedVideoConstraint !== 'boolean') {
|
||||
previousComputedVideoConstraint = {...previousComputedVideoConstraint};
|
||||
}
|
||||
if (typeof previousComputedAudioConstraint !== 'boolean') {
|
||||
previousComputedAudioConstraint = {...previousComputedAudioConstraint};
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
// Let's wait a little bit to avoid sending too many constraint changes.
|
||||
timeout = setTimeout(() => {
|
||||
if ($enableCameraSceneVisibilityStore) {
|
||||
set({
|
||||
video: currentVideoConstraint,
|
||||
audio: currentAudioConstraint,
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}, {
|
||||
video: false,
|
||||
audio: false
|
||||
} as MediaStreamConstraints);
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable webcam if the user requested so
|
||||
if ($requestedCameraState === false) {
|
||||
currentVideoConstraint = false;
|
||||
}
|
||||
|
||||
// Disable microphone if the user requested so
|
||||
if ($requestedMicrophoneState === false) {
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam and microphone when in a Jitsi
|
||||
if ($gameOverlayVisibilityStore === false) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam for privacy reasons (the game is not visible and we were talking to noone)
|
||||
if ($privacyShutdownStore === true) {
|
||||
currentVideoConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam for energy reasons (the user is not moving and we are talking to noone)
|
||||
if ($cameraEnergySavingStore === true) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Let's make the changes only if the new value is different from the old one.
|
||||
if (
|
||||
previousComputedVideoConstraint != currentVideoConstraint ||
|
||||
previousComputedAudioConstraint != currentAudioConstraint
|
||||
) {
|
||||
previousComputedVideoConstraint = currentVideoConstraint;
|
||||
previousComputedAudioConstraint = currentAudioConstraint;
|
||||
// Let's copy the objects.
|
||||
if (typeof previousComputedVideoConstraint !== "boolean") {
|
||||
previousComputedVideoConstraint = { ...previousComputedVideoConstraint };
|
||||
}
|
||||
if (typeof previousComputedAudioConstraint !== "boolean") {
|
||||
previousComputedAudioConstraint = { ...previousComputedAudioConstraint };
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
// Let's wait a little bit to avoid sending too many constraint changes.
|
||||
timeout = setTimeout(() => {
|
||||
set({
|
||||
video: currentVideoConstraint,
|
||||
audio: currentAudioConstraint,
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
{
|
||||
video: false,
|
||||
audio: false,
|
||||
} as MediaStreamConstraints
|
||||
);
|
||||
|
||||
export type LocalStreamStoreValue = StreamSuccessValue | StreamErrorValue;
|
||||
|
||||
interface StreamSuccessValue {
|
||||
type: "success",
|
||||
stream: MediaStream|null,
|
||||
type: "success";
|
||||
stream: MediaStream | null;
|
||||
// The constraints that we got (and not the one that have been requested)
|
||||
constraints: MediaStreamConstraints
|
||||
constraints: MediaStreamConstraints;
|
||||
}
|
||||
|
||||
interface StreamErrorValue {
|
||||
type: "error",
|
||||
error: Error,
|
||||
constraints: MediaStreamConstraints
|
||||
type: "error";
|
||||
error: Error;
|
||||
constraints: MediaStreamConstraints;
|
||||
}
|
||||
|
||||
let currentStream : MediaStream|null = null;
|
||||
let currentStream: MediaStream | null = null;
|
||||
|
||||
/**
|
||||
* Stops the camera from filming
|
||||
|
@ -411,84 +361,94 @@ function stopMicrophone(): void {
|
|||
/**
|
||||
* A store containing the MediaStream object (or null if nothing requested, or Error if an error occurred)
|
||||
*/
|
||||
export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalStreamStoreValue>(mediaStreamConstraintsStore, ($mediaStreamConstraintsStore, set) => {
|
||||
const constraints = { ...$mediaStreamConstraintsStore };
|
||||
export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalStreamStoreValue>(
|
||||
mediaStreamConstraintsStore,
|
||||
($mediaStreamConstraintsStore, set) => {
|
||||
const constraints = { ...$mediaStreamConstraintsStore };
|
||||
|
||||
if (navigator.mediaDevices === undefined) {
|
||||
if (window.location.protocol === 'http:') {
|
||||
//throw new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.');
|
||||
if (navigator.mediaDevices === undefined) {
|
||||
if (window.location.protocol === "http:") {
|
||||
//throw new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.');
|
||||
set({
|
||||
type: "error",
|
||||
error: new Error("Unable to access your camera or microphone. You need to use a HTTPS connection."),
|
||||
constraints,
|
||||
});
|
||||
return;
|
||||
} else if (isIOS()) {
|
||||
set({
|
||||
type: "error",
|
||||
error: new WebviewOnOldIOS(),
|
||||
constraints,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
set({
|
||||
type: "error",
|
||||
error: new BrowserTooOldError(),
|
||||
constraints,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (constraints.audio === false) {
|
||||
stopMicrophone();
|
||||
}
|
||||
if (constraints.video === false) {
|
||||
stopCamera();
|
||||
}
|
||||
|
||||
if (constraints.audio === false && constraints.video === false) {
|
||||
currentStream = null;
|
||||
set({
|
||||
type: 'error',
|
||||
error: new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'),
|
||||
constraints
|
||||
});
|
||||
return;
|
||||
} else if (isIOS()) {
|
||||
set({
|
||||
type: 'error',
|
||||
error: new WebviewOnOldIOS(),
|
||||
constraints
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
set({
|
||||
type: 'error',
|
||||
error: new BrowserTooOldError(),
|
||||
constraints
|
||||
type: "success",
|
||||
stream: null,
|
||||
constraints,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (constraints.audio === false) {
|
||||
stopMicrophone();
|
||||
}
|
||||
if (constraints.video === false) {
|
||||
stopCamera();
|
||||
}
|
||||
|
||||
if (constraints.audio === false && constraints.video === false) {
|
||||
currentStream = null;
|
||||
set({
|
||||
type: 'success',
|
||||
stream: null,
|
||||
constraints
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
stopMicrophone();
|
||||
stopCamera();
|
||||
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
set({
|
||||
type: 'success',
|
||||
stream: currentStream,
|
||||
constraints
|
||||
});
|
||||
return;
|
||||
} catch (e) {
|
||||
if (constraints.video !== false) {
|
||||
console.info("Error. Unable to get microphone and/or camera access. Trying audio only.", $mediaStreamConstraintsStore, e);
|
||||
// TODO: does it make sense to pop this error when retrying?
|
||||
(async () => {
|
||||
try {
|
||||
stopMicrophone();
|
||||
stopCamera();
|
||||
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
set({
|
||||
type: 'error',
|
||||
error: e,
|
||||
constraints
|
||||
type: "success",
|
||||
stream: currentStream,
|
||||
constraints,
|
||||
});
|
||||
// Let's try without video constraints
|
||||
requestedCameraState.disableWebcam();
|
||||
} else {
|
||||
console.info("Error. Unable to get microphone and/or camera access.", $mediaStreamConstraintsStore, e);
|
||||
set({
|
||||
type: 'error',
|
||||
error: e,
|
||||
constraints
|
||||
});
|
||||
}
|
||||
return;
|
||||
} catch (e) {
|
||||
if (constraints.video !== false) {
|
||||
console.info(
|
||||
"Error. Unable to get microphone and/or camera access. Trying audio only.",
|
||||
$mediaStreamConstraintsStore,
|
||||
e
|
||||
);
|
||||
// TODO: does it make sense to pop this error when retrying?
|
||||
set({
|
||||
type: "error",
|
||||
error: e,
|
||||
constraints,
|
||||
});
|
||||
// Let's try without video constraints
|
||||
requestedCameraState.disableWebcam();
|
||||
} else {
|
||||
console.info(
|
||||
"Error. Unable to get microphone and/or camera access.",
|
||||
$mediaStreamConstraintsStore,
|
||||
e
|
||||
);
|
||||
set({
|
||||
type: "error",
|
||||
error: e,
|
||||
constraints,
|
||||
});
|
||||
}
|
||||
|
||||
/*constraints.video = false;
|
||||
/*constraints.video = false;
|
||||
if (constraints.audio === false) {
|
||||
console.info("Error. Unable to get microphone and/or camera access.", $mediaStreamConstraintsStore, e);
|
||||
set({
|
||||
|
@ -517,9 +477,10 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
|
|||
});
|
||||
}
|
||||
}*/
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* A store containing the real active media constrained (not the one requested by the user, but the one we got from the system)
|
||||
|
@ -536,12 +497,15 @@ export const deviceListStore = readable<MediaDeviceInfo[]>([], function start(se
|
|||
|
||||
const queryDeviceList = () => {
|
||||
// Note: so far, we are ignoring any failures.
|
||||
navigator.mediaDevices.enumerateDevices().then((mediaDeviceInfos) => {
|
||||
set(mediaDeviceInfos);
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((mediaDeviceInfos) => {
|
||||
set(mediaDeviceInfos);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
const unsubscribe = localStreamStore.subscribe((streamResult) => {
|
||||
|
@ -554,23 +518,23 @@ export const deviceListStore = readable<MediaDeviceInfo[]>([], function start(se
|
|||
});
|
||||
|
||||
if (navigator.mediaDevices) {
|
||||
navigator.mediaDevices.addEventListener('devicechange', queryDeviceList);
|
||||
navigator.mediaDevices.addEventListener("devicechange", queryDeviceList);
|
||||
}
|
||||
|
||||
return function stop() {
|
||||
unsubscribe();
|
||||
if (navigator.mediaDevices) {
|
||||
navigator.mediaDevices.removeEventListener('devicechange', queryDeviceList);
|
||||
navigator.mediaDevices.removeEventListener("devicechange", queryDeviceList);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const cameraListStore = derived(deviceListStore, ($deviceListStore) => {
|
||||
return $deviceListStore.filter(device => device.kind === 'videoinput');
|
||||
return $deviceListStore.filter((device) => device.kind === "videoinput");
|
||||
});
|
||||
|
||||
export const microphoneListStore = derived(deviceListStore, ($deviceListStore) => {
|
||||
return $deviceListStore.filter(device => device.kind === 'audioinput');
|
||||
return $deviceListStore.filter((device) => device.kind === "audioinput");
|
||||
});
|
||||
|
||||
// TODO: detect the new webcam and automatically switch on it.
|
||||
|
@ -583,7 +547,7 @@ cameraListStore.subscribe((devices) => {
|
|||
|
||||
// If we cannot find the device ID, let's remove it.
|
||||
// @ts-ignore
|
||||
if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) {
|
||||
if (!devices.find((device) => device.deviceId === constraints.deviceId.exact)) {
|
||||
videoConstraintStore.setDeviceId(undefined);
|
||||
}
|
||||
});
|
||||
|
@ -591,7 +555,7 @@ cameraListStore.subscribe((devices) => {
|
|||
microphoneListStore.subscribe((devices) => {
|
||||
// If the selected camera is unplugged, let's remove the constraint on deviceId
|
||||
const constraints = get(audioConstraintStore);
|
||||
if (typeof constraints === 'boolean') {
|
||||
if (typeof constraints === "boolean") {
|
||||
return;
|
||||
}
|
||||
if (!constraints.deviceId) {
|
||||
|
@ -600,13 +564,13 @@ microphoneListStore.subscribe((devices) => {
|
|||
|
||||
// If we cannot find the device ID, let's remove it.
|
||||
// @ts-ignore
|
||||
if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) {
|
||||
if (!devices.find((device) => device.deviceId === constraints.deviceId.exact)) {
|
||||
audioConstraintStore.setDeviceId(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
localStreamStore.subscribe(streamResult => {
|
||||
if (streamResult.type === 'error') {
|
||||
localStreamStore.subscribe((streamResult) => {
|
||||
if (streamResult.type === "error") {
|
||||
if (streamResult.error.name === BrowserTooOldError.NAME || streamResult.error.name === WebviewOnOldIOS.NAME) {
|
||||
errorStore.addErrorMessage(streamResult.error);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue