Migrating MediaManager game part to Svelte store
This commit is contained in:
parent
28d78a7988
commit
8af8ccd54b
@ -190,7 +190,6 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
private originalMapUrl: string|undefined;
|
||||
private pinchManager: PinchManager|undefined;
|
||||
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
|
||||
private onVisibilityChangeCallback: () => void;
|
||||
private emoteManager!: EmoteManager;
|
||||
|
||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||
@ -211,7 +210,6 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||
this.connectionAnswerPromiseResolve = resolve;
|
||||
});
|
||||
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
@ -516,8 +514,6 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
|
||||
this.emoteManager = new EmoteManager(this);
|
||||
}
|
||||
|
||||
@ -641,7 +637,6 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
self.chatModeSprite.setVisible(false);
|
||||
self.openChatIcon.setVisible(false);
|
||||
audioManager.restoreVolume();
|
||||
self.onVisibilityChange();
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -946,8 +941,6 @@ ${escapedMessage}
|
||||
for(const iframeEvents of this.iframeSubscriptionList){
|
||||
iframeEvents.unsubscribe();
|
||||
}
|
||||
|
||||
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
}
|
||||
|
||||
private removeAllRemotePlayers(): void {
|
||||
@ -1507,8 +1500,6 @@ ${escapedMessage}
|
||||
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
|
||||
this.stopJitsi();
|
||||
});
|
||||
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
public stopJitsi(): void {
|
||||
@ -1517,7 +1508,6 @@ ${escapedMessage}
|
||||
mediaManager.showGameOverlay();
|
||||
|
||||
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||
@ -1557,20 +1547,4 @@ ${escapedMessage}
|
||||
waScaleManager.zoomModifier *= zoomFactor;
|
||||
this.updateCameraOffset();
|
||||
}
|
||||
|
||||
private onVisibilityChange(): void {
|
||||
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
|
||||
if (!mediaManager.isGameOverlayVisible()) {
|
||||
mediaManager.blurCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
mediaManager.focusCamera();
|
||||
} else {
|
||||
if (this.simplePeer.getNbConnections() === 0) {
|
||||
mediaManager.blurCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {DirtyScene} from "../Game/DirtyScene";
|
||||
import {get} from "svelte/store";
|
||||
import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore";
|
||||
|
||||
export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene';
|
||||
const helpCameraSettings = 'helpCameraSettings';
|
||||
@ -41,7 +43,7 @@ export class HelpCameraSettingsScene extends DirtyScene {
|
||||
}
|
||||
});
|
||||
|
||||
if(!localUserStore.getHelpCameraSettingsShown() && (!mediaManager.constraintsMedia.audio || !mediaManager.constraintsMedia.video)){
|
||||
if(!localUserStore.getHelpCameraSettingsShown() && (!get(requestedMicrophoneState) || !get(requestedCameraState))){
|
||||
this.openHelpCameraSettingsOpened();
|
||||
localUserStore.setHelpCameraSettingsShown();
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import {GameConnexionTypes} from "../../Url/UrlManager";
|
||||
import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer";
|
||||
import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream";
|
||||
import {menuIconVisible} from "../../Stores/MenuStore";
|
||||
import {videoConstraintStore} from "../../Stores/MediaStore";
|
||||
|
||||
export const MenuSceneName = 'MenuScene';
|
||||
const gameMenuKey = 'gameMenu';
|
||||
@ -324,7 +325,7 @@ export class MenuScene extends Phaser.Scene {
|
||||
if (valueVideo !== this.videoQualityValue) {
|
||||
this.videoQualityValue = valueVideo;
|
||||
localUserStore.setVideoQualityValue(valueVideo);
|
||||
mediaManager.updateCameraQuality(valueVideo);
|
||||
videoConstraintStore.setFrameRate(valueVideo);
|
||||
}
|
||||
this.closeGameQualityMenu();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {derived, Readable, readable, writable, Writable} from "svelte/store";
|
||||
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";
|
||||
@ -76,6 +76,40 @@ 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 that contains video constraints.
|
||||
*/
|
||||
@ -87,22 +121,20 @@ function createVideoConstraintStore() {
|
||||
facingMode: "user",
|
||||
resizeMode: 'crop-and-scale',
|
||||
aspectRatio: 1.777777778
|
||||
} as boolean|MediaTrackConstraints);
|
||||
|
||||
let selectedDeviceId = null;
|
||||
} as MediaTrackConstraints);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setDeviceId: (deviceId: string) => update((constraints) => {
|
||||
selectedDeviceId = deviceId;
|
||||
|
||||
if (typeof(constraints) === 'boolean') {
|
||||
constraints = {}
|
||||
}
|
||||
constraints.deviceId = {
|
||||
exact: selectedDeviceId
|
||||
exact: deviceId
|
||||
};
|
||||
|
||||
return constraints;
|
||||
}),
|
||||
setFrameRate: (frameRate: number) => update((constraints) => {
|
||||
constraints.frameRate = { ideal: frameRate };
|
||||
|
||||
return constraints;
|
||||
})
|
||||
};
|
||||
@ -145,6 +177,9 @@ export const audioConstraintStore = createAudioConstraintStore();
|
||||
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
let previousComputedVideoConstraint: boolean|MediaTrackConstraints = false;
|
||||
let previousComputedAudioConstraint: boolean|MediaTrackConstraints = false;
|
||||
|
||||
/**
|
||||
* A store containing the media constraints we want to apply.
|
||||
*/
|
||||
@ -152,24 +187,23 @@ export const mediaStreamConstraintsStore = derived(
|
||||
[
|
||||
requestedCameraState,
|
||||
requestedMicrophoneState,
|
||||
visibilityStore,
|
||||
gameOverlayVisibilityStore,
|
||||
peerStore,
|
||||
enableCameraSceneVisibilityStore,
|
||||
videoConstraintStore,
|
||||
audioConstraintStore,
|
||||
privacyShutdownStore,
|
||||
], (
|
||||
[
|
||||
$requestedCameraState,
|
||||
$requestedMicrophoneState,
|
||||
$visibilityStore,
|
||||
$gameOverlayVisibilityStore,
|
||||
$peerStore,
|
||||
$enableCameraSceneVisibilityStore,
|
||||
$videoConstraintStore,
|
||||
$audioConstraintStore,
|
||||
$privacyShutdownStore,
|
||||
], set
|
||||
) => {
|
||||
|
||||
let currentVideoConstraint: boolean|MediaTrackConstraints = $videoConstraintStore;
|
||||
let currentAudioConstraint: boolean|MediaTrackConstraints = $audioConstraintStore;
|
||||
|
||||
@ -197,11 +231,23 @@ export const mediaStreamConstraintsStore = derived(
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Disable webcam if the game is not visible and we are talking to noone.
|
||||
if ($visibilityStore === false && $peerStore.size === 0) {
|
||||
// Disable webcam for privacy reasons (the game is not visible and we were talking to noone)
|
||||
if ($privacyShutdownStore === true) {
|
||||
currentVideoConstraint = 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);
|
||||
}
|
||||
@ -212,7 +258,8 @@ export const mediaStreamConstraintsStore = derived(
|
||||
video: currentVideoConstraint,
|
||||
audio: currentAudioConstraint,
|
||||
});
|
||||
}, 100)
|
||||
}, 100);
|
||||
}
|
||||
}, {
|
||||
video: false,
|
||||
audio: false
|
||||
@ -289,6 +336,7 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
|
||||
}
|
||||
|
||||
if (constraints.audio === false && constraints.video === false) {
|
||||
currentStream = null;
|
||||
set({
|
||||
type: 'success',
|
||||
stream: null,
|
||||
@ -299,6 +347,8 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
stopMicrophone();
|
||||
stopCamera();
|
||||
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
set({
|
||||
type: 'success',
|
||||
@ -358,3 +408,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)
|
||||
*/
|
||||
export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStreamStore) => {
|
||||
return $localStreamStore.constraints;
|
||||
});
|
||||
|
@ -17,12 +17,16 @@ function createPeerStore() {
|
||||
set(users);
|
||||
simplePeer.registerPeerConnectionListener({
|
||||
onConnect(user: UserSimplePeerInterface) {
|
||||
update(users => {
|
||||
users.set(user.userId, user);
|
||||
set(users);
|
||||
return users;
|
||||
});
|
||||
},
|
||||
onDisconnect(userId: number) {
|
||||
update(users => {
|
||||
users.delete(userId);
|
||||
set(users);
|
||||
return users;
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {JITSI_URL} from "../Enum/EnvironmentVariable";
|
||||
import {mediaManager} from "./MediaManager";
|
||||
import {coWebsiteManager} from "./CoWebsiteManager";
|
||||
import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
|
||||
import {get} from "svelte/store";
|
||||
declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
interface jitsiConfigInterface {
|
||||
@ -11,10 +12,9 @@ interface jitsiConfigInterface {
|
||||
}
|
||||
|
||||
const getDefaultConfig = () : jitsiConfigInterface => {
|
||||
const constraints = mediaManager.getConstraintRequestedByUser();
|
||||
return {
|
||||
startWithAudioMuted: !constraints.audio,
|
||||
startWithVideoMuted: constraints.video === false,
|
||||
startWithAudioMuted: !get(requestedMicrophoneState),
|
||||
startWithVideoMuted: !get(requestedCameraState),
|
||||
prejoinPageEnabled: false
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,6 @@ class JitsiFactory {
|
||||
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
private audioCallback = this.onAudioChange.bind(this);
|
||||
private videoCallback = this.onVideoChange.bind(this);
|
||||
private previousConfigMeet! : jitsiConfigInterface;
|
||||
private jitsiScriptLoaded: boolean = false;
|
||||
|
||||
/**
|
||||
@ -84,9 +83,6 @@ class JitsiFactory {
|
||||
}
|
||||
|
||||
public start(roomName: string, playerName:string, jwt?: string, config?: object, interfaceConfig?: object, jitsiUrl?: string): void {
|
||||
//save previous config
|
||||
this.previousConfigMeet = getDefaultConfig();
|
||||
|
||||
coWebsiteManager.insertCoWebsite((async cowebsiteDiv => {
|
||||
// Jitsi meet external API maintains some data in local storage
|
||||
// which is sent via the appData URL parameter when joining a
|
||||
@ -135,31 +131,22 @@ class JitsiFactory {
|
||||
this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback);
|
||||
this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback);
|
||||
this.jitsiApi?.dispose();
|
||||
|
||||
//restore previous config
|
||||
if(this.previousConfigMeet?.startWithAudioMuted){
|
||||
await mediaManager.disableMicrophone();
|
||||
requestedMicrophoneState.disableMicrophone();
|
||||
}else{
|
||||
await mediaManager.enableMicrophone();
|
||||
requestedMicrophoneState.enableMicrophone();
|
||||
}
|
||||
|
||||
if(this.previousConfigMeet?.startWithVideoMuted){
|
||||
await mediaManager.disableCamera();
|
||||
requestedCameraState.disableWebcam();
|
||||
}else{
|
||||
await mediaManager.enableCamera();
|
||||
requestedCameraState.enableWebcam();
|
||||
}
|
||||
}
|
||||
|
||||
private onAudioChange({muted}: {muted: boolean}): void {
|
||||
this.previousConfigMeet.startWithAudioMuted = muted;
|
||||
if (muted) {
|
||||
requestedMicrophoneState.disableMicrophone();
|
||||
} else {
|
||||
requestedMicrophoneState.enableMicrophone();
|
||||
}
|
||||
}
|
||||
|
||||
private onVideoChange({muted}: {muted: boolean}): void {
|
||||
this.previousConfigMeet.startWithVideoMuted = muted;
|
||||
if (muted) {
|
||||
requestedCameraState.disableWebcam();
|
||||
} else {
|
||||
requestedCameraState.enableWebcam();
|
||||
}
|
||||
}
|
||||
|
||||
private async loadJitsiScript(domain: string): Promise<void> {
|
||||
|
@ -7,7 +7,7 @@ import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
|
||||
import {
|
||||
gameOverlayVisibilityStore,
|
||||
gameOverlayVisibilityStore, localStreamStore,
|
||||
mediaStreamConstraintsStore,
|
||||
requestedCameraState,
|
||||
requestedMicrophoneState
|
||||
@ -15,7 +15,7 @@ import {
|
||||
|
||||
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
let videoConstraint: boolean|MediaTrackConstraints = {
|
||||
const videoConstraint: boolean|MediaTrackConstraints = {
|
||||
width: { min: 640, ideal: 1280, max: 1920 },
|
||||
height: { min: 400, ideal: 720 },
|
||||
frameRate: { ideal: localUserStore.getVideoQualityValue() },
|
||||
@ -37,7 +37,6 @@ export type ReportCallback = (message: string) => void;
|
||||
export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
|
||||
export type HelpCameraSettingsCallBack = () => void;
|
||||
|
||||
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
|
||||
export class MediaManager {
|
||||
localStream: MediaStream|null = null;
|
||||
localScreenCapture: MediaStream|null = null;
|
||||
@ -53,10 +52,6 @@ export class MediaManager {
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//mySoundMeterElement: HTMLDivElement;
|
||||
private webrtcOutAudio: HTMLAudioElement;
|
||||
constraintsMedia : MediaStreamConstraints = {
|
||||
audio: audioConstraint,
|
||||
video: videoConstraint
|
||||
};
|
||||
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
|
||||
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||
@ -67,11 +62,8 @@ export class MediaManager {
|
||||
private cinemaBtn: HTMLDivElement;
|
||||
private monitorBtn: HTMLDivElement;
|
||||
|
||||
private previousConstraint : MediaStreamConstraints;
|
||||
private focused : boolean = true;
|
||||
|
||||
private hasCamera = true;
|
||||
|
||||
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
||||
|
||||
private userInputManager?: UserInputManager;
|
||||
@ -94,15 +86,11 @@ export class MediaManager {
|
||||
this.microphoneClose.style.display = "none";
|
||||
this.microphoneClose.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.enableMicrophone();
|
||||
//update tracking
|
||||
requestedMicrophoneState.enableMicrophone();
|
||||
});
|
||||
this.microphone = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('microphone');
|
||||
this.microphone.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.disableMicrophone();
|
||||
//update tracking
|
||||
requestedMicrophoneState.disableMicrophone();
|
||||
});
|
||||
|
||||
@ -111,15 +99,11 @@ export class MediaManager {
|
||||
this.cinemaClose.style.display = "none";
|
||||
this.cinemaClose.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.enableCamera();
|
||||
//update tracking
|
||||
requestedCameraState.enableWebcam();
|
||||
});
|
||||
this.cinema = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('cinema');
|
||||
this.cinema.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.disableCamera();
|
||||
//update tracking
|
||||
requestedCameraState.disableWebcam();
|
||||
});
|
||||
|
||||
@ -129,20 +113,17 @@ export class MediaManager {
|
||||
this.monitorClose.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.enableScreenSharing();
|
||||
//update tracking
|
||||
});
|
||||
this.monitor = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('monitor');
|
||||
this.monitor.style.display = "none";
|
||||
this.monitor.addEventListener('click', (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.disableScreenSharing();
|
||||
//update tracking
|
||||
});
|
||||
|
||||
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
|
||||
this.pingCameraStatus();
|
||||
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//FIX ME SOUNDMETER: check stability of sound meter calculation
|
||||
/*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
|
||||
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
|
||||
this.mySoundMeterElement.children.item(index)?.classList.remove('active');
|
||||
@ -150,37 +131,40 @@ export class MediaManager {
|
||||
|
||||
//Check of ask notification navigator permission
|
||||
this.getNotification();
|
||||
|
||||
localStreamStore.subscribe((result) => {
|
||||
if (result.type === 'error') {
|
||||
console.error(result.error);
|
||||
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
|
||||
this.showHelpCameraSettingsCallBack();
|
||||
}, this.userInputManager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.constraints.video !== false) {
|
||||
this.enableCameraStyle();
|
||||
} else {
|
||||
this.disableCameraStyle();
|
||||
}
|
||||
if (result.constraints.audio !== false) {
|
||||
this.enableMicrophoneStyle();
|
||||
} else {
|
||||
this.disableMicrophoneStyle();
|
||||
}
|
||||
|
||||
this.localStream = result.stream;
|
||||
this.myCamVideo.srcObject = this.localStream;
|
||||
|
||||
// TODO: migrate all listeners to the store directly.
|
||||
this.triggerUpdatedLocalStreamCallbacks(result.stream);
|
||||
});
|
||||
}
|
||||
|
||||
public updateScene(){
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//FIX ME SOUNDMETER: check stability of sound meter calculation
|
||||
//this.updateSoudMeter();
|
||||
}
|
||||
|
||||
public blurCamera() {
|
||||
if(!this.focused){
|
||||
return;
|
||||
}
|
||||
this.focused = false;
|
||||
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
|
||||
this.disableCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the constraint that the user wants (independently of the visibility / jitsi state...)
|
||||
*/
|
||||
public getConstraintRequestedByUser(): MediaStreamConstraints {
|
||||
return this.previousConstraint ?? this.constraintsMedia;
|
||||
}
|
||||
|
||||
public focusCamera() {
|
||||
if(this.focused){
|
||||
return;
|
||||
}
|
||||
this.focused = true;
|
||||
this.applyPreviousConfig();
|
||||
}
|
||||
|
||||
public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
|
||||
this.updatedLocalStreamCallBacks.add(callback);
|
||||
}
|
||||
@ -241,110 +225,6 @@ export class MediaManager {
|
||||
gameOverlayVisibilityStore.hideGameOverlay();
|
||||
}
|
||||
|
||||
public isGameOverlayVisible(): boolean {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||
return gameOverlay.classList.contains('active');
|
||||
}
|
||||
|
||||
public updateCameraQuality(value: number) {
|
||||
this.enableCameraStyle();
|
||||
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
|
||||
newVideoConstraint.frameRate = {exact: value, ideal: value};
|
||||
videoConstraint = newVideoConstraint;
|
||||
this.constraintsMedia.video = videoConstraint;
|
||||
this.getCamera().then((stream: MediaStream) => {
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
});
|
||||
}
|
||||
|
||||
public async enableCamera() {
|
||||
this.constraintsMedia.video = videoConstraint;
|
||||
|
||||
try {
|
||||
const stream = await this.getCamera()
|
||||
//TODO show error message tooltip upper of camera button
|
||||
//TODO message : please check camera permission of your navigator
|
||||
if(stream.getVideoTracks().length === 0) {
|
||||
throw new Error('Video track is empty, please check camera permission of your navigator')
|
||||
}
|
||||
this.enableCameraStyle();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
|
||||
this.showHelpCameraSettingsCallBack();
|
||||
}, this.userInputManager);
|
||||
}
|
||||
}
|
||||
|
||||
public async disableCamera() {
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
if (this.constraintsMedia.audio !== false) {
|
||||
const stream = await this.getCamera();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
} else {
|
||||
this.triggerUpdatedLocalStreamCallbacks(null);
|
||||
}
|
||||
}
|
||||
|
||||
public async enableMicrophone() {
|
||||
this.constraintsMedia.audio = audioConstraint;
|
||||
|
||||
try {
|
||||
const stream = await this.getCamera();
|
||||
|
||||
//TODO show error message tooltip upper of camera button
|
||||
//TODO message : please check microphone permission of your navigator
|
||||
if (stream.getAudioTracks().length === 0) {
|
||||
throw Error('Audio track is empty, please check microphone permission of your navigator')
|
||||
}
|
||||
this.enableMicrophoneStyle();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
this.disableMicrophoneStyle();
|
||||
|
||||
layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => {
|
||||
this.showHelpCameraSettingsCallBack();
|
||||
}, this.userInputManager);
|
||||
}
|
||||
}
|
||||
|
||||
public async disableMicrophone() {
|
||||
this.disableMicrophoneStyle();
|
||||
this.stopMicrophone();
|
||||
|
||||
if (this.constraintsMedia.video !== false) {
|
||||
const stream = await this.getCamera();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
} else {
|
||||
this.triggerUpdatedLocalStreamCallbacks(null);
|
||||
}
|
||||
}
|
||||
|
||||
private applyPreviousConfig() {
|
||||
this.constraintsMedia = this.previousConstraint;
|
||||
if(!this.constraintsMedia.video){
|
||||
this.disableCameraStyle();
|
||||
}else{
|
||||
this.enableCameraStyle();
|
||||
}
|
||||
if(!this.constraintsMedia.audio){
|
||||
this.disableMicrophoneStyle()
|
||||
}else{
|
||||
this.enableMicrophoneStyle()
|
||||
}
|
||||
|
||||
this.getCamera().then((stream: MediaStream) => {
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
});
|
||||
}
|
||||
|
||||
private enableCameraStyle(){
|
||||
this.cinemaClose.style.display = "none";
|
||||
this.cinemaBtn.classList.remove("disabled");
|
||||
@ -355,8 +235,6 @@ export class MediaManager {
|
||||
this.cinemaClose.style.display = "block";
|
||||
this.cinema.style.display = "none";
|
||||
this.cinemaBtn.classList.add("disabled");
|
||||
this.constraintsMedia.video = false;
|
||||
this.myCamVideo.srcObject = null;
|
||||
}
|
||||
|
||||
private enableMicrophoneStyle(){
|
||||
@ -369,7 +247,6 @@ export class MediaManager {
|
||||
this.microphoneClose.style.display = "block";
|
||||
this.microphone.style.display = "none";
|
||||
this.microphoneBtn.classList.add("disabled");
|
||||
this.constraintsMedia.audio = false;
|
||||
}
|
||||
|
||||
private enableScreenSharing() {
|
||||
@ -403,12 +280,12 @@ export class MediaManager {
|
||||
return;
|
||||
}
|
||||
const localScreenCapture = this.localScreenCapture;
|
||||
this.getCamera().then((stream) => {
|
||||
//this.getCamera().then((stream) => {
|
||||
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
||||
}).catch((err) => { //catch error get camera
|
||||
/*}).catch((err) => { //catch error get camera
|
||||
console.error(err);
|
||||
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
||||
});
|
||||
});*/
|
||||
this.localScreenCapture = null;
|
||||
}
|
||||
|
||||
@ -454,55 +331,6 @@ export class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
//get camera
|
||||
async getCamera(): Promise<MediaStream> {
|
||||
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.');
|
||||
} else {
|
||||
throw new Error('Unable to access your camera or microphone. Your browser is too old.');
|
||||
}
|
||||
}
|
||||
|
||||
return this.getLocalStream().catch((err) => {
|
||||
console.info('Error get camera, trying with video option at null =>', err);
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
return this.getLocalStream().then((stream : MediaStream) => {
|
||||
this.hasCamera = false;
|
||||
return stream;
|
||||
}).catch((err) => {
|
||||
this.disableMicrophoneStyle();
|
||||
console.info("error get media ", this.constraintsMedia.video, this.constraintsMedia.audio, err);
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
|
||||
//TODO resize remote cam
|
||||
/*console.log(this.localStream.getTracks());
|
||||
let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video");
|
||||
let {width, height} = videoMediaStreamTrack.getSettings();
|
||||
console.info(`${width}x${height}`); // 6*/
|
||||
}
|
||||
|
||||
private getLocalStream() : Promise<MediaStream> {
|
||||
return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream : MediaStream) => {
|
||||
this.localStream = stream;
|
||||
this.myCamVideo.srcObject = this.localStream;
|
||||
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*this.mySoundMeter = null;
|
||||
if(this.constraintsMedia.audio){
|
||||
this.mySoundMeter = new SoundMeter();
|
||||
this.mySoundMeter.connectToSource(stream, new AudioContext());
|
||||
}*/
|
||||
return stream;
|
||||
}).catch((err: Error) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the camera from filming
|
||||
*/
|
||||
@ -526,30 +354,6 @@ export class MediaManager {
|
||||
//this.mySoundMeter?.stop();
|
||||
}
|
||||
|
||||
setCamera(id: string): Promise<MediaStream> {
|
||||
let video = this.constraintsMedia.video;
|
||||
if (typeof(video) === 'boolean' || video === undefined) {
|
||||
video = {}
|
||||
}
|
||||
video.deviceId = {
|
||||
exact: id
|
||||
};
|
||||
|
||||
return this.getCamera();
|
||||
}
|
||||
|
||||
setMicrophone(id: string): Promise<MediaStream> {
|
||||
let audio = this.constraintsMedia.audio;
|
||||
if (typeof(audio) === 'boolean' || audio === undefined) {
|
||||
audio = {}
|
||||
}
|
||||
audio.deviceId = {
|
||||
exact: id
|
||||
};
|
||||
|
||||
return this.getCamera();
|
||||
}
|
||||
|
||||
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
|
||||
this.webrtcInAudio.play();
|
||||
const userId = ''+user.userId
|
||||
|
@ -14,6 +14,8 @@ import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||
import {blackListManager} from "./BlackListManager";
|
||||
import {get} from "svelte/store";
|
||||
import {localStreamStore, obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
||||
|
||||
export interface UserSimplePeerInterface{
|
||||
userId: number;
|
||||
@ -82,12 +84,11 @@ export class SimplePeer {
|
||||
});
|
||||
|
||||
mediaManager.showGameOverlay();
|
||||
mediaManager.getCamera().finally(() => {
|
||||
|
||||
//receive message start
|
||||
this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => {
|
||||
this.receiveWebrtcStart(message);
|
||||
});
|
||||
});
|
||||
|
||||
this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => {
|
||||
this.closeConnection(data.userId);
|
||||
@ -344,8 +345,15 @@ export class SimplePeer {
|
||||
if (!PeerConnection) {
|
||||
throw new Error('While adding media, cannot find user with ID ' + userId);
|
||||
}
|
||||
const localStream: MediaStream | null = mediaManager.localStream;
|
||||
PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia})));
|
||||
|
||||
const result = get(localStreamStore);
|
||||
|
||||
PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...result.constraints})));
|
||||
|
||||
if (result.type === 'error') {
|
||||
return;
|
||||
}
|
||||
const localStream: MediaStream | null = result.stream;
|
||||
|
||||
if(!localStream){
|
||||
return;
|
||||
|
@ -5,6 +5,8 @@ import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {blackListManager} from "./BlackListManager";
|
||||
import type {Subscription} from "rxjs";
|
||||
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import {get} from "svelte/store";
|
||||
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||
|
||||
@ -191,7 +193,7 @@ export class VideoPeer extends Peer {
|
||||
private pushVideoToRemoteUser() {
|
||||
try {
|
||||
const localStream: MediaStream | null = mediaManager.localStream;
|
||||
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia})));
|
||||
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)})));
|
||||
|
||||
if(!localStream){
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user