From e06b20fe9683ad1634b7a59e32366ae4dae62c7c Mon Sep 17 00:00:00 2001 From: gparant Date: Fri, 1 May 2020 21:15:00 +0200 Subject: [PATCH 1/9] Update video style and start peer connexion --- back/src/Controller/IoSocketController.ts | 37 +++++---- back/src/Model/Group.ts | 23 ++++-- back/src/Model/World.ts | 10 +-- front/dist/index.html | 2 - front/dist/resources/style/style.css | 96 ++++++++-------------- front/src/Phaser/Player/Player.ts | 6 +- front/src/WebRtc/SimplePeer.ts | 97 +++++++++++++++-------- 7 files changed, 140 insertions(+), 131 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 23a238e6..3a17242a 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -8,7 +8,7 @@ import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_E import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface"; import {World} from "../Model/World"; -import { uuid } from 'uuidv4'; +import {Group} from "_Model/Group"; enum SockerIoEvent { CONNECTION = "connection", @@ -44,10 +44,10 @@ export class IoSocketController{ this.shareUsersPosition(); //don't send only function because the context will be not this - this.World = new World((user1 : string, user2 : string) => { - this.connectedUser(user1, user2); - }, (user1 : string, user2 : string) => { - this.disConnectedUser(user1, user2); + this.World = new World((user1 : string, user2 : string, group: Group) => { + this.connectedUser(user1, user2, group); + }, (user1 : string, user2 : string, group: Group) => { + this.disConnectedUser(user1, user2, group); }); } @@ -230,24 +230,27 @@ export class IoSocketController{ } //connected user - connectedUser(user1 : string, user2 : string){ - /* TODO manager room and group user to enter and leave */ - let roomId = uuid(); - let clients : Array = Object.values(this.Io.sockets.sockets); - let User1 = clients.find((user : ExSocketInterface) => user.userId === user1); - let User2 = clients.find((user : ExSocketInterface) => user.userId === user2); - - if(User1) { - this.joinWebRtcRoom(User1, roomId); + connectedUser(user1 : string, user2 : string, group : Group) { + if(!group){ + return; } - if(User2) { - this.joinWebRtcRoom(User2, roomId); + /* TODO manager room and group user to enter and leave */ + let clients: Array = Object.values(this.Io.sockets.sockets); + let User1 = clients.find((user: ExSocketInterface) => user.userId === user1); + let User2 = clients.find((user: ExSocketInterface) => user.userId === user2); + + if (User1) { + this.joinWebRtcRoom(User1, group.getId()); + } + if (User2) { + this.joinWebRtcRoom(User2, group.getId()); } } //connected user - disConnectedUser(user1 : string, user2 : string){ + disConnectedUser(user1 : string, user2 : string, group : Group){ console.log("disConnectedUser => user1", user1); console.log("disConnectedUser => user2", user2); + console.log("group", group); } } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 795c0e8e..099e871b 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,19 +1,25 @@ import {MessageUserPosition} from "./Websocket/MessageUserPosition"; import { World } from "./World"; import { UserInterface } from "./UserInterface"; +import {uuid} from "uuidv4"; export class Group { static readonly MAX_PER_GROUP = 4; + private id: string; private users: UserInterface[]; - private connectCallback: (user1: string, user2: string) => void; - private disconnectCallback: (user1: string, user2: string) => void; + private connectCallback: (user1: string, user2: string, group: Group) => void; + private disconnectCallback: (user1: string, user2: string, group: Group) => void; - constructor(users: UserInterface[], connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) { + constructor(users: UserInterface[], + connectCallback: (user1: string, user2: string, group: Group) => void, + disconnectCallback: (user1: string, user2: string, group: Group) => void + ) { this.users = []; this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; + this.id = uuid(); users.forEach((user: UserInterface) => { this.join(user); @@ -24,6 +30,10 @@ export class Group { return this.users; } + getId() : string{ + return this.id; + } + isFull(): boolean { return this.users.length >= Group.MAX_PER_GROUP; } @@ -31,9 +41,10 @@ export class Group { join(user: UserInterface): void { // Broadcast on the right event - this.users.forEach((groupUser: UserInterface) => { - this.connectCallback(user.id, groupUser.id); - }); + for(let i = 0; i < this.users.length; i++){ + let groupUser : UserInterface = this.users[i]; + this.connectCallback(user.id, groupUser.id, this); + } this.users.push(user); user.group = this; } diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index ff1f58ff..02391ec8 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -12,10 +12,10 @@ export class World { private users: Map; private groups: Group[]; - private connectCallback: (user1: string, user2: string) => void; - private disconnectCallback: (user1: string, user2: string) => void; + private connectCallback: (user1: string, user2: string, group: Group) => void; + private disconnectCallback: (user1: string, user2: string, group: Group) => void; - constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) + constructor(connectCallback: (user1: string, user2: string, group: Group) => void, disconnectCallback: (user1: string, user2: string, group: Group) => void) { this.users = new Map(); this.groups = []; @@ -48,7 +48,6 @@ export class World { // If the user is not part of a group: // should he join a group? let closestUser: UserInterface|null = this.searchClosestAvailableUser(user); - if (closestUser !== null) { // Is the closest user part of a group? if (typeof closestUser.group === 'undefined') { @@ -60,9 +59,8 @@ export class World { closestUser.group.join(user); } } - } - // TODO : vérifier qu'ils ne sont pas déja dans un groupe plein + // TODO : vérifier qu'ils ne sont pas déja dans un groupe plein } /** diff --git a/front/dist/index.html b/front/dist/index.html index a9f51d4d..c1b3eb52 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -12,8 +12,6 @@
-
-
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 9e2d2daa..58965b2e 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -1,77 +1,44 @@ .webrtc{ display: none; + position: absolute; + right: 0px; + height: 100%; + width: 300px; } .webrtc.active{ display: block; } -.webrtc, .activeCam{ - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - background: black; -} +.webrtc, .activeCam{} .activeCam video{ position: absolute; - width: 100%; - height: 100%; + width: 300px; + height: 20%; + top: 10px; + right: 10px; + margin: 5px; + background-color: white; +} +.activeCam video#myCamVideo{ + width: 200px; + height: 100px; } /*CSS size for 2 - 3 elements*/ -video:nth-child(1):nth-last-child(3), -video:nth-child(2):nth-last-child(2), -video:nth-child(3):nth-last-child(1), -video:nth-child(1):nth-last-child(2), -video:nth-child(2):nth-last-child(1){ - width: 50%; +.activeCam video:nth-child(1){ + /*this is for camera of user*/ + top: 75%; } -video:nth-child(1):nth-last-child(3), -video:nth-child(2):nth-last-child(2), -video:nth-child(3):nth-last-child(1){ - height: 50%; +.activeCam video:nth-child(2){ + top: 0%; } - -/*CSS position for 2 elements*/ -video:nth-child(1):nth-last-child(2){ - left: 0; +.activeCam video:nth-child(3){ + top: 25%; } -video:nth-child(2):nth-last-child(1){ - left: 50%; -} - -/*CSS position for 3 elements*/ -video:nth-child(1):nth-last-child(3){ - top: 0; - left: 0; -} -video:nth-child(2):nth-last-child(2){ - top: 0; - left: 50%; -} -video:nth-child(3):nth-last-child(1) { +.activeCam video:nth-child(4) { top: 50%; - left: 25%; } -.myCam{ - height: 200px; - width: 300px; - position: absolute; - right: 10px; - background: black; - border: none; - bottom: 20px; - max-height: 17%; - max-width: 17%; - opacity: 1; - display: block; - transition: opacity 1s; -} -.myCam video{ - width: 100%; - height: 100%; -} +/*btn animation*/ .btn-cam-action div{ cursor: pointer; position: absolute; @@ -79,14 +46,14 @@ video:nth-child(3):nth-last-child(1) { width: 64px; height: 64px; background: #666; - left: 6vw; box-shadow: 2px 2px 24px #444; border-radius: 48px; - transform: translateX(calc(-6vw - 96px)); + transform: translateY(12vw); transition-timing-function: ease-in-out; + bottom: 20px; } .webrtc:hover .btn-cam-action.active div{ - transform: translateX(0); + transform: translateY(0); } .btn-cam-action div:hover{ background: #407cf7; @@ -94,16 +61,16 @@ video:nth-child(3):nth-last-child(1) { transition: 280ms; } .btn-micro{ - bottom: 277px; transition: all .3s; + left: 168px; } .btn-video{ - bottom: 177px; transition: all .2s; + left: 84px; } .btn-call{ - bottom: 77px; transition: all .1s; + left: 0px; } .btn-cam-action div img{ height: 32px; @@ -112,6 +79,8 @@ video:nth-child(3):nth-last-child(1) { left: calc(48px - 35px); position: relative; } + +/*Phone Animation*/ .phone-open{ position: absolute; border-radius: 50%; @@ -136,7 +105,6 @@ video:nth-child(3):nth-last-child(1) { animation: none; cursor: pointer; } - @keyframes phone-move { 0% { left: calc(50% - 70px); diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index a7798720..7eaf4dfb 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -25,6 +25,7 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G userId: string; PlayerValue: string; userInputManager: UserInputManager; + previousMove: string; constructor( userId: string, @@ -90,7 +91,10 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G direction = PlayerAnimationNames.None; this.stop(); } - this.sharePosition(direction); + if(this.previousMove !== PlayerAnimationNames.None || direction !== PlayerAnimationNames.None){ + this.sharePosition(direction); + } + this.previousMove = direction; } private sharePosition(direction: string) { diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 3c94bbaf..60a2c5a9 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -4,14 +4,19 @@ let Peer = require('simple-peer'); export interface SimplePeerInterface { } - +enum PeerConnexionStatus{ + DISABLED = 1, + ACTIVATED = 2 +} export class SimplePeer { - Connexion: ConnexionInterface; - MediaManager: MediaManager; - WebRtcRoomId: string; - Users: Array; + private Connexion: ConnexionInterface; + private MediaManager: MediaManager; + private WebRtcRoomId: string; + private Users: Array; - PeerConnexionArray: Array = new Array(); + private PeerConnexionArray: Array = new Array(); + + private PeerConnexionStatus : number = PeerConnexionStatus.DISABLED; constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") { this.Connexion = Connexion; @@ -31,55 +36,71 @@ export class SimplePeer { }); //when button to call is clicked, start video - this.MediaManager.getElementActivePhone().addEventListener("click", () => { + /*this.MediaManager.getElementActivePhone().addEventListener("click", () => { this.startWebRtc(); this.disablePhone(); + });*/ + + return this.MediaManager.getCamera().then((stream: MediaStream) => { + this.MediaManager.activeVisio(); + this.MediaManager.localStream = stream; }); } /** * server has two person connected, start the meet */ - startWebRtc() { - this.MediaManager.activeVisio(); - return this.MediaManager.getCamera().then((stream: MediaStream) => { - this.MediaManager.localStream = stream; + private startWebRtc() { + //create pear connexion + this.createPeerConnexion(); - //create pear connexion - this.createPeerConnexion(); - - //receive signal by gemer - this.Connexion.receiveWebrtcSignal((message: string) => { - this.receiveWebrtcSignal(message); - }); - }).catch((err) => { - console.error(err); + //receive signal by gemer + this.Connexion.receiveWebrtcSignal((message: string) => { + this.receiveWebrtcSignal(message); }); + + // add media or new media for all peer connexion + this.Users.forEach((user: any) => { + this.addMedia(user.userId); + }); + + //change status to manage other user + this.PeerConnexionStatus = PeerConnexionStatus.ACTIVATED; } /** * * @param message */ - receiveWebrtcStart(message: string) { + private receiveWebrtcStart(message: string) { let data = JSON.parse(message); this.WebRtcRoomId = data.roomId; this.Users = data.clients; - //active button for player - this.activePhone(); + console.log("receiveWebrtcStart", this.Users); + + //start connexion + this.startWebRtc(); } - createPeerConnexion() { + private createPeerConnexion() { this.Users.forEach((user: any) => { - if(this.PeerConnexionArray[user.userId]){ + if (this.PeerConnexionArray[user.userId]) { return; } this.MediaManager.addActiveVideo(user.userId); - this.PeerConnexionArray[user.userId] = new Peer({initiator: user.initiator}); + console.info("createPeerConnexion => create peerConexion", user); + this.PeerConnexionArray[user.userId] = new Peer({ + initiator: user.initiator, + config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:global.stun.twilio.com:3478?transport=udp' }] } + }); + + //add lof info PeerConnexionArray + this.PeerConnexionArray[user.userId]._debug = console.info; this.PeerConnexionArray[user.userId].on('signal', (data: any) => { + console.info("createPeerConnexion => sendWebrtcSignal : "+user.userId, data); this.sendWebrtcSignal(data, user.userId); }); @@ -88,15 +109,15 @@ export class SimplePeer { }); this.PeerConnexionArray[user.userId].on('close', () => { + console.info("createPeerConnexion => close", user.userId); this.closeConnexion(user.userId); }); this.addMedia(user.userId); }); - } - closeConnexion(userId : string){ + private closeConnexion(userId : string){ // @ts-ignore this.PeerConnexionArray[userId] = null; this.MediaManager.removeActiveVideo(userId) @@ -107,7 +128,7 @@ export class SimplePeer { * @param userId * @param data */ - sendWebrtcSignal(data: any, userId : string) { + private sendWebrtcSignal(data: any, userId : string) { this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId); } @@ -115,8 +136,10 @@ export class SimplePeer { * * @param message */ - receiveWebrtcSignal(message: string) { + private receiveWebrtcSignal(message: string) { let data = JSON.parse(message); + console.log("receiveWebrtcSignal", data); + console.log("this.PeerConnexionArray[data.userId]", this.PeerConnexionArray[data.userId]); if(!this.PeerConnexionArray[data.userId]){ return; } @@ -128,7 +151,7 @@ export class SimplePeer { * @param userId * @param stream */ - stream(userId : any, stream: MediaStream) { + private stream(userId : any, stream: MediaStream) { this.MediaManager.remoteVideo[userId].srcObject = stream; } @@ -136,15 +159,19 @@ export class SimplePeer { * * @param userId */ - addMedia (userId : any) { - this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically + private addMedia (userId : any = null) { + if (!this.MediaManager.localStream || !this.PeerConnexionArray[userId]) { + return; + } + this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically + return; } - activePhone(){ + private activePhone(){ this.MediaManager.activePhoneOpen(); } - disablePhone(){ + private disablePhone(){ this.MediaManager.disablePhoneOpen(); } } \ No newline at end of file From c7f8f92e85e6c445b9c6e64d18d55643a6150cad Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 2 May 2020 00:31:44 +0200 Subject: [PATCH 2/9] Fix webrtc --- back/src/Controller/IoSocketController.ts | 2 +- front/src/WebRtc/SimplePeer.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 3a17242a..967c78a5 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -222,7 +222,7 @@ export class IoSocketController{ } arrayMap.forEach((value : any) => { let roomId = value[0]; - this.Io.in(roomId).emit('user-position', JSON.stringify(arrayMap)); + this.Io.in(roomId).emit(SockerIoEvent.USER_POSITION, JSON.stringify(arrayMap)); }); this.seTimeOutInProgress = setTimeout(() => { this.shareUsersPosition(); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 60a2c5a9..776ed470 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -97,7 +97,7 @@ export class SimplePeer { }); //add lof info PeerConnexionArray - this.PeerConnexionArray[user.userId]._debug = console.info; + //this.PeerConnexionArray[user.userId]._debug = console.info; this.PeerConnexionArray[user.userId].on('signal', (data: any) => { console.info("createPeerConnexion => sendWebrtcSignal : "+user.userId, data); @@ -138,8 +138,8 @@ export class SimplePeer { */ private receiveWebrtcSignal(message: string) { let data = JSON.parse(message); - console.log("receiveWebrtcSignal", data); - console.log("this.PeerConnexionArray[data.userId]", this.PeerConnexionArray[data.userId]); + console.log("receiveWebrtcSignal", data.userId); + console.log("receiveWebrtcSignal => data", data); if(!this.PeerConnexionArray[data.userId]){ return; } From fdb40ec3e21a503ec3c5fca198c404b1af57b2dc Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 2 May 2020 20:46:02 +0200 Subject: [PATCH 3/9] Fix webrtc multi --- back/src/Controller/IoSocketController.ts | 73 ++++++-- front/dist/resources/style/style.css | 1 - front/src/Connexion.ts | 14 +- front/src/Phaser/Login/LogincScene.ts | 2 + front/src/WebRtc/MediaManager.ts | 14 +- front/src/WebRtc/SimplePeer.ts | 208 ++++++++++++---------- 6 files changed, 195 insertions(+), 117 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 967c78a5..96fdb212 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -12,11 +12,13 @@ import {Group} from "_Model/Group"; enum SockerIoEvent { CONNECTION = "connection", - DISCONNECTION = "disconnect", + DISCONNECT = "disconnect", JOIN_ROOM = "join-room", USER_POSITION = "user-position", WEBRTC_SIGNAL = "webrtc-signal", + WEBRTC_OFFER = "webrtc-offer", WEBRTC_START = "webrtc-start", + WEBRTC_DISCONNECT = "webrtc-disconect", MESSAGE_ERROR = "message-error", } @@ -77,9 +79,7 @@ export class IoSocketController{ this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); //add function to refresh position user in real time. - let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); - rooms.refreshUserPosition = RefreshUserPositionFunction; - rooms.refreshUserPosition(rooms, this.Io); + this.refreshUserPosition(); socket.to(messageUserPosition.roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserPosition.toString()); }); @@ -97,30 +97,41 @@ export class IoSocketController{ this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); //refresh position of all user in all rooms in real time - let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); - if(!rooms.refreshUserPosition){ - rooms.refreshUserPosition = RefreshUserPositionFunction; - } - rooms.refreshUserPosition(rooms, this.Io); + this.refreshUserPosition(); }); socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message : string) => { let data : any = JSON.parse(message); - //send only at user - let clients: Array = Object.values(this.Io.sockets.sockets); - for(let i = 0; i < clients.length; i++){ - let client : ExSocketInterface = clients[i]; - if(client.userId !== data.receiverId){ - continue - } - client.emit(SockerIoEvent.WEBRTC_SIGNAL, message); - break; + let client = this.searchClientById(data.receiverId); + if(!client){ + console.error("client doesn't exist for ", data.receiverId); + return; } + return client.emit(SockerIoEvent.WEBRTC_SIGNAL, message); }); - socket.on(SockerIoEvent.DISCONNECTION, (reason : string) => { + socket.on(SockerIoEvent.WEBRTC_OFFER, (message : string) => { + let data : any = JSON.parse(message); + + //send only at user + let client = this.searchClientById(data.receiverId); + if(!client){ + console.error("client doesn't exist for ", data.receiverId); + return; + } + client.emit(SockerIoEvent.WEBRTC_OFFER, message); + }); + + socket.on(SockerIoEvent.DISCONNECT, () => { let Client = (socket as ExSocketInterface); + socket.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({ + userId: Client.userId + })); + + //refresh position of all user in all rooms in real time + this.refreshUserPosition(); + //leave group of user this.World.leave(Client); @@ -138,6 +149,21 @@ export class IoSocketController{ }); } + /** + * + * @param userId + */ + searchClientById(userId : string) : ExSocketInterface | null{ + let clients: Array = Object.values(this.Io.sockets.sockets); + for(let i = 0; i < clients.length; i++){ + let client : ExSocketInterface = clients[i]; + if(client.userId !== userId){ + continue + } + return client; + } + return null; + } /** * * @param socket @@ -181,6 +207,15 @@ export class IoSocketController{ socket.userId = message.userId; } + refreshUserPosition(){ + //refresh position of all user in all rooms in real time + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); + if(!rooms.refreshUserPosition){ + rooms.refreshUserPosition = RefreshUserPositionFunction; + } + rooms.refreshUserPosition(rooms, this.Io); + } + //Hydrate and manage error hydrateMessageReceive(message : string) : MessageUserPosition | Error{ try { diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 58965b2e..049c3d1d 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -16,7 +16,6 @@ top: 10px; right: 10px; margin: 5px; - background-color: white; } .activeCam video#myCamVideo{ width: 200px; diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index dc48833c..56531b67 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -7,9 +7,11 @@ import {API_URL, ROOM} from "./Enum/EnvironmentVariable"; enum EventMessage{ WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_START = "webrtc-start", + WEBRTC_JOIN_ROOM = "webrtc-join-room", JOIN_ROOM = "join-room", USER_POSITION = "user-position", - MESSAGE_ERROR = "message-error" + MESSAGE_ERROR = "message-error", + WEBRTC_DISCONNECT = "webrtc-disconect" } class Message { @@ -131,6 +133,8 @@ export interface ConnexionInterface { receiveWebrtcSignal(callBack: Function): void; receiveWebrtcStart(callBack: Function): void; + + disconnectMessage(callBack: Function): void; } export class Connexion implements ConnexionInterface { @@ -227,7 +231,7 @@ export class Connexion implements ConnexionInterface { } sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) { - this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({ + return this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({ userId: userId ? userId : this.userId, receiverId: receiverId ? receiverId : this.userId, roomId: roomId, @@ -240,7 +244,7 @@ export class Connexion implements ConnexionInterface { } receiveWebrtcSignal(callback: Function) { - this.socket.on(EventMessage.WEBRTC_SIGNAL, callback); + return this.socket.on(EventMessage.WEBRTC_SIGNAL, callback); } errorMessage(): void { @@ -248,4 +252,8 @@ export class Connexion implements ConnexionInterface { console.error(EventMessage.MESSAGE_ERROR, message); }) } + + disconnectMessage(callback: Function): void { + this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback); + } } \ No newline at end of file diff --git a/front/src/Phaser/Login/LogincScene.ts b/front/src/Phaser/Login/LogincScene.ts index 15ead519..c9c22f0f 100644 --- a/front/src/Phaser/Login/LogincScene.ts +++ b/front/src/Phaser/Login/LogincScene.ts @@ -3,6 +3,8 @@ import {TextField} from "../Components/TextField"; import {TextInput} from "../Components/TextInput"; import {ClickButton} from "../Components/ClickButton"; import {GameSceneName} from "../Game/GameScene"; +import {SimplePeer} from "../../WebRtc/SimplePeer"; +import {Connexion} from "../../Connexion"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 5b8d0076..4a875af2 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -6,7 +6,7 @@ export class MediaManager { cinema: any = null; microphoneClose: any = null; microphone: any = null; - constraintsMedia = {audio: false, video: true}; + constraintsMedia = {audio: true, video: true}; getCameraPromise : Promise = null; constructor() { @@ -37,9 +37,6 @@ export class MediaManager { this.disabledCamera(); //update tracking }); - - this.enabledCamera(); - this.enabledMicrophone(); } activeVisio(){ @@ -127,6 +124,15 @@ export class MediaManager { this.remoteVideo[(userId as any)] = document.getElementById(userId); } + /** + * + * @param userId + * @param stream + */ + addStreamRemoteVideo(userId : string, stream : MediaStream){ + this.remoteVideo[(userId as any)].srcObject = stream; + } + /** * * @param userId diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 776ed470..0e430326 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -4,67 +4,52 @@ let Peer = require('simple-peer'); export interface SimplePeerInterface { } -enum PeerConnexionStatus{ - DISABLED = 1, - ACTIVATED = 2 -} + export class SimplePeer { private Connexion: ConnexionInterface; - private MediaManager: MediaManager; private WebRtcRoomId: string; private Users: Array; - private PeerConnexionArray: Array = new Array(); + private MediaManager: MediaManager; - private PeerConnexionStatus : number = PeerConnexionStatus.DISABLED; + private PeerConnexionArray: Array = new Array(); constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") { this.Connexion = Connexion; this.WebRtcRoomId = WebRtcRoomId; this.MediaManager = new MediaManager(); + this.PeerConnexionArray = new Array(); + this.initialise(); } /** * permit to listen when user could start visio */ - private initialise(){ - - //receive message start - this.Connexion.receiveWebrtcStart((message: string) => { - this.receiveWebrtcStart(message); - }); - - //when button to call is clicked, start video - /*this.MediaManager.getElementActivePhone().addEventListener("click", () => { - this.startWebRtc(); - this.disablePhone(); - });*/ - - return this.MediaManager.getCamera().then((stream: MediaStream) => { - this.MediaManager.activeVisio(); - this.MediaManager.localStream = stream; - }); - } - /** - * server has two person connected, start the meet - */ - private startWebRtc() { - //create pear connexion - this.createPeerConnexion(); + private initialise() { //receive signal by gemer this.Connexion.receiveWebrtcSignal((message: string) => { this.receiveWebrtcSignal(message); }); - // add media or new media for all peer connexion - this.Users.forEach((user: any) => { - this.addMedia(user.userId); + this.MediaManager.activeVisio(); + this.MediaManager.getCamera().then(() => { + + //receive message start + this.Connexion.receiveWebrtcStart((message: string) => { + this.receiveWebrtcStart(message); + }); + + }).catch((err) => { + console.error("err", err); }); - //change status to manage other user - this.PeerConnexionStatus = PeerConnexionStatus.ACTIVATED; + //receive signal by gemer + this.Connexion.disconnectMessage((message: string) => { + let data = JSON.parse(message); + this.closeConnexion(data.userId); + }); } /** @@ -76,51 +61,92 @@ export class SimplePeer { this.WebRtcRoomId = data.roomId; this.Users = data.clients; - console.log("receiveWebrtcStart", this.Users); - //start connexion this.startWebRtc(); } - - private createPeerConnexion() { + /** + * server has two person connected, start the meet + */ + private startWebRtc() { this.Users.forEach((user: any) => { - if (this.PeerConnexionArray[user.userId]) { + //if it's not an initiator, peer connexion will be created when gamer will receive offer signal + if(!user.initiator){ return; } - this.MediaManager.addActiveVideo(user.userId); - - console.info("createPeerConnexion => create peerConexion", user); - this.PeerConnexionArray[user.userId] = new Peer({ - initiator: user.initiator, - config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:global.stun.twilio.com:3478?transport=udp' }] } - }); - - //add lof info PeerConnexionArray - //this.PeerConnexionArray[user.userId]._debug = console.info; - - this.PeerConnexionArray[user.userId].on('signal', (data: any) => { - console.info("createPeerConnexion => sendWebrtcSignal : "+user.userId, data); - this.sendWebrtcSignal(data, user.userId); - }); - - this.PeerConnexionArray[user.userId].on('stream', (stream: MediaStream) => { - this.stream(user.userId, stream); - }); - - this.PeerConnexionArray[user.userId].on('close', () => { - console.info("createPeerConnexion => close", user.userId); - this.closeConnexion(user.userId); - }); - - this.addMedia(user.userId); + this.createPeerConnexion(user); }); } - private closeConnexion(userId : string){ - // @ts-ignore - this.PeerConnexionArray[userId] = null; - this.MediaManager.removeActiveVideo(userId) + /** + * create peer connexion to bind users + */ + private createPeerConnexion(user : any) { + if(this.PeerConnexionArray[user.userId]) { + return; + } + + this.MediaManager.removeActiveVideo(user.userId); + this.MediaManager.addActiveVideo(user.userId); + + this.PeerConnexionArray[user.userId] = new Peer({ + initiator: user.initiator ? user.initiator : false, + reconnectTimer: 10000, + config: { + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + }, + { + urls: 'turn:numb.viagenie.ca', + username: 'g.parant@thecodingmachine.com', + credential: 'Muzuvo$6' + }, + ] + }, + }); + + //start listen signal for the peer connexion + this.PeerConnexionArray[user.userId].on('signal', (data: any) => { + this.sendWebrtcSignal(data, user.userId); + }); + + this.PeerConnexionArray[user.userId].on('stream', (stream: MediaStream) => { + this.stream(user.userId, stream); + }); + + this.PeerConnexionArray[user.userId].on('track', (track: MediaStreamTrack, stream: MediaStream) => { + this.stream(user.userId, stream); + }); + + this.PeerConnexionArray[user.userId].on('close', () => { + this.closeConnexion(user.userId); + }); + + this.PeerConnexionArray[user.userId].on('error', (err: any) => { + console.error(`error => ${user.userId} => ${err.code}`, err); + }); + + this.PeerConnexionArray[user.userId].on('connect', () => { + console.info(`connect => ${user.userId}`); + }); + + this.addMedia(user.userId); + } + + private closeConnexion(userId : string) { + try { + this.MediaManager.removeActiveVideo(userId); + if (!this.PeerConnexionArray[(userId as any)]) { + return; + } + // @ts-ignore + this.PeerConnexionArray[(userId as any)].destroy(); + this.PeerConnexionArray[(userId as any)] = null; + delete this.PeerConnexionArray[(userId as any)]; + } catch (err) { + console.error("closeConnexion", err) + } } /** @@ -129,7 +155,11 @@ export class SimplePeer { * @param data */ private sendWebrtcSignal(data: any, userId : string) { - this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId); + try { + this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId); + }catch (e) { + console.error(`sendWebrtcSignal => ${userId}`, e); + } } /** @@ -138,12 +168,15 @@ export class SimplePeer { */ private receiveWebrtcSignal(message: string) { let data = JSON.parse(message); - console.log("receiveWebrtcSignal", data.userId); - console.log("receiveWebrtcSignal => data", data); - if(!this.PeerConnexionArray[data.userId]){ - return; + try { + //if offer type, create peer connexion + if(data.signal.type === "offer"){ + this.createPeerConnexion(data); + } + this.PeerConnexionArray[data.userId].signal(data.signal); + } catch (e) { + console.error(`receiveWebrtcSignal => ${data.userId}`, e); } - this.PeerConnexionArray[data.userId].signal(data.signal); } /** @@ -152,7 +185,7 @@ export class SimplePeer { * @param stream */ private stream(userId : any, stream: MediaStream) { - this.MediaManager.remoteVideo[userId].srcObject = stream; + this.MediaManager.addStreamRemoteVideo(userId, stream); } /** @@ -160,18 +193,13 @@ export class SimplePeer { * @param userId */ private addMedia (userId : any = null) { - if (!this.MediaManager.localStream || !this.PeerConnexionArray[userId]) { - return; + try { + let transceiver : any = null; + this.MediaManager.localStream.getTracks().forEach( + transceiver = (track: MediaStreamTrack) => this.PeerConnexionArray[userId].addTrack(track, this.MediaManager.localStream) + ) + }catch (e) { + console.error(`addMedia => addMedia => ${userId}`, e); } - this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically - return; - } - - private activePhone(){ - this.MediaManager.activePhoneOpen(); - } - - private disablePhone(){ - this.MediaManager.disablePhoneOpen(); } } \ No newline at end of file From bae03fb1cf5eec7991be993a8a09093acb323d05 Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 2 May 2020 23:48:33 +0200 Subject: [PATCH 4/9] Fix CI --- back/src/Model/Group.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 39128c96..c7971a4b 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -116,7 +116,7 @@ export class Group { // Broadcast on the right event this.users.forEach((groupUser: UserInterface) => { - this.disconnectCallback(user.id, groupUser.id); + this.disconnectCallback(user.id, groupUser.id, this); }); } From 85ab0a231fe637496ed7ba2f006c6031029ad1ce Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 3 May 2020 14:29:45 +0200 Subject: [PATCH 5/9] Fix style cam --- front/dist/resources/style/style.css | 15 +++++++++++---- front/src/WebRtc/MediaManager.ts | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 049c3d1d..4bcb70bc 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -1,3 +1,7 @@ +video{ + -webkit-transform: scaleX(-1); + transform: scaleX(-1); +} .webrtc{ display: none; position: absolute; @@ -11,15 +15,18 @@ .webrtc, .activeCam{} .activeCam video{ position: absolute; - width: 300px; - height: 20%; + height: 25%; top: 10px; - right: 10px; margin: 5px; + right: -100px; + transition: all 0.2s ease; +} +.activeCam:hover video{ + right: 10px; } .activeCam video#myCamVideo{ width: 200px; - height: 100px; + height: 113px; } /*CSS size for 2 - 3 elements*/ diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 4a875af2..a0b6c0b8 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,3 +1,8 @@ +const videoConstraint: {width : any, height: any, facingMode : string} = { + width: { ideal: 1280 }, + height: { ideal: 720 }, + facingMode: "user" +}; export class MediaManager { localStream: MediaStream; remoteVideo: Array = new Array(); @@ -6,7 +11,10 @@ export class MediaManager { cinema: any = null; microphoneClose: any = null; microphone: any = null; - constraintsMedia = {audio: true, video: true}; + constraintsMedia : {audio : any, video : any} = { + audio: true, + video: videoConstraint + }; getCameraPromise : Promise = null; constructor() { @@ -47,7 +55,7 @@ export class MediaManager { enabledCamera() { this.cinemaClose.style.display = "none"; this.cinema.style.display = "block"; - this.constraintsMedia.video = true; + this.constraintsMedia.video = videoConstraint; this.localStream = null; this.myCamVideo.srcObject = null; } @@ -106,6 +114,13 @@ export class MediaManager { .then((stream: MediaStream) => { this.localStream = stream; this.myCamVideo.srcObject = this.localStream; + + //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*/ + return stream; }).catch((err) => { console.error(err); From 58565a7f4581dd94298c1ce83d8c9071279726d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 3 May 2020 16:08:04 +0200 Subject: [PATCH 6/9] Refactoring events to connect/disconnect a user to a group --- back/src/Model/Group.ts | 16 ++++------- back/src/Model/World.ts | 9 ++++-- back/tests/WorldTest.ts | 61 +++++------------------------------------ 3 files changed, 19 insertions(+), 67 deletions(-) diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index caf9b926..eacd2d03 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,4 +1,4 @@ -import { World } from "./World"; +import { World, ConnectCallback, DisconnectCallback } from "./World"; import { UserInterface } from "./UserInterface"; import {PositionInterface} from "_Model/PositionInterface"; @@ -6,11 +6,11 @@ export class Group { static readonly MAX_PER_GROUP = 4; private users: UserInterface[]; - private connectCallback: (user1: string, user2: string) => void; - private disconnectCallback: (user1: string, user2: string) => void; + private connectCallback: ConnectCallback; + private disconnectCallback: DisconnectCallback; - constructor(users: UserInterface[], connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) { + constructor(users: UserInterface[], connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) { this.users = []; this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; @@ -54,9 +54,7 @@ export class Group { join(user: UserInterface): void { // Broadcast on the right event - this.users.forEach((groupUser: UserInterface) => { - this.connectCallback(user.id, groupUser.id); - }); + this.connectCallback(user.id, this); this.users.push(user); user.group = this; } @@ -105,9 +103,7 @@ export class Group { user.group = undefined; // Broadcast on the right event - this.users.forEach((groupUser: UserInterface) => { - this.disconnectCallback(user.id, groupUser.id); - }); + this.disconnectCallback(user.id, this); } /** diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index 795cc8be..b04b34ee 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -6,6 +6,9 @@ import {UserInterface} from "./UserInterface"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; import {PositionInterface} from "_Model/PositionInterface"; +export type ConnectCallback = (user: string, group: Group) => void; +export type DisconnectCallback = (user: string, group: Group) => void; + export class World { static readonly MIN_DISTANCE = 160; @@ -13,10 +16,10 @@ export class World { private users: Map; private groups: Group[]; - private connectCallback: (user1: string, user2: string) => void; - private disconnectCallback: (user1: string, user2: string) => void; + private connectCallback: ConnectCallback; + private disconnectCallback: DisconnectCallback; - constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) + constructor(connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) { this.users = new Map(); this.groups = []; diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts index 1d499727..7fbd88de 100644 --- a/back/tests/WorldTest.ts +++ b/back/tests/WorldTest.ts @@ -1,6 +1,6 @@ import "jasmine"; import {Message} from "../src/Model/Websocket/Message"; -import {World} from "../src/Model/World"; +import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World"; import {MessageUserPosition, Point} from "../src/Model/Websocket/MessageUserPosition"; import { Group } from "../src/Model/Group"; import {Distance} from "../src/Model//Distance"; @@ -8,10 +8,10 @@ import {Distance} from "../src/Model//Distance"; describe("World", () => { it("should connect user1 and user2", () => { let connectCalled: boolean = false; - let connect = (user1: string, user2: string): void => { + let connect = (user: string, group: Group): void => { connectCalled = true; } - let disconnect = (user1: string, user2: string): void => { + let disconnect = (user: string, group: Group): void => { } @@ -56,10 +56,10 @@ describe("World", () => { it("should connect 3 users", () => { let connectCalled: boolean = false; - let connect = (user1: string, user2: string): void => { + let connect = (user: string, group: Group): void => { connectCalled = true; } - let disconnect = (user1: string, user2: string): void => { + let disconnect = (user: string, group: Group): void => { } @@ -101,10 +101,10 @@ describe("World", () => { it("should disconnect user1 and user2", () => { let connectCalled: boolean = false; let disconnectCalled: boolean = false; - let connect = (user1: string, user2: string): void => { + let connect = (user: string, group: Group): void => { connectCalled = true; } - let disconnect = (user1: string, user2: string): void => { + let disconnect = (user: string, group: Group): void => { disconnectCalled = true; } @@ -142,51 +142,4 @@ describe("World", () => { expect(disconnectCalled).toBe(false); }); - /** - it('Should return the distances between all users', () => { - let connectCalled: boolean = false; - let connect = (user1: string, user2: string): void => { - connectCalled = true; - } - let disconnect = (user1: string, user2: string): void => { - - } - - let world = new World(connect, disconnect); - let user1 = new MessageUserPosition({ - userId: "foo", - roomId: 1, - position: new Point(100, 100) - }); - - world.join(user1); - - let user2 = new MessageUserPosition({ - userId: "bar", - roomId: 1, - position: new Point(500, 100) - }); - world.join(user2); - - let user3 = new MessageUserPosition({ - userId: "baz", - roomId: 1, - position: new Point(101, 100) - }); - - let user4 = new MessageUserPosition({ - userId: "buz", - roomId: 1, - position: new Point(105, 100) - }) - - let group = new Group([user1, user2, user3, user4]); - - let distances = world.getDistancesBetweenGroupUsers(group) - - console.log(distances); - - //expect(distances).toBe([]); - }) - **/ }) From 372f938bbbd4f70421a1471c2485c2f582fccd87 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 3 May 2020 16:28:18 +0200 Subject: [PATCH 7/9] Connect and Disconnect event. --- back/src/Controller/IoSocketController.ts | 122 +++++++++++----------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 96fdb212..e57f550e 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -22,14 +22,15 @@ enum SockerIoEvent { MESSAGE_ERROR = "message-error", } -export class IoSocketController{ +export class IoSocketController { Io: socketIO.Server; World: World; - constructor(server : http.Server) { + + constructor(server: http.Server) { this.Io = socketIO(server); // Authentication with token. it will be decoded and stored in the socket. - this.Io.use( (socket: Socket, next) => { + this.Io.use((socket: Socket, next) => { if (!socket.handshake.query || !socket.handshake.query.token) { return next(new Error('Authentication error')); } @@ -46,15 +47,15 @@ export class IoSocketController{ this.shareUsersPosition(); //don't send only function because the context will be not this - this.World = new World((user1 : string, user2 : string, group: Group) => { + this.World = new World((user1: string, user2: string, group: Group) => { this.connectedUser(user1, user2, group); - }, (user1 : string, user2 : string, group: Group) => { + }, (user1: string, user2: string, group: Group) => { this.disConnectedUser(user1, user2, group); }); } ioConnection() { - this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => { + this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => { /*join-rom event permit to join one room. message : userId : user identification @@ -63,9 +64,9 @@ export class IoSocketController{ x: user x position on map y: user y position on map */ - socket.on(SockerIoEvent.JOIN_ROOM, (message : string) => { + socket.on(SockerIoEvent.JOIN_ROOM, (message: string) => { let messageUserPosition = this.hydrateMessageReceive(message); - if(messageUserPosition instanceof Error){ + if (messageUserPosition instanceof Error) { return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message})) } @@ -84,7 +85,7 @@ export class IoSocketController{ socket.to(messageUserPosition.roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserPosition.toString()); }); - socket.on(SockerIoEvent.USER_POSITION, (message : string) => { + socket.on(SockerIoEvent.USER_POSITION, (message: string) => { let messageUserPosition = this.hydrateMessageReceive(message); if (messageUserPosition instanceof Error) { return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message})); @@ -100,34 +101,32 @@ export class IoSocketController{ this.refreshUserPosition(); }); - socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message : string) => { - let data : any = JSON.parse(message); + socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message: string) => { + let data: any = JSON.parse(message); //send only at user let client = this.searchClientById(data.receiverId); - if(!client){ + if (!client) { console.error("client doesn't exist for ", data.receiverId); return; } - return client.emit(SockerIoEvent.WEBRTC_SIGNAL, message); + return client.emit(SockerIoEvent.WEBRTC_SIGNAL, message); }); - socket.on(SockerIoEvent.WEBRTC_OFFER, (message : string) => { - let data : any = JSON.parse(message); + socket.on(SockerIoEvent.WEBRTC_OFFER, (message: string) => { + let data: any = JSON.parse(message); //send only at user let client = this.searchClientById(data.receiverId); - if(!client){ + if (!client) { console.error("client doesn't exist for ", data.receiverId); return; } - client.emit(SockerIoEvent.WEBRTC_OFFER, message); + client.emit(SockerIoEvent.WEBRTC_OFFER, message); }); socket.on(SockerIoEvent.DISCONNECT, () => { let Client = (socket as ExSocketInterface); - socket.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({ - userId: Client.userId - })); + this.sendDisconnectedEvent(Client); //refresh position of all user in all rooms in real time this.refreshUserPosition(); @@ -153,24 +152,35 @@ export class IoSocketController{ * * @param userId */ - searchClientById(userId : string) : ExSocketInterface | null{ + searchClientById(userId: string): ExSocketInterface | null { let clients: Array = Object.values(this.Io.sockets.sockets); - for(let i = 0; i < clients.length; i++){ - let client : ExSocketInterface = clients[i]; - if(client.userId !== userId){ + for (let i = 0; i < clients.length; i++) { + let client: ExSocketInterface = clients[i]; + if (client.userId !== userId) { continue } return client; } return null; } + + /** + * + * @param Client: ExSocketInterface + */ + sendDisconnectedEvent(Client: ExSocketInterface) { + Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({ + userId: Client.userId + })); + } + /** * * @param socket * @param roomId */ - joinWebRtcRoom(socket : ExSocketInterface, roomId : string) { - if(socket.webRtcRoomId === roomId){ + joinWebRtcRoom(socket: ExSocketInterface, roomId: string) { + if (socket.webRtcRoomId === roomId) { return; } socket.join(roomId); @@ -201,36 +211,36 @@ export class IoSocketController{ } //permit to save user position in socket - saveUserInformation(socket : ExSocketInterface, message : MessageUserPosition){ + saveUserInformation(socket: ExSocketInterface, message: MessageUserPosition) { socket.position = message.position; socket.roomId = message.roomId; socket.userId = message.userId; } - refreshUserPosition(){ + refreshUserPosition() { //refresh position of all user in all rooms in real time let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); - if(!rooms.refreshUserPosition){ + if (!rooms.refreshUserPosition) { rooms.refreshUserPosition = RefreshUserPositionFunction; } rooms.refreshUserPosition(rooms, this.Io); } //Hydrate and manage error - hydrateMessageReceive(message : string) : MessageUserPosition | Error{ + hydrateMessageReceive(message: string): MessageUserPosition | Error { try { return new MessageUserPosition(JSON.parse(message)); - }catch (err) { + } catch (err) { //TODO log error return new Error(err); } } /** permit to share user position - ** users position will send in event 'user-position' - ** The data sent is an array with information for each user : - [ - { + ** users position will send in event 'user-position' + ** The data sent is an array with information for each user : + [ + { userId: , roomId: , position: { @@ -239,23 +249,24 @@ export class IoSocketController{ direction: } }, - ... - ] + ... + ] **/ - seTimeOutInProgress : any = null; - shareUsersPosition(){ - if(this.seTimeOutInProgress){ + seTimeOutInProgress: any = null; + + shareUsersPosition() { + if (this.seTimeOutInProgress) { clearTimeout(this.seTimeOutInProgress); } //send for each room, all data of position user let arrayMap = (this.Io.sockets.adapter.rooms as ExtRooms).userPositionMapByRoom; - if(!arrayMap){ + if (!arrayMap) { this.seTimeOutInProgress = setTimeout(() => { this.shareUsersPosition(); }, 10); return; } - arrayMap.forEach((value : any) => { + arrayMap.forEach((value: any) => { let roomId = value[0]; this.Io.in(roomId).emit(SockerIoEvent.USER_POSITION, JSON.stringify(arrayMap)); }); @@ -265,27 +276,20 @@ export class IoSocketController{ } //connected user - connectedUser(user1 : string, user2 : string, group : Group) { - if(!group){ + connectedUser(userId: string, group: Group) { + let Client = this.searchClientById(userId); + if (!Client) { return; } - /* TODO manager room and group user to enter and leave */ - let clients: Array = Object.values(this.Io.sockets.sockets); - let User1 = clients.find((user: ExSocketInterface) => user.userId === user1); - let User2 = clients.find((user: ExSocketInterface) => user.userId === user2); - - if (User1) { - this.joinWebRtcRoom(User1, group.getId()); - } - if (User2) { - this.joinWebRtcRoom(User2, group.getId()); - } + this.joinWebRtcRoom(Client, group.getId()); } //connected user - disConnectedUser(user1 : string, user2 : string, group : Group){ - console.log("disConnectedUser => user1", user1); - console.log("disConnectedUser => user2", user2); - console.log("group", group); + disConnectedUser(userId: string, group: Group) { + let Client = this.searchClientById(userId); + if (!Client) { + return; + } + this.sendDisconnectedEvent(Client) } } From c48073b90882e3fd9bb8e31e5dfcc61584e1ce35 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 3 May 2020 16:30:22 +0200 Subject: [PATCH 8/9] Fix update callback --- back/src/Controller/IoSocketController.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index e57f550e..560deed7 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -47,10 +47,10 @@ export class IoSocketController { this.shareUsersPosition(); //don't send only function because the context will be not this - this.World = new World((user1: string, user2: string, group: Group) => { - this.connectedUser(user1, user2, group); - }, (user1: string, user2: string, group: Group) => { - this.disConnectedUser(user1, user2, group); + this.World = new World((user1: string, group: Group) => { + this.connectedUser(user1, group); + }, (user1: string, group: Group) => { + this.disConnectedUser(user1, group); }); } From fb255140e517cc1aa59bb449bea2e3c3a2794314 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 3 May 2020 17:19:42 +0200 Subject: [PATCH 9/9] Add feature to mute or switch off cam --- front/dist/index.html | 11 +++--- front/dist/resources/style/style.css | 52 ++++------------------------ front/src/Phaser/Game/GameScene.ts | 4 +-- front/src/WebRtc/MediaManager.ts | 33 ++++++++++-------- front/src/WebRtc/SimplePeer.ts | 12 +++++-- 5 files changed, 41 insertions(+), 71 deletions(-) diff --git a/front/dist/index.html b/front/dist/index.html index c1b3eb52..85aced1e 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -12,9 +12,9 @@
- +
-
+
@@ -23,13 +23,10 @@
-
+
-
- -
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 4bcb70bc..58c32bb7 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -21,7 +21,7 @@ video{ right: -100px; transition: all 0.2s ease; } -.activeCam:hover video{ +.webrtc:hover .activeCam video{ right: 10px; } .activeCam video#myCamVideo{ @@ -58,7 +58,7 @@ video{ transition-timing-function: ease-in-out; bottom: 20px; } -.webrtc:hover .btn-cam-action.active div{ +.webrtc:hover .btn-cam-action div{ transform: translateY(0); } .btn-cam-action div:hover{ @@ -68,60 +68,20 @@ video{ } .btn-micro{ transition: all .3s; - left: 168px; + right: 10px; } .btn-video{ transition: all .2s; - left: 84px; + right: 114px; } -.btn-call{ +/*.btn-call{ transition: all .1s; left: 0px; -} +}*/ .btn-cam-action div img{ height: 32px; width: 40px; top: calc(48px - 32px); left: calc(48px - 35px); position: relative; -} - -/*Phone Animation*/ -.phone-open{ - position: absolute; - border-radius: 50%; - width: 50px; - height: 50px; - left: calc(50% - 70px); - padding: 20px; - bottom: 20px; - box-shadow: 2px 2px 24px #444; - background-color: green; - opacity: 0; - transition: all .4s ease-in-out; -} -.phone-open.active{ - opacity: 1; - animation-name: phone-move; - animation-duration: 0.4s; - animation-iteration-count: infinite; - animation-timing-function: linear; -} -.phone-open:hover{ - animation: none; - cursor: pointer; -} -@keyframes phone-move { - 0% { - left: calc(50% - 70px); - bottom: 20px; - } - 25% { - left: calc(50% - 65px); - bottom: 15px; - } - 25% { - left: calc(50% - 75px); - bottom: 25px; - } } \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 8764e959..9666ff9c 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -26,8 +26,8 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ Layers : Array; Objects : Array; map: ITiledMap; - startX = (window.innerWidth / 2) / RESOLUTION; - startY = (window.innerHeight / 2) / RESOLUTION; + startX = 704;// 22 case + startY = 32; // 1 case constructor() { diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index a0b6c0b8..5ebbc0c7 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -16,11 +16,15 @@ export class MediaManager { video: videoConstraint }; getCameraPromise : Promise = null; + updatedLocalStreamCallBack : Function; + + constructor(updatedLocalStreamCallBack : Function) { + this.updatedLocalStreamCallBack = updatedLocalStreamCallBack; - constructor() { this.myCamVideo = document.getElementById('myCamVideo'); - this.microphoneClose = document.getElementById('microphone-close'); + this.microphoneClose = document.getElementById('microphone-close'); + this.microphoneClose.style.display = "none"; this.microphoneClose.addEventListener('click', (e: any) => { e.preventDefault(); this.enabledMicrophone(); @@ -34,6 +38,7 @@ export class MediaManager { }); this.cinemaClose = document.getElementById('cinema-close'); + this.cinemaClose.style.display = "none"; this.cinemaClose.addEventListener('click', (e: any) => { e.preventDefault(); this.enabledCamera(); @@ -58,6 +63,9 @@ export class MediaManager { this.constraintsMedia.video = videoConstraint; this.localStream = null; this.myCamVideo.srcObject = null; + this.getCamera().then((stream) => { + this.updatedLocalStreamCallBack(stream); + }); } disabledCamera() { @@ -75,12 +83,18 @@ export class MediaManager { } this.localStream = null; this.myCamVideo.srcObject = null; + this.getCamera().then((stream) => { + this.updatedLocalStreamCallBack(stream); + }); } enabledMicrophone() { this.microphoneClose.style.display = "none"; this.microphone.style.display = "block"; this.constraintsMedia.audio = true; + this.getCamera().then((stream) => { + this.updatedLocalStreamCallBack(stream); + }); } disabledMicrophone() { @@ -94,18 +108,9 @@ export class MediaManager { } }); } - } - - getElementActivePhone(){ - return document.getElementById('phone-open'); - } - - activePhoneOpen(){ - return this.getElementActivePhone().classList.add("active"); - } - - disablePhoneOpen(){ - return this.getElementActivePhone().classList.remove("active"); + this.getCamera().then((stream) => { + this.updatedLocalStreamCallBack(stream); + }); } //get camera diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 0e430326..3bff9580 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -17,7 +17,9 @@ export class SimplePeer { constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") { this.Connexion = Connexion; this.WebRtcRoomId = WebRtcRoomId; - this.MediaManager = new MediaManager(); + this.MediaManager = new MediaManager((stream : MediaStream) => { + this.updatedLocalStream(); + }); this.PeerConnexionArray = new Array(); this.initialise(); @@ -100,7 +102,7 @@ export class SimplePeer { { urls: 'turn:numb.viagenie.ca', username: 'g.parant@thecodingmachine.com', - credential: 'Muzuvo$6' + credential: 'itcugcOHxle9Acqi$' }, ] }, @@ -202,4 +204,10 @@ export class SimplePeer { console.error(`addMedia => addMedia => ${userId}`, e); } } + + updatedLocalStream(){ + this.Users.forEach((user) => { + this.addMedia(user.userId); + }) + } } \ No newline at end of file