@spatialwalk/avatarkit-rtc 1.0.0-beta.1

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 (85) hide show
  1. package/README.md +417 -0
  2. package/dist/assets/animation-worker-CUXZycUw.js.map +1 -0
  3. package/dist/core/AnimationHandler.d.ts +17 -0
  4. package/dist/core/AnimationHandler.d.ts.map +1 -0
  5. package/dist/core/AvatarPlayer.d.ts +119 -0
  6. package/dist/core/AvatarPlayer.d.ts.map +1 -0
  7. package/dist/core/RTCProvider.d.ts +84 -0
  8. package/dist/core/RTCProvider.d.ts.map +1 -0
  9. package/dist/core/index.d.ts +7 -0
  10. package/dist/core/index.d.ts.map +1 -0
  11. package/dist/core/types.d.ts +7 -0
  12. package/dist/core/types.d.ts.map +1 -0
  13. package/dist/index.d.ts +16 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +13 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/index10.js +67 -0
  18. package/dist/index10.js.map +1 -0
  19. package/dist/index11.js +390 -0
  20. package/dist/index11.js.map +1 -0
  21. package/dist/index12.js +108 -0
  22. package/dist/index12.js.map +1 -0
  23. package/dist/index13.js +18 -0
  24. package/dist/index13.js.map +1 -0
  25. package/dist/index14.js +48 -0
  26. package/dist/index14.js.map +1 -0
  27. package/dist/index15.js +29 -0
  28. package/dist/index15.js.map +1 -0
  29. package/dist/index16.js +144 -0
  30. package/dist/index16.js.map +1 -0
  31. package/dist/index17.js +106 -0
  32. package/dist/index17.js.map +1 -0
  33. package/dist/index18.js +28 -0
  34. package/dist/index18.js.map +1 -0
  35. package/dist/index2.js +220 -0
  36. package/dist/index2.js.map +1 -0
  37. package/dist/index3.js +586 -0
  38. package/dist/index3.js.map +1 -0
  39. package/dist/index4.js +410 -0
  40. package/dist/index4.js.map +1 -0
  41. package/dist/index5.js +20 -0
  42. package/dist/index5.js.map +1 -0
  43. package/dist/index6.js +282 -0
  44. package/dist/index6.js.map +1 -0
  45. package/dist/index7.js +53 -0
  46. package/dist/index7.js.map +1 -0
  47. package/dist/index8.js +189 -0
  48. package/dist/index8.js.map +1 -0
  49. package/dist/index9.js +178 -0
  50. package/dist/index9.js.map +1 -0
  51. package/dist/proto/animation.d.ts +12 -0
  52. package/dist/proto/animation.d.ts.map +1 -0
  53. package/dist/providers/agora/AgoraProvider.d.ts +71 -0
  54. package/dist/providers/agora/AgoraProvider.d.ts.map +1 -0
  55. package/dist/providers/agora/SEIExtractor.d.ts +29 -0
  56. package/dist/providers/agora/SEIExtractor.d.ts.map +1 -0
  57. package/dist/providers/agora/index.d.ts +11 -0
  58. package/dist/providers/agora/index.d.ts.map +1 -0
  59. package/dist/providers/agora/types.d.ts +14 -0
  60. package/dist/providers/agora/types.d.ts.map +1 -0
  61. package/dist/providers/base/BaseProvider.d.ts +11 -0
  62. package/dist/providers/base/BaseProvider.d.ts.map +1 -0
  63. package/dist/providers/index.d.ts +10 -0
  64. package/dist/providers/index.d.ts.map +1 -0
  65. package/dist/providers/livekit/LiveKitProvider.d.ts +64 -0
  66. package/dist/providers/livekit/LiveKitProvider.d.ts.map +1 -0
  67. package/dist/providers/livekit/VP8Extractor.d.ts +10 -0
  68. package/dist/providers/livekit/VP8Extractor.d.ts.map +1 -0
  69. package/dist/providers/livekit/animation-transform.d.ts +11 -0
  70. package/dist/providers/livekit/animation-transform.d.ts.map +1 -0
  71. package/dist/providers/livekit/animation-worker.d.ts +14 -0
  72. package/dist/providers/livekit/animation-worker.d.ts.map +1 -0
  73. package/dist/providers/livekit/index.d.ts +11 -0
  74. package/dist/providers/livekit/index.d.ts.map +1 -0
  75. package/dist/providers/livekit/types.d.ts +11 -0
  76. package/dist/providers/livekit/types.d.ts.map +1 -0
  77. package/dist/providers/livekit/utils.d.ts +11 -0
  78. package/dist/providers/livekit/utils.d.ts.map +1 -0
  79. package/dist/types/index.d.ts +77 -0
  80. package/dist/types/index.d.ts.map +1 -0
  81. package/dist/utils/index.d.ts +7 -0
  82. package/dist/utils/index.d.ts.map +1 -0
  83. package/dist/utils/logger.d.ts +13 -0
  84. package/dist/utils/logger.d.ts.map +1 -0
  85. package/package.json +61 -0
