@stormstreaming/stormstreamer 0.9.2-beta.2 → 0.9.3-beta.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.
Files changed (51) hide show
  1. package/README.md +130 -3
  2. package/dist/amd/index.js +2206 -105
  3. package/dist/cjs/index.js +3 -3
  4. package/dist/esm/index.js +3 -3
  5. package/dist/iife/index.js +4 -4
  6. package/dist/types/StormStreamer.d.ts +394 -3
  7. package/dist/types/config/AudioData.d.ts +46 -0
  8. package/dist/types/config/ConfigManager.d.ts +47 -0
  9. package/dist/types/config/DebugData.d.ts +108 -0
  10. package/dist/types/config/IConfig.d.ts +3 -0
  11. package/dist/types/config/SettingsData.d.ts +114 -0
  12. package/dist/types/config/StorageData.d.ts +46 -0
  13. package/dist/types/config/StreamData.d.ts +75 -0
  14. package/dist/types/config/VideoData.d.ts +115 -0
  15. package/dist/types/config/enum/LogType.d.ts +3 -0
  16. package/dist/types/config/enum/ProtocolType.d.ts +3 -0
  17. package/dist/types/config/enum/ScalingType.d.ts +3 -0
  18. package/dist/types/config/enum/SecurityType.d.ts +3 -0
  19. package/dist/types/config/enum/SizeCalculationType.d.ts +3 -0
  20. package/dist/types/events/EventDispatcher.d.ts +34 -0
  21. package/dist/types/graph/MicrophoneGraph.d.ts +11 -0
  22. package/dist/types/logger/Logger.d.ts +103 -0
  23. package/dist/types/model/AbstractSourceItem.d.ts +23 -0
  24. package/dist/types/model/GatewayServerItem.d.ts +53 -0
  25. package/dist/types/model/IServerItem.d.ts +3 -0
  26. package/dist/types/model/ISourceItem.d.ts +3 -0
  27. package/dist/types/model/IStreamItem.d.ts +3 -0
  28. package/dist/types/model/RTMPSourceItem.d.ts +50 -0
  29. package/dist/types/model/RTSPSourceItem.d.ts +50 -0
  30. package/dist/types/model/StormMetaDataItem.d.ts +3 -0
  31. package/dist/types/model/StormServerItem.d.ts +53 -0
  32. package/dist/types/model/StormSourceItem.d.ts +27 -0
  33. package/dist/types/model/StreamInfo.d.ts +46 -0
  34. package/dist/types/network/AbstractSocket.d.ts +94 -0
  35. package/dist/types/network/NetworkController.d.ts +33 -0
  36. package/dist/types/network/WowzaConnection.d.ts +63 -0
  37. package/dist/types/network/WowzaStatusConnection.d.ts +54 -0
  38. package/dist/types/playback/CooldownMonitor.d.ts +17 -0
  39. package/dist/types/playback/StreamerController.d.ts +267 -1
  40. package/dist/types/playback/enum/ConnectionState.d.ts +3 -0
  41. package/dist/types/playback/player/AbstractPlayer.d.ts +23 -0
  42. package/dist/types/playback/task/IPlaybackTask.d.ts +3 -0
  43. package/dist/types/stage/ScreenElement.d.ts +54 -0
  44. package/dist/types/stage/StageController.d.ts +86 -0
  45. package/dist/types/statistics/StatsController.d.ts +8 -0
  46. package/dist/types/storage/StorageManager.d.ts +37 -0
  47. package/dist/types/utilities/DomUtilities.d.ts +7 -0
  48. package/dist/types/utilities/NumberUtilities.d.ts +7 -0
  49. package/dist/types/utilities/UserCapabilities.d.ts +44 -0
  50. package/dist/umd/index.js +4 -4
  51. package/package.json +1 -1
package/dist/amd/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * contact@stormstreaming.com
5
5
  * https://stormstreaming.com
6
6
  *
7
- * Version: 0.9.2-beta.2
8
- * Version: 3/6/2025, 2:53:17 PM
7
+ * Version: 0.9.3-beta.0
8
+ * Version: 3/8/2025, 11:51:36 PM
9
9
  *
10
10
  * LEGAL NOTICE:
11
11
  * This software is subject to the terms and conditions defined in
@@ -45,7 +45,18 @@
45
45
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
46
  };
47
47
 
48
+ /**
49
+ * Class stores information related to storm servers
50
+ */
48
51
  class StormServerItem {
52
+ /**
53
+ * Constructor
54
+ *
55
+ * @param host server URL e.g. "cdn-e001.stormstreaming.com"
56
+ * @param applicationName e.g. "live
57
+ * @param port usually 443
58
+ * @param isSSL whenever connection should be stablished via SSL (true by default)
59
+ */
49
60
  constructor(host, application, port = 443, isSSL = true) {
50
61
  this.host = host;
51
62
  this.application = application;
@@ -53,24 +64,46 @@
53
64
  this.isSSL = isSSL;
54
65
  this.hasFaild = false;
55
66
  }
67
+ /**
68
+ * Returns server URL
69
+ */
56
70
  getHost() {
57
71
  return this.host;
58
72
  }
73
+ /**
74
+ * Returns server application
75
+ */
59
76
  getApplication() {
60
77
  return this.application;
61
78
  }
79
+ /**
80
+ * Returns port number
81
+ */
62
82
  getPort() {
63
83
  return this.port;
64
84
  }
85
+ /**
86
+ * Returns whenever connection should be established via SSL
87
+ */
65
88
  getIfSSL() {
66
89
  return this.isSSL;
67
90
  }
91
+ /**
92
+ * Returns whenever connection faild while trying to connect
93
+ */
68
94
  getIfFaild() {
69
95
  return this.hasFaild;
70
96
  }
97
+ /**
98
+ * Marks this server as faild, prevent it from being used anymore
99
+ * @param value
100
+ */
71
101
  setAsFaild(value) {
72
102
  this.hasFaild = value;
73
103
  }
104
+ /**
105
+ * Returns data from this object
106
+ */
74
107
  getData() {
75
108
  return {
76
109
  serverURL: this.getHost(),
@@ -84,13 +117,29 @@
84
117
  }
85
118
  }
86
119
 
120
+ /**
121
+ * Class contains streaming data
122
+ */
87
123
  class StreamData {
124
+ //------------------------------------------------------------------------//
125
+ // CONSTRUCTOR
126
+ //------------------------------------------------------------------------//
127
+ /**
128
+ * Constructor
129
+ * @param streamConfig
130
+ */
88
131
  constructor(streamConfig) {
89
132
  this._serverList = new Array();
90
133
  this._sourceList = new Array();
91
134
  this._streamKey = null;
92
135
  this.parse(streamConfig);
93
136
  }
137
+ //------------------------------------------------------------------------//
138
+ // MAIN METHODS
139
+ //------------------------------------------------------------------------//
140
+ /**
141
+ * Parses provided config
142
+ */
94
143
  parse(streamConfig) {
95
144
  var _a, _b, _c;
96
145
  this._streamConfig = streamConfig;
@@ -110,30 +159,68 @@
110
159
  } else throw new Error("StormLibrary: Server list configuration is missing. Please check the config!");
111
160
  this._streamKey = (_c = this._streamConfig.streamKey) !== null && _c !== void 0 ? _c : this._streamKey;
112
161
  }
162
+ //------------------------------------------------------------------------//
163
+ // GETS & SETS
164
+ //------------------------------------------------------------------------//
165
+ /**
166
+ * Returns list of all provided servers
167
+ */
113
168
  getServerList() {
114
169
  return this._serverList;
115
170
  }
171
+ /**
172
+ * Returns list of all video sources
173
+ */
116
174
  getSourceList() {
117
175
  return this._sourceList;
118
176
  }
177
+ /**
178
+ * Returns group name;
179
+ */
119
180
  get streamKey() {
120
181
  return this._streamKey;
121
182
  }
183
+ /**
184
+ * Returns group name;
185
+ */
122
186
  set streamKey(newValue) {
123
187
  this._streamKey = newValue;
124
188
  }
189
+ /**
190
+ * Allows to push server list to the config
191
+ * @param serverList
192
+ */
125
193
  set serverList(serverList) {
126
194
  this._serverList = serverList;
127
195
  }
196
+ /**
197
+ * Allows to push source list to the config
198
+ * @param sourceList
199
+ */
128
200
  set sourceList(sourceList) {
129
201
  this._sourceList = sourceList;
130
202
  }
203
+ //------------------------------------------------------------------------//
204
+ // OTHER
205
+ //------------------------------------------------------------------------//
206
+ /**
207
+ * Removes all sources
208
+ */
131
209
  clearSourceList() {
132
210
  this._sourceList = new Array();
133
211
  }
212
+ /**
213
+ * Removes all SERVERS
214
+ */
134
215
  clearServerList() {
135
216
  this._serverList = new Array();
136
217
  }
218
+ /**
219
+ * Prints current settings.
220
+ *
221
+ * @param logger reference to logger
222
+ * @param force if printing is disabled, this parameter can overwrite it
223
+ */
137
224
  print(logger, force = false) {
138
225
  if (StreamData.PRINT_ON_STARTUP || force) {
139
226
  logger.info(this, "Server List:");
@@ -144,10 +231,26 @@
144
231
  }
145
232
  }
146
233
  }
234
+ /**
235
+ * Decides whenever player will print this config data on startup
236
+ *
237
+ * @private
238
+ */
147
239
  StreamData.PRINT_ON_STARTUP = true;
240
+ /**
241
+ * Default storm port (usually 443)
242
+ * @private
243
+ */
148
244
  StreamData.DEFAULT_CONNECTION_PORT = 443;
245
+ /**
246
+ * Whenever all connection to strom should be made via SSL
247
+ * @private
248
+ */
149
249
  StreamData.IS_SSL_BY_DEFAULT = true;
150
250
 
251
+ /**
252
+ * Different types of video scaling mechanisms
253
+ */
151
254
  var ScalingType;
152
255
  (function (ScalingType) {
153
256
  ScalingType["FILL"] = "fill";
@@ -156,6 +259,9 @@
156
259
  ScalingType["ORIGINAL"] = "original";
157
260
  })(ScalingType || (ScalingType = {}));
158
261
 
262
+ /**
263
+ * Different types of security methods for player
264
+ */
159
265
  var SizeCalculationType;
160
266
  (function (SizeCalculationType) {
161
267
  SizeCalculationType["CLIENT_DIMENSIONS"] = "clientDimensions";
@@ -163,20 +269,77 @@
163
269
  SizeCalculationType["FULL_BOX"] = "fullBox";
164
270
  })(SizeCalculationType || (SizeCalculationType = {}));
165
271
 
272
+ /**
273
+ * Class contains settings for video object
274
+ */
166
275
  class VideoData {
276
+ //------------------------------------------------------------------------//
277
+ // CONSTRUCTOR
278
+ //------------------------------------------------------------------------//
279
+ /**
280
+ * Constructor
281
+ *
282
+ * @param videoConfig video config object
283
+ */
167
284
  constructor(videoConfig) {
285
+ /**
286
+ * Selected scaling mode
287
+ * @private
288
+ */
168
289
  this._scalingMode = ScalingType.LETTER_BOX;
290
+ /**
291
+ * Aspect ratio saved as a string (two numbers with : in between)
292
+ * @private
293
+ */
169
294
  this._aspectRatio = "none";
295
+ /**
296
+ * Initial video container width
297
+ * @private
298
+ */
170
299
  this._videoWidthValue = 100;
300
+ /**
301
+ * Whenever width is in pixels
302
+ * @private
303
+ */
171
304
  this._isVideoWidthInPixels = false;
305
+ /**
306
+ * Whenever width was provided
307
+ * @private
308
+ */
172
309
  this._wasVideoWidthProvided = false;
310
+ /**
311
+ * Initial video container height;
312
+ * @private
313
+ */
173
314
  this._videoHeightValue = 100;
315
+ /**
316
+ * Whenever height is in pixels
317
+ * @private
318
+ */
174
319
  this._isVideoHeightInPixels = false;
320
+ /**
321
+ * Whenever height was provided
322
+ * @private
323
+ */
175
324
  this._wasVideoHeightProvided = false;
325
+ /**
326
+ * Resize debounce parameter
327
+ * @private
328
+ */
176
329
  this._resizeDebounce = 250;
330
+ /**
331
+ * Method used for calculating parent size;
332
+ * @private
333
+ */
177
334
  this._parentSizeCalculationMethod = SizeCalculationType.CLIENT_DIMENSIONS;
178
335
  this.parse(videoConfig);
179
336
  }
337
+ //------------------------------------------------------------------------//
338
+ // MAIN METHODS
339
+ //------------------------------------------------------------------------//
340
+ /**
341
+ * Parses provided config
342
+ */
180
343
  parse(config) {
181
344
  var _a, _b;
182
345
  this.videoConfig = config;
@@ -262,30 +425,60 @@
262
425
  this._resizeDebounce = (_b = this.videoConfig.resizeDebounce) !== null && _b !== void 0 ? _b : this._resizeDebounce;
263
426
  } else throw new Error("Missing video configuration. Please check player config!");
264
427
  }
428
+ //------------------------------------------------------------------------//
429
+ // GETS & SETS
430
+ //------------------------------------------------------------------------//
431
+ /**
432
+ * Returns selected scaling mode
433
+ */
265
434
  get scalingMode() {
266
435
  return this._scalingMode;
267
436
  }
437
+ /**
438
+ * Returns ID of the main video container
439
+ */
268
440
  get containerID() {
269
441
  return this._containerID;
270
442
  }
443
+ /**
444
+ * Returns video screen initial width
445
+ */
271
446
  get videoWidthValue() {
272
447
  return this._videoWidthValue;
273
448
  }
449
+ /**
450
+ * Returns whenever screen width was provided in pixels
451
+ */
274
452
  get videoWidthInPixels() {
275
453
  return this._isVideoWidthInPixels;
276
454
  }
455
+ /**
456
+ * Returns whenever screen width was provided in pixels
457
+ */
277
458
  get videoWidthProvided() {
278
459
  return this._wasVideoWidthProvided;
279
460
  }
461
+ /**
462
+ * Returns video container initial width
463
+ */
280
464
  get videoHeightValue() {
281
465
  return this._videoHeightValue;
282
466
  }
467
+ /**
468
+ * Returns video container initial width
469
+ */
283
470
  get videoHeightInPixels() {
284
471
  return this._isVideoHeightInPixels;
285
472
  }
473
+ /**
474
+ * Returns whenever screen width was provided in pixels
475
+ */
286
476
  get videoHeightProvided() {
287
477
  return this._wasVideoHeightProvided;
288
478
  }
479
+ /**
480
+ * Returns aspect ratio;
481
+ */
289
482
  get aspectRatio() {
290
483
  return this._aspectRatio;
291
484
  }
@@ -295,21 +488,45 @@
295
488
  set resizeDebounce(newValue) {
296
489
  this._resizeDebounce = newValue;
297
490
  }
491
+ /**
492
+ * Sets new width to the player config
493
+ * @param newWidth
494
+ */
298
495
  set videoWidthValue(newWidth) {
299
496
  this._videoWidthValue = newWidth;
300
497
  }
498
+ /**
499
+ * Sets new width to the player config
500
+ * @param newWidth
501
+ */
301
502
  set videoWidthInPixels(value) {
302
503
  this._isVideoWidthInPixels = value;
303
504
  }
505
+ /**
506
+ * Sets new height to the player config
507
+ * @param newHeight
508
+ */
304
509
  set videoHeightValue(newHeight) {
305
510
  this._videoHeightValue = newHeight;
306
511
  }
512
+ /**
513
+ * Sets new width to the player config
514
+ * @param newWidth
515
+ */
307
516
  set videoHeightInPixels(value) {
308
517
  this._isVideoHeightInPixels = value;
309
518
  }
519
+ /**
520
+ * Sets new containerID for the player
521
+ * @param newContainerID
522
+ */
310
523
  set containerID(newContainerID) {
311
524
  this._containerID = newContainerID;
312
525
  }
526
+ /**
527
+ * Sets new scaling mode
528
+ * @param newScalingMode
529
+ */
313
530
  set scalingMode(newScalingMode) {
314
531
  switch (newScalingMode.toLowerCase()) {
315
532
  case "fill":
@@ -331,6 +548,14 @@
331
548
  get parentSizeCalculationMethod() {
332
549
  return this._parentSizeCalculationMethod;
333
550
  }
551
+ //------------------------------------------------------------------------//
552
+ // OTHER
553
+ //------------------------------------------------------------------------//
554
+ /**
555
+ * Prints current settings
556
+ *
557
+ * @param logger
558
+ */
334
559
  print(logger) {
335
560
  let scalingMode = "";
336
561
  switch (this._scalingMode) {
@@ -355,6 +580,9 @@
355
580
  }
356
581
  }
357
582
 
583
+ /**
584
+ * Contains all log types
585
+ */
358
586
  var LogType;
359
587
  (function (LogType) {
360
588
  LogType[LogType["TRACE"] = 0] = "TRACE";
@@ -364,18 +592,61 @@
364
592
  LogType[LogType["ERROR"] = 4] = "ERROR";
365
593
  })(LogType || (LogType = {}));
366
594
 
595
+ /**
596
+ * This class represents "debug" section of an original player config. If any field/value is missing in the config,
597
+ * default values defined within this class will be used instead.
598
+ */
367
599
  class DebugData {
600
+ //------------------------------------------------------------------------//
601
+ // CONSTRUCTOR
602
+ //------------------------------------------------------------------------//
603
+ /**
604
+ * Creates debug object based on parameters from config object
605
+ *
606
+ * @param debugConfig
607
+ */
368
608
  constructor(debugConfig) {
609
+ /**
610
+ * Decides whenever log will be outputted in browser console
611
+ * @private
612
+ */
369
613
  this._consoleLogEnabled = false;
614
+ /**
615
+ * List of enabled console log types (order doesn't matter)
616
+ * @private
617
+ */
370
618
  this._enabledConsoleTypes = [LogType.INFO, LogType.ERROR, LogType.SUCCESS, LogType.TRACE, LogType.WARNING];
619
+ /**
620
+ * If true, different types of logs for console will have the same color, but each player will have
621
+ * its own color (easier to debug with multiple players)
622
+ *
623
+ * @private
624
+ */
371
625
  this._consoleMonoColor = false;
626
+ /**
627
+ * List of enabled console log types (order doesn't matter)
628
+ * @private
629
+ */
372
630
  this._containerLogEnabled = false;
631
+ /**
632
+ *
633
+ * @private
634
+ */
373
635
  this._enabledContainerTypes = [LogType.INFO, LogType.ERROR, LogType.SUCCESS, LogType.TRACE, LogType.WARNING];
636
+ /**
637
+ * If true, different types of logs for container will have the same color, but each player will have
638
+ * its own color (easier to debug with multiple players)
639
+ *
640
+ * @private
641
+ */
374
642
  this._containerLogMonoColor = false;
375
643
  this._stageController = true;
376
644
  this._streamerController = true;
377
645
  this.parse(debugConfig);
378
646
  }
647
+ /**
648
+ * Parses provided config
649
+ */
379
650
  parse(debugConfig) {
380
651
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
381
652
  this._debugConfig = debugConfig;
@@ -409,15 +680,32 @@
409
680
  }
410
681
  });
411
682
  }
683
+ //------------------------------------------------------------------------//
684
+ // GETS & SETS
685
+ //------------------------------------------------------------------------//
686
+ /**
687
+ * Returns whenever logs from debug will be pushed to browser console. False by default.
688
+ */
412
689
  get consoleLogEnabled() {
413
690
  return this._consoleLogEnabled;
414
691
  }
692
+ /**
693
+ * Sets console logging on/off
694
+ * @param newValue
695
+ */
415
696
  set consoleLogEnabled(newValue) {
416
697
  this._consoleLogEnabled = newValue;
417
698
  }
699
+ /**
700
+ * Returns all enabled types of logs for console logging
701
+ */
418
702
  get enabledConsoleTypes() {
419
703
  return this._enabledConsoleTypes;
420
704
  }
705
+ /**
706
+ * Sets console log types
707
+ * @param newValue
708
+ */
421
709
  set enabledConsoleTypes(newValue) {
422
710
  this._enabledConsoleTypes = new Array();
423
711
  for (let i = 0; i < newValue.length; i++) {
@@ -440,21 +728,42 @@
440
728
  }
441
729
  }
442
730
  }
731
+ /**
732
+ * Returns whenever logs from debug will be pushed to a cointainer. False by default.
733
+ */
443
734
  get containerLogEnabled() {
444
735
  return this._containerLogEnabled;
445
736
  }
737
+ /**
738
+ * Sets container debugging on/off
739
+ * @param newValue
740
+ */
446
741
  set containerLogEnabled(newValue) {
447
742
  this._consoleLogEnabled = newValue;
448
743
  }
744
+ /**
745
+ * Retruns true if all console outputs will have the same color (depending on playerID)
746
+ */
449
747
  get consoleLogMonoColor() {
450
748
  return this._consoleMonoColor;
451
749
  }
750
+ /**
751
+ * Sets console logging to monocolor
752
+ * @param newValue
753
+ */
452
754
  set consoleLogMonoColor(newValue) {
453
755
  this._consoleMonoColor = newValue;
454
756
  }
757
+ /**
758
+ * Returns all enabled types of logs for container logging
759
+ */
455
760
  get enabledContainerTypes() {
456
761
  return this._enabledContainerTypes;
457
762
  }
763
+ /**
764
+ * Sets console log types
765
+ * @param newValue
766
+ */
458
767
  set enabledContainerTypes(newValue) {
459
768
  this._enabledContainerTypes = new Array();
460
769
  for (let i = 0; i < newValue.length; i++) {
@@ -477,15 +786,29 @@
477
786
  }
478
787
  }
479
788
  }
789
+ /**
790
+ * Return a reference to a object where logs will be pushed as text. Null by default.
791
+ */
480
792
  get containerID() {
481
793
  return this._containerID;
482
794
  }
795
+ /**
796
+ * Sets container for logging
797
+ * @param object
798
+ */
483
799
  set containerID(object) {
484
800
  this._containerID = object;
485
801
  }
802
+ /**
803
+ * Retruns true if all container outputs will have the same color (depending on playerID)
804
+ */
486
805
  get containerLogMonoColor() {
487
806
  return this._containerLogMonoColor;
488
807
  }
808
+ /**
809
+ * Sets container logging to monocolor
810
+ * @param newValue
811
+ */
489
812
  set containerLogMonoColor(newValue) {
490
813
  this._containerLogMonoColor = newValue;
491
814
  }
@@ -495,6 +818,14 @@
495
818
  get streamerControllerDebug() {
496
819
  return this._streamerController;
497
820
  }
821
+ //------------------------------------------------------------------------//
822
+ // OTHER
823
+ //------------------------------------------------------------------------//
824
+ /**
825
+ * Prints current settings
826
+ *
827
+ * @param logger
828
+ */
498
829
  print(logger, force = false) {
499
830
  if (DebugData.PRINT_ON_STARTUP || force) {
500
831
  let consoleLogTypes = "";
@@ -547,14 +878,43 @@
547
878
  }
548
879
  }
549
880
  }
881
+ /**
882
+ * Decides whenever player will print this config data on startup
883
+ *
884
+ * @private
885
+ */
550
886
  DebugData.PRINT_ON_STARTUP = true;
551
887
 
888
+ /**
889
+ * Class contains all parameters related to volume
890
+ */
552
891
  class AudioData {
892
+ //------------------------------------------------------------------------//
893
+ // CONSTRUCTOR
894
+ //------------------------------------------------------------------------//
895
+ /**
896
+ * Constructor
897
+ * @param volumeConfig
898
+ */
553
899
  constructor(volumeConfig) {
900
+ /**
901
+ * Default value for volume
902
+ * @private
903
+ */
554
904
  this._startVolume = 100;
905
+ /**
906
+ * Whenever video is muted
907
+ * @private
908
+ */
555
909
  this._isMuted = false;
556
910
  this.parse(volumeConfig);
557
911
  }
912
+ //------------------------------------------------------------------------//
913
+ // MAIN METHODS
914
+ //------------------------------------------------------------------------//
915
+ /**
916
+ * Parses provided config
917
+ */
558
918
  parse(config) {
559
919
  var _a, _b, _c, _d;
560
920
  this._audioConfig = config;
@@ -563,67 +923,203 @@
563
923
  this._isMuted = (_d = (_c = this._audioConfig) === null || _c === void 0 ? void 0 : _c.muted) !== null && _d !== void 0 ? _d : this._isMuted;
564
924
  }
565
925
  }
