Closing game webcame in Jitsi

When stepping in Jitsi, the game webcam (from mediaManager) was not shut down.
And when enabling/disabling the webcam in Jitsi, the webcam in mediaManager was also
enabled/disabled. This PR fixes those issues.

It also fixes a race condition when closing a Jitsi where the mic/cam would be enabled at the same time.
This commit is contained in:
David Négrier 2021-05-11 14:52:51 +02:00
parent 23bf78a026
commit ad39b43df3
3 changed files with 50 additions and 30 deletions

View File

@ -1475,6 +1475,8 @@ ${escapedMessage}
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
this.stopJitsi();
});
this.onVisibilityChange();
}
public stopJitsi(): void {
@ -1483,6 +1485,7 @@ ${escapedMessage}
mediaManager.showGameOverlay();
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
this.onVisibilityChange();
}
//todo: put this into an 'orchestrator' scene (EntryScene?)
@ -1519,6 +1522,12 @@ ${escapedMessage}
}
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 {

View File

@ -10,9 +10,10 @@ interface jitsiConfigInterface {
}
const getDefaultConfig = () : jitsiConfigInterface => {
const constraints = mediaManager.getConstraintRequestedByUser();
return {
startWithAudioMuted: !mediaManager.constraintsMedia.audio,
startWithVideoMuted: mediaManager.constraintsMedia.video === false,
startWithAudioMuted: !constraints.audio,
startWithVideoMuted: constraints.video === false,
prejoinPageEnabled: false
}
}
@ -71,7 +72,7 @@ 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 previousConfigMeet! : jitsiConfigInterface;
private jitsiScriptLoaded: boolean = false;
/**
@ -136,32 +137,24 @@ class JitsiFactory {
//restore previous config
if(this.previousConfigMeet?.startWithAudioMuted){
mediaManager.disableMicrophone();
await mediaManager.disableMicrophone();
}else{
mediaManager.enableMicrophone();
await mediaManager.enableMicrophone();
}
if(this.previousConfigMeet?.startWithVideoMuted){
mediaManager.disableCamera();
await mediaManager.disableCamera();
}else{
mediaManager.enableCamera();
await mediaManager.enableCamera();
}
}
private onAudioChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.audio === true) {
mediaManager.disableMicrophone();
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
mediaManager.enableMicrophone();
}
this.previousConfigMeet.startWithAudioMuted = muted;
}
private onVideoChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.video !== false) {
mediaManager.disableCamera();
} else if(!muted && mediaManager.constraintsMedia.video === false) {
mediaManager.enableCamera();
}
this.previousConfigMeet.startWithVideoMuted = muted;
}
private async loadJitsiScript(domain: string): Promise<void> {

View File

@ -155,6 +155,13 @@ export class MediaManager {
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;
@ -197,7 +204,7 @@ export class MediaManager {
}
}
public showGameOverlay(){
public showGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.add('active');
@ -208,7 +215,7 @@ export class MediaManager {
buttonCloseFrame.removeEventListener('click', functionTrigger);
}
public hideGameOverlay(){
public hideGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.remove('active');
@ -219,6 +226,11 @@ export class MediaManager {
buttonCloseFrame.addEventListener('click', functionTrigger);
}
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));
@ -230,29 +242,32 @@ export class MediaManager {
});
}
public enableCamera() {
public async enableCamera() {
this.constraintsMedia.video = videoConstraint;
this.getCamera().then((stream: MediaStream) => {
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 Error('Video track is empty, please check camera permission of your navigator')
throw new Error('Video track is empty, please check camera permission of your navigator')
}
this.enableCameraStyle();
this.triggerUpdatedLocalStreamCallbacks(stream);
}).catch((err) => {
} 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();
@ -262,25 +277,27 @@ export class MediaManager {
}
}
public enableMicrophone() {
public async enableMicrophone() {
this.constraintsMedia.audio = audioConstraint;
this.getCamera().then((stream) => {
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) {
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) => {
} 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() {
@ -325,7 +342,6 @@ export class MediaManager {
this.cinemaBtn.classList.add("disabled");
this.constraintsMedia.video = false;
this.myCamVideo.srcObject = null;
this.stopCamera();
}
private enableMicrophoneStyle(){
@ -436,6 +452,8 @@ export class MediaManager {
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;