Merge pull request #376 from thecodingmachine/windows-focus-blur-camera

Switch off camera when user is not focused on WorkAdventure windows
This commit is contained in:
grégoire parant 2020-11-23 15:35:45 +01:00 committed by GitHub
commit b0c54776b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 113 additions and 25 deletions

View File

@ -317,7 +317,7 @@ export class GameScene extends ResizableScene implements CenterListener {
// Let's alter browser history // Let's alter browser history
let path = this.room.id; let path = this.room.id;
if (this.room.hash) { if (this.room.hash) {
path += '#'+this.room.hash; path += '#' + this.room.hash;
} }
window.history.pushState({}, 'WorkAdventure', path); window.history.pushState({}, 'WorkAdventure', path);
@ -906,6 +906,7 @@ export class GameScene extends ResizableScene implements CenterListener {
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate. * @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/ */
update(time: number, delta: number) : void { update(time: number, delta: number) : void {
mediaManager.setLastUpdateScene();
this.currentTick = time; this.currentTick = time;
this.CurrentPlayer.moveUser(delta); this.CurrentPlayer.moveUser(delta);

View File

@ -40,6 +40,12 @@ export class MediaManager {
private cinemaBtn: HTMLDivElement; private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement; private monitorBtn: HTMLDivElement;
private previousConstraint : MediaStreamConstraints;
private focused : boolean = true;
private lastUpdateScene : Date = new Date();
private setTimeOutlastUpdateScene? : NodeJS.Timeout;
private discussionManager: DiscussionManager; private discussionManager: DiscussionManager;
private userInputManager?: UserInputManager; private userInputManager?: UserInputManager;
@ -98,9 +104,35 @@ export class MediaManager {
//update tracking //update tracking
}); });
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
this.pingCameraStatus();
this.checkActiveUser();
this.discussionManager = new DiscussionManager(this,''); this.discussionManager = new DiscussionManager(this,'');
} }
public setLastUpdateScene(){
this.lastUpdateScene = new Date();
}
public blurCamera() {
if(!this.focused){
return;
}
this.focused = false;
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
this.disableCamera();
}
public focusCamera() {
if(this.focused){
return;
}
this.focused = true;
this.applyPreviousConfig();
}
public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
this.updatedLocalStreamCallBacks.add(callback); this.updatedLocalStreamCallBacks.add(callback);
} }
@ -146,12 +178,7 @@ export class MediaManager {
} }
public enableCamera() { public enableCamera() {
if(!this.hasCamera){ this.enableCameraStyle();
return;
}
this.cinemaClose.style.display = "none";
this.cinemaBtn.classList.remove("disabled");
this.cinema.style.display = "block";
this.constraintsMedia.video = videoConstraint; this.constraintsMedia.video = videoConstraint;
this.getCamera().then((stream: MediaStream) => { this.getCamera().then((stream: MediaStream) => {
this.triggerUpdatedLocalStreamCallbacks(stream); this.triggerUpdatedLocalStreamCallbacks(stream);
@ -159,7 +186,8 @@ export class MediaManager {
} }
public async disableCamera() { public async disableCamera() {
this.disabledCameraView(); this.disableCameraStyle();
if (this.constraintsMedia.audio !== false) { if (this.constraintsMedia.audio !== false) {
const stream = await this.getCamera(); const stream = await this.getCamera();
this.triggerUpdatedLocalStreamCallbacks(stream); this.triggerUpdatedLocalStreamCallbacks(stream);
@ -168,19 +196,8 @@ export class MediaManager {
} }
} }
private disabledCameraView(){
this.cinemaClose.style.display = "block";
this.cinema.style.display = "none";
this.cinemaBtn.classList.add("disabled");
this.constraintsMedia.video = false;
this.myCamVideo.srcObject = null;
this.stopCamera();
}
public enableMicrophone() { public enableMicrophone() {
this.microphoneClose.style.display = "none"; this.enableMicrophoneStyle();
this.microphone.style.display = "block";
this.microphoneBtn.classList.remove("disabled");
this.constraintsMedia.audio = true; this.constraintsMedia.audio = true;
this.getCamera().then((stream) => { this.getCamera().then((stream) => {
@ -189,10 +206,7 @@ export class MediaManager {
} }
public async disableMicrophone() { public async disableMicrophone() {
this.microphoneClose.style.display = "block"; this.disableMicrophoneStyle();
this.microphone.style.display = "none";
this.microphoneBtn.classList.add("disabled");
this.constraintsMedia.audio = false;
this.stopMicrophone(); this.stopMicrophone();
if (this.constraintsMedia.video !== false) { if (this.constraintsMedia.video !== false) {
@ -203,6 +217,52 @@ export class MediaManager {
} }
} }
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");
this.cinema.style.display = "block";
}
private disableCameraStyle(){
this.cinemaClose.style.display = "block";
this.cinema.style.display = "none";
this.cinemaBtn.classList.add("disabled");
this.constraintsMedia.video = false;
this.myCamVideo.srcObject = null;
this.stopCamera();
}
private enableMicrophoneStyle(){
this.microphoneClose.style.display = "none";
this.microphone.style.display = "block";
this.microphoneBtn.classList.remove("disabled");
}
private disableMicrophoneStyle(){
this.microphoneClose.style.display = "block";
this.microphone.style.display = "none";
this.microphoneBtn.classList.add("disabled");
this.constraintsMedia.audio = false;
}
private enableScreenSharing() { private enableScreenSharing() {
this.monitorClose.style.display = "none"; this.monitorClose.style.display = "none";
this.monitor.style.display = "block"; this.monitor.style.display = "block";
@ -285,7 +345,7 @@ export class MediaManager {
return this.getLocalStream().catch(() => { return this.getLocalStream().catch(() => {
console.info('Error get camera, trying with video option at null'); console.info('Error get camera, trying with video option at null');
this.disabledCameraView(); this.disableCameraStyle();
return this.getLocalStream().then((stream : MediaStream) => { return this.getLocalStream().then((stream : MediaStream) => {
this.hasCamera = false; this.hasCamera = false;
return stream; return stream;
@ -594,6 +654,17 @@ export class MediaManager {
public removeParticipant(userId: number|string){ public removeParticipant(userId: number|string){
this.discussionManager.removeParticipant(userId); this.discussionManager.removeParticipant(userId);
} }
/**
* For some reasons, the microphone muted icon or the stream is not always up to date.
* Here, every 30 seconds, we are "reseting" the streams and sending again the constraints to the other peers via the data channel again (see SimplePeer::pushVideoToRemoteUser)
**/
private pingCameraStatus(){
setTimeout(() => {
console.log('ping camera status');
this.triggerUpdatedLocalStreamCallbacks(this.localStream);
this.pingCameraStatus();
}, 30000);
}
public addNewMessage(name: string, message: string, isMe: boolean = false){ public addNewMessage(name: string, message: string, isMe: boolean = false){
this.discussionManager.addMessage(name, message, isMe); this.discussionManager.addMessage(name, message, isMe);
@ -615,6 +686,22 @@ export class MediaManager {
public setUserInputManager(userInputManager : UserInputManager){ public setUserInputManager(userInputManager : UserInputManager){
this.discussionManager.setUserInputManager(userInputManager); this.discussionManager.setUserInputManager(userInputManager);
} }
//check if user is active
private checkActiveUser(){
if(this.setTimeOutlastUpdateScene){
clearTimeout(this.setTimeOutlastUpdateScene);
}
this.setTimeOutlastUpdateScene = setTimeout(() => {
const now = new Date();
//if last update is more of 10 sec
if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) {
this.blurCamera();
}else{
this.focusCamera();
}
this.checkActiveUser();
}, this.focused ? 10000 : 1000);
}
} }
export const mediaManager = new MediaManager(); export const mediaManager = new MediaManager();