926
+ //------------------------------------------------------------------------//
927
+ // GETS & SETS
928
+ //------------------------------------------------------------------------//
929
+ /**
930
+ * Returns player start volume
931
+ */
566
932
  get startVolume() {
567
933
  return this._startVolume;
568
934
  }
935
+ /**
936
+ * Sets start volume for the player (by default it's 100)
937
+ * @param newValue
938
+ */
569
939
  set startVolume(newValue) {
570
940
  this._startVolume = newValue;
571
941
  }
942
+ /**
943
+ * Returns whenever library should be muted on start
944
+ */
572
945
  get muted() {
573
946
  return this._isMuted;
574
947
  }
948
+ /**
949
+ * Sets whenever library should be muted or not
950
+ * @param newValue
951
+ */
575
952
  set muted(newValue) {
576
953
  this._isMuted = newValue;
577
954
  }
955
+ //------------------------------------------------------------------------//
956
+ // OTHER
957
+ //------------------------------------------------------------------------//
958
+ /**
959
+ * Prints current settings
960
+ *
961
+ * @param logger
962
+ */
578
963
  print(logger, force = false) {
579
964
  if (AudioData.PRINT_ON_STARTUP || force) logger.info(this, "Audio :: startVolume: " + this._startVolume + " | isMuted: " + this._isMuted);
580
965
  }
581
966
  }
967
+ /**
968
+ * Decides whenever player will print this config data on startup
969
+ *
970
+ * @private
971
+ */
582
972
  AudioData.PRINT_ON_STARTUP = true;
583
973
 
974
+ /**
975
+ * Class contains all parameters related to volume
976
+ */
584
977
  class StorageData {
978
+ //------------------------------------------------------------------------//
979
+ // CONSTRUCTOR
980
+ //------------------------------------------------------------------------//
981
+ /**
982
+ * Constructor
983
+ * @param storageConfig
984
+ */
585
985
  constructor(storageConfig) {
986
+ /**
987
+ * Whenever storage is enabled or not (true by default)
988
+ * @private
989
+ */
586
990
  this._enabled = true;
991
+ /**
992
+ * Prefix for loading and saving settings
993
+ * @private
994
+ */
587
995
  this._prefix = "storm";
588
996
  this.parse(storageConfig);
589
997
  }
998
+ //------------------------------------------------------------------------//
999
+ // MAIN METHODS
1000
+ //------------------------------------------------------------------------//
1001
+ /**
1002
+ * Parses provided config
1003
+ */
590
1004
  parse(config) {
591
1005
  var _a, _b, _c, _d;
592
1006
  this._storageConfig = config;
593
1007
  this._enabled = (_b = (_a = this._storageConfig) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : this._enabled;
594
1008
  this._prefix = (_d = (_c = this._storageConfig) === null || _c === void 0 ? void 0 : _c.prefix) !== null && _d !== void 0 ? _d : this._prefix;
595
1009
  }
1010
+ //------------------------------------------------------------------------//
1011
+ // GETS & SETS
1012
+ //------------------------------------------------------------------------//
1013
+ /**
1014
+ * Returns if storage is enabled
1015
+ */
596
1016
  get enabled() {
597
1017
  return this._enabled;
598
1018
  }
1019
+ /**
1020
+ * Sets new value for enabled
1021
+ * @param newValue
1022
+ */
599
1023
  set enabled(newValue) {
600
1024
  this._enabled = newValue;
601
1025
  }
1026
+ /**
1027
+ * Returns storage prefix
1028
+ */
602
1029
  get prefix() {
603
1030
  return this._prefix;
604
1031
  }
1032
+ /**
1033
+ * Sets storage prefix
1034
+ * @param newValue
1035
+ */
605
1036
  set prefix(newValue) {
606
1037
  this._prefix = newValue;
607
1038
  }
1039
+ //------------------------------------------------------------------------//
1040
+ // OTHER
1041
+ //------------------------------------------------------------------------//
1042
+ /**
1043
+ * Prints current settings
1044
+ *
1045
+ * @param logger
1046
+ */
608
1047
  print(logger, force = false) {
609
1048
  if (StorageData.PRINT_ON_STARTUP || force) logger.info(this, "Storage :: startVolume: " + this._enabled + " | prefix: " + this._prefix);
610
1049
  }
611
1050
  }
1051
+ /**
1052
+ * Decides whenever player will print this config data on startup
1053
+ *
1054
+ * @private
1055
+ */
612
1056
  StorageData.PRINT_ON_STARTUP = true;
613
1057
 
1058
+ /**
1059
+ * Store all settings related to player behavior
1060
+ */
614
1061
  class SettingsData {
1062
+ //------------------------------------------------------------------------//
1063
+ // CONSTRUCTOR
1064
+ //------------------------------------------------------------------------//
1065
+ /**
1066
+ * Constructor
1067
+ * @param config
1068
+ */
615
1069
  constructor(config) {
1070
+ /**
1071
+ * Whenver player should try reconnecting upon its error
1072
+ * @private
1073
+ */
616
1074
  this._restartOnError = true;
1075
+ /**
1076
+ * Number of seconds between player reconnects
1077
+ * @private
1078
+ */
617
1079
  this._reconnectTime = 1;
1080
+ /**
1081
+ * Decides whenver player will automatically start playing content
1082
+ * @private
1083
+ */
618
1084
  this._autoStart = false;
1085
+ /**
1086
+ * Decides whenver player will automatically connect to a server
1087
+ * @private
1088
+ */
619
1089
  this._autoConnect = true;
1090
+ /**
1091
+ * Start player only when DOM is ready
1092
+ *
1093
+ * @private
1094
+ */
620
1095
  this.startOnDOMReady = false;
1096
+ /**
1097
+ * Starts video playback on iOS only once DOM is ready
1098
+ *
1099
+ * @private
1100
+ *
1101
+ */
621
1102
  this.iOSOnDomReadyFix = true;
1103
+ /**
1104
+ * Decides whenever player will restart on focus/blur
1105
+ * @private
1106
+ */
622
1107
  this._restartOnFocus = true;
1108
+ /**
1109
+ * Whenever streamer should preselect some camera and microphone if no was selected prior
1110
+ * @private
1111
+ */
623
1112
  this._preselectDevices = true;
624
1113
  this._cancelPublishOnError = true;
625
1114
  this.parse(config);
626
1115
  }
1116
+ //------------------------------------------------------------------------//
1117
+ // MAIN METHODS
1118
+ //------------------------------------------------------------------------//
1119
+ /**
1120
+ * Parses provided config
1121
+ * @param config
1122
+ */
627
1123
  parse(config) {
628
1124
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
629
1125
  this._settingsConfig = config;
@@ -639,33 +1135,67 @@
639
1135
  this._storageData = new StorageData((_k = this._settingsConfig.storage) !== null && _k !== void 0 ? _k : null);
640
1136
  this._debugData = new DebugData((_l = this._settingsConfig.debug) !== null && _l !== void 0 ? _l : null);
641
1137
  }
1138
+ //------------------------------------------------------------------------//
1139
+ // GETS & SETS
1140
+ //------------------------------------------------------------------------//
1141
+ /**
1142
+ * Returns Volume Config
1143
+ */
642
1144
  getAudioData() {
643
1145
  return this._audioData;
644
1146
  }
1147
+ /**
1148
+ * Returns Video Config
1149
+ */
645
1150
  getVideoData() {
646
1151
  return this._videoData;
647
1152
  }
1153
+ /**
1154
+ * Returns Storage Data
1155
+ */
648
1156
  getStorageData() {
649
1157
  return this._storageData;
650
1158
  }
1159
+ /**
1160
+ * Returns if player should reset connection after error
1161
+ */
651
1162
  getIfRestartOnError() {
652
1163
  return this._restartOnError;
653
1164
  }
1165
+ /**
1166
+ * Returns time (in ms) when player should attempt new connection after error
1167
+ */
654
1168
  getReconnectTime() {
655
1169
  return this._reconnectTime;
656
1170
  }
1171
+ /**
1172
+ * Returns true whenever autostart is activated
1173
+ */
657
1174
  get autoStart() {
658
1175
  return this._autoStart;
659
1176
  }
1177
+ /**
1178
+ * Sets autostart value
1179
+ * @param newValue
1180
+ */
660
1181
  set autoStart(newValue) {
661
1182
  this._autoStart = newValue;
662
1183
  }
1184
+ /**
1185
+ * Returns true/false whenever library is set to auto-connect with a Storm server
1186
+ */
663
1187
  get autoConnect() {
664
1188
  return this._autoConnect;
665
1189
  }
1190
+ /**
1191
+ * Returns true whenever library should restart the connection on document focus/blur
1192
+ */
666
1193
  get restartOnFocus() {
667
1194
  return this._restartOnFocus;
668
1195
  }
1196
+ /**
1197
+ * Returns Debug Config
1198
+ */
669
1199
  getDebugData() {
670
1200
  return this._debugData;
671
1201
  }
@@ -675,12 +1205,26 @@
675
1205
  getIfCancelPublishOnError() {
676
1206
  return this._cancelPublishOnError;
677
1207
  }
1208
+ /**
1209
+ * Returns whenever player should start only after DOM has been initialized
1210
+ */
678
1211
  getIfStartOnDOMReadyEnabled() {
679
1212
  return this.startOnDOMReady;
680
1213
  }
1214
+ /**
1215
+ * Returns whenever player (ios mode) should start only after DOM has been initialized
1216
+ */
681
1217
  getIfIOSOnDomStartFixEnabled() {
682
1218
  return this.iOSOnDomReadyFix;
683
1219
  }
1220
+ //------------------------------------------------------------------------//
1221
+ // OTHER
1222
+ //------------------------------------------------------------------------//
1223
+ /**
1224
+ * Prints current settings
1225
+ *
1226
+ * @param logger
1227
+ */
684
1228
  print(logger, force = false) {
685
1229
  if (SettingsData.PRINT_ON_STARTUP || force) {
686
1230
  let enabledProtocols = "";
@@ -696,14 +1240,45 @@
696
1240
  }
697
1241
  }
698
1242
  }
1243
+ /**
1244
+ * Decides whenever player will print this config data on startup
1245
+ *
1246
+ * @private
1247
+ */
699
1248
  SettingsData.PRINT_ON_STARTUP = true;
700
1249
 
1250
+ /**
1251
+ * Class responsible for parsing config object.
1252
+ */
701
1253
  class ConfigManager {
1254
+ //------------------------------------------------------------------------//
1255
+ // CONSTRUCTOR
1256
+ //------------------------------------------------------------------------//
1257
+ /**
1258
+ * Constuctor
1259
+ * @param config config object
1260
+ */
702
1261
  constructor(config) {
1262
+ /**
1263
+ * Decides whenever player will print this config data on startup
1264
+ *
1265
+ * @private
1266
+ */
703
1267
  this.PRINT_ON_STARTUP = true;
1268
+ /**
1269
+ * Whenver we're in demo mode
1270
+ * @private
1271
+ */
704
1272
  this.demoMode = false;
705
1273
  this.parse(config);
706
1274
  }
1275
+ //------------------------------------------------------------------------//
1276
+ // MAIN METHODS
1277
+ //------------------------------------------------------------------------//
1278
+ /**
1279
+ * Parses config objects into smaller chunkes related to their kind.
1280
+ * @private
1281
+ */
707
1282
  parse(config) {
708
1283
  var _a, _b;
709
1284
  this.configTemplate = config;
@@ -712,15 +1287,36 @@
712
1287
  this.settingsData = new SettingsData((_a = this.configTemplate.settings) !== null && _a !== void 0 ? _a : null);
713
1288
  this.demoMode = (_b = this.configTemplate.demoMode) !== null && _b !== void 0 ? _b : false;
714
1289
  }
1290
+ //------------------------------------------------------------------------//
1291
+ // GETS & SETS
1292
+ //------------------------------------------------------------------------//
1293
+ /**
1294
+ * Returns the part of the config with player stream data (what to play)
1295
+ */
715
1296
  getStreamData() {
716
1297
  return this.streamData;
717
1298
  }
1299
+ /**
1300
+ * Returns the part of the config with player settings
1301
+ */
718
1302
  getSettingsData() {
719
1303
  return this.settingsData;
720
1304
  }
1305
+ /**
1306
+ * Returns true/false whenever we're in demo mode or not
1307
+ */
721
1308
  getIfDemoMode() {
722
1309
  return this.demoMode;
723
1310
  }
1311
+ //------------------------------------------------------------------------//
1312
+ // OTHER
1313
+ //------------------------------------------------------------------------//
1314
+ /**
1315
+ * Prints current settings.
1316
+ *
1317
+ * @param logger reference to logger
1318
+ * @param force if printing is disabled, this parameter can overwrite it
1319
+ */
724
1320
  print(logger, force = false) {
725
1321
  if (this.PRINT_ON_STARTUP || force) {
726
1322
  this.streamData.print(logger);
@@ -729,11 +1325,29 @@
729
1325
  }
730
1326
  }
731
1327
 
1328
+ /**
1329
+ * General class for event-listeners
1330
+ */
732
1331
  class EventDispatcher {
733
1332
  constructor() {
1333
+ /**
1334
+ * Whenever instance of this class has been removed
1335
+ * @protected
1336
+ */
734
1337
  this._isRemoved = false;
1338
+ /**
1339
+ * An array storing all the listeners
1340
+ * @private
1341
+ */
735
1342
  this._listeners = {};
736
- }
1343
+ // nothing
1344
+ }
1345
+ /**
1346
+ * Method registers event listener with the object
1347
+ * @param eventName name of an event (as a string)
1348
+ * @param listener a reference to a method
1349
+ * @param removable whenever this listener can be removed or not
1350
+ */
737
1351
  addEventListener(eventName, listener, removable = true) {
738
1352
  if (!this._listeners[eventName]) this._listeners[eventName] = [];
739
1353
  let elementFound = false;
@@ -754,6 +1368,11 @@
754
1368
  return true;
755
1369
  } else return false;
756
1370
  }
1371
+ /**
1372
+ * Method removes a listener from this object based on event name and used method
1373
+ * @param eventName name of an event (as a string)
1374
+ * @param listenera reference to a method (optional)
1375
+ */
757
1376
  removeEventListener(eventName, listener) {
758
1377
  let elementFound = false;
759
1378
  if (this._listeners[eventName] != undefined) {
@@ -780,6 +1399,9 @@
780
1399
  this._logger.success(this, "Removing listener: " + eventName);
781
1400
  return elementFound;
782
1401
  }
1402
+ /**
1403
+ * Method removes all event listeners
1404
+ */
783
1405
  removeAllEventListeners() {
784
1406
  this._logger.success(this, "Removing all listeners!");
785
1407
  for (const eventName in this._listeners) {
@@ -795,6 +1417,11 @@
795
1417
  }
796
1418
  }
797
1419
  }
1420
+ /**
1421
+ * Method dispatches an event of a given eventName
1422
+ * @param eventName
1423
+ * @param event
1424
+ */
798
1425
  dispatchEvent(eventName, event) {
799
1426
  if (this._isRemoved) return;
800
1427
  if (this._listeners[eventName] != undefined) {
@@ -805,10 +1432,18 @@
805
1432
  }
806
1433
  }
807
1434
  }
1435
+ //console.log('%c⏵ Event: '+eventName+' was dispatched: '+count+' times', 'background: yellow; color: black;');
808
1436
  }
809
1437
  }
810
1438
 
1439
+ /**
1440
+ * Class responsible for operations on numbers
1441
+ */
811
1442
  class NumberUtilities {
1443
+ /**
1444
+ * Adds leading "zero" before the number and returns it as a string. Example: "1" -> "01"
1445
+ * @param number
1446
+ */
812
1447
  static addLeadingZero(number) {
813
1448
  if (number < 10) {
814
1449
  return "0" + number;
@@ -846,9 +1481,27 @@
846
1481
  };
847
1482
 
848
1483
  class Logger {
1484
+ /**
1485
+ * Constructor
1486
+ *
1487
+ * @param config object containing settings for logger
1488
+ * @param stormLibrary reference to main class
1489
+ */
849
1490
  constructor(config, stormStreamer) {
1491
+ /**
1492
+ * List of colors used for monoColor option
1493
+ * @private
1494
+ */
850
1495
  this.colorOrder = ["red", "green", "blue", "orange", "black", "violet"];
1496
+ /**
1497
+ * Stores all loges within this array
1498
+ * @private
1499
+ */
851
1500
  this._logMemory = [];
1501
+ /**
1502
+ * Main name of the instance
1503
+ * @private
1504
+ */
852
1505
  this._streamerInstanceID = -1;
853
1506
  this._debugConfig = config;
854
1507
  this._stormStreamer = stormStreamer;
@@ -856,6 +1509,12 @@
856
1509
  let colorID = this.colorOrder.length < stormStreamer.getStreamerID() ? this.colorOrder.length - 1 : stormStreamer.getStreamerID();
857
1510
  this._monoColor = this.colorOrder[colorID];
858
1511
  }
1512
+ /**
1513
+ * Creates new "info" type log
1514
+ *
1515
+ * @param objectName object that called the log
1516
+ * @param message log message
1517
+ */
859
1518
  info(objectName, message) {
860
1519
  let output = this.logData(objectName, message);
861
1520
  if (this._debugConfig.consoleLogEnabled) {
@@ -871,6 +1530,12 @@
871
1530
  }
872
1531
  }
873
1532
  }
1533
+ /**
1534
+ * Creates new "warning" type log
1535
+ *
1536
+ * @param objectName object that called the log
1537
+ * @param message log message
1538
+ */
874
1539
  warning(objectName, message) {
875
1540
  let output = this.logData(objectName, message);
876
1541
  if (this._debugConfig.consoleLogEnabled) {
@@ -899,10 +1564,16 @@
899
1564
  const id = this._stormStreamer.getStreamerID();
900
1565
  const keyCaps = id >= 0 && id < EMOJI_MAP.length ? EMOJI_MAP[id] : `[${id}]`;
901
1566
  const color = COLOR_SCHEMES[scheme];
902
- if (!color) return;
1567
+ if (!color) return; // Early return if invalid scheme
903
1568
  const style = `background: black; color: ${color}; border: 1px solid ${color}; padding: 5px 5px 5px 0px`;
904
1569
  console.log(`%c 🎦️${keyCaps} ${text}`, style);
905
1570
  }
1571
+ /**
1572
+ * Creates new "error" type log
1573
+ *
1574
+ * @param objectName object that called the log
1575
+ * @param message log message
1576
+ */
906
1577
  error(objectName, message) {
907
1578
  let output = this.logData(objectName, message);
908
1579
  if (this._debugConfig.consoleLogEnabled) {
@@ -918,6 +1589,12 @@
918
1589
  }
919
1590
  }
920
1591
  }
1592
+ /**
1593
+ * Creates new "success" type log
1594
+ *
1595
+ * @param objectName object that called the log
1596
+ * @param message log message
1597
+ */
921
1598
  success(objectName, message) {
922
1599
  let output = this.logData(objectName, message);
923
1600
  if (this._debugConfig.consoleLogEnabled) {
@@ -933,6 +1610,12 @@
933
1610
  }
934
1611
  }
935
1612
  }
1613
+ /**
1614
+ * Creates new "trace" type log
1615
+ *
1616
+ * @param objectName object that called the log
1617
+ * @param message log message
1618
+ */
936
1619
  trace(objectName, message) {
937
1620
  let output = this.logData(objectName, message);
938
1621
  if (this._debugConfig.consoleLogEnabled) {
@@ -948,6 +1631,12 @@
948
1631
  }
949
1632
  }
950
1633
  }
1634
+ /**
1635
+ * Prepares console message and formats its text
1636
+ * @param _objectName object that called the log
1637
+ * @param message log message
1638
+ * @private
1639
+ */
951
1640
  logData(_objectName, message) {
952
1641
  let date = new Date();
953
1642
  let hour = NumberUtilities.addLeadingZero(date.getHours());
@@ -959,6 +1648,13 @@
959
1648
  this._logMemory.push(finalString);
960
1649
  return finalString;
961
1650
  }
1651
+ /**
1652
+ * Writes log to a DOM container
1653
+ *
1654
+ * @param message the message
1655
+ * @param color of the log
1656
+ * @private
1657
+ */
962
1658
  writeToContainer(message, color) {
963
1659
  let containerName = this._debugConfig.containerID;
964
1660
  if (containerName) {
@@ -969,28 +1665,67 @@
969
1665
  container.appendChild(log);
970
1666
  }
971
1667
  }
1668
+ /**
1669
+ * Sets player id
1670
+ * @param playerID
1671
+ */
972
1672
  setPlayerID(playerID) {
973
1673
  this._streamerInstanceID = playerID;
974
1674
  }
1675
+ /**
1676
+ * Returns all logs
1677
+ */
975
1678
  getAllLogs() {
976
1679
  return this._logMemory;
977
1680
  }
978
1681
  }
1682
+ /**
1683
+ * Defines what color an info log would be outputted (can be either a color name or #hex)
1684
+ * @private
1685
+ */
979
1686
  Logger.INFO_COLOR = "blue";
1687
+ /**
1688
+ * Defines what color a warning log would be outputted (can be either a color name or #hex)
1689
+ * @private
1690
+ */
980
1691
  Logger.WARNING_COLOR = "orange";
1692
+ /**
1693
+ * Defines what color an error log would be outputted (can be either a color name or #hex)
1694
+ * @private
1695
+ */
981
1696
  Logger.ERROR_COLOR = "red";
1697
+ /**
1698
+ * Defines what color a success log would be outputted (can be either a color name or #hex)
1699
+ * @private
1700
+ */
982
1701
  Logger.SUCCESS_COLOR = "green";
1702
+ /**
1703
+ * Defines what color a success log would be outputted (can be either a color name or #hex)
1704
+ * @private
1705
+ */
983
1706
  Logger.TRACE_COLOR = "black";
984
1707
 
1708
+ /**
1709
+ * Class for detecting user device, browser, etc.
1710
+ */
985
1711
  class UserCapabilities {
1712
+ /**
1713
+ * Returns true or false depending on availability of WebSockets
1714
+ */
986
1715
  static hasWebSocketsSupport() {
987
1716
  return window.WebSocket != null;
988
1717
  }
1718
+ /**
1719
+ * Returns true if user is using a mobile browser
1720
+ */
989
1721
  static isMobile() {
990
1722
  const mobileCheckString = "Mobile|mini|Fennec|Android|iP(ad|od|hone)";
991
1723
  const mobileCheckRegExp = new RegExp(mobileCheckString);
992
1724
  return mobileCheckRegExp.test(navigator.userAgent);
993
1725
  }
1726
+ /**
1727
+ * Returns true if cookies are enabled for this browser/device
1728
+ */
994
1729
  static isCookieEnabled() {
995
1730
  let cookieEnabled = navigator.cookieEnabled ? true : false;
996
1731
  if (typeof navigator.cookieEnabled == 'undefined' && !cookieEnabled) {
@@ -999,6 +1734,9 @@
999
1734
  }
1000
1735
  return cookieEnabled;
1001
1736
  }
1737
+ /**
1738
+ * Returns an OS Version
1739
+ */
1002
1740
  static getOSVersion() {
1003
1741
  let osVersion = "Unknown version";
1004
1742
  let os = UserCapabilities.getOS();
@@ -1008,7 +1746,9 @@
1008
1746
  if (windowsCheckRegExp.test(os)) {
1009
1747
  const windowsExecString = "Windows (.*)";
1010
1748
  const windowsExecRegExp = new RegExp(windowsExecString);
1749
+ // @ts-ignore
1011
1750
  osVersion = windowsExecRegExp.exec(os)[1] != null ? windowsExecRegExp.exec(os)[1] : osVersion;
1751
+ // @ts-ignore
1012
1752
  os = 'Windows';
1013
1753
  }
1014
1754
  switch (os) {
@@ -1017,37 +1757,53 @@
1017
1757
  case 'Android':
1018
1758
  const versionExecString = "(?:Android|Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([\\.\\_\\d]+)";
1019
1759
  const versionExecRegExp = new RegExp(versionExecString);
1760
+ // @ts-ignore
1020
1761
  osVersion = versionExecRegExp.exec(navigator.userAgent)[1];
1021
1762
  break;
1022
1763
  case 'iOS':
1023
1764
  const iOSExecString = "OS (\\d+)_(\\d+)_?(\\d+)?";
1024
1765
  const iOSExecRegExp = new RegExp(iOSExecString);
1766
+ // @ts-ignore
1025
1767
  osVersion = iOSExecRegExp.exec(navigator.userAgent);
1768
+ // @ts-ignore
1026
1769
  osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0);
1027
1770
  break;
1028
1771
  }
1029
1772
  }
