zhyz-cloudrender-v5 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/p2p.js ADDED
@@ -0,0 +1,1426 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ /* eslint-disable no-undef */
3
+ /* eslint-disable no-mixed-spaces-and-tabs */
4
+ /* eslint-disable no-unused-vars */
5
+ // Copyright Epic Games, Inc. All Rights Reserved.
6
+
7
+ const webRtcPlayer = require('./webRtcPlayer');
8
+ let className = '';
9
+ let roomId = '';
10
+ let receivedBytesMeasurement = '';
11
+ let receivedBytes = 0;
12
+ let color = 'red';
13
+ // Window events for a gamepad connecting
14
+ let haveEvents = 'GamepadEvent' in window;
15
+ let playerElement = null;
16
+ let videoElement = null;
17
+ let haveWebkitEvents = 'WebKitGamepadEvent' in window;
18
+ let controllers = {};
19
+ let rAF = window.mozRequestAnimationFrame ||
20
+ window.webkitRequestAnimationFrame ||
21
+ window.requestAnimationFrame;
22
+ let kbEvent = document.createEvent("KeyboardEvent");
23
+ let initMethod = typeof kbEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
24
+
25
+ let webRtcPlayerObj = null;
26
+ let webRtcDataChannelObj = null;
27
+ let print_stats = false;
28
+ let print_inputs = true;
29
+ let connect_on_load = true;
30
+
31
+ import store, {
32
+ SET_WEBRTC_OBJ
33
+ } from './store'
34
+
35
+
36
+
37
+ //let webRtcDataChannel_pc = null;
38
+ let is_reconnection = false;
39
+ let ws;
40
+ const WS_OPEN_STATE = 1;
41
+
42
+ let qualityControlOwnershipCheckBox;
43
+ let matchViewportResolution;
44
+ // TODO: Remove this - workaround because of bug causing UE to crash when switching resolutions too quickly
45
+ let lastTimeResized = new Date().getTime();
46
+ let resizeTimeout;
47
+
48
+ let onDataChannelConnected;
49
+ let responseEventListeners = new Map();
50
+
51
+ // A freeze frame is a still JPEG image shown instead of the video.
52
+ let freezeFrame = {
53
+ receiving: false,
54
+ size: 0,
55
+ jpeg: undefined,
56
+ height: 0,
57
+ width: 0,
58
+ valid: false
59
+ };
60
+
61
+ // Optionally detect if the user is not interacting (AFK) and disconnect them.
62
+ let afk = {
63
+ enabled: false, // Set to true to enable the AFK system.
64
+ warnTimeout: 120, // The time to elapse before warning the user they are inactive.
65
+ closeTimeout: 10, // The time after the warning when we disconnect the user.
66
+
67
+ active: false, // Whether the AFK system is currently looking for inactivity.
68
+ overlay: undefined, // The UI overlay warning the user that they are inactive.
69
+ warnTimer: undefined, // The timer which waits to show the inactivity warning overlay.
70
+ countdown: 0, // The inactivity warning overlay has a countdown to show time until disconnect.
71
+ countdownTimer: undefined, // The timer used to tick the seconds shown on the inactivity warning overlay.
72
+ }
73
+
74
+ // If the user focuses on a UE4 input widget then we show them a button to open
75
+ // the on-screen keyboard. JavaScript security means we can only show the
76
+ // on-screen keyboard in response to a user interaction.
77
+ let editTextButton = undefined;
78
+
79
+ // A hidden input text box which is used only for focusing and opening the
80
+ // on-screen keyboard.
81
+ let hiddenInput = undefined;
82
+
83
+ let t0 = Date.now();
84
+
85
+ let params = {
86
+ roomname: '',
87
+ videousername: '',
88
+ rtc_ip: '',
89
+ rtc_port: '',
90
+ };
91
+
92
+ function gamepadConnectHandler(e) {
93
+ gamepad = e.gamepad;
94
+ controllers[gamepad.index] = {};
95
+ controllers[gamepad.index].currentState = gamepad;
96
+ controllers[gamepad.index].prevState = gamepad;
97
+ rAF(updateStatus);
98
+ }
99
+
100
+ function gamepadDisconnectHandler(e) {
101
+ delete controllers[e.gamepad.index];
102
+ }
103
+
104
+ function setupHtmlEvents() {
105
+ //Window events
106
+ window.addEventListener('orientationchange', onOrientationChange);
107
+
108
+ //Gamepad events
109
+ if (haveEvents) {
110
+ window.addEventListener("gamepadconnected", gamepadConnectHandler);
111
+ window.addEventListener("gamepaddisconnected", gamepadDisconnectHandler);
112
+ } else if (haveWebkitEvents) {
113
+ window.addEventListener("webkitgamepadconnected", gamepadConnectHandler);
114
+ window.addEventListener("webkitgamepaddisconnected", gamepadDisconnectHandler);
115
+ }
116
+
117
+ //HTML elements controls
118
+ let overlayButton = document.getElementById('overlayButton');
119
+ overlayButton && overlayButton.addEventListener('click', onExpandOverlay_Click);
120
+
121
+ let resizeCheckBox = document.getElementById('enlarge-display-to-fill-window-tgl');
122
+ if (resizeCheckBox !== null) {
123
+ resizeCheckBox.onchange = function(event) {
124
+ // resizePlayerStyle();
125
+ };
126
+ }
127
+
128
+ qualityControlOwnershipCheckBox = document.getElementById('quality-control-ownership-tgl');
129
+ if (qualityControlOwnershipCheckBox !== null) {
130
+ qualityControlOwnershipCheckBox.onchange = function(event) {
131
+ requestQualityControl();
132
+ };
133
+ }
134
+
135
+ let encoderParamsSubmit = document.getElementById('encoder-params-submit');
136
+ if (encoderParamsSubmit !== null) {
137
+ encoderParamsSubmit.onclick = function(event) {
138
+ let rateControl = document.getElementById('encoder-rate-control').value;
139
+ let targetBitrate = document.getElementById('encoder-target-bitrate-text').value * 1000;
140
+ let maxBitrate = document.getElementById('encoder-max-bitrate-text').value * 1000;
141
+ let minQP = document.getElementById('encoder-min-qp-text').value;
142
+ let maxQP = document.getElementById('encoder-max-qp-text').value;
143
+ let fillerData = document.getElementById('encoder-filler-data-tgl').checked ? 1 : 0;
144
+ let multipass = document.getElementById('encoder-multipass').value;
145
+
146
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.RateControl ' + rateControl });
147
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.TargetBitrate ' + targetBitrate > 0 ? targetBitrate : -1 });
148
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.MaxBitrateVBR ' + maxBitrate > 0 ? maxBitrate : -1 });
149
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.MinQP ' + minQP });
150
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.MaxQP ' + maxQP });
151
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.EnableFillerData ' + fillerData });
152
+ emitUIInteraction({ Console: 'PixelStreaming.Encoder.Multipass ' + multipass });
153
+ };
154
+ }
155
+
156
+ let webrtcParamsSubmit = document.getElementById('webrtc-params-submit');
157
+ if (webrtcParamsSubmit !== null) {
158
+ webrtcParamsSubmit.onclick = function(event) {
159
+ let degradationPref = document.getElementById('webrtc-degradation-pref').value;
160
+ let maxFPS = document.getElementById('webrtc-max-fps-text').value;
161
+ let minBitrate = document.getElementById('webrtc-min-bitrate-text').value * 1000;
162
+ let maxBitrate = document.getElementById('webrtc-max-bitrate-text').value * 1000;
163
+ let lowQP = document.getElementById('webrtc-low-qp-text').value;
164
+ let highQP = document.getElementById('webrtc-high-qp-text').value;
165
+
166
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.DegradationPreference ' + degradationPref });
167
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.MaxFps ' + maxFPS });
168
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.MinBitrate ' + minBitrate });
169
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.MaxBitrate ' + maxBitrate });
170
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.LowQpThreshold ' + lowQP });
171
+ emitUIInteraction({ Console: 'PixelStreaming.WebRTC.HighQpThreshold ' + highQP });
172
+ };
173
+ }
174
+
175
+ let showFPSButton = document.getElementById('show-fps-button');
176
+ if (showFPSButton !== null) {
177
+ showFPSButton.onclick = function (event) {
178
+ let consoleDescriptor = {
179
+ Console: 'stat fps'
180
+ };
181
+ emitUIInteraction(consoleDescriptor);
182
+ };
183
+ }
184
+
185
+ let matchViewportResolutionCheckBox = document.getElementById('match-viewport-res-tgl');
186
+ if (matchViewportResolutionCheckBox !== null) {
187
+ matchViewportResolutionCheckBox.onchange = function (event) {
188
+ matchViewportResolution = matchViewportResolutionCheckBox.checked;
189
+ };
190
+ }
191
+
192
+ let statsCheckBox = document.getElementById('show-stats-tgl');
193
+ if (statsCheckBox !== null) {
194
+ statsCheckBox.onchange = function(event) {
195
+ let stats = document.getElementById('statsContainer');
196
+ stats.style.display = event.target.checked ? "block" : "none";
197
+ };
198
+ }
199
+
200
+ let kickButton = document.getElementById('kick-other-players-button');
201
+ if (kickButton) {
202
+ kickButton.onclick = function (event) {
203
+ ws.send(JSON.stringify({
204
+ type: 'kick'
205
+ }));
206
+ };
207
+ }
208
+
209
+ let latencyButton = document.getElementById('test-latency-button');
210
+ if (latencyButton) {
211
+ latencyButton.onclick = () => {
212
+ sendStartLatencyTest();
213
+ };
214
+ }
215
+ }
216
+
217
+ function sendStartLatencyTest() {
218
+ // We need WebRTC to be active to do a latency test.
219
+ if (!webRtcPlayerObj) {
220
+ return;
221
+ }
222
+
223
+ let onTestStarted = function(StartTimeMs) {
224
+ let descriptor = {
225
+ StartTime: StartTimeMs
226
+ };
227
+ emitDescriptor(MessageType.LatencyTest, descriptor);
228
+ };
229
+
230
+ webRtcPlayerObj.startLatencyTest(onTestStarted);
231
+ }
232
+
233
+ function setOverlay() {
234
+ return;
235
+ }
236
+
237
+ function showConnectOverlay() {
238
+ connect();
239
+ startAfkWarningTimer();
240
+ }
241
+
242
+ function showTextOverlay(text) {
243
+ let textOverlay = document.createElement('div');
244
+ textOverlay.id = 'messageOverlay';
245
+ textOverlay.innerHTML = text ? text : '';
246
+ setOverlay('textDisplayState', textOverlay);
247
+ }
248
+
249
+ function playVideoStream() {
250
+ if (webRtcPlayerObj && webRtcPlayerObj.video) {
251
+
252
+ webRtcPlayerObj.video.play().catch(function(onRejectedReason){
253
+ console.error(onRejectedReason);
254
+ });
255
+
256
+ requestInitialSettings();
257
+ requestQualityControl();
258
+ hideOverlay();
259
+ } else {
260
+ console.error("Could not player video stream because webRtcPlayerObj.video was not valid.")
261
+ }
262
+ }
263
+
264
+
265
+
266
+ function updateAfkOverlayText() {
267
+ afk.overlay.innerHTML = '<center>No activity detected<br>Disconnecting in ' + afk.countdown + ' seconds<br>Click to continue<br></center>';
268
+ }
269
+
270
+ function showAfkOverlay() {
271
+ // Pause the timer while the user is looking at the inactivity warning overlay.
272
+ stopAfkWarningTimer();
273
+
274
+ // Show the inactivity warning overlay.
275
+ afk.overlay = document.createElement('div');
276
+ afk.overlay.id = 'afkOverlay';
277
+ setOverlay('clickableState', afk.overlay, event => {
278
+ // The user clicked so start the timer again and carry on.
279
+ hideOverlay();
280
+ clearInterval(afk.countdownTimer);
281
+ startAfkWarningTimer();
282
+ });
283
+
284
+ afk.countdown = afk.closeTimeout;
285
+ updateAfkOverlayText();
286
+
287
+
288
+ if (inputOptions.controlScheme == ControlSchemeType.LockedMouse) {
289
+ document.exitPointerLock();
290
+ }
291
+
292
+
293
+ afk.countdownTimer = setInterval(function() {
294
+ afk.countdown--;
295
+ if (afk.countdown == 0) {
296
+ // The user failed to click so disconnect them.
297
+ hideOverlay();
298
+ ws.close();
299
+ } else {
300
+ // Update the countdown message.
301
+ updateAfkOverlayText();
302
+ }
303
+ }, 1000);
304
+ }
305
+
306
+ function hideOverlay() {
307
+ setOverlay('hiddenState');
308
+ }
309
+
310
+ // Start a timer which when elapsed will warn the user they are inactive.
311
+ function startAfkWarningTimer() {
312
+ afk.active = afk.enabled;
313
+ resetAfkWarningTimer();
314
+ }
315
+
316
+ // Stop the timer which when elapsed will warn the user they are inactive.
317
+ function stopAfkWarningTimer() {
318
+ afk.active = false;
319
+ }
320
+
321
+ // If the user interacts then reset the warning timer.
322
+ function resetAfkWarningTimer() {
323
+ if (afk.active) {
324
+ clearTimeout(afk.warnTimer);
325
+ afk.warnTimer = setTimeout(function () {
326
+ showAfkOverlay();
327
+ }, afk.warnTimeout * 1000);
328
+ }
329
+ }
330
+
331
+ function createWebRtcOffer() {
332
+ if (webRtcPlayerObj ) {
333
+ showTextOverlay('Starting connection to server, please wait');
334
+ webRtcPlayerObj.createOffer();
335
+ // webRtcDataChannelObj.createOffer();
336
+ } else {
337
+ showTextOverlay('Unable to setup video');
338
+ }
339
+ }
340
+
341
+
342
+
343
+
344
+ /**
345
+ 功能: 获取Offer SDP 描述符的
346
+
347
+ */
348
+ function getOffer(desc)
349
+ {
350
+ //设置Offer
351
+ webRtcDataChannelObj.setLocalDescription(desc);
352
+
353
+ if (ws && ws.readyState === WS_OPEN_STATE)
354
+ {
355
+ let offersdp = JSON.stringify({
356
+ msg_id: 208,
357
+ data: {
358
+ rtc_type:1,
359
+ offer : desc.sdp
360
+ }
361
+ });
362
+
363
+ ws.send(offersdp);
364
+ }
365
+ }
366
+
367
+ function handleOfferError(err)
368
+ {
369
+ console.log('Failed ot carete offer:', err);
370
+ }
371
+
372
+
373
+ function createWebRtcDataChannelOffer()
374
+ {
375
+ if ( webRtcDataChannelObj) {
376
+ showTextOverlay('Starting connection to server, please wait');
377
+ let datachannel = webRtcDataChannelObj.createDataChannel('rtc', {ordered: true});
378
+ // Inform browser we would like binary data as an ArrayBuffer (FF chooses Blob by default!)
379
+ datachannel.binaryType = "arraybuffer";
380
+ datachannel.onopen = function (e) {
381
+
382
+ }
383
+
384
+ datachannel.onclose = function (e) {
385
+ console.log(`data channel closed`);
386
+ }
387
+
388
+ datachannel.onmessage = function (e) {
389
+ console.log(`Got message `, e.data);
390
+
391
+ }
392
+ webRtcDataChannelObj.datachannel = datachannel;
393
+
394
+ webRtcDataChannelObj.createOffer()
395
+ .then(getOffer)
396
+ .catch(handleOfferError);
397
+ // webRtcDataChannelObj.createOffer() .then(async function (offerStr){
398
+ // webRtcDataChannelObj.setLocalDescription(offerStr);
399
+ // if (ws && ws.readyState === WS_OPEN_STATE)
400
+ // {
401
+ // console.log('DataChannel cs --> offer : = ', offerStr);
402
+ // let offersdp = JSON.stringify({
403
+ // msg_id: 208,
404
+ // data: {
405
+ // rtc_type:0,
406
+ // offer : offerStr.sdp
407
+ // }
408
+ // });
409
+ // console.log(`DataChannel -> SS: offer:\n${offersdp}`);
410
+ //
411
+ // ws.send(offersdp);
412
+ // }
413
+ // }, function(){console.warn("Couldn't create offer") ;});
414
+ // .catch(handleOfferError);
415
+
416
+ // pc.createOffer(self.sdpConstraints).then(function (offer) {
417
+
418
+ // // Munging is where we modifying the sdp string to set parameters that are not exposed to the browser's WebRTC API
419
+ // mungeSDPOffer(offer);
420
+
421
+ // // Set our munged SDP on the local peer connection so it is "set" and will be send across
422
+ // pc.setLocalDescription(offer);
423
+ // if (self.onWebRtcDataChannelOffer) {
424
+ // console.log('rtctype == 1-');
425
+ // self.onWebRtcDataChannelOffer(1, offer);
426
+ // }
427
+ // else
428
+ // {
429
+ // console.warn('not find DataChannel Offer callback send msg function ');
430
+ // }
431
+ // },
432
+ // function () { console.warn("Couldn't create offer") });
433
+ //webRtcDataChannelObj.createOffer();
434
+ } else {
435
+ console.log('WebRTC DataChannel not setup, cannot create offer');
436
+ //showTextOverlay('Unable to setup video');
437
+ }
438
+ }
439
+
440
+
441
+ function arrayBufferToString(buffer, encoding = 'gb2312') {
442
+ const decoder = new TextDecoder(encoding);
443
+ return decoder.decode(buffer);
444
+ }
445
+
446
+ function buf2hex(buffer) { // buffer is an ArrayBuffer
447
+ return [...new Uint8Array(buffer)]
448
+ .map(x => x.toString(2).padStart(1, '0'))
449
+ .join(' ');
450
+ }
451
+ function bufhex(buffer) { // buffer is an ArrayBuffer
452
+ return [...new Uint8Array(buffer)]
453
+ .map(x => x.toString(16).padStart(2, '0'))
454
+ .join('');
455
+ }
456
+ function xsendInputData(data) {
457
+ // if (webRtcPlayerObj) {
458
+ // resetAfkWarningTimer();
459
+ // webRtcPlayerObj.send(data);
460
+ // }
461
+
462
+ }
463
+
464
+ function sendInputData(data) {
465
+ //
466
+ //if (webRtcDataChannelObj && webRtcDataChannelObj.datachannel.readyState == 'open') {
467
+ // // resetAfkWarningTimer();
468
+ // webRtcDataChannelObj.datachannel.send(data);
469
+ // // console.log('send input data =', data);
470
+ // }
471
+ // else
472
+ // {
473
+ //
474
+ // console.warn('send message failed !!! webRtcDataChannelObj.readyState =', webRtcDataChannelObj.readyState);
475
+ // }
476
+ // return;
477
+ if (webRtcPlayerObj) {
478
+ // resetAfkWarningTimer();
479
+ webRtcPlayerObj.send(data);
480
+ //console.log('hex = ', bufhex(data));
481
+ // if (ws && ws.readyState === WS_OPEN_STATE)
482
+ // {
483
+ //
484
+ // // let textDecoder = new TextDecoder("utf-8");
485
+ // // let str = textDecoder.decode(data);
486
+ //
487
+ // //console.log('CS--> data channel : = ', str);
488
+ // //arrayBufferToString(data)
489
+ // let offersdp = JSON.stringify({
490
+ // msg_id: 210,
491
+ // datachannel: data
492
+ // });
493
+ // // console.log(`-> SS: offer:\n${offersdp}`);
494
+ //
495
+ //
496
+ //
497
+ // ws.send(offersdp);
498
+ //
499
+ // }
500
+ }
501
+ }
502
+
503
+ function addResponseEventListener(name, listener) {
504
+ responseEventListeners.set(name, listener);
505
+ }
506
+
507
+ function removeResponseEventListener(name) {
508
+ responseEventListeners.remove(name);
509
+ }
510
+
511
+ // Must be kept in sync with PixelStreamingProtocol::EToPlayerMsg C++ enum.
512
+ const ToClientMessageType = {
513
+ QualityControlOwnership: 0,
514
+ Response: 1,
515
+ Command: 2,
516
+ FreezeFrame: 3,
517
+ UnfreezeFrame: 4,
518
+ VideoEncoderAvgQP: 5,
519
+ LatencyTest: 6,
520
+ InitialSettings: 7
521
+ };
522
+
523
+ let VideoEncoderQP = "N/A";
524
+ function getQueryVariable(variable)
525
+ {
526
+
527
+ return params[variable];
528
+ }
529
+
530
+ function setupWebRtcPlayer(htmlElement, config) {
531
+
532
+ webRtcPlayerObj = new webRtcPlayer(config);
533
+ webRtcPlayerObj.video.className = className;
534
+ htmlElement.appendChild(webRtcPlayerObj.video);
535
+
536
+ webRtcPlayerObj.onWebRtcOffer = function(webrtc_type, offerStr) {
537
+ if (ws && ws.readyState === WS_OPEN_STATE)
538
+ {
539
+ var room_name = getQueryVariable('roomname');
540
+ var video_user_name = getQueryVariable('videousername');
541
+
542
+ let offersdp = JSON.stringify({
543
+ msg_id: 208,
544
+ data: {
545
+ rtc_type:webrtc_type,
546
+ offer : offerStr.sdp
547
+ }
548
+ });
549
+
550
+
551
+
552
+
553
+ ws.send(offersdp);
554
+ }
555
+ };
556
+
557
+ webRtcPlayerObj.onWebRtcCandidate = function(webrtc_type, iceCandidate) {
558
+ if (ws && ws.readyState === WS_OPEN_STATE) {
559
+ ws.send(JSON.stringify({
560
+ /* type: 'iceCandidate',*/
561
+ /*candidate: candidate*/
562
+ msg_id: 208,
563
+ data: {
564
+ rtc_type:webrtc_type,
565
+ type : 'candidate',
566
+ label : iceCandidate.sdpMLineIndex,
567
+ id : iceCandidate.sdpMid,
568
+ candidate : iceCandidate.candidate
569
+ }
570
+ }));
571
+ }
572
+ };
573
+
574
+ webRtcPlayerObj.onVideoInitialised = function() {
575
+ if (ws && ws.readyState === WS_OPEN_STATE) {
576
+ playVideoStream();
577
+ }
578
+ };
579
+
580
+ // webRtcPlayerObj.onDataChannelConnected = function() {
581
+ // if (ws && ws.readyState === WS_OPEN_STATE) {
582
+ // showTextOverlay('WebRTC connected, waiting for video');
583
+
584
+ // if (webRtcPlayerObj.video && webRtcPlayerObj.video.srcObject && webRtcPlayerObj.onVideoInitialised) {
585
+ // webRtcPlayerObj.onVideoInitialised();
586
+ // }
587
+ // }
588
+ // };
589
+
590
+
591
+
592
+ function processFreezeFrameMessage(view) {
593
+ // Reset freeze frame if we got a freeze frame message and we are not "receiving" yet.
594
+ if (!freezeFrame.receiving) {
595
+ freezeFrame.receiving = true;
596
+ freezeFrame.valid = false;
597
+ freezeFrame.size = 0;
598
+ freezeFrame.jpeg = undefined;
599
+ }
600
+
601
+ // Extract total size of freeze frame (across all chunks)
602
+ freezeFrame.size = (new DataView(view.slice(1, 5).buffer)).getInt32(0, true);
603
+
604
+ // Get the jpeg part of the payload
605
+ let jpegBytes = view.slice(1 + 4);
606
+
607
+ // Append to existing jpeg that holds the freeze frame
608
+ if (freezeFrame.jpeg) {
609
+ let jpeg = new Uint8Array(freezeFrame.jpeg.length + jpegBytes.length);
610
+ jpeg.set(freezeFrame.jpeg, 0);
611
+ jpeg.set(jpegBytes, freezeFrame.jpeg.length);
612
+ freezeFrame.jpeg = jpeg;
613
+ }
614
+ // No existing freeze frame jpeg, make one
615
+ else {
616
+ freezeFrame.jpeg = jpegBytes;
617
+ freezeFrame.receiving = true;
618
+ }
619
+
620
+ // Uncomment for debug
621
+ //console.log(`Received freeze frame chunk: ${freezeFrame.jpeg.length}/${freezeFrame.size}`);
622
+
623
+ // Finished receiving freeze frame, we can show it now
624
+ if (freezeFrame.jpeg.length === freezeFrame.size) {
625
+ freezeFrame.receiving = false;
626
+ freezeFrame.valid = true;
627
+ showFreezeFrame();
628
+ }
629
+ // We received more data than the freeze frame payload message indicate (this is an error)
630
+ else if (freezeFrame.jpeg.length > freezeFrame.size) {
631
+ console.error(`received bigger freeze frame than advertised: ${freezeFrame.jpeg.length}/${freezeFrame.size}`);
632
+ freezeFrame.jpeg = undefined;
633
+ freezeFrame.receiving = false;
634
+ }
635
+ }
636
+
637
+ // webRtcPlayerObj.onDataChannelMessage = function(data) {
638
+ // let view = new Uint8Array(data);
639
+
640
+ // if (view[0] === ToClientMessageType.QualityControlOwnership) {
641
+ // let ownership = view[1] === 0 ? false : true;
642
+ // console.log("Received quality controller message, will control quality: " + ownership);
643
+ // // If we own the quality control, we can't relenquish it. We only loose
644
+ // // quality control when another peer asks for it
645
+ // if (qualityControlOwnershipCheckBox !== null) {
646
+ // qualityControlOwnershipCheckBox.disabled = ownership;
647
+ // qualityControlOwnershipCheckBox.checked = ownership;
648
+ // }
649
+ // } else if (view[0] === ToClientMessageType.Response) {
650
+ // let response = new TextDecoder("utf-16").decode(data.slice(1));
651
+ // for (let listener of responseEventListeners.values()) {
652
+ // listener(response);
653
+ // }
654
+ // } else if (view[0] === ToClientMessageType.Command) {
655
+ // let commandAsString = new TextDecoder("utf-16").decode(data.slice(1));
656
+ // console.log(commandAsString);
657
+ // let command = JSON.parse(commandAsString);
658
+ // if (command.command === 'onScreenKeyboard') {
659
+ // showOnScreenKeyboard(command);
660
+ // }
661
+ // } else if (view[0] === ToClientMessageType.FreezeFrame) {
662
+ // processFreezeFrameMessage(view);
663
+ // } else if (view[0] === ToClientMessageType.UnfreezeFrame) {
664
+ // invalidateFreezeFrameOverlay();
665
+ // } else if (view[0] === ToClientMessageType.VideoEncoderAvgQP) {
666
+ // VideoEncoderQP = new TextDecoder("utf-16").decode(data.slice(1));
667
+ // //console.log(`received VideoEncoderAvgQP ${VideoEncoderQP}`);
668
+ // } else if (view[0] == ToClientMessageType.LatencyTest) {
669
+ // let latencyTimingsAsString = new TextDecoder("utf-16").decode(data.slice(1));
670
+ // console.log("Got latency timings from UE.")
671
+ // console.log(latencyTimingsAsString);
672
+ // let latencyTimingsFromUE = JSON.parse(latencyTimingsAsString);
673
+ // if (webRtcPlayerObj) {
674
+ // webRtcPlayerObj.latencyTestTimings.SetUETimings(latencyTimingsFromUE);
675
+ // }
676
+ // } else if (view[0] == ToClientMessageType.InitialSettings) {
677
+ // let settingsString = new TextDecoder("utf-16").decode(data.slice(1));
678
+ // let settingsJSON = JSON.parse(settingsString);
679
+
680
+ // // reminder bitrates are sent in bps but displayed in kbps
681
+
682
+ // if (settingsJSON.Encoder) {
683
+ // document.getElementById('encoder-rate-control').value = settingsJSON.Encoder.RateControl;
684
+ // document.getElementById('encoder-target-bitrate-text').value = settingsJSON.Encoder.TargetBitrate > 0 ? settingsJSON.Encoder.TargetBitrate / 1000 : settingsJSON.Encoder.TargetBitrate;
685
+ // document.getElementById('encoder-max-bitrate-text').value = settingsJSON.Encoder.MaxBitrate > 0 ? settingsJSON.Encoder.MaxBitrate / 1000 : settingsJSON.Encoder.MaxBitrate;
686
+ // document.getElementById('encoder-min-qp-text').value = settingsJSON.Encoder.MinQP;
687
+ // document.getElementById('encoder-max-qp-text').value = settingsJSON.Encoder.MaxQP;
688
+ // document.getElementById('encoder-filler-data-tgl').checked = settingsJSON.Encoder.FillerData == 1;
689
+ // document.getElementById('encoder-multipass').value = settingsJSON.Encoder.MultiPass;
690
+ // }
691
+ // if (settingsJSON.WebRTC) {
692
+ // document.getElementById('webrtc-degradation-pref').value = settingsJSON.WebRTC.DegradationPref;
693
+ // document.getElementById("webrtc-max-fps-text").value = settingsJSON.WebRTC.MaxFPS;
694
+ // document.getElementById("webrtc-min-bitrate-text").value = settingsJSON.WebRTC.MinBitrate / 1000;
695
+ // document.getElementById("webrtc-max-bitrate-text").value = settingsJSON.WebRTC.MaxBitrate / 1000;
696
+ // document.getElementById("webrtc-low-qp-text").value = settingsJSON.WebRTC.LowQP;
697
+ // document.getElementById("webrtc-high-qp-text").value = settingsJSON.WebRTC.HighQP;
698
+ // }
699
+ // } else {
700
+ // console.error(`unrecognized data received, packet ID ${view[0]}`);
701
+ // }
702
+ // };
703
+
704
+ videoElement = webRtcPlayerObj.video;
705
+ store.commit(SET_WEBRTC_OBJ, {
706
+ [roomId]: webRtcPlayerObj
707
+ });
708
+ // On a touch device we will need special ways to show the on-screen keyboard.
709
+ if ('ontouchstart' in document.documentElement) {
710
+ createOnScreenKeyboardHelpers(htmlElement);
711
+ }
712
+
713
+ createWebRtcOffer();
714
+
715
+ return webRtcPlayerObj.video;
716
+ }
717
+
718
+
719
+ function setupWebRtcDataChannel(config)
720
+ {
721
+ // if (!webRtcPlayerObj)
722
+ // {
723
+
724
+ // console.warn('webrtcPlayer Obj == nullptr');
725
+ // return;
726
+ // }
727
+ webRtcDataChannelObj = new RTCPeerConnection(null);
728
+ webRtcDataChannelObj.addEventListener('', () => {
729
+
730
+ switch (webRtcDataChannelObj.iceConnectionState) {
731
+ case 'checking':
732
+ //this.emit('@connectionstatechange', 'connecting');
733
+
734
+ break;
735
+ case 'connected':
736
+ case 'completed':
737
+ //this.emit('@connectionstatechange', 'connected');
738
+ break;
739
+ case 'failed':
740
+ // this.emit('@connectionstatechange', 'failed');
741
+ break;
742
+ case 'disconnected':
743
+ // this.emit('@connectionstatechange', 'disconnected');
744
+ break;
745
+ case 'closed':
746
+ // this.emit('@connectionstatechange', 'closed');
747
+ break;
748
+ }
749
+ });
750
+
751
+
752
+ webRtcDataChannelObj.addEventListener('icegatheringstatechange', (state) => {
753
+
754
+ switch (webRtcDataChannelObj.iceConnectionState) {
755
+ case 'checking':
756
+ //this.emit('@connectionstatechange', 'connecting');
757
+
758
+ break;
759
+ case 'connected':
760
+ case 'completed':
761
+ //this.emit('@connectionstatechange', 'connected');
762
+ break;
763
+ case 'failed':
764
+ // this.emit('@connectionstatechange', 'failed');
765
+ break;
766
+ case 'disconnected':
767
+ // this.emit('@connectionstatechange', 'disconnected');
768
+ break;
769
+ case 'closed':
770
+ // this.emit('@connectionstatechange', 'closed');
771
+ break;
772
+ }
773
+ });
774
+ webRtcDataChannelObj.addEventListener('onicecandidate', (e)=>{
775
+ if (ws && ws.readyState === WS_OPEN_STATE) {
776
+ ws.send(JSON.stringify({
777
+ /* type: 'iceCandidate',*/
778
+ /*candidate: candidate*/
779
+ msg_id: 208,
780
+ data: {
781
+ rtc_type:1,
782
+ type : 'candidate',
783
+ label : e.sdpMLineIndex,
784
+ id : e.sdpMid,
785
+ candidate : e.candidate
786
+ }
787
+ }));
788
+ }
789
+ });
790
+ // webRtcDataChannelObj = new webRtcDataChannel(config);
791
+ // webRtcDataChannelObj.onWebRtcDataChannelOffer = function(webrtc_type, offerStr)
792
+ //{
793
+ // if (ws && ws.readyState === WS_OPEN_STATE)
794
+ // {
795
+ // var room_name = getQueryVariable('roomname');
796
+ // var video_user_name = getQueryVariable('videousername');
797
+ // var codecs = getQueryVariable('codec');;
798
+ //
799
+ // console.log('datachannel cs --> offer : = ', offerStr);
800
+ // let offersdp = JSON.stringify({
801
+ // msg_id: 208,
802
+ // data: {
803
+ // rtc_type: webrtc_type,
804
+ // offer : offerStr.sdp
805
+ // }
806
+ // });
807
+ // console.log(`-> SS: offer:\n${offersdp}`);
808
+ //
809
+ //
810
+ //
811
+ //
812
+ // ws.send(offersdp);
813
+ // }
814
+ //};
815
+
816
+ // webRtcDataChannelObj.onWebRtcDataChannelOCandidate = function(webrtc_type, iceCandidate) {
817
+ // if (ws && ws.readyState === WS_OPEN_STATE) {
818
+ // console.log(`datachannel -> SS: iceCandidate\n${JSON.stringify(iceCandidate, undefined, 4)}`);
819
+ // ws.send(JSON.stringify({
820
+ // /* type: 'iceCandidate',*/
821
+ // /*candidate: candidate*/
822
+ // msg_id: 208,
823
+ // data: {
824
+ // rtc_type:webrtc_type,
825
+ // type : 'candidate',
826
+ // label : iceCandidate.sdpMLineIndex,
827
+ // id : iceCandidate.sdpMid,
828
+ // candidate : iceCandidate.candidate
829
+ // }
830
+ // }));
831
+ // }
832
+ // };
833
+
834
+ // webRtcDataChannelObj.onVideoDataChannelOInitialised = function() {
835
+ // if (ws && ws.readyState === WS_OPEN_STATE) {
836
+ // if (shouldShowPlayOverlay) {
837
+ // showPlayOverlay();
838
+ // resizePlayerStyle();
839
+ // }
840
+ // else {
841
+ // resizePlayerStyle();
842
+ // playVideoStream();
843
+ // }
844
+ // }
845
+ // };
846
+ //
847
+ // webRtcDataChannelObj.onDataChannelDataChannelOConnected = function() {
848
+ // if (ws && ws.readyState === WS_OPEN_STATE)
849
+ // {
850
+ // showTextOverlay('WebRTC connected, waiting for video');
851
+ //
852
+ // if (webRtcPlayerObj.video && webRtcPlayerObj.video.srcObject && webRtcPlayerObj.onVideoInitialised) {
853
+ // webRtcPlayerObj.onVideoInitialised();
854
+ // }
855
+ // }
856
+ // };
857
+ // createWebRtcDataChannelOffer();
858
+ }
859
+
860
+
861
+
862
+ function onWebRtcAnswer(webRTCData) {
863
+ webRtcPlayerObj.receiveAnswer(webRTCData);
864
+
865
+ let printInterval = 5 * 60 * 1000; /*Print every 5 minutes*/
866
+ let nextPrintDuration = printInterval;
867
+
868
+ webRtcPlayerObj.onAggregatedStats = (aggregatedStats) => {
869
+ let numberFormat = new Intl.NumberFormat(window.navigator.language, {
870
+ maximumFractionDigits: 0
871
+ });
872
+ let timeFormat = new Intl.NumberFormat(window.navigator.language, {
873
+ maximumFractionDigits: 0,
874
+ minimumIntegerDigits: 2
875
+ });
876
+
877
+ // Calculate duration of run
878
+ let runTime = (aggregatedStats.timestamp - aggregatedStats.timestampStart) / 1000;
879
+ let timeValues = [];
880
+ let timeDurations = [60, 60];
881
+ for (let timeIndex = 0; timeIndex < timeDurations.length; timeIndex++) {
882
+ timeValues.push(runTime % timeDurations[timeIndex]);
883
+ runTime = runTime / timeDurations[timeIndex];
884
+ }
885
+ timeValues.push(runTime);
886
+
887
+ let runTimeSeconds = timeValues[0];
888
+ let runTimeMinutes = Math.floor(timeValues[1]);
889
+ let runTimeHours = Math.floor([timeValues[2]]);
890
+
891
+ receivedBytesMeasurement = 'B';
892
+ // eslint-disable-next-line no-prototype-builtins
893
+ receivedBytes = aggregatedStats.hasOwnProperty('bytesReceived') ? aggregatedStats.bytesReceived : 0;
894
+ let dataMeasurements = ['kB', 'MB', 'GB'];
895
+ for (let index = 0; index < dataMeasurements.length; index++) {
896
+ if (receivedBytes < 100 * 1000)
897
+ break;
898
+ receivedBytes = receivedBytes / 1000;
899
+ receivedBytesMeasurement = dataMeasurements[index];
900
+ }
901
+
902
+ const orangeQP = 26;
903
+ const redQP = 35;
904
+
905
+ if (print_stats) {
906
+ if (aggregatedStats.timestampStart) {
907
+ if ((aggregatedStats.timestamp - aggregatedStats.timestampStart) > nextPrintDuration) {
908
+ if (ws && ws.readyState === WS_OPEN_STATE) {
909
+ ws.send(JSON.stringify({
910
+ type: 'stats',
911
+ data: aggregatedStats
912
+ }));
913
+ }
914
+ nextPrintDuration += printInterval;
915
+ }
916
+ }
917
+ }
918
+ };
919
+
920
+ webRtcPlayerObj.aggregateStats(1 * 1000 /*Check every 1 second*/ );
921
+
922
+ webRtcPlayerObj.latencyTestTimings.OnAllLatencyTimingsReady = function(timings) {
923
+
924
+ if (!timings.BrowserReceiptTimeMs) {
925
+ return;
926
+ }
927
+
928
+ let latencyExcludingDecode = timings.BrowserReceiptTimeMs - timings.TestStartTimeMs;
929
+ let uePixelStreamLatency = timings.UEPreEncodeTimeMs == 0 || timings.UEPreCaptureTimeMs == 0 ? "???" : timings.UEPostEncodeTimeMs - timings.UEPreCaptureTimeMs;
930
+ let captureLatency = timings.UEPostCaptureTimeMs - timings.UEPreCaptureTimeMs;
931
+ let encodeLatency = timings.UEPostEncodeTimeMs - timings.UEPreEncodeTimeMs;
932
+ let ueLatency = timings.UETransmissionTimeMs - timings.UEReceiptTimeMs;
933
+ let networkLatency = latencyExcludingDecode - ueLatency;
934
+ let browserSendLatency = latencyExcludingDecode - networkLatency - ueLatency;
935
+
936
+ //these ones depend on FrameDisplayDeltaTimeMs
937
+ let endToEndLatency = null;
938
+ let browserSideLatency = null;
939
+
940
+ if (timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs) {
941
+ endToEndLatency = timings.FrameDisplayDeltaTimeMs + latencyExcludingDecode;
942
+ browserSideLatency = endToEndLatency - networkLatency - ueLatency;
943
+ }
944
+ }
945
+ }
946
+
947
+
948
+
949
+ function onWebRtcDataChannelAnswer(webRtcDataChannelData)
950
+ {
951
+ if (webRtcDataChannelObj)
952
+ {
953
+ ///console.log(answer);
954
+ // var answerDesc = new RTCSessionDescription(webRtcDataChannelData);
955
+ webRtcDataChannelObj.setRemoteDescription(webRtcDataChannelData);
956
+
957
+
958
+ }
959
+ }
960
+
961
+ function onWebRtcIce(iceCandidate) {
962
+ if (webRtcPlayerObj)
963
+ webRtcPlayerObj.handleCandidateFromServer(iceCandidate);
964
+ }
965
+
966
+ function onWebRtcDataChannelIce(iceCandidate)
967
+ {
968
+ if (webRtcDataChannelObj)
969
+ {
970
+
971
+ let candidate = new RTCIceCandidate(iceCandidate);
972
+ webRtcDataChannelObj.addIceCandidate(candidate);
973
+ // webRtcDataChannelObj.handleCandidateFromServer(iceCandidate);
974
+ }
975
+ }
976
+
977
+ let styleWidth;
978
+ let styleHeight;
979
+ let styleTop;
980
+ let styleLeft;
981
+ let styleCursor = 'default';
982
+ let styleAdditional;
983
+
984
+ const ControlSchemeType = {
985
+ // A mouse can lock inside the WebRTC player so the user can simply move the
986
+ // mouse to control the orientation of the camera. The user presses the
987
+ // Escape key to unlock the mouse.
988
+ LockedMouse: 0,
989
+
990
+ // A mouse can hover over the WebRTC player so the user needs to click and
991
+ // drag to control the orientation of the camera.
992
+ HoveringMouse: 1
993
+ };
994
+
995
+ let inputOptions = {
996
+ // The control scheme controls the behaviour of the mouse when it interacts
997
+ // with the WebRTC player.
998
+ controlScheme: ControlSchemeType.LockedMouse,
999
+
1000
+ // Browser keys are those which are typically used by the browser UI. We
1001
+ // usually want to suppress these to allow, for example, UE4 to show shader
1002
+ // complexity with the F5 key without the web page refreshing.
1003
+ suppressBrowserKeys: true,
1004
+
1005
+ // UE4 has a faketouches option which fakes a single finger touch when the
1006
+ // user drags with their mouse. We may perform the reverse; a single finger
1007
+ // touch may be converted into a mouse drag UE4 side. This allows a
1008
+ // non-touch application to be controlled partially via a touch device.
1009
+ fakeMouseWithTouches: false
1010
+ };
1011
+
1012
+
1013
+ // Fix for bug in iOS where windowsize is not correct at instance or orientation change
1014
+ // https://github.com/dimsemenov/PhotoSwipe/issues/1315
1015
+ let _orientationChangeTimeout;
1016
+
1017
+ function onOrientationChange(event) {
1018
+ clearTimeout(_orientationChangeTimeout);
1019
+ _orientationChangeTimeout = setTimeout(function() {
1020
+ // resizePlayerStyle();
1021
+ }, 500);
1022
+ }
1023
+
1024
+ // Must be kept in sync with PixelStreamingProtocol::EToUE4Msg C++ enum.
1025
+ const MessageType = {
1026
+
1027
+ /**********************************************************************/
1028
+
1029
+ /*
1030
+ * Control Messages. Range = 0..49.
1031
+ */
1032
+ IFrameRequest: 0,
1033
+ RequestQualityControl: 1,
1034
+ MaxFpsRequest: 2,
1035
+ AverageBitrateRequest: 3,
1036
+ StartStreaming: 4,
1037
+ StopStreaming: 5,
1038
+ LatencyTest: 6,
1039
+ RequestInitialSettings: 7,
1040
+
1041
+ /**********************************************************************/
1042
+
1043
+ /*
1044
+ * Input Messages. Range = 50..89.
1045
+ */
1046
+
1047
+ // Generic Input Messages. Range = 50..59.
1048
+ UIInteraction: 50,
1049
+ Command: 51,
1050
+
1051
+ // Keyboard Input Message. Range = 60..69.
1052
+ KeyDown: 60,
1053
+ KeyUp: 61,
1054
+ KeyPress: 62,
1055
+
1056
+ // Mouse Input Messages. Range = 70..79.
1057
+ MouseEnter: 70,
1058
+ MouseLeave: 71,
1059
+ MouseDown: 72,
1060
+ MouseUp: 73,
1061
+ MouseMove: 74,
1062
+ MouseWheel: 75,
1063
+
1064
+ // Touch Input Messages. Range = 80..89.
1065
+ TouchStart: 80,
1066
+ TouchEnd: 81,
1067
+ TouchMove: 82,
1068
+
1069
+ // Gamepad Input Messages. Range = 90..99
1070
+ GamepadButtonPressed: 90,
1071
+ GamepadButtonReleased: 91,
1072
+ GamepadAnalog: 92
1073
+
1074
+ /**************************************************************************/
1075
+ };
1076
+
1077
+ // A generic message has a type and a descriptor.
1078
+ function emitDescriptor(messageType, descriptor) {
1079
+ // Convert the dscriptor object into a JSON string.
1080
+ let descriptorAsString = JSON.stringify(descriptor);
1081
+
1082
+ // Add the UTF-16 JSON string to the array byte buffer, going two bytes at
1083
+ // a time.
1084
+ let data = new DataView(new ArrayBuffer(1 + 2 + 2 * descriptorAsString.length));
1085
+ let byteIdx = 0;
1086
+ data.setUint8(byteIdx, messageType);
1087
+ byteIdx++;
1088
+ data.setUint16(byteIdx, descriptorAsString.length, true);
1089
+ byteIdx += 2;
1090
+ for (let i = 0; i < descriptorAsString.length; i++) {
1091
+ data.setUint16(byteIdx, descriptorAsString.charCodeAt(i), true);
1092
+ byteIdx += 2;
1093
+ }
1094
+
1095
+ //let str = data.getString(0, data.byteLength, "utf-8");
1096
+ // console.log('str ===== ', str);
1097
+ sendInputData(data.buffer);
1098
+ // sendInputData({Type : });
1099
+ }
1100
+
1101
+ // A UI interation will occur when the user presses a button powered by
1102
+ // JavaScript as opposed to pressing a button which is part of the pixel
1103
+ // streamed UI from the UE4 client.
1104
+ function emitUIInteraction(descriptor) {
1105
+ emitDescriptor(MessageType.UIInteraction, descriptor);
1106
+ }
1107
+
1108
+ // A build-in command can be sent to UE4 client. The commands are defined by a
1109
+ // JSON descriptor and will be executed automatically.
1110
+ // The currently supported commands are:
1111
+ //
1112
+ // 1. A command to run any console command:
1113
+ // "{ ConsoleCommand: <string> }"
1114
+ //
1115
+ // 2. A command to change the resolution to the given width and height.
1116
+ // "{ Resolution.Width: <value>, Resolution.Height: <value> } }"
1117
+ //
1118
+ function emitCommand(descriptor) {
1119
+ emitDescriptor(MessageType.Command, descriptor);
1120
+ }
1121
+
1122
+ function requestInitialSettings() {
1123
+ sendInputData(new Uint8Array([MessageType.RequestInitialSettings]).buffer);
1124
+ }
1125
+
1126
+ function requestQualityControl() {
1127
+ sendInputData(new Uint8Array([MessageType.RequestQualityControl]).buffer);
1128
+ }
1129
+
1130
+ let playerElementClientRect = undefined;
1131
+ let normalizeAndQuantizeUnsigned = undefined;
1132
+ let normalizeAndQuantizeSigned = undefined;
1133
+
1134
+ function setupNormalizeAndQuantize() {
1135
+
1136
+ if (playerElement && videoElement.length > 0) {
1137
+ let playerAspectRatio = playerElement.clientHeight / playerElement.clientWidth;
1138
+ let videoAspectRatio = videoElement[0].videoHeight / videoElement[0].videoWidth;
1139
+ // console.log('videoElement[0].videoHeight = ' + videoElement[0].videoHeight + ', videoElement[0].videoWidth = ' + videoElement[0].videoWidth);
1140
+ // console.log('playerAspectRatio =', playerAspectRatio);
1141
+ // console.log('videoAspectRatio =', videoAspectRatio);
1142
+ // Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
1143
+ // quantized into an uint16 (0..65536).
1144
+ // Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
1145
+ // quantized into an int16 (-32767..32767).
1146
+ // This allows the browser viewport and client viewport to have a different
1147
+ // size.
1148
+ // Hack: Currently we set an out-of-range position to an extreme (65535)
1149
+ // as we can't yet accurately detect mouse enter and leave events
1150
+ // precisely inside a video with an aspect ratio which causes mattes.
1151
+ if (playerAspectRatio > videoAspectRatio)
1152
+ {
1153
+
1154
+ let ratio = playerAspectRatio / videoAspectRatio;
1155
+ // console.log('ratio = ', ratio);
1156
+ // Unsigned.
1157
+ normalizeAndQuantizeUnsigned = (x, y) => {
1158
+ let normalizedX = x / playerElement.clientWidth;
1159
+ let normalizedY = ratio * (y / playerElement.clientHeight - 0.5) + 0.5;
1160
+ // console.log('normalizedX = ' + normalizedX + ', normalizedY = '+ normalizedY);
1161
+ if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0)
1162
+ {
1163
+ return {
1164
+ inRange: false,
1165
+ x: 65535,
1166
+ y: 65535
1167
+ };
1168
+ }
1169
+ else
1170
+ {
1171
+ return {
1172
+ inRange: true,
1173
+ x: normalizedX * 65536,
1174
+ y: normalizedY * 65536
1175
+ };
1176
+ }
1177
+ };
1178
+ unquantizeAndDenormalizeUnsigned = (x, y) => {
1179
+ let normalizedX = x / 65536;
1180
+ let normalizedY = (y / 65536 - 0.5) / ratio + 0.5;
1181
+ return {
1182
+ x: normalizedX * playerElement.clientWidth,
1183
+ y: normalizedY * playerElement.clientHeight
1184
+ };
1185
+ };
1186
+ // Signed.
1187
+ normalizeAndQuantizeSigned = (x, y) => {
1188
+ let normalizedX = x / (0.5 * playerElement.clientWidth);
1189
+ let normalizedY = (ratio * y) / (0.5 * playerElement.clientHeight);
1190
+ return {
1191
+ x: normalizedX * 32767,
1192
+ y: normalizedY * 32767
1193
+ };
1194
+ };
1195
+ }
1196
+ else
1197
+ {
1198
+
1199
+ let ratio = videoAspectRatio / playerAspectRatio;
1200
+ // Unsigned.
1201
+ normalizeAndQuantizeUnsigned = (x, y) => {
1202
+ let normalizedX = ratio * (x / playerElement.clientWidth - 0.5) + 0.5;
1203
+ let normalizedY = y / playerElement.clientHeight;
1204
+ if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
1205
+ return {
1206
+ inRange: false,
1207
+ x: 65535,
1208
+ y: 65535
1209
+ };
1210
+ } else {
1211
+ return {
1212
+ inRange: true,
1213
+ x: normalizedX * 65536,
1214
+ y: normalizedY * 65536
1215
+ };
1216
+ }
1217
+ };
1218
+ // eslint-disable-next-line no-undef
1219
+ unquantizeAndDenormalizeUnsigned = (x, y) => {
1220
+ let normalizedX = (x / 65536 - 0.5) / ratio + 0.5;
1221
+ let normalizedY = y / 65536;
1222
+ return {
1223
+ x: normalizedX * playerElement.clientWidth,
1224
+ y: normalizedY * playerElement.clientHeight
1225
+ };
1226
+ };
1227
+ // Signed.
1228
+ normalizeAndQuantizeSigned = (x, y) => {
1229
+ let normalizedX = (ratio * x) / (0.5 * playerElement.clientWidth);
1230
+ let normalizedY = y / (0.5 * playerElement.clientHeight);
1231
+ return {
1232
+ x: normalizedX * 32767,
1233
+ y: normalizedY * 32767
1234
+ };
1235
+ };
1236
+ }
1237
+ }
1238
+ }
1239
+
1240
+ function createOnScreenKeyboardHelpers(htmlElement) {
1241
+ if (document.getElementById('hiddenInput') === null) {
1242
+ hiddenInput = document.createElement('input');
1243
+ hiddenInput.id = 'hiddenInput';
1244
+ hiddenInput.maxLength = 0;
1245
+ htmlElement.appendChild(hiddenInput);
1246
+ }
1247
+
1248
+ if (document.getElementById('editTextButton') === null) {
1249
+ editTextButton = document.createElement('button');
1250
+ editTextButton.id = 'editTextButton';
1251
+ editTextButton.innerHTML = 'edit text';
1252
+ htmlElement.appendChild(editTextButton);
1253
+
1254
+ // Hide the 'edit text' button.
1255
+ editTextButton.classList.add('hiddenState');
1256
+
1257
+ editTextButton.addEventListener('click', function() {
1258
+ // Show the on-screen keyboard.
1259
+ hiddenInput.focus();
1260
+ });
1261
+ }
1262
+ }
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+ function onExpandOverlay_Click( /* e */ ) {
1269
+ let overlay = document.getElementById('overlay');
1270
+ overlay.classList.toggle("overlay-shown");
1271
+ }
1272
+
1273
+ function start() {
1274
+ // update "quality status" to "disconnected" state
1275
+ connect();
1276
+ updateKickButton(0);
1277
+ }
1278
+
1279
+ function updateKickButton(playersCount) {
1280
+ let kickButton = document.getElementById('kick-other-players-button');
1281
+ if (kickButton)
1282
+ kickButton.value = `Kick (${playersCount})`;
1283
+ }
1284
+
1285
+ function connect() {
1286
+ "use strict";
1287
+
1288
+ window.WebSocket = window.WebSocket || window.MozWebSocket;
1289
+
1290
+ if (!window.WebSocket) {
1291
+ alert('Your browser doesn\'t support WebSocket');
1292
+ return;
1293
+ }
1294
+
1295
+ //window.location.href.replace('http://', 'ws://').replace('https://', 'wss://')
1296
+ var rtc_ip = getQueryVariable('rtc_ip');
1297
+ var rtc_port = getQueryVariable('rtc_port');
1298
+ var video_user_name = getQueryVariable('videousername');
1299
+ var ws_url = 'ws://'+rtc_ip+':'+rtc_port +'/?peerId='+video_user_name;
1300
+
1301
+ ws = new WebSocket(ws_url);
1302
+
1303
+ ws.onmessage = function(event) {
1304
+
1305
+ let msg = JSON.parse(event.data);
1306
+ /*
1307
+ if (msg.type === 'config') {
1308
+ onConfig(msg);
1309
+ } else if (msg.type === 'playerCount') {
1310
+ updateKickButton(msg.count - 1);
1311
+ } else
1312
+ */
1313
+ if (msg.msg_id === 1075)
1314
+ // eslint-disable-next-line no-mixed-spaces-and-tabs
1315
+ {
1316
+ // console.log(msg.data.sdp);
1317
+ onWebRtcAnswer(msg.data);
1318
+ /*
1319
+ } else if (msg.type === 'iceCandidate') {
1320
+ onWebRtcIce(msg.candidate);
1321
+ */
1322
+ }
1323
+ else if (msg.msg_id === 211)
1324
+ {
1325
+ console.log('')
1326
+ }
1327
+ else if (msg.msg_id === 500)
1328
+ {
1329
+ if (msg.data.type === 'answer')
1330
+ {
1331
+ var answerDesc = new RTCSessionDescription({type: 'answer', sdp: msg.data.sdp});
1332
+ if (msg.data.rtc_type === 0)
1333
+ {
1334
+ onWebRtcAnswer(answerDesc);
1335
+ }
1336
+ else
1337
+ {
1338
+ onWebRtcDataChannelAnswer(answerDesc);
1339
+ }
1340
+ }
1341
+ // eslint-disable-next-line no-prototype-builtins
1342
+ else if (msg.data.hasOwnProperty('candidate'))
1343
+ {
1344
+ //let candidate = new RTCIceCandidate({type: 'candidate', sdp: msg.data.candidate});
1345
+ //var candidate = new RTCIceCandidate({
1346
+ // sdpMLineIndex :0,
1347
+ // candidate: msg.data.candidate
1348
+ //});
1349
+
1350
+ if (msg.data.rtc_type === 0)
1351
+ {
1352
+ onWebRtcIce({sdpMid:msg.data.id, sdpMLineIndex :msg.data.label, candidate:msg.data.candidate});
1353
+ }
1354
+ else
1355
+ {
1356
+ onWebRtcDataChannelIce({sdpMid:msg.data.id, sdpMLineIndex :msg.data.label, candidate:msg.data.candidate});
1357
+ }
1358
+ }
1359
+ }
1360
+ else
1361
+ {
1362
+ }
1363
+
1364
+ };
1365
+
1366
+ ws.onerror = function(event) {
1367
+ console.log(`WS error: ${JSON.stringify(event)}`);
1368
+ };
1369
+
1370
+ ws.onclose = function(event) {
1371
+ console.log(`WS closed: ${JSON.stringify(event.code)} - ${event.reason}`);
1372
+ ws = undefined;
1373
+ is_reconnection = true;
1374
+
1375
+ // destroy `webRtcPlayerObj` if any
1376
+ if (webRtcPlayerObj) {
1377
+ playerElement.removeChild(webRtcPlayerObj.video);
1378
+ webRtcPlayerObj.close();
1379
+ webRtcPlayerObj = undefined;
1380
+ }
1381
+ if (webRtcDataChannelObj)
1382
+ {
1383
+ webRtcDataChannelObj.close();
1384
+ webRtcDataChannelObj = null;
1385
+ }
1386
+ // if (webRtcDataChannelObj)
1387
+ {
1388
+ // webRtcDataChannelObj.close();
1389
+ // webRtcDataChannelObj = undefined;
1390
+ }
1391
+
1392
+ showTextOverlay(`Disconnected: ${event.reason}`);
1393
+ start()
1394
+ };
1395
+ ws.onopen = function(event)
1396
+ {
1397
+ var room_name = getQueryVariable('roomname');
1398
+ var video_user_name = getQueryVariable('videousername');
1399
+ let offersdp = JSON.stringify({
1400
+ msg_id: 202,
1401
+ data: {
1402
+ user_name : video_user_name.toString(),
1403
+ room_name : room_name.toString()
1404
+ }
1405
+ });
1406
+
1407
+ ws.send(offersdp);
1408
+ onConfig('');
1409
+ }
1410
+ }
1411
+
1412
+ // Config data received from WebRTC sender via the Cirrus web server
1413
+ function onConfig(config) {
1414
+ setupWebRtcPlayer(playerElement, config);
1415
+ //setupWebRtcDataChannel(config);
1416
+ }
1417
+
1418
+ export const load = async (ele, id, obj, styleName) => {
1419
+ roomId = id;
1420
+ playerElement = ele;
1421
+ params = obj;
1422
+ // eslint-disable-next-line no-self-assign
1423
+ className = styleName;
1424
+ await start();
1425
+ return videoElement;
1426
+ }