Media webrtcA
authorgparant <g.parant@thecodingmachine.com>
Thu, 14 May 2020 18:39:30 +0000 (20:39 +0200)
committergparant <g.parant@thecodingmachine.com>
Thu, 14 May 2020 18:39:30 +0000 (20:39 +0200)
 - Update peerConnexion manage
 - Add muted microphone logo
 - Add icon user
 - Sound when user enter in room webrtc

back/src/Controller/IoSocketController.ts
front/dist/index.html
front/dist/resources/objects/webrtc-in.mp3 [new file with mode: 0644]
front/dist/resources/style/style.css
front/src/Phaser/Game/GameManager.ts
front/src/WebRtc/MediaManager.ts
front/src/WebRtc/SimplePeer.ts

index 60bbe6ad7e0426acdeedac55f09f190fc5634dd2..a2ffc81580e67696a29d7f33cec5675306acba9b 100644 (file)
@@ -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;
index 0815a8827fbd718d162ebdb2969bfb6f3a9a4f12..60ced6ec974efe2a4e7cedba760b2be9bd4ae6eb 100644 (file)
@@ -13,7 +13,9 @@
         <script src="bundle.js"></script>
         <div id="webRtc" class="webrtc">
             <div id="activeCam" class="activeCam">
-                <video id="myCamVideo" autoplay muted></video>
+                <div id="div-myCamVideo" class="video-container">
+                    <video id="myCamVideo" autoplay muted></video>
+                </div>
             </div>
             <div class="btn-cam-action">
                 <div class="btn-micro">
@@ -29,5 +31,8 @@
                 </div>-->
             </div>
         </div>
+        <audio id="audio-webrtc-in">
+            <source src="/resources/objects/webrtc-in.mp3" type="audio/mp3">
+        </audio>
     </body>
 </html>
diff --git a/front/dist/resources/objects/webrtc-in.mp3 b/front/dist/resources/objects/webrtc-in.mp3
new file mode 100644 (file)
index 0000000..34e2200
Binary files /dev/null and b/front/dist/resources/objects/webrtc-in.mp3 differ
index 8c5975a0dcfe8116e7984715166dfd7080fb11da..3f3c2c625d49eda47ee11bb9cc1d824bf91342eb 100644 (file)
@@ -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;
+}
+.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;
 }
-.webrtc:hover .activeCam video{
+.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%;
 }
 
index 78436429aa62900625f8b7998ae70452ad4225af..6a4381ddf9d9c7894887caa767f72027e1dbdf70 100644 (file)
@@ -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 {
index 1dabf2c6263925dcfef90aa9016a581bfb70b684..3bfc2873ce53b2ec172bf1082f849a45c1bc661a 100644 (file)
@@ -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', '<video id="'+userId+'" autoplay></video>');
+        userName = userName.toUpperCase();
+        let color = this.getColorByString(userName);
+        elementRemoteVideo.insertAdjacentHTML('beforeend', `
+            <div id="div-${userId}" class="video-container" style="border-color: ${color};">
+                <i style="background-color: ${color};">${userName}</i>
+                <img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
+                <video id="${userId}" autoplay></video>
+            </div>
+        `);
         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
index 3bff958033ee04087fb9a214893f960b6893410b..a4be6bcac4e46c12bbec5c0fb3a87a505c459bfb 100644 (file)
@@ -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<any>;
+    private Users: Array<UserSimplePear> = new Array<UserSimplePear>();
 
     private MediaManager: MediaManager;
 
-    private PeerConnexionArray: Array<any> = new Array<any>();
+    private PeerConnexionArray: Map<string, any> = new Map<string, any>();
 
     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<any>();
-
         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);
         })
     }