@stormstreaming/stormstreamer 0.9.0-beta.2 → 0.9.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.0-beta.2
8
- * Version: 11/29/2024, 6:55:47 PM
7
+ * Version: 0.9.0-beta.4
8
+ * Version: 12/7/2024, 6:37:55 PM
9
9
  *
10
10
  * LEGAL NOTICE:
11
11
  * This software is subject to the terms and conditions defined in
@@ -1180,6 +1180,12 @@
1180
1180
  localStorage.setItem(this.prefix + name, value);
1181
1181
  }
1182
1182
  }
1183
+ removeField(name) {
1184
+ if (this.isEnabled) {
1185
+ if (this.LOG_ACTIVITY) this.logger.info(this, "Removing data: " + name);
1186
+ localStorage.removeItem(this.prefix + name);
1187
+ }
1188
+ }
1183
1189
  getField(name) {
1184
1190
  if (this.isEnabled == true) {
1185
1191
  const value = localStorage.getItem(this.prefix + name);
@@ -1626,6 +1632,13 @@
1626
1632
  this.dispatchVolumeEvent();
1627
1633
  };
1628
1634
  this._videoElement.onpause = () => {};
1635
+ this._videoElement.addEventListener('loadedmetadata', () => {
1636
+ this._main.dispatchEvent("metadata", {
1637
+ ref: this._main,
1638
+ videoWidth: this._videoElement.videoWidth,
1639
+ videoHeight: this._videoElement.videoHeight
1640
+ });
1641
+ });
1629
1642
  this._videoElement.ontimeupdate = function (event) {};
1630
1643
  this._videoElement.onended = event => {
1631
1644
  this._logger.info(this, "VideoElement :: onended");
@@ -1703,7 +1716,9 @@
1703
1716
  this._videoHeight = 0;
1704
1717
  this._scalingMode = ScalingType.FILL;
1705
1718
  this.isInFullScreenMode = false;
1719
+ this._isResizing = false;
1706
1720
  this._autoResizeEnabled = true;
1721
+ this._parentOriginalOverflow = '';
1707
1722
  this.onFullScreenChange = () => {
1708
1723
  if (document.fullscreenElement == null) {
1709
1724
  this.isInFullScreenMode = false;
@@ -1719,35 +1734,6 @@
1719
1734
  });
1720
1735
  }
1721
1736
  };
1722
- this.onResize = () => {
1723
- if (this._parentElement != null) {
1724
- const calcMethod = this._main.getConfigManager().getSettingsData().getVideoData().parentSizeCalculationMethod;
1725
- this._videoContainer.style.display = "none";
1726
- switch (calcMethod) {
1727
- case SizeCalculationType.CLIENT_DIMENSIONS:
1728
- this._tempContainerWidth = this._parentElement.clientWidth;
1729
- this._tempContainerHeight = this._parentElement.clientHeight;
1730
- break;
1731
- case SizeCalculationType.BOUNDING_BOX:
1732
- this._tempContainerWidth = this._parentElement.getBoundingClientRect().width;
1733
- this._tempContainerHeight = this._parentElement.getBoundingClientRect().height;
1734
- break;
1735
- case SizeCalculationType.FULL_BOX:
1736
- this._tempContainerWidth = DomUtilities.calculateDimensionsWithMargins(this._parentElement).width;
1737
- this._tempContainerHeight = DomUtilities.calculateDimensionsWithMargins(this._parentElement).height;
1738
- break;
1739
- }
1740
- this._logger.info(this, "onResize called: " + this._tempContainerWidth + "x" + this._tempContainerHeight + " (" + calcMethod + ")");
1741
- this.resizeVideoContainer();
1742
- this.scaleVideo();
1743
- this._videoContainer.style.display = "block";
1744
- this._main.dispatchEvent("resizeUpdate", {
1745
- ref: this._main,
1746
- width: this._tempContainerWidth,
1747
- height: this._tempContainerHeight
1748
- });
1749
- }
1750
- };
1751
1737
  this._main = main;
1752
1738
  this._logger = main.getLogger();
1753
1739
  this._logger.info(this, "Creating new StageController");
@@ -1767,14 +1753,14 @@
1767
1753
  const debounceValue = this._main.getConfigManager().getSettingsData().getVideoData().resizeDebounce;
1768
1754
  if (debounceValue > 0) {
1769
1755
  this._resizeObserver = new ResizeObserver(debounce$1(() => () => {
1770
- if (this._autoResizeEnabled) this.onResize();
1756
+ if (this._autoResizeEnabled) this.handleResize();
1771
1757
  }, debounceValue, {
1772
1758
  leading: false,
1773
1759
  trailing: true
1774
1760
  }));
1775
1761
  } else {
1776
1762
  this._resizeObserver = new ResizeObserver(() => {
1777
- if (this._autoResizeEnabled) this.onResize();
1763
+ if (this._autoResizeEnabled) this.handleResize();
1778
1764
  });
1779
1765
  }
1780
1766
  document.addEventListener('fullscreenchange', this.onFullScreenChange, false);
@@ -1782,6 +1768,11 @@
1782
1768
  document.addEventListener('mozfullscreenchange', this.onFullScreenChange, false);
1783
1769
  document.addEventListener('webkitendfullscreen', this.onFullScreenChange, false);
1784
1770
  this._screenElement.getVideoElement().addEventListener('webkitendfullscreen', this.onFullScreenChange, false);
1771
+ this._main.addEventListener("metadata", event => {
1772
+ this._videoWidth = event.videoWidth;
1773
+ this._videoHeight = event.videoHeight;
1774
+ this.handleResize();
1775
+ }, false);
1785
1776
  if (containerID) {
1786
1777
  this.attachToParent(containerID);
1787
1778
  } else this._logger.warning(this, `Could not create HTMLObject for the library - "containerID" was not provided`);