package/dist/index3.js ADDED
@@ -0,0 +1,586 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { BaseProvider } from "./index10.js";
5
+ import { isLiveKitConfig } from "./index5.js";
6
+ import { VP8Extractor } from "./index12.js";
7
+ import { getInsertableStreamsMethod } from "./index13.js";
8
+ import { logger } from "./index7.js";
9
+ const globalLiveKitProviderRegistry = /* @__PURE__ */ new Set();
10
+ let rtcPeerConnectionPatched = false;
11
+ function patchRTCPeerConnection() {
12
+ if (rtcPeerConnectionPatched) return;
13
+ if (typeof RTCPeerConnection === "undefined") return;
14
+ rtcPeerConnectionPatched = true;
15
+ const originalAddEventListener = RTCPeerConnection.prototype.addEventListener;
16
+ RTCPeerConnection.prototype.addEventListener = function(type, listener, options) {
17
+ if (type === "track") {
18
+ const wrappedListener = function(event) {
19
+ var _a, _b;
20
+ const trackEvent = event;
21
+ if ((((_a = trackEvent.track) == null ? void 0 : _a.kind) === "audio" || ((_b = trackEvent.track) == null ? void 0 : _b.kind) === "video") && trackEvent.receiver) {
22
+ for (const provider of globalLiveKitProviderRegistry) {
23
+ provider.handleEarlyTrack(trackEvent.receiver, trackEvent.track);
24
+ }
25
+ }
26
+ if (typeof listener === "function") {
27
+ listener.call(this, event);
28
+ } else {
29
+ listener.handleEvent(event);
30
+ }
31
+ };
32
+ return originalAddEventListener.call(this, type, wrappedListener, options);
33
+ }
34
+ return originalAddEventListener.call(this, type, listener, options);
35
+ };
36
+ const originalOnTrackDescriptor = Object.getOwnPropertyDescriptor(RTCPeerConnection.prototype, "ontrack");
37
+ if (originalOnTrackDescriptor) {
38
+ Object.defineProperty(RTCPeerConnection.prototype, "ontrack", {
39
+ get: originalOnTrackDescriptor.get,
40
+ set: function(handler) {
41
+ var _a, _b;
42
+ if (handler) {
43
+ const wrappedHandler = function(event) {
44
+ var _a2, _b2;
45
+ if ((((_a2 = event.track) == null ? void 0 : _a2.kind) === "audio" || ((_b2 = event.track) == null ? void 0 : _b2.kind) === "video") && event.receiver) {
46
+ for (const provider of globalLiveKitProviderRegistry) {
47
+ provider.handleEarlyTrack(event.receiver, event.track);
48
+ }
49
+ }
50
+ handler.call(this, event);
51
+ };
52
+ (_a = originalOnTrackDescriptor.set) == null ? void 0 : _a.call(this, wrappedHandler);
53
+ } else {
54
+ (_b = originalOnTrackDescriptor.set) == null ? void 0 : _b.call(this, handler);
55
+ }
56
+ },
57
+ configurable: true,
58
+ enumerable: true
59
+ });
60
+ }
61
+ }
62
+ patchRTCPeerConnection();
63
+ class LiveKitProvider extends BaseProvider {
64
+ constructor() {
65
+ super();
66
+ /** Provider name identifier */
67
+ __publicField(this, "name", "livekit");
68
+ /** @internal */
69
+ __publicField(this, "room", null);
70
+ /** @internal */
71
+ __publicField(this, "livekitSDK", null);
72
+ // Animation track subscription
73
+ /** @internal */
74
+ __publicField(this, "animationCallbacks", null);
75
+ /** @internal */
76
+ __publicField(this, "vp8Extractor", null);
77
+ /** @internal */
78
+ __publicField(this, "vp8Extractors", /* @__PURE__ */ new Map());
79
+ /** @internal */
80
+ __publicField(this, "transformedReceivers", /* @__PURE__ */ new Set());
81
+ /** @internal */
82
+ __publicField(this, "receiverParticipantMap", /* @__PURE__ */ new Map());
83
+ // Audio track subscription
84
+ /** @internal */
85
+ __publicField(this, "audioCallbacks", null);
86
+ /** @internal */
87
+ __publicField(this, "audioElements", /* @__PURE__ */ new Map());
88
+ // Microphone publishing
89
+ /** @internal */
90
+ __publicField(this, "localAudioTrack", null);
91
+ /** @internal */
92
+ __publicField(this, "isMicrophoneEnabled", false);
93
+ globalLiveKitProviderRegistry.add(this);
94
+ }
95
+ /**
96
+ * Load LiveKit SDK dynamically.
97
+ * @internal
98
+ */
99
+ async loadSDK() {
100
+ if (this.livekitSDK) {
101
+ return this.livekitSDK;
102
+ }
103
+ try {
104
+ this.livekitSDK = await import("livekit-client");
105
+ return this.livekitSDK;
106
+ } catch (error) {
107
+ throw new Error(
108
+ "❌ Failed to load livekit-client.\nPlease ensure it is installed: pnpm add livekit-client"
109
+ );
110
+ }
111
+ }
112
+ /**
113
+ * Map LiveKit connection state to string.
114
+ * @internal
115
+ */
116
+ mapLiveKitConnectionState(livekit, state) {
117
+ const { ConnectionState: LKConnectionState } = livekit;
118
+ switch (state) {
119
+ case LKConnectionState.Disconnected:
120
+ return "disconnected";
121
+ case LKConnectionState.Connecting:
122
+ return "connecting";
123
+ case LKConnectionState.Connected:
124
+ return "connected";
125
+ case LKConnectionState.Reconnecting:
126
+ return "reconnecting";
127
+ default:
128
+ return "failed";
129
+ }
130
+ }
131
+ /**
132
+ * Handle early track event from the global RTCPeerConnection patch.
133
+ * This is called BEFORE LiveKit processes the track.
134
+ * @internal
135
+ */
136
+ handleEarlyTrack(receiver, track) {
137
+ if (!this.room) return;
138
+ const roomState = this.room.state;
139
+ if (roomState === "disconnected" || roomState === 0) return;
140
+ if (track.kind === "video" && this.animationCallbacks) {
141
+ this.applyAnimationReceiverTransform(receiver, track);
142
+ }
143
+ }
144
+ /**
145
+ * Find all RTCPeerConnection instances in the room
146
+ * @internal
147
+ */
148
+ findAllPeerConnections(root) {
149
+ if (typeof RTCPeerConnection === "undefined") return [];
150
+ if (!root || typeof root !== "object") return [];
151
+ const seen = /* @__PURE__ */ new Set();
152
+ const queue = [{ v: root, d: 0 }];
153
+ const pcs = [];
154
+ let steps = 0;
155
+ while (queue.length && steps < 2e3) {
156
+ steps++;
157
+ const { v, d } = queue.shift();
158
+ if (!v || typeof v !== "object") continue;
159
+ if (seen.has(v)) continue;
160
+ seen.add(v);
161
+ if (v instanceof RTCPeerConnection) {
162
+ pcs.push(v);
163
+ continue;
164
+ }
165
+ if (d >= 6) continue;
166
+ if (Array.isArray(v)) {
167
+ for (const item of v) queue.push({ v: item, d: d + 1 });
168
+ continue;
169
+ }
170
+ if (v instanceof Map) {
171
+ for (const item of v.values()) queue.push({ v: item, d: d + 1 });
172
+ continue;
173
+ }
174
+ if (v instanceof Set) {
175
+ for (const item of v.values()) queue.push({ v: item, d: d + 1 });
176
+ continue;
177
+ }
178
+ try {
179
+ for (const key of Object.keys(v)) {
180
+ queue.push({ v: v[key], d: d + 1 });
181
+ }
182
+ } catch {
183
+ }
184
+ }
185
+ return pcs;
186
+ }
187
+ /**
188
+ * Find video receiver by track ID
189
+ * @internal
190
+ */
191
+ findVideoReceiverByTrackId(trackId) {
192
+ var _a;
193
+ if (!trackId) return null;
194
+ const pcs = this.findAllPeerConnections(this.room);
195
+ for (const pc of pcs) {
196
+ try {
197
+ const receivers = pc.getReceivers();
198
+ for (const r of receivers) {
199
+ if (((_a = r.track) == null ? void 0 : _a.kind) === "video" && r.track.id === trackId) return r;
200
+ }
201
+ } catch {
202
+ }
203
+ }
204
+ for (const pc of pcs) {
205
+ try {
206
+ const r = pc.getReceivers().find((x) => {
207
+ var _a2;
208
+ return ((_a2 = x.track) == null ? void 0 : _a2.kind) === "video";
209
+ });
210
+ if (r) return r;
211
+ } catch {
212
+ }
213
+ }
214
+ return null;
215
+ }
216
+ /**
217
+ * Find audio receiver by track ID
218
+ * @internal
219
+ */
220
+ findAudioReceiverByTrackId(trackId) {
221
+ var _a;
222
+ if (!trackId) return null;
223
+ const pcs = this.findAllPeerConnections(this.room);
224
+ for (const pc of pcs) {
225
+ try {
226
+ const receivers = pc.getReceivers();
227
+ for (const r of receivers) {
228
+ if (((_a = r.track) == null ? void 0 : _a.kind) === "audio" && r.track.id === trackId) return r;
229
+ }
230
+ } catch {
231
+ }
232
+ }
233
+ for (const pc of pcs) {
234
+ try {
235
+ const r = pc.getReceivers().find((x) => {
236
+ var _a2;
237
+ return ((_a2 = x.track) == null ? void 0 : _a2.kind) === "audio";
238
+ });
239
+ if (r) return r;
240
+ } catch {
241
+ }
242
+ }
243
+ return null;
244
+ }
245
+ /**
246
+ * Apply animation receiver transform on a video receiver
247
+ * @internal
248
+ */
249
+ applyAnimationReceiverTransform(receiver, mediaTrack) {
250
+ if (mediaTrack.kind !== "video") return;
251
+ if (this.transformedReceivers.has(receiver)) return;
252
+ const method = getInsertableStreamsMethod();
253
+ if (method !== "scriptTransform") return;
254
+ if (receiver.transform) return;
255
+ if (!this.animationCallbacks) return;
256
+ this.transformedReceivers.add(receiver);
257
+ try {
258
+ const extractor = new VP8Extractor();
259
+ this.vp8Extractors.set(receiver, extractor);
260
+ this.vp8Extractor = extractor;
261
+ extractor.initialize(receiver, this.animationCallbacks).catch((err) => {
262
+ logger.error("LiveKit", "Failed to initialize VP8Extractor:", err);
263
+ this.transformedReceivers.delete(receiver);
264
+ this.vp8Extractors.delete(receiver);
265
+ });
266
+ } catch (e) {
267
+ logger.error("LiveKit", "Failed to apply transform:", e);
268
+ this.transformedReceivers.delete(receiver);
269
+ this.vp8Extractors.delete(receiver);
270
+ }
271
+ }
272
+ async connect(config) {
273
+ var _a;
274
+ logger.info("LiveKit", "connect() called with config:", {
275
+ hasUrl: "url" in config,
276
+ hasRoomName: "roomName" in config,
277
+ hasToken: "token" in config,
278
+ config
279
+ });
280
+ if (!isLiveKitConfig(config)) {
281
+ logger.error("LiveKit", "Config validation failed - missing url or roomName");
282
+ throw new Error("LiveKitProvider requires url and roomName in connection config");
283
+ }
284
+ const livekitConfig = config;
285
+ logger.info("LiveKit", "Config validated, connecting to:", livekitConfig.url);
286
+ const livekit = await this.loadSDK();
287
+ logger.info("LiveKit", "SDK loaded successfully");
288
+ const { Room } = livekit;
289
+ this.room = new Room({
290
+ videoCaptureDefaults: void 0,
291
+ audioCaptureDefaults: void 0
292
+ });
293
+ this.setConnectionState("connecting");
294
+ this.setupEventListeners(livekit);
295
+ try {
296
+ logger.info("LiveKit", "Attempting to connect to room...");
297
+ await this.room.connect(livekitConfig.url, livekitConfig.token);
298
+ logger.info("LiveKit", "Room connected, state:", this.room.state);
299
+ logger.info("LiveKit", "Room name:", this.room.name);
300
+ logger.info("LiveKit", "Local participant:", (_a = this.room.localParticipant) == null ? void 0 : _a.identity);
301
+ logger.info("LiveKit", "Remote participants:", this.room.remoteParticipants.size);
302
+ this.room.remoteParticipants.forEach((participant, sid) => {
303
+ logger.info("LiveKit", `Remote participant: ${participant.identity}, sid: ${sid}`);
304
+ participant.trackPublications.forEach((pub, trackSid) => {
305
+ logger.info("LiveKit", ` Track: ${pub.trackName}, kind: ${pub.kind}, subscribed: ${pub.isSubscribed}, sid: ${trackSid}`);
306
+ });
307
+ });
308
+ this.setConnectionState(this.mapLiveKitConnectionState(livekit, this.room.state));
309
+ this.emit("connected");
310
+ } catch (error) {
311
+ logger.error("LiveKit", "Connection failed:", error);
312
+ this.setConnectionState("failed");
313
+ this.emit("error", error);
314
+ throw error;
315
+ }
316
+ }
317
+ /**
318
+ * Setup LiveKit room event listeners
319
+ * @internal
320
+ */
321
+ setupEventListeners(livekit) {
322
+ if (!this.room) return;
323
+ const room = this.room;
324
+ const { RoomEvent, Track } = livekit;
325
+ room.on(RoomEvent.Connected, () => {
326
+ this.setConnectionState("connected");
327
+ this.emit("connected");
328
+ });
329
+ room.on(RoomEvent.ParticipantConnected, (participant) => {
330
+ logger.info("LiveKit", `ParticipantConnected: ${participant.identity}`);
331
+ });
332
+ room.on(RoomEvent.TrackPublished, (publication, participant) => {
333
+ logger.info("LiveKit", "TrackPublished:", {
334
+ trackName: publication.trackName,
335
+ kind: publication.kind,
336
+ participant: participant.identity
337
+ });
338
+ });
339
+ room.on(RoomEvent.Disconnected, () => {
340
+ this.cleanup();
341
+ this.setConnectionState("disconnected");
342
+ this.emit("disconnected");
343
+ });
344
+ this.room.on(
345
+ RoomEvent.TrackSubscribed,
346
+ (track, publication, participant) => {
347
+ var _a, _b;
348
+ logger.info("LiveKit", "TrackSubscribed:", {
349
+ kind: track.kind,
350
+ trackName: publication.trackName,
351
+ participant: participant.identity
352
+ });
353
+ const mediaStreamTrack = track.mediaStreamTrack;
354
+ const trackName = publication.trackName;
355
+ if (track.kind === Track.Kind.Audio) {
356
+ logger.info("LiveKit", "Audio track received, attaching...");
357
+ const audioElement = track.attach();
358
+ audioElement.id = `audio-${participant.identity}`;
359
+ audioElement.autoplay = true;
360
+ audioElement.controls = false;
361
+ audioElement.style.display = "none";
362
+ if (typeof document !== "undefined") {
363
+ document.body.appendChild(audioElement);
364
+ logger.info("LiveKit", "Audio element appended to body");
365
+ }
366
+ this.audioElements.set(participant.identity, audioElement);
367
+ if (this.audioCallbacks) {
368
+ (_b = (_a = this.audioCallbacks).onAudioReceived) == null ? void 0 : _b.call(_a, participant);
369
+ }
370
+ } else if (track.kind === Track.Kind.Video) {
371
+ logger.info("LiveKit", "Video track received, setting up animation transform...");
372
+ if (mediaStreamTrack) {
373
+ const receiver = this.findVideoReceiverByTrackId(mediaStreamTrack.id);
374
+ if (receiver) {
375
+ this.receiverParticipantMap.set(receiver, { participant, trackName });
376
+ if (!receiver.transform || !this.transformedReceivers.has(receiver)) {
377
+ this.applyAnimationReceiverTransform(receiver, mediaStreamTrack);
378
+ }
379
+ }
380
+ }
381
+ const videoElement = track.attach();
382
+ videoElement.id = `video-animation-${participant.identity}`;
383
+ videoElement.muted = true;
384
+ videoElement.autoplay = true;
385
+ videoElement.style.display = "none";
386
+ if (typeof document !== "undefined") {
387
+ document.body.appendChild(videoElement);
388
+ videoElement.play().catch(() => {
389
+ });
390
+ }
391
+ }
392
+ }
393
+ );
394
+ this.room.on(
395
+ RoomEvent.TrackUnsubscribed,
396
+ (track, _publication, participant) => {
397
+ var _a, _b;
398
+ if (track.kind === Track.Kind.Audio) {
399
+ track.detach().forEach((el) => el.remove());
400
+ this.audioElements.delete(participant.identity);
401
+ if (this.audioCallbacks) {
402
+ (_b = (_a = this.audioCallbacks).onAudioLost) == null ? void 0 : _b.call(_a, participant);
403
+ }
404
+ }
405
+ }
406
+ );
407
+ room.on(RoomEvent.ConnectionStateChanged, (state) => {
408
+ this.setConnectionState(this.mapLiveKitConnectionState(livekit, state));
409
+ });
410
+ }
411
+ async disconnect() {
412
+ this.cleanup();
413
+ if (this.room) {
414
+ this.room.disconnect();
415
+ this.room = null;
416
+ }
417
+ this.setConnectionState("disconnected");
418
+ globalLiveKitProviderRegistry.delete(this);
419
+ }
420
+ getConnectionState() {
421
+ if (!this.room) {
422
+ return "disconnected";
423
+ }
424
+ return this.connectionState;
425
+ }
426
+ async subscribeAnimationTrack(callbacks) {
427
+ var _a;
428
+ this.animationCallbacks = callbacks;
429
+ if (this.room) {
430
+ const livekit = await this.loadSDK();
431
+ const { ConnectionState: LKConnectionState } = livekit;
432
+ const roomState = this.room.state;
433
+ if (roomState === LKConnectionState.Connected || roomState === "connected") {
434
+ const remoteParticipants = this.room.remoteParticipants.values();
435
+ for (const participant of remoteParticipants) {
436
+ const pubs = participant.videoTrackPublications ?? participant.trackPublications;
437
+ if (pubs && typeof pubs.values === "function") {
438
+ for (const publication of pubs.values()) {
439
+ if (((_a = publication.trackName) == null ? void 0 : _a.includes("animation")) && publication.track) {
440
+ const mediaStreamTrack = publication.track.mediaStreamTrack;
441
+ if (mediaStreamTrack) {
442
+ const receiver = this.findVideoReceiverByTrackId(mediaStreamTrack.id);
443
+ if (receiver) {
444
+ this.applyAnimationReceiverTransform(receiver, mediaStreamTrack);
445
+ }
446
+ }
447
+ }
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+ }
454
+ async unsubscribeAnimationTrack() {
455
+ this.animationCallbacks = null;
456
+ this.vp8Extractors.forEach((extractor) => {
457
+ extractor.dispose();
458
+ });
459
+ this.vp8Extractors.clear();
460
+ if (this.vp8Extractor) {
461
+ this.vp8Extractor.dispose();
462
+ this.vp8Extractor = null;
463
+ }
464
+ this.transformedReceivers.clear();
465
+ this.receiverParticipantMap.clear();
466
+ }
467
+ async subscribeAudioTrack(callbacks) {
468
+ this.audioCallbacks = callbacks;
469
+ }
470
+ async unsubscribeAudioTrack() {
471
+ this.audioCallbacks = null;
472
+ this.audioElements.forEach((el) => {
473
+ el.pause();
474
+ el.remove();
475
+ });
476
+ this.audioElements.clear();
477
+ }
478
+ async publishAudioTrack(track) {
479
+ if (!this.room) {
480
+ throw new Error("Not connected to room");
481
+ }
482
+ try {
483
+ const livekit = await this.loadSDK();
484
+ const { createLocalAudioTrack, Track: TrackEnum } = livekit;
485
+ if (track) {
486
+ await this.room.localParticipant.publishTrack(track, {
487
+ name: "user-microphone",
488
+ source: TrackEnum.Source.Microphone,
489
+ red: false
490
+ });
491
+ } else {
492
+ const localTrack = await createLocalAudioTrack({
493
+ echoCancellation: true,
494
+ noiseSuppression: true,
495
+ autoGainControl: true
496
+ });
497
+ this.localAudioTrack = localTrack;
498
+ await this.room.localParticipant.publishTrack(localTrack, {
499
+ name: "user-microphone",
500
+ source: TrackEnum.Source.Microphone,
501
+ red: false
502
+ });
503
+ }
504
+ this.isMicrophoneEnabled = true;
505
+ } catch (error) {
506
+ logger.error("LiveKit", "Failed to publish audio track:", error);
507
+ this.isMicrophoneEnabled = false;
508
+ throw error;
509
+ }
510
+ }
511
+ async unpublishAudioTrack() {
512
+ if (!this.room || !this.localAudioTrack) {
513
+ return;
514
+ }
515
+ await this.room.localParticipant.unpublishTrack(this.localAudioTrack);
516
+ this.localAudioTrack.stop();
517
+ this.localAudioTrack = null;
518
+ this.isMicrophoneEnabled = false;
519
+ }
520
+ /**
521
+ * Play remote audio (resume playback)
522
+ */
523
+ playRemoteAudio() {
524
+ this.audioElements.forEach((audioElement) => {
525
+ if (audioElement.paused) {
526
+ audioElement.play().catch(() => {
527
+ });
528
+ }
529
+ });
530
+ }
531
+ /**
532
+ * Pause remote audio
533
+ */
534
+ pauseRemoteAudio() {
535
+ this.audioElements.forEach((audioElement) => {
536
+ if (!audioElement.paused) {
537
+ audioElement.pause();
538
+ }
539
+ });
540
+ }
541
+ /**
542
+ * Get the native LiveKit Room instance.
543
+ *
544
+ * Allows advanced users to access LiveKit-specific features
545
+ * not exposed through the unified API.
546
+ *
547
+ * @returns The LiveKit Room instance, or null if not connected
548
+ *
549
+ * @example
550
+ * ```typescript
551
+ * const room = provider.getNativeClient();
552
+ * if (room) {
553
+ * // Access LiveKit-specific features
554
+ * console.log('Participants:', room.remoteParticipants.size);
555
+ * }
556
+ * ```
557
+ */
558
+ getNativeClient() {
559
+ return this.room;
560
+ }
561
+ /**
562
+ * Cleanup resources
563
+ * @internal
564
+ */
565
+ cleanup() {
566
+ this.audioElements.forEach((el) => {
567
+ el.pause();
568
+ el.remove();
569
+ });
570
+ this.audioElements.clear();
571
+ this.vp8Extractors.forEach((extractor) => extractor.dispose());
572
+ this.vp8Extractors.clear();
573
+ this.vp8Extractor = null;
574
+ this.transformedReceivers.clear();
575
+ this.receiverParticipantMap.clear();
576
+ if (this.localAudioTrack) {
577
+ this.localAudioTrack.stop();
578
+ this.localAudioTrack = null;
579
+ }
580
+ this.isMicrophoneEnabled = false;
581
+ }
582
+ }
583
+ export {
584
+ LiveKitProvider
585
+ };
586
+ //# sourceMappingURL=index3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index3.js","sources":["../src/providers/livekit/LiveKitProvider.ts"],"sourcesContent":["/**\n * LiveKit Provider Implementation.\n *\n * This provider uses LiveKit's VP8 video track approach\n * to transport animation data via RTCRtpScriptTransform.\n *\n * @packageDocumentation\n */\n\nimport { BaseProvider } from '../base/BaseProvider';\nimport type { RTCConnectionConfig, LiveKitConnectionConfig } from '../../types';\nimport { isLiveKitConfig } from '../../types';\nimport type { AnimationTrackCallbacks, AudioTrackCallbacks } from '../../core/types';\nimport { VP8Extractor } from './VP8Extractor';\nimport { getInsertableStreamsMethod } from './utils';\nimport { logger } from '../../utils';\nimport type {\n LiveKitModule,\n LiveKitRoom,\n LiveKitLocalAudioTrack,\n} from './types';\n\n/**\n * Global registry for LiveKitProvider instances to receive early track events.\n * @internal\n */\nconst globalLiveKitProviderRegistry = new Set<LiveKitProvider>();\n\n/**\n * Flag to ensure we only patch RTCPeerConnection once.\n * @internal\n */\nlet rtcPeerConnectionPatched = false;\n\n/**\n * Patch RTCPeerConnection.prototype to intercept track events at the earliest possible moment.\n * This runs BEFORE any PeerConnection is created by LiveKit.\n * @internal\n */\nfunction patchRTCPeerConnection(): void {\n if (rtcPeerConnectionPatched) return;\n if (typeof RTCPeerConnection === 'undefined') return;\n\n rtcPeerConnectionPatched = true;\n\n const originalAddEventListener = RTCPeerConnection.prototype.addEventListener;\n\n RTCPeerConnection.prototype.addEventListener = function(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ) {\n if (type === 'track') {\n const wrappedListener = function(this: RTCPeerConnection, event: Event) {\n const trackEvent = event as RTCTrackEvent;\n\n if ((trackEvent.track?.kind === 'audio' || trackEvent.track?.kind === 'video') && trackEvent.receiver) {\n for (const provider of globalLiveKitProviderRegistry) {\n provider.handleEarlyTrack(trackEvent.receiver, trackEvent.track);\n }\n }\n\n if (typeof listener === 'function') {\n listener.call(this, event);\n } else {\n listener.handleEvent(event);\n }\n };\n\n return originalAddEventListener.call(this, type, wrappedListener as EventListener, options);\n }\n\n return originalAddEventListener.call(this, type, listener, options);\n };\n\n // Also patch the ontrack setter\n const originalOnTrackDescriptor = Object.getOwnPropertyDescriptor(RTCPeerConnection.prototype, 'ontrack');\n if (originalOnTrackDescriptor) {\n Object.defineProperty(RTCPeerConnection.prototype, 'ontrack', {\n get: originalOnTrackDescriptor.get,\n set: function(handler: ((this: RTCPeerConnection, ev: RTCTrackEvent) => void) | null) {\n if (handler) {\n const wrappedHandler = function(this: RTCPeerConnection, event: RTCTrackEvent) {\n if ((event.track?.kind === 'audio' || event.track?.kind === 'video') && event.receiver) {\n for (const provider of globalLiveKitProviderRegistry) {\n provider.handleEarlyTrack(event.receiver, event.track);\n }\n }\n handler.call(this, event);\n };\n originalOnTrackDescriptor.set?.call(this, wrappedHandler);\n } else {\n originalOnTrackDescriptor.set?.call(this, handler);\n }\n },\n configurable: true,\n enumerable: true,\n });\n }\n}\n\n// Apply the patch immediately when this module loads\npatchRTCPeerConnection();\n\n/**\n * LiveKit Provider.\n *\n * Implements RTCProvider interface for LiveKit platform.\n * Uses RTCRtpScriptTransform to extract animation data from VP8 video tracks.\n *\n * @example\n * ```typescript\n * import { AvatarPlayer, LiveKitProvider } from '@spatialwalk/avatarkit-rtc';\n *\n * const provider = new LiveKitProvider();\n * const player = new AvatarPlayer(provider, renderer);\n *\n * await player.connect({\n * url: 'wss://your-livekit-server.com',\n * token: 'your-token',\n * });\n * ```\n */\nexport class LiveKitProvider extends BaseProvider {\n /** Provider name identifier */\n readonly name = 'livekit';\n\n /** @internal */\n private room: LiveKitRoom | null = null;\n /** @internal */\n private livekitSDK: LiveKitModule | null = null;\n\n // Animation track subscription\n /** @internal */\n private animationCallbacks: AnimationTrackCallbacks | null = null;\n /** @internal */\n private vp8Extractor: VP8Extractor | null = null;\n /** @internal */\n private vp8Extractors: Map<RTCRtpReceiver, VP8Extractor> = new Map();\n /** @internal */\n private transformedReceivers: Set<RTCRtpReceiver> = new Set();\n /** @internal */\n private receiverParticipantMap: Map<RTCRtpReceiver, { participant: unknown; trackName?: string }> = new Map();\n\n // Audio track subscription\n /** @internal */\n private audioCallbacks: AudioTrackCallbacks | null = null;\n /** @internal */\n private audioElements: Map<string, HTMLAudioElement> = new Map();\n\n // Microphone publishing\n /** @internal */\n private localAudioTrack: LiveKitLocalAudioTrack | null = null;\n /** @internal */\n private isMicrophoneEnabled = false;\n\n constructor() {\n super();\n // Register to receive early track events\n globalLiveKitProviderRegistry.add(this);\n }\n\n /**\n * Load LiveKit SDK dynamically.\n * @internal\n */\n private async loadSDK(): Promise<LiveKitModule> {\n if (this.livekitSDK) {\n return this.livekitSDK;\n }\n\n try {\n this.livekitSDK = await import('livekit-client');\n return this.livekitSDK;\n } catch (error) {\n throw new Error(\n '❌ Failed to load livekit-client.\\n' +\n 'Please ensure it is installed: pnpm add livekit-client'\n );\n }\n }\n\n /**\n * Map LiveKit connection state to string.\n * @internal\n */\n private mapLiveKitConnectionState(livekit: typeof import('livekit-client'), state: unknown): string {\n const { ConnectionState: LKConnectionState } = livekit;\n switch (state) {\n case LKConnectionState.Disconnected:\n return 'disconnected';\n case LKConnectionState.Connecting:\n return 'connecting';\n case LKConnectionState.Connected:\n return 'connected';\n case LKConnectionState.Reconnecting:\n return 'reconnecting';\n default:\n return 'failed';\n }\n }\n\n /**\n * Handle early track event from the global RTCPeerConnection patch.\n * This is called BEFORE LiveKit processes the track.\n * @internal\n */\n handleEarlyTrack(receiver: RTCRtpReceiver, track: MediaStreamTrack): void {\n if (!this.room) return;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const roomState = (this.room as any).state;\n if (roomState === 'disconnected' || roomState === 0) return;\n\n // Apply transform to video tracks - animation data is sent as VP8 video\n if (track.kind === 'video' && this.animationCallbacks) {\n this.applyAnimationReceiverTransform(receiver, track);\n }\n }\n\n /**\n * Find all RTCPeerConnection instances in the room\n * @internal\n */\n private findAllPeerConnections(root: unknown): RTCPeerConnection[] {\n if (typeof RTCPeerConnection === 'undefined') return [];\n if (!root || typeof root !== 'object') return [];\n\n const seen = new Set<unknown>();\n const queue: Array<{ v: unknown; d: number }> = [{ v: root, d: 0 }];\n const pcs: RTCPeerConnection[] = [];\n let steps = 0;\n\n while (queue.length && steps < 2000) {\n steps++;\n const { v, d } = queue.shift()!;\n if (!v || typeof v !== 'object') continue;\n if (seen.has(v)) continue;\n seen.add(v);\n\n if (v instanceof RTCPeerConnection) {\n pcs.push(v);\n continue;\n }\n if (d >= 6) continue;\n\n if (Array.isArray(v)) {\n for (const item of v) queue.push({ v: item, d: d + 1 });\n continue;\n }\n\n if (v instanceof Map) {\n for (const item of v.values()) queue.push({ v: item, d: d + 1 });\n continue;\n }\n\n if (v instanceof Set) {\n for (const item of v.values()) queue.push({ v: item, d: d + 1 });\n continue;\n }\n\n try {\n for (const key of Object.keys(v as object)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n queue.push({ v: (v as any)[key], d: d + 1 });\n }\n } catch {\n // ignore\n }\n }\n\n return pcs;\n }\n\n /**\n * Find video receiver by track ID\n * @internal\n */\n private findVideoReceiverByTrackId(trackId?: string): RTCRtpReceiver | null {\n if (!trackId) return null;\n const pcs = this.findAllPeerConnections(this.room);\n for (const pc of pcs) {\n try {\n const receivers = pc.getReceivers();\n for (const r of receivers) {\n if (r.track?.kind === 'video' && r.track.id === trackId) return r;\n }\n } catch {\n // ignore\n }\n }\n // fallback: any video receiver\n for (const pc of pcs) {\n try {\n const r = pc.getReceivers().find((x) => x.track?.kind === 'video');\n if (r) return r;\n } catch {\n // ignore\n }\n }\n return null;\n }\n\n /**\n * Find audio receiver by track ID\n * @internal\n */\n private findAudioReceiverByTrackId(trackId?: string): RTCRtpReceiver | null {\n if (!trackId) return null;\n const pcs = this.findAllPeerConnections(this.room);\n for (const pc of pcs) {\n try {\n const receivers = pc.getReceivers();\n for (const r of receivers) {\n if (r.track?.kind === 'audio' && r.track.id === trackId) return r;\n }\n } catch {\n // ignore\n }\n }\n // fallback: any audio receiver\n for (const pc of pcs) {\n try {\n const r = pc.getReceivers().find((x) => x.track?.kind === 'audio');\n if (r) return r;\n } catch {\n // ignore\n }\n }\n return null;\n }\n\n /**\n * Apply animation receiver transform on a video receiver\n * @internal\n */\n private applyAnimationReceiverTransform(receiver: RTCRtpReceiver, mediaTrack: MediaStreamTrack): void {\n if (mediaTrack.kind !== 'video') return;\n if (this.transformedReceivers.has(receiver)) return;\n\n const method = getInsertableStreamsMethod();\n if (method !== 'scriptTransform') return;\n if (receiver.transform) return;\n if (!this.animationCallbacks) return;\n\n this.transformedReceivers.add(receiver);\n\n try {\n const extractor = new VP8Extractor();\n this.vp8Extractors.set(receiver, extractor);\n this.vp8Extractor = extractor;\n \n extractor.initialize(receiver, this.animationCallbacks).catch((err) => {\n logger.error('LiveKit', 'Failed to initialize VP8Extractor:', err);\n this.transformedReceivers.delete(receiver);\n this.vp8Extractors.delete(receiver);\n });\n } catch (e) {\n logger.error('LiveKit', 'Failed to apply transform:', e);\n this.transformedReceivers.delete(receiver);\n this.vp8Extractors.delete(receiver);\n }\n }\n\n\n async connect(config: RTCConnectionConfig): Promise<void> {\n logger.info('LiveKit', 'connect() called with config:', {\n hasUrl: 'url' in config,\n hasRoomName: 'roomName' in config,\n hasToken: 'token' in config,\n config,\n });\n\n if (!isLiveKitConfig(config)) {\n logger.error('LiveKit', 'Config validation failed - missing url or roomName');\n throw new Error('LiveKitProvider requires url and roomName in connection config');\n }\n\n const livekitConfig: LiveKitConnectionConfig = config;\n logger.info('LiveKit', 'Config validated, connecting to:', livekitConfig.url);\n\n const livekit = await this.loadSDK();\n logger.info('LiveKit', 'SDK loaded successfully');\n const { Room } = livekit;\n\n this.room = new Room({\n videoCaptureDefaults: undefined,\n audioCaptureDefaults: undefined,\n });\n\n this.setConnectionState('connecting');\n\n // Setup event listeners\n this.setupEventListeners(livekit);\n\n try {\n logger.info('LiveKit', 'Attempting to connect to room...');\n await this.room.connect(livekitConfig.url, livekitConfig.token);\n logger.info('LiveKit', 'Room connected, state:', this.room.state);\n logger.info('LiveKit', 'Room name:', this.room.name);\n logger.info('LiveKit', 'Local participant:', this.room.localParticipant?.identity);\n logger.info('LiveKit', 'Remote participants:', this.room.remoteParticipants.size);\n \n // Log existing participants and their tracks\n this.room.remoteParticipants.forEach((participant: any, sid: string) => {\n logger.info('LiveKit', `Remote participant: ${participant.identity}, sid: ${sid}`);\n participant.trackPublications.forEach((pub: any, trackSid: string) => {\n logger.info('LiveKit', ` Track: ${pub.trackName}, kind: ${pub.kind}, subscribed: ${pub.isSubscribed}, sid: ${trackSid}`);\n });\n });\n\n this.setConnectionState(this.mapLiveKitConnectionState(livekit, this.room.state));\n this.emit('connected');\n } catch (error) {\n logger.error('LiveKit', 'Connection failed:', error);\n this.setConnectionState('failed');\n this.emit('error', error as Error);\n throw error;\n }\n }\n\n /**\n * Setup LiveKit room event listeners\n * @internal\n */\n private setupEventListeners(livekit: LiveKitModule): void {\n if (!this.room) return;\n \n const room = this.room;\n const { RoomEvent, Track } = livekit;\n\n // Connected\n room.on(RoomEvent.Connected, () => {\n this.setConnectionState('connected');\n this.emit('connected');\n });\n\n // Participant connected\n room.on(RoomEvent.ParticipantConnected, (participant: any) => {\n logger.info('LiveKit', `ParticipantConnected: ${participant.identity}`);\n });\n\n // Track published\n room.on(RoomEvent.TrackPublished, (publication: any, participant: any) => {\n logger.info('LiveKit', 'TrackPublished:', {\n trackName: publication.trackName,\n kind: publication.kind,\n participant: participant.identity,\n });\n });\n\n // Disconnected\n room.on(RoomEvent.Disconnected, () => {\n this.cleanup();\n this.setConnectionState('disconnected');\n this.emit('disconnected');\n });\n\n // Track subscribed\n this.room.on(\n RoomEvent.TrackSubscribed,\n (track: any, publication: any, participant: any) => {\n logger.info('LiveKit', 'TrackSubscribed:', {\n kind: track.kind,\n trackName: publication.trackName,\n participant: participant.identity,\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mediaStreamTrack = (track as any).mediaStreamTrack as MediaStreamTrack | undefined;\n const trackName = publication.trackName;\n\n if (track.kind === Track.Kind.Audio) {\n logger.info('LiveKit', 'Audio track received, attaching...');\n // Audio track\n const audioElement = track.attach() as HTMLAudioElement;\n audioElement.id = `audio-${participant.identity}`;\n audioElement.autoplay = true;\n audioElement.controls = false;\n audioElement.style.display = 'none';\n \n if (typeof document !== 'undefined') {\n document.body.appendChild(audioElement);\n logger.info('LiveKit', 'Audio element appended to body');\n }\n \n this.audioElements.set(participant.identity, audioElement);\n\n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioReceived?.(participant);\n }\n } else if (track.kind === Track.Kind.Video) {\n logger.info('LiveKit', 'Video track received, setting up animation transform...');\n // Video track - apply animation transform\n if (mediaStreamTrack) {\n const receiver = this.findVideoReceiverByTrackId(mediaStreamTrack.id);\n if (receiver) {\n this.receiverParticipantMap.set(receiver, { participant, trackName });\n \n // Apply transform if not already applied in handleEarlyTrack\n if (!receiver.transform || !this.transformedReceivers.has(receiver)) {\n this.applyAnimationReceiverTransform(receiver, mediaStreamTrack);\n }\n }\n }\n\n // Attach to hidden video element to trigger data flow\n const videoElement = track.attach() as HTMLVideoElement;\n videoElement.id = `video-animation-${participant.identity}`;\n videoElement.muted = true;\n videoElement.autoplay = true;\n videoElement.style.display = 'none';\n if (typeof document !== 'undefined') {\n document.body.appendChild(videoElement);\n videoElement.play().catch(() => {});\n }\n }\n }\n );\n\n // Track unsubscribed\n this.room.on(\n RoomEvent.TrackUnsubscribed,\n (track: any, _publication: any, participant: any) => {\n if (track.kind === Track.Kind.Audio) {\n track.detach().forEach((el: HTMLElement) => el.remove());\n this.audioElements.delete(participant.identity);\n \n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioLost?.(participant);\n }\n }\n }\n );\n\n // Connection state changed\n room.on(RoomEvent.ConnectionStateChanged, (state: unknown) => {\n this.setConnectionState(this.mapLiveKitConnectionState(livekit, state));\n });\n }\n\n async disconnect(): Promise<void> {\n this.cleanup();\n if (this.room) {\n this.room.disconnect();\n this.room = null;\n }\n this.setConnectionState('disconnected');\n globalLiveKitProviderRegistry.delete(this);\n }\n\n getConnectionState(): string {\n if (!this.room) {\n return 'disconnected';\n }\n return this.connectionState;\n }\n\n async subscribeAnimationTrack(callbacks: AnimationTrackCallbacks): Promise<void> {\n this.animationCallbacks = callbacks;\n \n // If room is already connected, check for existing tracks\n if (this.room) {\n const livekit = await this.loadSDK();\n const { ConnectionState: LKConnectionState } = livekit;\n \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const roomState = (this.room as any).state;\n \n if (roomState === LKConnectionState.Connected || roomState === 'connected') {\n // Check for existing video tracks that might be animation tracks\n const remoteParticipants = this.room.remoteParticipants.values();\n for (const participant of remoteParticipants) {\n // Get video track publications (handle different SDK versions)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pubs = (participant as any).videoTrackPublications ?? (participant as any).trackPublications;\n if (pubs && typeof pubs.values === 'function') {\n for (const publication of pubs.values()) {\n if (publication.trackName?.includes('animation') && publication.track) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mediaStreamTrack = (publication.track as any).mediaStreamTrack as MediaStreamTrack | undefined;\n if (mediaStreamTrack) {\n const receiver = this.findVideoReceiverByTrackId(mediaStreamTrack.id);\n if (receiver) {\n this.applyAnimationReceiverTransform(receiver, mediaStreamTrack);\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n async unsubscribeAnimationTrack(): Promise<void> {\n this.animationCallbacks = null;\n \n // Dispose all extractors\n this.vp8Extractors.forEach((extractor) => {\n extractor.dispose();\n });\n this.vp8Extractors.clear();\n \n if (this.vp8Extractor) {\n this.vp8Extractor.dispose();\n this.vp8Extractor = null;\n }\n this.transformedReceivers.clear();\n this.receiverParticipantMap.clear();\n }\n\n async subscribeAudioTrack(callbacks: AudioTrackCallbacks): Promise<void> {\n this.audioCallbacks = callbacks;\n // Audio tracks are automatically handled in TrackSubscribed event\n }\n\n async unsubscribeAudioTrack(): Promise<void> {\n this.audioCallbacks = null;\n this.audioElements.forEach((el) => {\n el.pause();\n el.remove();\n });\n this.audioElements.clear();\n }\n\n async publishAudioTrack(track: MediaStreamTrack): Promise<void> {\n if (!this.room) {\n throw new Error('Not connected to room');\n }\n\n try {\n const livekit = await this.loadSDK();\n const { createLocalAudioTrack, Track: TrackEnum } = livekit;\n\n // If track is provided, use it; otherwise create from microphone\n if (track) {\n await this.room.localParticipant.publishTrack(track, {\n name: 'user-microphone',\n source: TrackEnum.Source.Microphone,\n red: false,\n });\n } else {\n // Create and publish microphone track\n const localTrack = await createLocalAudioTrack({\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n });\n \n this.localAudioTrack = localTrack;\n \n await this.room.localParticipant.publishTrack(localTrack, {\n name: 'user-microphone',\n source: TrackEnum.Source.Microphone,\n red: false,\n });\n }\n\n this.isMicrophoneEnabled = true;\n } catch (error) {\n logger.error('LiveKit', 'Failed to publish audio track:', error);\n this.isMicrophoneEnabled = false;\n throw error;\n }\n }\n\n async unpublishAudioTrack(): Promise<void> {\n if (!this.room || !this.localAudioTrack) {\n return;\n }\n\n await this.room.localParticipant.unpublishTrack(this.localAudioTrack);\n this.localAudioTrack.stop();\n this.localAudioTrack = null;\n this.isMicrophoneEnabled = false;\n }\n\n /**\n * Play remote audio (resume playback)\n */\n playRemoteAudio(): void {\n this.audioElements.forEach((audioElement) => {\n if (audioElement.paused) {\n audioElement.play().catch(() => {});\n }\n });\n }\n\n /**\n * Pause remote audio\n */\n pauseRemoteAudio(): void {\n this.audioElements.forEach((audioElement) => {\n if (!audioElement.paused) {\n audioElement.pause();\n }\n });\n }\n\n /**\n * Get the native LiveKit Room instance.\n * \n * Allows advanced users to access LiveKit-specific features\n * not exposed through the unified API.\n * \n * @returns The LiveKit Room instance, or null if not connected\n * \n * @example\n * ```typescript\n * const room = provider.getNativeClient();\n * if (room) {\n * // Access LiveKit-specific features\n * console.log('Participants:', room.remoteParticipants.size);\n * }\n * ```\n */\n getNativeClient(): LiveKitRoom | null {\n return this.room;\n }\n\n /**\n * Cleanup resources\n * @internal\n */\n private cleanup(): void {\n // Cleanup audio elements\n this.audioElements.forEach((el) => {\n el.pause();\n el.remove();\n });\n this.audioElements.clear();\n \n // Dispose all extractors\n this.vp8Extractors.forEach((extractor) => extractor.dispose());\n this.vp8Extractors.clear();\n this.vp8Extractor = null;\n \n this.transformedReceivers.clear();\n this.receiverParticipantMap.clear();\n\n // Stop local audio track\n if (this.localAudioTrack) {\n this.localAudioTrack.stop();\n this.localAudioTrack = null;\n }\n this.isMicrophoneEnabled = false;\n }\n}\n"],"names":["_a","_b"],"mappings":"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"}