1030
1773
  return osVersion;
1031
1774
  }
1775
+ /**
1776
+ * Returns a browser name
1777
+ */
1032
1778
  static getBrowserName() {
1033
1779
  return UserCapabilities.getFullBrowser().name;
1034
1780
  }
1781
+ /**
1782
+ * Returns a browser version
1783
+ */
1035
1784
  static getBrowserVersion() {
1036
1785
  return UserCapabilities.getFullBrowser().version;
1037
1786
  }
1787
+ /**
1788
+ * Returns a full browser name
1789
+ */
1038
1790
  static getFullBrowser() {
1791
+ //browser
1039
1792
  let nAgt = navigator.userAgent;
1040
1793
  let browser = navigator.appName;
1041
1794
  let version = '' + parseFloat(navigator.appVersion);
1042
1795
  let majorVersion = parseInt(navigator.appVersion, 10);
1043
1796
  let nameOffset, verOffset, ix;
1797
+ // Opera
1044
1798
  if ((verOffset = nAgt.indexOf('Opera')) != -1) {
1045
1799
  browser = 'Opera';
1046
1800
  version = nAgt.substring(verOffset + 6);
1047
1801
  if ((verOffset = nAgt.indexOf('Version')) != -1) {
1048
1802
  version = nAgt.substring(verOffset + 8);
1049
1803
  }
1050
- } else if ((verOffset = nAgt.indexOf('MSIE')) != -1) {
1804
+ }
1805
+ // MSIE
1806
+ else if ((verOffset = nAgt.indexOf('MSIE')) != -1) {
1051
1807
  browser = 'Microsoft Internet Explorer';
1052
1808
  version = nAgt.substring(verOffset + 5);
1053
1809
  } else if (browser == 'Netscape' && nAgt.indexOf('Trident/') != -1) {
@@ -1056,34 +1812,50 @@
1056
1812
  if ((verOffset = nAgt.indexOf('rv:')) != -1) {
1057
1813
  version = nAgt.substring(verOffset + 3);
1058
1814
  }
1059
- } else if ((verOffset = nAgt.indexOf('Chrome')) != -1) {
1815
+ }
1816
+ // Chrome
1817
+ else if ((verOffset = nAgt.indexOf('Chrome')) != -1) {
1060
1818
  browser = 'Chrome';
1061
1819
  if (nAgt.indexOf("FBAV") > -1 || nAgt.indexOf("FBAN") > -1) browser = 'Facebook';
1062
1820
  if (nAgt.indexOf("OPR") > -1) browser = 'Opera';
1063
1821
  if (nAgt.indexOf("SamsungBrowser") > -1) browser = 'Samsung';
1064
1822
  version = nAgt.substring(verOffset + 7);
1065
- } else if ((verOffset = nAgt.indexOf('Safari')) != -1) {
1823
+ }
1824
+ // Safari
1825
+ else if ((verOffset = nAgt.indexOf('Safari')) != -1) {
1066
1826
  browser = 'Safari';
1067
1827
  version = nAgt.substring(verOffset + 7);
1068
1828
  if ((verOffset = nAgt.indexOf('Version')) != -1) {
1069
1829
  version = nAgt.substring(verOffset + 8);
1070
1830
  }
1831
+ // Chrome on iPad identifies itself as Safari. Actual results do not match what Google claims
1832
+ // at: https://developers.google.com/chrome/mobile/docs/user-agent?hl=ja
1833
+ // No mention of chrome in the user agent string. However it does mention CriOS, which presumably
1834
+ // can be keyed on to detect it.
1071
1835
  if (nAgt.indexOf('CriOS') != -1) {
1836
+ //Chrome on iPad spoofing Safari...correct it.
1072
1837
  browser = 'Chrome';
1838
+ //Don't believe there is a way to grab the accurate version number, so leaving that for now.
1073
1839
  }
1840
+
1074
1841
  if (nAgt.indexOf('FxiOS') != -1) {
1075
1842
  browser = "Firefox";
1076
1843
  }
1077
- } else if ((verOffset = nAgt.indexOf('Firefox')) != -1) {
1844
+ }
1845
+ // Firefox
1846
+ else if ((verOffset = nAgt.indexOf('Firefox')) != -1) {
1078
1847
  browser = 'Firefox';
1079
1848
  version = nAgt.substring(verOffset + 8);
1080
- } else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
1849
+ }
1850
+ // Other browsers
1851
+ else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
1081
1852
  browser = nAgt.substring(nameOffset, verOffset);
1082
1853
  version = nAgt.substring(verOffset + 1);
1083
1854
  if (browser.toLowerCase() == browser.toUpperCase()) {
1084
1855
  browser = navigator.appName;
1085
1856
  }
1086
1857
  }
1858
+ // trim the version string
1087
1859
  if ((ix = version.indexOf(';')) != -1) version = version.substring(0, ix);
1088
1860
  if ((ix = version.indexOf(' ')) != -1) version = version.substring(0, ix);
1089
1861
  if ((ix = version.indexOf(')')) != -1) version = version.substring(0, ix);
@@ -1098,6 +1870,9 @@
1098
1870
  "version": majorVersion
1099
1871
  };
1100
1872
  }
1873
+ /**
1874
+ * Returns Operating System name
1875
+ */
1101
1876
  static getOS() {
1102
1877
  let os = "Unknown OS";
1103
1878
  let oscodes = [{
@@ -1192,9 +1967,15 @@
1192
1967
  }
1193
1968
  return os;
1194
1969
  }
1970
+ /**
1971
+ * Returns true whenever device supports WebRTC
1972
+ */
1195
1973
  static hasWebRTCSupport() {
1974
+ //if(this.getBrowserName() == "Facebook" && this.getOS() == "Android")
1975
+ //return false;
1196
1976
  let isSupported = false;
1197
1977
  try {
1978
+ // @ts-ignore
1198
1979
  let webrtc = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || window.RTCPeerConnection;
1199
1980
  isSupported = true;
1200
1981
  } catch (error) {
@@ -1202,47 +1983,99 @@
1202
1983
  }
1203
1984
  return isSupported;
1204
1985
  }
1986
+ /**
1987
+ * Checks whenever player support "out-of-box" HLS (mobile browsers mostly do)
1988
+ * @param videoObject html element video
1989
+ */
1205
1990
  static hasHLSSupport(videoObject) {
1991
+ //if(this.getBrowserName() == "Facebook" && this.getOS() == "Android")
1992
+ //return false;
1206
1993
  if (videoObject !== null) {
1207
1994
  return Boolean(videoObject.canPlayType('application/vnd.apple.mpegURL') || videoObject.canPlayType('audio/mpegurl'));
1208
1995
  } else return false;
1209
1996
  }
1997
+ /**
1998
+ * Returns true or false depending on availability of Media Source Extensions (MSE)
1999
+ */
1210
2000
  static hasMSESupport() {
2001
+ // @ts-ignore
1211
2002
  const mediaSource = window.MediaSource = window.MediaSource || window.WebKitMediaSource;
2003
+ // @ts-ignore
1212
2004
  window.SourceBuffer = window.SourceBuffer || window.WebKitSourceBuffer;
1213
2005
  return mediaSource && typeof mediaSource.isTypeSupported === 'function';
1214
2006
  }
2007
+ /**
2008
+ * Returns true or false depending on availability of Managed Media Source Extensions (MMS).
2009
+ * This API is MacOS / iOS only.
2010
+ */
1215
2011
  static hasMMSSupport() {
2012
+ // @ts-ignore
1216
2013
  return window.ManagedMediaSource;
1217
2014
  }
2015
+ /**
2016
+ * Returns true whenever user is connected via HTTPS
2017
+ */
1218
2018
  static isSSL() {
1219
2019
  if (location.protocol === 'https:') return true;else return false;
1220
2020
  }
1221
2021
  }
1222
2022
 