@@ -1789,11 +1780,9 @@
1789
1780
  attachToParent(container) {
1790
1781
  let result = false;
1791
1782
  let tempParentElement = null;
1792
- console.log("xxxxxxxxx", container);
1793
1783
  if (typeof container === "string") {
1794
1784
  this._logger.info(this, "Attaching container to ID: " + container);
1795
1785
  tempParentElement = document.getElementById(container);
1796
- console.log(">>", document.getElementById(container));
1797
1786
  } else if (container instanceof HTMLElement) {
1798
1787
  this._logger.info(this, "Attaching container to HTMLElement: " + container);
1799
1788
  tempParentElement = container;
@@ -1807,13 +1796,13 @@
1807
1796
  this._parentElement.appendChild(this._videoContainer);
1808
1797
  this._resizeObserver.observe(this._parentElement);
1809
1798
  this._parentElement.addEventListener("transitionend", () => {
1810
- this.onResize();
1799
+ this.handleResize();
1811
1800
  });
1812
1801
  this._main.dispatchEvent("containerChange", {
1813
1802
  ref: this._main,
1814
1803
  container: this._parentElement
1815
1804
  });
1816
- this.onResize();
1805
+ this.handleResize();
1817
1806
  result = true;
1818
1807
  } else {
1819
1808
  console.log("tempParentElement", tempParentElement);
@@ -1831,7 +1820,7 @@
1831
1820
  this._resizeObserver.unobserve(this._parentElement);
1832
1821
  this._resizeObserver.disconnect();
1833
1822
  }
1834
- if (this._autoResizeEnabled) this._parentElement.removeEventListener("transitionend", this.onResize);
1823
+ if (this._autoResizeEnabled) this._parentElement.removeEventListener("transitionend", this.handleResize);
1835
1824
  this._main.dispatchEvent("containerChange", {
1836
1825
  ref: this._main,
1837
1826
  container: null
@@ -1843,6 +1832,46 @@
1843
1832
  this._parentElement = null;
1844
1833
  return result;
1845
1834
  }
1835
+ handleResize() {
1836
+ if (!this._parentElement || this._isResizing) return;
1837
+ this._isResizing = true;
1838
+ this._parentOriginalOverflow = this._parentElement.style.overflow;
1839
+ this._parentElement.style.overflow = 'hidden';
1840
+ requestAnimationFrame(() => {
1841
+ this.calculateNewDimensions();
1842
+ this._parentElement.style.overflow = this._parentOriginalOverflow;
1843
+ this._isResizing = false;
1844
+ if (this._tempContainerWidth !== this._containerWidth || this._tempContainerHeight !== this._containerHeight) {
1845
+ this._main.dispatchEvent("resizeUpdate", {
1846
+ ref: this._main,
1847
+ width: this._tempContainerWidth,
1848
+ height: this._tempContainerHeight
1849
+ });
1850
+ }
1851
+ });
1852
+ }
1853
+ calculateNewDimensions() {
1854
+ const calcMethod = this._main.getConfigManager().getSettingsData().getVideoData().parentSizeCalculationMethod;
1855
+ switch (calcMethod) {
1856
+ case SizeCalculationType.CLIENT_DIMENSIONS:
1857
+ this._tempContainerWidth = this._parentElement.clientWidth;
1858
+ this._tempContainerHeight = this._parentElement.clientHeight;
1859
+ break;
1860
+ case SizeCalculationType.BOUNDING_BOX:
1861
+ const rect = this._parentElement.getBoundingClientRect();
1862
+ this._tempContainerWidth = rect.width;
1863
+ this._tempContainerHeight = rect.height;
1864
+ break;
1865
+ case SizeCalculationType.FULL_BOX:
1866
+ const dims = DomUtilities.calculateDimensionsWithMargins(this._parentElement);
1867
+ this._tempContainerWidth = dims.width;
1868
+ this._tempContainerHeight = dims.height;
1869
+ break;
1870
+ }
1871
+ this._logger.info(this, `New dimensions calculated: ${this._tempContainerWidth}x${this._tempContainerHeight} (${calcMethod})`);
1872
+ this.resizeVideoContainer();
1873
+ this.scaleVideo();
1874
+ }
1846
1875
  resizeVideoContainer() {
1847
1876
  const isWidthInPX = this._main.getConfigManager().getSettingsData().getVideoData().videoWidthInPixels;
1848
1877
  const isHeightInPX = this._main.getConfigManager().getSettingsData().getVideoData().videoHeightInPixels;
@@ -1877,8 +1906,6 @@
1877
1906
  }
1878
1907
  this._containerWidth = Math.ceil(finalVideoWidth);
1879
1908
  this._containerHeight = Math.ceil(finalVideoHeight);
1880
- this._videoWidth = this._containerWidth;
1881
- this._videoHeight = this._containerHeight;
1882
1909
  if (this._videoContainer !== null) {
1883
1910
  this._videoContainer.style.width = this._containerWidth + "px";
1884
1911
  this._videoContainer.style.height = this._containerHeight + "px";
@@ -2056,6 +2083,38 @@
2056
2083
  }
2057
2084
  StageController.LOG_ACTIVITY = true;
2058
2085
 
2086
+ /******************************************************************************
2087
+ Copyright (c) Microsoft Corporation.
2088
+
2089
+ Permission to use, copy, modify, and/or distribute this software for any
2090
+ purpose with or without fee is hereby granted.
2091
+
2092
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
2093
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2094
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
2095
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
2096
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
2097
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2098
+ PERFORMANCE OF THIS SOFTWARE.
2099
+ ***************************************************************************** */
2100
+ /* global Reflect, Promise, SuppressedError, Symbol */
2101
+
2102
+
2103
+ function __awaiter(thisArg, _arguments, P, generator) {
2104
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2105
+ return new (P || (P = Promise))(function (resolve, reject) {
2106
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
2107
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
2108
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
2109
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
2110
+ });
2111
+ }
2112
+
2113
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
2114
+ var e = new Error(message);
2115
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
2116
+ };
2117
+
2059
2118
  class MungeSDP {
2060
2119
  constructor() {}
2061
2120
  addAudio(sdpStr, audioLine) {
@@ -2408,50 +2467,78 @@
2408
2467
 
2409
2468
  class SoundMeter {
2410
2469
  constructor(main) {
2470
+ this._stream = null;
2471
+ this._audioContext = null;
2472
+ this._analyser = null;
2473
+ this._microphone = null;
2474
+ this._lastEventTime = 0;
2475
+ this.THROTTLE_INTERVAL = 100;
2411
2476
  this._instant = 0.0;
2412
2477
  this._slow = 0.0;
2413
- this.clip = 0.0;
2478
+ this._clip = 0.0;
2414
2479
  this._main = main;
2415
2480
  }
2416
2481
  attach(stream) {
2417
2482
  this._stream = stream;
2418
- this.audioContext = new AudioContext();
2419
- this.microphone = this.audioContext.createMediaStreamSource(stream);
2420
- this.processor = this.audioContext.createScriptProcessor(2048, 1, 1);
2421
- this.microphone.connect(this.processor);
2422
- this.processor.connect(this.audioContext.destination);
2423
- this.processor.onaudioprocess = event => {
2424
- this.onAudioProcess(event);
2425
- };
2483
+ this._audioContext = new AudioContext();
2484
+ this._microphone = this._audioContext.createMediaStreamSource(stream);
2485
+ this._analyser = this._audioContext.createAnalyser();
2486
+ this._analyser.fftSize = 2048;
2487
+ this._analyser.smoothingTimeConstant = 0.3;
2488
+ this._microphone.connect(this._analyser);
2489
+ this.startMonitoring();
2426
2490
  }
2427
2491
  detach() {
2428
- if (this.microphone !== null) this.microphone.disconnect();
2429
- if (this.processor !== null) this.processor.disconnect();
2492
+ if (this._microphone) {
2493
+ this._microphone.disconnect();
2494
+ this._microphone = null;
2495
+ }
2496
+ if (this._analyser) {
2497
+ this._analyser.disconnect();
2498
+ this._analyser = null;
2499
+ }
2500
+ if (this._audioContext) {
2501
+ this._audioContext.close();
2502
+ this._audioContext = null;
2503
+ }
2504
+ this._stream = null;
2505
+ this.clear();
2430
2506
  }
2431
2507
  clear() {
2432
2508
  this._instant = 0;
2433
2509
  this._slow = 0;
2434
- this.clip = 0;
2435
- }
2436
- onAudioProcess(event) {
2437
- const input = event.inputBuffer.getChannelData(0);
2438
- let i;
2439
- let sum = 0.0;
2440
- let clipcount = 0;
2441
- for (i = 0; i < input.length; ++i) {
2442
- sum += input[i] * input[i];
2443
- if (Math.abs(input[i]) > 0.99) {
2444
- clipcount += 1;
2445
- }
2446
- }
2447
- this._instant = Math.sqrt(sum / input.length);
2448
- this._slow = 0.05 * this._instant + 0.95 * this._slow;
2449
- this.clip = clipcount / input.length;
2450
- this._main.dispatchEvent("soundMeter", {
2451
- ref: this._main,
2452
- high: this._instant,
2453
- low: this._slow
2454
- });
2510
+ this._clip = 0;
2511
+ }
2512
+ startMonitoring() {
2513
+ if (!this._analyser) return;
2514
+ const dataArray = new Float32Array(this._analyser.frequencyBinCount);
2515
+ const analyze = () => {
2516
+ if (!this._analyser) return;
2517
+ const now = Date.now();
2518
+ this._analyser.getFloatTimeDomainData(dataArray);
2519
+ let sum = 0.0;
2520
+ let clipcount = 0;
2521
+ for (let i = 0; i < dataArray.length; ++i) {
2522
+ const amplitude = dataArray[i] * 3.16;
2523
+ sum += amplitude * amplitude;
2524
+ if (Math.abs(amplitude) > 0.99) {
2525
+ clipcount += 1;
2526
+ }
2527
+ }
2528
+ this._instant = Math.min(1, Math.sqrt(sum / dataArray.length));
2529
+ this._slow = Math.min(1, 0.05 * this._instant + 0.95 * this._slow);
2530
+ this._clip = clipcount / dataArray.length;
2531
+ if (now - this._lastEventTime >= this.THROTTLE_INTERVAL) {
2532
+ this._lastEventTime = now;
2533
+ this._main.dispatchEvent("soundMeter", {
2534
+ ref: this._main,
2535
+ high: Number(this._instant.toFixed(4)),
2536
+ low: Number(this._slow.toFixed(4))
2537
+ });
2538
+ }
2539
+ requestAnimationFrame(analyze);
2540
+ };
2541
+ requestAnimationFrame(analyze);
2455
2542
  }
2456
2543
  }
2457
2544
 
@@ -2462,6 +2549,8 @@
2462
2549
  'iceServers': []
2463
2550
  };
2464
2551
  this._isMicrophoneMuted = false;
2552
+ this._pendingMicrophoneState = null;
2553
+ this._permissionChecked = false;
2465
2554
  this._constraints = {
2466
2555
  video: {
2467
2556
  width: {
@@ -2485,6 +2574,9 @@
2485
2574
  this._publishState = exports.PublishState.NOT_INITIALIZED;
2486
2575
  this.onServerDisconnect = () => {};
2487
2576
  this.onServerConnect = () => {
2577
+ if (this._peerConnection) {
2578
+ this.closeWebRTCConnection();
2579
+ }
2488
2580
  this._peerConnection = new RTCPeerConnection(this._peerConnectionConfig);
2489
2581
  this._peerConnection.onicecandidate = event => {
2490
2582
  this.onIceCandidate(event);
@@ -2493,17 +2585,21 @@
2493
2585
  this.onConnectionStateChange(event);
2494
2586
  };
2495
2587
  this._peerConnection.onnegotiationneeded = event => {
2496
- this._peerConnection.createOffer(description => {
2497
- this.onDescriptionSuccess(description);
2498
- }, error => {
2499
- this.onDescriptionError(error);
2500
- }).catch(reason => {
2501
- console.log(reason);
2502
- });
2588
+ if (this._peerConnection) {
2589
+ this._peerConnection.createOffer(description => {
2590
+ this.onDescriptionSuccess(description);
2591
+ }, error => {
2592
+ this.onDescriptionError(error);
2593
+ }).catch(reason => {
2594
+ console.log(reason);
2595
+ });
2596
+ }
2503
2597
  };
2504
- let localTracks = this._stream.getTracks();
2505
- for (let localTrack in localTracks) {
2506
- this._peerConnection.addTrack(localTracks[localTrack], this._stream);
2598
+ if (this._stream) {
2599
+ let localTracks = this._stream.getTracks();
2600
+ for (let localTrack in localTracks) {
2601
+ this._peerConnection.addTrack(localTracks[localTrack], this._stream);
2602
+ }
2507
2603
  }
2508
2604
  };
2509
2605
  this.onDescriptionSuccess = description => {
@@ -2521,12 +2617,14 @@
2521
2617
  videoCodec: "42e01f",
2522
2618
  audioCodec: "opus"
2523
2619
  });
2524
- this._peerConnection.setLocalDescription(description).then(() => {
2525
- var _a;
2526
- (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.sendMessage('{"direction":"publish", "command":"sendOffer", "streamInfo":' + JSON.stringify(streamInfo) + ', "sdp":' + JSON.stringify(description) + '}');
2527
- }).catch(error => {
2528
- console.log(error);
2529
- });
2620
+ if (this._peerConnection) {
2621
+ this._peerConnection.setLocalDescription(description).then(() => {
2622
+ var _a;
2623
+ (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.sendMessage('{"direction":"publish", "command":"sendOffer", "streamInfo":' + JSON.stringify(streamInfo) + ', "sdp":' + JSON.stringify(description) + '}');
2624
+ }).catch(error => {
2625
+ console.log(error);
2626
+ });
2627
+ }
2530
2628
  };
2531
2629
  this.visibilityChange = () => {
2532
2630
  if (document.visibilityState === 'hidden') {
@@ -2549,104 +2647,225 @@
2549
2647
  };
2550
2648
  this._main = main;
2551
2649
  this._logger = main.getLogger();
2552
- this._logger.info(this, "Creating new PlaybackController");
2553
2650
  this._mungeSDP = new MungeSDP();
2554
2651
  this._soundMeter = new SoundMeter(this._main);
2555
- this.initialize();
2652
+ this.initializeDevices();
2556
2653
  }
2557
2654
  initialize() {
2558
- var _a, _b, _c;
2655
+ var _a, _b;
2559
2656
  this._main.addEventListener("serverConnect", this.onServerConnect, false);
2560
2657
  this._main.addEventListener("serverDisconnect", this.onServerDisconnect, false);
2561
2658
  document.addEventListener("visibilitychange", this.visibilityChange);
2562
2659
  window.addEventListener("blur", this.onWindowBlur);
2563
2660
  window.addEventListener("focus", this.onWindowFocus);
2661
+ if (this._selectedCamera || this._selectedMicrophone) {
2662
+ this.startCamera().then(() => {
2663
+ var _a;
2664
+ if (((_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey) != null) {
2665
+ this.initializeWebRTC();
2666
+ }
2667
+ });
2668
+ }
2564
2669
  if ((_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getSettingsData().autoConnect) {
2565
2670
  this._logger.info(this, "Initializing NetworkController (autoConnect is true)");
2566
2671
  (_b = this._main.getNetworkController()) === null || _b === void 0 ? void 0 : _b.initialize();
2567
- } else this._logger.warning(this, "Warning - autoConnect is set to false, switching to standby mode!");
2568
- if (((_c = this._main.getConfigManager()) === null || _c === void 0 ? void 0 : _c.getStreamData().streamKey) != null) this.startWebRTC();
2672
+ } else {
2673
+ this._logger.warning(this, "Warning - autoConnect is set to false, switching to standby mode!");
2674
+ }
2675
+ this.setupPermissionListeners();
2569
2676
  }
2570
- startWebRTC() {
2571
- try {
2572
- if (navigator.mediaDevices.getUserMedia) {
2573
- navigator.mediaDevices.getUserMedia(this._constraints).then(stream => {
2574
- this.onUserMediaSuccess(stream);
2575
- }).catch(error => {
2576
- this.onUserMediaError(error);
2577
- });
2578
- } else {
2579
- this._logger.error(this, "WebRTCStreamer :: Browser does not support WebRTC");
2580
- this._main.dispatchEvent("compatibilityError", {
2581
- ref: this._main,
2582
- message: "WebRTC is not supported"
2583
- });
2677
+ initializeDevices() {
2678
+ return __awaiter(this, void 0, void 0, function* () {
2679
+ try {
2680
+ yield this.grabDevices();
2681
+ this.setupPermissionListeners();
2682
+ this.initialize();
2683
+ } catch (error) {
2684
+ this._logger.error(this, "Error initializing devices: " + JSON.stringify(error));
2685
+ this.initialize();
2584
2686
  }
2585
- } catch (error) {
2586
- this.onUserMediaError(error);
2587
- }
2687
+ });
2588
2688
  }
2589
- publish(streamKey) {
2590
- if (this._publishState == exports.PublishState.PUBLISHED) this.closeStream();
2591
- this._main.getConfigManager().getStreamData().streamKey = streamKey;
2592
- this.startWebRTC();
2689
+ setupPermissionListeners() {
2690
+ const cameraQuery = {
2691
+ name: 'camera'
2692
+ };
2693
+ const microphoneQuery = {
2694
+ name: 'microphone'
2695
+ };
2696
+ navigator.permissions.query(cameraQuery).then(permissionStatus => {
2697
+ permissionStatus.onchange = () => {
2698
+ this._permissionChecked = false;
2699
+ this.handlePermissionChange('camera', permissionStatus.state);
2700
+ };
2701
+ });
2702
+ navigator.permissions.query(microphoneQuery).then(permissionStatus => {
2703
+ permissionStatus.onchange = () => {
2704
+ this._permissionChecked = false;
2705
+ this.handlePermissionChange('microphone', permissionStatus.state);
2706
+ };
2707
+ });
2593
2708
  }
2594
- unpublish() {
2595
- this.closeStream();
2709
+ handlePermissionChange(device, state) {
2710
+ return __awaiter(this, void 0, void 0, function* () {
2711
+ if (state === 'denied') {
2712
+ if (this._publishState == exports.PublishState.PUBLISHED) {
2713
+ this.closeStream();
2714
+ }
2715
+ }
2716
+ yield this.grabDevices();
2717
+ if (state === 'granted') {
2718
+ yield this.startCamera();
2719
+ }
2720
+ });
2596
2721
  }
2597
- onUserMediaSuccess(stream) {
2598
- var _a;
2599
- console.log('%c⏵ 🎥 onUserMediaSuccess', 'background: green; color: white;');
2600
- this._logger.success(this, "WebRTCStreamer :: WebRTC UserMedia successfully retrieved");
2722
+ onCameraStreamSuccess(stream) {
2723
+ this._logger.success(this, "Camera stream successfully retrieved");
2601
2724
  this._stream = stream;
2602
- this._soundMeter.attach(this._stream);
2603
- this.setPublishState(exports.PublishState.INITIALIZED);
2604
- if (this._cameraList == null || this._microphoneList == null) this.grabDevices();
2725
+ if (this._pendingMicrophoneState !== null) {
2726
+ this.applyMicrophoneState(this._pendingMicrophoneState);
2727
+ this._pendingMicrophoneState = null;
2728
+ } else {
2729
+ this.applyMicrophoneState(!this._isMicrophoneMuted);
2730
+ }
2731
+ if (this._stream.getAudioTracks().length > 0) {
2732
+ this._soundMeter.attach(this._stream);
2733
+ }
2734
+ if (this._cameraList == null || this._microphoneList == null) {
2735
+ this.grabDevices();
2736
+ }
2605
2737
  const videoElement = this._main.getStageController().getScreenElement().getVideoElement();
2606
2738
  videoElement.srcObject = stream;
2607
2739
  videoElement.autoplay = true;
2608
2740
  videoElement.playsInline = true;
2609
2741
  videoElement.disableRemotePlayback = true;
2610
2742
  videoElement.controls = false;
2611
- (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.start();
2743
+ videoElement.muted = true;
2744
+ this.setPublishState(exports.PublishState.INITIALIZED);
2612
2745
  }
2613
- onUserMediaError(error) {
2614
- console.log('%c⏵ 🎥 onUserMediaError: ' + JSON.stringify(error), 'background: green; color: white;');
2615
- switch (error.message) {
2616
- case "Permission denied":
2617
- this._logger.warning(this, "WebRTCStreamer :: No permission to access camera & microphone");
2618
- this._main.dispatchEvent("inputDeviceDenied", {
2619
- ref: this._main
2620
- });
2621
- break;
2622
- case "The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.":
2623
- this._logger.warning(this, "WebRTCStreamer :: No permission to access camera & microphone");
2624
- this._main.dispatchEvent("inputDeviceDenied", {
2625
- ref: this._main
2626
- });
2627
- break;
2628
- case "The request is not allowed by the user agent or the platform in the current context.":
2629
- this._logger.warning(this, "WebRTCStreamer :: No permission to access camera & microphone");
2630
- this._main.dispatchEvent("inputDeviceDenied", {
2631
- ref: this._main
2632
- });
2633
- break;
2634
- case "The object can not be found here.":
2635
- this._logger.warning(this, "WebRTCStreamer :: Could not access camera or microphone");
2636
- this._main.dispatchEvent("inputDeviceError", {
2637
- ref: this._main
2638
- });
2639
- break;
2640
- case "Requested device not found":
2641
- this._logger.warning(this, "WebRTCStreamer :: Could not access camera or microphone");
2642
- this._main.dispatchEvent("inputDeviceError", {
2643
- ref: this._main
2644
- });
2645
- break;
2646
- default:
2647
- this._logger.warning(this, "WebRTCStreamer :: Unsupported onUserMediaError: " + error.message);
2648
- break;
2746
+ initializeWebRTC() {
2747
+ var _a;
2748
+ if (!this._stream) {
2749
+ this._logger.error(this, "Cannot initialize WebRTC - no camera stream available");
2750
+ return;
2751
+ }
2752
+ if (this._peerConnection) {
2753
+ this._logger.info(this, "WebRTC connection already exists, updating stream");
2754
+ this.updateWebRTCStream();
2755
+ } else {
2756
+ this._logger.info(this, "Initializing new WebRTC connection");
2757
+ (_a = this._main.getNetworkController()) === null || _a === void 0 ? void 0 : _a.start();
2758
+ }
2759
+ }
2760
+ publish(streamKey) {
2761
+ if (!this.isStreamReady(true, true)) {
2762
+ this._logger.warning(this, "Cannot publish - stream not ready (missing video or audio track)");
2763
+ return false;
2649
2764
  }
2765
+ this.closeWebRTCConnection();
2766
+ this._main.getConfigManager().getStreamData().streamKey = streamKey;
2767
+ this._main.dispatchEvent("publish", {
2768
+ ref: this._main,
2769
+ streamKey: streamKey
2770
+ });
2771
+ this.initializeWebRTC();
2772
+ return true;
2773
+ }
2774
+ unpublish() {
2775
+ this._main.getConfigManager().getStreamData().streamKey = null;
2776
+ this._main.dispatchEvent("unpublish", {
2777
+ ref: this._main
2778
+ });
2779
+ this.closeStream();
2780
+ }
2781
+ onUserMediaError(error) {
2782
+ return __awaiter(this, void 0, void 0, function* () {
2783
+ yield this.grabDevices();
2784
+ if (error.name === "OverconstrainedError") {
2785
+ this._logger.warning(this, "Device constraints not satisfied");
2786
+ }
2787
+ });
2788
+ }
2789
+ checkIndividualDeviceAccess() {
2790
+ return __awaiter(this, void 0, void 0, function* () {
2791
+ const results = {
2792
+ camera: {
2793
+ allowed: false,
2794
+ available: false
2795
+ },
2796
+ microphone: {
2797
+ allowed: false,
2798
+ available: false
2799
+ }
2800
+ };
2801
+ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
2802
+ const isEdge = /edg/i.test(navigator.userAgent);
2803
+ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
2804
+ try {
2805
+ const devices = yield navigator.mediaDevices.enumerateDevices();
2806
+ results.camera.available = devices.some(device => device.kind === 'videoinput');
2807
+ results.microphone.available = devices.some(device => device.kind === 'audioinput');
2808
+ } catch (error) {
2809
+ console.error("Error checking device availability:", error);
2810
+ }
2811
+ if (isFirefox) {
2812
+ console.log("Firefox detected, using direct getUserMedia check...");
2813
+ if (results.camera.available) {
2814
+ try {
2815
+ const stream = yield navigator.mediaDevices.getUserMedia({
2816
+ video: true
2817
+ });
2818
+ results.camera.allowed = true;
2819
+ stream.getTracks().forEach(track => track.stop());
2820
+ } catch (e) {
2821
+ console.log("Camera permission check failed:", e);
2822
+ results.camera.allowed = false;
2823
+ }
2824
+ }
2825
+ if (results.microphone.available) {
2826
+ try {
2827
+ const stream = yield navigator.mediaDevices.getUserMedia({
2828
+ audio: true
2829
+ });
2830
+ results.microphone.allowed = true;
2831
+ stream.getTracks().forEach(track => track.stop());
2832
+ } catch (e) {
2833
+ console.log("Microphone permission check failed:", e);
2834
+ results.microphone.allowed = false;
2835
+ }
2836
+ }
2837
+ } else {
2838
+ try {
2839
+ const [cameraPermission, microphonePermission] = yield Promise.all([navigator.permissions.query({
2840
+ name: 'camera'
2841
+ }), navigator.permissions.query({
2842
+ name: 'microphone'
2843
+ })]);
2844
+ results.camera.allowed = cameraPermission.state === "granted";
2845
+ results.microphone.allowed = microphonePermission.state === "granted";
2846
+ if ((isSafari || isEdge) && (!results.camera.allowed || !results.microphone.allowed)) {
2847
+ try {
2848
+ const stream = yield navigator.mediaDevices.getUserMedia({
2849
+ video: results.camera.available && !results.camera.allowed,
2850
+ audio: results.microphone.available && !results.microphone.allowed
2851
+ });
2852
+ if (stream.getVideoTracks().length > 0) {
2853
+ results.camera.allowed = true;
2854
+ }
2855
+ if (stream.getAudioTracks().length > 0) {
2856
+ results.microphone.allowed = true;
2857
+ }
2858
+ stream.getTracks().forEach(track => track.stop());
2859
+ } catch (error) {
2860
+ console.log("Additional permission check failed:", error);
2861
+ }
2862
+ }
2863
+ } catch (error) {
2864
+ console.error("Error checking permissions:", error);
2865
+ }
2866
+ }
2867
+ return results;
2868
+ });
2650
2869
  }
2651
2870
  onSocketMessage(data) {
2652
2871
  let msgJSON = JSON.parse(data);
@@ -2661,7 +2880,7 @@
2661
2880
  let iceCandidates = msgJSON['iceCandidates'];
2662
2881
  if (iceCandidates !== undefined) {
2663
2882
  for (let index in iceCandidates) {
2664
- this._peerConnection.addIceCandidate(new RTCIceCandidate(iceCandidates[index]));
2883
+ if (this._peerConnection) this._peerConnection.addIceCandidate(new RTCIceCandidate(iceCandidates[index]));
2665
2884
  }
2666
2885
  }
2667
2886
  break;
@@ -2703,78 +2922,240 @@
2703
2922
  }
2704
2923
  }
2705
2924
  grabDevices() {
2706
- navigator.mediaDevices.enumerateDevices().then(devices => {
2707
- this._cameraList = new InputDeviceList();
2708
- this._microphoneList = new InputDeviceList();
2709
- for (let i = 0; i < devices.length; i++) {
2925
+ return __awaiter(this, void 0, void 0, function* () {
2926
+ try {
2927
+ const deviceAccess = yield this.checkIndividualDeviceAccess();
2928
+ this._cameraList = new InputDeviceList();
2929
+ this._microphoneList = new InputDeviceList();
2930
+ if (!this._permissionChecked) {
2931
+ if (!deviceAccess.camera.allowed) {
2932
+ this._main.dispatchEvent("cameraAccessDenied", {
2933
+ ref: this._main
2934
+ });
2935
+ } else if (!deviceAccess.camera.available) {
2936
+ this._main.dispatchEvent("noCameraFound", {
2937
+ ref: this._main
2938
+ });
2939
+ }
2940
+ if (!deviceAccess.microphone.allowed) {
2941
+ this._main.dispatchEvent("microphoneAccessDenied", {
2942
+ ref: this._main
2943
+ });
2944
+ } else if (!deviceAccess.microphone.available) {
2945
+ this._main.dispatchEvent("noMicrophoneFound", {
2946
+ ref: this._main
2947
+ });
2948
+ }
2949
+ }
2950
+ const devices = yield navigator.mediaDevices.enumerateDevices();
2951
+ for (const device of devices) {
2952
+ if (device.deviceId && device.label) {
2953
+ if (device.kind === 'videoinput' && deviceAccess.camera.allowed) {
2954
+ const inputDevice = new InputDevice(device, this._cameraList.getSize());
2955
+ this._cameraList.push(inputDevice);
2956
+ } else if (device.kind === 'audioinput' && deviceAccess.microphone.allowed) {
2957
+ const inputDevice = new InputDevice(device, this._microphoneList.getSize());
2958
+ this._microphoneList.push(inputDevice);
2959
+ }
2960
+ }
2961
+ }
2710
2962
  try {
2711
- if (devices[i].kind === 'videoinput') {
2712
- let device = new InputDevice(devices[i], i);
2713
- this._cameraList.push(device);
2714
- } else if (devices[i].kind === 'audioinput') {
2715
- let device = new InputDevice(devices[i], i);
2716
- this._microphoneList.push(device);
2963
+ if (deviceAccess.camera.allowed) {
2964
+ this._selectedCamera = this.pickCamera();
2965
+ }
2966
+ if (deviceAccess.microphone.allowed) {
2967
+ this._selectedMicrophone = this.pickMicrophone();
2717
2968
  }
2718
2969
  } catch (error) {
2719
- this._logger.error(this, "WebRTCStreamer :: Input Device Error: " + error);
2970
+ this._logger.error(this, "Errror on grab devices: " + JSON.stringify(error));
2720
2971
  }
2721
- }
2722
- if (this._cameraList.getSize() == 0) {
2723
- this._main.dispatchEvent("noCameraFound", {
2724
- ref: this._main
2972
+ this._main.dispatchEvent("deviceListUpdate", {
2973
+ ref: this._main,
2974
+ cameraList: this._cameraList.getArray(),
2975
+ microphoneList: this._microphoneList.getArray()
2976
+ });
2977
+ this._permissionChecked = true;
2978
+ } catch (error) {
2979
+ console.error("Error in grabDevices:", error);
2980
+ this._cameraList = new InputDeviceList();
2981
+ this._microphoneList = new InputDeviceList();
2982
+ this._main.dispatchEvent("deviceListUpdate", {
2983
+ ref: this._main,
2984
+ cameraList: this._cameraList.getArray(),
2985
+ microphoneList: this._microphoneList.getArray()
2725
2986
  });
2726
- } else if (this._microphoneList.getSize() == 0) {
2727
- this._main.dispatchEvent("noMicrophoneFound", {
2987
+ this._main.dispatchEvent("inputDeviceError", {
2728
2988
  ref: this._main
2729
2989
  });
2730
- } else {
2731
- this._logger.info(this, "Camera list:");
2732
- for (let i = 0; i < this._cameraList.getSize(); i++) this._logger.info(this, "=> [" + i + "] InputDevice: " + this._cameraList.get(i).getLabel());
2733
- this._logger.info(this, "Microphone list:");
2734
- for (let k = 0; k < this._microphoneList.getSize(); k++) this._logger.info(this, "=> [" + k + "] InputDevice: " + this._microphoneList.get(k).getLabel());
2735
- this._selectedCamera = this.pickCamera();
2736
- this._selectedMicrophone = this.pickMicrophone();
2737
- }
2738
- }).catch(() => {
2739
- this._main.dispatchEvent("inputDeviceError", {
2740
- ref: this._main
2741
- });
2990
+ }
2742
2991
  });
2743
2992
  }
2744
2993
  selectCamera(cameraID) {
2745
- var _a;
2994
+ var _a, _b;
2995
+ const streamKey = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey;
2996
+ const wasPublished = this._publishState === exports.PublishState.PUBLISHED;
2746
2997
  for (let i = 0; i < this._cameraList.getSize(); i++) {
2747
2998
  if (this._cameraList.get(i).getID() == cameraID) {
2748
2999
  this._selectedCamera = this._cameraList.get(i);
2749
- (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.saveField("cameraID", this._selectedCamera.getID());
3000
+ console.log("kamera znaleziona i zapisana " + this._cameraList.get(i).getLabel() + " " + this._cameraList.get(i).getID());
3001
+ (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.saveField("cameraID", this._selectedCamera.getID());
2750
3002
  break;
2751
3003
  }
2752
3004
  }
2753
- this.closeStream();
3005
+ this.stopCameraStream();
2754
3006
  this._constraints.video.deviceId = this._selectedCamera.getID();
2755
- this.startWebRTC();
3007
+ this.startCamera().then(() => {
3008
+ if (wasPublished && streamKey) {
3009
+ this.publish(streamKey);
3010
+ }
3011
+ });
2756
3012
  }
2757
3013
  selectMicrophone(micID) {
2758
- var _a;
2759
- for (let i = 0; i < this._microphoneList.getSize(); i++) {
2760
- if (this._microphoneList.get(i).getID() == micID) {
2761
- this._selectedMicrophone = this._microphoneList.get(i);
2762
- (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.saveField("microphoneID", this._selectedMicrophone.getID());
2763
- break;
3014
+ var _a, _b, _c, _d, _e, _f;
3015
+ return __awaiter(this, void 0, void 0, function* () {
3016
+ console.log("🎤 Selecting microphone:", micID);
3017
+ const streamKey = (_a = this._main.getConfigManager()) === null || _a === void 0 ? void 0 : _a.getStreamData().streamKey;
3018
+ const wasPublished = this._publishState === exports.PublishState.PUBLISHED;
3019
+ for (let i = 0; i < this._microphoneList.getSize(); i++) {
3020
+ if (this._microphoneList.get(i).getID() == micID) {
3021
+ this._selectedMicrophone = this._microphoneList.get(i);
3022
+ (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.saveField("microphoneID", this._selectedMicrophone.getID());
3023
+ break;
3024
+ }
2764
3025
  }
3026
+ this._soundMeter.detach();
3027
+ (_c = this._stream) === null || _c === void 0 ? void 0 : _c.getAudioTracks().forEach(track => track.stop());
3028
+ try {
3029
+ const audioStream = yield navigator.mediaDevices.getUserMedia({
3030
+ audio: {
3031
+ deviceId: {
3032
+ exact: (_d = this._selectedMicrophone) === null || _d === void 0 ? void 0 : _d.getID()
3033
+ }
3034
+ }
3035
+ });
3036
+ const oldAudioTracks = ((_e = this._stream) === null || _e === void 0 ? void 0 : _e.getAudioTracks()) || [];
3037
+ oldAudioTracks.forEach(track => {
3038
+ var _a;
3039
+ (_a = this._stream) === null || _a === void 0 ? void 0 : _a.removeTrack(track);
3040
+ track.stop();
3041
+ });
3042
+ const newAudioTrack = audioStream.getAudioTracks()[0];
3043
+ (_f = this._stream) === null || _f === void 0 ? void 0 : _f.addTrack(newAudioTrack);
3044
+ this._soundMeter.detach();
3045
+ yield new Promise(resolve => setTimeout(resolve, 100));
3046
+ if (this._stream) {
3047
+ this._soundMeter.attach(this._stream);
3048
+ }
3049
+ if (wasPublished && streamKey) {
3050
+ this.publish(streamKey);
3051
+ }
3052
+ } catch (error) {
3053
+ console.error("Error changing microphone:", error);
3054
+ }
3055
+ });
3056
+ }
3057
+ startCamera() {
3058
+ return __awaiter(this, void 0, void 0, function* () {
3059
+ if (this._stream) {
3060
+ console.log("Stopping existing stream before starting new one");
3061
+ this._stream.getTracks().forEach(track => {
3062
+ track.stop();
3063
+ });
3064
+ this._stream = null;
3065
+ }
3066
+ try {
3067
+ const constraints = {
3068
+ video: this._selectedCamera ? Object.assign(Object.assign({}, this._constraints.video), {
3069
+ deviceId: {
3070
+ exact: this._selectedCamera.getID()
3071
+ }
3072
+ }) : false,
3073
+ audio: this._selectedMicrophone ? {
3074
+ deviceId: {
3075
+ exact: this._selectedMicrophone.getID()
3076
+ }
3077
+ } : false
3078
+ };
3079
+ try {
3080
+ const stream = yield navigator.mediaDevices.getUserMedia(constraints);
3081
+ this._stream = stream;
3082
+ this.onCameraStreamSuccess(this._stream);
3083
+ } catch (error) {
3084
+ if (constraints.video) {
3085
+ this.onUserMediaError({
3086
+ name: error.name || 'Error',
3087
+ message: error.message || 'Unknown error',
3088
+ deviceType: 'video'
3089
+ });
3090
+ }
3091
+ if (constraints.audio) {
3092
+ this.onUserMediaError({
3093
+ name: error.name || 'Error',
3094
+ message: error.message || 'Unknown error',
3095
+ deviceType: 'audio'
3096
+ });
3097
+ }
3098
+ }
3099
+ } catch (error) {
3100
+ console.error("Error in startCamera:", error);
3101
+ yield this.grabDevices();
3102
+ }
3103
+ });
3104
+ }
3105
+ updateWebRTCStream() {
3106
+ if (!this._peerConnection || !this._stream) {
3107
+ return;
2765
3108
  }
2766
- this.closeStream();
2767
- this._constraints.audio = {
2768
- deviceId: this._selectedMicrophone.getID()
2769
- };
2770
- this.startWebRTC();
3109
+ const senders = this._peerConnection.getSenders();
3110
+ senders.forEach(sender => {
3111
+ if (this._peerConnection) this._peerConnection.removeTrack(sender);
3112
+ });
3113
+ this._stream.getTracks().forEach(track => {
3114
+ if (this._stream != null && this._peerConnection) {
3115
+ this._peerConnection.addTrack(track, this._stream);
3116
+ }
3117
+ });
3118
+ }
3119
+ closeStream() {
3120
+ if (this._peerConnection !== undefined && this._peerConnection !== null) {
3121
+ this._peerConnection.close();
3122
+ }
3123
+ this.setPublishState(exports.PublishState.UNPUBLISHED);
2771
3124
  }
2772
3125
  pickCamera() {
2773
3126
  var _a, _b;
2774
- for (let i = 0; i < this._cameraList.getSize(); i++) this._cameraList.get(i).setSelected(false);
3127
+ for (let i = 0; i < this._cameraList.getSize(); i++) {
3128
+ this._cameraList.get(i).setSelected(false);
3129
+ }
2775
3130
  let savedCameraID = (_b = (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.getField("cameraID")) !== null && _b !== void 0 ? _b : null;
2776
- if (this._selectedCamera == null || savedCameraID == null) this._selectedCamera = this._cameraList.get(0);else this.selectCamera(savedCameraID);
2777
- this._selectedCamera.setSelected(true);
3131
+ if (this._cameraList.getSize() > 0) {
3132
+ if (savedCameraID) {
3133
+ let found = false;
3134
+ for (let i = 0; i < this._cameraList.getSize(); i++) {
3135
+ if (this._cameraList.get(i).getID() === savedCameraID) {
3136
+ this._selectedCamera = this._cameraList.get(i);
3137
+ this._selectedCamera.setSelected(true);
3138
+ found = true;
3139
+ this._constraints.video.deviceId = this._selectedCamera.getID();
3140
+ break;
3141
+ }
3142
+ }
3143
+ if (!found) {
3144
+ this._main.dispatchEvent("savedCameraNotFound", {
3145
+ ref: this._main
3146
+ });
3147
+ throw new Error("Specific camera was not found!");
3148
+ }
3149
+ }
3150
+ if (!this._selectedCamera) {
3151
+ this._main.dispatchEvent("savedCameraNotFound", {
3152
+ ref: this._main
3153
+ });
3154
+ this._selectedCamera = this._cameraList.get(0);
3155
+ this._selectedCamera.setSelected(true);
3156
+ this._constraints.video.deviceId = this._selectedCamera.getID();
3157
+ }
3158
+ }
2778
3159
  this._main.dispatchEvent("deviceListUpdate", {
2779
3160
  ref: this._main,
2780
3161
  cameraList: this._cameraList.getArray(),
@@ -2784,56 +3165,101 @@
2784
3165
  }
2785
3166
  pickMicrophone() {
2786
3167
  var _a, _b;
2787
- for (let i = 0; i < this._microphoneList.getSize(); i++) this._microphoneList.get(i).setSelected(false);
3168
+ for (let i = 0; i < this._microphoneList.getSize(); i++) {
3169
+ this._microphoneList.get(i).setSelected(false);
3170
+ }
2788
3171
  let savedMicID = (_b = (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.getField("microphoneID")) !== null && _b !== void 0 ? _b : null;
2789
- if (this._selectedMicrophone == null || savedMicID == null) this._selectedMicrophone = this._microphoneList.get(0);else this.selectMicrophone(savedMicID);
2790
- this._selectedMicrophone.setSelected(true);
2791
- this._main.dispatchEvent("deviceListUpdate", {
2792
- ref: this._main,
2793
- cameraList: this._cameraList.getArray(),
2794
- microphoneList: this._microphoneList.getArray()
2795
- });
2796
- return this._selectedMicrophone;
2797
- }
2798
- muteMicrophone(microphoneState) {
2799
- this._isMicrophoneMuted = microphoneState;
2800
- if (this._stream != null) {
2801
- if (microphoneState) {
2802
- this._logger.success(this, "WebRTCStreamer1 :: Unmuting microphone");
2803
- } else {
2804
- this._logger.success(this, "WebRTCStreamer1 :: Muting microphone");
3172
+ if (this._microphoneList.getSize() > 0) {
3173
+ if (savedMicID) {
3174
+ let found = false;
3175
+ for (let i = 0; i < this._microphoneList.getSize(); i++) {
3176
+ if (this._microphoneList.get(i).getID() === savedMicID) {
3177
+ this._selectedMicrophone = this._microphoneList.get(i);
3178
+ found = true;
3179
+ break;
3180
+ }
3181
+ }
3182
+ if (!found) {
3183
+ this._main.dispatchEvent("savedMicrophoneNotFound", {
3184
+ ref: this._main
3185
+ });
3186
+ throw new Error("Specific microphone was not found!");
3187
+ }
2805
3188
  }
2806
- if (this._stream.getAudioTracks() != null) {
2807
- for (let i = 0; i < this._stream.getAudioTracks().length; i++) this._stream.getAudioTracks()[i].enabled = microphoneState;
3189
+ if (!this._selectedMicrophone) {
3190
+ this._selectedMicrophone = this._microphoneList.get(0);
2808
3191
  }
2809
- this._isMicrophoneMuted = !microphoneState;
3192
+ this._selectedMicrophone.setSelected(true);
3193
+ this._constraints.audio = {
3194
+ deviceId: this._selectedMicrophone.getID()
3195
+ };
3196
+ }
3197
+ return this._selectedMicrophone;
3198
+ }
3199
+ clearSavedDevices() {
3200
+ var _a, _b;
3201
+ (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.removeField("cameraID");
3202
+ (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.removeField("microphoneID");
3203
+ }
3204
+ messSavedDevices() {
3205
+ var _a, _b;
3206
+ (_a = this._main.getStorageManager()) === null || _a === void 0 ? void 0 : _a.saveField("cameraID", "a");
3207
+ (_b = this._main.getStorageManager()) === null || _b === void 0 ? void 0 : _b.saveField("microphoneID", "b");
3208
+ }
3209
+ muteMicrophone(shouldMute) {
3210
+ if (this._isMicrophoneMuted === shouldMute) {
3211
+ return;
3212
+ }
3213
+ this._isMicrophoneMuted = shouldMute;
3214
+ if (this._stream) {
3215
+ this.applyMicrophoneState(!shouldMute);
2810
3216
  } else {
2811
- this._logger.warning(this, "WebRTCStreamer :: Stream object not present!");
3217
+ this._pendingMicrophoneState = !shouldMute;
3218
+ this._logger.info(this, `WebRTCStreamer :: Stream not yet available, storing microphone state (muted: ${shouldMute})`);
2812
3219
  }
2813
3220
  this._main.dispatchEvent("microphoneStateChange", {
2814
3221
  ref: this._main,
2815
3222
  isMuted: this._isMicrophoneMuted
2816
3223
  });
2817
3224
  }
3225
+ applyMicrophoneState(enabled) {
3226
+ if (!this._stream) {
3227
+ this._logger.warning(this, "WebRTCStreamer :: Cannot apply microphone state - stream not available");
3228
+ return;
3229
+ }
3230
+ const audioTracks = this._stream.getAudioTracks();
3231
+ if (audioTracks && audioTracks.length > 0) {
3232
+ this._logger.success(this, `WebRTCStreamer :: ${enabled ? 'Unmuting' : 'Muting'} microphone`);
3233
+ audioTracks.forEach(track => track.enabled = enabled);
3234
+ } else {
3235
+ this._logger.warning(this, "WebRTCStreamer :: No audio tracks found in stream");
3236
+ }
3237
+ }
2818
3238
  isMicrophoneMuted() {
2819
3239
  return this._isMicrophoneMuted;
2820
3240
  }
3241
+ isStreamReady(requireVideo = true, requireAudio = true) {
3242
+ if (!this._stream) {
3243
+ return false;
3244
+ }
3245
+ const videoTracks = this._stream.getVideoTracks();
3246
+ const audioTracks = this._stream.getAudioTracks();
3247
+ const videoReady = !requireVideo || videoTracks.length > 0 && videoTracks[0].readyState === 'live';
3248
+ const audioReady = !requireAudio || audioTracks.length > 0 && audioTracks[0].readyState === 'live';
3249
+ return videoReady && audioReady;
3250
+ }
3251
+ closeWebRTCConnection() {
3252
+ if (this._peerConnection) {
3253
+ this._peerConnection.close();
3254
+ this._peerConnection = null;
3255
+ }
3256
+ }
2821
3257
  onDescriptionError(error) {
2822
3258
  this._logger.info(this, "WebRTCStreamer :: onDescriptionError: " + JSON.stringify(error));
2823
3259
  }
2824
3260
  onIceCandidate(event) {
2825
3261
  if (event.candidate !== null) ;
2826
3262
  }
2827
- closeStream() {
2828
- if (this._stream) {
2829
- this._stream.getTracks().forEach(function (track) {
2830
- track.stop();
2831
- });
2832
- }
2833
- this._soundMeter.detach();
2834
- if (this._peerConnection !== undefined && this._peerConnection !== null) this._peerConnection.close();
2835
- this.setPublishState(exports.PublishState.UNPUBLISHED);
2836
- }
2837
3263
  getCurrentCamera() {
2838
3264
  return this._selectedCamera;
2839
3265
  }
@@ -2859,15 +3285,105 @@
2859
3285
  getPlayer() {
2860
3286
  return this._selectedPlayer;
2861
3287
  }
3288
+ stopCameraStream() {
3289
+ var _a, _b;
3290
+ if (this._stream) {
3291
+ this._stream.getTracks().forEach(track => track.stop());
3292
+ const videoElement = (_b = (_a = this._main.getStageController()) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getVideoElement();
3293
+ if (videoElement) {
3294
+ videoElement.srcObject = null;
3295
+ }
3296
+ this._soundMeter.detach();
3297
+ this._stream = null;
3298
+ }
3299
+ }
3300
+ forceStopAllStreams() {
3301
+ var _a, _b;
3302
+ console.log("Force stopping all streams...");
3303
+ if (this._peerConnection) {
3304
+ try {
3305
+ const senders = this._peerConnection.getSenders();
3306
+ senders.forEach(sender => {
3307
+ var _a;
3308
+ try {
3309
+ if (sender.track) {
3310
+ sender.track.enabled = false;
3311
+ sender.track.stop();
3312
+ (_a = this._peerConnection) === null || _a === void 0 ? void 0 : _a.removeTrack(sender);
3313
+ }
3314
+ } catch (e) {
3315
+ console.error('Error stopping sender track:', e);
3316
+ }
3317
+ });
3318
+ this._peerConnection.close();
3319
+ this._peerConnection = null;
3320
+ } catch (e) {
3321
+ console.error('Error closing peer connection:', e);
3322
+ }
3323
+ }
3324
+ try {
3325
+ const videoElement = (_b = (_a = this._main.getStageController()) === null || _a === void 0 ? void 0 : _a.getScreenElement()) === null || _b === void 0 ? void 0 : _b.getVideoElement();
3326
+ if (videoElement && videoElement.srcObject instanceof MediaStream) {
3327
+ const videoTracks = videoElement.srcObject.getTracks();
3328
+ videoTracks.forEach(track => {
3329
+ try {
3330
+ track.enabled = false;
3331
+ track.stop();
3332
+ } catch (e) {
3333
+ console.error('Error stopping video element track:', e);
3334
+ }
3335
+ });
3336
+ videoElement.srcObject = null;
3337
+ videoElement.removeAttribute('src');
3338
+ videoElement.load();
3339
+ }
3340
+ } catch (e) {
3341
+ console.error('Error cleaning video element:', e);
3342
+ }
3343
+ if (this._stream) {
3344
+ try {
3345
+ const tracks = this._stream.getTracks();
3346
+ console.log(`Stopping ${tracks.length} tracks from main stream`);
3347
+ tracks.forEach(track => {
3348
+ try {
3349
+ console.log(`Stopping ${track.kind} track, enabled: ${track.enabled}, state: ${track.readyState}`);
3350
+ track.enabled = false;
3351
+ track.stop();
3352
+ console.log(`Track after stop - state: ${track.readyState}`);
3353
+ } catch (e) {
3354
+ console.error(`Error stopping ${track.kind} track:`, e);
3355
+ }
3356
+ });
3357
+ this._stream = null;
3358
+ } catch (e) {
3359
+ console.error('Error stopping main stream:', e);
3360
+ }
3361
+ }
3362
+ console.log("All streams should be stopped now");
3363
+ }
2862
3364
  destroy() {
2863
- var _a;
2864
- this.closeStream();
2865
- (_a = this._selectedPlayer) === null || _a === void 0 ? void 0 : _a.clear();
2866
- this._selectedPlayer = null;
2867
- this._main.removeEventListener("serverConnect", this.onServerConnect);
2868
- document.removeEventListener("visibilitychange", this.visibilityChange);
2869
- window.removeEventListener("blur", this.onWindowBlur);
2870
- window.removeEventListener("focus", this.onWindowFocus);
3365
+ console.log("Starting destroy process...");
3366
+ this.forceStopAllStreams();
3367
+ if (this._soundMeter) {
3368
+ this._soundMeter.detach();
3369
+ }
3370
+ this._pendingMicrophoneState = null;
3371
+ this._cameraList = new InputDeviceList();
3372
+ this._microphoneList = new InputDeviceList();
3373
+ this._permissionChecked = false;
3374
+ this._isWindowActive = false;
3375
+ this._isMicrophoneMuted = false;
3376
+ try {
3377
+ this._main.removeEventListener("serverConnect", this.onServerConnect);
3378
+ this._main.removeEventListener("serverDisconnect", this.onServerDisconnect);
3379
+ document.removeEventListener("visibilitychange", this.visibilityChange);
3380
+ window.removeEventListener("blur", this.onWindowBlur);
3381
+ window.removeEventListener("focus", this.onWindowFocus);
3382
+ } catch (e) {
3383
+ console.error('Error removing event listeners:', e);
3384
+ }
3385
+ this._publishState = exports.PublishState.NOT_INITIALIZED;
3386
+ console.log("Destroy process completed");
2871
3387
  }
2872
3388
  }
2873
3389
 
@@ -3169,8 +3685,8 @@
3169
3685
  constructor(streamConfig, autoInitialize = false) {
3170
3686
  super();
3171
3687
  this.DEV_MODE = true;
3172
- this.STREAMER_VERSION = "0.9.0-beta.2";
3173
- this.COMPILE_DATE = "11/29/2024, 6:55:45 PM";
3688
+ this.STREAMER_VERSION = "0.9.0-beta.4";
3689
+ this.COMPILE_DATE = "12/7/2024, 6:37:54 PM";
3174
3690
  this.STREAMER_BRANCH = "Experimental";
3175
3691
  this.STREAMER_PROTOCOL_VERSION = 1;
3176
3692
  this._initialized = false;
@@ -3323,8 +3839,20 @@
3323
3839
  return (_b = (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.getPublishState()) !== null && _b !== void 0 ? _b : exports.PublishState.NOT_INITIALIZED;
3324
3840
  }
3325
3841
  publish(streamKey) {
3842
+ var _a, _b;
3843
+ return (_b = (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.publish(streamKey)) !== null && _b !== void 0 ? _b : false;
3844
+ }
3845
+ clearSavedDevices() {
3846
+ var _a;
3847
+ return (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.clearSavedDevices();
3848
+ }
3849
+ messSavedDevices() {
3326
3850
  var _a;
3327
- (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.publish(streamKey);
3851
+ return (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.messSavedDevices();
3852
+ }
3853
+ isStreamReady() {
3854
+ var _a, _b;
3855
+ return (_b = (_a = this._playbackController) === null || _a === void 0 ? void 0 : _a.isStreamReady()) !== null && _b !== void 0 ? _b : false;
3328
3856
  }
3329
3857
  unpublish() {
3330
3858
  var _a;
@@ -3382,7 +3910,7 @@
3382
3910
  }
3383
3911
  updateToSize() {
3384
3912
  if (this._initialized) {
3385
- this._stageController.onResize();
3913
+ this._stageController.handleResize();
3386
3914
  }
3387
3915
  }
3388
3916
  makeScreenshot() {
@@ -3454,14 +3982,14 @@
3454
3982
  super.dispatchEvent(eventName, event);
3455
3983
  }
3456
3984
  destroy() {
3457
- var _a, _b, _c;
3985
+ var _a, _b, _c, _d;
3458
3986
  this._logger.warning(this, "Destroying library instance, bye, bye!");
3459
3987
  if (this.DEV_MODE && 'StormStreamerArray' in window) window.StormStreamerArray[this._streamerID] = null;
3460
3988
  this._initialized = false;
3461
3989
  this._isRemoved = true;
3462
- (_a = this._networkController) === null || _a === void 0 ? void 0 : _a.getConnection().destroy();
3463
- (_b = this._stageController) === null || _b === void 0 ? void 0 : _b.destroy();
3990
+ (_b = (_a = this._networkController) === null || _a === void 0 ? void 0 : _a.getConnection()) === null || _b === void 0 ? void 0 : _b.destroy();
3464
3991
  (_c = this._playbackController) === null || _c === void 0 ? void 0 : _c.destroy();
3992
+ (_d = this._stageController) === null || _d === void 0 ? void 0 : _d.destroy();
3465
3993
  this.removeAllEventListeners();
3466
3994
  }
3467
3995
  }