Create webrtc connexion between two player
authorgparant <g.parant@thecodingmachine.com>
Sun, 19 Apr 2020 17:32:38 +0000 (19:32 +0200)
committergparant <g.parant@thecodingmachine.com>
Sun, 19 Apr 2020 17:32:38 +0000 (19:32 +0200)
13 files changed:
back/src/Controller/IoSocketController.ts
front/dist/index.html
front/dist/resources/logos/cinema-close.svg [new file with mode: 0644]
front/dist/resources/logos/cinema.svg [new file with mode: 0644]
front/dist/resources/logos/microphone-close.svg [new file with mode: 0644]
front/dist/resources/logos/microphone.svg [new file with mode: 0644]
front/dist/resources/logos/phone.svg [new file with mode: 0644]
front/dist/resources/style/style.css [new file with mode: 0644]
front/src/Phaser/Game/GameManager.ts
front/src/WebRtc/Index.ts [new file with mode: 0644]
front/src/WebRtc/MediaManager.ts [new file with mode: 0644]
front/src/WebRtc/PeerConnexionManager.ts [new file with mode: 0644]
front/src/WebRtc/WebRtcEventManager.ts [new file with mode: 0644]

index 1fc114a49083ce3631dd0360c82812f1afade598..88af801ad20d354a64f8ccbaf6b7d2249acf31d9 100644 (file)
@@ -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<any> = 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);
+            });
         });
     }
 
index 61213be67e0f46d82e3bd1da951b38fde90606a3..656c903b00464c5d36b30dea51669cfcbeca0d6e 100644 (file)
@@ -1,11 +1,35 @@
 <!doctype html>
 <html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport"
-          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
-    <meta http-equiv="X-UA-Compatible" content="ie=edge">
-    <title>Document</title>
-</head>
-<body style="margin: 0"><script src="bundle.js"></script></body>
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport"
+              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+        <meta http-equiv="X-UA-Compatible" content="ie=edge">
+        <link rel="stylesheet" href="/resources/style/style.css">
+        <title>Document</title>
+    </head>
+    <body style="margin: 0">
+        <script src="bundle.js"></script>
+        <div id="webRtc" class="webrtc">
+            <div class="activeCam">
+                <video id="activeCamVideo" autoplay></video>
+            </div>
+            <div id="myCam" class="myCam active">
+                <video id="myCamVideo" autoplay></video>
+            </div>
+            <div class="btn-cam-action active">
+                <div class="btn-micro">
+                    <img id="microphone" src="resources/logos/microphone.svg">
+                    <img id="microphone-close" src="resources/logos/microphone-close.svg">
+                </div>
+                <div class="btn-video">
+                    <img id="cinema" src="resources/logos/cinema.svg">
+                    <img id="cinema-close" src="resources/logos/cinema-close.svg">
+                </div>
+                <div class="btn-call">
+                    <img src="resources/logos/phone.svg">
+                </div>
+            </div>
+        </div>
+    </body>
 </html>
