From 5b62ac39fb346b49dfdfca4b27013721b74a41c0 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 19 Apr 2020 19:32:38 +0200 Subject: [PATCH 01/18] Create webrtc connexion between two player --- back/src/Controller/IoSocketController.ts | 30 +++ front/dist/index.html | 40 +++- front/dist/resources/logos/cinema-close.svg | 41 ++++ front/dist/resources/logos/cinema.svg | 24 +++ .../dist/resources/logos/microphone-close.svg | 27 +++ front/dist/resources/logos/microphone.svg | 21 ++ front/dist/resources/logos/phone.svg | 16 ++ front/dist/resources/style/style.css | 81 ++++++++ front/src/Phaser/Game/GameManager.ts | 3 + front/src/WebRtc/Index.ts | 183 ++++++++++++++++++ front/src/WebRtc/MediaManager.ts | 89 +++++++++ front/src/WebRtc/PeerConnexionManager.ts | 136 +++++++++++++ front/src/WebRtc/WebRtcEventManager.ts | 86 ++++++++ 13 files changed, 769 insertions(+), 8 deletions(-) create mode 100644 front/dist/resources/logos/cinema-close.svg create mode 100644 front/dist/resources/logos/cinema.svg create mode 100644 front/dist/resources/logos/microphone-close.svg create mode 100644 front/dist/resources/logos/microphone.svg create mode 100644 front/dist/resources/logos/phone.svg create mode 100644 front/dist/resources/style/style.css create mode 100644 front/src/WebRtc/Index.ts create mode 100644 front/src/WebRtc/MediaManager.ts create mode 100644 front/src/WebRtc/PeerConnexionManager.ts create mode 100644 front/src/WebRtc/WebRtcEventManager.ts diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 1fc114a4..88af801a 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -74,6 +74,36 @@ export class IoSocketController{ let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface) rooms.refreshUserPosition(rooms, this.Io); }); + + socket.on('webrtc-room', (message : string) => { + let data = JSON.parse(message); + socket.join(data.roomId); + (socket as ExSocketInterface).roomId = data.roomId; + + //if two persone in room share + if(this.Io.sockets.adapter.rooms[data.roomId].length < 2) { + return; + } + let clients : Array = Object.values(this.Io.sockets.sockets); + + //send start at one client to initialise offer webrtc + clients[0].emit('webrtc-start'); + }); + + socket.on('video-offer', (message : string) => { + let data : any = JSON.parse(message); + socket.to(data.roomId).emit('video-offer', message); + }); + + socket.on('video-answer', (message : string) => { + let data : any = JSON.parse(message); + socket.to(data.roomId).emit('video-answer', message); + }); + + socket.on('ice-candidate', (message : string) => { + let data : any = JSON.parse(message); + socket.to(data.roomId).emit('ice-candidate', message); + }); }); } diff --git a/front/dist/index.html b/front/dist/index.html index 61213be6..656c903b 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -1,11 +1,35 @@ - - - - - Document - - + + + + + + Document + + + +
+
+ +
+
+ +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ diff --git a/front/dist/resources/logos/cinema-close.svg b/front/dist/resources/logos/cinema-close.svg new file mode 100644 index 00000000..aa1d9b17 --- /dev/null +++ b/front/dist/resources/logos/cinema-close.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/cinema.svg b/front/dist/resources/logos/cinema.svg new file mode 100644 index 00000000..1167d09d --- /dev/null +++ b/front/dist/resources/logos/cinema.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/microphone-close.svg b/front/dist/resources/logos/microphone-close.svg new file mode 100644 index 00000000..16731829 --- /dev/null +++ b/front/dist/resources/logos/microphone-close.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/microphone.svg b/front/dist/resources/logos/microphone.svg new file mode 100644 index 00000000..ff5727ca --- /dev/null +++ b/front/dist/resources/logos/microphone.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/phone.svg b/front/dist/resources/logos/phone.svg new file mode 100644 index 00000000..ac8e595a --- /dev/null +++ b/front/dist/resources/logos/phone.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css new file mode 100644 index 00000000..4c27c584 --- /dev/null +++ b/front/dist/resources/style/style.css @@ -0,0 +1,81 @@ +.webrtc{ + display: none; +} +.webrtc.active{ + display: block; +} +.myCam{ + display: none; +} +.myCam.active{ + display: block; +} +.webrtc, .activeCam{ + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background: black; +} +.webrtc video{ + width: 100%; + height: 100%; +} +.myCam{ + height: 200px; + width: 300px; + position: absolute; + right: 10px; + background: black; + border: none; + bottom: 20px; + max-height: 17%; + max-width: 17%; + opacity: 1; + transition: opacity 1s; +} +.myCam video{ + width: 100%; + height: 100%; +} +.btn-cam-action div{ + cursor: pointer; + position: absolute; + border: solid 0px black; + width: 64px; + height: 64px; + background: #666; + left: 6vw; + box-shadow: 2px 2px 24px #444; + border-radius: 48px; + transform: translateX(calc(-6vw - 96px)); + transition-timing-function: ease-in-out; +} +.webrtc:hover .btn-cam-action.active div{ + transform: translateX(0); +} +.btn-cam-action div:hover{ + background: #407cf7; + box-shadow: 4px 4px 48px #666; + transition: 280ms; +} +.btn-micro{ + bottom: 277px; + transition: all .3s; +} +.btn-video{ + bottom: 177px; + transition: all .2s; +} +.btn-call{ + bottom: 77px; + transition: all .1s; +} +.btn-cam-action div img{ + height: 32px; + width: 40px; + top: calc(48px - 32px); + left: calc(48px - 35px); + position: relative; +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index d03a3152..fffca2e9 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,6 +1,7 @@ import {GameSceneInterface, GameScene} from "./GameScene"; import {ROOM} from "../../Enum/EnvironmentVariable" import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion"; +import {WebRtcEventManager} from "../../WebRtc/WebRtcEventManager"; export enum StatusGameManagerEnum { IN_PROGRESS = 1, @@ -28,6 +29,8 @@ export class GameManager implements GameManagerInterface { return ConnexionInstance.createConnexion().then(() => { this.configureGame(); /** TODO add loader in the page **/ + //initialise cam + new WebRtcEventManager(ConnexionInstance); }).catch((err) => { console.error(err); throw err; diff --git a/front/src/WebRtc/Index.ts b/front/src/WebRtc/Index.ts new file mode 100644 index 00000000..cc636bb8 --- /dev/null +++ b/front/src/WebRtc/Index.ts @@ -0,0 +1,183 @@ +/*import {ConnexionInterface} from "../Connexion"; + +const Peer = require('simple-peer'); + +let cinemaClose : any = null; +let cinema : any = null; +let microphoneClose : any = null; +let microphone : any = null; + +let localStream : MediaStream = null; +let remoteStream : MediaStream = null; +let remoteVideo : any = null; +let myCamVideo : any = null; + +let promiseGetCam : Promise = null; + +let peer : any = null; + +let Connexion : ConnexionInterface = null; + +let roomId = "test-wertc"; + +let gettingCamera : Promise = null; +let constraintsMedia = {audio: true, video: true}; + +function joinRoom(){ + Connexion.JoinRoomWebRtc(roomId); + Connexion.startRoomWebRtc(initialiseWebSocket) +} + +function initialiseWebSocket(message : any){ + console.log('initialiseWebSocket => message', message); + peer = new Peer({ + initiator: message.initiator + }); + + peer.on('signal', (data : any) => { + //send signal + //permit to send message and initialise peer connexion + console.log('signal sended', data); + Connexion.shareSignalWebRtc({ + roomId: roomId, + signal: data + }); + }); + + //permit to receive message and initialise peer connexion + Connexion.receiveSignalWebRtc((data : any) => { + let signal = JSON.parse(data); + console.log('receiveSignalWebRtc => signal', signal); + peer.signal(signal.signal); + }); + + peer.on('stream', (stream : MediaStream) => { + // got remote video stream, now let's show it in a video tag + console.log("peer => stream", stream); + + //set local stream in little cam + myCamVideo.srcObject = localStream; + + //set remote stream in remote video + remoteStream = stream; + remoteVideo.srcObject = stream; + }); + + peer.on('connect', () => { + console.log('CONNECT') + peer.send('whatever' + Math.random()) + }); + + peer.on('data', (data : any) => { + console.log('data: ' + data) + }); + + peer.on('close', (err : any) => console.error('close', err)); + peer.on('error', (err : any) => console.error('error', err)); + + + peer.on('track', (track : any, stream : any) => { + remoteStream = stream; + remoteVideo.srcObject = stream; + track.onended = (e : any) => remoteVideo.srcObject = remoteVideo.srcObject; // Chrome/Firefox bug + }); + + gettingCamera.then(() => { + addMedia(); + }); +} + +//get camera +function getCamera() { + gettingCamera = navigator.mediaDevices.getUserMedia(constraintsMedia) + .then((stream: MediaStream) => { + localStream = stream; + remoteVideo.srcObject = stream; + }).catch((err) => { + console.error(err); + localStream = null; + throw err; + }); + return gettingCamera; +} + +function addMedia () { + if(peer) { + peer.addStream(localStream) // <- add streams to peer dynamically + } +} + +function enabledCamera(){ + cinemaClose.style.display = "none"; + cinema.style.display = "block"; + constraintsMedia.video = true; +} + +function disabledCamera(){ + cinemaClose.style.display = "block"; + cinema.style.display = "none"; + constraintsMedia.video = false; +} + +function enabledMicrophone(){ + microphoneClose.style.display = "none"; + microphone.style.display = "block"; + constraintsMedia.audio = true; +} + +function disabledMicrophone(){ + microphoneClose.style.display = "block"; + microphone.style.display = "none"; + constraintsMedia.audio = false; +} + +function showWebRtc(){ + remoteVideo = document.getElementById('activeCamVideo'); + myCamVideo = document.getElementById('myCamVideo'); + + microphoneClose = document.getElementById('microphone-close'); + microphoneClose.addEventListener('click', (e : any) => { + e.preventDefault(); + enabledMicrophone(); + //update tracking + }); + + microphone = document.getElementById('microphone'); + microphone.addEventListener('click', (e : any) => { + e.preventDefault(); + disabledMicrophone(); + //update tracking + }); + + cinemaClose = document.getElementById('cinema-close'); + cinemaClose.addEventListener('click', (e : any) => { + e.preventDefault(); + enabledCamera(); + //update tracking + }); + cinema = document.getElementById('cinema'); + cinema.addEventListener('click', (e : any) => { + e.preventDefault(); + disabledCamera(); + //update tracking + }); + + enabledMicrophone(); + enabledCamera(); + + let webRtc = document.getElementById('webRtc'); + webRtc.classList.add('active'); +} + +export const initialisation = (ConnexionInterface : ConnexionInterface) => { + Connexion = ConnexionInterface; + + //show camera + showWebRtc(); + + //open the camera + getCamera(); + + //join room to create webrtc + joinRoom(); +};*/ \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts new file mode 100644 index 00000000..bab1c7c8 --- /dev/null +++ b/front/src/WebRtc/MediaManager.ts @@ -0,0 +1,89 @@ +export class MediaManager { + localStream: MediaStream; + remoteStream: MediaStream; + remoteVideo: any; + myCamVideo: any; + cinemaClose: any = null; + cinema: any = null; + microphoneClose: any = null; + microphone: any = null; + constraintsMedia = {audio: true, video: true}; + getCameraPromise : Promise = null; + + constructor() { + this.remoteVideo = document.getElementById('activeCamVideo'); + this.myCamVideo = document.getElementById('myCamVideo'); + + this.microphoneClose = document.getElementById('microphone-close'); + this.microphoneClose.addEventListener('click', (e: any) => { + e.preventDefault(); + this.enabledMicrophone(); + //update tracking + }); + + this.microphone = document.getElementById('microphone'); + this.microphone.addEventListener('click', (e: any) => { + e.preventDefault(); + this.disabledMicrophone(); + //update tracking + }); + + this.cinemaClose = document.getElementById('cinema-close'); + this.cinemaClose.addEventListener('click', (e: any) => { + e.preventDefault(); + this.enabledCamera(); + //update tracking + }); + this.cinema = document.getElementById('cinema'); + this.cinema.addEventListener('click', (e: any) => { + e.preventDefault(); + this.disabledCamera(); + //update tracking + }); + + this.enabledMicrophone(); + this.enabledCamera(); + + let webRtc = document.getElementById('webRtc'); + webRtc.classList.add('active'); + + this.getCamera(); + } + + enabledCamera() { + this.cinemaClose.style.display = "none"; + this.cinema.style.display = "block"; + this.constraintsMedia.video = true; + } + + disabledCamera() { + this.cinemaClose.style.display = "block"; + this.cinema.style.display = "none"; + this.constraintsMedia.video = false; + } + + enabledMicrophone() { + this.microphoneClose.style.display = "none"; + this.microphone.style.display = "block"; + this.constraintsMedia.audio = true; + } + + disabledMicrophone() { + this.microphoneClose.style.display = "block"; + this.microphone.style.display = "none"; + this.constraintsMedia.audio = false; + } + + //get camera + getCamera() { + this.getCameraPromise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) + .then((stream: MediaStream) => { + this.localStream = stream; + this.myCamVideo.srcObject = this.localStream; + }).catch((err) => { + console.error(err); + this.localStream = null; + throw err; + }); + } +} \ No newline at end of file diff --git a/front/src/WebRtc/PeerConnexionManager.ts b/front/src/WebRtc/PeerConnexionManager.ts new file mode 100644 index 00000000..e6b531a9 --- /dev/null +++ b/front/src/WebRtc/PeerConnexionManager.ts @@ -0,0 +1,136 @@ +import {WebRtcEventManager} from "./WebRtcEventManager"; +import {MediaManager} from "./MediaManager"; +const offerOptions = { + offerToReceiveAudio: 1, + offerToReceiveVideo: 1, + iceServers: [{url:'stun:stun.l.google.com:19302'}], +}; + +export class PeerConnexionManager { + + WebRtcEventManager: WebRtcEventManager; + MediaManager : MediaManager; + + peerConnection: RTCPeerConnection; + + constructor(WebRtcEventManager : WebRtcEventManager) { + this.WebRtcEventManager = WebRtcEventManager; + this.MediaManager = new MediaManager(); + } + + createPeerConnection(data: any = null): Promise { + this.peerConnection = new RTCPeerConnection(); + + //init all events peer connection + this.createEventPeerConnection(); + + this.MediaManager.getCameraPromise.then(() => { + this.MediaManager.localStream.getTracks().forEach( + (track : MediaStreamTrack) => this.peerConnection.addTrack(track, this.MediaManager.localStream) + ); + }); + + //if no data, create offer + if (!data || !data.message) { + return this.createOffer(); + } + + let description = new RTCSessionDescription(data.message); + return this.peerConnection.setRemoteDescription(description).catch((err) => { + console.error("createPeerConnection => setRemoteDescription", err); + throw err; + }) + } + + createOffer(): Promise { + console.log('pc1 createOffer start'); + // @ts-ignore + return this.peerConnection.createOffer(offerOptions).then((offer: RTCSessionDescriptionInit) => { + this.peerConnection.setLocalDescription(offer).then(() => { + let message = {message: this.peerConnection.localDescription}; + this.WebRtcEventManager.emitVideoOffer(message); + }).catch((err) => { + console.error("createOffer => setLocalDescription", err); + throw err; + }); + }).catch((err: Error) => { + console.error("createOffer => createOffer", err); + throw err; + }); + } + + createAnswer(): Promise { + return this.peerConnection.createAnswer().then((answer : RTCSessionDescriptionInit) => { + this.peerConnection.setLocalDescription(answer).then(() => { + //push video-answer + let messageSend = {message: this.peerConnection.localDescription}; + this.WebRtcEventManager.emitVideoAnswer(messageSend); + console.info("video-answer => send", messageSend); + }).catch((err) => { + console.error("eventVideoOffer => createAnswer => setLocalDescription", err); + throw err; + }) + }).catch((err) => { + console.error("eventVideoOffer => createAnswer", err); + throw err; + }) + } + + setRemoteDescription(data: any): Promise { + let description = new RTCSessionDescription(data.message); + return this.peerConnection.setRemoteDescription(description).catch((err) => { + console.error("PeerConnexionManager => setRemoteDescription", err); + throw err; + }) + } + + addIceCandidate(data: any): Promise { + return this.peerConnection.addIceCandidate(data.message) + .catch((err) => { + console.error("PeerConnexionManager => addIceCandidate", err); + throw err; + }) + } + + hangup() { + console.log('Ending call'); + if (this.peerConnection) { + this.peerConnection.close(); + } + this.peerConnection = null; + } + + createEventPeerConnection(){ + //define creator of offer + this.peerConnection.onicecandidate = (event: RTCPeerConnectionIceEvent) => { + let message = {message: event.candidate}; + if (!event.candidate) { + return; + } + this.WebRtcEventManager.emitIceCandidate(message); + }; + + this.peerConnection.ontrack = (e:RTCTrackEvent) => { + console.info('Event:track', e); + this.MediaManager.remoteVideo.srcObject = e.streams[0]; + this.MediaManager.myCamVideo.srcObject = e.streams[0]; + }; + + this.peerConnection.onnegotiationneeded = (e : Event) => { + console.info("Event:negotiationneeded => call()", e); + this.createOffer() + }; + this.peerConnection.oniceconnectionstatechange = (e) => { + console.info('ICE state change event: ', e); + }; + this.peerConnection.oniceconnectionstatechange = (e:Event) => { + console.info('oniceconnectionstatechange => iceConnectionState', this.peerConnection.iceConnectionState); + }; + this.peerConnection.onicegatheringstatechange = () => { + console.info('onicegatheringstatechange => iceConnectionState', this.peerConnection.iceConnectionState); + }; + this.peerConnection.onsignalingstatechange = () => { + console.info('onsignalingstatechange => iceConnectionState', this.peerConnection.iceConnectionState); + }; + } +} \ No newline at end of file diff --git a/front/src/WebRtc/WebRtcEventManager.ts b/front/src/WebRtc/WebRtcEventManager.ts new file mode 100644 index 00000000..19c46701 --- /dev/null +++ b/front/src/WebRtc/WebRtcEventManager.ts @@ -0,0 +1,86 @@ +import {ConnexionInterface} from "../Connexion"; +import {PeerConnexionManager} from "./PeerConnexionManager"; + +export class WebRtcEventManager { + Connexion: ConnexionInterface; + PeerConnexionManager: PeerConnexionManager; + RoomId : string; + + constructor(Connexion : ConnexionInterface, roomId : string = "test-webrtc") { + this.RoomId = roomId; + this.Connexion = Connexion; + this.PeerConnexionManager = new PeerConnexionManager(this); + + this.start(); + this.eventVideoOffer(); + this.eventVideoAnswer(); + this.eventIceCandidate(); + + //connect on the room to create a meet + Connexion.socket.emit('webrtc-room', JSON.stringify({roomId: roomId})); + } + + /** + * server has two person connected, start the meet + */ + start(){ + this.Connexion.socket.on('webrtc-start', () => { + return this.PeerConnexionManager.createPeerConnection(); + }); + } + + /** + * Receive video offer + */ + eventVideoOffer() { + this.Connexion.socket.on("video-offer", (message : any) => { + let data = JSON.parse(message); + console.info("video-offer", data); + this.PeerConnexionManager.createPeerConnection(data).then(() => { + return this.PeerConnexionManager.createAnswer(); + }); + }); + } + + /** + * Receive video answer + */ + eventVideoAnswer() { + this.Connexion.socket.on("video-answer", (message : any) => { + let data = JSON.parse(message); + console.info("video-answer", data); + this.PeerConnexionManager.setRemoteDescription(data) + .catch((err) => { + console.error("video-answer => setRemoteDescription", err) + }) + }); + } + + /** + * Receive ice candidate + */ + eventIceCandidate() { + this.Connexion.socket.on("ice-candidate", (message : any) => { + let data = JSON.parse(message); + console.info("ice-candidate", data); + this.PeerConnexionManager.addIceCandidate(data).then(() => { + console.log(`ICE candidate:\n${data.message ? data.message.candidate : '(null)'}`); + }); + }); + } + + emitIceCandidate(message : any){ + message.roomId = this.RoomId; + this.Connexion.socket.emit('ice-candidate', JSON.stringify(message)); + } + + emitVideoOffer(message : any){ + message.roomId = this.RoomId; + this.Connexion.socket.emit('video-offer', JSON.stringify(message)); + } + + emitVideoAnswer(message : any){ + message.roomId = this.RoomId; + this.Connexion.socket.emit("video-answer", JSON.stringify(message)); + } +} \ No newline at end of file From 89816a13e4f3a553d4efe49c84af2c736a13e85b Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 19 Apr 2020 22:30:42 +0200 Subject: [PATCH 02/18] Fix video call --- front/src/WebRtc/MediaManager.ts | 26 +++++++++ front/src/WebRtc/PeerConnexionManager.ts | 67 ++++++++++++------------ 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index bab1c7c8..605be965 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -54,32 +54,58 @@ export class MediaManager { this.cinemaClose.style.display = "none"; this.cinema.style.display = "block"; this.constraintsMedia.video = true; + this.localStream = null; + this.myCamVideo.srcObject = null; + this.getCamera(); } disabledCamera() { this.cinemaClose.style.display = "block"; this.cinema.style.display = "none"; this.constraintsMedia.video = false; + + this.myCamVideo.pause(); + if(this.localStream) { + this.localStream.getTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { + if (MediaStreamTrack.kind === "video") { + MediaStreamTrack.stop(); + } + }); + } + this.localStream = null; + this.myCamVideo.srcObject = null; + this.getCamera(); } enabledMicrophone() { this.microphoneClose.style.display = "none"; this.microphone.style.display = "block"; this.constraintsMedia.audio = true; + this.getCamera(); } disabledMicrophone() { this.microphoneClose.style.display = "block"; this.microphone.style.display = "none"; this.constraintsMedia.audio = false; + if(this.localStream) { + this.localStream.getTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { + if (MediaStreamTrack.kind === "audio") { + MediaStreamTrack.stop(); + } + }); + } + this.getCamera(); } //get camera getCamera() { this.getCameraPromise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) .then((stream: MediaStream) => { + console.log("constraintsMedia", stream); this.localStream = stream; this.myCamVideo.srcObject = this.localStream; + this.myCamVideo.play(); }).catch((err) => { console.error(err); this.localStream = null; diff --git a/front/src/WebRtc/PeerConnexionManager.ts b/front/src/WebRtc/PeerConnexionManager.ts index e6b531a9..f2b4b284 100644 --- a/front/src/WebRtc/PeerConnexionManager.ts +++ b/front/src/WebRtc/PeerConnexionManager.ts @@ -19,27 +19,27 @@ export class PeerConnexionManager { } createPeerConnection(data: any = null): Promise { - this.peerConnection = new RTCPeerConnection(); + return this.MediaManager.getCameraPromise.then(() => { + this.peerConnection = new RTCPeerConnection(); - //init all events peer connection - this.createEventPeerConnection(); + //init all events peer connection + this.createEventPeerConnection(); - this.MediaManager.getCameraPromise.then(() => { this.MediaManager.localStream.getTracks().forEach( - (track : MediaStreamTrack) => this.peerConnection.addTrack(track, this.MediaManager.localStream) + (track: MediaStreamTrack) => this.peerConnection.addTrack(track, this.MediaManager.localStream) ); + + //if no data, create offer + if (!data || !data.message) { + return this.createOffer(); + } + + let description = new RTCSessionDescription(data.message); + return this.peerConnection.setRemoteDescription(description).catch((err) => { + console.error("createPeerConnection => setRemoteDescription", err); + throw err; + }); }); - - //if no data, create offer - if (!data || !data.message) { - return this.createOffer(); - } - - let description = new RTCSessionDescription(data.message); - return this.peerConnection.setRemoteDescription(description).catch((err) => { - console.error("createPeerConnection => setRemoteDescription", err); - throw err; - }) } createOffer(): Promise { @@ -102,30 +102,31 @@ export class PeerConnexionManager { createEventPeerConnection(){ //define creator of offer - this.peerConnection.onicecandidate = (event: RTCPeerConnectionIceEvent) => { - let message = {message: event.candidate}; - if (!event.candidate) { + this.peerConnection.addEventListener('icecandidate', ({candidate}) => { + let message = {message: candidate}; + if (!candidate) { return; } this.WebRtcEventManager.emitIceCandidate(message); - }; + }); - this.peerConnection.ontrack = (e:RTCTrackEvent) => { - console.info('Event:track', e); - this.MediaManager.remoteVideo.srcObject = e.streams[0]; - this.MediaManager.myCamVideo.srcObject = e.streams[0]; - }; + this.peerConnection.addEventListener('iceconnectionstatechange', (e : Event) => { + console.info('oniceconnectionstatechange => iceConnectionState', this.peerConnection.iceConnectionState); + }); - this.peerConnection.onnegotiationneeded = (e : Event) => { + this.peerConnection.addEventListener('negotiationneeded', (e : Event) => { console.info("Event:negotiationneeded => call()", e); this.createOffer() - }; - this.peerConnection.oniceconnectionstatechange = (e) => { - console.info('ICE state change event: ', e); - }; - this.peerConnection.oniceconnectionstatechange = (e:Event) => { - console.info('oniceconnectionstatechange => iceConnectionState', this.peerConnection.iceConnectionState); - }; + }); + + this.peerConnection.addEventListener("track", (e:RTCTrackEvent) => { + console.info('Event:track', e); + if (this.MediaManager.remoteVideo.srcObject !== e.streams[0]) { + this.MediaManager.remoteVideo.srcObject = e.streams[0]; + console.log('pc1 received remote stream'); + } + }); + this.peerConnection.onicegatheringstatechange = () => { console.info('onicegatheringstatechange => iceConnectionState', this.peerConnection.iceConnectionState); }; From a5b5072de17e5459b9fccd6ed20c542f22c6a9ef Mon Sep 17 00:00:00 2001 From: gparant Date: Mon, 20 Apr 2020 01:10:47 +0200 Subject: [PATCH 03/18] Fix webrtc --- back/src/Controller/IoSocketController.ts | 5 ++++- front/src/WebRtc/WebRtcEventManager.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 88af801a..ff844358 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -71,7 +71,10 @@ 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) + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); + if(!rooms.refreshUserPosition){ + rooms.refreshUserPosition = RefreshUserPositionFunction; + } rooms.refreshUserPosition(rooms, this.Io); }); diff --git a/front/src/WebRtc/WebRtcEventManager.ts b/front/src/WebRtc/WebRtcEventManager.ts index 19c46701..28c6acfc 100644 --- a/front/src/WebRtc/WebRtcEventManager.ts +++ b/front/src/WebRtc/WebRtcEventManager.ts @@ -16,8 +16,9 @@ export class WebRtcEventManager { this.eventVideoAnswer(); this.eventIceCandidate(); - //connect on the room to create a meet - Connexion.socket.emit('webrtc-room', JSON.stringify({roomId: roomId})); + //start to connect on event + //TODO test + this.emitWebRtcRoom(); } /** @@ -69,6 +70,11 @@ export class WebRtcEventManager { }); } + emitWebRtcRoom(){ + //connect on the room to create a meet + this.Connexion.socket.emit('webrtc-room', JSON.stringify({roomId: this.RoomId})); + } + emitIceCandidate(message : any){ message.roomId = this.RoomId; this.Connexion.socket.emit('ice-candidate', JSON.stringify(message)); From c28108f6c98d6f71d65d4491775a53c1e8d8f144 Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 25 Apr 2020 16:05:33 +0200 Subject: [PATCH 04/18] Use WebRtc with SimplePeer --- back/src/Controller/IoSocketController.ts | 24 ++-- front/package.json | 2 + front/src/Connexion.ts | 104 +++++++++++----- front/src/Phaser/Game/GameManager.ts | 4 +- front/src/WebRtc/MediaManager.ts | 14 +-- front/src/WebRtc/PeerConnexionManager.ts | 137 ---------------------- front/src/WebRtc/SimplePeer.ts | 106 +++++++++++++++++ front/src/WebRtc/WebRtcEventManager.ts | 92 --------------- front/yarn.lock | 32 ++++- 9 files changed, 233 insertions(+), 282 deletions(-) delete mode 100644 front/src/WebRtc/PeerConnexionManager.ts create mode 100644 front/src/WebRtc/SimplePeer.ts delete mode 100644 front/src/WebRtc/WebRtcEventManager.ts diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index ff844358..57adf92d 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -7,6 +7,7 @@ import Jwt, {JsonWebTokenError} from "jsonwebtoken"; import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; import {ExtRoomsInterface} from "_Model/Websocket/ExtRoomsInterface"; +import {ExtWebSocket} from "../../../../publicis/sources/api/src/Entities/WebSocket/ExtWebSocket"; export class IoSocketController{ Io: socketIO.Server; @@ -84,28 +85,25 @@ export class IoSocketController{ (socket as ExSocketInterface).roomId = data.roomId; //if two persone in room share + console.log("nb user => " + data.roomId, this.Io.sockets.adapter.rooms[data.roomId].length); if(this.Io.sockets.adapter.rooms[data.roomId].length < 2) { return; } let clients : Array = Object.values(this.Io.sockets.sockets); //send start at one client to initialise offer webrtc - clients[0].emit('webrtc-start'); + clients.forEach((client: ExtWebSocket, index : number) => { + client.emit('webrtc-start', JSON.stringify({ + userId: client.userId, + initiator : index === 0 + })); + }); }); - socket.on('video-offer', (message : string) => { + socket.on('webrtc-signal', (message : string) => { let data : any = JSON.parse(message); - socket.to(data.roomId).emit('video-offer', message); - }); - - socket.on('video-answer', (message : string) => { - let data : any = JSON.parse(message); - socket.to(data.roomId).emit('video-answer', message); - }); - - socket.on('ice-candidate', (message : string) => { - let data : any = JSON.parse(message); - socket.to(data.roomId).emit('ice-candidate', message); + console.info('webrtc-signal', message); + socket.to(data.roomId).emit('webrtc-signal', message); }); }); } diff --git a/front/package.json b/front/package.json index 25d613e6..17c08137 100644 --- a/front/package.json +++ b/front/package.json @@ -15,8 +15,10 @@ }, "dependencies": { "@types/axios": "^0.14.0", + "@types/simple-peer": "^9.6.0", "@types/socket.io-client": "^1.4.32", "phaser": "^3.22.0", + "simple-peer": "^9.6.2", "socket.io-client": "^2.3.0" }, "scripts": { diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index 0d9f998a..b6f2db72 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -4,6 +4,15 @@ const SocketIo = require('socket.io-client'); import Axios from "axios"; import {API_URL} from "./Enum/EnvironmentVariable"; +enum EventMessage{ + WEBRTC_SIGNAL = "webrtc-signal", + WEBRTC_START = "webrtc-start", + WEBRTC_ROOM = "webrtc-room", + JOIN_ROOM = "join-room", + USER_POSITION = "user-position", + MESSAGE_ERROR = "message-error" +} + class Message { userId: string; roomId: string; @@ -56,6 +65,7 @@ export interface MessageUserPositionInterface { roomId: string; position: PointInterface; } + class MessageUserPosition extends Message implements MessageUserPositionInterface{ position: PointInterface; @@ -76,14 +86,15 @@ class MessageUserPosition extends Message implements MessageUserPositionInterfac } export interface ListMessageUserPositionInterface { - roomId : string; + roomId: string; listUsersPosition: Array; } -class ListMessageUserPosition{ - roomId : string; + +class ListMessageUserPosition { + roomId: string; listUsersPosition: Array; - constructor(roomId : string, data : any) { + constructor(roomId: string, data: any) { this.roomId = roomId; this.listUsersPosition = new Array(); data.forEach((userPosition: any) => { @@ -99,32 +110,47 @@ class ListMessageUserPosition{ }); } } + export interface ConnexionInterface { - socket : any; - token : string; - email : string; + socket: any; + token: string; + email: string; userId: string; - startedRoom : string; - createConnexion() : Promise; - joinARoom(roomId : string) : void; - sharePosition(roomId : string, x : number, y : number, direction : string) : void; - positionOfAllUser() : void; + startedRoom: string; + + createConnexion(): Promise; + + joinARoom(roomId: string): void; + + sharePosition(roomId: string, x: number, y: number, direction: string): void; + + positionOfAllUser(): void; + + /*webrtc*/ + sendWebrtcRomm(roomId: string): void; + + sendWebrtcSignal(signal: any, roomId: string): void; + + receiveWebrtcSignal(callBack: Function): void; + + receiveWebrtcStart(callBack: Function): void; } -export class Connexion implements ConnexionInterface{ - socket : any; - token : string; - email : string; + +export class Connexion implements ConnexionInterface { + socket: any; + token: string; + email: string; userId: string; - startedRoom : string; + startedRoom: string; GameManager: GameManagerInterface; - constructor(email : string, GameManager: GameManagerInterface) { + constructor(email: string, GameManager: GameManagerInterface) { this.email = email; this.GameManager = GameManager; } - createConnexion() : Promise{ + createConnexion(): Promise { return Axios.post(`${API_URL}/login`, {email: this.email}) .then((res) => { this.token = res.data.token; @@ -159,9 +185,9 @@ export class Connexion implements ConnexionInterface{ * Permit to join a room * @param roomId */ - joinARoom(roomId : string) : void { + joinARoom(roomId: string): void { let messageUserPosition = new MessageUserPosition(this.userId, this.startedRoom, new Point(0, 0)); - this.socket.emit('join-room', messageUserPosition.toString()); + this.socket.emit(EventMessage.JOIN_ROOM, messageUserPosition.toString()); } /** @@ -171,12 +197,12 @@ export class Connexion implements ConnexionInterface{ * @param y * @param direction */ - sharePosition(roomId : string, x : number, y : number, direction : string = "none") : void{ - if(!this.socket){ + sharePosition(roomId: string, x: number, y: number, direction: string = "none"): void { + if (!this.socket) { return; } let messageUserPosition = new MessageUserPosition(this.userId, roomId, new Point(x, y, direction)); - this.socket.emit('user-position', messageUserPosition.toString()); + this.socket.emit(EventMessage.USER_POSITION, messageUserPosition.toString()); } /** @@ -194,8 +220,8 @@ export class Connexion implements ConnexionInterface{ * ... * ] **/ - positionOfAllUser() : void { - this.socket.on("user-position", (message: string) => { + positionOfAllUser(): void { + this.socket.on(EventMessage.USER_POSITION, (message: string) => { let dataList = JSON.parse(message); dataList.forEach((UserPositions: any) => { let listMessageUserPosition = new ListMessageUserPosition(UserPositions[0], UserPositions[1]); @@ -204,9 +230,29 @@ export class Connexion implements ConnexionInterface{ }); } - errorMessage() : void { - this.socket.on('message-error', (message : string) => { - console.error("message-error", message); + sendWebrtcSignal(signal: any, roomId: string) { + this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({ + userId: this.userId, + roomId: roomId, + signal: signal + })); + } + + sendWebrtcRomm(roomId: string) { + this.socket.emit(EventMessage.WEBRTC_ROOM, JSON.stringify({roomId: roomId})); + } + + receiveWebrtcStart(callback: Function) { + this.socket.on(EventMessage.WEBRTC_START, callback); + } + + receiveWebrtcSignal(callback: Function) { + this.socket.on(EventMessage.WEBRTC_SIGNAL, callback); + } + + errorMessage(): void { + this.socket.on(EventMessage.MESSAGE_ERROR, (message: string) => { + console.error(EventMessage.MESSAGE_ERROR, message); }) } } \ No newline at end of file diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index fffca2e9..fbc1d5bd 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,7 +1,7 @@ import {GameSceneInterface, GameScene} from "./GameScene"; import {ROOM} from "../../Enum/EnvironmentVariable" import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion"; -import {WebRtcEventManager} from "../../WebRtc/WebRtcEventManager"; +import {SimplePeer} from "../../WebRtc/SimplePeer"; export enum StatusGameManagerEnum { IN_PROGRESS = 1, @@ -30,7 +30,7 @@ export class GameManager implements GameManagerInterface { this.configureGame(); /** TODO add loader in the page **/ //initialise cam - new WebRtcEventManager(ConnexionInstance); + new SimplePeer(ConnexionInstance); }).catch((err) => { console.error(err); throw err; diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 605be965..e8666066 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -47,7 +47,7 @@ export class MediaManager { let webRtc = document.getElementById('webRtc'); webRtc.classList.add('active'); - this.getCamera(); + //this.getCamera(); } enabledCamera() { @@ -56,7 +56,7 @@ export class MediaManager { this.constraintsMedia.video = true; this.localStream = null; this.myCamVideo.srcObject = null; - this.getCamera(); + //this.getCamera(); } disabledCamera() { @@ -74,14 +74,14 @@ export class MediaManager { } this.localStream = null; this.myCamVideo.srcObject = null; - this.getCamera(); + //this.getCamera(); } enabledMicrophone() { this.microphoneClose.style.display = "none"; this.microphone.style.display = "block"; this.constraintsMedia.audio = true; - this.getCamera(); + //this.getCamera(); } disabledMicrophone() { @@ -95,17 +95,17 @@ export class MediaManager { } }); } - this.getCamera(); + //this.getCamera(); } //get camera getCamera() { - this.getCameraPromise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) + return this.getCameraPromise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) .then((stream: MediaStream) => { - console.log("constraintsMedia", stream); this.localStream = stream; this.myCamVideo.srcObject = this.localStream; this.myCamVideo.play(); + return stream; }).catch((err) => { console.error(err); this.localStream = null; diff --git a/front/src/WebRtc/PeerConnexionManager.ts b/front/src/WebRtc/PeerConnexionManager.ts deleted file mode 100644 index f2b4b284..00000000 --- a/front/src/WebRtc/PeerConnexionManager.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {WebRtcEventManager} from "./WebRtcEventManager"; -import {MediaManager} from "./MediaManager"; -const offerOptions = { - offerToReceiveAudio: 1, - offerToReceiveVideo: 1, - iceServers: [{url:'stun:stun.l.google.com:19302'}], -}; - -export class PeerConnexionManager { - - WebRtcEventManager: WebRtcEventManager; - MediaManager : MediaManager; - - peerConnection: RTCPeerConnection; - - constructor(WebRtcEventManager : WebRtcEventManager) { - this.WebRtcEventManager = WebRtcEventManager; - this.MediaManager = new MediaManager(); - } - - createPeerConnection(data: any = null): Promise { - return this.MediaManager.getCameraPromise.then(() => { - this.peerConnection = new RTCPeerConnection(); - - //init all events peer connection - this.createEventPeerConnection(); - - this.MediaManager.localStream.getTracks().forEach( - (track: MediaStreamTrack) => this.peerConnection.addTrack(track, this.MediaManager.localStream) - ); - - //if no data, create offer - if (!data || !data.message) { - return this.createOffer(); - } - - let description = new RTCSessionDescription(data.message); - return this.peerConnection.setRemoteDescription(description).catch((err) => { - console.error("createPeerConnection => setRemoteDescription", err); - throw err; - }); - }); - } - - createOffer(): Promise { - console.log('pc1 createOffer start'); - // @ts-ignore - return this.peerConnection.createOffer(offerOptions).then((offer: RTCSessionDescriptionInit) => { - this.peerConnection.setLocalDescription(offer).then(() => { - let message = {message: this.peerConnection.localDescription}; - this.WebRtcEventManager.emitVideoOffer(message); - }).catch((err) => { - console.error("createOffer => setLocalDescription", err); - throw err; - }); - }).catch((err: Error) => { - console.error("createOffer => createOffer", err); - throw err; - }); - } - - createAnswer(): Promise { - return this.peerConnection.createAnswer().then((answer : RTCSessionDescriptionInit) => { - this.peerConnection.setLocalDescription(answer).then(() => { - //push video-answer - let messageSend = {message: this.peerConnection.localDescription}; - this.WebRtcEventManager.emitVideoAnswer(messageSend); - console.info("video-answer => send", messageSend); - }).catch((err) => { - console.error("eventVideoOffer => createAnswer => setLocalDescription", err); - throw err; - }) - }).catch((err) => { - console.error("eventVideoOffer => createAnswer", err); - throw err; - }) - } - - setRemoteDescription(data: any): Promise { - let description = new RTCSessionDescription(data.message); - return this.peerConnection.setRemoteDescription(description).catch((err) => { - console.error("PeerConnexionManager => setRemoteDescription", err); - throw err; - }) - } - - addIceCandidate(data: any): Promise { - return this.peerConnection.addIceCandidate(data.message) - .catch((err) => { - console.error("PeerConnexionManager => addIceCandidate", err); - throw err; - }) - } - - hangup() { - console.log('Ending call'); - if (this.peerConnection) { - this.peerConnection.close(); - } - this.peerConnection = null; - } - - createEventPeerConnection(){ - //define creator of offer - this.peerConnection.addEventListener('icecandidate', ({candidate}) => { - let message = {message: candidate}; - if (!candidate) { - return; - } - this.WebRtcEventManager.emitIceCandidate(message); - }); - - this.peerConnection.addEventListener('iceconnectionstatechange', (e : Event) => { - console.info('oniceconnectionstatechange => iceConnectionState', this.peerConnection.iceConnectionState); - }); - - this.peerConnection.addEventListener('negotiationneeded', (e : Event) => { - console.info("Event:negotiationneeded => call()", e); - this.createOffer() - }); - - this.peerConnection.addEventListener("track", (e:RTCTrackEvent) => { - console.info('Event:track', e); - if (this.MediaManager.remoteVideo.srcObject !== e.streams[0]) { - this.MediaManager.remoteVideo.srcObject = e.streams[0]; - console.log('pc1 received remote stream'); - } - }); - - this.peerConnection.onicegatheringstatechange = () => { - console.info('onicegatheringstatechange => iceConnectionState', this.peerConnection.iceConnectionState); - }; - this.peerConnection.onsignalingstatechange = () => { - console.info('onsignalingstatechange => iceConnectionState', this.peerConnection.iceConnectionState); - }; - } -} \ No newline at end of file diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts new file mode 100644 index 00000000..9d199ccc --- /dev/null +++ b/front/src/WebRtc/SimplePeer.ts @@ -0,0 +1,106 @@ +import {ConnexionInterface} from "../Connexion"; +import {MediaManager} from "./MediaManager"; +let Peer = require('simple-peer'); + +export class SimplePeer { + Connexion: ConnexionInterface; + MediaManager: MediaManager; + RoomId: string; + + PeerConnexion: any; + + constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { + this.Connexion = Connexion; + this.MediaManager = new MediaManager(); + this.RoomId = roomId; + this.initialise(); + } + + /** + * server has two person connected, start the meet + */ + initialise() { + return this.MediaManager.getCamera().then(() => { + //send message to join a room + this.Connexion.sendWebrtcRomm(this.RoomId); + + //receive message start + this.Connexion.receiveWebrtcStart((message : string) => { + this.receiveWebrtcStart(message); + }); + + //receive signal by gemer + this.Connexion.receiveWebrtcSignal((message : string) => { + this.receiveWebrtcSignal(message); + }); + + }).catch((err) => { + console.error(err); + }); + } + + /** + * + */ + receiveWebrtcStart(message: string) { + let data = JSON.parse(message); + + //create pear connexion of user stared + this.createPeerConnexion(data.initiator); + } + + /** + * + * @param userId + * @param initiator + */ + createPeerConnexion(initiator : boolean = false){ + this.PeerConnexion = new Peer({initiator: initiator}); + this.addMedia(); + + this.PeerConnexion.on('signal', (data: any) => { + this.sendWebrtcSignal(data); + }); + + this.PeerConnexion.on('stream', (stream: MediaStream) => { + this.stream(stream) + }); + } + + /** + * permit to send signal + * @param data + */ + sendWebrtcSignal(data: any) { + this.Connexion.sendWebrtcSignal(data, this.RoomId); + } + + /** + * + * @param message + */ + receiveWebrtcSignal(message: string) { + let data = JSON.parse(message); + if(!this.PeerConnexion){ + return; + } + this.PeerConnexion.signal(data.signal); + } + + /** + * permit stream video + * @param stream + */ + stream(stream: MediaStream) { + this.MediaManager.remoteStream = stream; + this.MediaManager.remoteVideo.srcObject = this.MediaManager.remoteStream; + } + + /** + * Permit to update stream + * @param stream + */ + addMedia () { + this.PeerConnexion.addStream(this.MediaManager.localStream) // <- add streams to peer dynamically + } +} \ No newline at end of file diff --git a/front/src/WebRtc/WebRtcEventManager.ts b/front/src/WebRtc/WebRtcEventManager.ts deleted file mode 100644 index 28c6acfc..00000000 --- a/front/src/WebRtc/WebRtcEventManager.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {ConnexionInterface} from "../Connexion"; -import {PeerConnexionManager} from "./PeerConnexionManager"; - -export class WebRtcEventManager { - Connexion: ConnexionInterface; - PeerConnexionManager: PeerConnexionManager; - RoomId : string; - - constructor(Connexion : ConnexionInterface, roomId : string = "test-webrtc") { - this.RoomId = roomId; - this.Connexion = Connexion; - this.PeerConnexionManager = new PeerConnexionManager(this); - - this.start(); - this.eventVideoOffer(); - this.eventVideoAnswer(); - this.eventIceCandidate(); - - //start to connect on event - //TODO test - this.emitWebRtcRoom(); - } - - /** - * server has two person connected, start the meet - */ - start(){ - this.Connexion.socket.on('webrtc-start', () => { - return this.PeerConnexionManager.createPeerConnection(); - }); - } - - /** - * Receive video offer - */ - eventVideoOffer() { - this.Connexion.socket.on("video-offer", (message : any) => { - let data = JSON.parse(message); - console.info("video-offer", data); - this.PeerConnexionManager.createPeerConnection(data).then(() => { - return this.PeerConnexionManager.createAnswer(); - }); - }); - } - - /** - * Receive video answer - */ - eventVideoAnswer() { - this.Connexion.socket.on("video-answer", (message : any) => { - let data = JSON.parse(message); - console.info("video-answer", data); - this.PeerConnexionManager.setRemoteDescription(data) - .catch((err) => { - console.error("video-answer => setRemoteDescription", err) - }) - }); - } - - /** - * Receive ice candidate - */ - eventIceCandidate() { - this.Connexion.socket.on("ice-candidate", (message : any) => { - let data = JSON.parse(message); - console.info("ice-candidate", data); - this.PeerConnexionManager.addIceCandidate(data).then(() => { - console.log(`ICE candidate:\n${data.message ? data.message.candidate : '(null)'}`); - }); - }); - } - - emitWebRtcRoom(){ - //connect on the room to create a meet - this.Connexion.socket.emit('webrtc-room', JSON.stringify({roomId: this.RoomId})); - } - - emitIceCandidate(message : any){ - message.roomId = this.RoomId; - this.Connexion.socket.emit('ice-candidate', JSON.stringify(message)); - } - - emitVideoOffer(message : any){ - message.roomId = this.RoomId; - this.Connexion.socket.emit('video-offer', JSON.stringify(message)); - } - - emitVideoAnswer(message : any){ - message.roomId = this.RoomId; - this.Connexion.socket.emit("video-answer", JSON.stringify(message)); - } -} \ No newline at end of file diff --git a/front/yarn.lock b/front/yarn.lock index c9b34f3f..5b3a1cc0 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -65,6 +65,13 @@ version "13.11.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" +"@types/simple-peer@^9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.6.0.tgz#b5828d835b7f42dde27db584ba127e7a9f9072f4" + integrity sha512-X2y6s+vE/3j03hkI90oqld2JH2J/m1L7yFCYYPyFV/whrOK1h4neYvJL3GIE+UcACJacXZqzdmDKudwec18RbA== + dependencies: + "@types/node" "*" + "@types/socket.io-client@^1.4.32": version "1.4.32" resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" @@ -1718,6 +1725,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +get-browser-rtc@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" + integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk= + get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -3097,7 +3109,12 @@ querystringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +queue-microtask@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.2.tgz#139bf8186db0c545017ec66c2664ac646d5c571e" + integrity sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.3, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" dependencies: @@ -3135,7 +3152,7 @@ raw-body@2.4.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6: +readable-stream@^3.0.6, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" dependencies: @@ -3418,6 +3435,17 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" +simple-peer@^9.6.2: + version "9.6.2" + resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.6.2.tgz#42418e77cf8f9184e4fa22ef1017b195c2bf84d7" + integrity sha512-EOKoImCaqtNvXIntxT1CBBK/3pVi7tMAoJ3shdyd9qk3zLm3QPiRLb/sPC1G2xvKJkJc5fkQjCXqRZ0AknwTig== + dependencies: + debug "^4.0.1" + get-browser-rtc "^1.0.0" + queue-microtask "^1.1.0" + randombytes "^2.0.3" + readable-stream "^3.4.0" + slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" From 89db8558f6d6fffb94100745c64618e07b6bde32 Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 25 Apr 2020 17:14:05 +0200 Subject: [PATCH 05/18] Add multi SimplePear connection --- back/src/Controller/IoSocketController.ts | 10 ++++++- front/src/WebRtc/SimplePeer.ts | 34 ++++++++++++++--------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 57adf92d..cd715bb8 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -92,9 +92,17 @@ export class IoSocketController{ let clients : Array = Object.values(this.Io.sockets.sockets); //send start at one client to initialise offer webrtc + //send all users in room to create PeerConnection in front + let clientsId = clients.reduce((tabs : Array, client: ExtWebSocket) => { + if(!client.userId){ + return tabs; + } + tabs.push(client.userId); + return tabs; + }, []); clients.forEach((client: ExtWebSocket, index : number) => { client.emit('webrtc-start', JSON.stringify({ - userId: client.userId, + usersId: clientsId.filter((userId : any) => userId !== client.userId), initiator : index === 0 })); }); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 9d199ccc..966254be 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -8,6 +8,7 @@ export class SimplePeer { RoomId: string; PeerConnexion: any; + PeerConnexionArray: Array = new Array(); constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { this.Connexion = Connexion; @@ -46,7 +47,7 @@ export class SimplePeer { let data = JSON.parse(message); //create pear connexion of user stared - this.createPeerConnexion(data.initiator); + this.createPeerConnexion(data.usersId, data.initiator); } /** @@ -54,16 +55,23 @@ export class SimplePeer { * @param userId * @param initiator */ - createPeerConnexion(initiator : boolean = false){ - this.PeerConnexion = new Peer({initiator: initiator}); - this.addMedia(); + createPeerConnexion(usersId : Array, initiator : boolean = false) { + usersId.forEach((userId: any) => { + if(this.PeerConnexionArray[userId]){ + return; + } + this.PeerConnexion = new Peer({initiator: initiator}); - this.PeerConnexion.on('signal', (data: any) => { - this.sendWebrtcSignal(data); - }); + this.PeerConnexion.on('signal', (data: any) => { + this.sendWebrtcSignal(data); + }); - this.PeerConnexion.on('stream', (stream: MediaStream) => { - this.stream(stream) + this.PeerConnexion.on('stream', (stream: MediaStream) => { + this.stream(stream); + }); + + this.PeerConnexionArray[userId] = this.PeerConnexion; + this.addMedia(userId); }); } @@ -81,10 +89,10 @@ export class SimplePeer { */ receiveWebrtcSignal(message: string) { let data = JSON.parse(message); - if(!this.PeerConnexion){ + if(!this.PeerConnexionArray[data.userId]){ return; } - this.PeerConnexion.signal(data.signal); + this.PeerConnexionArray[data.userId].signal(data.signal); } /** @@ -100,7 +108,7 @@ export class SimplePeer { * Permit to update stream * @param stream */ - addMedia () { - this.PeerConnexion.addStream(this.MediaManager.localStream) // <- add streams to peer dynamically + addMedia (userId : any) { + this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically } } \ No newline at end of file From 575054fe4f1d56f23b1b4d9550d31637d8289674 Mon Sep 17 00:00:00 2001 From: gparant Date: Sat, 25 Apr 2020 20:29:03 +0200 Subject: [PATCH 06/18] Fix multi video --- front/dist/index.html | 5 ++- front/dist/resources/style/style.css | 47 +++++++++++++++++++++++----- front/src/WebRtc/MediaManager.ts | 14 +++++++-- front/src/WebRtc/SimplePeer.ts | 21 +++++++------ 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/front/dist/index.html b/front/dist/index.html index 656c903b..4b628398 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -11,10 +11,9 @@
-
- +
-
+
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 4c27c584..71b948bd 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -4,12 +4,6 @@ .webrtc.active{ display: block; } -.myCam{ - display: none; -} -.myCam.active{ - display: block; -} .webrtc, .activeCam{ position: absolute; left: 0; @@ -18,10 +12,48 @@ height: 100%; background: black; } -.webrtc video{ +.activeCam video{ + position: absolute; width: 100%; height: 100%; } + +/*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%; +} +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%; +} + +/*CSS position for 2 elements*/ +video:nth-child(1):nth-last-child(2){ + left: 0; +} +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) { + top: 50%; + left: 25%; +} + .myCam{ height: 200px; width: 300px; @@ -33,6 +65,7 @@ max-height: 17%; max-width: 17%; opacity: 1; + display: block; transition: opacity 1s; } .myCam video{ diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index e8666066..ac1e9bbb 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,7 +1,7 @@ export class MediaManager { localStream: MediaStream; remoteStream: MediaStream; - remoteVideo: any; + remoteVideo: Array = new Array(); myCamVideo: any; cinemaClose: any = null; cinema: any = null; @@ -11,7 +11,6 @@ export class MediaManager { getCameraPromise : Promise = null; constructor() { - this.remoteVideo = document.getElementById('activeCamVideo'); this.myCamVideo = document.getElementById('myCamVideo'); this.microphoneClose = document.getElementById('microphone-close'); @@ -112,4 +111,15 @@ export class MediaManager { throw err; }); } + + /** + * + * @param userId + */ + addActiveVideo(userId : any){ + let elementRemoteVideo = document.getElementById("activeCam"); + elementRemoteVideo.insertAdjacentHTML('beforeend', ''); + + this.remoteVideo[userId] = document.getElementById('myCamVideo'+userId); + } } \ No newline at end of file diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 966254be..c5656c3b 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -26,15 +26,14 @@ export class SimplePeer { this.Connexion.sendWebrtcRomm(this.RoomId); //receive message start - this.Connexion.receiveWebrtcStart((message : string) => { + this.Connexion.receiveWebrtcStart((message: string) => { this.receiveWebrtcStart(message); }); //receive signal by gemer - this.Connexion.receiveWebrtcSignal((message : string) => { + this.Connexion.receiveWebrtcSignal((message: string) => { this.receiveWebrtcSignal(message); }); - }).catch((err) => { console.error(err); }); @@ -60,6 +59,8 @@ export class SimplePeer { if(this.PeerConnexionArray[userId]){ return; } + this.MediaManager.addActiveVideo(userId); + this.PeerConnexion = new Peer({initiator: initiator}); this.PeerConnexion.on('signal', (data: any) => { @@ -67,7 +68,7 @@ export class SimplePeer { }); this.PeerConnexion.on('stream', (stream: MediaStream) => { - this.stream(stream); + this.stream(userId, stream); }); this.PeerConnexionArray[userId] = this.PeerConnexion; @@ -96,17 +97,17 @@ export class SimplePeer { } /** - * permit stream video + * + * @param userId * @param stream */ - stream(stream: MediaStream) { - this.MediaManager.remoteStream = stream; - this.MediaManager.remoteVideo.srcObject = this.MediaManager.remoteStream; + stream(userId : any, stream: MediaStream) { + this.MediaManager.remoteVideo[userId].srcObject = stream; } /** - * Permit to update stream - * @param stream + * + * @param userId */ addMedia (userId : any) { this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically From e4a30712b5cfbeaf6632622bdc4ce2e83a37f70a Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 17:42:49 +0200 Subject: [PATCH 07/18] Update front mutli peer conncexion --- front/src/WebRtc/MediaManager.ts | 4 +-- front/src/WebRtc/SimplePeer.ts | 42 +++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index ac1e9bbb..56e02d44 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -118,8 +118,8 @@ export class MediaManager { */ addActiveVideo(userId : any){ let elementRemoteVideo = document.getElementById("activeCam"); - elementRemoteVideo.insertAdjacentHTML('beforeend', ''); + elementRemoteVideo.insertAdjacentHTML('beforeend', ''); - this.remoteVideo[userId] = document.getElementById('myCamVideo'+userId); + this.remoteVideo[userId] = document.getElementById(userId); } } \ No newline at end of file diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index c5656c3b..fabc876e 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -41,47 +41,61 @@ export class SimplePeer { /** * + * @param message */ receiveWebrtcStart(message: string) { let data = JSON.parse(message); //create pear connexion of user stared - this.createPeerConnexion(data.usersId, data.initiator); + this.createPeerConnexion(data); } /** * - * @param userId - * @param initiator + * @param users */ - createPeerConnexion(usersId : Array, initiator : boolean = false) { - usersId.forEach((userId: any) => { - if(this.PeerConnexionArray[userId]){ + createPeerConnexion(users : Array) { + console.log("createPeerConnexion", users); + users.forEach((user: any) => { + if(this.PeerConnexionArray[user.userId]){ return; } - this.MediaManager.addActiveVideo(userId); + this.MediaManager.addActiveVideo(user.userId); - this.PeerConnexion = new Peer({initiator: initiator}); + this.PeerConnexion = new Peer({initiator: user.initiator}); this.PeerConnexion.on('signal', (data: any) => { this.sendWebrtcSignal(data); }); this.PeerConnexion.on('stream', (stream: MediaStream) => { - this.stream(userId, stream); + this.stream(user.userId, stream); }); - this.PeerConnexionArray[userId] = this.PeerConnexion; - this.addMedia(userId); + this.PeerConnexionArray[user.userId] = this.PeerConnexion; + this.addMedia(user.userId); }); + + /*let elements = document.getElementById("activeCam"); + console.log("element.childNodes", elements.childNodes); + elements.childNodes.forEach((element : any) => { + if(!element.id){ + return; + } + if(users.find((user) => user.userId === element.id)){ + return; + } + elements.removeChild(element); + });*/ } /** - * permit to send signal + * + * @param userId * @param data */ - sendWebrtcSignal(data: any) { - this.Connexion.sendWebrtcSignal(data, this.RoomId); + sendWebrtcSignal(data: any, userId : string) { + this.Connexion.sendWebrtcSignal(data, this.RoomId, userId); } /** From e17871245720c111cad960102edc0c2db1d41ca0 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 17:43:21 +0200 Subject: [PATCH 08/18] Fix send message webrtc --- front/src/Connexion.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index b6f2db72..35dbd068 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -129,7 +129,7 @@ export interface ConnexionInterface { /*webrtc*/ sendWebrtcRomm(roomId: string): void; - sendWebrtcSignal(signal: any, roomId: string): void; + sendWebrtcSignal(signal: any, roomId: string, userId?: string): void; receiveWebrtcSignal(callBack: Function): void; @@ -230,9 +230,9 @@ export class Connexion implements ConnexionInterface { }); } - sendWebrtcSignal(signal: any, roomId: string) { + sendWebrtcSignal(signal: any, roomId: string, userId? : string ) { this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({ - userId: this.userId, + userId: userId ? userId : this.userId, roomId: roomId, signal: signal })); From d7d7be9ed0a4c4a30ede92adcdc9af95480203d5 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 19:12:01 +0200 Subject: [PATCH 09/18] Web visio, add and remove video element --- back/src/Controller/IoSocketController.ts | 38 ++++++++++++++--------- front/src/Connexion.ts | 5 +-- front/src/WebRtc/MediaManager.ts | 16 ++++++++-- front/src/WebRtc/SimplePeer.ts | 34 +++++++++----------- 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index cd715bb8..42906851 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -85,7 +85,6 @@ export class IoSocketController{ (socket as ExSocketInterface).roomId = data.roomId; //if two persone in room share - console.log("nb user => " + data.roomId, this.Io.sockets.adapter.rooms[data.roomId].length); if(this.Io.sockets.adapter.rooms[data.roomId].length < 2) { return; } @@ -93,25 +92,36 @@ export class IoSocketController{ //send start at one client to initialise offer webrtc //send all users in room to create PeerConnection in front - let clientsId = clients.reduce((tabs : Array, client: ExtWebSocket) => { - if(!client.userId){ - return tabs; - } - tabs.push(client.userId); - return tabs; - }, []); clients.forEach((client: ExtWebSocket, index : number) => { - client.emit('webrtc-start', JSON.stringify({ - usersId: clientsId.filter((userId : any) => userId !== client.userId), - initiator : index === 0 - })); + + let clientsId = clients.reduce((tabs : Array, clientId: ExtWebSocket, indexClientId: number) => { + if(!clientId.userId || clientId.userId === client.userId){ + return tabs; + } + tabs.push({ + userId: clientId.userId, + initiator : index <= indexClientId + }); + return tabs; + }, []); + + client.emit('webrtc-start', JSON.stringify(clientsId)); }); }); socket.on('webrtc-signal', (message : string) => { let data : any = JSON.parse(message); - console.info('webrtc-signal', message); - socket.to(data.roomId).emit('webrtc-signal', message); + + //send only at user + let clients: Array = Object.values(this.Io.sockets.sockets); + for(let i = 0; i < clients.length; i++){ + let client : ExtWebSocket = clients[i]; + if(client.userId !== data.receiverId){ + continue + } + client.emit('webrtc-signal', message); + break; + } }); }); } diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index 35dbd068..a9c10df5 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -129,7 +129,7 @@ export interface ConnexionInterface { /*webrtc*/ sendWebrtcRomm(roomId: string): void; - sendWebrtcSignal(signal: any, roomId: string, userId?: string): void; + sendWebrtcSignal(signal: any, roomId: string, userId?: string, receiverId?: string): void; receiveWebrtcSignal(callBack: Function): void; @@ -230,9 +230,10 @@ export class Connexion implements ConnexionInterface { }); } - sendWebrtcSignal(signal: any, roomId: string, userId? : string ) { + sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) { this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({ userId: userId ? userId : this.userId, + receiverId: receiverId ? receiverId : this.userId, roomId: roomId, signal: signal })); diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 56e02d44..7363f155 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -116,10 +116,22 @@ export class MediaManager { * * @param userId */ - addActiveVideo(userId : any){ + addActiveVideo(userId : string){ let elementRemoteVideo = document.getElementById("activeCam"); elementRemoteVideo.insertAdjacentHTML('beforeend', ''); - this.remoteVideo[userId] = document.getElementById(userId); + this.remoteVideo[(userId as any)] = document.getElementById(userId); + } + + /** + * + * @param userId + */ + removeActiveVideo(userId : string){ + let element = document.getElementById(userId); + if(!element){ + return; + } + element.remove(); } } \ No newline at end of file diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index fabc876e..a410906b 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -7,7 +7,6 @@ export class SimplePeer { MediaManager: MediaManager; RoomId: string; - PeerConnexion: any; PeerConnexionArray: Array = new Array(); constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { @@ -55,38 +54,35 @@ export class SimplePeer { * @param users */ createPeerConnexion(users : Array) { - console.log("createPeerConnexion", users); users.forEach((user: any) => { if(this.PeerConnexionArray[user.userId]){ return; } this.MediaManager.addActiveVideo(user.userId); - this.PeerConnexion = new Peer({initiator: user.initiator}); + this.PeerConnexionArray[user.userId] = new Peer({initiator: user.initiator}); - this.PeerConnexion.on('signal', (data: any) => { - this.sendWebrtcSignal(data); + this.PeerConnexionArray[user.userId].on('signal', (data: any) => { + this.sendWebrtcSignal(data, user.userId); }); - this.PeerConnexion.on('stream', (stream: MediaStream) => { + this.PeerConnexionArray[user.userId].on('stream', (stream: MediaStream) => { this.stream(user.userId, stream); }); - this.PeerConnexionArray[user.userId] = this.PeerConnexion; + this.PeerConnexionArray[user.userId].on('close', () => { + this.closeConnexion(user.userId); + }); + this.addMedia(user.userId); }); - /*let elements = document.getElementById("activeCam"); - console.log("element.childNodes", elements.childNodes); - elements.childNodes.forEach((element : any) => { - if(!element.id){ - return; - } - if(users.find((user) => user.userId === element.id)){ - return; - } - elements.removeChild(element); - });*/ + } + + closeConnexion(userId : string){ + // @ts-ignore + this.PeerConnexionArray[userId] = null; + this.MediaManager.removeActiveVideo(userId) } /** @@ -95,7 +91,7 @@ export class SimplePeer { * @param data */ sendWebrtcSignal(data: any, userId : string) { - this.Connexion.sendWebrtcSignal(data, this.RoomId, userId); + this.Connexion.sendWebrtcSignal(data, this.RoomId, null, userId); } /** From 707931724fdd39839daaeeae9c5df6359105b81d Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 19:59:51 +0200 Subject: [PATCH 10/18] Start visio with user colision When user enter in colision with other colision, webrtc start visio --- front/src/Phaser/Game/GameManager.ts | 13 ++++++++++--- front/src/Phaser/Game/GameScene.ts | 1 + front/src/WebRtc/MediaManager.ts | 10 +--------- front/src/WebRtc/SimplePeer.ts | 12 ++++++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index fbc1d5bd..6121d262 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,7 +1,7 @@ import {GameSceneInterface, GameScene} from "./GameScene"; import {ROOM} from "../../Enum/EnvironmentVariable" import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion"; -import {SimplePeer} from "../../WebRtc/SimplePeer"; +import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; export enum StatusGameManagerEnum { IN_PROGRESS = 1, @@ -13,12 +13,15 @@ export let ConnexionInstance : ConnexionInterface; export interface GameManagerInterface { GameScenes: Array; status : number; + SimplePeer: SimplePeerInterface; createCurrentPlayer() : void; + startWebRtc() : void; shareUserPosition(ListMessageUserPosition : ListMessageUserPositionInterface): void; } export class GameManager implements GameManagerInterface { GameScenes: Array = []; status: number; + SimplePeer : SimplePeerInterface; constructor() { this.status = StatusGameManagerEnum.IN_PROGRESS; @@ -29,8 +32,8 @@ export class GameManager implements GameManagerInterface { return ConnexionInstance.createConnexion().then(() => { this.configureGame(); /** TODO add loader in the page **/ - //initialise cam - new SimplePeer(ConnexionInstance); + //initialise Pear Connexion of game + this.SimplePeer = new SimplePeer(ConnexionInstance); }).catch((err) => { console.error(err); throw err; @@ -59,6 +62,10 @@ export class GameManager implements GameManagerInterface { this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; } + startWebRtc() : void { + this.SimplePeer.startWebRtc(); + } + /** * Share position in game * @param ListMessageUserPosition diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index a08c8fd2..0fac397e 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -253,6 +253,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //init colision this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => { CurrentPlayer.say("Hello, how are you ? "); + this.GameManager.startWebRtc(); }); } } diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 7363f155..0d1a3a99 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -40,13 +40,11 @@ export class MediaManager { //update tracking }); - this.enabledMicrophone(); this.enabledCamera(); + this.enabledMicrophone(); let webRtc = document.getElementById('webRtc'); webRtc.classList.add('active'); - - //this.getCamera(); } enabledCamera() { @@ -55,7 +53,6 @@ export class MediaManager { this.constraintsMedia.video = true; this.localStream = null; this.myCamVideo.srcObject = null; - //this.getCamera(); } disabledCamera() { @@ -73,14 +70,12 @@ export class MediaManager { } this.localStream = null; this.myCamVideo.srcObject = null; - //this.getCamera(); } enabledMicrophone() { this.microphoneClose.style.display = "none"; this.microphone.style.display = "block"; this.constraintsMedia.audio = true; - //this.getCamera(); } disabledMicrophone() { @@ -94,7 +89,6 @@ export class MediaManager { } }); } - //this.getCamera(); } //get camera @@ -103,7 +97,6 @@ export class MediaManager { .then((stream: MediaStream) => { this.localStream = stream; this.myCamVideo.srcObject = this.localStream; - this.myCamVideo.play(); return stream; }).catch((err) => { console.error(err); @@ -119,7 +112,6 @@ export class MediaManager { addActiveVideo(userId : string){ let elementRemoteVideo = document.getElementById("activeCam"); elementRemoteVideo.insertAdjacentHTML('beforeend', ''); - this.remoteVideo[(userId as any)] = document.getElementById(userId); } diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index a410906b..86b33850 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -2,6 +2,10 @@ import {ConnexionInterface} from "../Connexion"; import {MediaManager} from "./MediaManager"; let Peer = require('simple-peer'); +export interface SimplePeerInterface { + startWebRtc(): void; +} + export class SimplePeer { Connexion: ConnexionInterface; MediaManager: MediaManager; @@ -11,16 +15,16 @@ export class SimplePeer { constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { this.Connexion = Connexion; - this.MediaManager = new MediaManager(); this.RoomId = roomId; - this.initialise(); } /** * server has two person connected, start the meet */ - initialise() { - return this.MediaManager.getCamera().then(() => { + startWebRtc() { + this.MediaManager = new MediaManager(); + return this.MediaManager.getCamera().then((stream: MediaStream) => { + this.MediaManager.localStream = stream; //send message to join a room this.Connexion.sendWebrtcRomm(this.RoomId); From b49c01231922a994fa06e5a46e35657ccc0fc360 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 20:55:20 +0200 Subject: [PATCH 11/18] Add button to enter un visio --- front/dist/index.html | 3 ++ front/dist/resources/logos/phone-open.svg | 16 ++++++++++ front/dist/resources/style/style.css | 39 +++++++++++++++++++++++ front/src/Phaser/Game/GameManager.ts | 5 --- front/src/Phaser/Game/GameScene.ts | 2 +- front/src/WebRtc/MediaManager.ts | 20 +++++++++--- front/src/WebRtc/SimplePeer.ts | 18 +++++++++-- 7 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 front/dist/resources/logos/phone-open.svg diff --git a/front/dist/index.html b/front/dist/index.html index 4b628398..a9f51d4d 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -30,5 +30,8 @@
+
+ +
diff --git a/front/dist/resources/logos/phone-open.svg b/front/dist/resources/logos/phone-open.svg new file mode 100644 index 00000000..73b08951 --- /dev/null +++ b/front/dist/resources/logos/phone-open.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 71b948bd..9e2d2daa 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -111,4 +111,43 @@ video:nth-child(3):nth-last-child(1) { top: calc(48px - 32px); left: calc(48px - 35px); position: relative; +} +.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/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 6121d262..f4f24ef5 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -15,7 +15,6 @@ export interface GameManagerInterface { status : number; SimplePeer: SimplePeerInterface; createCurrentPlayer() : void; - startWebRtc() : void; shareUserPosition(ListMessageUserPosition : ListMessageUserPositionInterface): void; } export class GameManager implements GameManagerInterface { @@ -62,10 +61,6 @@ export class GameManager implements GameManagerInterface { this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; } - startWebRtc() : void { - this.SimplePeer.startWebRtc(); - } - /** * Share position in game * @param ListMessageUserPosition diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 0fac397e..5a320c38 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -253,7 +253,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //init colision this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => { CurrentPlayer.say("Hello, how are you ? "); - this.GameManager.startWebRtc(); + this.GameManager.SimplePeer.activePhone(); }); } } diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 0d1a3a99..5b8d0076 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,25 +1,23 @@ export class MediaManager { localStream: MediaStream; - remoteStream: MediaStream; remoteVideo: Array = new Array(); myCamVideo: any; cinemaClose: any = null; cinema: any = null; microphoneClose: any = null; microphone: any = null; - constraintsMedia = {audio: true, video: true}; + constraintsMedia = {audio: false, video: true}; getCameraPromise : Promise = null; constructor() { this.myCamVideo = document.getElementById('myCamVideo'); - this.microphoneClose = document.getElementById('microphone-close'); + this.microphoneClose.addEventListener('click', (e: any) => { e.preventDefault(); this.enabledMicrophone(); //update tracking }); - this.microphone = document.getElementById('microphone'); this.microphone.addEventListener('click', (e: any) => { e.preventDefault(); @@ -42,7 +40,9 @@ export class MediaManager { this.enabledCamera(); this.enabledMicrophone(); + } + activeVisio(){ let webRtc = document.getElementById('webRtc'); webRtc.classList.add('active'); } @@ -91,6 +91,18 @@ export class MediaManager { } } + getElementActivePhone(){ + return document.getElementById('phone-open'); + } + + activePhoneOpen(){ + return this.getElementActivePhone().classList.add("active"); + } + + disablePhoneOpen(){ + return this.getElementActivePhone().classList.remove("active"); + } + //get camera getCamera() { return this.getCameraPromise = navigator.mediaDevices.getUserMedia(this.constraintsMedia) diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 86b33850..a53fd754 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -3,7 +3,8 @@ import {MediaManager} from "./MediaManager"; let Peer = require('simple-peer'); export interface SimplePeerInterface { - startWebRtc(): void; + activePhone(): void; + disablePhone(): void; } export class SimplePeer { @@ -16,13 +17,18 @@ export class SimplePeer { constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { this.Connexion = Connexion; this.RoomId = roomId; + this.MediaManager = new MediaManager(); + this.MediaManager.getElementActivePhone().addEventListener("click", () => { + this.startWebRtc(); + this.disablePhone(); + }); } /** * server has two person connected, start the meet */ startWebRtc() { - this.MediaManager = new MediaManager(); + this.MediaManager.activeVisio(); return this.MediaManager.getCamera().then((stream: MediaStream) => { this.MediaManager.localStream = stream; //send message to join a room @@ -126,4 +132,12 @@ export class SimplePeer { addMedia (userId : any) { this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically } + + activePhone(){ + this.MediaManager.activePhoneOpen(); + } + + disablePhone(){ + this.MediaManager.disablePhoneOpen(); + } } \ No newline at end of file From d396ad2f413e3e52b5793128a06073d401b7fe53 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 22:35:16 +0200 Subject: [PATCH 12/18] Fix CI CD --- back/src/Controller/IoSocketController.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 42906851..b9d00ce7 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -7,7 +7,6 @@ import Jwt, {JsonWebTokenError} from "jsonwebtoken"; import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; import {ExtRoomsInterface} from "_Model/Websocket/ExtRoomsInterface"; -import {ExtWebSocket} from "../../../../publicis/sources/api/src/Entities/WebSocket/ExtWebSocket"; export class IoSocketController{ Io: socketIO.Server; @@ -92,9 +91,9 @@ export class IoSocketController{ //send start at one client to initialise offer webrtc //send all users in room to create PeerConnection in front - clients.forEach((client: ExtWebSocket, index : number) => { + clients.forEach((client: ExSocketInterface, index : number) => { - let clientsId = clients.reduce((tabs : Array, clientId: ExtWebSocket, indexClientId: number) => { + let clientsId = clients.reduce((tabs : Array, clientId: ExSocketInterface, indexClientId: number) => { if(!clientId.userId || clientId.userId === client.userId){ return tabs; } @@ -115,7 +114,7 @@ export class IoSocketController{ //send only at user let clients: Array = Object.values(this.Io.sockets.sockets); for(let i = 0; i < clients.length; i++){ - let client : ExtWebSocket = clients[i]; + let client : ExSocketInterface = clients[i]; if(client.userId !== data.receiverId){ continue } From 3f975171c2c4dd85705a5a6d8b35bc90a82c5391 Mon Sep 17 00:00:00 2001 From: gparant Date: Sun, 26 Apr 2020 23:22:13 +0200 Subject: [PATCH 13/18] Fix import --- front/src/Phaser/Game/GameScene.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 5a320c38..159498e1 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,8 +1,7 @@ -import {GameManagerInterface, StatusGameManagerEnum} from "./GameManager"; +import {GameManagerInterface} from "./GameManager"; import {MessageUserPositionInterface} from "../../Connexion"; import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player"; import {DEBUG_MODE, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; -import Tile = Phaser.Tilemaps.Tile; import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap"; import {cypressAsserter} from "../../Cypress/CypressAsserter"; From 881bb04eb04002dc306fbc5c1c5c4ff3cd639bc3 Mon Sep 17 00:00:00 2001 From: gparant Date: Mon, 27 Apr 2020 00:44:25 +0200 Subject: [PATCH 14/18] Implement Distance Merge Request --- back/src/Controller/IoSocketController.ts | 26 ++++++++++++++++--- back/src/Model/Websocket/Message.ts | 4 +-- .../Model/Websocket/MessageUserPosition.ts | 5 ++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index c69a64e3..77f3ac9e 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -6,10 +6,12 @@ import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO f import Jwt, {JsonWebTokenError} from "jsonwebtoken"; import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; -import {ExtRoomsInterface} from "_Model/Websocket/ExtRoomsInterface"; +import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface"; +import {World} from "../Model/World"; export class IoSocketController{ Io: socketIO.Server; + World: World; constructor(server : http.Server) { this.Io = socketIO(server); @@ -29,6 +31,7 @@ export class IoSocketController{ this.ioConnection(); this.shareUsersPosition(); + this.World = new World(this.connectedUser, this.disConnectedUser); } ioConnection() { @@ -50,6 +53,9 @@ export class IoSocketController{ //join user in room socket.join(messageUserPosition.roomId); + //join user in world + this.World.join(messageUserPosition); + // sending to all clients in room except sender this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); @@ -67,6 +73,9 @@ export class IoSocketController{ return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})); } + // update position in the worl + this.World.updatePosition(messageUserPosition); + // sending to all clients in room except sender this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); @@ -135,8 +144,7 @@ export class IoSocketController{ //Hydrate and manage error hydrateMessageReceive(message : string) : MessageUserPosition | Error{ try { - let data = JSON.parse(message); - return new MessageUserPosition(data); + return new MessageUserPosition(JSON.parse(message)); }catch (err) { //TODO log error return new Error(err); @@ -180,4 +188,16 @@ export class IoSocketController{ this.shareUsersPosition(); }, 10); } + + //connected user + connectedUser(user1 : string, user2 : string){ + console.log("connectedUser => user1", user1); + console.log("connectedUser => user2", user2); + } + + //connected user + disConnectedUser(user1 : string, user2 : string){ + console.log("disConnectedUser => user1", user1); + console.log("disConnectedUser => user2", user2); + } } diff --git a/back/src/Model/Websocket/Message.ts b/back/src/Model/Websocket/Message.ts index d726968f..da265464 100644 --- a/back/src/Model/Websocket/Message.ts +++ b/back/src/Model/Websocket/Message.ts @@ -3,7 +3,7 @@ export class Message { roomId: string; constructor(data: any) { - if(!data.userId || !data.roomId){ + if (!data.userId || !data.roomId) { throw Error("userId or roomId cannot be null"); } this.userId = data.userId; @@ -13,7 +13,7 @@ export class Message { toJson() { return { userId: this.userId, - roomId: this.roomId, + roomId: this.roomId } } } \ No newline at end of file diff --git a/back/src/Model/Websocket/MessageUserPosition.ts b/back/src/Model/Websocket/MessageUserPosition.ts index 117c99cb..65f8f6c0 100644 --- a/back/src/Model/Websocket/MessageUserPosition.ts +++ b/back/src/Model/Websocket/MessageUserPosition.ts @@ -27,10 +27,9 @@ export class Point implements PointInterface{ export class MessageUserPosition extends Message{ position: PointInterface; - constructor(message: string) { + constructor(message: any) { super(message); - let data = JSON.parse(message); - this.position = new Point(data.position.x, data.position.y, data.position.direction); + this.position = new Point(message.position.x, message.position.y, message.position.direction); } toString() { From 4e556782af8999e2bf997a54b2dced5c0a4ab4f5 Mon Sep 17 00:00:00 2001 From: gparant Date: Wed, 29 Apr 2020 00:01:37 +0200 Subject: [PATCH 15/18] Fix integration login scene in game manager --- front/src/Phaser/Game/GameManager.ts | 24 ++++++++++++++++----- front/src/Phaser/Login/LogincScene.ts | 31 ++++++++++++++++++--------- front/src/index.ts | 2 +- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index fe04a950..52f08056 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -2,6 +2,7 @@ import {GameSceneInterface, GameScene} from "./GameScene"; import {ROOM} from "../../Enum/EnvironmentVariable" import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion"; import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; +import {LogincScene} from "../Login/LogincScene"; export enum StatusGameManagerEnum { IN_PROGRESS = 1, @@ -16,6 +17,7 @@ export interface GameManagerInterface { SimplePeer: SimplePeerInterface; createCurrentPlayer() : void; shareUserPosition(ListMessageUserPosition : ListMessageUserPositionInterface): void; + connect(email : string) : Promise; } export class GameManager implements GameManagerInterface { GameScenes: Array = []; @@ -27,15 +29,26 @@ export class GameManager implements GameManagerInterface { this.configureGame(); } - connect(email:string) { + /** + * + * @param email + */ + connect(email : string) : Promise { ConnexionInstance = new Connexion(email, this); - this.SimplePeer = new SimplePeer(ConnexionInstance); + return ConnexionInstance.createConnexion().then(() => { + this.SimplePeer = new SimplePeer(ConnexionInstance); + }); } /** * permit to config rooms */ configureGame() { + //create login scene + let LoginScene = new LogincScene(); + this.GameScenes.push(LoginScene) + + //create scene ROOM.forEach((roomId) => { let newGame = new GameScene(roomId, this); this.GameScenes.push((newGame as GameSceneInterface)); @@ -43,13 +56,14 @@ export class GameManager implements GameManagerInterface { } /** - * Permit to create player in started room - * @param RoomId - * @param UserId + * */ createCurrentPlayer(): void { //Get started room send by the backend let game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ConnexionInstance.startedRoom); + if(!game){ + return; + } game.createCurrentPlayer(ConnexionInstance.userId); this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; } diff --git a/front/src/Phaser/Login/LogincScene.ts b/front/src/Phaser/Login/LogincScene.ts index 0fb4d2a4..1aa1e0af 100644 --- a/front/src/Phaser/Login/LogincScene.ts +++ b/front/src/Phaser/Login/LogincScene.ts @@ -1,9 +1,9 @@ -import KeyboardKeydownCallback = Phaser.Types.Input.Keyboard.KeyboardKeydownCallback; import {gameManager} from "../Game/GameManager"; -import {ROOM} from "../../Enum/EnvironmentVariable"; import {TextField} from "../Components/TextField"; import {TextInput} from "../Components/TextInput"; import {ClickButton} from "../Components/ClickButton"; +import {GameSceneInterface} from "../Game/GameScene"; +import {MessageUserPositionInterface} from "../../Connexion"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -11,21 +11,22 @@ enum LoginTextures { playButton = "play_button", } -export class LogincScene extends Phaser.Scene { +export class LogincScene extends Phaser.Scene implements GameSceneInterface { private emailInput: TextInput; private textField: TextField; private playButton: ClickButton; private infoTextField: TextField; + constructor() { super({ key: LoginSceneName }); } - + preload() { this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); } - + create() { this.textField = new TextField(this, 10, 10, 'Enter your email:'); this.emailInput = new TextInput(this, 10, 50); @@ -37,15 +38,25 @@ export class LogincScene extends Phaser.Scene { let infoText = "Commandes de base: \n - Z,Q,S,D (ou les flèches de direction) pour bouger\n - SHIFT pour accélerer"; this.infoTextField = new TextField(this, 10, 300, infoText); } - + update(time: number, delta: number): void { - + } - + async login() { let email = this.emailInput.text; if (!email) return; - await gameManager.connect(email); - this.scene.start("GameScene"); + gameManager.connect(email).then(() => { + this.scene.start("GameScene"); + }); + } + + Map: Phaser.Tilemaps.Tilemap; + RoomId: string; + + createCurrentPlayer(UserId: string): void { + } + + shareUserPosition(UsersPosition: Array): void { } } \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index c55a5d0d..09fd9782 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -10,7 +10,7 @@ const config: GameConfig = { width: window.innerWidth / RESOLUTION, height: window.innerHeight / RESOLUTION, parent: "game", - scene: [LogincScene, ...gameManager.GameScenes as any], + scene: gameManager.GameScenes, zoom: RESOLUTION, physics: { default: "arcade", From 2bfa57b0bad274711bcd6c763c636881fe2c3415 Mon Sep 17 00:00:00 2001 From: gparant Date: Wed, 29 Apr 2020 01:40:32 +0200 Subject: [PATCH 16/18] Merge world and webrtc conexion --- back/src/App.ts | 2 +- back/src/Controller/IoSocketController.ts | 132 ++++++++++++------ back/src/Model/Websocket/ExSocketInterface.ts | 1 + back/src/Model/World.ts | 7 +- front/src/Connexion.ts | 7 - front/src/Phaser/Game/GameScene.ts | 1 - front/src/WebRtc/SimplePeer.ts | 41 +++--- 7 files changed, 123 insertions(+), 68 deletions(-) diff --git a/back/src/App.ts b/back/src/App.ts index 4b3b3c53..82ca317a 100644 --- a/back/src/App.ts +++ b/back/src/App.ts @@ -28,7 +28,7 @@ class App { private config(): void { this.app.use(bodyParser.json()); this.app.use(bodyParser.urlencoded({extended: false})); - this.app.use(function (req: Request, res: Response, next) { + this.app.use((req: Request, res: Response, next) => { res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 77f3ac9e..23a238e6 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -8,6 +8,17 @@ 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'; + +enum SockerIoEvent { + CONNECTION = "connection", + DISCONNECTION = "disconnect", + JOIN_ROOM = "join-room", + USER_POSITION = "user-position", + WEBRTC_SIGNAL = "webrtc-signal", + WEBRTC_START = "webrtc-start", + MESSAGE_ERROR = "message-error", +} export class IoSocketController{ Io: socketIO.Server; @@ -31,11 +42,17 @@ export class IoSocketController{ this.ioConnection(); this.shareUsersPosition(); - this.World = new World(this.connectedUser, this.disConnectedUser); + + //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); + }); } ioConnection() { - this.Io.on('connection', (socket: Socket) => { + this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => { /*join-rom event permit to join one room. message : userId : user identification @@ -44,10 +61,10 @@ export class IoSocketController{ x: user x position on map y: user y position on map */ - socket.on('join-room', (message : string) => { + socket.on(SockerIoEvent.JOIN_ROOM, (message : string) => { let messageUserPosition = this.hydrateMessageReceive(message); if(messageUserPosition instanceof Error){ - return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})) + return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message})) } //join user in room @@ -64,13 +81,13 @@ export class IoSocketController{ rooms.refreshUserPosition = RefreshUserPositionFunction; rooms.refreshUserPosition(rooms, this.Io); - socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString()); + socket.to(messageUserPosition.roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserPosition.toString()); }); - socket.on('user-position', (message : string) => { + socket.on(SockerIoEvent.USER_POSITION, (message : string) => { let messageUserPosition = this.hydrateMessageReceive(message); if (messageUserPosition instanceof Error) { - return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})); + return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message})); } // update position in the worl @@ -87,37 +104,7 @@ export class IoSocketController{ rooms.refreshUserPosition(rooms, this.Io); }); - socket.on('webrtc-room', (message : string) => { - let data = JSON.parse(message); - socket.join(data.roomId); - (socket as ExSocketInterface).roomId = data.roomId; - - //if two persone in room share - if(this.Io.sockets.adapter.rooms[data.roomId].length < 2) { - return; - } - let clients : Array = Object.values(this.Io.sockets.sockets); - - //send start at one client to initialise offer webrtc - //send all users in room to create PeerConnection in front - clients.forEach((client: ExSocketInterface, index : number) => { - - let clientsId = clients.reduce((tabs : Array, clientId: ExSocketInterface, indexClientId: number) => { - if(!clientId.userId || clientId.userId === client.userId){ - return tabs; - } - tabs.push({ - userId: clientId.userId, - initiator : index <= indexClientId - }); - return tabs; - }, []); - - client.emit('webrtc-start', JSON.stringify(clientsId)); - }); - }); - - socket.on('webrtc-signal', (message : string) => { + socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message : string) => { let data : any = JSON.parse(message); //send only at user @@ -127,10 +114,63 @@ export class IoSocketController{ if(client.userId !== data.receiverId){ continue } - client.emit('webrtc-signal', message); + client.emit(SockerIoEvent.WEBRTC_SIGNAL, message); break; } }); + + socket.on(SockerIoEvent.DISCONNECTION, (reason : string) => { + let Client = (socket as ExSocketInterface); + //leave group of user + this.World.leave(Client); + + //leave room + socket.leave(Client.roomId); + socket.leave(Client.webRtcRoomId); + + //delete all socket information + delete Client.userId; + delete Client.webRtcRoomId; + delete Client.roomId; + delete Client.token; + delete Client.position; + }); + }); + } + + /** + * + * @param socket + * @param roomId + */ + joinWebRtcRoom(socket : ExSocketInterface, roomId : string) { + if(socket.webRtcRoomId === roomId){ + return; + } + socket.join(roomId); + socket.webRtcRoomId = roomId; + //if two persone in room share + if (this.Io.sockets.adapter.rooms[roomId].length < 2) { + return; + } + let clients: Array = Object.values(this.Io.sockets.sockets); + + //send start at one client to initialise offer webrtc + //send all users in room to create PeerConnection in front + clients.forEach((client: ExSocketInterface, index: number) => { + + let clientsId = clients.reduce((tabs: Array, clientId: ExSocketInterface, indexClientId: number) => { + if (!clientId.userId || clientId.userId === client.userId) { + return tabs; + } + tabs.push({ + userId: clientId.userId, + initiator: index <= indexClientId + }); + return tabs; + }, []); + + client.emit(SockerIoEvent.WEBRTC_START, JSON.stringify({clients: clientsId, roomId: roomId})); }); } @@ -191,8 +231,18 @@ export class IoSocketController{ //connected user connectedUser(user1 : string, user2 : string){ - console.log("connectedUser => user1", user1); - console.log("connectedUser => user2", user2); + /* 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); + } + if(User2) { + this.joinWebRtcRoom(User2, roomId); + } } //connected user diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts index 095d3cbc..4d1d9fee 100644 --- a/back/src/Model/Websocket/ExSocketInterface.ts +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -4,6 +4,7 @@ import {PointInterface} from "./PointInterface"; export interface ExSocketInterface extends Socket { token: any; roomId: string; + webRtcRoomId: string; userId: string; position: PointInterface; } \ No newline at end of file diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index 804a176b..ff1f58ff 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -3,6 +3,7 @@ import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; import {Distance} from "./Distance"; import {UserInterface} from "./UserInterface"; +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; export class World { static readonly MIN_DISTANCE = 160; @@ -29,8 +30,12 @@ export class World { }); } + public leave(user : ExSocketInterface){ + /*TODO leaver user in group*/ + this.users.delete(user.userId); + } + public updatePosition(userPosition: MessageUserPosition): void { - let context = this; let user = this.users.get(userPosition.userId); if(typeof user === 'undefined') { return; diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index a9c10df5..77c9619f 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -7,7 +7,6 @@ import {API_URL} from "./Enum/EnvironmentVariable"; enum EventMessage{ WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_START = "webrtc-start", - WEBRTC_ROOM = "webrtc-room", JOIN_ROOM = "join-room", USER_POSITION = "user-position", MESSAGE_ERROR = "message-error" @@ -127,8 +126,6 @@ export interface ConnexionInterface { positionOfAllUser(): void; /*webrtc*/ - sendWebrtcRomm(roomId: string): void; - sendWebrtcSignal(signal: any, roomId: string, userId?: string, receiverId?: string): void; receiveWebrtcSignal(callBack: Function): void; @@ -239,10 +236,6 @@ export class Connexion implements ConnexionInterface { })); } - sendWebrtcRomm(roomId: string) { - this.socket.emit(EventMessage.WEBRTC_ROOM, JSON.stringify({roomId: roomId})); - } - receiveWebrtcStart(callback: Function) { this.socket.on(EventMessage.WEBRTC_START, callback); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e996d140..b08db791 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -258,7 +258,6 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //init colision this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => { CurrentPlayer.say("Hello, how are you ? "); - this.GameManager.SimplePeer.activePhone(); }); } } diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index a53fd754..b04511d7 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -3,14 +3,13 @@ import {MediaManager} from "./MediaManager"; let Peer = require('simple-peer'); export interface SimplePeerInterface { - activePhone(): void; - disablePhone(): void; } export class SimplePeer { Connexion: ConnexionInterface; MediaManager: MediaManager; RoomId: string; + Users: Array; PeerConnexionArray: Array = new Array(); @@ -18,12 +17,25 @@ export class SimplePeer { this.Connexion = Connexion; this.RoomId = roomId; this.MediaManager = new MediaManager(); + 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(); }); } - /** * server has two person connected, start the meet */ @@ -31,13 +43,9 @@ export class SimplePeer { this.MediaManager.activeVisio(); return this.MediaManager.getCamera().then((stream: MediaStream) => { this.MediaManager.localStream = stream; - //send message to join a room - this.Connexion.sendWebrtcRomm(this.RoomId); - //receive message start - this.Connexion.receiveWebrtcStart((message: string) => { - this.receiveWebrtcStart(message); - }); + //create pear connexion + this.createPeerConnexion(); //receive signal by gemer this.Connexion.receiveWebrtcSignal((message: string) => { @@ -54,17 +62,16 @@ export class SimplePeer { */ receiveWebrtcStart(message: string) { let data = JSON.parse(message); + this.RoomId = data.roomId; + this.Users = data.clients; - //create pear connexion of user stared - this.createPeerConnexion(data); + //active button for player + this.activePhone(); } - /** - * - * @param users - */ - createPeerConnexion(users : Array) { - users.forEach((user: any) => { + + createPeerConnexion() { + this.Users.forEach((user: any) => { if(this.PeerConnexionArray[user.userId]){ return; } From 58df4118d1db8cd037b6090b4fbe2fbb742b2ba5 Mon Sep 17 00:00:00 2001 From: gparant Date: Wed, 29 Apr 2020 17:49:40 +0200 Subject: [PATCH 17/18] Refactor --- front/src/WebRtc/SimplePeer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index b04511d7..3c94bbaf 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -8,14 +8,14 @@ export interface SimplePeerInterface { export class SimplePeer { Connexion: ConnexionInterface; MediaManager: MediaManager; - RoomId: string; + WebRtcRoomId: string; Users: Array; PeerConnexionArray: Array = new Array(); - constructor(Connexion: ConnexionInterface, roomId: string = "test-webrtc") { + constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") { this.Connexion = Connexion; - this.RoomId = roomId; + this.WebRtcRoomId = WebRtcRoomId; this.MediaManager = new MediaManager(); this.initialise(); } @@ -62,7 +62,7 @@ export class SimplePeer { */ receiveWebrtcStart(message: string) { let data = JSON.parse(message); - this.RoomId = data.roomId; + this.WebRtcRoomId = data.roomId; this.Users = data.clients; //active button for player @@ -108,7 +108,7 @@ export class SimplePeer { * @param data */ sendWebrtcSignal(data: any, userId : string) { - this.Connexion.sendWebrtcSignal(data, this.RoomId, null, userId); + this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId); } /** From dbf0bef14975f3d1f0739bd9f79aaa8d5ed72bec Mon Sep 17 00:00:00 2001 From: gparant Date: Wed, 29 Apr 2020 17:55:55 +0200 Subject: [PATCH 18/18] Delete file --- front/src/WebRtc/Index.ts | 183 -------------------------------------- 1 file changed, 183 deletions(-) delete mode 100644 front/src/WebRtc/Index.ts diff --git a/front/src/WebRtc/Index.ts b/front/src/WebRtc/Index.ts deleted file mode 100644 index cc636bb8..00000000 --- a/front/src/WebRtc/Index.ts +++ /dev/null @@ -1,183 +0,0 @@ -/*import {ConnexionInterface} from "../Connexion"; - -const Peer = require('simple-peer'); - -let cinemaClose : any = null; -let cinema : any = null; -let microphoneClose : any = null; -let microphone : any = null; - -let localStream : MediaStream = null; -let remoteStream : MediaStream = null; -let remoteVideo : any = null; -let myCamVideo : any = null; - -let promiseGetCam : Promise = null; - -let peer : any = null; - -let Connexion : ConnexionInterface = null; - -let roomId = "test-wertc"; - -let gettingCamera : Promise = null; -let constraintsMedia = {audio: true, video: true}; - -function joinRoom(){ - Connexion.JoinRoomWebRtc(roomId); - Connexion.startRoomWebRtc(initialiseWebSocket) -} - -function initialiseWebSocket(message : any){ - console.log('initialiseWebSocket => message', message); - peer = new Peer({ - initiator: message.initiator - }); - - peer.on('signal', (data : any) => { - //send signal - //permit to send message and initialise peer connexion - console.log('signal sended', data); - Connexion.shareSignalWebRtc({ - roomId: roomId, - signal: data - }); - }); - - //permit to receive message and initialise peer connexion - Connexion.receiveSignalWebRtc((data : any) => { - let signal = JSON.parse(data); - console.log('receiveSignalWebRtc => signal', signal); - peer.signal(signal.signal); - }); - - peer.on('stream', (stream : MediaStream) => { - // got remote video stream, now let's show it in a video tag - console.log("peer => stream", stream); - - //set local stream in little cam - myCamVideo.srcObject = localStream; - - //set remote stream in remote video - remoteStream = stream; - remoteVideo.srcObject = stream; - }); - - peer.on('connect', () => { - console.log('CONNECT') - peer.send('whatever' + Math.random()) - }); - - peer.on('data', (data : any) => { - console.log('data: ' + data) - }); - - peer.on('close', (err : any) => console.error('close', err)); - peer.on('error', (err : any) => console.error('error', err)); - - - peer.on('track', (track : any, stream : any) => { - remoteStream = stream; - remoteVideo.srcObject = stream; - track.onended = (e : any) => remoteVideo.srcObject = remoteVideo.srcObject; // Chrome/Firefox bug - }); - - gettingCamera.then(() => { - addMedia(); - }); -} - -//get camera -function getCamera() { - gettingCamera = navigator.mediaDevices.getUserMedia(constraintsMedia) - .then((stream: MediaStream) => { - localStream = stream; - remoteVideo.srcObject = stream; - }).catch((err) => { - console.error(err); - localStream = null; - throw err; - }); - return gettingCamera; -} - -function addMedia () { - if(peer) { - peer.addStream(localStream) // <- add streams to peer dynamically - } -} - -function enabledCamera(){ - cinemaClose.style.display = "none"; - cinema.style.display = "block"; - constraintsMedia.video = true; -} - -function disabledCamera(){ - cinemaClose.style.display = "block"; - cinema.style.display = "none"; - constraintsMedia.video = false; -} - -function enabledMicrophone(){ - microphoneClose.style.display = "none"; - microphone.style.display = "block"; - constraintsMedia.audio = true; -} - -function disabledMicrophone(){ - microphoneClose.style.display = "block"; - microphone.style.display = "none"; - constraintsMedia.audio = false; -} - -function showWebRtc(){ - remoteVideo = document.getElementById('activeCamVideo'); - myCamVideo = document.getElementById('myCamVideo'); - - microphoneClose = document.getElementById('microphone-close'); - microphoneClose.addEventListener('click', (e : any) => { - e.preventDefault(); - enabledMicrophone(); - //update tracking - }); - - microphone = document.getElementById('microphone'); - microphone.addEventListener('click', (e : any) => { - e.preventDefault(); - disabledMicrophone(); - //update tracking - }); - - cinemaClose = document.getElementById('cinema-close'); - cinemaClose.addEventListener('click', (e : any) => { - e.preventDefault(); - enabledCamera(); - //update tracking - }); - cinema = document.getElementById('cinema'); - cinema.addEventListener('click', (e : any) => { - e.preventDefault(); - disabledCamera(); - //update tracking - }); - - enabledMicrophone(); - enabledCamera(); - - let webRtc = document.getElementById('webRtc'); - webRtc.classList.add('active'); -} - -export const initialisation = (ConnexionInterface : ConnexionInterface) => { - Connexion = ConnexionInterface; - - //show camera - showWebRtc(); - - //open the camera - getCamera(); - - //join room to create webrtc - joinRoom(); -};*/ \ No newline at end of file