359eac67a08ce47bb89f240af5c3cf7fe8262229
[libreadventure.git] / front / src / WebRtc / MediaManager.ts
1 const videoConstraint: {width : any, height: any, facingMode : string} = {
2 width: { ideal: 1280 },
3 height: { ideal: 720 },
4 facingMode: "user"
5 };
6 export class MediaManager {
7 localStream: MediaStream|null = null;
8 remoteVideo: Array<any> = new Array<any>();
9 myCamVideo: HTMLVideoElement;
10 cinemaClose: any = null;
11 cinema: any = null;
12 microphoneClose: any = null;
13 microphone: any = null;
14 webrtcInAudio: HTMLAudioElement;
15 constraintsMedia : {audio : any, video : any} = {
16 audio: true,
17 video: videoConstraint
18 };
19 updatedLocalStreamCallBack : Function;
20
21 constructor(updatedLocalStreamCallBack : Function) {
22 this.updatedLocalStreamCallBack = updatedLocalStreamCallBack;
23
24 this.myCamVideo = this.getElementByIdOrFail<HTMLVideoElement>('myCamVideo');
25 this.webrtcInAudio = this.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-in');
26 this.webrtcInAudio.volume = 0.2;
27
28 this.microphoneClose = document.getElementById('microphone-close');
29 this.microphoneClose.style.display = "none";
30 this.microphoneClose.addEventListener('click', (e: any) => {
31 e.preventDefault();
32 this.enabledMicrophone();
33 //update tracking
34 });
35 this.microphone = document.getElementById('microphone');
36 this.microphone.addEventListener('click', (e: any) => {
37 e.preventDefault();
38 this.disabledMicrophone();
39 //update tracking
40 });
41
42 this.cinemaClose = document.getElementById('cinema-close');
43 this.cinemaClose.style.display = "none";
44 this.cinemaClose.addEventListener('click', (e: any) => {
45 e.preventDefault();
46 this.enabledCamera();
47 //update tracking
48 });
49 this.cinema = document.getElementById('cinema');
50 this.cinema.addEventListener('click', (e: any) => {
51 e.preventDefault();
52 this.disabledCamera();
53 //update tracking
54 });
55 }
56
57 activeVisio(){
58 let webRtc = this.getElementByIdOrFail('webRtc');
59 webRtc.classList.add('active');
60 }
61
62 enabledCamera() {
63 this.cinemaClose.style.display = "none";
64 this.cinema.style.display = "block";
65 this.constraintsMedia.video = videoConstraint;
66 this.getCamera().then((stream) => {
67 this.updatedLocalStreamCallBack(stream);
68 });
69 }
70
71 disabledCamera() {
72 this.cinemaClose.style.display = "block";
73 this.cinema.style.display = "none";
74 this.constraintsMedia.video = false;
75 this.myCamVideo.srcObject = null;
76 if (this.localStream) {
77 this.localStream.getVideoTracks().forEach((MediaStreamTrack: MediaStreamTrack) => {
78 MediaStreamTrack.stop();
79 });
80 }
81 this.getCamera().then((stream) => {
82 this.updatedLocalStreamCallBack(stream);
83 });
84 }
85
86 enabledMicrophone() {
87 this.microphoneClose.style.display = "none";
88 this.microphone.style.display = "block";
89 this.constraintsMedia.audio = true;
90 this.getCamera().then((stream) => {
91 this.updatedLocalStreamCallBack(stream);
92 });
93 }
94
95 disabledMicrophone() {
96 this.microphoneClose.style.display = "block";
97 this.microphone.style.display = "none";
98 this.constraintsMedia.audio = false;
99 if(this.localStream) {
100 this.localStream.getAudioTracks().forEach((MediaStreamTrack: MediaStreamTrack) => {
101 MediaStreamTrack.stop();
102 });
103 }
104 this.getCamera().then((stream) => {
105 this.updatedLocalStreamCallBack(stream);
106 });
107 }
108
109 //get camera
110 getCamera() {
111 let promise = null;
112 try {
113 promise = navigator.mediaDevices.getUserMedia(this.constraintsMedia)
114 .then((stream: MediaStream) => {
115 this.localStream = stream;
116 this.myCamVideo.srcObject = this.localStream;
117
118 //TODO resize remote cam
119 /*console.log(this.localStream.getTracks());
120 let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video");
121 let {width, height} = videoMediaStreamTrack.getSettings();
122 console.info(`${width}x${height}`); // 6*/
123
124 return stream;
125 }).catch((err) => {
126 console.info(`error get media {video: ${this.constraintsMedia.video}},{audio: ${this.constraintsMedia.audio}}`,err);
127 this.localStream = null;
128 });
129 } catch (e) {
130 promise = Promise.reject(false);
131 }
132 return promise;
133 }
134
135 /**
136 *
137 * @param userId
138 */
139 addActiveVideo(userId : string, userName: string = ""){
140 this.webrtcInAudio.play();
141 let elementRemoteVideo = this.getElementByIdOrFail("activeCam");
142 userName = userName.toUpperCase();
143 let color = this.getColorByString(userName);
144 elementRemoteVideo.insertAdjacentHTML('beforeend', `
145 <div id="div-${userId}" class="video-container" style="border-color: ${color};">
146 <div class="connecting-spinner"></div>
147 <div class="rtc-error" style="display: none"></div>
148 <i style="background-color: ${color};">${userName}</i>
149 <img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
150 <video id="${userId}" autoplay></video>
151 </div>
152 `);
153 this.remoteVideo[(userId as any)] = document.getElementById(userId);
154 }
155
156 /**
157 *
158 * @param userId
159 */
160 disabledMicrophoneByUserId(userId: string){
161 let element = document.getElementById(`microphone-${userId}`);
162 if(!element){
163 return;
164 }
165 element.classList.add('active')
166 }
167
168 /**
169 *
170 * @param userId
171 */
172 enabledMicrophoneByUserId(userId: string){
173 let element = document.getElementById(`microphone-${userId}`);
174 if(!element){
175 return;
176 }
177 element.classList.remove('active')
178 }
179
180 /**
181 *
182 * @param userId
183 */
184 disabledVideoByUserId(userId: string) {
185 let element = document.getElementById(`${userId}`);
186 if (element) {
187 element.style.opacity = "0";
188 }
189 element = document.getElementById(`div-${userId}`);
190 if (!element) {
191 return;
192 }
193 element.style.borderStyle = "solid";
194 }
195
196 /**
197 *
198 * @param userId
199 */
200 enabledVideoByUserId(userId: string){
201 let element = document.getElementById(`${userId}`);
202 if(element){
203 element.style.opacity = "1";
204 }
205 element = document.getElementById(`div-${userId}`);
206 if(!element){
207 return;
208 }
209 element.style.borderStyle = "none";
210 }
211
212 /**
213 *
214 * @param userId
215 * @param stream
216 */
217 addStreamRemoteVideo(userId : string, stream : MediaStream){
218 this.remoteVideo[(userId as any)].srcObject = stream;
219 }
220
221 /**
222 *
223 * @param userId
224 */
225 removeActiveVideo(userId : string){
226 let element = document.getElementById(`div-${userId}`);
227 if(!element){
228 return;
229 }
230 element.remove();
231 }
232
233 isConnecting(userId : string): void {
234 let connectingSpinnerDiv = this.getSpinner(userId);
235 if (connectingSpinnerDiv === null) {
236 return;
237 }
238 connectingSpinnerDiv.style.display = 'block';
239 }
240
241 isConnected(userId : string): void {
242 let connectingSpinnerDiv = this.getSpinner(userId);
243 if (connectingSpinnerDiv === null) {
244 return;
245 }
246 connectingSpinnerDiv.style.display = 'none';
247 }
248
249 isError(userId : string): void {
250 let element = document.getElementById(`div-${userId}`);
251 if(!element){
252 return;
253 }
254 let errorDiv = element.getElementsByClassName('rtc-error').item(0) as HTMLDivElement|null;
255 if (errorDiv === null) {
256 return;
257 }
258 errorDiv.style.display = 'block';
259 }
260
261 private getSpinner(userId : string): HTMLDivElement|null {
262 let element = document.getElementById(`div-${userId}`);
263 if(!element){
264 return null;
265 }
266 let connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null;
267 return connnectingSpinnerDiv;
268 }
269
270 /**
271 *
272 * @param str
273 */
274 private getColorByString(str: String) : String|null {
275 let hash = 0;
276 if (str.length === 0) return null;
277 for (let i = 0; i < str.length; i++) {
278 hash = str.charCodeAt(i) + ((hash << 5) - hash);
279 hash = hash & hash;
280 }
281 let color = '#';
282 for (let i = 0; i < 3; i++) {
283 let value = (hash >> (i * 8)) & 255;
284 color += ('00' + value.toString(16)).substr(-2);
285 }
286 return color;
287 }
288
289 private getElementByIdOrFail<T extends HTMLElement>(id: string): T {
290 let elem = document.getElementById(id);
291 if (elem === null) {
292 throw new Error("Cannot find HTML element with id '"+id+"'");
293 }
294 // FIXME: does not check the type of the returned type
295 return elem as T;
296 }
297
298 }