diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 60bbe6ad..a2ffc815 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -99,7 +99,7 @@ export class IoSocketController { let Client = (socket as ExSocketInterface); - if(Client.roomId === messageUserPosition.roomId){ + if (Client.roomId === messageUserPosition.roomId) { return; } @@ -140,7 +140,7 @@ export class IoSocketController { console.error('An error occurred on "user_position" event'); console.error(e); } - }); + }); socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message: string) => { let data: any = JSON.parse(message); @@ -316,6 +316,7 @@ export class IoSocketController { } tabs.push({ userId: clientId.userId, + name: clientId.name, initiator: index <= indexClientId }); return tabs; diff --git a/front/dist/index.html b/front/dist/index.html index 0815a882..60ced6ec 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -13,7 +13,9 @@
- +
+ +
@@ -29,5 +31,8 @@
-->
+ diff --git a/front/dist/resources/objects/webrtc-in.mp3 b/front/dist/resources/objects/webrtc-in.mp3 new file mode 100644 index 00000000..34e22003 Binary files /dev/null and b/front/dist/resources/objects/webrtc-in.mp3 differ diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 8c5975a0..3f3c2c62 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -37,35 +37,74 @@ video{ .webrtc.active{ display: block; } + .webrtc, .activeCam{} -.activeCam video{ +.activeCam .video-container{ position: absolute; height: 25%; top: 10px; margin: 5px; right: -100px; transition: all 0.2s ease; + border-color: black; + border-style: solid; + border-width: 0.2px; } -.webrtc:hover .activeCam video{ +.activeCam .video-container i{ + position: absolute; + width: 100px; + height: 65px; + left: calc(50% - 50px); + top: calc(50% - 50px); + background-color: black; + border-radius: 50%; + text-align: center; + padding-top: 35px; + font-size: 28px; + color: white; +} +.activeCam .video-container img.active{ + display: block; +} +.activeCam .video-container img{ + position: absolute; + display: none; + width: 15px; + height: 15px; + background: #d93025; + border-radius: 48px; + left: 5px; + bottom: 5px; + padding: 10px; + z-index: 2; +} +.activeCam .video-container video{ + height: 100%; +} + +.webrtc:hover .activeCam .video-container{ right: 10px; } -.activeCam video#myCamVideo{ +.activeCam .video-container#div-myCamVideo{ + border: none; +} +.activeCam .video-container video#myCamVideo{ width: 200px; height: 113px; } /*CSS size for 2 - 3 elements*/ -.activeCam video:nth-child(1){ +.activeCam .video-container:nth-child(1){ /*this is for camera of user*/ top: 75%; } -.activeCam video:nth-child(2){ +.activeCam .video-container:nth-child(2){ top: 0%; } -.activeCam video:nth-child(3){ +.activeCam .video-container:nth-child(3){ top: 25%; } -.activeCam video:nth-child(4) { +.activeCam .video-container:nth-child(4) { top: 50%; } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 78436429..6a4381dd 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -6,7 +6,6 @@ import { } from "../../Connexion"; import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; import {getMapKeyByUrl} from "../Login/LogincScene"; -import SceneManager = Phaser.Scenes.SceneManager; import ScenePlugin = Phaser.Scenes.ScenePlugin; export enum StatusGameManagerEnum { diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 1dabf2c6..3bfc2873 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -11,6 +11,7 @@ export class MediaManager { cinema: any = null; microphoneClose: any = null; microphone: any = null; + webrtcInAudio: any; constraintsMedia : {audio : any, video : any} = { audio: true, video: videoConstraint @@ -22,6 +23,8 @@ export class MediaManager { this.updatedLocalStreamCallBack = updatedLocalStreamCallBack; this.myCamVideo = document.getElementById('myCamVideo'); + this.webrtcInAudio = document.getElementById('audio-webrtc-in'); + this.webrtcInAudio.volume = 0.2; this.microphoneClose = document.getElementById('microphone-close'); this.microphoneClose.style.display = "none"; @@ -61,8 +64,6 @@ export class MediaManager { this.cinemaClose.style.display = "none"; this.cinema.style.display = "block"; this.constraintsMedia.video = videoConstraint; - this.localStream = null; - this.myCamVideo.srcObject = null; this.getCamera().then((stream) => { this.updatedLocalStreamCallBack(stream); }); @@ -72,17 +73,12 @@ export class MediaManager { 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.myCamVideo.srcObject = null; + if (this.localStream) { + this.localStream.getVideoTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { + MediaStreamTrack.stop(); }); } - this.localStream = null; - this.myCamVideo.srcObject = null; this.getCamera().then((stream) => { this.updatedLocalStreamCallBack(stream); }); @@ -102,10 +98,8 @@ export class MediaManager { 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.localStream.getAudioTracks().forEach((MediaStreamTrack: MediaStreamTrack) => { + MediaStreamTrack.stop(); }); } this.getCamera().then((stream) => { @@ -130,9 +124,8 @@ export class MediaManager { return stream; }).catch((err) => { - console.error(err); + console.info(`error get media {video: ${this.constraintsMedia.video}},{audio: ${this.constraintsMedia.audio}}`,err); this.localStream = null; - throw err; }); } catch (e) { promise = Promise.reject(false); @@ -144,12 +137,69 @@ export class MediaManager { * * @param userId */ - addActiveVideo(userId : string){ + addActiveVideo(userId : string, userName: string = ""){ + this.webrtcInAudio.play(); let elementRemoteVideo = document.getElementById("activeCam"); - elementRemoteVideo.insertAdjacentHTML('beforeend', ''); + userName = userName.toUpperCase(); + let color = this.getColorByString(userName); + elementRemoteVideo.insertAdjacentHTML('beforeend', ` +
+ ${userName} + + +
+ `); this.remoteVideo[(userId as any)] = document.getElementById(userId); } + /** + * + * @param userId + */ + disabledMicrophoneByUserId(userId: string){ + let element = document.getElementById(`microphone-${userId}`); + if(!element){ + return; + } + element.classList.add('active') + } + + /** + * + * @param userId + */ + enabledMicrophoneByUserId(userId: string){ + let element = document.getElementById(`microphone-${userId}`); + if(!element){ + return; + } + element.classList.remove('active') + } + + /** + * + * @param userId + */ + disabledVideoByUserId(userId: string){ + let element = document.getElementById(`${userId}`); + if(!element){ + return; + } + element.style.opacity = "0"; + } + + /** + * + * @param userId + */ + enabledVideoByUserId(userId: string){ + let element = document.getElementById(`${userId}`); + if(!element){ + return; + } + element.style.opacity = "1"; + } + /** * * @param userId @@ -164,10 +214,29 @@ export class MediaManager { * @param userId */ removeActiveVideo(userId : string){ - let element = document.getElementById(userId); + let element = document.getElementById(`div-${userId}`); if(!element){ return; } element.remove(); } + + /** + * + * @param str + */ + private getColorByString(str: String) : String|null { + let hash = 0; + if (str.length === 0) return null; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + hash = hash & hash; + } + let color = '#'; + for (let i = 0; i < 3; i++) { + let value = (hash >> (i * 8)) & 255; + color += ('00' + value.toString(16)).substr(-2); + } + return color; + } } \ No newline at end of file diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 3bff9580..a4be6bca 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -2,17 +2,20 @@ import {ConnexionInterface} from "../Connexion"; import {MediaManager} from "./MediaManager"; let Peer = require('simple-peer'); -export interface SimplePeerInterface { +class UserSimplePear{ + userId: string; + name?: string; + initiator?: boolean; } - -export class SimplePeer { +export class SimplePeerInterface {} +export class SimplePeer implements SimplePeerInterface{ private Connexion: ConnexionInterface; private WebRtcRoomId: string; - private Users: Array; + private Users: Array = new Array(); private MediaManager: MediaManager; - private PeerConnexionArray: Array = new Array(); + private PeerConnexionArray: Map = new Map(); constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") { this.Connexion = Connexion; @@ -20,8 +23,6 @@ export class SimplePeer { this.MediaManager = new MediaManager((stream : MediaStream) => { this.updatedLocalStream(); }); - this.PeerConnexionArray = new Array(); - this.initialise(); } @@ -71,7 +72,7 @@ export class SimplePeer { * server has two person connected, start the meet */ private startWebRtc() { - this.Users.forEach((user: any) => { + this.Users.forEach((user: UserSimplePear) => { //if it's not an initiator, peer connexion will be created when gamer will receive offer signal if(!user.initiator){ return; @@ -83,15 +84,23 @@ export class SimplePeer { /** * create peer connexion to bind users */ - private createPeerConnexion(user : any) { - if(this.PeerConnexionArray[user.userId]) { + private createPeerConnexion(user : UserSimplePear) { + if(this.PeerConnexionArray.has(user.userId)) { return; } + let name = user.name; + if(!name){ + let userSearch = this.Users.find((userSearch: UserSimplePear) => userSearch.userId === user.userId); + if(userSearch) { + name = userSearch.name; + } + } this.MediaManager.removeActiveVideo(user.userId); - this.MediaManager.addActiveVideo(user.userId); + console.log("name", name); + this.MediaManager.addActiveVideo(user.userId, name); - this.PeerConnexionArray[user.userId] = new Peer({ + let peer : any = new Peer({ initiator: user.initiator ? user.initiator : false, reconnectTimer: 10000, config: { @@ -107,29 +116,51 @@ export class SimplePeer { ] }, }); + this.PeerConnexionArray.set(user.userId, peer); //start listen signal for the peer connexion - this.PeerConnexionArray[user.userId].on('signal', (data: any) => { + this.PeerConnexionArray.get(user.userId).on('signal', (data: any) => { this.sendWebrtcSignal(data, user.userId); }); - this.PeerConnexionArray[user.userId].on('stream', (stream: MediaStream) => { + this.PeerConnexionArray.get(user.userId).on('stream', (stream: MediaStream) => { + let videoActive = false; + let microphoneActive = false; + stream.getTracks().forEach((track : MediaStreamTrack) => { + if(track.kind === "audio"){ + microphoneActive = true; + } + if(track.kind === "video"){ + videoActive = true; + } + }); + if(microphoneActive){ + this.MediaManager.enabledMicrophoneByUserId(user.userId); + }else{ + this.MediaManager.disabledMicrophoneByUserId(user.userId); + } + + if(videoActive){ + this.MediaManager.enabledVideoByUserId(user.userId); + }else{ + this.MediaManager.disabledVideoByUserId(user.userId); + } this.stream(user.userId, stream); }); - this.PeerConnexionArray[user.userId].on('track', (track: MediaStreamTrack, stream: MediaStream) => { + this.PeerConnexionArray.get(user.userId).on('track', (track: MediaStreamTrack, stream: MediaStream) => { this.stream(user.userId, stream); }); - this.PeerConnexionArray[user.userId].on('close', () => { + this.PeerConnexionArray.get(user.userId).on('close', () => { this.closeConnexion(user.userId); }); - this.PeerConnexionArray[user.userId].on('error', (err: any) => { + this.PeerConnexionArray.get(user.userId).on('error', (err: any) => { console.error(`error => ${user.userId} => ${err.code}`, err); }); - this.PeerConnexionArray[user.userId].on('connect', () => { + this.PeerConnexionArray.get(user.userId).on('connect', () => { console.info(`connect => ${user.userId}`); }); @@ -139,13 +170,12 @@ export class SimplePeer { private closeConnexion(userId : string) { try { this.MediaManager.removeActiveVideo(userId); - if (!this.PeerConnexionArray[(userId as any)]) { + if (!this.PeerConnexionArray.get(userId)) { return; } // @ts-ignore - this.PeerConnexionArray[(userId as any)].destroy(); - this.PeerConnexionArray[(userId as any)] = null; - delete this.PeerConnexionArray[(userId as any)]; + this.PeerConnexionArray.get(userId).destroy(); + this.PeerConnexionArray.delete(userId) } catch (err) { console.error("closeConnexion", err) } @@ -175,7 +205,7 @@ export class SimplePeer { if(data.signal.type === "offer"){ this.createPeerConnexion(data); } - this.PeerConnexionArray[data.userId].signal(data.signal); + this.PeerConnexionArray.get(data.userId).signal(data.signal); } catch (e) { console.error(`receiveWebrtcSignal => ${data.userId}`, e); } @@ -197,8 +227,11 @@ export class SimplePeer { private addMedia (userId : any = null) { try { let transceiver : any = null; + if(!this.MediaManager.localStream){ + return; + } this.MediaManager.localStream.getTracks().forEach( - transceiver = (track: MediaStreamTrack) => this.PeerConnexionArray[userId].addTrack(track, this.MediaManager.localStream) + transceiver = (track: MediaStreamTrack) => this.PeerConnexionArray.get(userId).addTrack(track, this.MediaManager.localStream) ) }catch (e) { console.error(`addMedia => addMedia => ${userId}`, e); @@ -206,7 +239,7 @@ export class SimplePeer { } updatedLocalStream(){ - this.Users.forEach((user) => { + this.Users.forEach((user: UserSimplePear) => { this.addMedia(user.userId); }) }