@ikonai/sdk 1.10.0 → 1.11.2

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 (210) hide show
  1. package/app-messaging.d.ts +3 -2
  2. package/app-messaging.d.ts.map +1 -0
  3. package/assets/audio-playback-worker-BA5LbdyM.js +1 -0
  4. package/assets/protocol-worker-Byc-OmNW.js +3 -0
  5. package/assets/video-capture-worker-DRYxkwR0.js +1 -0
  6. package/assets/video-playback-worker-v179IJ-C.js +1 -0
  7. package/assets/viseme-worker-CmvzHAbf.js +1 -0
  8. package/audio-constants-0i9B4zLJ.js +4 -0
  9. package/backend/account-functions.d.ts +22 -0
  10. package/backend/account-functions.d.ts.map +1 -0
  11. package/backend/user-account.d.ts +58 -0
  12. package/backend/user-account.d.ts.map +1 -0
  13. package/capabilities-ljRyAKpA.js +248 -0
  14. package/capture/capture-functions.d.ts +34 -0
  15. package/capture/capture-functions.d.ts.map +1 -0
  16. package/capture/view-capture.d.ts +35 -0
  17. package/capture/view-capture.d.ts.map +1 -0
  18. package/channel/channel-manager.d.ts +3 -2
  19. package/channel/channel-manager.d.ts.map +1 -0
  20. package/channel/channel.d.ts +6 -2
  21. package/channel/channel.d.ts.map +1 -0
  22. package/channel/index.d.ts +1 -0
  23. package/channel/index.d.ts.map +1 -0
  24. package/channel-manager-ehgy3BJp.js +878 -0
  25. package/client/connection-state.d.ts +1 -0
  26. package/client/connection-state.d.ts.map +1 -0
  27. package/client/endpoint-selector.d.ts +2 -1
  28. package/client/endpoint-selector.d.ts.map +1 -0
  29. package/client/ikon-client-config.d.ts +14 -1
  30. package/client/ikon-client-config.d.ts.map +1 -0
  31. package/client/ikon-client.d.ts +135 -6
  32. package/client/ikon-client.d.ts.map +1 -0
  33. package/connection/authenticator.d.ts +6 -1
  34. package/connection/authenticator.d.ts.map +1 -0
  35. package/connection/urls.d.ts +9 -0
  36. package/connection/urls.d.ts.map +1 -0
  37. package/consent/consent-api.d.ts +61 -0
  38. package/consent/consent-api.d.ts.map +1 -0
  39. package/dist-BrkppKKs.js +801 -0
  40. package/errors/index.d.ts +11 -0
  41. package/errors/index.d.ts.map +1 -0
  42. package/feedback/feedback-api.d.ts +63 -0
  43. package/feedback/feedback-api.d.ts.map +1 -0
  44. package/functions/browser-functions.d.ts +2 -1
  45. package/functions/browser-functions.d.ts.map +1 -0
  46. package/functions/download-functions.d.ts +12 -0
  47. package/functions/download-functions.d.ts.map +1 -0
  48. package/functions/function-registry.d.ts +2 -1
  49. package/functions/function-registry.d.ts.map +1 -0
  50. package/functions/index.d.ts +2 -1
  51. package/functions/index.d.ts.map +1 -0
  52. package/functions/media-capture-functions.d.ts +1 -0
  53. package/functions/media-capture-functions.d.ts.map +1 -0
  54. package/functions/sound-functions.d.ts +3 -1
  55. package/functions/sound-functions.d.ts.map +1 -0
  56. package/functions/type-conversion.d.ts +1 -0
  57. package/functions/type-conversion.d.ts.map +1 -0
  58. package/functions/types.d.ts +2 -1
  59. package/functions/types.d.ts.map +1 -0
  60. package/ikon-media-BQjL59S_.js +2074 -0
  61. package/ikon-media-capture-4aEsbGgC.js +1422 -0
  62. package/index.d.ts +23 -10
  63. package/index.d.ts.map +1 -0
  64. package/index.js +6774 -10104
  65. package/log-type-DewKVFaU.js +832 -0
  66. package/logger-gjegmH6K.js +80 -0
  67. package/login/index.d.ts +1 -0
  68. package/login/index.d.ts.map +1 -0
  69. package/login/login-error-interceptor.d.ts +1 -0
  70. package/login/login-error-interceptor.d.ts.map +1 -0
  71. package/login/login-store.d.ts +1 -0
  72. package/login/login-store.d.ts.map +1 -0
  73. package/login/reprovision.d.ts +1 -0
  74. package/login/reprovision.d.ts.map +1 -0
  75. package/media/audio-constants.d.ts +1 -0
  76. package/media/audio-constants.d.ts.map +1 -0
  77. package/media/capabilities.d.ts +1 -0
  78. package/media/capabilities.d.ts.map +1 -0
  79. package/media/device-detection.d.ts +1 -0
  80. package/media/device-detection.d.ts.map +1 -0
  81. package/media/ikon-audio-capture.d.ts +1 -0
  82. package/media/ikon-audio-capture.d.ts.map +1 -0
  83. package/media/ikon-audio-playback.d.ts +2 -1
  84. package/media/ikon-audio-playback.d.ts.map +1 -0
  85. package/media/ikon-image-capture.d.ts +1 -0
  86. package/media/ikon-image-capture.d.ts.map +1 -0
  87. package/media/ikon-media-capture.d.ts +1 -0
  88. package/media/ikon-media-capture.d.ts.map +1 -0
  89. package/media/ikon-media.d.ts +1 -0
  90. package/media/ikon-media.d.ts.map +1 -0
  91. package/media/ikon-video-capture.d.ts +1 -0
  92. package/media/ikon-video-capture.d.ts.map +1 -0
  93. package/media/ikon-video-playback.d.ts +2 -1
  94. package/media/ikon-video-playback.d.ts.map +1 -0
  95. package/media/index.d.ts +1 -0
  96. package/media/index.d.ts.map +1 -0
  97. package/media/media-permission.d.ts +1 -0
  98. package/media/media-permission.d.ts.map +1 -0
  99. package/media/media-session-helper.d.ts +1 -0
  100. package/media/media-session-helper.d.ts.map +1 -0
  101. package/media/ring-buffer.d.ts +1 -0
  102. package/media/ring-buffer.d.ts.map +1 -0
  103. package/media/video-constants.d.ts +1 -0
  104. package/media/video-constants.d.ts.map +1 -0
  105. package/media/viseme-tracker.d.ts +2 -1
  106. package/media/viseme-tracker.d.ts.map +1 -0
  107. package/media-session-helper-Do-OxyU6.js +51 -0
  108. package/package.json +1 -1
  109. package/reactive/reactive-registry.d.ts +1 -0
  110. package/reactive/reactive-registry.d.ts.map +1 -0
  111. package/sha2-DPTGfEM2.js +261 -0
  112. package/storage.d.ts +1 -0
  113. package/storage.d.ts.map +1 -0
  114. package/transport/http-stream-transport.d.ts +16 -1
  115. package/transport/http-stream-transport.d.ts.map +1 -0
  116. package/transport/keepalive-watchdog.d.ts +19 -0
  117. package/transport/keepalive-watchdog.d.ts.map +1 -0
  118. package/transport/message-framer.d.ts +1 -0
  119. package/transport/message-framer.d.ts.map +1 -0
  120. package/transport/transport-probe.d.ts +2 -1
  121. package/transport/transport-probe.d.ts.map +1 -0
  122. package/transport/transport.d.ts +9 -1
  123. package/transport/transport.d.ts.map +1 -0
  124. package/transport/web-socket-transport.d.ts +3 -1
  125. package/transport/web-socket-transport.d.ts.map +1 -0
  126. package/transport/web-transport-support.d.ts +5 -0
  127. package/transport/web-transport-support.d.ts.map +1 -0
  128. package/transport/web-transport-transport.d.ts +3 -8
  129. package/transport/web-transport-transport.d.ts.map +1 -0
  130. package/upload/file-upload.d.ts +62 -0
  131. package/upload/file-upload.d.ts.map +1 -0
  132. package/utils/abort-signal.d.ts +1 -0
  133. package/utils/abort-signal.d.ts.map +1 -0
  134. package/utils/app-routes.d.ts +1 -0
  135. package/utils/app-routes.d.ts.map +1 -0
  136. package/utils/boot-signal.d.ts +1 -0
  137. package/utils/boot-signal.d.ts.map +1 -0
  138. package/utils/browser-compat.d.ts +1 -0
  139. package/utils/browser-compat.d.ts.map +1 -0
  140. package/utils/browser-support.d.ts +1 -0
  141. package/utils/browser-support.d.ts.map +1 -0
  142. package/utils/client-identity.d.ts +1 -0
  143. package/utils/client-identity.d.ts.map +1 -0
  144. package/utils/connect-timeline.d.ts +1 -0
  145. package/utils/connect-timeline.d.ts.map +1 -0
  146. package/utils/debug-mode.d.ts +1 -0
  147. package/utils/debug-mode.d.ts.map +1 -0
  148. package/utils/deferred.d.ts +1 -0
  149. package/utils/deferred.d.ts.map +1 -0
  150. package/utils/delay.d.ts +1 -0
  151. package/utils/delay.d.ts.map +1 -0
  152. package/utils/embedded-oauth.d.ts +1 -0
  153. package/utils/embedded-oauth.d.ts.map +1 -0
  154. package/utils/external-link.d.ts +1 -0
  155. package/utils/external-link.d.ts.map +1 -0
  156. package/utils/inspect-mode.d.ts +1 -0
  157. package/utils/inspect-mode.d.ts.map +1 -0
  158. package/utils/log-sender.d.ts +1 -0
  159. package/utils/log-sender.d.ts.map +1 -0
  160. package/utils/log-sink.d.ts +1 -0
  161. package/utils/log-sink.d.ts.map +1 -0
  162. package/utils/logger.d.ts +1 -0
  163. package/utils/logger.d.ts.map +1 -0
  164. package/utils/oauth-start-url.d.ts +22 -0
  165. package/utils/oauth-start-url.d.ts.map +1 -0
  166. package/utils/opcode-names.d.ts +1 -0
  167. package/utils/opcode-names.d.ts.map +1 -0
  168. package/utils/parent-log-forward.d.ts +1 -0
  169. package/utils/parent-log-forward.d.ts.map +1 -0
  170. package/utils/pkce.d.ts +1 -0
  171. package/utils/pkce.d.ts.map +1 -0
  172. package/utils/platform.d.ts +1 -0
  173. package/utils/platform.d.ts.map +1 -0
  174. package/utils/query-params.d.ts +21 -0
  175. package/utils/query-params.d.ts.map +1 -0
  176. package/utils/user-gesture.d.ts +1 -0
  177. package/utils/user-gesture.d.ts.map +1 -0
  178. package/utils/uuid.d.ts +1 -0
  179. package/utils/uuid.d.ts.map +1 -0
  180. package/viseme-tracker-CAtczW00.js +183 -0
  181. package/web-transport-support-Cdyk4mQe.js +229 -0
  182. package/webrtc/index.d.ts +1 -0
  183. package/webrtc/index.d.ts.map +1 -0
  184. package/webrtc/webrtc-config.d.ts +1 -0
  185. package/webrtc/webrtc-config.d.ts.map +1 -0
  186. package/webrtc/webrtc-connection.d.ts +10 -0
  187. package/webrtc/webrtc-connection.d.ts.map +1 -0
  188. package/webrtc/webrtc-reachability.d.ts +1 -0
  189. package/webrtc/webrtc-reachability.d.ts.map +1 -0
  190. package/webrtc/webrtc-signaling.d.ts +12 -1
  191. package/webrtc/webrtc-signaling.d.ts.map +1 -0
  192. package/worker/audio-capture-worker.d.ts +1 -0
  193. package/worker/audio-capture-worker.d.ts.map +1 -0
  194. package/worker/audio-playback-worker.d.ts +2 -1
  195. package/worker/audio-playback-worker.d.ts.map +1 -0
  196. package/worker/protocol-worker.d.ts +1 -0
  197. package/worker/protocol-worker.d.ts.map +1 -0
  198. package/worker/video-capture-worker.d.ts +1 -0
  199. package/worker/video-capture-worker.d.ts.map +1 -0
  200. package/worker/video-playback-worker.d.ts +2 -1
  201. package/worker/video-playback-worker.d.ts.map +1 -0
  202. package/worker/viseme-worker.d.ts +2 -1
  203. package/worker/viseme-worker.d.ts.map +1 -0
  204. package/assets/audio-playback-worker-D9_GcJw4.js +0 -1
  205. package/assets/protocol-worker-DA6wieqV.js +0 -3
  206. package/assets/video-capture-worker-DLndrZf8.js +0 -1
  207. package/assets/video-playback-worker-DAAVfeBm.js +0 -1
  208. package/assets/viseme-worker-Bfd95xP6.js +0 -1
  209. package/capabilities-lcsAlVUh.js +0 -1146
  210. package/vite.config.d.ts +0 -2