diff --git a/front/dist/resources/logos/cinema-close.svg b/front/dist/resources/logos/cinema-close.svg
new file mode 100644 (file)
index 0000000..aa1d9b1
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 332.8 332.8" style="enable-background:new 0 0 332.8 332.8;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+</style>
+<g>
+       <g>
+               <g>
+                       <path class="st0" d="M330.8,171c-3.6-6.4-12-8.8-18.8-4.8l-45.6,26.4l-11.6,6.8v63.2l10.8,6.4c0.4,0,0.4,0.4,0.8,0.4l44.8,26
+                               c2,1.6,4.8,2.4,7.6,2.4c7.6,0,13.6-6,13.6-13.6v-53.6l0.4-52.8C332.8,175.4,332.4,173,330.8,171z"/>
+                       <path class="st0" d="M193.2,150.6c35.6,0,64.4-28.8,64.4-64.4s-28.8-64.4-64.4-64.4s-64.4,28.8-64.4,64.4
+                               C128.8,121.8,157.6,150.6,193.2,150.6z M193.2,59.8c14.8,0,26.4,12,26.4,26.4c0,14.8-12,26.4-26.4,26.4s-26.4-12-26.4-26.4
+                               C166.8,71.4,178.4,59.8,193.2,59.8z"/>
+               </g>
+       </g>
+</g>
+<g>
+       <g>
+       </g>
+</g>
+<rect x="134.8" y="-45.3" transform="matrix(-0.7402 0.6723 -0.6723 -0.7402 376.0669 224.8258)" class="st0" width="19.6" height="460.7"/>
+<path class="st0" d="M90.6,83.3c-0.2-2.2-1.3-8.9-6.7-14.9c-5.4-5.9-11.9-7.6-14.1-8.1C59.7,49.2,49.5,38,39.4,26.8
+       c24.3-9.8,52-4.4,70.2,13.6c19.9,19.7,24.7,50.8,11.5,76.4C110.9,105.6,100.8,94.5,90.6,83.3z"/>
+<path class="st0" d="M10.1,51.6c9.4,10.2,18.8,20.4,28.2,30.6c-0.2,1.8-1.4,11.7,5.5,20.5c8.2,10.3,20.7,10.2,22.1,10.1
+       c9.2,10.3,18.5,20.6,27.7,30.8c-4.8,2.3-24.6,11.2-48.3,4.1c-6-1.8-20.7-7.3-32.1-22C-0.3,108.1-0.2,89.1,0.1,83.4
+       C0.8,68,6.8,56.8,10.1,51.6z"/>
+<g>
+       <path class="st0" d="M243.4,178.2c0.1,24.5,0.2,49,0.2,73.5c-30.7-33.8-61.3-67.7-92-101.5c5.9,3.9,20.9,12.4,41.6,12.4
+               c16,0,28.2-5.2,34.4-8.4c2.5,1.5,7,4.6,10.7,10.3C242,170,243,175.4,243.4,178.2z"/>
+       <g>
+               <path class="st0" d="M211.2,311C150.8,258.7,90.4,206.5,30,154.2c6.1,3.1,18.2,8.4,34.4,8.4c18.1,0,31.5-6.5,37.5-9.9
+                       c44.5,49,89.1,98.1,133.6,147.1c-1.8,2.1-5.3,5.5-10.6,8.1C219.2,310.6,214,311,211.2,311z"/>
+               <path class="st0" d="M46.8,311C36,267.7,25.2,224.3,14.4,181c0.1-3.2,0.7-11.3,6.5-18.8c3.1-4.1,6.7-6.6,9.1-8
+                       C90.4,206.5,150.8,258.7,211.2,311C156.4,311,101.6,311,46.8,311z"/>
+               <path class="st0" d="M14.4,278.6L14.4,278.6c0-32.5,0-65.1,0-97.6c10.8,43.3,21.6,86.7,32.4,130c-2.6,0-12.7-0.4-21.5-8.1
+                       C14.7,293.5,14.4,280.7,14.4,278.6z"/>
+       </g>
+</g>
+</svg>
diff --git a/front/dist/resources/logos/cinema.svg b/front/dist/resources/logos/cinema.svg
new file mode 100644 (file)
index 0000000..1167d09
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 332.8 332.8" style="enable-background:new 0 0 332.8 332.8;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+</style>
+<g>
+       <g>
+               <g>
+                       <path class="st0" d="M330.8,171c-3.6-6.4-12-8.8-18.8-4.8l-45.6,26.4l-11.6,6.8v63.2l10.8,6.4c0.4,0,0.4,0.4,0.8,0.4l44.8,26
+                               c2,1.6,4.8,2.4,7.6,2.4c7.6,0,13.6-6,13.6-13.6v-53.6l0.4-52.8C332.8,175.4,332.4,173,330.8,171z"/>
+                       <path class="st0" d="M64.4,150.6c35.6,0,64.4-28.8,64.4-64.4S100,21.8,64.4,21.8S0,50.6,0,86.2C-0.4,121.8,28.8,150.6,64.4,150.6
+                               z M64.4,59.8c14.8,0,26.4,12,26.4,26.4c0,14.8-12,26.4-26.4,26.4S38,100.6,38,86.2C37.6,71.4,49.6,59.8,64.4,59.8z"/>
+                       <path class="st0" d="M227.6,154.2c-10.4,5.2-22,8.4-34.4,8.4c-15.2,0-29.6-4.4-41.6-12.4H106c-12,8-26.4,12.4-41.6,12.4
+                               c-12.4,0-24-2.8-34.4-8.4c-9.2,5.2-15.6,15.6-15.6,26.8v97.6c0,18,14.8,32.4,32.4,32.4h164.4c18,0,32.4-14.8,32.4-32.4V181
+                               C243.2,169.8,236.8,159.4,227.6,154.2z"/>
+                       <path class="st0" d="M193.2,150.6c35.6,0,64.4-28.8,64.4-64.4s-28.8-64.4-64.4-64.4s-64.4,28.8-64.4,64.4
+                               C128.8,121.8,157.6,150.6,193.2,150.6z M193.2,59.8c14.8,0,26.4,12,26.4,26.4c0,14.8-12,26.4-26.4,26.4s-26.4-12-26.4-26.4
+                               C166.8,71.4,178.4,59.8,193.2,59.8z"/>
+               </g>
+       </g>
+</g>
+</svg>
diff --git a/front/dist/resources/logos/microphone-close.svg b/front/dist/resources/logos/microphone-close.svg
new file mode 100644 (file)
index 0000000..1673182
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+</style>
+<rect x="257" y="-47.9" transform="matrix(-0.7402 0.6723 -0.6723 -0.7402 643.9641 283.6469)" class="st0" width="20.4" height="628.3"/>
+<g>
+       <g>
+               <path class="st0" d="M333.6,250.3c-52.6-43.9-105.1-87.9-157.7-131.8c0-17.9,0-35.8,0-53.6c6.5-38.6,40.3-67,79.3-66.8
+                       c38.6,0.2,71.9,28.5,78.4,66.8C333.6,126.7,333.6,188.5,333.6,250.3z"/>
+               <path class="st0" d="M322.6,279.9c-48.9-53.8-97.8-107.6-146.6-161.4l0,0c52.6,43.9,105.1,87.9,157.7,131.8
+                       c-0.2,1.6-0.5,3.3-0.9,5C330.5,265.2,326.6,273.5,322.6,279.9z"/>
+       </g>
+       <path class="st0" d="M292.5,308.1c-2.3,1.2-39.5,20.3-76.7-1c-36.4-20.8-39.4-61.2-39.6-64.1c-0.1-21-0.1-42.1-0.2-63.1
+               C214.8,222.6,253.6,265.3,292.5,308.1z"/>
+</g>
+<path class="st0" d="M431.6,238.5c-0.9-8.4-8.5-14.4-16.6-13.5c-7.9,0.9-13.9,8.1-13.2,16.3c-0.1,13.3-2.2,34.6-12.6,57.9
+       c-6.3,14.2-14,25.2-20.6,33.1c6.8,7.5,13.6,14.9,20.3,22.4c9.5-10.9,23.4-29.7,32.8-56.3C430.3,273.9,431.8,252.5,431.6,238.5z"/>
+<line class="st0" x1="354.5" y1="347.2" x2="374.6" y2="369.4"/>
+<path class="st0" d="M338.5,359.9c6.8,7.4,13.5,14.9,20.3,22.3c-52.6,37.6-121.5,43.7-179.2,15.8c-60.3-29.1-98.9-90.7-99.3-158.2
+       c0-8.2,6.8-15,15-15s15,6.8,15,15c0.1,13.5,2.4,54.4,32.4,91.6c4.2,5.2,45.1,54.1,113.3,54.1C297,385.6,326.7,367.9,338.5,359.9z"/>
+<rect x="241" y="409.6" class="st0" width="29.9" height="102.3"/>
+<path class="st0" d="M304.2,511.9h-97.1c-8-0.4-14.3-7.1-14.3-15c0-8.1,6.7-14.9,15-15c31.7,0,63.4,0.1,95.1,0.1
+       c8.9-0.6,16.3,6.5,16.3,14.9C319.2,504.8,312.6,511.7,304.2,511.9z"/>
+</svg>
diff --git a/front/dist/resources/logos/microphone.svg b/front/dist/resources/logos/microphone.svg
new file mode 100644 (file)
index 0000000..ff5727c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+</style>
+<g>
+       <g>
+               <path class="st0" d="M431.7,239.9c0-8.3-6.7-15-15-15c-8.3,0-15,6.7-15,15c0,80.3-65.3,145.7-145.7,145.7s-145.7-65.3-145.7-145.7
+                       c0-8.3-6.7-15-15-15c-8.3,0-15,6.7-15,15c0,91.8,70.8,167.4,160.7,175v67h-33.2c-8.3,0-15,6.7-15,15s6.7,15,15,15h96.4
+                       c8.3,0,15-6.7,15-15s-6.7-15-15-15H271v-67C360.9,407.3,431.7,331.7,431.7,239.9z"/>
+       </g>
+</g>
+<g>
+       <g>
+               <path class="st0" d="M256,0c-43.7,0-79.3,35.6-79.3,79.3v160.7c0,43.7,35.6,79.3,79.3,79.3c43.7,0,79.3-35.6,79.3-79.3V79.3
+                       C335.3,35.6,299.7,0,256,0z"/>
+       </g>
+</g>
+</svg>
diff --git a/front/dist/resources/logos/phone.svg b/front/dist/resources/logos/phone.svg
new file mode 100644 (file)
index 0000000..ac8e595
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+</style>
+<g>
+       <g>
+               <path class="st0" d="M32.6,256.8c17.3-15.9,36.8-28.8,57.8-38.3c10.4-4.5,17.8-15.9,18.1-25.3l9.8-53.2
+                       c55-14.2,98.5-12.3,151.6,6.5l5.2,52.4c-0.5,11.6,5.4,22.5,15.6,28.3c20.3,11.3,38.5,25.8,54.4,43.2c11.5,12.5,31,13.4,43.5,1.9
+                       l51.9-47.7c12.5-11.5,13.4-31,1.9-43.5c-131.8-143.5-355.6-153-499-21.3c-12.5,11.5-13.4,31-1.9,43.5l47.5,51.8
+                       C0.6,267.4,20.1,268.3,32.6,256.8z"/>
+       </g>
+</g>
+</svg>
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css
new file mode 100644 (file)
index 0000000..4c27c58
--- /dev/null
@@ -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
index d03a3152233bbe3c957768ae5b496878bca5f929..fffca2e9cb04ce2c55e4fbc81244c5193d854868 100644 (file)
@@ -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 (file)
index 0000000..cc636bb
--- /dev/null
@@ -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<any> = null;
+
+let peer : any = null;
+
+let Connexion : ConnexionInterface = null;
+
+let roomId = "test-wertc";
+
+let gettingCamera : Promise<any> = 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 (file)
index 0000000..bab1c7c
--- /dev/null
@@ -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<any> = 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 (file)
index 0000000..e6b531a
--- /dev/null
@@ -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<any> {
+        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<any> {
+        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<any> {
+        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<any> {
+        let description = new RTCSessionDescription(data.message);
+        return this.peerConnection.setRemoteDescription(description).catch((err) => {
+            console.error("PeerConnexionManager => setRemoteDescription", err);
+            throw err;
+        })
+    }
+
+    addIceCandidate(data: any): Promise<any> {
+        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 (file)
index 0000000..19c4670
--- /dev/null
@@ -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