Browse code

Created repository.

DoubleBastionAdmin authored on 26/01/2022 20:32:42
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,408 @@
1
+/**
2
+ *  Copyright (C) 2021  Double Bastion LLC
3
+ *
4
+ *  This file is part of Roundpin, which is licensed under the
5
+ *  GNU Affero General Public License Version 3.0. The license terms
6
+ *  are detailed in the "LICENSE.txt" file located in the root directory.
7
+ *
8
+ *  This is a modified version of the original file "cyber_mega_phone.js",
9
+ *  first modified in 2020. The copyright notice for the original
10
+ *  content follows.
11
+ */
12
+///////////////////////////////////////////////////////////////////////////////
13
+//  Cyber Mega Phone 2K
14
+//  Copyright (C) 2017 Digium, Inc.
15
+//
16
+//  This program is free software, distributed under the terms of the
17
+//  MIT License. See the LICENSE file at the top of the source tree.
18
+///////////////////////////////////////////////////////////////////////////////
19
+
20
+'use_strict';
21
+
22
+let isFirefox = typeof InstallTrigger !== 'undefined';
23
+let isChrome = !!window.chrome && !!window.chrome;
24
+let currentSession = null;
25
+
26
+function ConferencePhone(id, name, password, host, StunServer, register, audio=true, video=true) {
27
+
28
+	EasyEvent.call(this);
29
+
30
+	this.id = id;
31
+	this.name = name;
32
+	this.password = password;
33
+	this.host = host;
34
+        this.StunServer = StunServer;
35
+	this.register = register;
36
+
37
+	this.audio = audio;
38
+	this.video = video;
39
+
40
+	this._locals = new Streams();
41
+	this._locals.bubble("streamAdded", this);
42
+	this._locals.bubble("streamRemoved", this);
43
+
44
+	this._remotes = new Streams();
45
+	this._remotes.bubble("streamAdded", this);
46
+	this._remotes.bubble("streamRemoved", this);
47
+};
48
+
49
+ConferencePhone.prototype = Object.create(EasyEvent.prototype);
50
+ConferencePhone.prototype.constructor = ConferencePhone;
51
+
52
+// This was taken from the WebRTC unified transition guide located at
53
+// https://docs.google.com/document/d/1-ZfikoUtoJa9k-GZG1daN0BU3IjIanQ_JSscHxQesvU/edit
54
+function isUnifiedPlanDefault() {
55
+	// Safari supports addTransceiver() but not Unified Plan when
56
+	// currentDirection is not defined.
57
+	if (!('currentDirection' in RTCRtpTransceiver.prototype))
58
+		return false;
59
+
60
+	// If Unified Plan is supported, addTransceiver() should not throw.
61
+	const tempPc = new RTCPeerConnection();
62
+	let canAddTransceiver = false;
63
+	try {
64
+		tempPc.addTransceiver('audio');
65
+		canAddTransceiver = true;
66
+	} catch (e) {
67
+	}
68
+
69
+	tempPc.close();
70
+	return canAddTransceiver;
71
+}
72
+
73
+ConferencePhone.prototype.connect = function () {
74
+	if (this._ua) {
75
+		this._ua.start(); // Just reconnect
76
+		return;
77
+	}
78
+
79
+	let that = this;
80
+
81
+	let socket = new JsSIP.WebSocketInterface('wss://' + this.host + ':8089/ws');
82
+	let uri = 'sip:' + this.id + '@' + this.host;
83
+
84
+	let config = {
85
+		sockets: [ socket ],
86
+		uri: uri,
87
+		contact_uri: uri,
88
+		username: this.name ? this.name : this.id,
89
+		password: this.password,
90
+		register: this.register,
91
+		register_expires : 300,
92
+                sessionDescriptionHandlerFactoryOptions: {
93
+                    peerConnectionOptions : {
94
+                        alwaysAcquireMediaFirst: true,
95
+                        iceCheckingTimeout: 500,
96
+                        rtcConfiguration: {
97
+                            iceServers : [
98
+                                { urls: "stun:" + this.StunServer }
99
+                            ]
100
+                        }
101
+                    }
102
+                }
103
+	};
104
+
105
+	this._unified = isUnifiedPlanDefault();
106
+
107
+	this._ua = new JsSIP.UA(config);
108
+
109
+	function bubble (obj, name) {
110
+		obj.on(name, function (data) {
111
+			that.raise(name, data);
112
+		});
113
+	};
114
+
115
+	bubble(this._ua, 'connected');
116
+	bubble(this._ua, 'disconnected');
117
+	bubble(this._ua, 'registered');
118
+	bubble(this._ua, 'unregistered');
119
+	bubble(this._ua, 'registrationFailed');
120
+
121
+	this._ua.on('newRTCSession', function (data) {
122
+
123
+                currentSession = data.session;
124
+
125
+		let rtc = data.session;
126
+		rtc.interop = new SdpInterop.InteropChrome();
127
+
128
+		console.log('new session - ' + rtc.direction + ' - ' + rtc);
129
+
130
+		rtc.on("confirmed", function () {
131
+			// ACK was received
132
+			let streams = rtc.connection.getLocalStreams();
133
+			for (let i = 0; i < streams.length; ++i) {
134
+				console.log('confirmed: adding local stream ' + streams[i].id);
135
+				streams[i].local = true;
136
+				that._locals.add(streams[i]);
137
+			}
138
+		});
139
+
140
+		rtc.on("sdp", function (data) {
141
+			if (isFirefox && data.originator === 'remote') {
142
+				data.sdp = data.sdp.replace(/actpass/g, 'active');
143
+			} else if (isChrome && !that._unified) {
144
+				let desc = new RTCSessionDescription({type:data.type, sdp:data.sdp});
145
+				if (data.originator === 'local') {
146
+					converted = rtc.interop.toUnifiedPlan(desc);
147
+				} else {
148
+					converted = rtc.interop.toPlanB(desc);
149
+				}
150
+
151
+				data.sdp = converted.sdp;
152
+			}
153
+		});
154
+
155
+		bubble(rtc, 'muted');
156
+		bubble(rtc, 'unmuted');
157
+		bubble(rtc, 'failed');
158
+		bubble(rtc, 'ended');
159
+
160
+		rtc.connection.ontrack = function (event) {
161
+
162
+			console.log('ontrack: ' + event.track.kind + ' - ' + event.track.id +
163
+						' stream ' + event.streams[0].id);
164
+
165
+                        $("#video-view"+event.streams[0].id+"").show();
166
+
167
+			if (event.track.kind == 'video') {
168
+				event.track.enabled = false;
169
+			}
170
+
171
+			for (let i = 0; i < event.streams.length; ++i) {
172
+				event.streams[i].local = false;
173
+				that._remotes.add(event.streams[i]);
174
+			}
175
+
176
+			event.track.onended = function() {
177
+
178
+                                $("#video-view"+event.streams[0].id+"").hide();
179
+			};
180
+		};
181
+
182
+		rtc.connection.onremovestream = function (event) {
183
+			console.log('onremovestream: ' + event.stream.id);
184
+			that._remotes.remove(event.stream);
185
+		};
186
+
187
+		if (data.originator === "remote") {
188
+			that.raise('incoming', data.request.ruri.toAor());
189
+		}
190
+	});
191
+
192
+	this._ua.start();
193
+};
194
+
195
+ConferencePhone.prototype.disconnect = function () {
196
+	this._locals.removeAll();
197
+	this._remotes.removeAll();
198
+	if (this._ua) {
199
+		this._ua.stop();
200
+	}
201
+};
202
+
203
+ConferencePhone.prototype.answer = function () {
204
+
205
+	if (!this._ua) {
206
+		return;
207
+	}
208
+
209
+	let options = {
210
+		'mediaConstraints': { 'audio': this.audio, 'video': this.video }
211
+	};
212
+
213
+	this._rtc.answer(options);
214
+};
215
+
216
+ConferencePhone.prototype.call = function (exten) {
217
+
218
+	if (!this._ua || !exten) {
219
+		return;
220
+	}
221
+
222
+	let options = {
223
+		'mediaConstraints': { 'audio': this.audio, 'video': this.video }
224
+        };
225
+
226
+	if (exten.startsWith('sip:')) {
227
+		this._rtc = this._ua.call(exten, options);
228
+	} else {
229
+		this._rtc = this._ua.call('sip:' + exten + '@' + this.host, options);
230
+	}
231
+};
232
+
233
+ConferencePhone.prototype.terminate = function () {
234
+	this._locals.removeAll();
235
+	this._remotes.removeAll();
236
+	if (this._ua) {
237
+		this._rtc.terminate();
238
+	}
239
+};
240
+
241
+ConferencePhone.prototype.ShareScreen = function () {
242
+
243
+    var localStream = new MediaStream();
244
+    var pc = currentSession.connection;
245
+
246
+    var screenShareConstraints = { video: true, audio: false };
247
+
248
+    navigator.mediaDevices.getDisplayMedia(screenShareConstraints).then(function(newStream) {
249
+
250
+            var newMediaTrack = newStream.getVideoTracks()[0];
251
+            pc.getSenders().forEach(function (RTCRtpSender) {
252
+                if (RTCRtpSender.track && RTCRtpSender.track.kind == "video") {
253
+                    RTCRtpSender.replaceTrack(newMediaTrack);
254
+                    localStream.addTrack(newMediaTrack);
255
+                }
256
+            });
257
+
258
+            var localVideo = $('[id*="locVideo"]').get(0);
259
+            localVideo.autoplay = true;
260
+            localVideo.srcObject = localStream;
261
+
262
+            var VidView = $('[id*="video-view"]').get(0);
263
+            VidView.append(localVideo);
264
+            $('[id*="new-media-view"]').get(0).append(VidView);
265
+          
266
+    }).catch(function() { console.error(e); });
267
+}
268
+
269
+ConferencePhone.prototype.ShareVideo = function () {
270
+
271
+    var localStream = new MediaStream();
272
+    var pc = currentSession.connection;
273
+
274
+    var videoShareConstraints = { video: true, audio: true };
275
+    navigator.mediaDevices.getUserMedia(videoShareConstraints).then(function(newStream) {
276
+
277
+            var newMediaTrack = newStream.getVideoTracks()[0];
278
+            pc.getSenders().forEach(function (RTCRtpSender) {
279
+                if (RTCRtpSender.track && RTCRtpSender.track.kind == "video") {
280
+                    RTCRtpSender.replaceTrack(newMediaTrack);
281
+                    localStream.addTrack(newMediaTrack);
282
+                }
283
+            });
284
+
285
+            var localVideo = $('[id*="locVideo"]').get(0);
286
+            localVideo.autoplay = true;
287
+            localVideo.srcObject = localStream;
288
+
289
+            var VidView = $('[id*="video-view"]').get(0);
290
+            VidView.append(localVideo);
291
+            $('[id*="new-media-view"]').get(0).append(VidView);
292
+         
293
+    }).catch(function() { console.error(e); });
294
+}
295
+
296
+ConferencePhone.prototype.dtmfSend = function (numPressed) {
297
+    $("#dialText").val(numPressed);
298
+    var pc = currentSession.connection;
299
+    var dtmfSender = pc.getSenders()[0].dtmf;
300
+    dtmfSender.insertDTMF(numPressed);
301
+}
302
+
303
+///////////////////////////////////////////////////////////////////////////////
304
+
305
+function mute(stream, options) {
306
+
307
+	function setTracks(tracks, val) {
308
+		if (!tracks) {
309
+			return;
310
+		}
311
+
312
+		for (let i = 0; i < tracks.length; ++i) {
313
+			if (tracks[i].enabled == val) {
314
+				tracks[i].enabled = !val;
315
+			}
316
+		}
317
+	};
318
+
319
+	options = options || { audio: true, video: true };
320
+
321
+	if (typeof options.audio != 'undefined') {
322
+		setTracks(stream.getAudioTracks(), options.audio);
323
+	}
324
+
325
+	if (typeof options.video != 'undefined') {
326
+		setTracks(stream.getVideoTracks(), options.video);
327
+	}
328
+}
329
+
330
+function unmute(stream, options) {
331
+	let opts = options || { audio: false, video: false };
332
+	mute(stream, opts);
333
+}
334
+
335
+///////////////////////////////////////////////////////////////////////////////
336
+
337
+function Streams () {
338
+	EasyEvent.call(this);
339
+	this._streams = [];
340
+};
341
+
342
+Streams.prototype = Object.create(EasyEvent.prototype);
343
+Streams.prototype.constructor = Streams;
344
+
345
+Streams.prototype.add = function (stream) {
346
+	if (this._streams.indexOf(stream) == -1) {
347
+		this._streams.push(stream);
348
+		console.log('Streams: added ' + stream.id);
349
+		this.raise('streamAdded', stream);
350
+	}
351
+};
352
+
353
+Streams.prototype.remove = function (stream) {
354
+	let index = typeof stream == 'number' ? stream : this._streams.indexOf(stream);
355
+
356
+	if (index == -1) {
357
+		return;
358
+	}
359
+
360
+	let removed = this._streams.splice(index, 1);
361
+	for (let i = 0; i < removed.length; ++i) {
362
+		console.log('Streams: removed ' + removed[i].id);
363
+		this.raise('streamRemoved', removed[i]);
364
+	}
365
+};
366
+
367
+Streams.prototype.removeAll = function () {
368
+	for (let i = this._streams.length - 1; i >= 0 ; --i) {
369
+		this.remove(i);
370
+	}
371
+};
372
+
373
+///////////////////////////////////////////////////////////////////////////////
374
+
375
+function EasyEvent () {
376
+	this._events = {};
377
+};
378
+
379
+EasyEvent.prototype.handle = function (name, fun) {
380
+	if (name in this._events) {
381
+		this._events[name].push(fun);
382
+	} else {
383
+		this._events[name] = [fun];
384
+	}
385
+};
386
+
387
+EasyEvent.prototype.raise = function (name) {
388
+	if (name in this._events) {
389
+		for (let i = 0; i < this._events[name].length; ++i) {
390
+			this._events[name][i].apply(this,
391
+					Array.prototype.slice.call(arguments, 1));
392
+		}
393
+	}
394
+};
395
+
396
+EasyEvent.prototype.bubble = function (name, obj) {
397
+	this.handle(name, function (data) {
398
+		obj.raise(name, data);
399
+	});
400
+};
401
+
402
+EasyEvent.prototype.raiseForEach = function (name, array) {
403
+	if (name in this._events) {
404
+		for (let i = 0; i < array.length; ++i) {
405
+			this.raise(name, array[i], i);
406
+		}
407
+	}
408
+};