@@ -0,0 +1,1422 @@
1
+ import { c as e, n as t } from "./logger-gjegmH6K.js";
2
+ import { _ as n, d as r, m as i, n as a, r as o, s, t as c } from "./capabilities-ljRyAKpA.js";
3
+ //#region src/media/ring-buffer.ts
4
+ var l = class {
5
+ static getStorageForCapacity(e, t) {
6
+ let n = Int32Array.BYTES_PER_ELEMENT * 2, r = t.BYTES_PER_ELEMENT * e;
7
+ return new SharedArrayBuffer(n + r);
8
+ }
9
+ readWrite;
10
+ storage;
11
+ buf;
12
+ constructor(e, t) {
13
+ this.buf = e, this.readWrite = new Int32Array(e, 0, 2);
14
+ let n = Int32Array.BYTES_PER_ELEMENT * 2, r = (e.byteLength - n) / Float32Array.BYTES_PER_ELEMENT;
15
+ this.storage = new t(e, n, r);
16
+ }
17
+ get capacity() {
18
+ return this.storage.length;
19
+ }
20
+ flush() {
21
+ Atomics.store(this.readWrite, 0, 0), Atomics.store(this.readWrite, 1, 0);
22
+ }
23
+ availableRead() {
24
+ let e = Atomics.load(this.readWrite, 0), t = Atomics.load(this.readWrite, 1);
25
+ return t >= e ? t - e : this.capacity - (e - t);
26
+ }
27
+ availableWrite() {
28
+ return this.capacity - this.availableRead() - 1;
29
+ }
30
+ push(e) {
31
+ let t = Math.min(this.availableWrite(), e.length);
32
+ if (t <= 0) return 0;
33
+ let n = Atomics.load(this.readWrite, 1), r = Math.min(t, this.capacity - n);
34
+ this.storage.set(e.subarray(0, r), n);
35
+ let i = t - r;
36
+ return i > 0 && this.storage.set(e.subarray(r, r + i), 0), n = (n + t) % this.capacity, Atomics.store(this.readWrite, 1, n), Atomics.notify(this.readWrite, 1), t;
37
+ }
38
+ pop(e) {
39
+ let t = Math.min(this.availableRead(), e.length);
40
+ if (t <= 0) return 0;
41
+ let n = Atomics.load(this.readWrite, 0), r = Math.min(t, this.capacity - n);
42
+ e.set(this.storage.subarray(n, n + r), 0);
43
+ let i = t - r;
44
+ return i > 0 && e.set(this.storage.subarray(0, i), r), n = (n + t) % this.capacity, Atomics.store(this.readWrite, 0, n), t;
45
+ }
46
+ skip(e) {
47
+ let t = Math.min(this.availableRead(), e);
48
+ if (t <= 0) return 0;
49
+ let n = Atomics.load(this.readWrite, 0);
50
+ return n = (n + t) % this.capacity, Atomics.store(this.readWrite, 0, n), t;
51
+ }
52
+ };
53
+ //#endregion
54
+ //#region src/worker/audio-capture-worker.ts?worker
55
+ function u(e) {
56
+ return new Worker(new URL("assets/audio-capture-worker-CJ5S-nQt.js", import.meta.url).href, {
57
+ type: "module",
58
+ name: e?.name
59
+ });
60
+ }
61
+ //#endregion
62
+ //#region src/media/ikon-audio-capture.ts
63
+ var d = t("IkonAudioCapture"), f = 3, p = 200, m = 2e3, h = 5e3, g = 3e3;
64
+ function _(e) {
65
+ return new Promise((t) => setTimeout(t, e));
66
+ }
67
+ function v(e) {
68
+ switch (e.name) {
69
+ case "NotReadableError": return {
70
+ action: "retry",
71
+ reason: "device-busy",
72
+ canRetry: !0
73
+ };
74
+ case "AbortError":
75
+ case "TrackStartError": return {
76
+ action: "retry",
77
+ reason: "unknown",
78
+ canRetry: !0
79
+ };
80
+ case "OverconstrainedError": return {
81
+ action: "fallback-constraints",
82
+ reason: "unknown",
83
+ canRetry: !0
84
+ };
85
+ case "NotAllowedError":
86
+ case "SecurityError": return {
87
+ action: "fail",
88
+ reason: "permission-denied",
89
+ canRetry: !1
90
+ };
91
+ case "NotFoundError": return {
92
+ action: "fail",
93
+ reason: "no-device",
94
+ canRetry: !1
95
+ };
96
+ default: return {
97
+ action: "fail",
98
+ reason: "unknown",
99
+ canRetry: !1
100
+ };
101
+ }
102
+ }
103
+ function y() {
104
+ let e = globalThis.crypto;
105
+ return e?.randomUUID ? e.randomUUID() : `cap_${Date.now()}_${Math.floor(Math.random() * 1e9)}`;
106
+ }
107
+ function b() {
108
+ return "\nclass RingBuffer {\n static getStorageForCapacity(capacity, type) {\n const indexBytes = Int32Array.BYTES_PER_ELEMENT * 2;\n const dataBytes = type.BYTES_PER_ELEMENT * capacity;\n return new SharedArrayBuffer(indexBytes + dataBytes);\n }\n\n constructor(sharedArrayBuffer, type) {\n this.buf = sharedArrayBuffer;\n this.readWrite = new Int32Array(sharedArrayBuffer, 0, 2);\n const dataOffset = Int32Array.BYTES_PER_ELEMENT * 2;\n const dataLength = (sharedArrayBuffer.byteLength - dataOffset) / Float32Array.BYTES_PER_ELEMENT;\n this.storage = new type(sharedArrayBuffer, dataOffset, dataLength);\n }\n\n get capacity() {\n return this.storage.length;\n }\n\n available_read() {\n const read = Atomics.load(this.readWrite, 0);\n const write = Atomics.load(this.readWrite, 1);\n if (write >= read) return write - read;\n return this.capacity - (read - write);\n }\n\n available_write() {\n return this.capacity - this.available_read() - 1;\n }\n\n push(input) {\n const writable = Math.min(this.available_write(), input.length);\n if (writable <= 0) return 0;\n\n const read = Atomics.load(this.readWrite, 0);\n let write = Atomics.load(this.readWrite, 1);\n\n const firstPart = Math.min(writable, this.capacity - write);\n this.storage.set(input.subarray(0, firstPart), write);\n const remaining = writable - firstPart;\n if (remaining > 0) {\n this.storage.set(input.subarray(firstPart, firstPart + remaining), 0);\n }\n\n write = (write + writable) % this.capacity;\n Atomics.store(this.readWrite, 1, write);\n Atomics.notify(this.readWrite, 1);\n if (this.available_read() > this.capacity - 2) {\n Atomics.store(this.readWrite, 0, read);\n }\n\n return writable;\n }\n}\n\nconst RENDER_QUANTUM_SIZE = 128;\n\nclass IkonAudioCaptureProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const cfg = options?.processorOptions || {};\n this.channels = cfg.channels || 1;\n this.transport = cfg.transport || 'pcm';\n this.ring = null;\n this.temp = new Float32Array(RENDER_QUANTUM_SIZE * this.channels);\n\n if (this.transport === 'sab' && cfg.sharedArrayBuffer) {\n this.ring = new RingBuffer(cfg.sharedArrayBuffer, Float32Array);\n }\n\n // Pre-allocated rotating transfer buffers for PCM mode\n // We use two buffers and alternate between them since postMessage transfers ownership\n this.transferBuffers = [\n new Float32Array(RENDER_QUANTUM_SIZE),\n new Float32Array(RENDER_QUANTUM_SIZE)\n ];\n this.transferBufferIndex = 0;\n\n this.port.postMessage({ type: 'ready' });\n }\n\n process(inputs) {\n const input = inputs[0];\n if (!input || input.length === 0) {\n return true;\n }\n\n const frames = input[0]?.length || 0;\n if (frames === 0) {\n return true;\n }\n\n const channelCount = input.length;\n for (let i = 0; i < frames; i++) {\n let sum = 0;\n for (let ch = 0; ch < channelCount; ch++) {\n const channel = input[ch];\n sum += channel ? (channel[i] || 0) : 0;\n }\n this.temp[i] = sum / channelCount;\n }\n\n const out = this.temp.subarray(0, frames);\n if (this.transport === 'sab' && this.ring) {\n this.ring.push(out);\n return true;\n }\n\n // Use rotating pre-allocated transfer buffer\n let buffer = this.transferBuffers[this.transferBufferIndex];\n // If buffer was transferred (detached), recreate it\n if (buffer.byteLength === 0) {\n buffer = new Float32Array(RENDER_QUANTUM_SIZE);\n this.transferBuffers[this.transferBufferIndex] = buffer;\n }\n buffer.set(out);\n const toSend = buffer.subarray(0, frames);\n this.port.postMessage({ type: 'pcm', pcm: toSend }, [buffer.buffer]);\n // Flip to next buffer for next call\n this.transferBufferIndex = (this.transferBufferIndex + 1) % 2;\n return true;\n }\n}\n\nregisterProcessor('ikon-audio-capture', IkonAudioCaptureProcessor);\n";
109
+ }
110
+ var x = class t {
111
+ client;
112
+ static RESUME_TIMEOUT_MS = 3e3;
113
+ worker = null;
114
+ sendPort = null;
115
+ workletModuleUrl = null;
116
+ workletReady = null;
117
+ workletContext = null;
118
+ audioContext = null;
119
+ nextTrackId = 1;
120
+ captures = /* @__PURE__ */ new Map();
121
+ keepAliveSource = null;
122
+ keepAliveGain = null;
123
+ pendingCaptures = /* @__PURE__ */ new Map();
124
+ activeWebRtcCaptureInvalidate = null;
125
+ config;
126
+ deviceChangeHandler = null;
127
+ constructor(e, t) {
128
+ this.client = e, this.config = t ?? {}, this.setupDeviceChangeListener();
129
+ }
130
+ emitDiagnostic(e) {
131
+ if (this.config.onDiagnosticEvent) try {
132
+ this.config.onDiagnosticEvent(e);
133
+ } catch {}
134
+ }
135
+ async isMicrophoneAvailable(e) {
136
+ if (!navigator.mediaDevices?.enumerateDevices) return !0;
137
+ try {
138
+ let t = (await navigator.mediaDevices.enumerateDevices()).filter((e) => e.kind === "audioinput");
139
+ return e ? t.some((t) => t.deviceId === e) : t.length > 0;
140
+ } catch {
141
+ return !0;
142
+ }
143
+ }
144
+ setupDeviceChangeListener() {
145
+ navigator.mediaDevices?.addEventListener && (this.deviceChangeHandler = () => {
146
+ this.handleDeviceChange();
147
+ }, navigator.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler));
148
+ }
149
+ removeDeviceChangeListener() {
150
+ this.deviceChangeHandler && navigator.mediaDevices?.removeEventListener && (navigator.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler), this.deviceChangeHandler = null);
151
+ }
152
+ extractDeviceId(e) {
153
+ if (!e?.deviceId) return null;
154
+ let t = e.deviceId;
155
+ return typeof t == "string" ? t : typeof t == "object" && "exact" in t ? String(t.exact) : typeof t == "object" && "ideal" in t ? String(t.ideal) : null;
156
+ }
157
+ async handleDeviceChange() {
158
+ if (this.captures.size === 0) return;
159
+ let e = Array.from(this.captures.values());
160
+ this.emitDiagnostic({
161
+ event: "device-change",
162
+ details: { captureCount: e.length }
163
+ });
164
+ for (let t of e) {
165
+ if (t.stopped) continue;
166
+ let e = t.intendedDeviceId ?? t.track.getSettings().deviceId, n = await this.isMicrophoneAvailable(e ?? void 0);
167
+ t.stopped || n || (d.warn(`Microphone device disconnected, deviceId=${e?.substring(0, 8) ?? "default"}`), this.config.deviceDisconnectPolicy?.autoStop ?? !0 ? this.handleRecoverableError(t, /* @__PURE__ */ Error("Microphone device disconnected"), "device-disconnected") : this.notifyError(t, /* @__PURE__ */ Error("Microphone device disconnected"), "device-disconnected", !0));
168
+ }
169
+ }
170
+ notifyError(e, t, n, r) {
171
+ if (e.stopped) return;
172
+ let i = Date.now(), a = t.name;
173
+ if (e.lastErrorReason === n && e.lastErrorName === a && i - e.lastErrorTime < h) return;
174
+ e.lastErrorTime = i, e.lastErrorReason = n, e.lastErrorName = a;
175
+ let o = this.config.autoRestartPolicy;
176
+ if (o?.enabled && r) {
177
+ let r = o.restartableReasons ?? ["track-ended", "device-disconnected"], a = o.maxRestarts ?? 3, s = o.backoffMs ?? [
178
+ 500,
179
+ 1e3,
180
+ 2e3
181
+ ];
182
+ if (i - e.lastStableTime > 3e4 && (e.restartCount = 0), r.includes(n) && e.restartCount < a) {
183
+ let r = s[Math.min(e.restartCount, s.length - 1)];
184
+ if (e.restartCount++, d.info(`Auto-restarting capture (attempt ${e.restartCount}/${a}) in ${r}ms, reason=${n}`), this.emitDiagnostic({
185
+ event: "auto-restart",
186
+ captureId: e.captureId,
187
+ details: {
188
+ attempt: e.restartCount,
189
+ maxRestarts: a,
190
+ delayMs: r,
191
+ reason: n
192
+ }
193
+ }), e.onError) try {
194
+ e.onError(t, n, !0);
195
+ } catch {}
196
+ e.pendingRestartTimer && clearTimeout(e.pendingRestartTimer);
197
+ let i = e.restartCount, o = e.onError, c = {
198
+ ...e.originalRequest,
199
+ constraints: e.currentConstraints,
200
+ options: e.currentOptions,
201
+ onError: e.onError,
202
+ onStateChange: e.onStateChange
203
+ };
204
+ e.pendingRestartTimer = setTimeout(() => {
205
+ e.pendingRestartTimer = null, !(!e.stopped || e.stoppedReason !== "recoverable-error") && this.startMic(c).then((e) => {
206
+ let t = this.captures.get(e.captureId);
207
+ t && (t.restartCount = i, t.lastStableTime = Date.now());
208
+ }).catch((e) => {
209
+ d.warn(`Auto-restart failed: ${e}`);
210
+ let t = e instanceof Error ? e : Error(String(e)), n = "unknown";
211
+ if (t.name === "NotAllowedError" && (n = "permission-revoked"), o) try {
212
+ o(/* @__PURE__ */ Error(`Auto-restart failed: ${t.message}`), n, i < a && n !== "permission-revoked");
213
+ } catch {}
214
+ });
215
+ }, r);
216
+ return;
217
+ }
218
+ }
219
+ if (e.onError) try {
220
+ e.onError(t, n, r);
221
+ } catch {}
222
+ }
223
+ handleRecoverableError(e, t, n) {
224
+ this.notifyError(e, t, n, !0), e.stop("recoverable-error");
225
+ }
226
+ async attemptResumeAsync(e, t, n = !1) {
227
+ if (e.stopped) return !1;
228
+ if (e.ctx.state === "running") return !0;
229
+ if (e.ctx.state !== "suspended") return !1;
230
+ let r = Date.now();
231
+ if (!n && r - e.lastResumeAttemptTime < g) return d.debug(`Resume attempt from ${t} skipped (cooldown)`), !1;
232
+ e.lastResumeAttemptTime = r, this.emitDiagnostic({
233
+ event: "resume-attempt",
234
+ captureId: e.captureId,
235
+ details: {
236
+ source: t,
237
+ isUserGesture: n
238
+ }
239
+ });
240
+ try {
241
+ await this.resumeWithTimeout(e.ctx);
242
+ let n = e.ctx.state, r = n === "running";
243
+ return this.emitDiagnostic({
244
+ event: "resume-result",
245
+ captureId: e.captureId,
246
+ details: {
247
+ source: t,
248
+ success: r,
249
+ state: n
250
+ }
251
+ }), r;
252
+ } catch (r) {
253
+ return d.warn(`Failed to resume AudioContext from ${t}: ${r}`), this.emitDiagnostic({
254
+ event: "resume-result",
255
+ captureId: e.captureId,
256
+ details: {
257
+ source: t,
258
+ success: !1,
259
+ error: String(r)
260
+ }
261
+ }), n && this.notifyError(e, /* @__PURE__ */ Error("AudioContext suspended - user gesture required"), "context-suspended", !0), !1;
262
+ }
263
+ }
264
+ attemptResume(e, t, n = !1) {
265
+ if (e.stopped || e.ctx.state !== "suspended") return;
266
+ let r = Date.now();
267
+ if (!n && r - e.lastResumeAttemptTime < g) {
268
+ d.debug(`Resume attempt from ${t} skipped (cooldown)`);
269
+ return;
270
+ }
271
+ e.lastResumeAttemptTime = r, d.info(`Attempting AudioContext resume from ${t}`), e.ctx.resume().then(() => {
272
+ d.debug("AudioContext resumed successfully");
273
+ }).catch((t) => {
274
+ d.warn(`Failed to resume AudioContext: ${t}`), n && this.notifyError(e, /* @__PURE__ */ Error("AudioContext suspended - user gesture required"), "context-suspended", !0);
275
+ });
276
+ }
277
+ setupAudioContextStateListener(e, t) {
278
+ let n = () => {
279
+ if (t.stopped) return;
280
+ let n = e.state;
281
+ if (d.debug(`AudioContext state changed to: ${n}`), this.emitDiagnostic({
282
+ event: "state-change",
283
+ captureId: t.captureId,
284
+ details: { state: n }
285
+ }), t.onStateChange) try {
286
+ t.onStateChange(n);
287
+ } catch {}
288
+ n === "suspended" && (this.attemptResume(t, "statechange"), this.notifyError(t, /* @__PURE__ */ Error("AudioContext suspended - user gesture may be required"), "context-suspended", !0)), n === "interrupted" && (d.info("AudioContext interrupted (iOS), waiting for user to return"), this.notifyError(t, /* @__PURE__ */ Error("Audio interrupted - waiting for user to return"), "context-interrupted", !0));
289
+ };
290
+ e.addEventListener("statechange", n), t.stateChangeHandler = n;
291
+ }
292
+ removeAudioContextStateListener(e, t) {
293
+ t.stateChangeHandler &&= (e.removeEventListener("statechange", t.stateChangeHandler), null);
294
+ }
295
+ setupVisibilityChangeListener(e) {
296
+ let t = () => {
297
+ document.visibilityState === "hidden" && !e.stopped ? d.debug("Page hidden - audio may be suspended soon") : document.visibilityState === "visible" && !e.stopped && this.attemptResume(e, "visibilitychange");
298
+ };
299
+ document.addEventListener("visibilitychange", t), e.visibilityHandler = t;
300
+ }
301
+ removeVisibilityChangeListener(e) {
302
+ e.visibilityHandler &&= (document.removeEventListener("visibilitychange", e.visibilityHandler), null);
303
+ }
304
+ setupBFCacheHandlers(e) {
305
+ let t = () => {
306
+ e.stopped || d.debug("Page hiding (BFCache) - audio may be suspended");
307
+ }, n = (t) => {
308
+ t.persisted && !e.stopped && (d.info("Page restored from BFCache - checking audio state"), e.ctx.state === "suspended" && this.attemptResume(e, "bfcache"));
309
+ };
310
+ window.addEventListener("pagehide", t), window.addEventListener("pageshow", n), e.pagehideHandler = t, e.pageshowHandler = n;
311
+ }
312
+ removeBFCacheHandlers(e) {
313
+ e.pagehideHandler &&= (window.removeEventListener("pagehide", e.pagehideHandler), null), e.pageshowHandler &&= (window.removeEventListener("pageshow", e.pageshowHandler), null);
314
+ }
315
+ allocateTrackId() {
316
+ let e = this.nextTrackId;
317
+ return this.nextTrackId += 1, e;
318
+ }
319
+ async startMicWebRtc(e) {
320
+ let t = this.client.webRtcSignaling;
321
+ if (!t) throw Error("WebRTC signaling is not connected");
322
+ let n = e.constraints ? { ...e.constraints } : void 0;
323
+ n && typeof n.deviceId == "string" && (n.deviceId = { exact: n.deviceId }), await t.startAudioCapture(n);
324
+ let r = "webrtc-audio", i = !0, a = e.constraints ?? {}, o = e.options ?? {};
325
+ t.onAudioCaptureError = (t) => {
326
+ if (!i || !e.onError) return;
327
+ let n = v(t);
328
+ try {
329
+ e.onError(t, n.reason === "permission-denied" ? "permission-revoked" : n.reason, n.canRetry);
330
+ } catch (e) {
331
+ d.warn(`onError callback threw: ${e}`);
332
+ }
333
+ };
334
+ let s = () => {
335
+ i && (i = !1, e.onStopped?.());
336
+ }, c = () => {
337
+ this.activeWebRtcCaptureInvalidate === s && (this.activeWebRtcCaptureInvalidate = null);
338
+ };
339
+ this.activeWebRtcCaptureInvalidate = s;
340
+ let l = {
341
+ captureId: r,
342
+ streamId: r,
343
+ get isActive() {
344
+ return i;
345
+ },
346
+ get currentConstraints() {
347
+ return a;
348
+ },
349
+ get currentOptions() {
350
+ return o;
351
+ },
352
+ startSegment(e) {
353
+ t.startAudioSegment(e);
354
+ },
355
+ endSegment(e) {
356
+ t.endAudioSegment(e);
357
+ },
358
+ async stop() {
359
+ i && (i = !1, c(), t.onAudioCaptureError = null, t.stopAudioCapture(), e.onStopped?.());
360
+ },
361
+ async updateConstraints() {
362
+ return !1;
363
+ },
364
+ updateOptions() {},
365
+ async resume() {
366
+ return !0;
367
+ },
368
+ async restart() {
369
+ return l;
370
+ }
371
+ };
372
+ return l;
373
+ }
374
+ ensureWorker() {
375
+ if (this.worker) return this.worker;
376
+ let e = new u();
377
+ return this.worker = e, e.addEventListener("message", (e) => {
378
+ let t = e.data;
379
+ if (t?.type !== "error") return;
380
+ let n = t.error, r = `${n?.name ?? "Error"} ${n?.message ?? ""}`.trim(), i = typeof t.captureId == "string" ? this.captures.get(t.captureId) : void 0;
381
+ if (!i || i.stopped) {
382
+ d.warn(`Audio capture worker error: ${r}`);
383
+ return;
384
+ }
385
+ d.error(`Audio capture worker error, stopping captureId=${i.captureId.substring(0, 8)}: ${r}`), this.handleRecoverableError(i, /* @__PURE__ */ Error(`Audio capture worker error: ${r}`), "unknown");
386
+ }), e.addEventListener("error", (t) => {
387
+ let n = [
388
+ t.message,
389
+ t.filename,
390
+ t.lineno,
391
+ t.colno
392
+ ].filter((e) => e != null && String(e).length > 0);
393
+ if (d.error(`Audio capture worker failed: ${n.join(" ")}`.trim()), this.worker === e) {
394
+ try {
395
+ e.terminate();
396
+ } catch {}
397
+ this.worker = null, this.sendPort?.close(), this.sendPort = null;
398
+ }
399
+ this.failActiveCapturesAfterWorkerCrash(Error(`Audio capture worker failed: ${n.join(" ")}`.trim()));
400
+ }), e;
401
+ }
402
+ failActiveCapturesAfterWorkerCrash(e) {
403
+ let t = this.client.sessionId;
404
+ for (let r of Array.from(this.captures.values())) r.stopped || (t && this.client.sendProtocolMessage(n(i({}), t, { trackId: r.trackId })), this.handleRecoverableError(r, e, "unknown"));
405
+ }
406
+ ensureSendPort() {
407
+ if (!this.client.sessionId) throw Error("Cannot start audio capture before client is connected");
408
+ let t = this.ensureWorker();
409
+ if (!this.sendPort) {
410
+ let n = this.client.createProtocolSendPort({ opcodeGroupsMask: e.GROUP_AUDIO });
411
+ if (!n) throw Error("Failed to create protocol send port for audio capture");
412
+ this.sendPort = n, t.postMessage({
413
+ type: "attachSendPort",
414
+ port: n.port
415
+ }, [n.port]);
416
+ }
417
+ }
418
+ startKeepAlive(e) {
419
+ if (!(this.keepAliveSource || e.state === "closed")) try {
420
+ let t = e.createGain();
421
+ t.gain.value = 0, t.connect(e.destination);
422
+ let n = e.createOscillator();
423
+ n.connect(t), n.start(), this.keepAliveGain = t, this.keepAliveSource = n, d.debug("Audio keep-alive started - capture will survive a backgrounded tab");
424
+ } catch (e) {
425
+ d.debug("Could not start the audio keep-alive", e), this.stopKeepAlive();
426
+ }
427
+ }
428
+ stopKeepAlive() {
429
+ try {
430
+ this.keepAliveSource?.stop();
431
+ } catch {}
432
+ try {
433
+ this.keepAliveSource?.disconnect(), this.keepAliveGain?.disconnect();
434
+ } catch {}
435
+ this.keepAliveSource = null, this.keepAliveGain = null;
436
+ }
437
+ async ensureAudioContext() {
438
+ if (typeof AudioContext > "u") throw Error("AudioContext is not available in this environment");
439
+ if (this.audioContext && this.audioContext.state !== "closed") {
440
+ let e = this.audioContext;
441
+ return e.state === "running" || await this.resumeWithTimeout(e), e;
442
+ }
443
+ let e = new AudioContext({ latencyHint: "interactive" });
444
+ return this.audioContext = e, e.state === "running" || await this.resumeWithTimeout(e), e;
445
+ }
446
+ async resumeWithTimeout(e) {
447
+ let n = new Promise((e, n) => {
448
+ setTimeout(() => n(/* @__PURE__ */ Error("AudioContext.resume() timed out")), t.RESUME_TIMEOUT_MS);
449
+ });
450
+ await Promise.race([e.resume(), n]);
451
+ }
452
+ async acquireMicrophoneStream(e) {
453
+ if (!navigator.mediaDevices) throw Error("Media devices are not available in this environment");
454
+ let t = {
455
+ autoGainControl: !1,
456
+ noiseSuppression: !1,
457
+ echoCancellation: !1
458
+ }, n = e.constraints ? {
459
+ ...t,
460
+ ...e.constraints
461
+ } : t;
462
+ typeof n.deviceId == "string" && (n.deviceId = { exact: n.deviceId });
463
+ let r = this.buildFallbackConstraints(n), i = await this.tryGetUserMediaWithRetries(n);
464
+ if (i.stream) return {
465
+ stream: i.stream,
466
+ constraints: n
467
+ };
468
+ if (i.errorInfo.action === "fallback-constraints") {
469
+ d.info(`Constraints failed (${i.error.name}), trying fallback constraints`), this.emitDiagnostic({
470
+ event: "fallback",
471
+ details: {
472
+ errorName: i.error.name,
473
+ originalConstraints: Object.keys(n)
474
+ }
475
+ });
476
+ let e = await this.tryGetUserMediaWithRetries(r);
477
+ if (e.stream) return d.info("getUserMedia succeeded with fallback constraints"), {
478
+ stream: e.stream,
479
+ constraints: r
480
+ };
481
+ let t = e.error;
482
+ throw t.captureReason = "constraints-failed", t;
483
+ }
484
+ let a = i.error;
485
+ throw a.captureReason = i.errorInfo.reason, a;
486
+ }
487
+ buildFallbackConstraints(e) {
488
+ let t = { ...e };
489
+ if (t.deviceId) {
490
+ let e = t.deviceId;
491
+ typeof e == "object" && "exact" in e && (t.deviceId = { ideal: e.exact });
492
+ }
493
+ return delete t.sampleRate, delete t.sampleSize, delete t.channelCount, t;
494
+ }
495
+ async tryGetUserMediaWithRetries(e) {
496
+ let t = /* @__PURE__ */ Error("No attempts made"), n = {
497
+ action: "fail",
498
+ reason: "unknown",
499
+ canRetry: !1
500
+ };
501
+ for (let r = 0; r < f; r++) try {
502
+ return {
503
+ stream: await navigator.mediaDevices.getUserMedia({ audio: e }),
504
+ error: t,
505
+ errorInfo: n
506
+ };
507
+ } catch (e) {
508
+ if (t = e instanceof Error ? e : Error(String(e)), n = v(t), d.debug(`getUserMedia attempt ${r + 1}/${f} failed: ${t.name} (action: ${n.action})`), this.emitDiagnostic({
509
+ event: "retry",
510
+ details: {
511
+ attempt: r + 1,
512
+ maxAttempts: f,
513
+ errorName: t.name,
514
+ action: n.action
515
+ }
516
+ }), n.action !== "retry") break;
517
+ if (r < 2) {
518
+ let e = p * (r + 1);
519
+ d.debug(`Retrying getUserMedia in ${e}ms`), await _(e);
520
+ }
521
+ }
522
+ return {
523
+ stream: null,
524
+ error: t,
525
+ errorInfo: n
526
+ };
527
+ }
528
+ async ensureWorklet(e) {
529
+ if (!c(e)) throw Error("AudioWorklet is not supported in this browser");
530
+ if (this.workletModuleUrl ||= URL.createObjectURL(new Blob([b()], { type: "text/javascript" })), this.workletReady && this.workletContext === e) try {
531
+ await this.workletReady;
532
+ return;
533
+ } catch {
534
+ this.workletReady = null;
535
+ }
536
+ this.workletContext = e;
537
+ let t = e.audioWorklet.addModule(this.workletModuleUrl);
538
+ this.workletReady = t;
539
+ try {
540
+ await t;
541
+ } catch (e) {
542
+ throw this.workletReady === t && (this.workletReady = null), e;
543
+ }
544
+ }
545
+ handleWebRtcTransportLost() {
546
+ if (this.activeWebRtcCaptureInvalidate) {
547
+ d.warn("WebRTC transport lost during active capture; invalidating the capture handle");
548
+ let e = this.activeWebRtcCaptureInvalidate;
549
+ this.activeWebRtcCaptureInvalidate = null, e();
550
+ }
551
+ }
552
+ async startMic(e = {}) {
553
+ let t = this.client.sessionId;
554
+ if (!t) throw Error("Cannot start audio capture before client is connected");
555
+ if (this.client.isWebRtcCaptureAvailable) return this.startMicWebRtc(e);
556
+ this.client.isWebRtcFirstConnecting ? d.debug("WebRTC still connecting, capturing audio via protocol channel") : this.client.isWebRtcEnabled && d.warn("WebRTC unavailable, capturing audio via protocol channel");
557
+ let n = y(), r = {
558
+ captureId: n,
559
+ aborted: !1,
560
+ stream: null,
561
+ source: null,
562
+ worklet: null,
563
+ gain: null
564
+ };
565
+ this.pendingCaptures.set(n, r);
566
+ let i = () => {
567
+ if (r.aborted) throw Error("Capture was aborted");
568
+ };
569
+ try {
570
+ this.ensureSendPort();
571
+ let s = await this.ensureAudioContext();
572
+ i();
573
+ let c, u;
574
+ try {
575
+ let t = await this.acquireMicrophoneStream(e);
576
+ c = t.stream, u = t.constraints, r.stream = c;
577
+ } catch (t) {
578
+ let n = t instanceof Error ? t : Error(String(t)), r = n.captureReason ?? "unknown";
579
+ if (e.onError) try {
580
+ e.onError(n, r, r !== "permission-denied" && r !== "no-device");
581
+ } catch {}
582
+ throw t;
583
+ }
584
+ i();
585
+ let f = c.getAudioTracks()[0];
586
+ if (!f) throw c.getTracks().forEach((e) => e.stop()), Error("No audio track available");
587
+ await this.ensureWorklet(s), i();
588
+ let p = this.allocateTrackId(), h = s.createMediaStreamSource(c);
589
+ r.source = h;
590
+ let g = s.createGain();
591
+ g.gain.value = 0, g.connect(s.destination), r.gain = g;
592
+ let _ = a() ? "sab" : "pcm", v;
593
+ if (_ === "sab") {
594
+ let e = Math.floor(s.sampleRate * 10);
595
+ v = l.getStorageForCapacity(e, Float32Array);
596
+ }
597
+ let y = this.ensureWorker(), b = e.onStopped;
598
+ y.postMessage({
599
+ type: "start",
600
+ captureId: n,
601
+ streamId: n,
602
+ sourceType: "microphone",
603
+ senderId: t,
604
+ trackId: p,
605
+ sampleRate: s.sampleRate,
606
+ channels: 1,
607
+ options: {
608
+ ...e.options,
609
+ preferWebCodecs: this.config.performance?.preferWebCodecs
610
+ },
611
+ transport: _,
612
+ sharedArrayBuffer: v,
613
+ correlationId: e.correlationId
614
+ }), await new Promise((e, t) => {
615
+ let r = setTimeout(() => {
616
+ y.removeEventListener("message", i), t(/* @__PURE__ */ Error("Audio capture worker start timeout"));
617
+ }, 1e4), i = (a) => {
618
+ let o = a.data;
619
+ if (o?.type === "started" && o.captureId === n) clearTimeout(r), y.removeEventListener("message", i), e();
620
+ else if (o?.type === "error" && o.captureId === n) {
621
+ clearTimeout(r), y.removeEventListener("message", i);
622
+ let e = o.error;
623
+ t(Error(e?.message ?? "Audio capture failed to start"));
624
+ }
625
+ };
626
+ y.addEventListener("message", i);
627
+ }), i();
628
+ let x = new AudioWorkletNode(s, "ikon-audio-capture", {
629
+ numberOfInputs: 1,
630
+ numberOfOutputs: 1,
631
+ outputChannelCount: [1],
632
+ processorOptions: {
633
+ transport: _,
634
+ channels: 1,
635
+ sharedArrayBuffer: v
636
+ }
637
+ });
638
+ r.worklet = x, x.onprocessorerror = () => {
639
+ d.error(`Audio capture worklet processor crashed (captureId=${n})`);
640
+ let e = this.captures.get(n);
641
+ e && !e.stopped && this.notifyError(e, /* @__PURE__ */ Error("Audio capture worklet processor crashed"), "unknown", !0);
642
+ }, h.connect(x), x.connect(g), _ === "pcm" && (x.port.onmessage = (e) => {
643
+ let t = e.data;
644
+ if (!(!t || typeof t != "object") && t.type === "pcm" && t.pcm instanceof Float32Array) {
645
+ let e = t.pcm;
646
+ y.postMessage({
647
+ type: "pcm",
648
+ captureId: n,
649
+ pcm: e
650
+ }, [e.buffer]);
651
+ }
652
+ });
653
+ let S = this.extractDeviceId(e.constraints), C = {
654
+ captureId: n,
655
+ trackId: p,
656
+ stream: c,
657
+ track: f,
658
+ ctx: s,
659
+ source: h,
660
+ worklet: x,
661
+ gain: g,
662
+ stopped: !1,
663
+ intendedDeviceId: S,
664
+ currentConstraints: u,
665
+ currentOptions: e.options ?? {},
666
+ healthCheckTimer: null,
667
+ visibilityHandler: null,
668
+ pagehideHandler: null,
669
+ pageshowHandler: null,
670
+ trackEndedHandler: null,
671
+ lastErrorTime: 0,
672
+ lastErrorReason: null,
673
+ lastErrorName: null,
674
+ lastResumeAttemptTime: 0,
675
+ restartCount: 0,
676
+ lastStableTime: Date.now(),
677
+ mutedSinceTime: null,
678
+ originalRequest: e,
679
+ stateChangeHandler: null,
680
+ pendingRestartTimer: null,
681
+ stoppedReason: null,
682
+ onError: e.onError,
683
+ onStateChange: e.onStateChange,
684
+ startSegment: (e) => {
685
+ C.stopped || y.postMessage({
686
+ type: "startSegment",
687
+ captureId: n
688
+ });
689
+ },
690
+ endSegment: (e) => {
691
+ C.stopped || (y.postMessage({
692
+ type: "endSegment",
693
+ captureId: n
694
+ }), o() && C.stop());
695
+ },
696
+ stop: async (e = "user") => {
697
+ if (!C.stopped) {
698
+ if (C.stoppedReason = e, C.stopped = !0, this.captures.delete(n), this.captures.size === 0 && this.stopKeepAlive(), C.healthCheckTimer &&= (clearInterval(C.healthCheckTimer), null), e !== "recoverable-error" && C.pendingRestartTimer && (clearTimeout(C.pendingRestartTimer), C.pendingRestartTimer = null), this.removeVisibilityChangeListener(C), this.removeBFCacheHandlers(C), C.trackEndedHandler) {
699
+ try {
700
+ C.track.removeEventListener("ended", C.trackEndedHandler);
701
+ } catch {}
702
+ C.trackEndedHandler = null;
703
+ }
704
+ this.removeAudioContextStateListener(s, C);
705
+ try {
706
+ h.disconnect();
707
+ } catch {}
708
+ try {
709
+ x.disconnect();
710
+ } catch {}
711
+ try {
712
+ g.disconnect();
713
+ } catch {}
714
+ try {
715
+ c.getTracks().forEach((e) => e.stop());
716
+ } catch {}
717
+ if (y.postMessage({
718
+ type: "stop",
719
+ captureId: n
720
+ }), b) try {
721
+ b();
722
+ } catch {}
723
+ }
724
+ },
725
+ updateConstraints: async (e) => {
726
+ if (C.stopped) return !1;
727
+ try {
728
+ return await C.track.applyConstraints(e), C.currentConstraints = e, !0;
729
+ } catch {
730
+ return !1;
731
+ }
732
+ },
733
+ updateOptions: (e) => {
734
+ C.stopped || (C.currentOptions = e, y.postMessage({
735
+ type: "updateOptions",
736
+ captureId: n,
737
+ options: e
738
+ }));
739
+ }
740
+ };
741
+ this.setupAudioContextStateListener(s, C), this.setupVisibilityChangeListener(C), this.setupBFCacheHandlers(C), C.healthCheckTimer = setInterval(() => {
742
+ this.performHealthCheck(C);
743
+ }, m);
744
+ let w = () => {
745
+ C.stopped || (d.warn(`Microphone track ended unexpectedly, captureId=${n.substring(0, 8)}`), this.handleRecoverableError(C, /* @__PURE__ */ Error("Microphone track ended unexpectedly"), "track-ended"));
746
+ };
747
+ return C.trackEndedHandler = w, f.addEventListener("ended", w), this.captures.set(n, C), this.startKeepAlive(C.ctx), this.pendingCaptures.delete(n), {
748
+ captureId: n,
749
+ streamId: n,
750
+ get isActive() {
751
+ return !C.stopped;
752
+ },
753
+ get currentConstraints() {
754
+ return C.currentConstraints;
755
+ },
756
+ get currentOptions() {
757
+ return C.currentOptions;
758
+ },
759
+ startSegment: C.startSegment,
760
+ endSegment: C.endSegment,
761
+ stop: C.stop,
762
+ updateConstraints: C.updateConstraints,
763
+ updateOptions: C.updateOptions,
764
+ resume: this.resumeContext.bind(this, n),
765
+ restart: (e) => this.restartCapture(n, e)
766
+ };
767
+ } finally {
768
+ let e = this.pendingCaptures.get(n);
769
+ if (this.pendingCaptures.delete(n), e?.aborted) {
770
+ if (e.source) try {
771
+ e.source.disconnect();
772
+ } catch {}
773
+ if (e.worklet) try {
774
+ e.worklet.disconnect();
775
+ } catch {}
776
+ if (e.gain) try {
777
+ e.gain.disconnect();
778
+ } catch {}
779
+ if (e.stream) try {
780
+ e.stream.getTracks().forEach((e) => e.stop());
781
+ } catch {}
782
+ }
783
+ }
784
+ }
785
+ async stop(e) {
786
+ let t = this.captures.get(e);
787
+ return t ? (await t.stop(), !0) : !1;
788
+ }
789
+ startSegment(e) {
790
+ let t = this.captures.get(e);
791
+ return t ? (t.startSegment(), !0) : !1;
792
+ }
793
+ endSegment(e) {
794
+ let t = this.captures.get(e);
795
+ return t ? (t.endSegment(), !0) : !1;
796
+ }
797
+ dispose() {
798
+ this.removeDeviceChangeListener();
799
+ for (let e of this.pendingCaptures.values()) {
800
+ if (e.aborted = !0, e.source) try {
801
+ e.source.disconnect();
802
+ } catch {}
803
+ if (e.worklet) try {
804
+ e.worklet.disconnect();
805
+ } catch {}
806
+ if (e.gain) try {
807
+ e.gain.disconnect();
808
+ } catch {}
809
+ if (e.stream) try {
810
+ e.stream.getTracks().forEach((e) => e.stop());
811
+ } catch {}
812
+ }
813
+ this.pendingCaptures.clear();
814
+ for (let e of this.captures.values()) e.stop("dispose");
815
+ if (this.captures.clear(), this.worker) {
816
+ try {
817
+ this.worker.postMessage({ type: "dispose" });
818
+ } catch {}
819
+ try {
820
+ this.worker.terminate();
821
+ } catch {}
822
+ this.worker = null;
823
+ }
824
+ if (this.sendPort?.close(), this.sendPort = null, this.stopKeepAlive(), this.audioContext) {
825
+ try {
826
+ this.audioContext.onstatechange = null, this.audioContext.close();
827
+ } catch {}
828
+ this.audioContext = null;
829
+ }
830
+ if (this.workletContext = null, this.workletReady = null, this.workletModuleUrl) {
831
+ try {
832
+ URL.revokeObjectURL(this.workletModuleUrl);
833
+ } catch {}
834
+ this.workletModuleUrl = null;
835
+ }
836
+ }
837
+ performHealthCheck(e) {
838
+ if (e.stopped) return;
839
+ if (e.track.readyState !== "live") {
840
+ d.warn(`Health check: track is ${e.track.readyState}, captureId=${e.captureId.substring(0, 8)}`), this.handleRecoverableError(e, /* @__PURE__ */ Error(`Microphone track is ${e.track.readyState}`), "track-ended");
841
+ return;
842
+ }
843
+ let t = e.ctx.state;
844
+ if (t === "closed") {
845
+ d.warn(`Health check: AudioContext closed, captureId=${e.captureId.substring(0, 8)}`), this.notifyError(e, /* @__PURE__ */ Error("AudioContext was closed"), "context-closed", !1), e.stop("dispose");
846
+ return;
847
+ }
848
+ t === "suspended" && this.attemptResume(e, "healthcheck"), e.track.readyState === "live" && t === "running" && (e.lastStableTime = Date.now());
849
+ let n = this.config.mutedInputDetection;
850
+ if (e.track.muted) {
851
+ if (d.debug(`Health check: track is muted, captureId=${e.captureId.substring(0, 8)}`), n?.enabled) {
852
+ let t = Date.now();
853
+ if (e.mutedSinceTime === null) e.mutedSinceTime = t;
854
+ else {
855
+ let r = t - e.mutedSinceTime;
856
+ r >= (n.durationMs ?? 5e3) && (d.warn(`Health check: input muted for ${r}ms, captureId=${e.captureId.substring(0, 8)}`), this.notifyError(e, /* @__PURE__ */ Error(`Microphone input muted for ${Math.round(r / 1e3)}s`), "input-muted", !0), e.mutedSinceTime = t);
857
+ }
858
+ }
859
+ } else e.mutedSinceTime = null;
860
+ }
861
+ getActiveCaptures() {
862
+ return Array.from(this.captures.values()).filter((e) => !e.stopped).map((e) => ({
863
+ captureId: e.captureId,
864
+ trackState: e.track.readyState,
865
+ contextState: e.ctx.state,
866
+ isMuted: e.track.muted,
867
+ intendedDeviceId: e.intendedDeviceId,
868
+ lastErrorReason: e.lastErrorReason,
869
+ lastErrorAt: e.lastErrorTime
870
+ }));
871
+ }
872
+ async restartCapture(e, t) {
873
+ let n = this.captures.get(e), r = n !== void 0, i = {
874
+ constraints: n?.currentConstraints,
875
+ options: n?.currentOptions,
876
+ onError: n?.onError,
877
+ onStateChange: n?.onStateChange,
878
+ ...t
879
+ };
880
+ n && !n.stopped && await n.stop("user");
881
+ try {
882
+ return await this.startMic(i);
883
+ } catch (e) {
884
+ let t = e instanceof Error ? e : Error(String(e));
885
+ throw r && t.name === "NotAllowedError" && (t.captureReason = "permission-revoked"), t;
886
+ }
887
+ }
888
+ async resumeContext(e) {
889
+ let t = this.captures.get(e);
890
+ return !t || t.stopped ? !1 : this.attemptResumeAsync(t, "userGesture", !0);
891
+ }
892
+ }, S = 3e3;
893
+ function C(e, t, n, r) {
894
+ if (!n && !r) return {
895
+ width: e,
896
+ height: t
897
+ };
898
+ let i = n ? n / e : Infinity, a = r ? r / t : Infinity, o = Math.min(i, a);
899
+ return {
900
+ width: Math.max(1, Math.floor(e * o)),
901
+ height: Math.max(1, Math.floor(t * o))
902
+ };
903
+ }
904
+ async function w(e, t) {
905
+ let n = t.mime ?? "image/jpeg", r = t.quality ?? .92;
906
+ if (typeof OffscreenCanvas < "u" && e instanceof OffscreenCanvas) try {
907
+ return await e.convertToBlob({
908
+ type: n,
909
+ quality: r
910
+ });
911
+ } catch {}
912
+ let i = e;
913
+ return await new Promise((e, t) => {
914
+ i.toBlob((n) => {
915
+ n ? e(n) : t(/* @__PURE__ */ Error("Canvas toBlob returned null"));
916
+ }, n, r);
917
+ });
918
+ }
919
+ var T = class {
920
+ async captureFromStream(e, t = {}) {
921
+ let n = e.getVideoTracks()[0];
922
+ if (!n) throw Error("No video track available in the provided stream");
923
+ let r = document.createElement("video");
924
+ r.style.position = "fixed", r.style.left = "-99999px", r.style.top = "0", r.muted = !0, r.playsInline = !0, r.autoplay = !0, r.srcObject = e;
925
+ try {
926
+ document.body.appendChild(r);
927
+ } catch {}
928
+ try {
929
+ await new Promise((e, t) => {
930
+ let n = setTimeout(() => t(/* @__PURE__ */ Error("Timed out waiting for video metadata")), S);
931
+ r.addEventListener("loadedmetadata", () => {
932
+ clearTimeout(n), e();
933
+ }, { once: !0 }), r.addEventListener("error", () => {
934
+ clearTimeout(n), t(/* @__PURE__ */ Error("Failed to load video element"));
935
+ }, { once: !0 });
936
+ });
937
+ try {
938
+ await r.play();
939
+ } catch {}
940
+ let { width: e, height: i } = C(r.videoWidth || (n.getSettings().width ?? 1280), r.videoHeight || (n.getSettings().height ?? 720), t.width, t.height), a;
941
+ if (typeof OffscreenCanvas < "u") a = new OffscreenCanvas(e, i);
942
+ else {
943
+ let t = document.createElement("canvas");
944
+ t.width = e, t.height = i, a = t;
945
+ }
946
+ let o = a.getContext("2d", { alpha: !1 });
947
+ if (!o) throw Error("2D canvas context unavailable");
948
+ o.drawImage(r, 0, 0, e, i);
949
+ let s = await w(a, t), c = await s.arrayBuffer();
950
+ return {
951
+ mime: s.type || t.mime || "image/jpeg",
952
+ width: e,
953
+ height: i,
954
+ data: new Uint8Array(c)
955
+ };
956
+ } finally {
957
+ try {
958
+ r.pause();
959
+ } catch {}
960
+ try {
961
+ r.srcObject = null;
962
+ } catch {}
963
+ try {
964
+ r.remove();
965
+ } catch {}
966
+ }
967
+ }
968
+ async acquireCameraStream(e) {
969
+ if (!navigator.mediaDevices) throw Error("Media devices are not available in this environment");
970
+ let t = e.constraints ?? {}, n = e.width ?? 1280, r = e.height ?? 720, i = {
971
+ ...t,
972
+ width: t.width ?? { ideal: n },
973
+ height: t.height ?? { ideal: r }
974
+ };
975
+ return await navigator.mediaDevices.getUserMedia({
976
+ video: i,
977
+ audio: !1
978
+ });
979
+ }
980
+ async captureFromCamera(e = {}) {
981
+ let t = await this.acquireCameraStream(e), n = t.getVideoTracks()[0];
982
+ if (!n) throw t.getTracks().forEach((e) => e.stop()), Error("No video track available");
983
+ let r = document.createElement("video");
984
+ r.style.position = "fixed", r.style.left = "-99999px", r.style.top = "0", r.muted = !0, r.playsInline = !0, r.autoplay = !0, r.srcObject = t;
985
+ try {
986
+ document.body.appendChild(r);
987
+ } catch {}
988
+ try {
989
+ await new Promise((e, t) => {
990
+ let n = setTimeout(() => t(/* @__PURE__ */ Error("Timed out waiting for video metadata")), S);
991
+ r.addEventListener("loadedmetadata", () => {
992
+ clearTimeout(n), e();
993
+ }, { once: !0 }), r.addEventListener("error", () => {
994
+ clearTimeout(n), t(/* @__PURE__ */ Error("Failed to load video element"));
995
+ }, { once: !0 });
996
+ });
997
+ try {
998
+ await r.play();
999
+ } catch {}
1000
+ let { width: t, height: i } = C(r.videoWidth || (n.getSettings().width ?? 1280), r.videoHeight || (n.getSettings().height ?? 720), e.width, e.height), a;
1001
+ if (typeof OffscreenCanvas < "u") a = new OffscreenCanvas(t, i);
1002
+ else {
1003
+ let e = document.createElement("canvas");
1004
+ e.width = t, e.height = i, a = e;
1005
+ }
1006
+ let o = a.getContext("2d", { alpha: !1 });
1007
+ if (!o) throw Error("2D canvas context unavailable");
1008
+ o.drawImage(r, 0, 0, t, i);
1009
+ let s = await w(a, e), c = await s.arrayBuffer();
1010
+ return {
1011
+ mime: s.type || e.mime || "image/jpeg",
1012
+ width: t,
1013
+ height: i,
1014
+ data: new Uint8Array(c)
1015
+ };
1016
+ } finally {
1017
+ try {
1018
+ r.pause();
1019
+ } catch {}
1020
+ try {
1021
+ r.srcObject = null;
1022
+ } catch {}
1023
+ try {
1024
+ r.remove();
1025
+ } catch {}
1026
+ try {
1027
+ t.getTracks().forEach((e) => e.stop());
1028
+ } catch {}
1029
+ }
1030
+ }
1031
+ async captureFromNativeCamera(e = {}) {
1032
+ if (typeof document > "u") throw Error("Native camera capture requires a DOM environment");
1033
+ let t = await E(e.facing);
1034
+ return t ? await D(t, e) : null;
1035
+ }
1036
+ };
1037
+ function E(e) {
1038
+ return new Promise((t) => {
1039
+ let n = document.createElement("input");
1040
+ n.type = "file", n.accept = "image/*", n.setAttribute("capture", e ?? "environment"), n.style.position = "fixed", n.style.left = "-99999px", n.style.top = "0";
1041
+ let r = !1, i = (e) => {
1042
+ if (!r) {
1043
+ r = !0;
1044
+ try {
1045
+ n.remove();
1046
+ } catch {}
1047
+ t(e);
1048
+ }
1049
+ };
1050
+ n.addEventListener("change", () => {
1051
+ let e = n.files && n.files[0] ? n.files[0] : null;
1052
+ i(e);
1053
+ }), n.addEventListener("cancel", () => i(null));
1054
+ try {
1055
+ document.body.appendChild(n);
1056
+ } catch {
1057
+ i(null);
1058
+ return;
1059
+ }
1060
+ n.click();
1061
+ });
1062
+ }
1063
+ async function D(e, t) {
1064
+ let n = URL.createObjectURL(e), r;
1065
+ try {
1066
+ r = await new Promise((e, t) => {
1067
+ let r = new Image();
1068
+ r.onload = () => e(r), r.onerror = () => t(/* @__PURE__ */ Error("Failed to decode image")), r.src = n;
1069
+ });
1070
+ } finally {
1071
+ URL.revokeObjectURL(n);
1072
+ }
1073
+ let i = r.naturalWidth || r.width, a = r.naturalHeight || r.height, { width: o, height: s } = C(i, a, t.width, t.height), c = t.mime ?? (e.type === "image/png" ? "image/png" : "image/jpeg"), l = t.quality ?? .92;
1074
+ if (o === i && s === a && e.type === c) {
1075
+ let t = await e.arrayBuffer();
1076
+ return {
1077
+ mime: e.type,
1078
+ width: i,
1079
+ height: a,
1080
+ data: new Uint8Array(t)
1081
+ };
1082
+ }
1083
+ let u = document.createElement("canvas");
1084
+ u.width = o, u.height = s;
1085
+ let d = u.getContext("2d", { alpha: !1 });
1086
+ if (!d) throw Error("2D canvas context unavailable");
1087
+ d.drawImage(r, 0, 0, o, s);
1088
+ let f = await new Promise((e, t) => {
1089
+ u.toBlob((n) => n ? e(n) : t(/* @__PURE__ */ Error("Canvas toBlob returned null")), c, l);
1090
+ }), p = await f.arrayBuffer();
1091
+ return {
1092
+ mime: f.type || c,
1093
+ width: o,
1094
+ height: s,
1095
+ data: new Uint8Array(p)
1096
+ };
1097
+ }
1098
+ //#endregion
1099
+ //#region src/worker/video-capture-worker.ts?worker
1100
+ function O(e) {
1101
+ return new Worker(new URL("assets/video-capture-worker-DRYxkwR0.js", import.meta.url).href, {
1102
+ type: "module",
1103
+ name: e?.name
1104
+ });
1105
+ }
1106
+ //#endregion
1107
+ //#region src/media/ikon-video-capture.ts
1108
+ var k = t("IkonVideoCapture"), A = 1e4;
1109
+ function j() {
1110
+ let e = globalThis.crypto;
1111
+ return e?.randomUUID ? e.randomUUID() : `cap_${Date.now()}_${Math.floor(Math.random() * 1e9)}`;
1112
+ }
1113
+ function M() {
1114
+ let e = globalThis.MediaStreamTrackProcessor;
1115
+ if (!e) throw Error("MediaStreamTrackProcessor is not available in this browser");
1116
+ return e;
1117
+ }
1118
+ var N = class {
1119
+ client;
1120
+ worker = null;
1121
+ protocolPort = null;
1122
+ sendPort = null;
1123
+ nextTrackId = 1;
1124
+ captures = /* @__PURE__ */ new Map();
1125
+ constructor(e) {
1126
+ this.client = e;
1127
+ }
1128
+ allocateTrackId() {
1129
+ let e = this.nextTrackId;
1130
+ return this.nextTrackId += 1, e;
1131
+ }
1132
+ ensureWorker() {
1133
+ if (this.worker) return this.worker;
1134
+ let e = new O();
1135
+ return this.worker = e, e.addEventListener("message", (e) => {
1136
+ let t = e.data;
1137
+ if (t?.type === "error") {
1138
+ let e = t.error, n = `${e?.name ?? "Error"} ${e?.message ?? ""}`.trim(), r = typeof t.captureId == "string" ? this.captures.get(t.captureId) : void 0;
1139
+ r && !r.stopped ? (k.error(`Video capture worker error, stopping captureId=${r.captureId.substring(0, 8)}: ${n}`), this.failCapture(r, /* @__PURE__ */ Error(`Video capture worker error: ${n}`))) : typeof t.captureId == "string" ? k.debug(`Video capture worker error for an inactive capture: ${n}`) : k.warn(`Video capture worker error: ${n}`);
1140
+ } else t?.type === "log" ? k.info(t.message) : t?.type === "debug" && k.debug(t.message);
1141
+ }), e.addEventListener("error", (t) => {
1142
+ let n = [
1143
+ t.message,
1144
+ t.filename,
1145
+ t.lineno,
1146
+ t.colno
1147
+ ].filter((e) => e != null && String(e).length > 0);
1148
+ if (k.error(`Video capture worker failed: ${n.join(" ")}`.trim()), this.worker === e) {
1149
+ try {
1150
+ e.terminate();
1151
+ } catch {}
1152
+ this.worker = null, this.protocolPort?.close(), this.protocolPort = null, this.sendPort?.close(), this.sendPort = null;
1153
+ }
1154
+ let i = Error(`Video capture worker failed: ${n.join(" ")}`.trim()), a = this.client.sessionId;
1155
+ for (let e of Array.from(this.captures.values())) e.stopped || (a && this.client.sendProtocolMessage(r(s({}), a, { trackId: e.trackId })), this.failCapture(e, i));
1156
+ }), e;
1157
+ }
1158
+ failCapture(e, t) {
1159
+ try {
1160
+ e.onError?.(t);
1161
+ } catch (t) {
1162
+ k.warn(`onError callback threw for captureId=${e.captureId.substring(0, 8)}: ${t}`);
1163
+ }
1164
+ e.stop();
1165
+ }
1166
+ ensurePorts() {
1167
+ if (!this.client.sessionId) throw Error("Cannot start video capture before client is connected");
1168
+ let t = this.ensureWorker();
1169
+ if (!this.sendPort) {
1170
+ let n = this.client.createProtocolSendPort({ opcodeGroupsMask: e.GROUP_VIDEO });
1171
+ if (!n) throw Error("Failed to create protocol send port for video capture");
1172
+ this.sendPort = n, t.postMessage({
1173
+ type: "attachSendPort",
1174
+ port: n.port
1175
+ }, [n.port]);
1176
+ }
1177
+ if (!this.protocolPort) {
1178
+ let n = this.client.createProtocolMessagePort({ opcodeGroupsMask: e.GROUP_VIDEO });
1179
+ n && (this.protocolPort = n, t.postMessage({
1180
+ type: "attachProtocolPort",
1181
+ port: n.port
1182
+ }, [n.port]));
1183
+ }
1184
+ }
1185
+ async getMediaStream(e) {
1186
+ if (!navigator.mediaDevices) throw Error("Media devices are not available in this environment");
1187
+ let t = e.constraints ?? {}, n = e.options?.framerate ?? 30;
1188
+ if (e.sourceType === "screen") {
1189
+ let e = {
1190
+ ...t,
1191
+ frameRate: t.frameRate ?? { ideal: n }
1192
+ };
1193
+ return navigator.mediaDevices.getDisplayMedia({
1194
+ video: e,
1195
+ audio: !1
1196
+ });
1197
+ }
1198
+ let r = e.options?.width ?? 1280, i = e.options?.height ?? 720, a = {
1199
+ ...t,
1200
+ frameRate: t.frameRate ?? { ideal: n },
1201
+ width: t.width ?? { ideal: r },
1202
+ height: t.height ?? { ideal: i }
1203
+ };
1204
+ return typeof a.deviceId == "string" && (a.deviceId = { exact: a.deviceId }), navigator.mediaDevices.getUserMedia({ video: a });
1205
+ }
1206
+ async startCameraWebRtc(e) {
1207
+ let t = this.client.webRtcSignaling;
1208
+ if (!t) throw Error("WebRTC signaling is not connected");
1209
+ let n = { ...e.constraints };
1210
+ e.options?.width && !n.width && (n.width = { ideal: e.options.width }), e.options?.height && !n.height && (n.height = { ideal: e.options.height }), e.options?.framerate && !n.frameRate && (n.frameRate = { ideal: e.options.framerate }), typeof n.deviceId == "string" && (n.deviceId = { exact: n.deviceId }), t.sendVideoCaptureCorrelation(0, e.correlationId);
1211
+ let r = (await t.startVideoCapture(Object.keys(n).length > 0 ? n : void 0, e.options?.bitrate, 0, e.correlationId)).getVideoTracks()[0], i = "webrtc-video", a = !0, o = e.onStopped, s = {
1212
+ captureId: i,
1213
+ streamId: i,
1214
+ sourceType: "camera",
1215
+ get isActive() {
1216
+ return a;
1217
+ },
1218
+ get currentOptions() {
1219
+ return e.options;
1220
+ },
1221
+ async stop() {
1222
+ a && (a = !1, await t.stopVideoCapture(0), o?.());
1223
+ },
1224
+ async updateOptions() {}
1225
+ };
1226
+ return r && r.addEventListener("ended", () => {
1227
+ s.stop();
1228
+ }), s;
1229
+ }
1230
+ async startScreenWebRtc(e) {
1231
+ let t = this.client.webRtcSignaling;
1232
+ if (!t) throw Error("WebRTC signaling is not connected");
1233
+ let n = { ...e.constraints };
1234
+ e.options?.framerate && !n.frameRate && (n.frameRate = { ideal: e.options.framerate }), t.sendVideoCaptureCorrelation(1, e.correlationId);
1235
+ let r = await t.startScreenCapture(Object.keys(n).length > 0 ? n : void 0, e.options?.bitrate, 1, e.correlationId), i = r.getVideoTracks()[0], a = "webrtc-screen", o = !0, s = e.onStopped, c = {
1236
+ captureId: a,
1237
+ streamId: a,
1238
+ sourceType: "screen",
1239
+ get isActive() {
1240
+ return o;
1241
+ },
1242
+ get currentOptions() {
1243
+ return e.options;
1244
+ },
1245
+ async stop() {
1246
+ o && (o = !1, r.getTracks().forEach((e) => e.stop()), await t.stopVideoCapture(1), s?.());
1247
+ },
1248
+ async updateOptions() {}
1249
+ };
1250
+ return i && i.addEventListener("ended", () => {
1251
+ c.stop();
1252
+ }), c;
1253
+ }
1254
+ async start(e) {
1255
+ let t = this.client.sessionId;
1256
+ if (!t) throw Error("Cannot start video capture before client is connected");
1257
+ if (this.client.isWebRtcCaptureAvailable && e.sourceType === "camera") return this.startCameraWebRtc(e);
1258
+ if (this.client.isWebRtcCaptureAvailable && e.sourceType === "screen") return this.startScreenWebRtc(e);
1259
+ this.client.isWebRtcFirstConnecting ? k.debug("WebRTC still connecting, capturing video via protocol channel") : this.client.isWebRtcEnabled && k.warn("WebRTC unavailable, capturing video via protocol channel"), this.ensurePorts();
1260
+ let n = j(), r = await this.getMediaStream(e), i = r.getVideoTracks()[0];
1261
+ if (!i) throw r.getTracks().forEach((e) => e.stop()), Error("No video track available");
1262
+ e.sourceType === "screen" && await new Promise((e) => setTimeout(e, 200));
1263
+ let a = i.getSettings(), o = e.sourceType === "screen" ? a.width ?? 1280 : e.options?.width ?? a.width ?? 1280, s = e.sourceType === "screen" ? a.height ?? 720 : e.options?.height ?? a.height ?? 720, c = new (M())({ track: i }).readable, l = this.allocateTrackId(), u = this.ensureWorker(), d = e.sourceType, f = e.onStopped, p = new Promise((e, t) => {
1264
+ let r = setTimeout(() => {
1265
+ u.removeEventListener("message", i), t(/* @__PURE__ */ Error("Video capture worker start timeout"));
1266
+ }, A), i = (a) => {
1267
+ let o = a.data;
1268
+ if (o?.captureId === n) {
1269
+ if (o.type === "started") clearTimeout(r), u.removeEventListener("message", i), e();
1270
+ else if (o.type === "error") {
1271
+ clearTimeout(r), u.removeEventListener("message", i);
1272
+ let e = o.error;
1273
+ t(Error(e?.message ?? "Video capture failed to start"));
1274
+ }
1275
+ }
1276
+ };
1277
+ u.addEventListener("message", i);
1278
+ });
1279
+ u.postMessage({
1280
+ type: "start",
1281
+ captureId: n,
1282
+ streamId: n,
1283
+ sourceType: d,
1284
+ kind: e.sourceType,
1285
+ senderId: t,
1286
+ trackId: l,
1287
+ width: o,
1288
+ height: s,
1289
+ readable: c,
1290
+ options: e.options,
1291
+ correlationId: e.correlationId,
1292
+ trackSettings: {
1293
+ width: a.width,
1294
+ height: a.height,
1295
+ frameRate: a.frameRate
1296
+ }
1297
+ }, [c]);
1298
+ try {
1299
+ await p;
1300
+ } catch (e) {
1301
+ throw r.getTracks().forEach((e) => e.stop()), e;
1302
+ }
1303
+ let m = {
1304
+ captureId: n,
1305
+ trackId: l,
1306
+ stream: r,
1307
+ track: i,
1308
+ sourceType: e.sourceType,
1309
+ stopped: !1,
1310
+ currentOptions: e.options,
1311
+ onError: e.onError,
1312
+ stop: async () => {
1313
+ if (!m.stopped) {
1314
+ m.stopped = !0, this.captures.delete(n), m.dimensionCheckInterval &&= (clearInterval(m.dimensionCheckInterval), void 0);
1315
+ try {
1316
+ r.getTracks().forEach((e) => e.stop());
1317
+ } catch {}
1318
+ if (u.postMessage({
1319
+ type: "stop",
1320
+ captureId: n
1321
+ }), f) try {
1322
+ f();
1323
+ } catch {}
1324
+ }
1325
+ },
1326
+ updateOptions: async (e) => {
1327
+ if (!m.stopped) {
1328
+ if (m.sourceType === "camera") {
1329
+ let t = e.width !== void 0 && e.width !== m.currentOptions?.width, n = e.height !== void 0 && e.height !== m.currentOptions?.height, r = e.framerate !== void 0 && e.framerate !== m.currentOptions?.framerate;
1330
+ if (t || n || r) try {
1331
+ let t = {};
1332
+ e.width !== void 0 && (t.width = { ideal: e.width }), e.height !== void 0 && (t.height = { ideal: e.height }), e.framerate !== void 0 && (t.frameRate = { ideal: e.framerate }), await i.applyConstraints(t), k.info(`Camera track constraints updated: ${e.width}x${e.height}@${e.framerate}fps`);
1333
+ } catch (e) {
1334
+ k.warn(`Failed to apply camera constraints: ${e}`);
1335
+ }
1336
+ }
1337
+ m.currentOptions = e, u.postMessage({
1338
+ type: "updateOptions",
1339
+ captureId: n,
1340
+ options: e
1341
+ });
1342
+ }
1343
+ }
1344
+ };
1345
+ if (e.sourceType === "screen") {
1346
+ let e = o, t = s;
1347
+ m.dimensionCheckInterval = setInterval(() => {
1348
+ if (m.stopped) {
1349
+ m.dimensionCheckInterval &&= (clearInterval(m.dimensionCheckInterval), void 0);
1350
+ return;
1351
+ }
1352
+ let r = i.getSettings(), a = r.width ?? e, o = r.height ?? t;
1353
+ (a !== e || o !== t) && (e = a, t = o, u.postMessage({
1354
+ type: "updateDimensions",
1355
+ captureId: n,
1356
+ width: a,
1357
+ height: o
1358
+ }));
1359
+ }, 1e3);
1360
+ }
1361
+ return i.addEventListener("ended", () => {
1362
+ m.stop();
1363
+ }), this.captures.set(n, m), {
1364
+ captureId: n,
1365
+ streamId: n,
1366
+ sourceType: e.sourceType,
1367
+ get isActive() {
1368
+ return !m.stopped;
1369
+ },
1370
+ get currentOptions() {
1371
+ return m.currentOptions;
1372
+ },
1373
+ stop: m.stop,
1374
+ updateOptions: m.updateOptions
1375
+ };
1376
+ }
1377
+ async startCamera(e) {
1378
+ return this.start({
1379
+ sourceType: "camera",
1380
+ ...e
1381
+ });
1382
+ }
1383
+ async startScreen(e) {
1384
+ return this.start({
1385
+ sourceType: "screen",
1386
+ ...e
1387
+ });
1388
+ }
1389
+ async stop(e) {
1390
+ let t = this.captures.get(e);
1391
+ return t ? (await t.stop(), !0) : !1;
1392
+ }
1393
+ getActiveScreenStream() {
1394
+ for (let e of this.captures.values()) if (e.sourceType === "screen") return e.stream;
1395
+ return null;
1396
+ }
1397
+ dispose() {
1398
+ for (let e of this.captures.values()) e.stop();
1399
+ if (this.captures.clear(), this.worker) {
1400
+ try {
1401
+ this.worker.postMessage({ type: "dispose" });
1402
+ } catch {}
1403
+ try {
1404
+ this.worker.terminate();
1405
+ } catch {}
1406
+ this.worker = null;
1407
+ }
1408
+ this.protocolPort?.close(), this.protocolPort = null, this.sendPort?.close(), this.sendPort = null;
1409
+ }
1410
+ }, P = class {
1411
+ audio;
1412
+ video;
1413
+ image;
1414
+ constructor(e, t) {
1415
+ this.audio = new x(e, t?.audio), this.video = new N(e), this.image = new T();
1416
+ }
1417
+ dispose() {
1418
+ this.audio.dispose(), this.video.dispose();
1419
+ }
1420
+ };
1421
+ //#endregion
1422
+ export { P as IkonMediaCapture };