2023
+ /**
2024
+ * Saves and retrieve data from localStorage.
2025
+ */
1223
2026
  class StorageManager {
2027
+ /**
2028
+ * Constructor
2029
+ * @param main
2030
+ * @param prefix
2031
+ */
1224
2032
  constructor(main) {
1225
2033
  var _a, _b, _c, _d, _e, _f;
2034
+ /**
2035
+ * Decides whenever logs are being generated by this class
2036
+ * @private
2037
+ */
1226
2038
  this.LOG_ACTIVITY = false;
2039
+ /**
2040
+ * Whenever saving cookies is enabled at all
2041
+ * @private
2042
+ */
1227
2043
  this.isEnabled = true;
2044
+ /**
2045
+ * Prefix for multiple instances
2046
+ * @private
2047
+ */
1228
2048
  this.prefix = "";
1229
2049
  this.logger = main.getLogger();
1230
2050
  this.isEnabled = (_c = (_b = (_a = main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getSettingsData()) === null || _b === void 0 ? void 0 : _b.getStorageData().enabled) !== null && _c !== void 0 ? _c : this.isEnabled;
1231
2051
  this.prefix = (_f = (_e = (_d = main.getConfigManager()) === null || _d === void 0 ? void 0 : _d.getSettingsData()) === null || _e === void 0 ? void 0 : _e.getStorageData().prefix) !== null && _f !== void 0 ? _f : this.prefix;
1232
2052
  if (this.LOG_ACTIVITY) this.logger.info(this, "Creating new StorageManager");
1233
2053
  }
2054
+ /**
2055
+ * Saves data a a cookie
2056
+ * @param name name of a field
2057
+ * @param value value of a field
2058
+ */
1234
2059
  saveField(name, value) {
1235
2060
  if (this.isEnabled == true) {
1236
2061
  if (this.LOG_ACTIVITY) this.logger.info(this, "Saving data: " + name + " | " + value);
1237
2062
  localStorage.setItem(this.prefix + name, value);
1238
2063
  }
1239
2064
  }
2065
+ /**
2066
+ * Removes a field from localStorage
2067
+ * @param name the name of the field to remove
2068
+ */
1240
2069
  removeField(name) {
1241
2070
  if (this.isEnabled) {
1242
2071
  if (this.LOG_ACTIVITY) this.logger.info(this, "Removing data: " + name);
1243
2072
  localStorage.removeItem(this.prefix + name);
1244
2073
  }
1245
2074
  }
2075
+ /**
2076
+ * Retrieves field from cookie
2077
+ * @param name cookie name
2078
+ */
1246
2079
  getField(name) {
1247
2080
  if (this.isEnabled == true) {
1248
2081
  const value = localStorage.getItem(this.prefix + name);
@@ -1638,13 +2471,38 @@
1638
2471
 
1639
2472
  var debounce$1 = /*@__PURE__*/getDefaultExportFromCjs(lodash_debounce);
1640
2473
 
2474
+ /**
2475
+ * Class controls the VideoElement itself
2476
+ */
1641
2477
  class ScreenElement {
2478
+ //------------------------------------------------------------------------//
2479
+ // CONSTRUCTOR
2480
+ //------------------------------------------------------------------------//
1642
2481
  constructor(main) {
1643
2482
  var _a, _b, _c, _d, _e, _f;
2483
+ /**
2484
+ * Decides whenever logs are being generated by this class
2485
+ * @private
2486
+ */
1644
2487
  this.LOG_ACTIVITY = true;
2488
+ /**
2489
+ * Current volume level
2490
+ * @private
2491
+ */
1645
2492
  this._volume = 100;
2493
+ /**
2494
+ * Whenever video is muted
2495
+ * @private
2496
+ */
1646
2497
  this._isMuted = false;
2498
+ /**
2499
+ * Whenever video is muted by a browser
2500
+ * @private
2501
+ */
1647
2502
  this._isMutedByBrowser = false;
2503
+ /**
2504
+ * Called whenever browser stops playback
2505
+ */
1648
2506
  this.onForceMute = () => {
1649
2507
  this._isMuted = true;
1650
2508
  this._isMutedByBrowser = true;
@@ -1677,18 +2535,33 @@
1677
2535
  });
1678
2536
  this.initialize();
1679
2537
  }
2538
+ //------------------------------------------------------------------------//
2539
+ // MAIN METHODS
2540
+ //------------------------------------------------------------------------//
2541
+ /**
2542
+ * Initializes and pre-configures main video object
2543
+ */
1680
2544
  initialize() {
1681
- this._videoElement.onload = function (event) {};
2545
+ this._videoElement.onload = function (event) {
2546
+ //console.log('videoElement.onload', event);
2547
+ };
1682
2548
  this._videoElement.onstalled = event => {
1683
2549
  this._logger.info(this, "VideoElement :: onstalled");
1684
2550
  };
1685
2551
  this._videoElement.onerror = event => {
1686
2552
  this._logger.info(this, "VideoElement :: onerror :: " + JSON.stringify(event));
1687
2553
  };
2554
+ /**
2555
+ * Fires whenever something changes video object volume
2556
+ * @param event
2557
+ */
2558
+ // @ts-ignore
1688
2559
  this._videoElement.onvolumechange = () => {
1689
2560
  this.dispatchVolumeEvent();
1690
2561
  };
1691
- this._videoElement.onpause = () => {};
2562
+ this._videoElement.onpause = () => {
2563
+ // nothing
2564
+ };
1692
2565
  this._videoElement.addEventListener('loadedmetadata', () => {
1693
2566
  this._main.dispatchEvent("metadata", {
1694
2567
  ref: this._main,
@@ -1696,12 +2569,33 @@
1696
2569
  videoHeight: this._videoElement.videoHeight
1697
2570
  });
1698
2571
  });
1699
- this._videoElement.ontimeupdate = function (event) {};
2572
+ /**
2573
+ * Updates every second,
2574
+ * @param event
2575
+ */
2576
+ this._videoElement.ontimeupdate = function (event) {
2577
+ /*
2578
+ let newWidth: number = self.videoElement.videoWidth;
2579
+ let newHeight: number = self.videoElement.videoHeight;
2580
+ if (newWidth != 0 && newHeight != 0) {
2581
+ if (newWidth !== self.videoWidth || newHeight !== self.videoHeight) {
2582
+ self.videoWidth = newWidth;
2583
+ self.videoHeight = newHeight;
2584
+ self.scaleVideo();
2585
+ }
2586
+ }
2587
+ */
2588
+ };
2589
+ // @ts-ignore
1700
2590
  this._videoElement.onended = event => {
1701
2591
  this._logger.info(this, "VideoElement :: onended");
1702
2592
  };
1703
2593
  this._videoElement.onplay = () => {};
1704
2594
  }
2595
+ /**
2596
+ * Sets new volume for playback. It'll also try to store value in a browser memory
2597
+ * @param value
2598
+ */
1705
2599
  setVolume(value) {
1706
2600
  if (this._isMuted && value > 0) this.setMuted(false);
1707
2601
  this._volume = value;
@@ -1712,6 +2606,10 @@
1712
2606
  getVolume() {
1713
2607
  return this._volume;
1714
2608
  }
2609
+ /**
2610
+ * Allows to apply mute to a video element
2611
+ * @param isMuted
2612
+ */
1715
2613
  setMuted(isMuted) {
1716
2614
  this._isMuted = isMuted;
1717
2615
  this._videoElement.muted = isMuted;
@@ -1719,9 +2617,15 @@
1719
2617
  this._main.getStorageManager().saveField("muted", String(isMuted));
1720
2618
  if (this.getVolume() == 0 && !isMuted) this.setVolume(100);
1721
2619
  }
2620
+ /**
2621
+ * Returns true/false whenever video is muted or not
2622
+ */
1722
2623
  getIfMuted() {
1723
2624
  return this._isMuted;
1724
2625
  }
2626
+ /**
2627
+ * Dispatches event informing about volume chan
2628
+ */
1725
2629
  dispatchVolumeEvent() {
1726
2630
  const action = !this._isMutedByBrowser ? "user" : "browser";
1727
2631
  const eventObj = {
@@ -1737,12 +2641,22 @@
1737
2641
  invokedBy: action
1738
2642
  });
1739
2643
  }
2644
+ /**
2645
+ * Return VideoElement
2646
+ */
1740
2647
  getVideoElement() {
1741
2648
  return this._videoElement;
1742
2649
  }
1743
2650
  }
1744
2651
 
2652
+ /**
2653
+ * Class responsible for operations on numbers
2654
+ */
1745
2655
  class DomUtilities {
2656
+ /**
2657
+ * Calculates element dimensions including margins
2658
+ * @param element
2659
+ */
1746
2660
  static calculateDimensionsWithMargins(element) {
1747
2661
  const style = window.getComputedStyle(element);
1748
2662
  const rect = element.getBoundingClientRect();
@@ -1763,20 +2677,73 @@
1763
2677
  }
1764
2678
  }
1765
2679
 
2680
+ /**
2681
+ * Class controls all visual elements of the player
2682
+ */
1766
2683
  class StageController {
2684
+ //------------------------------------------------------------------------//
2685
+ // CONSTRUCTOR
2686
+ //------------------------------------------------------------------------//
1767
2687
  constructor(main) {
2688
+ /**
2689
+ * Video width
2690
+ * @private
2691
+ */
1768
2692
  this._containerWidth = 0;
2693
+ /**
2694
+ * Temp container width
2695
+ * @private
2696
+ */
1769
2697
  this._tempContainerWidth = 0;
2698
+ /**
2699
+ * Video height
2700
+ * @private
2701
+ */
1770
2702
  this._containerHeight = 0;
2703
+ /**
2704
+ * Temp container height
2705
+ * @private
2706
+ */
1771
2707
  this._tempContainerHeight = 0;
2708
+ /**
2709
+ * Video width (initally 0, after "onmetadata" event is updated to match the real one
2710
+ * @private
2711
+ */
1772
2712
  this._videoWidth = 0;
2713
+ /**
2714
+ * Video width (initally 0, after "onmetadata" event is updated to match the real one
2715
+ * @private
2716
+ */
1773
2717
  this._videoHeight = 0;
2718
+ /**
2719
+ * Current scaling type
2720
+ * @private
2721
+ */
1774
2722
  this._scalingMode = ScalingType.FILL;
2723
+ /**
2724
+ * If in fullscreen mode
2725
+ * @private
2726
+ */
1775
2727
  this.isInFullScreenMode = false;
2728
+ /**
2729
+ * Whenever we are resizing atm.
2730
+ * @private
2731
+ */
1776
2732
  this._isResizing = false;
2733
+ /**
2734
+ * Deco
2735
+ * @private
2736
+ */
1777
2737
  this._autoResizeEnabled = true;
2738
+ /**
2739
+ * Parameter holds original overflow style
2740
+ * @private
2741
+ */
1778
2742
  this._parentOriginalOverflow = '';
1779
2743
  this._debug = false;
2744
+ //------------------------------------------------------------------------//
2745
+ // FULLSCREEN
2746
+ //------------------------------------------------------------------------//
1780
2747
  this.onFullScreenChange = () => {
1781
2748
  if (document.fullscreenElement == null) {
1782
2749
  this.isInFullScreenMode = false;
@@ -1797,16 +2764,22 @@
1797
2764
  this._logger.info(this, "Creating new StageController");
1798
2765
  this.initialize();
1799
2766
  }
2767
+ //------------------------------------------------------------------------//
2768
+ // MAIN METHODS
2769
+ //------------------------------------------------------------------------//
1800
2770
  initialize() {
1801
2771
  var _a, _b, _c, _d, _e, _f, _g, _h;
2772
+ // prepare data
1802
2773
  const containerID = (_d = (_c = (_b = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getSettingsData()) === null || _b === void 0 ? void 0 : _b.getVideoData()) === null || _c === void 0 ? void 0 : _c.containerID) !== null && _d !== void 0 ? _d : null;
1803
2774
  this._scalingMode = (_f = (_e = this._main.getConfigManager()) === null || _e === void 0 ? void 0 : _e.getSettingsData().getVideoData().scalingMode) !== null && _f !== void 0 ? _f : ScalingType.FILL;
1804
2775
  this._debug = (_h = (_g = this._main.getConfigManager()) === null || _g === void 0 ? void 0 : _g.getSettingsData().getDebugData().stageControllerDebug) !== null && _h !== void 0 ? _h : this._debug;
2776
+ // pre-configuration for videoContainer
1805
2777
  this._videoContainer = document.createElement('div');
1806
2778
  this._videoContainer.setAttribute("id", "stormStreamer_" + this._main.getStreamerID());
1807
2779
  this._videoContainer.style.overflow = "hidden";
1808
2780
  this._videoContainer.style.position = "relative";
1809
2781
  this._videoContainer.classList.add("stormStreamer");
2782
+ // pre-configuration for videoElement
1810
2783
  this._screenElement = new ScreenElement(this._main);
1811
2784
  this._videoContainer.appendChild(this._screenElement.getVideoElement());
1812
2785
  const debounceValue = this._main.getConfigManager().getSettingsData().getVideoData().resizeDebounce;
@@ -1836,9 +2809,16 @@
1836
2809
  this.attachToParent(containerID);
1837
2810
  } else this._logger.warning(this, `Could not create HTMLObject for the library - "containerID" was not provided`);
1838
2811
  }
2812
+ /**
2813
+ * Method attaches element to a parent container
2814
+ *
2815
+ * @param container
2816
+ */
1839
2817
  attachToParent(container) {
1840
- let result = false;
2818
+ // preparing elements
2819
+ let result = false; // To indicate the success of the operation
1841
2820
  let tempParentElement = null;
2821
+ // Check if container is either an ID string or an HTMLElement instance
1842
2822
  if (typeof container === "string") {
1843
2823
  this._logger.info(this, "Attaching container to ID: " + container);
1844
2824
  tempParentElement = document.getElementById(container);
@@ -1846,11 +2826,13 @@
1846
2826
  this._logger.info(this, "Attaching container to HTMLElement: " + container);
1847
2827
  tempParentElement = container;
1848
2828
  }
2829
+ // Check if the new container is not the same as the old one
1849
2830
  if (tempParentElement === this._parentElement) {
1850
2831
  if (this._debug) this._logger.decoratedLog("Attaching Failed (container is the same)", "dark-pink");
1851
2832
  this._logger.warning(this, "attachToParent :: container is the same");
1852
2833
  return false;
1853
2834
  }
2835
+ // If a valid container is found and _videoContainer exists, append it
1854
2836
  if (tempParentElement && this._videoContainer) {
1855
2837
  if (this._debug) this._logger.decoratedLog("Attach To Parent: " + container + " (success)", "dark-pink");
1856
2838
  this._parentElement = tempParentElement;
@@ -1864,13 +2846,16 @@
1864
2846
  container: this._parentElement
1865
2847
  });
1866
2848
  this.handleResize();
1867
- result = true;
2849
+ result = true; // Operation successful
1868
2850
  } else {
1869
2851
  if (this._debug) this._logger.decoratedLog("Attach To Parent: " + container + " (failure - container not found)", "dark-pink");
1870
2852
  this._logger.warning(this, "attachToParent :: container \"" + container + "\"+ was not found");
1871
2853
  }
1872
- return result;
2854
+ return result; // Return the result of the operation
1873
2855
  }
2856
+ /**
2857
+ * Detaches the library from a parent container
2858
+ */
1874
2859
  detachFromParent() {
1875
2860
  if (this._debug) this._logger.decoratedLog("Detach From Parent", "dark-pink");
1876
2861
  let result = false;
@@ -1898,7 +2883,9 @@
1898
2883
  this._isResizing = true;
1899
2884
  this._parentOriginalOverflow = this._parentElement.style.overflow;
1900
2885
  this._parentElement.style.overflow = 'hidden';
2886
+ // Używamy requestAnimationFrame aby dać przeglądarce czas na zastosowanie overflow
1901
2887
  requestAnimationFrame(() => {
2888
+ // Obliczamy nowe wymiary
1902
2889
  this.calculateNewDimensions();
1903
2890
  this._parentElement.style.overflow = this._parentOriginalOverflow;
1904
2891
  this._isResizing = false;
@@ -1933,6 +2920,10 @@
1933
2920
  this.resizeVideoContainer();
1934
2921
  this.scaleVideo();
1935
2922
  }
2923
+ /**
2924
+ * Method resizes video container
2925
+ * @private
2926
+ */
1936
2927
  resizeVideoContainer() {
1937
2928
  const isWidthInPX = this._main.getConfigManager().getSettingsData().getVideoData().videoWidthInPixels;
1938
2929
  const isHeightInPX = this._main.getConfigManager().getSettingsData().getVideoData().videoHeightInPixels;
@@ -1979,12 +2970,14 @@
1979
2970
  let videoWidth = 0;
1980
2971
  let videoHeight = 0;
1981
2972
  switch (this._scalingMode) {
2973
+ //--- FILL, covers 100% of width & height
1982
2974
  case ScalingType.FILL:
1983
2975
  {
1984
2976
  videoWidth = this._containerWidth;
1985
2977
  videoHeight = this._containerHeight;
1986
2978
  }
1987
2979
  break;
2980
+ //--- CROP
1988
2981
  case ScalingType.CROP:
1989
2982
  {
1990
2983
  videoWidth = this._containerWidth;
@@ -2000,8 +2993,10 @@
2000
2993
  }
2001
2994
  }
2002
2995
  break;
2996
+ //--- LETTER BOX
2003
2997
  case ScalingType.LETTER_BOX:
2004
2998
  {
2999
+ // jeżeli szerokość
2005
3000
  videoWidth = this._containerWidth;
2006
3001
  videoHeight = this._videoHeight * this._containerWidth / this._videoWidth;
2007
3002
  if (videoHeight <= this._containerHeight) {
@@ -2021,6 +3016,7 @@
2021
3016
  }
2022
3017
  }
2023
3018
  break;
3019
+ //--- ORIGINAL
2024
3020
  case ScalingType.ORIGINAL:
2025
3021
  {
2026
3022
  videoWidth = this._videoWidth;
@@ -2040,7 +3036,9 @@
2040
3036
  }
2041
3037
  enterFullScreen() {
2042
3038
  var _a, _b, _c;
3039
+ // @ts-ignore
2043
3040
  if ((_a = this._screenElement) === null || _a === void 0 ? void 0 : _a.getVideoElement().webkitEnterFullScreen) {
3041
+ // @ts-ignore
2044
3042
  (_b = this._screenElement) === null || _b === void 0 ? void 0 : _b.getVideoElement().webkitEnterFullScreen();
2045
3043
  } else {
2046
3044
  (_c = this._screenElement) === null || _c === void 0 ? void 0 : _c.getVideoElement().requestFullscreen();
@@ -2048,7 +3046,9 @@
2048
3046
  }
2049
3047
  exitFullScreen() {
2050
3048
  var _a;
3049
+ // @ts-ignore
2051
3050
  if ((_a = this._screenElement) === null || _a === void 0 ? void 0 : _a.getVideoElement().webkitExitFullscreen) {
3051
+ // @ts-ignore
2052
3052
  document.webkitExitFullscreen();
2053
3053
  } else {
2054
3054
  document.exitFullscreen();
@@ -2057,6 +3057,9 @@
2057
3057
  isFullScreenMode() {
2058
3058
  return this.isInFullScreenMode;
2059
3059
  }
3060
+ //------------------------------------------------------------------------//
3061
+ // SETS & GETS
3062
+ //------------------------------------------------------------------------//
2060
3063
  setDimension(key, value) {
2061
3064
  const dimensionKey = key === 'width' ? 'videoWidth' : 'videoHeight';
2062
3065
  let valueInPixels;
@@ -2138,10 +3141,17 @@
2138
3141
  getContainer() {
2139
3142
  return this._videoContainer;
2140
3143
  }
3144
+ //------------------------------------------------------------------------//
3145
+ // CLEANUP
3146
+ //------------------------------------------------------------------------//
2141
3147
  destroy() {
2142
3148
  this.detachFromParent();
2143
3149
  }
2144
3150
  }
3151
+ /**
3152
+ * Decides whenever logs are being generated by this class
3153
+ * @private
3154
+ */
2145
3155
  StageController.LOG_ACTIVITY = true;
2146
3156
 
2147
3157
  class MungeSDP {
@@ -2265,7 +3275,9 @@
2265
3275
  MungeSDP.audioChoice = mungeData.audioCodec || "opus";
2266
3276
  MungeSDP.videoIndex = -1;
2267
3277
  MungeSDP.audioIndex = -1;
3278
+ // Zapewniamy, że frame rate jest w odpowiednim zakresie
2268
3279
  const frameRate = mungeData.videoFrameRate ? Math.min(Math.max(mungeData.videoFrameRate, 1), 60) : 30;
3280
+ // Pierwszy przebieg - zbieranie informacji o kodekach
2269
3281
  const sdpLines = sdpStr.split(/\r\n/);
2270
3282
  let sdpStrRet = '';
2271
3283
  for (const sdpLine of sdpLines) {
@@ -2274,23 +3286,26 @@
2274
3286
  if (!doneCheck) continue;
2275
3287
  sdpStrRet += sdpLine + '\r\n';
2276
3288
  }
3289
+ // Dodaj linie audio i video
2277
3290
  sdpStrRet = this.addAudio(sdpStrRet, this.deliverCheckLine(MungeSDP.audioChoice, "audio"));
2278
3291
  sdpStrRet = this.addVideo(sdpStrRet, this.deliverCheckLine(MungeSDP.videoChoice, "video"));
2279
3292
  const newSdpLines = sdpStrRet.split(/\r\n/);
2280
3293
  sdpStrRet = '';
2281
3294
  let sdpSection = 'header';
2282
3295
  let hitMID = false;
3296
+ // Dodajemy parametry frame rate dla każdego kodeka video
2283
3297
  const addFrameRateParameters = payloadType => {
2284
3298
  let params = '';
2285
3299
  params += `\r\na=rtcp-fb:${payloadType} ccm fir`;
2286
3300
  params += `\r\na=rtcp-fb:${payloadType} nack`;
2287
3301
  params += `\r\na=rtcp-fb:${payloadType} nack pli`;
2288
- params += `\r\na=rtcp-fb:${payloadType} goog-remb`;
3302
+ params += `\r\na=rtcp-fb:${payloadType} goog-remb`; // Dodajemy wsparcie dla REMB
2289
3303
  params += `\r\na=fmtp:${payloadType} max-fr=${frameRate};max-fps=${frameRate}`;
2290
3304
  return params;
2291
3305
  };
2292
3306
  for (const sdpLine of newSdpLines) {
2293
3307
  if (sdpLine.length <= 0) continue;
3308
+ // Sekcja audio
2294
3309
  if (sdpLine.indexOf("m=audio") === 0 && MungeSDP.audioIndex !== -1) {
2295
3310
  const audioMLines = sdpLine.split(" ");
2296
3311
  sdpStrRet += `${audioMLines[0]} ${audioMLines[1]} ${audioMLines[2]} ${MungeSDP.audioIndex}\r\n`;
@@ -2298,21 +3313,25 @@
2298
3313
  hitMID = false;
2299
3314
  continue;
2300
3315
  }
3316
+ // Sekcja video - tutaj dodajemy podstawowe parametry frame rate
2301
3317
  if (sdpLine.indexOf("m=video") === 0 && MungeSDP.videoIndex !== -1) {
2302
3318
  const videoMLines = sdpLine.split(" ");
2303
3319
  sdpStrRet += `${videoMLines[0]} ${videoMLines[1]} ${videoMLines[2]} ${MungeSDP.videoIndex}\r\n`;
2304
- sdpStrRet += `a=framerate:${frameRate}\r\n`;
2305
- sdpStrRet += `a=x-google-max-framerate:${frameRate}\r\n`;
2306
- sdpStrRet += `a=max-fr:${frameRate}\r\n`;
3320
+ // Dodajemy kompleksowe parametry frame rate
3321
+ sdpStrRet += `a=framerate:${frameRate}\r\n`; // Standardowy parametr
3322
+ sdpStrRet += `a=x-google-max-framerate:${frameRate}\r\n`; // Chrome
3323
+ sdpStrRet += `a=max-fr:${frameRate}\r\n`; // Dodatkowy parametr
2307
3324
  sdpSection = 'video';
2308
3325
  hitMID = false;
2309
3326
  continue;
2310
3327
  }
2311
3328
  sdpStrRet += sdpLine;
3329
+ // Obsługa rtpmap i parametrów kodeków
2312
3330
  if (sdpLine.indexOf("a=rtpmap") === 0) {
2313
3331
  const rtpmapID = this.getrtpMapID(sdpLine);
2314
3332
  if (rtpmapID !== null) {
2315
3333
  const codecName = rtpmapID[2].toLowerCase();
3334
+ // Parametry dla kodeków video
2316
3335
  if (codecName === 'vp9' || codecName === 'vp8' || codecName === 'h264') {
2317
3336
  sdpStrRet += addFrameRateParameters(rtpmapID[1]);
2318
3337
  }
@@ -2320,6 +3339,7 @@
2320
3339
  sdpSection = 'bandwidth';
2321
3340
  hitMID = false;
2322
3341
  }
3342
+ // Obsługa specyficzna dla przeglądarek
2323
3343
  const browser = UserCapabilities.getBrowserName().toLowerCase();
2324
3344
  if (browser === 'chrome' || browser === 'safari') {
2325
3345
  if (sdpLine.indexOf("a=mid:") === 0 || sdpLine.indexOf("a=rtpmap") === 0) {
@@ -2332,6 +3352,7 @@
2332
3352
  sdpStrRet += `\r\na=x-google-max-framerate:${frameRate}`;
2333
3353
  hitMID = true;
2334
3354
  }
3355
+ // Dodatkowe parametry dla Chrome
2335
3356
  if (browser === 'chrome' && sdpSection === 'bandwidth') {
2336
3357
  const rtpmapID = this.getrtpMapID(sdpLine);
2337
3358
  if (rtpmapID !== null) {
@@ -2489,8 +3510,9 @@
2489
3510
  this._analyser = null;
2490
3511
  this._microphone = null;
2491
3512
  this._lastEventTime = 0;
2492
- this.THROTTLE_INTERVAL = 100;
3513
+ this.THROTTLE_INTERVAL = 100; // ms between updates
2493
3514
  this._isMonitoring = false;
3515
+ // Moving average variables for smoothing
2494
3516
  this._instant = 0.0;
2495
3517
  this._slow = 0.0;
2496
3518
  this._main = main;
@@ -2500,23 +3522,30 @@
2500
3522
  console.warn('SoundMeter: Attempted to attach stream without audio tracks');
2501
3523
  return;
2502
3524
  }
3525
+ // Ensure clean state before attaching
2503
3526
  this.detach();
2504
3527
  try {
2505
3528
  this._audioContext = new AudioContext();
3529
+ // Create and configure nodes
2506
3530
  this._microphone = this._audioContext.createMediaStreamSource(stream);
2507
3531
  this._analyser = this._audioContext.createAnalyser();
2508
3532
  this._analyser.fftSize = 2048;
2509
3533
  this._analyser.smoothingTimeConstant = 0.3;
3534
+ // Connect nodes
2510
3535
  this._microphone.connect(this._analyser);
3536
+ // Start monitoring
2511
3537
  this.startMonitoring();
2512
3538
  } catch (error) {
2513
3539
  console.error('SoundMeter: Error during attach:', error);
2514
- this.detach();
3540
+ this.detach(); // Cleanup on error
2515
3541
  }
2516
3542
  }
3543
+
2517
3544
  detach() {
2518
3545
  var _a, _b;
3546
+ // Stop monitoring first
2519
3547
  this._isMonitoring = false;
3548
+ // Disconnect and cleanup nodes in reverse order
2520
3549
  if (this._microphone) {
2521
3550
  try {
2522
3551
  this._microphone.disconnect();
@@ -2556,7 +3585,9 @@
2556
3585
  if (!this._analyser || !this._isMonitoring) return;
2557
3586
  const now = Date.now();
2558
3587
  try {
3588
+ // Read time-domain data
2559
3589
  this._analyser.getFloatTimeDomainData(dataArray);
3590
+ // Calculate levels
2560
3591
  let sum = 0.0;
2561
3592
  let clipcount = 0;
2562
3593
  for (let i = 0; i < dataArray.length; ++i) {
@@ -2566,8 +3597,10 @@
2566
3597
  clipcount += 1;
2567
3598
  }
2568
3599
  }
3600
+ // Update metrics
2569
3601
  this._instant = Math.sqrt(sum / dataArray.length);
2570
3602
  this._slow = 0.05 * this._instant + 0.95 * this._slow;
3603
+ // Throttle event dispatch
2571
3604
  if (now - this._lastEventTime >= this.THROTTLE_INTERVAL) {
2572
3605
  this._lastEventTime = now;
2573
3606
  this._main.dispatchEvent("soundMeter", {
@@ -2581,8 +3614,10 @@
2581
3614
  this._isMonitoring = false;
2582
3615
  return;
2583
3616
  }
3617
+ // Schedule next analysis
2584
3618
  requestAnimationFrame(analyze);
2585
3619
  };
3620
+ // Start analysis loop
2586
3621
  requestAnimationFrame(analyze);
2587
3622
  }
2588
3623
  }
@@ -2607,6 +3642,9 @@
2607
3642
  DeviceState["STOPPED"] = "STOPPED";
2608
3643
  })(exports.DeviceState || (exports.DeviceState = {}));
2609
3644
 
3645
+ /**
3646
+ * Different connection states
3647
+ */
2610
3648
  var ConnectionState;
2611
3649
  (function (ConnectionState) {
2612
3650
  ConnectionState[ConnectionState["NOT_INITIALIZED"] = 0] = "NOT_INITIALIZED";
@@ -2617,16 +3655,56 @@
2617
3655
  ConnectionState[ConnectionState["FAILED"] = 5] = "FAILED";
2618
3656
  })(ConnectionState || (ConnectionState = {}));
2619
3657
 
3658
+ /**
3659
+ * Base for all classes that use WebSocksts
3660
+ */
3661
+ /**
3662
+ * Abstract socket connection
3663
+ */
2620
3664
  class AbstractSocket {
2621
3665
  constructor() {
3666
+ /**
3667
+ * Connection timeout
3668
+ * @protected
3669
+ */
2622
3670
  this.CONNECTION_TIMEOUT = 5;
3671
+ /**
3672
+ * Whenever it is binary data or not
3673
+ * @protected
3674
+ */
2623
3675
  this.isBinary = true;
3676
+ /**
3677
+ * Current state of the connection
3678
+ * @private
3679
+ */
2624
3680
  this._connectionState = ConnectionState.NOT_INITIALIZED;
3681
+ /**
3682
+ * Number of messages that arrived
3683
+ * @private
3684
+ */
2625
3685
  this._messageCount = 0;
3686
+ /**
3687
+ * Was disconnected by user?
3688
+ * @protected
3689
+ */
2626
3690
  this._disconnectedByUser = false;
3691
+ /**
3692
+ * Whenever we're connected to a server or not
3693
+ * @private
3694
+ */
2627
3695
  this._isConnected = false;
3696
+ /**
3697
+ * Counts connections
3698
+ * @protected
3699
+ */
2628
3700
  this._sequenceNumber = -1;
2629
3701
  }
3702
+ /**
3703
+ * Creates and starts new socket connection
3704
+ *
3705
+ * @param socketURL
3706
+ * @private
3707
+ */
2630
3708
  startConnection() {
2631
3709
  this._disconnectedByUser = false;
2632
3710
  this._messageCount = 0;
@@ -2660,24 +3738,61 @@
2660
3738
  if (this._connectionState == ConnectionState.CONNECTED) {
2661
3739
  try {
2662
3740
  this.socket.close();
2663
- } catch (error) {}
3741
+ } catch (error) {
3742
+ //
3743
+ }
2664
3744
  }
2665
3745
  };
2666
3746
  this._connectionTimeout = setTimeout(() => {
2667
3747
  try {
2668
3748
  this.socket.close();
2669
- } catch (error) {}
3749
+ } catch (error) {
3750
+ //
3751
+ }
2670
3752
  if (this._connectionState == ConnectionState.CONNECTING) {
2671
3753
  this._connectionState = ConnectionState.FAILED;
2672
3754
  this.onSocketError(new ErrorEvent("connectionTimeout"));
2673
3755
  }
2674
3756
  }, this.CONNECTION_TIMEOUT * 1000);
2675
3757
  }
2676
- onSocketOpen(event) {}
2677
- onSocketClose(event) {}
2678
- onSocketMessage(event) {}
2679
- onSocketError(event) {}
2680
- onError(error) {}
3758
+ /**
3759
+ * Method is called once connection with the server is established
3760
+ * @param event
3761
+ */
3762
+ onSocketOpen(event) {
3763
+ // nothing, for extension only
3764
+ }
3765
+ /**
3766
+ * Method is called once connection with the server is closed (
3767
+ *
3768
+ * @param event
3769
+ */
3770
+ onSocketClose(event) {
3771
+ // nothing, for extension only
3772
+ }
3773
+ /**
3774
+ * Method is called whenever a new message from socket arrives
3775
+ * @private
3776
+ */
3777
+ onSocketMessage(event) {
3778
+ // nothing, for extension only
3779
+ }
3780
+ /**
3781
+ * Method is called whenever an error on socket occures
3782
+ *
3783
+ * @param event
3784
+ * @private
3785
+ */
3786
+ onSocketError(event) {
3787
+ // nothing, for extension only
3788
+ }
3789
+ onError(error) {
3790
+ // nothing, for extension only
3791
+ }
3792
+ /**
3793
+ * Sends data via socket
3794
+ * @param data
3795
+ */
2681
3796
  sendData(data) {
2682
3797
  if (this._connectionState == ConnectionState.CONNECTED) {
2683
3798
  if (this.socket !== null) {
@@ -2689,9 +3804,15 @@
2689
3804
  }
2690
3805
  this.onError("socket not connected");
2691
3806
  }
3807
+ /**
3808
+ * Returns player state e.g. NOT_INITIALIZED, STARTED, CONNECTED, ENDED
3809
+ */
2692
3810
  getConnectionState() {
2693
3811
  return this._connectionState;
2694
3812
  }
3813
+ /**
3814
+ * Rozłacza z serwerem
3815
+ */
2695
3816
  disconnect(byUser = true) {
2696
3817
  this._isConnected = false;
2697
3818
  this._connectionState = ConnectionState.CLOSED;
@@ -2707,6 +3828,11 @@
2707
3828
  this.socket.close();
2708
3829
  }
2709
3830
  }
3831
+ /**
3832
+ * Destroys connection
3833
+ *
3834
+ * @protected
3835
+ */
2710
3836
  destroy() {
2711
3837
  if (this.socket !== undefined && this.socket !== null) {
2712
3838
  this.socket.onopen = null;
@@ -2717,6 +3843,10 @@
2717
3843
  }
2718
3844
  this._connectionState = ConnectionState.CLOSED;
2719
3845
  }
3846
+ /**
3847
+ * Returns currenly used socketURL
3848
+ * @protected
3849
+ */
2720
3850
  getSocketURL() {
2721
3851
  return this.socketURL;
2722
3852
  }
@@ -2790,7 +3920,18 @@
2790
3920
  }
2791
3921
  }
2792
3922
 
3923
+ /**
3924
+ * Main class for communicating with Storm Streaming Server
3925
+ */
2793
3926
  class WowzaStatusConnection extends AbstractSocket {
3927
+ //------------------------------------------------------------------------//
3928
+ // CONSTRUCTOR
3929
+ //------------------------------------------------------------------------//
3930
+ /**
3931
+ * Constructor
3932
+ * @param main
3933
+ * @constructor
3934
+ */
2794
3935
  constructor(main, server) {
2795
3936
  super();
2796
3937
  this._logger = main.getLogger();
@@ -2806,6 +3947,14 @@
2806
3947
  this.startConnection();
2807
3948
  } else this._logger.error(this, "Connection with the server could not be initialized!");
2808
3949
  }
3950
+ //------------------------------------------------------------------------//
3951
+ // BASE EVENTS
3952
+ //------------------------------------------------------------------------//
3953
+ /**
3954
+ * On server connection opened;
3955
+ * @param event
3956
+ * @protected
3957
+ */
2809
3958
  onSocketOpen(event) {
2810
3959
  this._logger.success(this, `Connection with the server has been established!`);
2811
3960
  this._main.dispatchEvent("statusServerConnect", {
@@ -2815,6 +3964,11 @@
2815
3964
  });
2816
3965
  this._isConnected = true;
2817
3966
  }
3967
+ /**
3968
+ * On server connection error
3969
+ * @param event
3970
+ * @protected
3971
+ */
2818
3972
  onSocketError(event) {
2819
3973
  this._isConnected = false;
2820
3974
  if (!this._disconnectedByUser) {
@@ -2827,6 +3981,11 @@
2827
3981
  });
2828
3982
  }
2829
3983
  }
3984
+ /**
3985
+ * On server connection closed
3986
+ * @param event
3987
+ * @protected
3988
+ */
2830
3989
  onSocketClose(event) {
2831
3990
  this._isConnected = false;
2832
3991
  if (!this._disconnectedByUser) {
@@ -2840,6 +3999,10 @@
2840
3999
  this.initiateReconnect();
2841
4000
  } else this._logger.warning(this, `Force disconnect from server!`);
2842
4001
  }
4002
+ /**
4003
+ * Method is called whenever a new message from socket arrives
4004
+ * @private
4005
+ */
2843
4006
  onSocketMessage(event) {
2844
4007
  if (typeof event.data == "string") {
2845
4008
  let msgJSON = JSON.parse(event.data);
@@ -2864,6 +4027,10 @@
2864
4027
  }
2865
4028
  }
2866
4029
  }
4030
+ /**
4031
+ * Initiates reconnection procedure
4032
+ * @private
4033
+ */
2867
4034
  initiateReconnect() {
2868
4035
  const shouldReconnect = this._main.getConfigManager().getSettingsData().getIfRestartOnError();
2869
4036
  const reconnectTime = this._main.getConfigManager().getSettingsData().getReconnectTime();
@@ -2884,6 +4051,14 @@
2884
4051
  }, reconnectTime * 1000);
2885
4052
  }
2886
4053
  }
4054
+ //------------------------------------------------------------------------//
4055
+ // BASE METHODS
4056
+ //------------------------------------------------------------------------//
4057
+ /**
4058
+ * Creates new URL for WebSockets based on serverItem object
4059
+ * @param serverItem
4060
+ * @private
4061
+ */
2887
4062
  createURL(serverItem) {
2888
4063
  let url = "";
2889
4064
  url += serverItem.getIfSSL() ? "wss://" : "ws://";
@@ -2892,12 +4067,18 @@
2892
4067
  url += "/statuschecker";
2893
4068
  return url;
2894
4069
  }
4070
+ /**
4071
+ * Returns true/false depending if a connection with a server is active
4072
+ */
2895
4073
  isConnectionActive() {
2896
4074
  return this._isConnected;
2897
4075
  }
2898
4076
  getCurrentServer() {
2899
4077
  return this._currServer;
2900
4078
  }
4079
+ /**
4080
+ * Destroys object and clean-ups everything that is needed
4081
+ */
2901
4082
  destroy() {
2902
4083
  super.destroy();
2903
4084
  }
@@ -2943,16 +4124,51 @@
2943
4124
  }
2944
4125
  }
2945
4126
 
4127
+ /**
4128
+ * This class (instance) is responsible to controlling video playback. It's the central point of the whole project, where
4129
+ * all components are being managed.
4130
+ */
2946
4131
  class StreamerController {
4132
+ //------------------------------------------------------------------------//
4133
+ // CONSTRUCTOR
4134
+ //------------------------------------------------------------------------//
4135
+ /**
4136
+ * Constructor - requires properly configured config object
4137
+ * @param config
4138
+ */
2947
4139
  constructor(main) {
2948
4140
  var _a, _b, _c;
4141
+ /**
4142
+ * Whenever current window is active or not
4143
+ * @private
4144
+ */
2949
4145
  this._isWindowActive = true;
4146
+ /**
4147
+ * Default configuration for ICE-Servers
4148
+ * @private
4149
+ */
2950
4150
  this._peerConnectionConfig = {
2951
4151
  'iceServers': []
2952
4152
  };
4153
+ /**
4154
+ * Whenever microphone is currenly muted
4155
+ * @private
4156
+ */
2953
4157
  this._isMicrophoneMuted = false;
4158
+ /**
4159
+ * Desired microphone state that should be applied when stream becomes available
4160
+ * @private
4161
+ */
2954
4162
  this._pendingMicrophoneState = null;
4163
+ /**
4164
+ * Whenever we have cheched for permissions
4165
+ * @private
4166
+ */
2955
4167
  this._permissionChecked = false;
4168
+ /**
4169
+ * A default set of permissions
4170
+ * @private
4171
+ */
2956
4172
  this._constraints = {
2957
4173
  video: {
2958
4174
  width: {
@@ -2973,17 +4189,45 @@
2973
4189
  },
2974
4190
  audio: true
2975
4191
  };
4192
+ /**
4193
+ * Current count for restart, _restartTimerMaxCount is the max number
4194
+ * @private
4195
+ */
2976
4196
  this._restartTimerCount = 0;
4197
+ /**
4198
+ * max
4199
+ * @private
4200
+ */
2977
4201
  this._restartTimerMaxCount = 5;
4202
+ /**
4203
+ * Current streamer state
4204
+ * @protected
4205
+ */
2978
4206
  this._publishState = exports.PublishState.NOT_INITIALIZED;
2979
4207
  this._publishTime = 0;
4208
+ /**
4209
+ * General state for streamer
4210
+ * @protected
4211
+ */
2980
4212
  this._inputDeviceState = exports.InputDevicesState.NOT_INITIALIZED;
4213
+ /**
4214
+ * Current Camera state
4215
+ * @protected
4216
+ */
2981
4217
  this._cameraState = exports.DeviceState.NOT_INITIALIZED;
4218
+ /**
4219
+ * Current Microphone state
4220
+ * @protected
4221
+ */
2982
4222
  this._microphoneState = exports.DeviceState.NOT_INITIALIZED;
2983
4223
  this._publishTimer = 0;
2984
4224
  this._currentOrientation = ((_a = window.screen.orientation) === null || _a === void 0 ? void 0 : _a.type) || '';
2985
4225
  this._statusTimer = null;
2986
4226
  this._debug = false;
4227
+ /**
4228
+ * Handles device state changes and initiates publishing if appropriate
4229
+ * @private
4230
+ */
2987
4231
  this.onDeviceStateChange = event => {
2988
4232
  var _a;
2989
4233
  const usedStreamKey = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey;
@@ -2991,15 +4235,21 @@
2991
4235
  this.publish(usedStreamKey);
2992
4236
  }
2993
4237
  };
4238
+ /**
4239
+ * This method is responsible for handing orientation changes for mobile devices
4240
+ */
2994
4241
  this.handleOrientationChange = () => __awaiter(this, void 0, void 0, function* () {
2995
4242
  var _d, _e;
4243
+ // Dajemy chwilę na ustabilizowanie się orientacji
2996
4244
  yield new Promise(resolve => setTimeout(resolve, 500));
2997
4245
  const newOrientation = ((_d = window.screen.orientation) === null || _d === void 0 ? void 0 : _d.type) || '';
2998
4246
  if (this._currentOrientation !== newOrientation) {
2999
4247
  this._logger.info(this, `Orientation changed from ${this._currentOrientation} to ${newOrientation}`);
3000
4248
  this._currentOrientation = newOrientation;
4249
+ // Zapamiętaj aktualny stream key i stan publikacji
3001
4250
  const streamKey = (_e = this._main.getConfigManager()) === null || _e === void 0 ? void 0 : _e.getStreamData().streamKey;
3002
4251
  this._publishState === exports.PublishState.PUBLISHED;
4252
+ // Zamknij istniejące połączenie i stream
3003
4253
  this.closeWebRTCConnection();
3004
4254
  if (this._stream) {
3005
4255
  this._stream.getTracks().forEach(track => {
@@ -3007,8 +4257,10 @@
3007
4257
  });
3008
4258
  this._stream = null;
3009
4259
  }
4260
+ // Rozpocznij wszystko od nowa
3010
4261
  try {
3011
4262
  yield this.startCamera();
4263
+ // Jeśli stream był opublikowany, publikujemy ponownie
3012
4264
  if (streamKey) {
3013
4265
  this.publish(streamKey);
3014
4266
  }
@@ -3019,6 +4271,9 @@
3019
4271
  }
3020
4272
  });
3021
4273
  this.onServerDisconnect = () => {};
4274
+ /**
4275
+ * Method for handling a situation when a given streamKey is already in use.
4276
+ */
3022
4277
  this.onStreamKeyTaken = () => {
3023
4278
  if (this._restartTimer != null) {
3024
4279
  clearInterval(this._restartTimer);
@@ -3053,17 +4308,26 @@
3053
4308
  }
3054
4309
  }, 1000);
3055
4310
  };
4311
+ //------------------------------------------------------------------------//
4312
+ // NETWORK AND RTC
4313
+ //------------------------------------------------------------------------//
4314
+ /**
4315
+ * Method fires once a connection with wowza is established. It's the main connection where we exchange
4316
+ * ice-candidates and SDP. We'll start setting up peer connections.
4317
+ */
3056
4318
  this.onServerConnect = () => {
3057
4319
  if (this._peerConnection) {
3058
4320
  this.closeWebRTCConnection();
3059
4321
  }
3060
4322
  this._peerConnection = new RTCPeerConnection(this._peerConnectionConfig);
4323
+ // Najpierw dodaj tracki
3061
4324
  if (this._stream) {
3062
4325
  let localTracks = this._stream.getTracks();
3063
4326
  for (let localTrack in localTracks) {
3064
4327
  this._peerConnection.addTrack(localTracks[localTrack], this._stream);
3065
4328
  }
3066
4329
  }
4330
+ // Potem dodaj event handlery
3067
4331
  this._peerConnection.onicecandidate = event => {
3068
4332
  this.onIceCandidate(event);
3069
4333
  };
@@ -3083,6 +4347,9 @@
3083
4347
  });
3084
4348
  this.createStatusConnection();
3085
4349
  };
4350
+ /**
4351
+ * Method fires once a status connection is established. We'll set an interval for monitoring stream status.
4352
+ */
3086
4353
  this.onStatusServerConnect = () => {
3087
4354
  const usedStreamKey = this._main.getConfigManager().getStreamData().streamKey;
3088
4355
  if (this._statusTimer == null) {
@@ -3095,13 +4362,20 @@
3095
4362
  };
3096
4363
  this.requestStatusData = () => {
3097
4364
  var _a;
3098
- const usedStreamKey = this._main.getConfigManager().getStreamData().streamKey;
3099
- (_a = this._statusConnection) === null || _a === void 0 ? void 0 : _a.sendData('{"packetID":"STREAM_STATUS", "streamKey": "' + usedStreamKey + '"}');
4365
+ (_a = this._statusConnection) === null || _a === void 0 ? void 0 : _a.sendData('{"packetID":"STREAM_STATUS", "streamKey": "' + this._fullStreamName + '"}');
3100
4366
  };
4367
+ /**
4368
+ * If for some reason the status connection is disconnected we have to clean the interval
4369
+ */
3101
4370
  this.onStatusServerDisconnect = () => {
3102
4371
  if (this._statusTimer != null) clearInterval(this._statusTimer);
3103
4372
  this._statusTimer = null;
3104
4373
  };
4374
+ /**
4375
+ * This event fires whenever "STREAM_STATUS_RESPONSE" packet form status connection reports stream status along some stream data. This gives
4376
+ * us an insight into whenever our stream is ok (works) or not.
4377
+ * @param event
4378
+ */
3105
4379
  this.onStreamStatsUpdate = event => {
3106
4380
  const update = event.streamStatus;
3107
4381
  if (this._publishState == exports.PublishState.PUBLISHED && update.publishState != exports.PublishState.PUBLISHED) this.setPublishState(exports.PublishState.UNPUBLISHED);
@@ -3109,9 +4383,10 @@
3109
4383
  };
3110
4384
  this.onDescriptionSuccess = description => {
3111
4385
  var _a, _b, _c;
4386
+ this._fullStreamName = ((_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey) + "_" + new Date().getTime();
3112
4387
  const streamInfo = {
3113
- applicationName: (_b = (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.getConnection().getCurrentServer()) === null || _b === void 0 ? void 0 : _b.getApplication(),
3114
- streamName: (_c = this._main.getConfigManager()) === null || _c === void 0 ? void 0 : _c.getStreamData().streamKey,
4388
+ applicationName: (_c = (_b = this._main.getNetworkController()) === null || _b === void 0 ? void 0 : _b.getConnection().getCurrentServer()) === null || _c === void 0 ? void 0 : _c.getApplication(),
4389
+ streamName: this._fullStreamName,
3115
4390
  sessionId: "[empty]"
3116
4391
  };
3117
4392
  description.sdp = this._mungeSDP.mungeSDPPublish(description.sdp, {
@@ -3127,9 +4402,16 @@
3127
4402
  (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.sendMessage('{"direction":"publish", "command":"sendOffer", "streamInfo":' + JSON.stringify(streamInfo) + ', "sdp":' + JSON.stringify(description) + '}');
3128
4403
  }).catch(error => {
3129
4404
  console.log(error);
4405
+ //this.onWebRTCError(error, self);
3130
4406
  });
3131
4407
  }
3132
4408
  };
4409
+ //------------------------------------------------------------------------//
4410
+ // BLUR & FOCUS
4411
+ //------------------------------------------------------------------------//
4412
+ /**
4413
+ * Methods handles visibility change events
4414
+ */
3133
4415
  this.visibilityChange = () => {
3134
4416
  if (document.visibilityState === 'hidden') {
3135
4417
  this.onWindowBlur();
@@ -3137,12 +4419,18 @@
3137
4419
  this.onWindowFocus();
3138
4420
  }
3139
4421
  };
4422
+ /**
4423
+ * Reacts to browser changing visibility of the document (or blur)
4424
+ */
3140
4425
  this.onWindowBlur = () => {
3141
4426
  if (this._isWindowActive) {
3142
4427
  this._logger.warning(this, "Player window is no longer in focus!");
3143
4428
  }
3144
4429
  this._isWindowActive = false;
3145
4430
  };
4431
+ /**
4432
+ * Reacts to browser changing visibility of the document (or focus)
4433
+ */
3146
4434
  this.onWindowFocus = () => {
3147
4435
  if (!this._isWindowActive) {
3148
4436
  this._logger.info(this, "Player window is focused again!");
@@ -3154,8 +4442,18 @@
3154
4442
  this._mungeSDP = new MungeSDP();
3155
4443
  this._soundMeter = new SoundMeter(this._main);
3156
4444
  this._debug = (_c = (_b = this._main.getConfigManager()) === null || _b === void 0 ? void 0 : _b.getSettingsData().getDebugData().streamerControllerDebug) !== null && _c !== void 0 ? _c : this._debug;
4445
+ // Start initialization process
3157
4446
  this.initialize();
3158
4447
  }
4448
+ //------------------------------------------------------------------------//
4449
+ // MAIN METHODS
4450
+ //------------------------------------------------------------------------//
4451
+ /**
4452
+ * Initializes the PlaybackController by setting up event listeners and device handling
4453
+ * This method orchestrates the initialization process by first checking device availability
4454
+ * and permissions, then setting up necessary event listeners and configurations
4455
+ * @private
4456
+ */
3159
4457
  initialize() {
3160
4458
  var _a, _b;
3161
4459
  return __awaiter(this, void 0, void 0, function* () {
@@ -3177,6 +4475,10 @@
3177
4475
  }
3178
4476
  });
3179
4477
  }
4478
+ /**
4479
+ * Sets up core event listeners for the controller
4480
+ * @private
4481
+ */
3180
4482
  setupEventListeners() {
3181
4483
  this._main.addEventListener("serverConnect", this.onServerConnect, false);
3182
4484
  this._main.addEventListener("serverDisconnect", this.onServerDisconnect, false);
@@ -3189,14 +4491,21 @@
3189
4491
  window.addEventListener("blur", this.onWindowBlur);
3190
4492
  window.addEventListener("focus", this.onWindowFocus);
3191
4493
  }
4494
+ /**
4495
+ * Initializes media devices by checking permissions and setting up initial device lists
4496
+ * @private
4497
+ */
3192
4498
  initializeDevices() {
3193
4499
  return __awaiter(this, void 0, void 0, function* () {
3194
4500
  try {
4501
+ // Request initial device permissions
3195
4502
  const stream = yield navigator.mediaDevices.getUserMedia({
3196
4503
  video: true,
3197
4504
  audio: true
3198
4505
  });
4506
+ // Stop the test stream
3199
4507
  stream.getTracks().forEach(track => track.stop());
4508
+ // Initialize device lists
3200
4509
  yield this.grabDevices();
3201
4510
  } catch (error) {
3202
4511
  this._logger.error(this, "Error initializing devices: " + JSON.stringify(error));
@@ -3204,6 +4513,10 @@
3204
4513
  }
3205
4514
  });
3206
4515
  }
4516
+ /**
4517
+ * Initializes the media stream if a camera or microphone is selected
4518
+ * @private
4519
+ */
3207
4520
  initializeStream() {
3208
4521
  if (this._selectedCamera || this._selectedMicrophone) {
3209
4522
  this.startCamera();
@@ -3243,8 +4556,12 @@
3243
4556
  }
3244
4557
  });
3245
4558
  }
4559
+ /**
4560
+ * Handles successful camera stream initialization
4561
+ */
3246
4562
  onCameraStreamSuccess(stream) {
3247
4563
  this._logger.success(this, "Camera stream successfully retrieved");
4564
+ // Get actual stream dimensions
3248
4565
  const videoTrack = stream.getVideoTracks()[0];
3249
4566
  const audioTrack = stream.getAudioTracks()[0];
3250
4567
  const streamData = new PublishMetadata();
@@ -3252,6 +4569,7 @@
3252
4569
  const settings = videoTrack.getSettings();
3253
4570
  let width = settings.width;
3254
4571
  let height = settings.height;
4572
+ // Na urządzeniach mobilnych potrzebujemy skorygować wymiary
3255
4573
  const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
3256
4574
  if (isMobile) {
3257
4575
  if (width > height && window.innerWidth < window.innerHeight || width < height && window.innerWidth > window.innerHeight) {
@@ -3261,10 +4579,12 @@
3261
4579
  streamData.streamWidth = width;
3262
4580
  streamData.streamHeight = height;
3263
4581
  streamData.videoTrackPresent = true;
4582
+ // Obliczanie proporcji i konwersja na string
3264
4583
  const gcd = (a, b) => b ? gcd(b, a % b) : a;
3265
4584
  const divisor = gcd(width, height);
3266
4585
  const widthRatio = width / divisor;
3267
4586
  const heightRatio = height / divisor;
4587
+ // Sprawdzamy typowe proporcje z pewną tolerancją
3268
4588
  const ratio = width / height;
3269
4589
  let aspectRatioString = `${widthRatio}:${heightRatio}`;
3270
4590
  if (Math.abs(ratio - 16 / 9) < 0.1) {
@@ -3278,6 +4598,7 @@
3278
4598
  }
3279
4599
  streamData.audioTrackPresent = !!audioTrack;
3280
4600
  this._logger.info(this, `Publish MetaData :: Resolution: ${streamData.streamWidth}x${streamData.streamHeight} | ` + `Aspect ratio: ${streamData.aspectRatio}, ` + `Video track: ${streamData.videoTrackPresent} | Audio track: ${streamData.audioTrackPresent}`);
4601
+ // Dispatch event with stream data
3281
4602
  this._main.dispatchEvent("publishMetadataUpdate", {
3282
4603
  ref: this._main,
3283
4604
  metadata: streamData
@@ -3304,6 +4625,9 @@
3304
4625
  videoElement.muted = true;
3305
4626
  this.setPublishState(exports.PublishState.INITIALIZED);
3306
4627
  }
4628
+ /**
4629
+ * Initializes WebRTC connection
4630
+ */
3307
4631
  initializeWebRTC() {
3308
4632
  var _a;
3309
4633
  if (!this._stream) {
@@ -3318,6 +4642,12 @@
3318
4642
  (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.start();
3319
4643
  }
3320
4644
  }
4645
+ /**
4646
+ * Modified publish method to handle both camera and WebRTC
4647
+ *
4648
+ * @param streamKey - klucz streamu
4649
+ * @returns {boolean} - true jeśli udało się rozpocząć publikowanie
4650
+ */
3321
4651
  publish(streamKey) {
3322
4652
  if (this._statusTimer != null) clearInterval(this._statusTimer);
3323
4653
  if (this._main.getConfigManager().getStreamData().streamKey == streamKey && this._publishState == exports.PublishState.CONNECTED) {
@@ -3357,21 +4687,40 @@
3357
4687
  });
3358
4688
  this.closeStream();
3359
4689
  }
4690
+ /**
4691
+ * This method
4692
+ * @private
4693
+ */
3360
4694
  setupOrientationListener() {
4695
+ // Sprawdzamy czy urządzenie wspiera event orientationchange
3361
4696
  if (window.screen && window.screen.orientation) {
3362
4697
  window.screen.orientation.addEventListener('change', this.handleOrientationChange);
3363
4698
  } else {
4699
+ // Fallback dla starszych urządzeń
3364
4700
  window.addEventListener('orientationchange', this.handleOrientationChange);
3365
4701
  }
3366
4702
  }
4703
+ //------------------------------------------------------------------------//
4704
+ // USER MEDIA
4705
+ //------------------------------------------------------------------------//
4706
+ /**
4707
+ * Error on trying to grab video stream (it usually means that browser does not support WebRTC streaming)
4708
+ *
4709
+ * @param error
4710
+ */
3367
4711
  onUserMediaError(error) {
3368
4712
  return __awaiter(this, void 0, void 0, function* () {
3369
4713
  yield this.grabDevices();
4714
+ // Dodatkowa obsługa specyficznych błędów getUserMedia, jeśli potrzebna
3370
4715
  if (error.name === "OverconstrainedError") {
3371
4716
  this._logger.warning(this, "Device constraints not satisfied");
3372
4717
  }
3373
4718
  });
3374
4719
  }
4720
+ /**
4721
+ * This method is used for checking individual access to output devices
4722
+ * @private
4723
+ */
3375
4724
  checkIndividualDeviceAccess() {
3376
4725
  return __awaiter(this, void 0, void 0, function* () {
3377
4726
  const results = {
@@ -3384,27 +4733,35 @@
3384
4733
  available: false
3385
4734
  }
3386
4735
  };
4736
+ // Sprawdzamy fizyczną dostępność urządzeń
3387
4737
  try {
3388
4738
  const devices = yield navigator.mediaDevices.enumerateDevices();
3389
4739
  results.camera.available = devices.some(device => device.kind === 'videoinput');
3390
4740
  results.microphone.available = devices.some(device => device.kind === 'audioinput');
4741
+ // Sprawdzamy czy mamy etykiety urządzeń - ich brak może oznaczać brak uprawnień
3391
4742
  const hasLabels = devices.some(device => device.label !== '');
3392
4743
  if (!hasLabels) {
4744
+ // Jeśli nie mamy etykiet, wymuszamy pytanie o uprawnienia
3393
4745
  try {
3394
4746
  const stream = yield navigator.mediaDevices.getUserMedia({
3395
4747
  video: results.camera.available,
3396
4748
  audio: results.microphone.available
3397
4749
  });
4750
+ // Jeśli udało się uzyskać strumień, mamy uprawnienia
3398
4751
  results.camera.allowed = stream.getVideoTracks().length > 0;
3399
4752
  results.microphone.allowed = stream.getAudioTracks().length > 0;
4753
+ // Zatrzymujemy strumień testowy
3400
4754
  stream.getTracks().forEach(track => track.stop());
4755
+ // Aktualizujemy listę urządzeń po uzyskaniu uprawnień
3401
4756
  yield this.grabDevices();
3402
4757
  } catch (error) {
3403
4758
  console.error('Error requesting permissions:', error);
4759
+ // Nie udało się uzyskać uprawnień
3404
4760
  results.camera.allowed = false;
3405
4761
  results.microphone.allowed = false;
3406
4762
  }
3407
4763
  } else {
4764
+ // Jeśli mamy etykiety, prawdopodobnie mamy już uprawnienia
3408
4765
  results.camera.allowed = devices.some(device => device.kind === 'videoinput' && device.label !== '');
3409
4766
  results.microphone.allowed = devices.some(device => device.kind === 'audioinput' && device.label !== '');
3410
4767
  }
@@ -3418,15 +4775,25 @@
3418
4775
  return results;
3419
4776
  });
3420
4777
  }
4778
+ //------------------------------------------------------------------------//
4779
+ // SOCKETS & SDP
4780
+ //------------------------------------------------------------------------//
4781
+ /**
4782
+ * This method handles basic SDP/ICE-Candidate exchange with a Wowza Server
4783
+ *
4784
+ * @param data
4785
+ */
3421
4786
  onSocketMessage(data) {
3422
4787
  var _a;
3423
4788
  let msgJSON = JSON.parse(data);
3424
4789
  let msgStatus = Number(msgJSON["status"]);
3425
4790
  switch (msgStatus) {
3426
4791
  case 200:
4792
+ // OK
3427
4793
  this._logger.info(this, "SDP Exchange Successful");
3428
4794
  let sdpData = msgJSON['sdp'];
3429
4795
  if (sdpData !== undefined) {
4796
+ // @ts-ignore
3430
4797
  this._peerConnection.setRemoteDescription(new RTCSessionDescription(sdpData), () => {}, () => {});
3431
4798
  }
3432
4799
  let iceCandidates = msgJSON['iceCandidates'];
@@ -3437,6 +4804,7 @@
3437
4804
  }
3438
4805
  break;
3439
4806
  case 503:
4807
+ // NOT OK
3440
4808
  this._logger.error(this, "StreamKey already use");
3441
4809
  const usedStreamKey = (_a = this._main.getConfigManager().getStreamData().streamKey) !== null && _a !== void 0 ? _a : "unknown";
3442
4810
  this._main.dispatchEvent("streamKeyInUse", {
@@ -3447,6 +4815,16 @@
3447
4815
  break;
3448
4816
  }
3449
4817
  }
4818
+ //------------------------------------------------------------------------//
4819
+ // EVENTS
4820
+ //------------------------------------------------------------------------//
4821
+ /**
4822
+ * Recives events related to peerConnection (change of state)
4823
+ *
4824
+ * @param event event with its data
4825
+ * @param thisRef reference to player classonConnectionStateChange
4826
+ * @private
4827
+ */
3450
4828
  onConnectionStateChange(event) {
3451
4829
  this._logger.info(this, "Connection State Change: " + JSON.stringify(event));
3452
4830
  if (event !== null) {
@@ -3474,12 +4852,20 @@
3474
4852
  }
3475
4853
  }
3476
4854
  }
4855
+ //------------------------------------------------------------------------//
4856
+ // DEVICES
4857
+ //------------------------------------------------------------------------//
4858
+ /**
4859
+ * Returns list od devices (cameras, microphones) available for user's device
4860
+ */
3477
4861
  grabDevices() {
3478
4862
  return __awaiter(this, void 0, void 0, function* () {
3479
4863
  try {
3480
4864
  const deviceAccess = yield this.checkIndividualDeviceAccess();
3481
4865
  this._cameraList = new InputDeviceList();
3482
4866
  this._microphoneList = new InputDeviceList();
4867
+ // Wysyłamy eventy tylko jeśli nie sprawdzaliśmy wcześniej uprawnień
4868
+ // lub jeśli zmienił się stan uprawnień (resetowana flaga)
3483
4869
  if (!this._permissionChecked) {
3484
4870
  if (!deviceAccess.camera.allowed) {
3485
4871
  this._main.dispatchEvent("cameraAccessDenied", {
@@ -3508,6 +4894,7 @@
3508
4894
  this.setInputDeviceState(exports.InputDevicesState.INVALID);
3509
4895
  }
3510
4896
  }
4897
+ // Wypełnianie list dostępnymi urządzeniami
3511
4898
  const devices = yield navigator.mediaDevices.enumerateDevices();
3512
4899
  for (const device of devices) {
3513
4900
  if (device.deviceId && device.label) {
@@ -3521,6 +4908,7 @@
3521
4908
  }
3522
4909
  }
3523
4910
  try {
4911
+ // Aktualizacja wybranych urządzeń
3524
4912
  if (deviceAccess.camera.allowed) {
3525
4913
  this._selectedCamera = this.pickCamera();
3526
4914
  }
@@ -3531,6 +4919,7 @@
3531
4919
  this.setInputDeviceState(exports.InputDevicesState.INVALID);
3532
4920
  this._logger.error(this, "Errror on grab devices: " + JSON.stringify(error));
3533
4921
  }
4922
+ // Zawsze wysyłamy aktualizację list urządzeń
3534
4923
  this._main.dispatchEvent("deviceListUpdate", {
3535
4924
  ref: this._main,
3536
4925
  cameraList: this._cameraList.getArray(),
@@ -3552,6 +4941,10 @@
3552
4941
  }
3553
4942
  });
3554
4943
  }
4944
+ /**
4945
+ * Selects camera based on camera device ID;
4946
+ * @param cameraID
4947
+ */
3555
4948
  selectCamera(cameraID) {
3556
4949
  var _a, _b;
3557
4950
  for (let i = 0; i < this._cameraList.getSize(); i++) {
@@ -3560,6 +4953,7 @@
3560
4953
  this._selectedCamera = null;
3561
4954
  this.setInputDeviceState(exports.InputDevicesState.UPDATING);
3562
4955
  this.setCameraState(exports.DeviceState.NOT_INITIALIZED);
4956
+ // Zapamiętaj aktualny stream key i stan publikacji
3563
4957
  const streamKey = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey;
3564
4958
  const wasPublished = this._publishState === exports.PublishState.CONNECTED;
3565
4959
  for (let i = 0; i < this._cameraList.getSize(); i++) {
@@ -3577,8 +4971,11 @@
3577
4971
  });
3578
4972
  this.stopCameraStream();
3579
4973
  if (this._selectedCamera != null) {
4974
+ // Update constraints with new device
3580
4975
  this._constraints.video.deviceId = this._selectedCamera.id;
4976
+ // Restart camera stream
3581
4977
  this.startCamera().then(() => {
4978
+ // Jeśli stream był opublikowany, publikujemy ponownie
3582
4979
  this.setCameraState(exports.DeviceState.ENABLED);
3583
4980
  if (this._cameraState == exports.DeviceState.ENABLED && this._microphoneState == exports.DeviceState.ENABLED) this.setInputDeviceState(exports.InputDevicesState.READY);else this.setInputDeviceState(exports.InputDevicesState.INVALID);
3584
4981
  if (wasPublished && streamKey) {
@@ -3589,6 +4986,10 @@
3589
4986
  this.setInputDeviceState(exports.InputDevicesState.INVALID);
3590
4987
  }
3591
4988
  }
4989
+ /**
4990
+ * Method tries to select (change) microphone based on its system ID
4991
+ * @param micID
4992
+ */
3592
4993
  selectMicrophone(micID) {
3593
4994
  var _a, _b;
3594
4995
  return __awaiter(this, void 0, void 0, function* () {
@@ -3599,8 +5000,10 @@
3599
5000
  this.setInputDeviceState(exports.InputDevicesState.UPDATING);
3600
5001
  this.setMicrophoneState(exports.DeviceState.NOT_INITIALIZED);
3601
5002
  this._logger.info(this, "Selecting microphone: " + micID);
5003
+ // Zapamiętaj aktualny stream key i stan publikacji
3602
5004
  const streamKey = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey;
3603
5005
  const wasPublished = this._publishState === exports.PublishState.CONNECTED;
5006
+ // Znajdź i zapisz wybrany mikrofon
3604
5007
  for (let i = 0; i < this._microphoneList.getSize(); i++) {
3605
5008
  if (this._microphoneList.get(i).id == micID) {
3606
5009
  this._selectedMicrophone = this._microphoneList.get(i);
@@ -3609,13 +5012,17 @@
3609
5012
  break;
3610
5013
  }
3611
5014
  }
5015
+ // Zawsze wysyłamy aktualizację list urządzeń
3612
5016
  this._main.dispatchEvent("deviceListUpdate", {
3613
5017
  ref: this._main,
3614
5018
  cameraList: this._cameraList.getArray(),
3615
5019
  microphoneList: this._microphoneList.getArray()
3616
5020
  });
5021
+ // Odłącz SoundMeter przed zmianą strumienia
3617
5022
  this._soundMeter.detach();
5023
+ // Zamknij istniejące połączenie WebRTC
3618
5024
  this.closeWebRTCConnection();
5025
+ // Zatrzymaj obecny strumień
3619
5026
  if (this._stream) {
3620
5027
  this._stream.getTracks().forEach(track => {
3621
5028
  track.stop();
@@ -3623,6 +5030,7 @@
3623
5030
  this._stream = null;
3624
5031
  }
3625
5032
  try {
5033
+ // Rozpocznij wszystko od nowa
3626
5034
  yield this.startCamera();
3627
5035
  this.setMicrophoneState(exports.DeviceState.ENABLED);
3628
5036
  if (this._cameraState == exports.DeviceState.ENABLED && this._microphoneState == exports.DeviceState.ENABLED) {
@@ -3630,6 +5038,7 @@
3630
5038
  } else {
3631
5039
  this.setInputDeviceState(exports.InputDevicesState.INVALID);
3632
5040
  }
5041
+ // Jeśli stream był opublikowany, publikujemy ponownie
3633
5042
  if (wasPublished && streamKey) {
3634
5043
  this.publish(streamKey);
3635
5044
  }
@@ -3642,6 +5051,11 @@
3642
5051
  }
3643
5052
  });
3644
5053
  }
5054
+ /**
5055
+ * This method tries to start a camera.
5056
+ *
5057
+ * @private
5058
+ */
3645
5059
  startCamera() {
3646
5060
  return __awaiter(this, void 0, void 0, function* () {
3647
5061
  if (this._stream) {
@@ -3690,24 +5104,37 @@
3690
5104
  }
3691
5105
  });
3692
5106
  }
5107
+ /**
5108
+ * Updates WebRTC connection with new stream
5109
+ */
3693
5110
  updateWebRTCStream() {
3694
5111
  if (!this._peerConnection || !this._stream) {
3695
5112
  return;
3696
5113
  }
5114
+ // Remove all existing tracks from the peer connection
3697
5115
  const senders = this._peerConnection.getSenders();
3698
5116
  senders.forEach(sender => {
3699
5117
  if (this._peerConnection) this._peerConnection.removeTrack(sender);
3700
5118
  });
5119
+ // Add new tracks
3701
5120
  this._stream.getTracks().forEach(track => {
3702
5121
  if (this._stream != null && this._peerConnection) {
3703
5122
  this._peerConnection.addTrack(track, this._stream);
3704
5123
  }
3705
5124
  });
3706
5125
  }
5126
+ /**
5127
+ * Modified closeStream to handle both camera and WebRTC completely
5128
+ */
3707
5129
  closeStream() {
3708
5130
  if (this._peerConnection !== undefined && this._peerConnection !== null) this._peerConnection.close();
3709
5131
  this.setPublishState(exports.PublishState.UNPUBLISHED);
3710
5132
  }
5133
+ /**
5134
+ * This method selects a camera based on previous uses or saved IDs
5135
+ *
5136
+ * @private
5137
+ */
3711
5138
  pickCamera() {
3712
5139
  var _a, _b, _c, _d, _e, _f;
3713
5140
  for (let i = 0; i < this._cameraList.getSize(); i++) {
@@ -3716,12 +5143,14 @@
3716
5143
  let savedCameraID = (_b = (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.getField("cameraID")) !== null && _b !== void 0 ? _b : null;
3717
5144
  if (this._cameraList.getSize() > 0) {
3718
5145
  if (savedCameraID) {
5146
+ // Szukamy zapisanej kamery
3719
5147
  let found = false;
3720
5148
  for (let i = 0; i < this._cameraList.getSize(); i++) {
3721
5149
  if (this._cameraList.get(i).id === savedCameraID) {
3722
5150
  this._selectedCamera = this._cameraList.get(i);
3723
5151
  this._selectedCamera.isSelected = true;
3724
5152
  this.setCameraState(exports.DeviceState.ENABLED);
5153
+ // Ustaw deviceId w constraints
3725
5154
  found = true;
3726
5155
  this._constraints.video.deviceId = this._selectedCamera.id;
3727
5156
  break;
@@ -3741,6 +5170,7 @@
3741
5170
  return null;
3742
5171
  }
3743
5172
  }
5173
+ // Jeśli nie znaleziono zapisanej kamery, używamy pierwszej
3744
5174
  if (!this._selectedCamera) {
3745
5175
  this._main.dispatchEvent("savedCameraNotFound", {
3746
5176
  ref: this._main,
@@ -3770,6 +5200,11 @@
3770
5200
  });
3771
5201
  return this._selectedCamera;
3772
5202
  }
5203
+ /**
5204
+ * This method selects a microphone based on previous uses or saved IDs
5205
+ *
5206
+ * @private
5207
+ */
3773
5208
  pickMicrophone() {
3774
5209
  var _a, _b, _c, _d, _e, _f;
3775
5210
  for (let i = 0; i < this._microphoneList.getSize(); i++) {
@@ -3827,32 +5262,51 @@
3827
5262
  }
3828
5263
  return this._selectedMicrophone;
3829
5264
  }
5265
+ /**
5266
+ * Cleans all saved cameras and microphones IDs.
5267
+ */
3830
5268
  clearSavedDevices() {
3831
5269
  var _a, _b;
3832
5270
  (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.removeField("cameraID");
3833
5271
  (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.removeField("microphoneID");
3834
5272
  }
5273
+ /**
5274
+ * Messes up camera's and microphone's id (for testing only)
5275
+ */
3835
5276
  messSavedDevices() {
3836
5277
  var _a, _b;
3837
5278
  (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.saveField("cameraID", "a");
3838
5279
  (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.saveField("microphoneID", "b");
3839
5280
  }
5281
+ /**
5282
+ * Handles microphone muting state
5283
+ * @param microphoneState true to unmute, false to mute
5284
+ */
3840
5285
  muteMicrophone(shouldMute) {
3841
5286
  if (this._isMicrophoneMuted === shouldMute) {
5287
+ // State hasn't changed, no need to do anything
3842
5288
  return;
3843
5289
  }
3844
5290
  this._isMicrophoneMuted = shouldMute;
3845
5291
  if (this._stream) {
3846
- this.applyMicrophoneState(!shouldMute);
5292
+ this.applyMicrophoneState(!shouldMute); // Odwracamy wartość dla track.enabled
3847
5293
  } else {
3848
- this._pendingMicrophoneState = !shouldMute;
5294
+ // Store the desired state to be applied when stream becomes available
5295
+ this._pendingMicrophoneState = !shouldMute; // Odwracamy wartość dla przyszłego track.enabled
3849
5296
  this._logger.info(this, `WebRTCStreamer :: Stream not yet available, storing microphone state (muted: ${shouldMute})`);
3850
5297
  }
5298
+ // Always dispatch the event to keep UI in sync
3851
5299
  this._main.dispatchEvent("microphoneStateChange", {
3852
5300
  ref: this._main,
3853
5301
  isMuted: this._isMicrophoneMuted
3854
5302
  });
3855
5303
  }
5304
+ /**
5305
+ * Applies the microphone state to the actual stream tracks
5306
+ *
5307
+ * @param enabled true to enable tracks, false to disable
5308
+ * @private
5309
+ */
3856
5310
  applyMicrophoneState(enabled) {
3857
5311
  if (!this._stream) {
3858
5312
  this._logger.warning(this, "WebRTCStreamer :: Cannot apply microphone state - stream not available");
@@ -3866,6 +5320,13 @@
3866
5320
  this._logger.warning(this, "WebRTCStreamer :: No audio tracks found in stream");
3867
5321
  }
3868
5322
  }
5323
+ /**
5324
+ * This methods is a final check whenever we're ready to publish a stream
5325
+ *
5326
+ * @param requireVideo - whenever video track is required
5327
+ * @param requireAudio - whenever audio track is required
5328
+ * @returns {boolean} true if stream is ready for publishing
5329
+ */
3869
5330
  isStreamReady(requireVideo = true, requireAudio = true) {
3870
5331
  if (!this._stream) {
3871
5332
  return false;
@@ -3888,6 +5349,9 @@
3888
5349
  onIceCandidate(event) {
3889
5350
  if (event.candidate !== null) ;
3890
5351
  }
5352
+ //------------------------------------------------------------------------//
5353
+ // SETS & GETS
5354
+ //------------------------------------------------------------------------//
3891
5355
  createStatusConnection() {
3892
5356
  var _a, _b, _c;
3893
5357
  const serverItem = (_c = (_b = (_a = this._main) === null || _a === void 0 ? void 0 : _a.getNetworkController()) === null || _b === void 0 ? void 0 : _b.getConnection()) === null || _c === void 0 ? void 0 : _c.getCurrentServer();
@@ -3922,6 +5386,7 @@
3922
5386
  getPublishTime() {
3923
5387
  return this._publishState == exports.PublishState.PUBLISHED ? this._publishTime : 0;
3924
5388
  }
5389
+ // DEVICE STATE
3925
5390
  setInputDeviceState(newState) {
3926
5391
  if (this._inputDeviceState == newState) return;
3927
5392
  this._inputDeviceState = newState;
@@ -3935,6 +5400,7 @@
3935
5400
  getInputDeviceState() {
3936
5401
  return this._inputDeviceState;
3937
5402
  }
5403
+ // CAMERA STATE
3938
5404
  setCameraState(newState) {
3939
5405
  if (this._cameraState == newState) return;
3940
5406
  this._cameraState = newState;
@@ -3944,9 +5410,10 @@
3944
5410
  selectedCamera: this._selectedCamera
3945
5411
  });
3946
5412
  }
3947
- getCamerState() {
5413
+ getCameraState() {
3948
5414
  return this._cameraState;
3949
5415
  }
5416
+ // MICROPHONE STATE
3950
5417
  setMicrophoneState(newState) {
3951
5418
  if (this._microphoneState == newState) return;
3952
5419
  this._microphoneState = newState;
@@ -3968,6 +5435,13 @@
3968
5435
  getPublishState() {
3969
5436
  return this._publishState;
3970
5437
  }
5438
+ //------------------------------------------------------------------------//
5439
+ // DESTROY & DELETE
5440
+ //------------------------------------------------------------------------//
5441
+ /**
5442
+ * Method used to stop camera from streaming
5443
+ * @private
5444
+ */
3971
5445
  stopCameraStream() {
3972
5446
  var _a, _b;
3973
5447
  if (this._stream) {
@@ -3980,66 +5454,112 @@
3980
5454
  this._stream = null;
3981
5455
  }
3982
5456
  }
5457
+ /**
5458
+ * Method stops streaming for all streams
5459
+ * @private
5460
+ */
3983
5461
  forceStopAllStreams() {
3984
5462
  var _a, _b;
3985
- if (this._peerConnection) {
5463
+ // 1. First, detach the sound meter
5464
+ if (this._soundMeter) {
5465
+ this._soundMeter.detach();
5466
+ }
5467
+ // 2. Stop the main stream if it exists
5468
+ if (this._stream) {
3986
5469
  try {
3987
- const senders = this._peerConnection.getSenders();
3988
- senders.forEach(sender => {
3989
- var _a;
5470
+ const tracks = this._stream.getTracks();
5471
+ this._logger.info(this, `Stopping ${tracks.length} tracks from main stream`);
5472
+ tracks.forEach(track => {
3990
5473
  try {
3991
- if (sender.track) {
3992
- sender.track.enabled = false;
3993
- sender.track.stop();
3994
- (_a = this._peerConnection) === null || _a === void 0 ? void 0 : _a.removeTrack(sender);
3995
- }
5474
+ track.enabled = false;
5475
+ track.stop();
5476
+ this._logger.info(this, `Stopped ${track.kind} track: ${track.id}`);
3996
5477
  } catch (e) {
3997
- console.error('Error stopping sender track:', e);
5478
+ this._logger.error(this, `Error stopping ${track.kind} track: ${e}`);
3998
5479
  }
3999
5480
  });
4000
- this._peerConnection.close();
4001
- this._peerConnection = null;
5481
+ this._stream = null;
4002
5482
  } catch (e) {
4003
- console.error('Error closing peer connection:', e);
5483
+ this._logger.error(this, 'Error stopping main stream');
4004
5484
  }
4005
5485
  }
5486
+ // 3. Clean up video element
4006
5487
  try {
4007
5488
  const videoElement = (_b = (_a = this._main.getStageController()) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getVideoElement();
4008
5489
  if (videoElement && videoElement.srcObject instanceof MediaStream) {
4009
5490
  const videoTracks = videoElement.srcObject.getTracks();
4010
- videoTracks.forEach(track => {
4011
- try {
4012
- track.enabled = false;
4013
- track.stop();
4014
- } catch (e) {
4015
- console.error('Error stopping video element track:', e);
4016
- }
4017
- });
5491
+ if (videoTracks.length > 0) {
5492
+ this._logger.info(this, `Stopping ${videoTracks.length} tracks from video element`);
5493
+ videoTracks.forEach(track => {
5494
+ try {
5495
+ track.enabled = false;
5496
+ track.stop();
5497
+ } catch (e) {
5498
+ this._logger.error(this, `Error stopping video element track: ${e}`);
5499
+ }
5500
+ });
5501
+ }
4018
5502
  videoElement.srcObject = null;
4019
5503
  videoElement.removeAttribute('src');
4020
5504
  videoElement.load();
4021
5505
  }
4022
5506
  } catch (e) {
4023
- console.error('Error cleaning video element:', e);
5507
+ this._logger.error(this, 'Error cleaning video element');
4024
5508
  }
4025
- if (this._stream) {
5509
+ // 4. Handle RTCPeerConnection last, with proper state checking
5510
+ if (this._peerConnection) {
4026
5511
  try {
4027
- const tracks = this._stream.getTracks();
4028
- tracks.forEach(track => {
4029
- try {
4030
- track.enabled = false;
4031
- track.stop();
4032
- } catch (e) {
4033
- console.error(`Error stopping ${track.kind} track:`, e);
4034
- }
4035
- });
4036
- this._stream = null;
5512
+ // Only try to remove tracks if the connection isn't already closed
5513
+ if (this._peerConnection.signalingState !== 'closed') {
5514
+ const senders = this._peerConnection.getSenders();
5515
+ this._logger.info(this, `Cleaning up ${senders.length} senders from peer connection`);
5516
+ senders.forEach(sender => {
5517
+ try {
5518
+ if (sender.track) {
5519
+ sender.track.enabled = false;
5520
+ sender.track.stop();
5521
+ // Only try to remove the track if connection is still open
5522
+ if (this._peerConnection && this._peerConnection.signalingState !== 'closed') {
5523
+ this._peerConnection.removeTrack(sender);
5524
+ }
5525
+ }
5526
+ } catch (e) {
5527
+ this._logger.error(this, `Error stopping sender track: ${e}`);
5528
+ }
5529
+ });
5530
+ }
5531
+ // Now close the peer connection
5532
+ this._peerConnection.close();
5533
+ this._peerConnection = null;
4037
5534
  } catch (e) {
4038
- console.error('Error stopping main stream:', e);
5535
+ this._logger.error(this, 'Error closing peer connection');
4039
5536
  }
4040
5537
  }
5538
+ // 5. Ensure we properly null out our device references
5539
+ this._selectedCamera = null;
5540
+ this._selectedMicrophone = null;
5541
+ // 6. Make a final check for any active tracks at the global level
5542
+ try {
5543
+ const allTracks = [];
5544
+ // Try to find any tracks that might still be active by querying devices
5545
+ navigator.mediaDevices.getUserMedia({
5546
+ audio: false,
5547
+ video: false
5548
+ }).then(() => {
5549
+ // This is just to trigger a device check
5550
+ this._logger.info(this, "Performed final device check");
5551
+ }).catch(() => {
5552
+ // Ignore errors from this check
5553
+ });
5554
+ } catch (e) {
5555
+ // Ignore errors from final check
5556
+ }
4041
5557
  }
5558
+ /**
5559
+ * Stops all streaming operations and cleans up resources
5560
+ */
4042
5561
  stop() {
5562
+ // Stop status connection and clear timer
4043
5563
  if (this._statusConnection) {
4044
5564
  this._statusConnection.destroy();
4045
5565
  this._statusConnection = null;
@@ -4049,12 +5569,16 @@
4049
5569
  this._statusTimer = null;
4050
5570
  }
4051
5571
  this._main.getConfigManager().getStreamData().streamKey = null;
5572
+ // Close WebRTC connection
4052
5573
  this.closeWebRTCConnection();
5574
+ // Stop all media streams
4053
5575
  this.stopCameraStream();
5576
+ // Reset states
4054
5577
  this.setPublishState(exports.PublishState.STOPPED);
4055
5578
  this.setInputDeviceState(exports.InputDevicesState.STOPPED);
4056
5579
  this.setCameraState(exports.DeviceState.STOPPED);
4057
5580
  this.setMicrophoneState(exports.DeviceState.STOPPED);
5581
+ // Clear restart timer if exists
4058
5582
  if (this._restartTimer) {
4059
5583
  clearInterval(this._restartTimer);
4060
5584
  this._restartTimer = null;
@@ -4062,19 +5586,26 @@
4062
5586
  this._restartTimerCount = 0;
4063
5587
  clearTimeout(this._publishTimer);
4064
5588
  }
5589
+ /**
5590
+ * Reinitializes the streaming setup
5591
+ */
4065
5592
  start() {
4066
5593
  var _a, _b, _c;
4067
5594
  return __awaiter(this, void 0, void 0, function* () {
4068
5595
  try {
5596
+ // Reset states
4069
5597
  this._publishState = exports.PublishState.NOT_INITIALIZED;
4070
5598
  this._inputDeviceState = exports.InputDevicesState.NOT_INITIALIZED;
4071
5599
  this._cameraState = exports.DeviceState.NOT_INITIALIZED;
4072
5600
  this._microphoneState = exports.DeviceState.NOT_INITIALIZED;
5601
+ // Reinitialize devices and stream
4073
5602
  yield this.initializeDevices();
4074
5603
  yield this.startCamera();
5604
+ // If autoConnect is enabled, initialize network
4075
5605
  if ((_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getSettingsData().autoConnect) {
4076
5606
  (_b = this._main.getNetworkController()) === null || _b === void 0 ? void 0 : _b.initialize();
4077
5607
  }
5608
+ // Reinitialize status connection if needed
4078
5609
  if ((_c = this._main.getConfigManager()) === null || _c === void 0 ? void 0 : _c.getStreamData().streamKey) {
4079
5610
  this.createStatusConnection();
4080
5611
  }
@@ -4085,38 +5616,76 @@
4085
5616
  }
4086
5617
  });
4087
5618
  }
5619
+ /**
5620
+ * Method used for destroying everything (one-time use)
5621
+ */
4088
5622
  destroy() {
5623
+ // Stop any ongoing timers and intervals first
5624
+ if (this._statusTimer != null) {
5625
+ clearInterval(this._statusTimer);
5626
+ this._statusTimer = null;
5627
+ }
5628
+ if (this._restartTimer != null) {
5629
+ clearInterval(this._restartTimer);
5630
+ this._restartTimer = null;
5631
+ }
5632
+ clearTimeout(this._publishTimer);
5633
+ // Remove orientation change listeners
4089
5634
  if (window.screen && window.screen.orientation) {
4090
5635
  window.screen.orientation.removeEventListener('change', this.handleOrientationChange);
4091
5636
  } else {
4092
5637
  window.removeEventListener('orientationchange', this.handleOrientationChange);
4093
5638
  }
4094
- this.forceStopAllStreams();
4095
- if (this._soundMeter) {
4096
- this._soundMeter.detach();
4097
- }
4098
- clearTimeout(this._publishTimer);
4099
- this._pendingMicrophoneState = null;
4100
- this._cameraList = new InputDeviceList();
4101
- this._microphoneList = new InputDeviceList();
4102
- this._permissionChecked = false;
4103
- this._isWindowActive = false;
4104
- this._isMicrophoneMuted = false;
5639
+ // Remove other event listeners
4105
5640
  try {
4106
5641
  this._main.removeEventListener("serverConnect", this.onServerConnect);
4107
5642
  this._main.removeEventListener("serverDisconnect", this.onServerDisconnect);
5643
+ this._main.removeEventListener("streamKeyInUse", this.onStreamKeyTaken);
5644
+ this._main.removeEventListener("statusServerConnect", this.onStatusServerConnect);
5645
+ this._main.removeEventListener("statusServerDisconnect", this.onStatusServerDisconnect);
5646
+ this._main.removeEventListener("streamStatusUpdate", this.onStreamStatsUpdate);
5647
+ this._main.removeEventListener("deviceStateChange", this.onDeviceStateChange);
4108
5648
  document.removeEventListener("visibilitychange", this.visibilityChange);
4109
5649
  window.removeEventListener("blur", this.onWindowBlur);
4110
5650
  window.removeEventListener("focus", this.onWindowFocus);
4111
5651
  } catch (e) {
4112
- console.error('Error removing event listeners:', e);
5652
+ this._logger.error(this, 'Error removing event listeners');
4113
5653
  }
5654
+ // Make sure we destroy the status connection
5655
+ if (this._statusConnection) {
5656
+ this._statusConnection.destroy();
5657
+ this._statusConnection = null;
5658
+ }
5659
+ // Stop all media streams and clean up the WebRTC connection
5660
+ this.forceStopAllStreams();
5661
+ // Reset all state variables
5662
+ this._pendingMicrophoneState = null;
5663
+ this._cameraList = new InputDeviceList();
5664
+ this._microphoneList = new InputDeviceList();
5665
+ this._permissionChecked = false;
5666
+ this._isWindowActive = false;
5667
+ this._isMicrophoneMuted = false;
4114
5668
  this._publishState = exports.PublishState.NOT_INITIALIZED;
4115
- if (this._restartTimer != null) clearInterval(this._restartTimer);
5669
+ this._inputDeviceState = exports.InputDevicesState.NOT_INITIALIZED;
5670
+ this._cameraState = exports.DeviceState.NOT_INITIALIZED;
5671
+ this._microphoneState = exports.DeviceState.NOT_INITIALIZED;
5672
+ // Log completion
5673
+ this._logger.success(this, "StreamerController successfully destroyed and all resources released");
4116
5674
  }
4117
5675
  }
4118
5676
 
5677
+ /**
5678
+ * Main class for communicating with Storm Streaming Server
5679
+ */
4119
5680
  class WowzaConnection extends AbstractSocket {
5681
+ //------------------------------------------------------------------------//
5682
+ // CONSTRUCTOR
5683
+ //------------------------------------------------------------------------//
5684
+ /**
5685
+ * Constructor
5686
+ * @param main
5687
+ * @constructor
5688
+ */
4120
5689
  constructor(main, networkController) {
4121
5690
  super();
4122
5691
  this._logger = main.getLogger();
@@ -4144,6 +5713,14 @@
4144
5713
  this.startConnection();
4145
5714
  } else this._logger.error(this, "Connection with the server could not be initialized!");
4146
5715
  }
5716
+ //------------------------------------------------------------------------//
5717
+ // BASE EVENTS
5718
+ //------------------------------------------------------------------------//
5719
+ /**
5720
+ * On server connection opened;
5721
+ * @param event
5722
+ * @protected
5723
+ */
4147
5724
  onSocketOpen(event) {
4148
5725
  this._logger.success(this, `Connection with the server has been established!`);
4149
5726
  this._main.dispatchEvent("serverConnect", {
@@ -4153,6 +5730,11 @@
4153
5730
  });
4154
5731
  this._isConnected = true;
4155
5732
  }
5733
+ /**
5734
+ * On server connection error
5735
+ * @param event
5736
+ * @protected
5737
+ */
4156
5738
  onSocketError(event) {
4157
5739
  this._isConnected = false;
4158
5740
  if (!this._disconnectedByUser) {
@@ -4169,6 +5751,11 @@
4169
5751
  }
4170
5752
  }
4171
5753
  }
5754
+ /**
5755
+ * On server connection closed
5756
+ * @param event
5757
+ * @protected
5758
+ */
4172
5759
  onSocketClose(event) {
4173
5760
  this._isConnected = false;
4174
5761
  if (!this._disconnectedByUser) {
@@ -4182,9 +5769,21 @@
4182
5769
  this.initiateReconnect();
4183
5770
  } else this._logger.warning(this, `Force disconnect from server!`);
4184
5771
  }
5772
+ /**
5773
+ * Method is called whenever a new message from socket arrives
5774
+ * @private
5775
+ */
4185
5776
  onSocketMessage(event) {
4186
5777
  this._networkController.onMessage(event);
4187
5778
  }
5779
+ //------------------------------------------------------------------------//
5780
+ // BASE METHODS
5781
+ //------------------------------------------------------------------------//
5782
+ /**
5783
+ * Creates new URL for WebSockets based on serverItem object
5784
+ * @param serverItem
5785
+ * @private
5786
+ */
4188
5787
  createURL(serverItem) {
4189
5788
  let url = "";
4190
5789
  url += serverItem.getIfSSL() ? "wss://" : "ws://";
@@ -4193,6 +5792,10 @@
4193
5792
  url += "/webrtc-session.json";
4194
5793
  return url;
4195
5794
  }
5795
+ /**
5796
+ * Initiates reconnection procedure
5797
+ * @private
5798
+ */
4196
5799
  initiateReconnect() {
4197
5800
  const shouldReconnect = this._main.getConfigManager().getSettingsData().getIfRestartOnError();
4198
5801
  const reconnectTime = this._main.getConfigManager().getSettingsData().getReconnectTime();
@@ -4216,6 +5819,11 @@
4216
5819
  }, reconnectTime * 1000);
4217
5820
  }
4218
5821
  }
5822
+ /**
5823
+ * Picks new server from this list of available ones
5824
+ * @param serverList
5825
+ * @private
5826
+ */
4219
5827
  pickServerFromList(serverList) {
4220
5828
  let server = null;
4221
5829
  for (let i = 0; i < serverList.length; i++) {
@@ -4236,23 +5844,52 @@
4236
5844
  this._currServer = server;
4237
5845
  }
4238
5846
  }
5847
+ /**
5848
+ * Returns true/false depending if a connection with a server is active
5849
+ */
4239
5850
  isConnectionActive() {
4240
5851
  return this._isConnected;
4241
5852
  }
4242
5853
  getCurrentServer() {
4243
5854
  return this._currServer;
4244
5855
  }
5856
+ /**
5857
+ * Destroys object and clean-ups everything that is needed
5858
+ */
4245
5859
  destroy() {
4246
5860
  super.destroy();
4247
5861
  if (this._reconnectTimer != null) clearTimeout(this._reconnectTimer);
4248
5862
  }
4249
5863
  }
4250
5864
 
5865
+ /**
5866
+ * Main class for managing socket connections (only one per instance) and parsing packets.
5867
+ */
4251
5868
  class NetworkController {
5869
+ //------------------------------------------------------------------------//
5870
+ // CONSTRUCTOR
5871
+ //------------------------------------------------------------------------//
5872
+ /**
5873
+ * Constructor
5874
+ * @param main reference to the main class
5875
+ */
4252
5876
  constructor(main) {
5877
+ /**
5878
+ * Current streamKey
5879
+ * @private
5880
+ */
4253
5881
  this._currentStreamKey = "none";
5882
+ /**
5883
+ * Last state
5884
+ * @private
5885
+ */
4254
5886
  this._lastState = "";
4255
- this.onServerConnect = () => {};
5887
+ //------------------------------------------------------------------------//
5888
+ // CONNECTION-RELATED METHODS
5889
+ //------------------------------------------------------------------------//
5890
+ this.onServerConnect = () => {
5891
+ // nothing
5892
+ };
4256
5893
  this.onServerDisconnect = () => {
4257
5894
  this._lastState = "none";
4258
5895
  };
@@ -4265,6 +5902,9 @@
4265
5902
  this._main.addEventListener("serverConnect", this.onServerConnect, false);
4266
5903
  this._main.addEventListener("serverDisconnect", this.onServerDisconnect, false);
4267
5904
  }
5905
+ //------------------------------------------------------------------------//
5906
+ // MAIN METHODS
5907
+ //------------------------------------------------------------------------//
4268
5908
  initialize() {
4269
5909
  if (this._connection != null) {
4270
5910
  if (this._connection.getConnectionState() == ConnectionState.CONNECTING || this._connection.getConnectionState() == ConnectionState.CONNECTED) {
@@ -4287,9 +5927,18 @@
4287
5927
  this._connection.sendData(message);
4288
5928
  }
4289
5929
  }
5930
+ //------------------------------------------------------------------------//
5931
+ // SETS & GETS
5932
+ //------------------------------------------------------------------------//
5933
+ /**
5934
+ * Returns current SocketConnection object
5935
+ */
4290
5936
  getConnection() {
4291
5937
  return this._connection;
4292
5938
  }
5939
+ /**
5940
+ * Returns streamKey that is currently being used
5941
+ */
4293
5942
  getCurrentStreamKey() {
4294
5943
  return this._currentStreamKey;
4295
5944
  }
@@ -4356,18 +6005,22 @@
4356
6005
  this._lastTimestamp = null;
4357
6006
  this._isTimemarkLine = false;
4358
6007
  this._borderWidth = 1;
6008
+ // Validate input arrays
4359
6009
  if (colors.length !== valueRanges.length + 1) {
4360
6010
  throw new Error(`Colors array must have exactly ${valueRanges.length + 1} elements (one more than valueRanges)`);
4361
6011
  }
6012
+ // Validate that valueRanges are in ascending order
4362
6013
  for (let i = 1; i < valueRanges.length; i++) {
4363
6014
  if (valueRanges[i] <= valueRanges[i - 1]) {
4364
6015
  throw new Error('ValueRanges must be in ascending order');
4365
6016
  }
4366
6017
  }
6018
+ // Validate colors format
4367
6019
  const hexColorRegex = /^#[0-9A-Fa-f]{6}$/;
4368
6020
  if (!colors.every(color => hexColorRegex.test(color))) {
4369
6021
  throw new Error('All colors must be in valid hex format (e.g., #FF0000)');
4370
6022
  }
6023
+ // Find container
4371
6024
  let container;
4372
6025
  if (typeof containerIdOrElement === 'string') {
4373
6026
  const foundElement = document.getElementById(containerIdOrElement);
@@ -4378,13 +6031,17 @@
4378
6031
  } else {
4379
6032
  container = containerIdOrElement;
4380
6033
  }
6034
+ // Get container dimensions
4381
6035
  const containerRect = container.getBoundingClientRect();
4382
6036
  this._width = Math.floor(containerRect.width);
4383
6037
  this._height = Math.floor(containerRect.height);
6038
+ // Calculate effective dimensions (accounting for border)
4384
6039
  this._effectiveWidth = this._width - this._borderWidth * 2;
4385
6040
  this._effectiveHeight = this._height - this._borderWidth * 2;
6041
+ // Create canvas element
4386
6042
  this._canvas = document.createElement('canvas');
4387
6043
  container.appendChild(this._canvas);
6044
+ // Get context
4388
6045
  const context = this._canvas.getContext('2d', {
4389
6046
  willReadFrequently: true
4390
6047
  });
@@ -4392,13 +6049,16 @@
4392
6049
  throw new Error('Failed to get canvas context');
4393
6050
  }
4394
6051
  this._ctx = context;
4395
- this._valueRanges = [0, ...valueRanges];
6052
+ this._valueRanges = [0, ...valueRanges]; // Add 0 at the beginning
4396
6053
  this._colors = colors;
4397
6054
  this._lineWidth = lineWidth;
6055
+ // Set canvas size
4398
6056
  this._canvas.width = this._width;
4399
6057
  this._canvas.height = this._height;
6058
+ // Set canvas style to ensure it displays at the correct size
4400
6059
  this._canvas.style.width = `${this._width}px`;
4401
6060
  this._canvas.style.height = `${this._height}px`;
6061
+ // Initial background and border
4402
6062
  this.clear();
4403
6063
  }
4404
6064
  drawBorder() {
@@ -4407,12 +6067,15 @@
4407
6067
  this._ctx.strokeRect(this._borderWidth / 2, this._borderWidth / 2, this._width - this._borderWidth, this._height - this._borderWidth);
4408
6068
  }
4409
6069
  clear() {
6070
+ // Clear entire canvas
4410
6071
  this._ctx.clearRect(0, 0, this._width, this._height);
6072
+ // Fill background within border
4411
6073
  this._ctx.fillStyle = this._colors[0];
4412
6074
  this._ctx.fillRect(this._borderWidth, this._borderWidth, this._effectiveWidth, this._effectiveHeight);
6075
+ // Draw border
4413
6076
  this.drawBorder();
4414
6077
  this._isFullyDrawn = false;
4415
- this._currentX = this._borderWidth;
6078
+ this._currentX = this._borderWidth; // Start after left border
4416
6079
  this._lastTimestamp = null;
4417
6080
  this._isTimemarkLine = false;
4418
6081
  }
@@ -4425,6 +6088,7 @@
4425
6088
  };
4426
6089
  }
4427
6090
  }
6091
+ // If value is greater than the last range
4428
6092
  return {
4429
6093
  lowerIndex: this._valueRanges.length - 2,
4430
6094
  upperIndex: this._valueRanges.length - 1
@@ -4434,10 +6098,14 @@
4434
6098
  return (value - lowerBound) / (upperBound - lowerBound);
4435
6099
  }
4436
6100
  shiftCanvasLeft(shiftAmount) {
6101
+ // Get the content within the borders
4437
6102
  const imageData = this._ctx.getImageData(this._borderWidth + shiftAmount, this._borderWidth, this._effectiveWidth - shiftAmount, this._effectiveHeight);
6103
+ // Clear the area within borders
4438
6104
  this._ctx.fillStyle = this._colors[0];
4439
6105
  this._ctx.fillRect(this._borderWidth, this._borderWidth, this._effectiveWidth, this._effectiveHeight);
6106
+ // Draw shifted content
4440
6107
  this._ctx.putImageData(imageData, this._borderWidth, this._borderWidth);
6108
+ // Redraw border as it might have been affected
4441
6109
  this.drawBorder();
4442
6110
  }
4443
6111
  applyOverlayEffect(baseColor, overlayOpacity = 0.5) {
@@ -4502,15 +6170,18 @@
4502
6170
  lowerColor = this.applyOverlayEffect(lowerColor, 0.3);
4503
6171
  upperColor = this.applyOverlayEffect(upperColor, 0.3);
4504
6172
  }
6173
+ // Calculate heights accounting for borders
4505
6174
  const upperHeight = Math.round(this._effectiveHeight * ratio);
4506
6175
  const lowerHeight = this._effectiveHeight - upperHeight;
6176
+ // Draw lower part
4507
6177
  this._ctx.fillStyle = lowerColor;
4508
6178
  this._ctx.fillRect(x, this._height - this._borderWidth - lowerHeight, width, lowerHeight);
6179
+ // Draw upper part
4509
6180
  this._ctx.fillStyle = upperColor;
4510
6181
  this._ctx.fillRect(x, this._borderWidth, width, upperHeight);
4511
6182
  }
4512
6183
  clearGraph() {
4513
- this._currentX = this._borderWidth;
6184
+ this._currentX = this._borderWidth; // Reset to after left border
4514
6185
  this._isFullyDrawn = false;
4515
6186
  this._lastTimestamp = null;
4516
6187
  this._isTimemarkLine = false;
@@ -4536,8 +6207,10 @@
4536
6207
  start() {
4537
6208
  if (this._graph != null) stop();
4538
6209
  const valueRanges = [1000000, 2000000, 3000000, 5000000, 6000000, 7000000];
4539
- const colors = ['#000000', '#0a3980', '#2793dd', '#3bc39c', '#c3df3e', '#f89539', '#f83f3f'];
4540
- this._graph = new GraphDrawer(this._object, valueRanges, colors);
6210
+ const colors = ['#000000', '#0a3980', '#2793dd', '#3bc39c', '#c3df3e', '#f89539', '#f83f3f' // MaxBuffer - End
6211
+ ];
6212
+
6213
+ this._graph = new GraphDrawer(this._object, valueRanges, colors, 5);
4541
6214
  this._main.addEventListener("streamStatusUpdate", this.onStreamStatsUpdate);
4542
6215
  return this;
4543
6216
  }
@@ -4562,8 +6235,10 @@
4562
6235
  start() {
4563
6236
  if (this._graph != null) stop();
4564
6237
  const valueRanges = [25, 26, 27, 28, 29, 30];
4565
- const colors = ['#000000', '#0a3980', '#2793dd', '#3bc39c', '#c3df3e', '#f89539', '#f83f3f'];
4566
- this._graph = new GraphDrawer(this._object, valueRanges, colors);
6238
+ const colors = ['#000000', '#0a3980', '#2793dd', '#3bc39c', '#c3df3e', '#f89539', '#f83f3f' // MaxBuffer - End
6239
+ ];
6240
+
6241
+ this._graph = new GraphDrawer(this._object, valueRanges, colors, 5);
4567
6242
  this._main.addEventListener("streamStatusUpdate", this.onStreamStatsUpdate);
4568
6243
  return this;
4569
6244
  }
@@ -4575,19 +6250,85 @@
4575
6250
  }
4576
6251
  }
4577
6252
 
6253
+ class MicrophoneGraph {
6254
+ constructor(main, container) {
6255
+ this.onStreamStatsUpdate = event => {
6256
+ var _a;
6257
+ (_a = this._graph) === null || _a === void 0 ? void 0 : _a.addEntry(event.high * 500);
6258
+ console.log(event.high * 200);
6259
+ };
6260
+ this._main = main;
6261
+ this._object = container;
6262
+ this._main.addGraph(this);
6263
+ }
6264
+ start() {
6265
+ if (this._graph != null) stop();
6266
+ const valueRanges = [0, 20, 40, 60, 80, 100];
6267
+ const colors = ['#000000', '#0a3980', '#2793dd', '#3bc39c', '#c3df3e', '#f89539', '#f83f3f' // MaxBuffer - End
6268
+ ];
6269
+
6270
+ this._graph = new GraphDrawer(this._object, valueRanges, colors, 2);
6271
+ this._main.addEventListener("soundMeter", this.onStreamStatsUpdate);
6272
+ return this;
6273
+ }
6274
+ stop() {
6275
+ this._main.removeEventListener("soundMeter", this.onStreamStatsUpdate);
6276
+ if (this._graph != null) this._graph.destroy();
6277
+ this._graph = null;
6278
+ return this;
6279
+ }
6280
+ }
6281
+
6282
+ /**
6283
+ * Main class of the player. The player itself has no GUI, but can be controlled via provided API.
6284
+ */
4578
6285
  class StormStreamer extends EventDispatcher {
6286
+ //------------------------------------------------------------------------//
6287
+ // CONSTRUCTOR
6288
+ //------------------------------------------------------------------------//
6289
+ /**
6290
+ * Constructor - creates a new StormStreamer instance
6291
+ *
6292
+ * @param streamConfig - Configuration object for the streamer
6293
+ * @param autoInitialize - Whether to automatically initialize the streamer after creation
6294
+ */
4579
6295
  constructor(streamConfig, autoInitialize = false) {
4580
6296
  super();
6297
+ /**
6298
+ * Indicates whether the streamer object is in development mode (provides more debug options)
6299
+ * @private
6300
+ */
4581
6301
  this.DEV_MODE = true;
4582
- this.STREAMER_VERSION = "0.9.2-beta.2";
4583
- this.COMPILE_DATE = "3/6/2025, 2:53:15 PM";
6302
+ /**
6303
+ * Version of this streamer in SemVer format (Major.Minor.Patch).
6304
+ * @private
6305
+ */
6306
+ this.STREAMER_VERSION = "0.9.3-beta.0";
6307
+ /**
6308
+ * Compile date for this streamer
6309
+ * @private
6310
+ */
6311
+ this.COMPILE_DATE = "3/8/2025, 11:51:35 PM";
6312
+ /**
6313
+ * Defines from which branch this streamer comes from e.g. "Main", "Experimental"
6314
+ * @private
6315
+ */
4584
6316
  this.STREAMER_BRANCH = "Experimental";
6317
+ /**
6318
+ * Defines number of streamer protocol that is required on server-side
6319
+ * @private
6320
+ */
4585
6321
  this.STREAMER_PROTOCOL_VERSION = 1;
6322
+ /**
6323
+ * Indicates whether streamer was initialized or not
6324
+ * @private
6325
+ */
4586
6326
  this._initialized = false;
4587
6327
  if (typeof window === 'undefined' || !window.document || !window.document.createElement) {
4588
6328
  console.error(`StormStreamer Creation Error - No "window" element in the provided context!`);
4589
6329
  return;
4590
6330
  }
6331
+ // WINDOW.StormStreamerArray
4591
6332
  if (this.DEV_MODE && !('StormStreamerArray' in window)) {
4592
6333
  window.StormStreamerArray = [];
4593
6334
  }
@@ -4597,13 +6338,17 @@
4597
6338
  this.setStreamConfig(streamConfig);
4598
6339
  if (autoInitialize) this.initialize();
4599
6340
  }
6341
+ /**
6342
+ * Initializes the streamer object. From this point, a connection to the server is established and authentication occurs.
6343
+ * It is recommended to add all event listeners before calling this method to ensure they can be properly captured.
6344
+ */
4600
6345
  initialize() {
4601
6346
  if (this._isRemoved) return;
4602
6347
  if (this._configManager == null) throw Error("Stream Config was not provided for this streamer! A properly configured object must be provided through the constructor or via the setConfig method before using the initialize() method.");
4603
- this._storageManager = new StorageManager(this);
4604
- this._stageController = new StageController(this);
4605
- this._networkController = new NetworkController(this);
4606
- this._streamerController = new StreamerController(this);
6348
+ this._storageManager = new StorageManager(this); // Storing user data
6349
+ this._stageController = new StageController(this); // Visual elements like VideoElement
6350
+ this._networkController = new NetworkController(this); // Networking and connection with a server
6351
+ this._streamerController = new StreamerController(this); // Video Playback
4607
6352
  this._statsController = new StatsController(this);
4608
6353
  this._graphs = [];
4609
6354
  this._initialized = true;
@@ -4611,8 +6356,16 @@
4611
6356
  ref: this
4612
6357
  });
4613
6358
  }
6359
+ /**
6360
+ * Sets stream config for the streamer (or overwrites an existing one).
6361
+ *
6362
+ * @param streamConfig - New configuration object for the streamer
6363
+ */
4614
6364
  setStreamConfig(streamConfig) {
4615
6365
  if (this._isRemoved) return;
6366
+ /**
6367
+ * In case the original streamConfig is modified elsewhere we have to create a separate copy and store it ourselves
6368
+ */
4616
6369
  const copiedStreamConfig = JSON.parse(JSON.stringify(streamConfig));
4617
6370
  if (this._configManager == null) {
4618
6371
  this._configManager = new ConfigManager(copiedStreamConfig);
@@ -4636,10 +6389,21 @@
4636
6389
  });
4637
6390
  }
4638
6391
  }
6392
+ //------------------------------------------------------------------------//
6393
+ // PLAYBACK / STREAMING
6394
+ //------------------------------------------------------------------------//
6395
+ /**
6396
+ * Returns true if this streamer instance is currently connected to a Storm Server/Cloud instance.
6397
+ *
6398
+ * @returns Boolean indicating connection status
6399
+ */
4639
6400
  isConnected() {
4640
6401
  var _a, _b;
4641
6402
  return (_b = (_a = this._networkController) === null || _a === void 0 ? void 0 : _a.getConnection().isConnectionActive()) !== null && _b !== void 0 ? _b : false;
4642
6403
  }
6404
+ /**
6405
+ * Mutes the streamer's video object. Audio output will be silenced.
6406
+ */
4643
6407
  mute() {
4644
6408
  if (this._stageController != null) {
4645
6409
  if (this._stageController.getScreenElement() != null) {
@@ -4649,6 +6413,9 @@
4649
6413
  }
4650
6414
  this._configManager.getSettingsData().getAudioData().muted = true;
4651
6415
  }
6416
+ /**
6417
+ * Unmutes the streamer's video object. Audio output will be restored.
6418
+ */
4652
6419
  unmute() {
4653
6420
  if (this._stageController != null) {
4654
6421
  if (this._stageController.getScreenElement() != null) {
@@ -4658,10 +6425,20 @@
4658
6425
  }
4659
6426
  this._configManager.getSettingsData().getAudioData().muted = false;
4660
6427
  }
6428
+ /**
6429
+ * Checks whether the streamer audio is currently muted.
6430
+ *
6431
+ * @returns Boolean indicating mute status
6432
+ */
4661
6433
  isMute() {
4662
6434
  var _a, _b, _c, _d;
4663
6435
  return (_d = (_c = (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getIfMuted()) !== null && _c !== void 0 ? _c : this._configManager.getSettingsData().getAudioData().muted) !== null && _d !== void 0 ? _d : false;
4664
6436
  }
6437
+ /**
6438
+ * Toggles between mute and unmute states. Returns the new mute state.
6439
+ *
6440
+ * @returns New mute state (true = muted, false = unmuted)
6441
+ */
4665
6442
  toggleMute() {
4666
6443
  const isMuted = this.isMute();
4667
6444
  if (isMuted) {
@@ -4671,6 +6448,12 @@
4671
6448
  }
4672
6449
  return !isMuted;
4673
6450
  }
6451
+ /**
6452
+ * Sets new volume for the streamer (0-100). Once the method is performed, the volumeChange event will be triggered.
6453
+ * If the video was muted prior to the volume change, it will be automatically unmuted.
6454
+ *
6455
+ * @param newVolume - Volume level (0-100)
6456
+ */
4674
6457
  setVolume(newVolume) {
4675
6458
  var _a, _b;
4676
6459
  if (((_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.setVolume(newVolume)) !== undefined) {
@@ -4678,96 +6461,217 @@
4678
6461
  }
4679
6462
  this._configManager.getSettingsData().getAudioData().startVolume = newVolume;
4680
6463
  }
6464
+ /**
6465
+ * Returns current streamer volume (0-100).
6466
+ *
6467
+ * @returns Current volume level
6468
+ */
4681
6469
  getVolume() {
4682
6470
  var _a, _b, _c;
4683
6471
  return (_c = (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getVolume()) !== null && _c !== void 0 ? _c : this._configManager.getSettingsData().getAudioData().startVolume;
4684
6472
  }
6473
+ /**
6474
+ * Returns the list of available camera devices.
6475
+ *
6476
+ * @returns Array of camera input devices
6477
+ */
4685
6478
  getCameraList() {
4686
6479
  var _a, _b;
4687
6480
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getCameraList()) !== null && _b !== void 0 ? _b : [];
4688
6481
  }
6482
+ /**
6483
+ * Returns the list of available microphone devices.
6484
+ *
6485
+ * @returns Array of microphone input devices
6486
+ */
4689
6487
  getMicrophoneList() {
4690
6488
  var _a, _b;
4691
6489
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getMicrophoneList()) !== null && _b !== void 0 ? _b : [];
4692
6490
  }
6491
+ /**
6492
+ * Sets the active camera device by ID.
6493
+ *
6494
+ * @param cameraID - ID of the camera device to use
6495
+ */
4693
6496
  setCamera(cameraID) {
4694
6497
  var _a;
4695
6498
  (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.selectCamera(cameraID);
4696
6499
  }
6500
+ /**
6501
+ * Sets the active microphone device by ID.
6502
+ *
6503
+ * @param microphoneID - ID of the microphone device to use
6504
+ */
4697
6505
  setMicrophone(microphoneID) {
4698
6506
  var _a;
4699
6507
  (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.selectMicrophone(microphoneID);
4700
6508
  }
6509
+ /**
6510
+ * Returns the currently active camera device.
6511
+ *
6512
+ * @returns Current camera device or null if none is active
6513
+ */
4701
6514
  getCurrentCamera() {
4702
6515
  return this._streamerController.getCurrentCamera();
4703
6516
  }
6517
+ /**
6518
+ * Returns the currently active microphone device.
6519
+ *
6520
+ * @returns Current microphone device or null if none is active
6521
+ */
4704
6522
  getCurrentMicrophone() {
4705
6523
  return this._streamerController.getCurrentMicrophone();
4706
6524
  }
6525
+ /**
6526
+ * Mutes or unmutes the microphone.
6527
+ *
6528
+ * @param microphoneState - True to mute, false to unmute
6529
+ */
4707
6530
  muteMicrophone(microphoneState) {
4708
6531
  var _a;
4709
6532
  (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.muteMicrophone(microphoneState);
4710
6533
  }
6534
+ /**
6535
+ * Checks if the microphone is currently muted.
6536
+ *
6537
+ * @returns Boolean indicating if microphone is muted
6538
+ */
4711
6539
  isMicrophoneMuted() {
4712
6540
  var _a, _b;
4713
6541
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.isMicrophoneMuted()) !== null && _b !== void 0 ? _b : false;
4714
6542
  }
6543
+ /**
6544
+ * Returns the current publishing state of the streamer.
6545
+ *
6546
+ * @returns Current publishing state
6547
+ */
4715
6548
  getPublishState() {
4716
6549
  var _a, _b;
4717
6550
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getPublishState()) !== null && _b !== void 0 ? _b : exports.PublishState.NOT_INITIALIZED;
4718
6551
  }
6552
+ /**
6553
+ * Returns the total time the stream has been publishing in milliseconds.
6554
+ *
6555
+ * @returns Publishing time in milliseconds
6556
+ */
4719
6557
  getPublishTime() {
4720
6558
  var _a, _b;
4721
6559
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getPublishTime()) !== null && _b !== void 0 ? _b : 0;
4722
6560
  }
6561
+ /**
6562
+ * Starts publishing a stream with the given stream key.
6563
+ *
6564
+ * @param streamKey - Key identifying the stream to publish
6565
+ * @returns Boolean indicating if publishing was successfully initiated
6566
+ */
4723
6567
  publish(streamKey) {
4724
6568
  var _a, _b;
4725
6569
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.publish(streamKey)) !== null && _b !== void 0 ? _b : false;
4726
6570
  }
6571
+ /**
6572
+ * Stops publishing the current stream.
6573
+ */
6574
+ unpublish() {
6575
+ var _a;
6576
+ (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.unpublish();
6577
+ }
6578
+ /**
6579
+ * Returns the current state of input devices (camera and microphone).
6580
+ *
6581
+ * @returns Current state of input devices
6582
+ */
4727
6583
  getInputDevicesState() {
4728
6584
  var _a, _b;
4729
6585
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getInputDeviceState()) !== null && _b !== void 0 ? _b : exports.InputDevicesState.NOT_INITIALIZED;
4730
6586
  }
4731
- getCamerState() {
6587
+ /**
6588
+ * Returns the current state of the camera device.
6589
+ *
6590
+ * @returns Current camera device state
6591
+ */
6592
+ getCameraState() {
4732
6593
  var _a, _b;
4733
- return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getCamerState()) !== null && _b !== void 0 ? _b : exports.DeviceState.NOT_INITIALIZED;
6594
+ return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getCameraState()) !== null && _b !== void 0 ? _b : exports.DeviceState.NOT_INITIALIZED;
4734
6595
  }
6596
+ /**
6597
+ * Returns the current state of the microphone device.
6598
+ *
6599
+ * @returns Current microphone device state
6600
+ */
4735
6601
  getMicrophoneState() {
4736
6602
  var _a, _b;
4737
6603
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.getMicrophoneState()) !== null && _b !== void 0 ? _b : exports.DeviceState.NOT_INITIALIZED;
4738
6604
  }
6605
+ /**
6606
+ * Clears saved device preferences from storage.
6607
+ */
4739
6608
  clearSavedDevices() {
4740
6609
  var _a;
4741
6610
  return (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.clearSavedDevices();
4742
6611
  }
6612
+ /**
6613
+ * Randomizes saved device preferences (for testing purposes).
6614
+ */
4743
6615
  messSavedDevices() {
4744
6616
  var _a;
4745
6617
  return (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.messSavedDevices();
4746
6618
  }
6619
+ /**
6620
+ * Checks if the stream is ready for publishing.
6621
+ *
6622
+ * @returns Boolean indicating if stream is ready
6623
+ */
4747
6624
  isStreamReady() {
4748
6625
  var _a, _b;
4749
6626
  return (_b = (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.isStreamReady()) !== null && _b !== void 0 ? _b : false;
4750
6627
  }
4751
- unpublish() {
4752
- var _a;
4753
- (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.unpublish();
4754
- }
6628
+ //------------------------------------------------------------------------//
6629
+ // CONTAINER
6630
+ //------------------------------------------------------------------------//
6631
+ /**
6632
+ * Attaches the streamer to a new parent container using either a container ID (string) or a reference to an HTMLElement.
6633
+ * If the instance is already attached, it will be moved to a new parent.
6634
+ *
6635
+ * @param container - Container ID (string) or HTMLElement reference
6636
+ * @returns Boolean indicating if attachment was successful
6637
+ */
4755
6638
  attachToContainer(container) {
4756
6639
  var _a, _b;
4757
6640
  let result = false;
4758
6641
  if (this._initialized) return (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.attachToParent(container)) !== null && _b !== void 0 ? _b : false;
4759
6642
  return result;
4760
6643
  }
6644
+ /**
6645
+ * Detaches the streamer from the current parent element, if possible.
6646
+ *
6647
+ * @returns Boolean indicating if detachment was successful
6648
+ */
4761
6649
  detachFromContainer() {
4762
6650
  var _a, _b;
4763
6651
  let result = false;
4764
6652
  if (this._initialized) return (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.detachFromParent()) !== null && _b !== void 0 ? _b : false;
4765
6653
  return result;
4766
6654
  }
6655
+ /**
6656
+ * Returns the current parent element of the streamer, or null if none exists.
6657
+ *
6658
+ * @returns Current container element or null
6659
+ */
4767
6660
  getContainer() {
4768
6661
  var _a, _b;
4769
6662
  return (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.getParentElement()) !== null && _b !== void 0 ? _b : null;
4770
6663
  }
6664
+ //------------------------------------------------------------------------//
6665
+ // SIZE & RESIZE
6666
+ //------------------------------------------------------------------------//
6667
+ /**
6668
+ * Sets a new width and height for the streamer. The values can be given as a number (in which case they are
6669
+ * treated as the number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%",
6670
+ * where the number is treated as a percentage of the parent container's value.
6671
+ *
6672
+ * @param width - Can be provided as number or a string with "%" or "px" suffix
6673
+ * @param height - Can be provided as number or a string with "%" or "px" suffix
6674
+ */
4771
6675
  setSize(width, height) {
4772
6676
  if (this._initialized) this._stageController.setSize(width, height);else {
4773
6677
  const parsedWidth = NumberUtilities.parseValue(width);
@@ -4778,6 +6682,13 @@
4778
6682
  this._configManager.getSettingsData().getVideoData().videoHeightInPixels = parsedHeight.isPixels;
4779
6683
  }
4780
6684
  }
6685
+ /**
6686
+ * Sets a new width for the streamer. The value can be given as a number (in which case it is treated as the
6687
+ * number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%", where the
6688
+ * number is treated as a percentage of the parent container's value.
6689
+ *
6690
+ * @param width - Can be provided as number or a string with "%" or "px" suffix
6691
+ */
4781
6692
  setWidth(width) {
4782
6693
  if (this._initialized) this._stageController.setWidth(width);else {
4783
6694
  const parsedWidth = NumberUtilities.parseValue(width);
@@ -4785,6 +6696,13 @@
4785
6696
  this._configManager.getSettingsData().getVideoData().videoWidthInPixels = parsedWidth.isPixels;
4786
6697
  }
4787
6698
  }
6699
+ /**
6700
+ * Sets a new height for the streamer. The value can be given as a number (in which case it is treated as the
6701
+ * number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%", where the
6702
+ * number is treated as a percentage of the parent container's value.
6703
+ *
6704
+ * @param height - Can be provided as number or a string with "%" or "px" suffix
6705
+ */
4788
6706
  setHeight(height) {
4789
6707
  if (this._initialized) this._stageController.setHeight(height);else {
4790
6708
  const parsedHeight = NumberUtilities.parseValue(height);
@@ -4792,18 +6710,33 @@
4792
6710
  this._configManager.getSettingsData().getVideoData().videoHeightInPixels = parsedHeight.isPixels;
4793
6711
  }
4794
6712
  }
6713
+ /**
6714
+ * Returns current streamer width in pixels.
6715
+ *
6716
+ * @returns Current width in pixels
6717
+ */
4795
6718
  getWidth() {
4796
6719
  if (this._initialized) return this._stageController.getContainerWidth();else {
4797
6720
  if (this._configManager.getSettingsData().getVideoData().videoWidthInPixels) return this._configManager.getSettingsData().getVideoData().videoWidthValue;
4798
6721
  }
4799
6722
  return 0;
4800
6723
  }
6724
+ /**
6725
+ * Returns current streamer height in pixels.
6726
+ *
6727
+ * @returns Current height in pixels
6728
+ */
4801
6729
  getHeight() {
4802
6730
  if (this._initialized) return this._stageController.getContainerHeight();else {
4803
6731
  if (this._configManager.getSettingsData().getVideoData().videoHeightInPixels) return this._configManager.getSettingsData().getVideoData().videoHeightValue;
4804
6732
  }
4805
6733
  return 0;
4806
6734
  }
6735
+ /**
6736
+ * Changes the streamer scaling mode. Available modes include fill, letterbox, original, and crop.
6737
+ *
6738
+ * @param newMode - New scaling mode name (fill, letterbox, original, crop)
6739
+ */
4807
6740
  setScalingMode(newMode) {
4808
6741
  if (this._stageController) {
4809
6742
  this._stageController.setScalingMode(newMode);
@@ -4811,6 +6744,11 @@
4811
6744
  this._configManager.getSettingsData().getVideoData().scalingMode = newMode;
4812
6745
  }
4813
6746
  }
6747
+ /**
6748
+ * Returns the current streamer scaling mode.
6749
+ *
6750
+ * @returns Current scaling mode
6751
+ */
4814
6752
  getScalingMode() {
4815
6753
  if (this._stageController) {
4816
6754
  return this._stageController.getScalingMode();
@@ -4818,11 +6756,20 @@
4818
6756
  return this._configManager.getSettingsData().getVideoData().scalingMode;
4819
6757
  }
4820
6758
  }
6759
+ /**
6760
+ * Forces the streamer to recalculate its size based on parent internal dimensions.
6761
+ */
4821
6762
  updateToSize() {
4822
6763
  if (this._initialized) {
4823
6764
  this._stageController.handleResize();
4824
6765
  }
4825
6766
  }
6767
+ /**
6768
+ * Returns a promise that resolves with a screenshot of the video element as a blob, or null if taking the
6769
+ * screenshot was not possible.
6770
+ *
6771
+ * @returns Promise resolving to a Blob containing the screenshot or null
6772
+ */
4826
6773
  makeScreenshot() {
4827
6774
  let canvas = document.createElement('canvas');
4828
6775
  let context = canvas.getContext('2d');
@@ -4844,15 +6791,53 @@
4844
6791
  }
4845
6792
  });
4846
6793
  }
6794
+ //------------------------------------------------------------------------//
6795
+ // GRAPHS
6796
+ //------------------------------------------------------------------------//
6797
+ /**
6798
+ * Creates a FPS performance graph in the specified location (container ID or reference). The graph is a
6799
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
6800
+ * of the graph depend on the dimensions of the specified container.
6801
+ *
6802
+ * @param container - Element ID or reference to HTMLElement
6803
+ * @returns FPS graph instance
6804
+ */
4847
6805
  createFPSGraph(container) {
4848
6806
  return new FPSGraph(this, container);
4849
6807
  }
6808
+ /**
6809
+ * Creates a bitrate performance graph in the specified location (container ID or reference). The graph is a
6810
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
6811
+ * of the graph depend on the dimensions of the specified container.
6812
+ *
6813
+ * @param container - Element ID or reference to HTMLElement
6814
+ * @returns Bitrate graph instance
6815
+ */
4850
6816
  createBitrateGraph(container) {
4851
6817
  return new BitrateGraph(this, container);
4852
6818
  }
6819
+ /**
6820
+ * Creates a microphone graph in the specified location (container ID or reference). The graph is a
6821
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
6822
+ * of the graph depend on the dimensions of the specified container.
6823
+ *
6824
+ * @param container - Element ID or reference to HTMLElement
6825
+ * @returns Bitrate graph instance
6826
+ */
6827
+ createMicrophoneGraph(container) {
6828
+ return new MicrophoneGraph(this, container);
6829
+ }
6830
+ /**
6831
+ * Adds new graph to the internal collection of active graphs.
6832
+ *
6833
+ * @param newGraph - Graph instance to add
6834
+ */
4853
6835
  addGraph(newGraph) {
4854
6836
  if (this._graphs != null) this._graphs.push(newGraph);
4855
6837
  }
6838
+ /**
6839
+ * Stops all active performance graphs.
6840
+ */
4856
6841
  stopAllGraphs() {
4857
6842
  if (this._graphs != null && this._graphs.length > 0) {
4858
6843
  for (let i = 0; i < this._graphs.length; i++) {
@@ -4860,82 +6845,198 @@
4860
6845
  }
4861
6846
  }
4862
6847
  }
6848
+ //------------------------------------------------------------------------//
6849
+ // FULLSCREEN
6850
+ //------------------------------------------------------------------------//
6851
+ /**
6852
+ * Enters fullscreen mode for the streamer container.
6853
+ */
4863
6854
  enterFullScreen() {
4864
6855
  if (this._initialized && this._stageController) this._stageController.enterFullScreen();
4865
6856
  }
6857
+ /**
6858
+ * Exits fullscreen mode.
6859
+ */
4866
6860
  exitFullScreen() {
4867
6861
  if (this._initialized && this._stageController) this._stageController.exitFullScreen();
4868
6862
  }
6863
+ /**
6864
+ * Returns true if the streamer is currently in fullscreen mode.
6865
+ *
6866
+ * @returns Boolean indicating fullscreen status
6867
+ */
4869
6868
  isFullScreenMode() {
4870
6869
  if (this._initialized && this._stageController) return this._stageController.isFullScreenMode();
4871
6870
  return false;
4872
6871
  }
6872
+ //------------------------------------------------------------------------//
6873
+ // SIMPLE GETS & SETS
6874
+ //------------------------------------------------------------------------//
6875
+ /**
6876
+ * Returns the current stream key or null if none is set.
6877
+ *
6878
+ * @returns Current stream key or null
6879
+ */
4873
6880
  getStreamKey() {
4874
6881
  var _a, _b, _c;
4875
6882
  return (_c = (_b = (_a = this.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData()) === null || _b === void 0 ? void 0 : _b.streamKey) !== null && _c !== void 0 ? _c : null;
4876
6883
  }
6884
+ /**
6885
+ * Returns the Stats Controller instance which contains statistical data about streaming performance.
6886
+ *
6887
+ * @returns Stats controller instance or null
6888
+ */
4877
6889
  getStatsController() {
4878
6890
  return this._statsController;
4879
6891
  }
6892
+ /**
6893
+ * Returns the unique ID of this streamer instance. Each subsequent instance has a higher number.
6894
+ *
6895
+ * @returns Streamer instance ID
6896
+ */
4880
6897
  getStreamerID() {
4881
6898
  return this._streamerID;
4882
6899
  }
6900
+ /**
6901
+ * Returns the logger instance used by this streamer.
6902
+ *
6903
+ * @returns Logger instance
6904
+ */
4883
6905
  getLogger() {
4884
6906
  return this._logger;
4885
6907
  }
6908
+ /**
6909
+ * Returns the configuration manager for this streamer.
6910
+ *
6911
+ * @returns Config manager instance or null
6912
+ */
4886
6913
  getConfigManager() {
4887
6914
  return this._configManager;
4888
6915
  }
6916
+ /**
6917
+ * Returns the network controller which manages all server communication.
6918
+ *
6919
+ * @returns Network controller instance or null
6920
+ */
4889
6921
  getNetworkController() {
4890
6922
  return this._networkController;
4891
6923
  }
6924
+ /**
6925
+ * Returns the streamer controller which manages media stream operations.
6926
+ *
6927
+ * @returns Streamer controller instance or null
6928
+ */
4892
6929
  getStreamerController() {
4893
6930
  return this._streamerController;
4894
6931
  }
6932
+ /**
6933
+ * Returns the stage controller which manages visual presentation.
6934
+ *
6935
+ * @returns Stage controller instance or null
6936
+ */
4895
6937
  getStageController() {
4896
6938
  return this._stageController;
4897
6939
  }
6940
+ /**
6941
+ * Returns the storage manager which handles persistent data storage.
6942
+ *
6943
+ * @returns Storage manager instance or null
6944
+ */
6945
+ getStorageManager() {
6946
+ return this._storageManager;
6947
+ }
6948
+ /**
6949
+ * Returns the HTML video element used by this streamer instance.
6950
+ *
6951
+ * @returns Video element or null
6952
+ */
4898
6953
  getVideoElement() {
4899
6954
  var _a, _b, _c;
4900
6955
  return (_c = (_b = (_a = this._stageController) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getVideoElement()) !== null && _c !== void 0 ? _c : null;
4901
6956
  }
6957
+ /**
6958
+ * Returns true if this streamer instance has already been initialized.
6959
+ *
6960
+ * @returns Boolean indicating initialization status
6961
+ */
4902
6962
  isInitialized() {
4903
6963
  return this._initialized;
4904
6964
  }
6965
+ /**
6966
+ * Returns the version of this streamer instance. The version is returned in the SemVer format (Major.Minor.Patch).
6967
+ *
6968
+ * @returns Streamer version string
6969
+ */
4905
6970
  getVersion() {
4906
6971
  return this.STREAMER_VERSION;
4907
6972
  }
6973
+ /**
6974
+ * Returns the development branch of this streamer (e.g., main, experimental).
6975
+ *
6976
+ * @returns Branch name string
6977
+ */
4908
6978
  getBranch() {
4909
6979
  return this.STREAMER_BRANCH;
4910
6980
  }
4911
- getStorageManager() {
4912
- return this._storageManager;
4913
- }
6981
+ //------------------------------------------------------------------------//
6982
+ // EVENT
6983
+ //------------------------------------------------------------------------//
6984
+ /**
6985
+ * Dispatches an event with the specified name and data.
6986
+ *
6987
+ * @param eventName - Name of the event to dispatch
6988
+ * @param event - Object containing event data
6989
+ */
4914
6990
  dispatchEvent(eventName, event) {
4915
6991
  super.dispatchEvent(eventName, event);
4916
6992
  }
6993
+ //------------------------------------------------------------------------//
6994
+ // CLEAN UP
6995
+ //------------------------------------------------------------------------//
6996
+ /**
6997
+ * Starts the streaming process.
6998
+ *
6999
+ * @returns Promise that resolves when streaming has started
7000
+ */
4917
7001
  start() {
4918
7002
  var _a;
4919
7003
  return __awaiter(this, void 0, void 0, function* () {
4920
7004
  return (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.start();
4921
7005
  });
4922
7006
  }
7007
+ /**
7008
+ * Stops the streaming process.
7009
+ */
4923
7010
  stop() {
4924
7011
  var _a;
4925
7012
  return (_a = this._streamerController) === null || _a === void 0 ? void 0 : _a.stop();
4926
7013
  }
7014
+ //------------------------------------------------------------------------//
7015
+ // CLEAN UP
7016
+ //------------------------------------------------------------------------//
7017
+ /**
7018
+ * Destroys this instance of StormStreamer, releasing all resources and disconnecting from the server.
7019
+ * After calling this method, the instance should not be used anymore.
7020
+ */
4927
7021
  destroy() {
4928
7022
  var _a, _b, _c, _d;
4929
7023
  this._logger.warning(this, "Destroying streamer instance, bye, bye!");
4930
7024
  if (this.DEV_MODE && 'StormStreamerArray' in window) window.StormStreamerArray[this._streamerID] = null;
7025
+ // part1
4931
7026
  this._initialized = false;
4932
7027
  this._isRemoved = true;
7028
+ // part3
4933
7029
  (_b = (_a = this._networkController) === null || _a === void 0 ? void 0 : _a.getConnection()) === null || _b === void 0 ? void 0 : _b.destroy();
4934
7030
  (_c = this._streamerController) === null || _c === void 0 ? void 0 : _c.destroy();
4935
7031
  (_d = this._stageController) === null || _d === void 0 ? void 0 : _d.destroy();
7032
+ // part2
4936
7033
  this.removeAllEventListeners();
4937
7034
  }
4938
7035
  }
7036
+ /**
7037
+ * Next ID for the streamer instance. Each subsequent instance has a higher number.
7038
+ * @private
7039
+ */
4939
7040
  StormStreamer.NEXT_STREAMER_ID = 0;
4940
7041
 
4941
7042
  function create(config) {