@spatialwalk/avatarkit-rtc 1.0.0-beta.1 → 1.0.0-beta.10

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 (63) hide show
  1. package/README.md +2 -410
  2. package/dist/assets/animation-worker-DOGeTjF0.js.map +1 -0
  3. package/dist/core/AvatarPlayer.d.ts +96 -12
  4. package/dist/core/AvatarPlayer.d.ts.map +1 -1
  5. package/dist/core/RTCProvider.d.ts +12 -16
  6. package/dist/core/RTCProvider.d.ts.map +1 -1
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +3 -2
  10. package/dist/index10.js +91 -39
  11. package/dist/index10.js.map +1 -1
  12. package/dist/index11.js +14 -386
  13. package/dist/index11.js.map +1 -1
  14. package/dist/index12.js +350 -64
  15. package/dist/index12.js.map +1 -1
  16. package/dist/index13.js +44 -14
  17. package/dist/index13.js.map +1 -1
  18. package/dist/index14.js +25 -44
  19. package/dist/index14.js.map +1 -1
  20. package/dist/index2.js +335 -46
  21. package/dist/index2.js.map +1 -1
  22. package/dist/index3.js +265 -54
  23. package/dist/index3.js.map +1 -1
  24. package/dist/index4.js +105 -86
  25. package/dist/index4.js.map +1 -1
  26. package/dist/index5.js +6 -2
  27. package/dist/index5.js.map +1 -1
  28. package/dist/index6.js +603 -39
  29. package/dist/index6.js.map +1 -1
  30. package/dist/index8.js +128 -167
  31. package/dist/index8.js.map +1 -1
  32. package/dist/index9.js +65 -164
  33. package/dist/index9.js.map +1 -1
  34. package/dist/providers/agora/AgoraProvider.d.ts +0 -13
  35. package/dist/providers/agora/AgoraProvider.d.ts.map +1 -1
  36. package/dist/providers/agora/index.d.ts +1 -5
  37. package/dist/providers/agora/index.d.ts.map +1 -1
  38. package/dist/providers/agora/types.d.ts.map +1 -1
  39. package/dist/providers/base/BaseProvider.d.ts +50 -8
  40. package/dist/providers/base/BaseProvider.d.ts.map +1 -1
  41. package/dist/providers/livekit/LiveKitProvider.d.ts +4 -15
  42. package/dist/providers/livekit/LiveKitProvider.d.ts.map +1 -1
  43. package/dist/providers/livekit/animation-worker.d.ts.map +1 -1
  44. package/dist/providers/livekit/index.d.ts +1 -5
  45. package/dist/providers/livekit/index.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +21 -0
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/utils/index.d.ts +2 -0
  49. package/dist/utils/index.d.ts.map +1 -1
  50. package/dist/utils/telemetry.d.ts +22 -0
  51. package/dist/utils/telemetry.d.ts.map +1 -0
  52. package/package.json +21 -12
  53. package/dist/assets/animation-worker-CUXZycUw.js.map +0 -1
  54. package/dist/index15.js +0 -29
  55. package/dist/index15.js.map +0 -1
  56. package/dist/index16.js +0 -144
  57. package/dist/index16.js.map +0 -1
  58. package/dist/index17.js +0 -106
  59. package/dist/index17.js.map +0 -1
  60. package/dist/index18.js +0 -28
  61. package/dist/index18.js.map +0 -1
  62. package/dist/proto/animation.d.ts +0 -12
  63. package/dist/proto/animation.d.ts.map +0 -1
@@ -1 +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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"}
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 {\n RTCConnectionConfig,\n RTCPrepareConnectionConfig,\n LiveKitConnectionConfig,\n} from '../../types';\nimport { isLiveKitConfig, isLiveKitPrepareConfig } from '../../types';\nimport type {\n AnimationTrackCallbacks,\n AudioTrackCallbacks,\n} 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 (\n (trackEvent.track?.kind === 'audio' ||\n trackEvent.track?.kind === 'video') &&\n trackEvent.receiver\n ) {\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(\n this,\n type,\n wrappedListener as EventListener,\n options,\n );\n }\n\n return originalAddEventListener.call(this, type, listener, options);\n };\n\n // Also patch the ontrack setter\n const originalOnTrackDescriptor = Object.getOwnPropertyDescriptor(\n RTCPeerConnection.prototype,\n 'ontrack',\n );\n if (originalOnTrackDescriptor) {\n Object.defineProperty(RTCPeerConnection.prototype, 'ontrack', {\n get: originalOnTrackDescriptor.get,\n set: function (\n handler: ((this: RTCPeerConnection, ev: RTCTrackEvent) => void) | null,\n ) {\n if (handler) {\n const wrappedHandler = function (\n this: RTCPeerConnection,\n event: RTCTrackEvent,\n ) {\n if (\n (event.track?.kind === 'audio' ||\n event.track?.kind === 'video') &&\n event.receiver\n ) {\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<\n RTCRtpReceiver,\n { participant: unknown; trackName?: string }\n > = 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 /** @internal */\n private awaitingAudioUnlock = false;\n /** @internal */\n private readonly audioUnlockEventNames: Array<keyof WindowEventMap> = [\n 'pointerdown',\n 'keydown',\n 'touchstart',\n ];\n /** @internal */\n private readonly handleAudioUnlockGesture = (): void => {\n void this.ensureAudioPlaybackUnlocked('user-gesture');\n };\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 private createRoom(livekit: LiveKitModule): LiveKitRoom {\n const { Room } = livekit;\n return new Room({\n videoCaptureDefaults: undefined,\n audioCaptureDefaults: undefined,\n });\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 {\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 async prepareConnection(config: RTCPrepareConnectionConfig): Promise<void> {\n logger.info('LiveKit', 'prepareConnection() called with config:', {\n hasUrl: 'url' in config,\n hasToken: 'token' in config,\n });\n\n if (!isLiveKitPrepareConfig(config)) {\n throw new Error('LiveKitProvider requires url in prepareConnection config');\n }\n\n const livekit = await this.loadSDK();\n\n if (!this.room) {\n this.room = this.createRoom(livekit);\n }\n\n try {\n await this.room.prepareConnection(config.url, config.token);\n logger.info('LiveKit', 'prepareConnection() completed');\n } catch (error) {\n logger.warn('LiveKit', 'prepareConnection() failed (non-fatal):', error);\n }\n }\n\n /**\n * Map LiveKit connection state to string.\n * @internal\n */\n private mapLiveKitConnectionState(\n livekit: typeof import('livekit-client'),\n state: unknown,\n ): 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(\n receiver: RTCRtpReceiver,\n mediaTrack: MediaStreamTrack,\n ): 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 * Add one-time user gesture listeners to recover from autoplay blocking.\n * @internal\n */\n private addAudioUnlockListeners(): void {\n if (typeof window === 'undefined' || this.awaitingAudioUnlock) {\n return;\n }\n\n this.awaitingAudioUnlock = true;\n for (const eventName of this.audioUnlockEventNames) {\n window.addEventListener(eventName, this.handleAudioUnlockGesture, {\n capture: true,\n passive: true,\n });\n }\n\n logger.warn(\n 'LiveKit',\n 'Audio playback blocked by autoplay policy, waiting for user interaction',\n );\n }\n\n /**\n * Remove user gesture listeners after playback is unlocked.\n * @internal\n */\n private removeAudioUnlockListeners(): void {\n if (typeof window === 'undefined' || !this.awaitingAudioUnlock) {\n return;\n }\n\n this.awaitingAudioUnlock = false;\n for (const eventName of this.audioUnlockEventNames) {\n window.removeEventListener(\n eventName,\n this.handleAudioUnlockGesture,\n true,\n );\n }\n }\n\n /**\n * Detect browser autoplay blocking errors from audio play/start calls.\n * @internal\n */\n private isAutoplayBlockedError(error: unknown): boolean {\n if (typeof DOMException !== 'undefined' && error instanceof DOMException) {\n return error.name === 'NotAllowedError';\n }\n\n if (error instanceof Error) {\n return (\n error.name === 'NotAllowedError' ||\n /didn't interact|user didn't interact|NotAllowedError/i.test(\n error.message,\n )\n );\n }\n\n return false;\n }\n\n /**\n * Try to resume all attached remote audio elements.\n * @returns true if at least one element is still blocked by autoplay policy\n * @internal\n */\n private async resumeAttachedAudioElements(reason: string): Promise<boolean> {\n let hasAutoplayBlockedElement = false;\n\n for (const [participantIdentity, audioElement] of this.audioElements) {\n if (!audioElement.paused) {\n continue;\n }\n\n try {\n await audioElement.play();\n } catch (error) {\n if (this.isAutoplayBlockedError(error)) {\n hasAutoplayBlockedElement = true;\n }\n logger.warn(\n 'LiveKit',\n `Failed to resume remote audio element (${reason}) for ${participantIdentity}:`,\n error,\n );\n }\n }\n\n return hasAutoplayBlockedElement;\n }\n\n /**\n * Ensure remote audio playback is unlocked and resumed.\n * @internal\n */\n private async ensureAudioPlaybackUnlocked(reason: string): Promise<void> {\n if (!this.room) {\n return;\n }\n\n const roomWithAudioControl = this.room as LiveKitRoom & {\n startAudio?: () => Promise<void>;\n canPlaybackAudio: boolean;\n };\n\n if (\n !roomWithAudioControl.canPlaybackAudio &&\n typeof roomWithAudioControl.startAudio === 'function'\n ) {\n try {\n await roomWithAudioControl.startAudio();\n logger.info('LiveKit', `Audio context resumed (${reason})`);\n } catch (error) {\n if (this.isAutoplayBlockedError(error)) {\n this.addAudioUnlockListeners();\n } else {\n logger.warn(\n 'LiveKit',\n `Failed to call room.startAudio() (${reason}):`,\n error,\n );\n }\n return;\n }\n }\n\n const hasAutoplayBlockedElement =\n await this.resumeAttachedAudioElements(reason);\n if (roomWithAudioControl.canPlaybackAudio && !hasAutoplayBlockedElement) {\n this.removeAudioUnlockListeners();\n } else {\n this.addAudioUnlockListeners();\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(\n 'LiveKit',\n 'Config validation failed - missing url or token',\n );\n throw new Error(\n 'LiveKitProvider requires url and token in connection config',\n );\n }\n\n const livekitConfig: LiveKitConnectionConfig = config;\n logger.info(\n 'LiveKit',\n 'Config validated, connecting to:',\n livekitConfig.url,\n );\n\n this.removeAudioUnlockListeners();\n\n const livekit = await this.loadSDK();\n logger.info('LiveKit', 'SDK loaded successfully');\n if (!this.room) {\n this.room = this.createRoom(livekit);\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(\n 'LiveKit',\n 'Local participant:',\n this.room.localParticipant?.identity,\n );\n logger.info(\n 'LiveKit',\n 'Remote participants:',\n this.room.remoteParticipants.size,\n );\n\n // Log existing participants and their tracks\n this.room.remoteParticipants.forEach((participant: any, sid: string) => {\n logger.info(\n 'LiveKit',\n `Remote participant: ${participant.identity}, sid: ${sid}`,\n );\n participant.trackPublications.forEach((pub: any, trackSid: string) => {\n logger.info(\n 'LiveKit',\n ` Track: ${pub.trackName}, kind: ${pub.kind}, subscribed: ${pub.isSubscribed}, sid: ${trackSid}`,\n );\n });\n });\n\n this.setConnectionState(\n this.mapLiveKitConnectionState(livekit, this.room.state),\n );\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 const roomWithAudioControl = room as LiveKitRoom & {\n canPlaybackAudio: boolean;\n };\n if (!roomWithAudioControl.canPlaybackAudio) {\n this.addAudioUnlockListeners();\n }\n });\n\n // Participant connected\n room.on(RoomEvent.ParticipantConnected, (participant: any) => {\n logger.info('LiveKit', `Participant connected: ${participant.identity}`);\n });\n\n // Participant disconnected\n room.on(RoomEvent.ParticipantDisconnected, (participant: any) => {\n logger.warn(\n 'LiveKit',\n `Participant disconnected: ${participant.identity}`,\n );\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 room.on(\n RoomEvent.AudioPlaybackStatusChanged,\n (canPlaybackAudio: boolean) => {\n logger.info(\n 'LiveKit',\n `Audio playback status changed: ${canPlaybackAudio ? 'enabled' : 'blocked'}`,\n );\n\n if (canPlaybackAudio) {\n void this.ensureAudioPlaybackUnlocked(\n 'audio-playback-status-changed',\n );\n } else {\n this.addAudioUnlockListeners();\n }\n },\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\n | MediaStreamTrack\n | 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 const previousAudioElement = this.audioElements.get(\n participant.identity,\n );\n if (previousAudioElement && previousAudioElement !== audioElement) {\n previousAudioElement.remove();\n }\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 void this.ensureAudioPlaybackUnlocked('audio-track-subscribed');\n\n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioReceived?.(participant);\n }\n } else if (track.kind === Track.Kind.Video) {\n logger.info(\n 'LiveKit',\n 'Video track received, setting up animation transform...',\n );\n // Video track - apply animation transform\n if (mediaStreamTrack) {\n const receiver = this.findVideoReceiverByTrackId(\n mediaStreamTrack.id,\n );\n if (receiver) {\n this.receiverParticipantMap.set(receiver, {\n participant,\n trackName,\n });\n\n // Apply transform if not already applied in handleEarlyTrack\n if (\n !receiver.transform ||\n !this.transformedReceivers.has(receiver)\n ) {\n this.applyAnimationReceiverTransform(\n receiver,\n mediaStreamTrack,\n );\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 /** @internal */\n async subscribeAnimationTrack(\n callbacks: AnimationTrackCallbacks,\n ): 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 (\n roomState === LKConnectionState.Connected ||\n roomState === 'connected'\n ) {\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 =\n (participant as any).videoTrackPublications ??\n (participant as any).trackPublications;\n if (pubs && typeof pubs.values === 'function') {\n for (const publication of pubs.values()) {\n if (\n publication.trackName?.includes('animation') &&\n publication.track\n ) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mediaStreamTrack = (publication.track as any)\n .mediaStreamTrack as MediaStreamTrack | undefined;\n if (mediaStreamTrack) {\n const receiver = this.findVideoReceiverByTrackId(\n mediaStreamTrack.id,\n );\n if (receiver) {\n this.applyAnimationReceiverTransform(\n receiver,\n mediaStreamTrack,\n );\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n /** @internal */\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 /** @internal */\n async subscribeAudioTrack(callbacks: AudioTrackCallbacks): Promise<void> {\n this.audioCallbacks = callbacks;\n // Audio tracks are automatically handled in TrackSubscribed event\n }\n\n /** @internal */\n async unsubscribeAudioTrack(): Promise<void> {\n this.audioCallbacks = null;\n this.audioElements.forEach((el) => {\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 * 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 this.removeAudioUnlockListeners();\n\n // Cleanup audio elements\n this.audioElements.forEach((el) => {\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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"}
package/dist/index4.js CHANGED
@@ -1,9 +1,9 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { BaseProvider } from "./index10.js";
4
+ import { BaseProvider } from "./index9.js";
5
5
  import { isAgoraConfig, ConnectionState } from "./index5.js";
6
- import { SEIExtractor } from "./index11.js";
6
+ import { SEIExtractor } from "./index12.js";
7
7
  import { logger } from "./index7.js";
8
8
  class AgoraProvider extends BaseProvider {
9
9
  constructor(_options = {}) {
@@ -67,18 +67,21 @@ class AgoraProvider extends BaseProvider {
67
67
  }
68
68
  async connect(config) {
69
69
  if (!isAgoraConfig(config)) {
70
- throw new Error("AgoraProvider requires appId and channel in connection config");
70
+ throw new Error(
71
+ "AgoraProvider requires appId and channel in connection config"
72
+ );
71
73
  }
72
74
  const agoraConfig = config;
73
75
  const AgoraRTC = await this.loadSDK();
74
76
  AgoraRTC.setParameter("ENABLE_VIDEO_SEI", true);
77
+ AgoraRTC.setParameter("ENABLE_FULL_LINK_AV_SYNC", true);
75
78
  this.client = AgoraRTC.createClient({
76
79
  mode: "rtc",
77
80
  codec: "h264"
78
81
  // Required for SEI support
79
82
  });
80
83
  this.setConnectionState("connecting");
81
- this.setupEventListeners(AgoraRTC);
84
+ this.setupEventListeners();
82
85
  try {
83
86
  await this.client.join(
84
87
  agoraConfig.appId,
@@ -98,74 +101,99 @@ class AgoraProvider extends BaseProvider {
98
101
  * Setup Agora client event listeners
99
102
  * @internal
100
103
  */
101
- setupEventListeners(_AgoraRTC) {
102
- this.client.on("connection-state-change", (curState, _revState) => {
103
- const mapConnectionState = (state) => {
104
- switch (state) {
105
- case "DISCONNECTED":
106
- case "DISCONNECTING":
107
- return ConnectionState.Disconnected;
108
- case "CONNECTING":
109
- return ConnectionState.Connecting;
110
- case "CONNECTED":
111
- return ConnectionState.Connected;
112
- case "RECONNECTING":
113
- return ConnectionState.Reconnecting;
114
- default:
115
- return ConnectionState.Failed;
104
+ setupEventListeners() {
105
+ this.client.on(
106
+ "connection-state-change",
107
+ (curState, _revState) => {
108
+ const mapConnectionState = (state) => {
109
+ switch (state) {
110
+ case "DISCONNECTED":
111
+ case "DISCONNECTING":
112
+ return ConnectionState.Disconnected;
113
+ case "CONNECTING":
114
+ return ConnectionState.Connecting;
115
+ case "CONNECTED":
116
+ return ConnectionState.Connected;
117
+ case "RECONNECTING":
118
+ return ConnectionState.Reconnecting;
119
+ default:
120
+ return ConnectionState.Failed;
121
+ }
122
+ };
123
+ this.setConnectionState(mapConnectionState(curState));
124
+ if (curState === "CONNECTED") {
125
+ this.emit("connected");
126
+ } else if (curState === "DISCONNECTED") {
127
+ this.cleanup();
128
+ this.emit("disconnected");
116
129
  }
117
- };
118
- this.setConnectionState(mapConnectionState(curState));
119
- if (curState === "CONNECTED") {
120
- this.emit("connected");
121
- } else if (curState === "DISCONNECTED") {
122
- this.cleanup();
123
- this.emit("disconnected");
124
- }
125
- });
126
- this.client.on("user-published", async (user, mediaType) => {
127
- if (this.debugLogging) {
128
- logger.info("Agora", `User published: ${user.uid}, mediaType=${mediaType}`);
129
130
  }
130
- try {
131
- await this.client.subscribe(user, mediaType);
132
- if (mediaType === "video") {
133
- if (user.videoTrack) {
134
- this.handleVideoTrack(user, user.videoTrack);
135
- } else {
136
- logger.warn("Agora", `Video track is null after subscribe for user ${user.uid}`);
137
- }
138
- } else if (mediaType === "audio") {
139
- if (user.audioTrack) {
140
- this.handleAudioTrack(user, user.audioTrack);
141
- } else {
142
- logger.warn("Agora", `Audio track is null after subscribe for user ${user.uid}`);
131
+ );
132
+ this.client.on(
133
+ "user-published",
134
+ async (user, mediaType) => {
135
+ if (this.debugLogging) {
136
+ logger.info(
137
+ "Agora",
138
+ `User published: ${user.uid}, mediaType=${mediaType}`
139
+ );
140
+ }
141
+ try {
142
+ await this.client.subscribe(user, mediaType);
143
+ if (mediaType === "video") {
144
+ if (user.videoTrack) {
145
+ this.handleVideoTrack(user, user.videoTrack);
146
+ } else {
147
+ logger.warn(
148
+ "Agora",
149
+ `Video track is null after subscribe for user ${user.uid}`
150
+ );
151
+ }
152
+ } else if (mediaType === "audio") {
153
+ if (user.audioTrack) {
154
+ this.handleAudioTrack(user, user.audioTrack);
155
+ } else {
156
+ logger.warn(
157
+ "Agora",
158
+ `Audio track is null after subscribe for user ${user.uid}`
159
+ );
160
+ }
143
161
  }
162
+ } catch (error) {
163
+ logger.error(
164
+ "Agora",
165
+ `Failed to subscribe to ${mediaType} from ${user.uid}:`,
166
+ error
167
+ );
168
+ this.emit("error", error);
144
169
  }
145
- } catch (error) {
146
- logger.error("Agora", `Failed to subscribe to ${mediaType} from ${user.uid}:`, error);
147
- this.emit("error", error);
148
170
  }
149
- });
150
- this.client.on("user-unpublished", (user, mediaType) => {
151
- var _a, _b;
152
- if (this.debugLogging) {
153
- logger.info("Agora", `User unpublished: ${user.uid}, mediaType=${mediaType}`);
154
- }
155
- if (mediaType === "video") {
156
- this.removeVideoContainer(user.uid);
157
- this.remoteUsers.delete(user.uid);
158
- } else if (mediaType === "audio") {
159
- const track = this.remoteAudioTracks.get(user.uid);
160
- if (track) {
161
- track.stop();
162
- this.remoteAudioTracks.delete(user.uid);
171
+ );
172
+ this.client.on(
173
+ "user-unpublished",
174
+ (user, mediaType) => {
175
+ var _a, _b;
176
+ if (this.debugLogging) {
177
+ logger.info(
178
+ "Agora",
179
+ `User unpublished: ${user.uid}, mediaType=${mediaType}`
180
+ );
163
181
  }
164
- if (this.audioCallbacks) {
165
- (_b = (_a = this.audioCallbacks).onAudioLost) == null ? void 0 : _b.call(_a, user);
182
+ if (mediaType === "video") {
183
+ this.removeVideoContainer(user.uid);
184
+ this.remoteUsers.delete(user.uid);
185
+ } else if (mediaType === "audio") {
186
+ const track = this.remoteAudioTracks.get(user.uid);
187
+ if (track) {
188
+ track.stop();
189
+ this.remoteAudioTracks.delete(user.uid);
190
+ }
191
+ if (this.audioCallbacks) {
192
+ (_b = (_a = this.audioCallbacks).onAudioLost) == null ? void 0 : _b.call(_a, user);
193
+ }
166
194
  }
167
195
  }
168
- });
196
+ );
169
197
  this.client.on("user-joined", (user) => {
170
198
  logger.info("Agora", `User joined: ${user.uid}`);
171
199
  });
@@ -209,7 +237,10 @@ class AgoraProvider extends BaseProvider {
209
237
  this.videoContainers.set(user.uid, container);
210
238
  track.play(container);
211
239
  if (this.debugLogging) {
212
- logger.info("Agora", `Video track playing, SEI listeners attached for user ${user.uid}`);
240
+ logger.info(
241
+ "Agora",
242
+ `Video track playing, SEI listeners attached for user ${user.uid}`
243
+ );
213
244
  }
214
245
  const existingUser = this.remoteUsers.get(user.uid);
215
246
  if (existingUser) {
@@ -263,6 +294,7 @@ class AgoraProvider extends BaseProvider {
263
294
  }
264
295
  return this.connectionState;
265
296
  }
297
+ /** @internal */
266
298
  async subscribeAnimationTrack(callbacks) {
267
299
  this._animationCallbacks = callbacks;
268
300
  this.seiExtractor = new SEIExtractor();
@@ -283,13 +315,14 @@ class AgoraProvider extends BaseProvider {
283
315
  }
284
316
  }
285
317
  }
318
+ /** @internal */
286
319
  async unsubscribeAnimationTrack() {
287
320
  this._animationCallbacks = null;
288
321
  if (this.seiExtractor) {
289
322
  this.seiExtractor.dispose();
290
323
  this.seiExtractor = null;
291
324
  }
292
- for (const [uid, user] of this.remoteUsers) {
325
+ for (const [_uid, user] of this.remoteUsers) {
293
326
  const videoTrack = user.videoTrack;
294
327
  if (videoTrack) {
295
328
  videoTrack.off("sei-received");
@@ -301,9 +334,11 @@ class AgoraProvider extends BaseProvider {
301
334
  }
302
335
  this.remoteUsers.clear();
303
336
  }
337
+ /** @internal */
304
338
  async subscribeAudioTrack(callbacks) {
305
339
  this.audioCallbacks = callbacks;
306
340
  }
341
+ /** @internal */
307
342
  async unsubscribeAudioTrack() {
308
343
  this.audioCallbacks = null;
309
344
  this.remoteAudioTracks.forEach((track) => {
@@ -338,30 +373,14 @@ class AgoraProvider extends BaseProvider {
338
373
  this.localAudioTrack.close();
339
374
  this.localAudioTrack = null;
340
375
  }
341
- /**
342
- * Play remote audio (resume playback)
343
- */
344
- playRemoteAudio() {
345
- this.remoteAudioTracks.forEach((track) => {
346
- track.play();
347
- });
348
- }
349
- /**
350
- * Pause remote audio
351
- */
352
- pauseRemoteAudio() {
353
- this.remoteAudioTracks.forEach((track) => {
354
- track.stop();
355
- });
356
- }
357
376
  /**
358
377
  * Get the native Agora RTC Client instance.
359
- *
378
+ *
360
379
  * Allows advanced users to access Agora-specific features
361
380
  * not exposed through the unified API.
362
- *
381
+ *
363
382
  * @returns The Agora IAgoraRTCClient instance, or null if not connected
364
- *
383
+ *
365
384
  * @example
366
385
  * ```typescript
367
386
  * const client = provider.getNativeClient();
@@ -383,7 +402,7 @@ class AgoraProvider extends BaseProvider {
383
402
  track.stop();
384
403
  });
385
404
  this.remoteAudioTracks.clear();
386
- for (const [uid, user] of this.remoteUsers) {
405
+ for (const [_uid, user] of this.remoteUsers) {
387
406
  const videoTrack = user.videoTrack;
388
407
  if (videoTrack) {
389
408
  videoTrack.off("sei-received");
@@ -1 +1 @@
1
- {"version":3,"file":"index4.js","sources":["../src/providers/agora/AgoraProvider.ts"],"sourcesContent":["/**\n * Agora Provider Implementation.\n *\n * This provider uses Agora's H.264 SEI approach\n * to transport animation data.\n *\n * Key differences from LiveKit:\n * - Uses native SEI events instead of RTCRtpScriptTransform\n * - No ALR (Application-Level Redundancy) needed - Agora handles reliability\n * - Simpler data extraction - uses SEI header parsing\n *\n * @packageDocumentation\n */\n\nimport { BaseProvider } from '../base/BaseProvider';\nimport type { RTCConnectionConfig, AgoraConnectionConfig } from '../../types';\nimport { isAgoraConfig, ConnectionState } from '../../types';\nimport type { AnimationTrackCallbacks, AudioTrackCallbacks } from '../../core/types';\nimport { SEIExtractor } from './SEIExtractor';\nimport { logger } from '../../utils';\nimport type {\n AgoraClient,\n AgoraLocalAudioTrack,\n AgoraRemoteAudioTrack,\n AgoraRemoteVideoTrack,\n AgoraUID,\n} from './types';\n\n/**\n * Agora Provider options.\n * @internal Reserved for future use\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface AgoraProviderOptions {\n // Reserved for future configuration options\n}\n\n/**\n * Remote user info with tracks\n * @internal\n */\ninterface RemoteUserInfo {\n videoTrack?: AgoraRemoteVideoTrack;\n audioTrack?: AgoraRemoteAudioTrack;\n}\n\n/**\n * Agora Provider.\n *\n * Implements RTCProvider interface for Agora platform.\n * Uses native SEI events to receive animation data from H.264 video tracks.\n *\n * @example\n * ```typescript\n * import { AvatarPlayer } from '@spatialwalk/avatarkit-rtc';\n * import { AgoraProvider } from '@spatialwalk/avatarkit-rtc/providers/agora';\n *\n * const provider = new AgoraProvider();\n * const player = new AvatarPlayer(provider, renderer);\n *\n * await player.connect({\n * appId: 'your-agora-app-id',\n * channel: 'your-channel',\n * token: 'your-token',\n * });\n * ```\n */\nexport class AgoraProvider extends BaseProvider {\n /** Provider name identifier */\n readonly name = 'agora';\n\n /** @internal */\n private client: AgoraClient | null = null;\n /** @internal Dynamic SDK - type is any due to dynamic import */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private agoraSDK: any = null;\n\n // Animation track subscription (stored for cleanup, actual callbacks handled by SEIExtractor)\n /** @internal */\n private _animationCallbacks: AnimationTrackCallbacks | null = null;\n /** @internal */\n private seiExtractor: SEIExtractor | null = null;\n /** @internal */\n private remoteUsers: Map<AgoraUID, RemoteUserInfo> = new Map();\n /** @internal */\n private videoContainers: Map<AgoraUID, HTMLDivElement> = new Map();\n\n // Audio track subscription\n /** @internal */\n private audioCallbacks: AudioTrackCallbacks | null = null;\n /** @internal */\n private localAudioTrack: AgoraLocalAudioTrack | null = null;\n /** @internal */\n private remoteAudioTracks: Map<AgoraUID, AgoraRemoteAudioTrack> = new Map();\n\n // Debug mode\n /** @internal */\n private debugLogging = false;\n\n constructor(_options: AgoraProviderOptions = {}) {\n super();\n }\n\n /**\n * Enable or disable debug logging.\n * @param enabled - Whether to enable debug logging\n */\n setDebugLogging(enabled: boolean): void {\n this.debugLogging = enabled;\n if (this.seiExtractor) {\n this.seiExtractor.setDebugLogging(enabled);\n }\n }\n\n /**\n * Load Agora SDK dynamically.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private async loadSDK(): Promise<any> {\n if (this.agoraSDK) {\n return this.agoraSDK;\n }\n\n try {\n // Direct dynamic import - Vite will handle this properly with optimizeDeps\n // Using 'any' type due to dynamic import type inference limitations\n const sdk = await import('agora-rtc-sdk-ng');\n // Agora SDK exports default as the main AgoraRTC object\n this.agoraSDK = sdk.default ?? sdk;\n return this.agoraSDK;\n } catch (error) {\n logger.error('Agora', 'Failed to load SDK:', error);\n throw new Error(\n '❌ Failed to load agora-rtc-sdk-ng.\\n' +\n 'Please ensure it is installed: pnpm add agora-rtc-sdk-ng'\n );\n }\n }\n\n async connect(config: RTCConnectionConfig): Promise<void> {\n if (!isAgoraConfig(config)) {\n throw new Error('AgoraProvider requires appId and channel in connection config');\n }\n\n const agoraConfig: AgoraConnectionConfig = config;\n\n const AgoraRTC = await this.loadSDK();\n\n // Enable SEI reception (required for animation data)\n AgoraRTC.setParameter('ENABLE_VIDEO_SEI', true);\n\n this.client = AgoraRTC.createClient({ \n mode: 'rtc', \n codec: 'h264' // Required for SEI support\n });\n\n this.setConnectionState('connecting');\n\n // Setup event listeners\n this.setupEventListeners(AgoraRTC);\n\n try {\n await this.client.join(\n agoraConfig.appId,\n agoraConfig.channel,\n agoraConfig.token || null,\n agoraConfig.uid ?? null\n );\n \n this.setConnectionState('connected');\n this.emit('connected');\n } catch (error) {\n this.setConnectionState('failed');\n this.emit('error', error as Error);\n throw error;\n }\n }\n\n /**\n * Setup Agora client event listeners\n * @internal\n */\n private setupEventListeners(_AgoraRTC: typeof import('agora-rtc-sdk-ng')): void {\n // Connection state changed\n this.client.on('connection-state-change', (curState: string, _revState: string) => {\n const mapConnectionState = (state: string): ConnectionState => {\n switch (state) {\n case 'DISCONNECTED':\n case 'DISCONNECTING':\n return ConnectionState.Disconnected;\n case 'CONNECTING':\n return ConnectionState.Connecting;\n case 'CONNECTED':\n return ConnectionState.Connected;\n case 'RECONNECTING':\n return ConnectionState.Reconnecting;\n default:\n return ConnectionState.Failed;\n }\n };\n \n this.setConnectionState(mapConnectionState(curState));\n \n if (curState === 'CONNECTED') {\n this.emit('connected');\n } else if (curState === 'DISCONNECTED') {\n this.cleanup();\n this.emit('disconnected');\n }\n });\n\n // User published (remote user joined and published tracks)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on('user-published', async (user: any, mediaType: 'audio' | 'video') => {\n if (this.debugLogging) {\n logger.info('Agora', `User published: ${user.uid}, mediaType=${mediaType}`);\n }\n\n try {\n // Subscribe to the track\n await this.client.subscribe(user, mediaType);\n\n if (mediaType === 'video') {\n if (user.videoTrack) {\n this.handleVideoTrack(user, user.videoTrack);\n } else {\n logger.warn('Agora', `Video track is null after subscribe for user ${user.uid}`);\n }\n } else if (mediaType === 'audio') {\n if (user.audioTrack) {\n this.handleAudioTrack(user, user.audioTrack);\n } else {\n logger.warn('Agora', `Audio track is null after subscribe for user ${user.uid}`);\n }\n }\n } catch (error) {\n logger.error('Agora', `Failed to subscribe to ${mediaType} from ${user.uid}:`, error);\n this.emit('error', error as Error);\n }\n });\n\n // User unpublished (remote user stopped publishing)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on('user-unpublished', (user: any, mediaType: 'audio' | 'video') => {\n if (this.debugLogging) {\n logger.info('Agora', `User unpublished: ${user.uid}, mediaType=${mediaType}`);\n }\n\n if (mediaType === 'video') {\n this.removeVideoContainer(user.uid);\n this.remoteUsers.delete(user.uid);\n } else if (mediaType === 'audio') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const track = this.remoteAudioTracks.get(user.uid) as any;\n if (track) {\n track.stop();\n this.remoteAudioTracks.delete(user.uid);\n }\n \n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioLost?.(user);\n }\n }\n });\n\n // User joined\n this.client.on('user-joined', (user: any) => {\n logger.info('Agora', `User joined: ${user.uid}`);\n });\n\n // User left\n this.client.on('user-left', (user: any, reason: string) => {\n logger.info('Agora', `User left: ${user.uid}, reason: ${reason}`);\n this.remoteUsers.delete(user.uid);\n const audioTrack = this.remoteAudioTracks.get(user.uid);\n if (audioTrack) {\n audioTrack.stop();\n this.remoteAudioTracks.delete(user.uid);\n }\n });\n\n // Error\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on('exception', (error: any) => {\n logger.error('Agora', 'Exception:', error);\n this.emit('error', new Error(error.msg || String(error)));\n });\n }\n\n /**\n * Handle video track from remote user.\n * Sets up SEI event listeners and plays to hidden container.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private handleVideoTrack(user: any, track: any): void {\n if (this.debugLogging) {\n logger.info('Agora', `Handling video track from ${user.uid}`);\n }\n\n // Listen for SEI data (animation frames)\n // Support both event names for SDK compatibility\n const seiHandler = (seiData: Uint8Array) => {\n if (this.seiExtractor) {\n this.seiExtractor.handleSEIData(seiData);\n }\n };\n\n // Try primary event name\n track.on('sei-received', seiHandler);\n // Also try alternative event name used in some SDK versions\n track.on('video-sei-received', seiHandler);\n\n // Play video to hidden element to ensure data flows\n // This is required for SEI events to be received\n const container = document.createElement('div');\n container.style.display = 'none';\n container.style.position = 'absolute';\n container.style.left = '-9999px';\n container.id = `agora-video-${user.uid}`;\n document.body.appendChild(container);\n this.videoContainers.set(user.uid, container);\n track.play(container);\n\n if (this.debugLogging) {\n logger.info('Agora', `Video track playing, SEI listeners attached for user ${user.uid}`);\n }\n\n // Store track reference\n const existingUser = this.remoteUsers.get(user.uid);\n if (existingUser) {\n existingUser.videoTrack = track;\n } else {\n this.remoteUsers.set(user.uid, { videoTrack: track });\n }\n }\n\n /**\n * Handle audio track from remote user.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private handleAudioTrack(user: any, track: any): void {\n if (this.debugLogging) {\n logger.info('Agora', `Handling audio track from ${user.uid}`);\n }\n\n // Play audio\n track.play();\n this.remoteAudioTracks.set(user.uid, track);\n\n if (this.debugLogging) {\n logger.info('Agora', `Audio track playing for user ${user.uid}`);\n }\n\n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioReceived?.(user);\n }\n }\n\n /**\n * Remove video container for a user.\n * @internal\n */\n private removeVideoContainer(uid: number): void {\n const container = this.videoContainers.get(uid);\n if (container) {\n container.remove();\n this.videoContainers.delete(uid);\n }\n }\n\n async disconnect(): Promise<void> {\n this.cleanup();\n if (this.client) {\n await this.client.leave();\n this.client = null;\n }\n this.setConnectionState('disconnected');\n this.emit('disconnected');\n }\n\n getConnectionState(): string {\n if (!this.client) {\n return 'disconnected';\n }\n return this.connectionState;\n }\n\n async subscribeAnimationTrack(callbacks: AnimationTrackCallbacks): Promise<void> {\n this._animationCallbacks = callbacks;\n \n // Create SEI extractor\n this.seiExtractor = new SEIExtractor();\n this.seiExtractor.setDebugLogging(this.debugLogging);\n this.seiExtractor.initialize(callbacks);\n \n // If already connected, check for existing remote video tracks\n if (this.client) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const remoteUsers = (this.client as any).remoteUsers;\n for (const user of remoteUsers) {\n if (user.videoTrack) {\n // Subscribe to SEI events with both event names for compatibility\n const seiHandler = (seiData: Uint8Array) => {\n if (this.seiExtractor) {\n this.seiExtractor.handleSEIData(seiData);\n }\n };\n user.videoTrack.on('sei-received', seiHandler);\n user.videoTrack.on('video-sei-received', seiHandler);\n }\n }\n }\n }\n\n async unsubscribeAnimationTrack(): Promise<void> {\n this._animationCallbacks = null;\n if (this.seiExtractor) {\n this.seiExtractor.dispose();\n this.seiExtractor = null;\n }\n \n // Remove SEI listeners from remote tracks\n for (const [uid, user] of this.remoteUsers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const videoTrack = user.videoTrack as any;\n if (videoTrack) {\n videoTrack.off('sei-received');\n videoTrack.off('video-sei-received');\n }\n }\n \n // Remove video containers\n for (const [uid] of this.videoContainers) {\n this.removeVideoContainer(uid as number);\n }\n \n this.remoteUsers.clear();\n }\n\n async subscribeAudioTrack(callbacks: AudioTrackCallbacks): Promise<void> {\n this.audioCallbacks = callbacks;\n // Audio tracks are automatically handled in user-published event\n }\n\n async unsubscribeAudioTrack(): Promise<void> {\n this.audioCallbacks = null;\n this.remoteAudioTracks.forEach((track) => {\n track.stop();\n });\n this.remoteAudioTracks.clear();\n }\n\n async publishAudioTrack(track?: MediaStreamTrack): Promise<void> {\n if (!this.client) {\n throw new Error('Not connected to channel');\n }\n\n const AgoraRTC = await this.loadSDK();\n\n if (track) {\n // Create local audio track from provided MediaStreamTrack\n this.localAudioTrack = AgoraRTC.createCustomAudioTrack({\n mediaStreamTrack: track,\n });\n } else {\n // Create local audio track from microphone\n this.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack({\n encoderConfig: 'music_standard',\n AEC: true,\n ANS: true,\n AGC: true,\n });\n }\n\n // Publish the track\n await this.client.publish(this.localAudioTrack);\n }\n\n async unpublishAudioTrack(): Promise<void> {\n if (!this.client || !this.localAudioTrack) {\n return;\n }\n\n await this.client.unpublish(this.localAudioTrack);\n this.localAudioTrack.close();\n this.localAudioTrack = null;\n }\n\n /**\n * Play remote audio (resume playback)\n */\n playRemoteAudio(): void {\n this.remoteAudioTracks.forEach((track) => {\n track.play();\n });\n }\n\n /**\n * Pause remote audio\n */\n pauseRemoteAudio(): void {\n this.remoteAudioTracks.forEach((track) => {\n track.stop();\n });\n }\n\n /**\n * Get the native Agora RTC Client instance.\n * \n * Allows advanced users to access Agora-specific features\n * not exposed through the unified API.\n * \n * @returns The Agora IAgoraRTCClient instance, or null if not connected\n * \n * @example\n * ```typescript\n * const client = provider.getNativeClient();\n * if (client) {\n * // Access Agora-specific features\n * console.log('Connection state:', client.connectionState);\n * }\n * ```\n */\n getNativeClient(): AgoraClient | null {\n return this.client;\n }\n\n /**\n * Cleanup resources\n * @internal\n */\n private cleanup(): void {\n // Cleanup remote audio tracks\n this.remoteAudioTracks.forEach((track) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (track as any).stop();\n });\n this.remoteAudioTracks.clear();\n \n // Cleanup remote video tracks and SEI listeners\n for (const [uid, user] of this.remoteUsers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const videoTrack = user.videoTrack as any;\n if (videoTrack) {\n videoTrack.off('sei-received');\n videoTrack.off('video-sei-received');\n }\n }\n this.remoteUsers.clear();\n \n // Cleanup video containers\n for (const [uid] of this.videoContainers) {\n this.removeVideoContainer(uid as number);\n }\n \n // Cleanup SEI extractor\n if (this.seiExtractor) {\n this.seiExtractor.dispose();\n this.seiExtractor = null;\n }\n \n // Cleanup local audio track\n if (this.localAudioTrack) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.localAudioTrack as any).close();\n this.localAudioTrack = null;\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index4.js","sources":["../src/providers/agora/AgoraProvider.ts"],"sourcesContent":["/**\n * Agora Provider Implementation.\n *\n * This provider uses Agora's H.264 SEI approach\n * to transport animation data.\n *\n * Key differences from LiveKit:\n * - Uses native SEI events instead of RTCRtpScriptTransform\n * - No ALR (Application-Level Redundancy) needed - Agora handles reliability\n * - Simpler data extraction - uses SEI header parsing\n *\n * @packageDocumentation\n */\n\nimport { BaseProvider } from '../base/BaseProvider';\nimport type { RTCConnectionConfig, AgoraConnectionConfig } from '../../types';\nimport { isAgoraConfig, ConnectionState } from '../../types';\nimport type {\n AnimationTrackCallbacks,\n AudioTrackCallbacks,\n} from '../../core/types';\nimport { SEIExtractor } from './SEIExtractor';\nimport { logger } from '../../utils';\nimport type {\n AgoraClient,\n AgoraLocalAudioTrack,\n AgoraRemoteAudioTrack,\n AgoraRemoteVideoTrack,\n AgoraUID,\n} from './types';\n\n/**\n * Agora Provider options.\n * @internal Reserved for future use\n */\nexport type AgoraProviderOptions = Record<string, never>;\n\n/**\n * Remote user info with tracks\n * @internal\n */\ninterface RemoteUserInfo {\n videoTrack?: AgoraRemoteVideoTrack;\n audioTrack?: AgoraRemoteAudioTrack;\n}\n\n/**\n * Agora Provider.\n *\n * Implements RTCProvider interface for Agora platform.\n * Uses native SEI events to receive animation data from H.264 video tracks.\n *\n * @example\n * ```typescript\n * import { AvatarPlayer } from '@spatialwalk/avatarkit-rtc';\n * import { AgoraProvider } from '@spatialwalk/avatarkit-rtc/providers/agora';\n *\n * const provider = new AgoraProvider();\n * const player = new AvatarPlayer(provider, renderer);\n *\n * await player.connect({\n * appId: 'your-agora-app-id',\n * channel: 'your-channel',\n * token: 'your-token',\n * });\n * ```\n */\nexport class AgoraProvider extends BaseProvider {\n /** Provider name identifier */\n readonly name = 'agora';\n\n /** @internal */\n private client: AgoraClient | null = null;\n /** @internal Dynamic SDK - type is any due to dynamic import */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private agoraSDK: any = null;\n\n // Animation track subscription (stored for cleanup, actual callbacks handled by SEIExtractor)\n /** @internal */\n private _animationCallbacks: AnimationTrackCallbacks | null = null;\n /** @internal */\n private seiExtractor: SEIExtractor | null = null;\n /** @internal */\n private remoteUsers: Map<AgoraUID, RemoteUserInfo> = new Map();\n /** @internal */\n private videoContainers: Map<AgoraUID, HTMLDivElement> = new Map();\n\n // Audio track subscription\n /** @internal */\n private audioCallbacks: AudioTrackCallbacks | null = null;\n /** @internal */\n private localAudioTrack: AgoraLocalAudioTrack | null = null;\n /** @internal */\n private remoteAudioTracks: Map<AgoraUID, AgoraRemoteAudioTrack> = new Map();\n\n // Debug mode\n /** @internal */\n private debugLogging = false;\n\n constructor(_options: AgoraProviderOptions = {}) {\n super();\n }\n\n /**\n * Enable or disable debug logging.\n * @param enabled - Whether to enable debug logging\n */\n setDebugLogging(enabled: boolean): void {\n this.debugLogging = enabled;\n if (this.seiExtractor) {\n this.seiExtractor.setDebugLogging(enabled);\n }\n }\n\n /**\n * Load Agora SDK dynamically.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private async loadSDK(): Promise<any> {\n if (this.agoraSDK) {\n return this.agoraSDK;\n }\n\n try {\n // Direct dynamic import - Vite will handle this properly with optimizeDeps\n // Using 'any' type due to dynamic import type inference limitations\n const sdk = await import('agora-rtc-sdk-ng');\n // Agora SDK exports default as the main AgoraRTC object\n this.agoraSDK = sdk.default ?? sdk;\n return this.agoraSDK;\n } catch (error) {\n logger.error('Agora', 'Failed to load SDK:', error);\n throw new Error(\n '❌ Failed to load agora-rtc-sdk-ng.\\n' +\n 'Please ensure it is installed: pnpm add agora-rtc-sdk-ng',\n );\n }\n }\n\n async connect(config: RTCConnectionConfig): Promise<void> {\n if (!isAgoraConfig(config)) {\n throw new Error(\n 'AgoraProvider requires appId and channel in connection config',\n );\n }\n\n const agoraConfig: AgoraConnectionConfig = config;\n\n const AgoraRTC = await this.loadSDK();\n\n // Enable SEI reception (required for animation data)\n AgoraRTC.setParameter('ENABLE_VIDEO_SEI', true);\n AgoraRTC.setParameter('ENABLE_FULL_LINK_AV_SYNC', true);\n\n this.client = AgoraRTC.createClient({\n mode: 'rtc',\n codec: 'h264', // Required for SEI support\n });\n\n this.setConnectionState('connecting');\n\n // Setup event listeners\n this.setupEventListeners();\n\n try {\n await this.client.join(\n agoraConfig.appId,\n agoraConfig.channel,\n agoraConfig.token || null,\n agoraConfig.uid ?? null,\n );\n\n this.setConnectionState('connected');\n this.emit('connected');\n } catch (error) {\n this.setConnectionState('failed');\n this.emit('error', error as Error);\n throw error;\n }\n }\n\n /**\n * Setup Agora client event listeners\n * @internal\n */\n private setupEventListeners(): void {\n // Connection state changed\n this.client.on(\n 'connection-state-change',\n (curState: string, _revState: string) => {\n const mapConnectionState = (state: string): ConnectionState => {\n switch (state) {\n case 'DISCONNECTED':\n case 'DISCONNECTING':\n return ConnectionState.Disconnected;\n case 'CONNECTING':\n return ConnectionState.Connecting;\n case 'CONNECTED':\n return ConnectionState.Connected;\n case 'RECONNECTING':\n return ConnectionState.Reconnecting;\n default:\n return ConnectionState.Failed;\n }\n };\n\n this.setConnectionState(mapConnectionState(curState));\n\n if (curState === 'CONNECTED') {\n this.emit('connected');\n } else if (curState === 'DISCONNECTED') {\n this.cleanup();\n this.emit('disconnected');\n }\n },\n );\n\n // User published (remote user joined and published tracks)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on(\n 'user-published',\n async (user: any, mediaType: 'audio' | 'video') => {\n if (this.debugLogging) {\n logger.info(\n 'Agora',\n `User published: ${user.uid}, mediaType=${mediaType}`,\n );\n }\n\n try {\n // Subscribe to the track\n await this.client.subscribe(user, mediaType);\n\n if (mediaType === 'video') {\n if (user.videoTrack) {\n this.handleVideoTrack(user, user.videoTrack);\n } else {\n logger.warn(\n 'Agora',\n `Video track is null after subscribe for user ${user.uid}`,\n );\n }\n } else if (mediaType === 'audio') {\n if (user.audioTrack) {\n this.handleAudioTrack(user, user.audioTrack);\n } else {\n logger.warn(\n 'Agora',\n `Audio track is null after subscribe for user ${user.uid}`,\n );\n }\n }\n } catch (error) {\n logger.error(\n 'Agora',\n `Failed to subscribe to ${mediaType} from ${user.uid}:`,\n error,\n );\n this.emit('error', error as Error);\n }\n },\n );\n\n // User unpublished (remote user stopped publishing)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on(\n 'user-unpublished',\n (user: any, mediaType: 'audio' | 'video') => {\n if (this.debugLogging) {\n logger.info(\n 'Agora',\n `User unpublished: ${user.uid}, mediaType=${mediaType}`,\n );\n }\n\n if (mediaType === 'video') {\n this.removeVideoContainer(user.uid);\n this.remoteUsers.delete(user.uid);\n } else if (mediaType === 'audio') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const track = this.remoteAudioTracks.get(user.uid) as any;\n if (track) {\n track.stop();\n this.remoteAudioTracks.delete(user.uid);\n }\n\n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioLost?.(user);\n }\n }\n },\n );\n\n // User joined\n this.client.on('user-joined', (user: any) => {\n logger.info('Agora', `User joined: ${user.uid}`);\n });\n\n // User left\n this.client.on('user-left', (user: any, reason: string) => {\n logger.info('Agora', `User left: ${user.uid}, reason: ${reason}`);\n this.remoteUsers.delete(user.uid);\n const audioTrack = this.remoteAudioTracks.get(user.uid);\n if (audioTrack) {\n audioTrack.stop();\n this.remoteAudioTracks.delete(user.uid);\n }\n });\n\n // Error\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.on('exception', (error: any) => {\n logger.error('Agora', 'Exception:', error);\n this.emit('error', new Error(error.msg || String(error)));\n });\n }\n\n /**\n * Handle video track from remote user.\n * Sets up SEI event listeners and plays to hidden container.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private handleVideoTrack(user: any, track: any): void {\n if (this.debugLogging) {\n logger.info('Agora', `Handling video track from ${user.uid}`);\n }\n\n // Listen for SEI data (animation frames)\n // Support both event names for SDK compatibility\n const seiHandler = (seiData: Uint8Array) => {\n if (this.seiExtractor) {\n this.seiExtractor.handleSEIData(seiData);\n }\n };\n\n // Try primary event name\n track.on('sei-received', seiHandler);\n // Also try alternative event name used in some SDK versions\n track.on('video-sei-received', seiHandler);\n\n // Play video to hidden element to ensure data flows\n // This is required for SEI events to be received\n const container = document.createElement('div');\n container.style.display = 'none';\n container.style.position = 'absolute';\n container.style.left = '-9999px';\n container.id = `agora-video-${user.uid}`;\n document.body.appendChild(container);\n this.videoContainers.set(user.uid, container);\n track.play(container);\n\n if (this.debugLogging) {\n logger.info(\n 'Agora',\n `Video track playing, SEI listeners attached for user ${user.uid}`,\n );\n }\n\n // Store track reference\n const existingUser = this.remoteUsers.get(user.uid);\n if (existingUser) {\n existingUser.videoTrack = track;\n } else {\n this.remoteUsers.set(user.uid, { videoTrack: track });\n }\n }\n\n /**\n * Handle audio track from remote user.\n * @internal\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private handleAudioTrack(user: any, track: any): void {\n if (this.debugLogging) {\n logger.info('Agora', `Handling audio track from ${user.uid}`);\n }\n\n // Play audio\n track.play();\n this.remoteAudioTracks.set(user.uid, track);\n\n if (this.debugLogging) {\n logger.info('Agora', `Audio track playing for user ${user.uid}`);\n }\n\n if (this.audioCallbacks) {\n this.audioCallbacks.onAudioReceived?.(user);\n }\n }\n\n /**\n * Remove video container for a user.\n * @internal\n */\n private removeVideoContainer(uid: number): void {\n const container = this.videoContainers.get(uid);\n if (container) {\n container.remove();\n this.videoContainers.delete(uid);\n }\n }\n\n async disconnect(): Promise<void> {\n this.cleanup();\n if (this.client) {\n await this.client.leave();\n this.client = null;\n }\n this.setConnectionState('disconnected');\n this.emit('disconnected');\n }\n\n getConnectionState(): string {\n if (!this.client) {\n return 'disconnected';\n }\n return this.connectionState;\n }\n\n /** @internal */\n async subscribeAnimationTrack(\n callbacks: AnimationTrackCallbacks,\n ): Promise<void> {\n this._animationCallbacks = callbacks;\n\n // Create SEI extractor\n this.seiExtractor = new SEIExtractor();\n this.seiExtractor.setDebugLogging(this.debugLogging);\n this.seiExtractor.initialize(callbacks);\n\n // If already connected, check for existing remote video tracks\n if (this.client) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const remoteUsers = (this.client as any).remoteUsers;\n for (const user of remoteUsers) {\n if (user.videoTrack) {\n // Subscribe to SEI events with both event names for compatibility\n const seiHandler = (seiData: Uint8Array) => {\n if (this.seiExtractor) {\n this.seiExtractor.handleSEIData(seiData);\n }\n };\n user.videoTrack.on('sei-received', seiHandler);\n user.videoTrack.on('video-sei-received', seiHandler);\n }\n }\n }\n }\n\n /** @internal */\n async unsubscribeAnimationTrack(): Promise<void> {\n this._animationCallbacks = null;\n if (this.seiExtractor) {\n this.seiExtractor.dispose();\n this.seiExtractor = null;\n }\n\n // Remove SEI listeners from remote tracks\n for (const [_uid, user] of this.remoteUsers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const videoTrack = user.videoTrack as any;\n if (videoTrack) {\n videoTrack.off('sei-received');\n videoTrack.off('video-sei-received');\n }\n }\n\n // Remove video containers\n for (const [uid] of this.videoContainers) {\n this.removeVideoContainer(uid as number);\n }\n\n this.remoteUsers.clear();\n }\n\n /** @internal */\n async subscribeAudioTrack(callbacks: AudioTrackCallbacks): Promise<void> {\n this.audioCallbacks = callbacks;\n // Audio tracks are automatically handled in user-published event\n }\n\n /** @internal */\n async unsubscribeAudioTrack(): Promise<void> {\n this.audioCallbacks = null;\n this.remoteAudioTracks.forEach((track) => {\n track.stop();\n });\n this.remoteAudioTracks.clear();\n }\n\n async publishAudioTrack(track?: MediaStreamTrack): Promise<void> {\n if (!this.client) {\n throw new Error('Not connected to channel');\n }\n\n const AgoraRTC = await this.loadSDK();\n\n if (track) {\n // Create local audio track from provided MediaStreamTrack\n this.localAudioTrack = AgoraRTC.createCustomAudioTrack({\n mediaStreamTrack: track,\n });\n } else {\n // Create local audio track from microphone\n this.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack({\n encoderConfig: 'music_standard',\n AEC: true,\n ANS: true,\n AGC: true,\n });\n }\n\n // Publish the track\n await this.client.publish(this.localAudioTrack);\n }\n\n async unpublishAudioTrack(): Promise<void> {\n if (!this.client || !this.localAudioTrack) {\n return;\n }\n\n await this.client.unpublish(this.localAudioTrack);\n this.localAudioTrack.close();\n this.localAudioTrack = null;\n }\n\n /**\n * Get the native Agora RTC Client instance.\n *\n * Allows advanced users to access Agora-specific features\n * not exposed through the unified API.\n *\n * @returns The Agora IAgoraRTCClient instance, or null if not connected\n *\n * @example\n * ```typescript\n * const client = provider.getNativeClient();\n * if (client) {\n * // Access Agora-specific features\n * console.log('Connection state:', client.connectionState);\n * }\n * ```\n */\n getNativeClient(): AgoraClient | null {\n return this.client;\n }\n\n /**\n * Cleanup resources\n * @internal\n */\n private cleanup(): void {\n // Cleanup remote audio tracks\n this.remoteAudioTracks.forEach((track) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (track as any).stop();\n });\n this.remoteAudioTracks.clear();\n\n // Cleanup remote video tracks and SEI listeners\n for (const [_uid, user] of this.remoteUsers) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const videoTrack = user.videoTrack as any;\n if (videoTrack) {\n videoTrack.off('sei-received');\n videoTrack.off('video-sei-received');\n }\n }\n this.remoteUsers.clear();\n\n // Cleanup video containers\n for (const [uid] of this.videoContainers) {\n this.removeVideoContainer(uid as number);\n }\n\n // Cleanup SEI extractor\n if (this.seiExtractor) {\n this.seiExtractor.dispose();\n this.seiExtractor = null;\n }\n\n // Cleanup local audio track\n if (this.localAudioTrack) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.localAudioTrack as any).close();\n this.localAudioTrack = null;\n }\n }\n}\n"],"names":[],"mappings":";;;;;;;AAmEO,MAAM,sBAAsB,aAAa;AAAA,EAgC9C,YAAY,WAAiC,IAAI;AAC/C,UAAA;AA/BO;AAAA,gCAAO;AAGR;AAAA,kCAA6B;AAG7B;AAAA;AAAA,oCAAgB;AAIhB;AAAA;AAAA,+CAAsD;AAEtD;AAAA,wCAAoC;AAEpC;AAAA,2DAAiD,IAAA;AAEjD;AAAA,+DAAqD,IAAA;AAIrD;AAAA;AAAA,0CAA6C;AAE7C;AAAA,2CAA+C;AAE/C;AAAA,iEAA8D,IAAA;AAI9D;AAAA;AAAA,wCAAe;AAAA,EAIvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,SAAwB;AACtC,SAAK,eAAe;AACpB,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,gBAAgB,OAAO;AAAA,IAC3C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,UAAwB;AACpC,QAAI,KAAK,UAAU;AACjB,aAAO,KAAK;AAAA,IACd;AAEA,QAAI;AAGF,YAAM,MAAM,MAAM,OAAO,kBAAkB;AAE3C,WAAK,WAAW,IAAI,WAAW;AAC/B,aAAO,KAAK;AAAA,IACd,SAAS,OAAO;AACd,aAAO,MAAM,SAAS,uBAAuB,KAAK;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAGJ;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,QAA4C;AACxD,QAAI,CAAC,cAAc,MAAM,GAAG;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,UAAM,cAAqC;AAE3C,UAAM,WAAW,MAAM,KAAK,QAAA;AAG5B,aAAS,aAAa,oBAAoB,IAAI;AAC9C,aAAS,aAAa,4BAA4B,IAAI;AAEtD,SAAK,SAAS,SAAS,aAAa;AAAA,MAClC,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,IAAA,CACR;AAED,SAAK,mBAAmB,YAAY;AAGpC,SAAK,oBAAA;AAEL,QAAI;AACF,YAAM,KAAK,OAAO;AAAA,QAChB,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,YAAY,SAAS;AAAA,QACrB,YAAY,OAAO;AAAA,MAAA;AAGrB,WAAK,mBAAmB,WAAW;AACnC,WAAK,KAAK,WAAW;AAAA,IACvB,SAAS,OAAO;AACd,WAAK,mBAAmB,QAAQ;AAChC,WAAK,KAAK,SAAS,KAAc;AACjC,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAA4B;AAElC,SAAK,OAAO;AAAA,MACV;AAAA,MACA,CAAC,UAAkB,cAAsB;AACvC,cAAM,qBAAqB,CAAC,UAAmC;AAC7D,kBAAQ,OAAA;AAAA,YACN,KAAK;AAAA,YACL,KAAK;AACH,qBAAO,gBAAgB;AAAA,YACzB,KAAK;AACH,qBAAO,gBAAgB;AAAA,YACzB,KAAK;AACH,qBAAO,gBAAgB;AAAA,YACzB,KAAK;AACH,qBAAO,gBAAgB;AAAA,YACzB;AACE,qBAAO,gBAAgB;AAAA,UAAA;AAAA,QAE7B;AAEA,aAAK,mBAAmB,mBAAmB,QAAQ,CAAC;AAEpD,YAAI,aAAa,aAAa;AAC5B,eAAK,KAAK,WAAW;AAAA,QACvB,WAAW,aAAa,gBAAgB;AACtC,eAAK,QAAA;AACL,eAAK,KAAK,cAAc;AAAA,QAC1B;AAAA,MACF;AAAA,IAAA;AAKF,SAAK,OAAO;AAAA,MACV;AAAA,MACA,OAAO,MAAW,cAAiC;AACjD,YAAI,KAAK,cAAc;AACrB,iBAAO;AAAA,YACL;AAAA,YACA,mBAAmB,KAAK,GAAG,eAAe,SAAS;AAAA,UAAA;AAAA,QAEvD;AAEA,YAAI;AAEF,gBAAM,KAAK,OAAO,UAAU,MAAM,SAAS;AAE3C,cAAI,cAAc,SAAS;AACzB,gBAAI,KAAK,YAAY;AACnB,mBAAK,iBAAiB,MAAM,KAAK,UAAU;AAAA,YAC7C,OAAO;AACL,qBAAO;AAAA,gBACL;AAAA,gBACA,gDAAgD,KAAK,GAAG;AAAA,cAAA;AAAA,YAE5D;AAAA,UACF,WAAW,cAAc,SAAS;AAChC,gBAAI,KAAK,YAAY;AACnB,mBAAK,iBAAiB,MAAM,KAAK,UAAU;AAAA,YAC7C,OAAO;AACL,qBAAO;AAAA,gBACL;AAAA,gBACA,gDAAgD,KAAK,GAAG;AAAA,cAAA;AAAA,YAE5D;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL;AAAA,YACA,0BAA0B,SAAS,SAAS,KAAK,GAAG;AAAA,YACpD;AAAA,UAAA;AAEF,eAAK,KAAK,SAAS,KAAc;AAAA,QACnC;AAAA,MACF;AAAA,IAAA;AAKF,SAAK,OAAO;AAAA,MACV;AAAA,MACA,CAAC,MAAW,cAAiC;;AAC3C,YAAI,KAAK,cAAc;AACrB,iBAAO;AAAA,YACL;AAAA,YACA,qBAAqB,KAAK,GAAG,eAAe,SAAS;AAAA,UAAA;AAAA,QAEzD;AAEA,YAAI,cAAc,SAAS;AACzB,eAAK,qBAAqB,KAAK,GAAG;AAClC,eAAK,YAAY,OAAO,KAAK,GAAG;AAAA,QAClC,WAAW,cAAc,SAAS;AAEhC,gBAAM,QAAQ,KAAK,kBAAkB,IAAI,KAAK,GAAG;AACjD,cAAI,OAAO;AACT,kBAAM,KAAA;AACN,iBAAK,kBAAkB,OAAO,KAAK,GAAG;AAAA,UACxC;AAEA,cAAI,KAAK,gBAAgB;AACvB,6BAAK,gBAAe,gBAApB,4BAAkC;AAAA,UACpC;AAAA,QACF;AAAA,MACF;AAAA,IAAA;AAIF,SAAK,OAAO,GAAG,eAAe,CAAC,SAAc;AAC3C,aAAO,KAAK,SAAS,gBAAgB,KAAK,GAAG,EAAE;AAAA,IACjD,CAAC;AAGD,SAAK,OAAO,GAAG,aAAa,CAAC,MAAW,WAAmB;AACzD,aAAO,KAAK,SAAS,cAAc,KAAK,GAAG,aAAa,MAAM,EAAE;AAChE,WAAK,YAAY,OAAO,KAAK,GAAG;AAChC,YAAM,aAAa,KAAK,kBAAkB,IAAI,KAAK,GAAG;AACtD,UAAI,YAAY;AACd,mBAAW,KAAA;AACX,aAAK,kBAAkB,OAAO,KAAK,GAAG;AAAA,MACxC;AAAA,IACF,CAAC;AAID,SAAK,OAAO,GAAG,aAAa,CAAC,UAAe;AAC1C,aAAO,MAAM,SAAS,cAAc,KAAK;AACzC,WAAK,KAAK,SAAS,IAAI,MAAM,MAAM,OAAO,OAAO,KAAK,CAAC,CAAC;AAAA,IAC1D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,MAAW,OAAkB;AACpD,QAAI,KAAK,cAAc;AACrB,aAAO,KAAK,SAAS,6BAA6B,KAAK,GAAG,EAAE;AAAA,IAC9D;AAIA,UAAM,aAAa,CAAC,YAAwB;AAC1C,UAAI,KAAK,cAAc;AACrB,aAAK,aAAa,cAAc,OAAO;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,GAAG,gBAAgB,UAAU;AAEnC,UAAM,GAAG,sBAAsB,UAAU;AAIzC,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,cAAU,MAAM,UAAU;AAC1B,cAAU,MAAM,WAAW;AAC3B,cAAU,MAAM,OAAO;AACvB,cAAU,KAAK,eAAe,KAAK,GAAG;AACtC,aAAS,KAAK,YAAY,SAAS;AACnC,SAAK,gBAAgB,IAAI,KAAK,KAAK,SAAS;AAC5C,UAAM,KAAK,SAAS;AAEpB,QAAI,KAAK,cAAc;AACrB,aAAO;AAAA,QACL;AAAA,QACA,wDAAwD,KAAK,GAAG;AAAA,MAAA;AAAA,IAEpE;AAGA,UAAM,eAAe,KAAK,YAAY,IAAI,KAAK,GAAG;AAClD,QAAI,cAAc;AAChB,mBAAa,aAAa;AAAA,IAC5B,OAAO;AACL,WAAK,YAAY,IAAI,KAAK,KAAK,EAAE,YAAY,OAAO;AAAA,IACtD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,MAAW,OAAkB;;AACpD,QAAI,KAAK,cAAc;AACrB,aAAO,KAAK,SAAS,6BAA6B,KAAK,GAAG,EAAE;AAAA,IAC9D;AAGA,UAAM,KAAA;AACN,SAAK,kBAAkB,IAAI,KAAK,KAAK,KAAK;AAE1C,QAAI,KAAK,cAAc;AACrB,aAAO,KAAK,SAAS,gCAAgC,KAAK,GAAG,EAAE;AAAA,IACjE;AAEA,QAAI,KAAK,gBAAgB;AACvB,uBAAK,gBAAe,oBAApB,4BAAsC;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,KAAmB;AAC9C,UAAM,YAAY,KAAK,gBAAgB,IAAI,GAAG;AAC9C,QAAI,WAAW;AACb,gBAAU,OAAA;AACV,WAAK,gBAAgB,OAAO,GAAG;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAChC,SAAK,QAAA;AACL,QAAI,KAAK,QAAQ;AACf,YAAM,KAAK,OAAO,MAAA;AAClB,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,mBAAmB,cAAc;AACtC,SAAK,KAAK,cAAc;AAAA,EAC1B;AAAA,EAEA,qBAA6B;AAC3B,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,wBACJ,WACe;AACf,SAAK,sBAAsB;AAG3B,SAAK,eAAe,IAAI,aAAA;AACxB,SAAK,aAAa,gBAAgB,KAAK,YAAY;AACnD,SAAK,aAAa,WAAW,SAAS;AAGtC,QAAI,KAAK,QAAQ;AAEf,YAAM,cAAe,KAAK,OAAe;AACzC,iBAAW,QAAQ,aAAa;AAC9B,YAAI,KAAK,YAAY;AAEnB,gBAAM,aAAa,CAAC,YAAwB;AAC1C,gBAAI,KAAK,cAAc;AACrB,mBAAK,aAAa,cAAc,OAAO;AAAA,YACzC;AAAA,UACF;AACA,eAAK,WAAW,GAAG,gBAAgB,UAAU;AAC7C,eAAK,WAAW,GAAG,sBAAsB,UAAU;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,4BAA2C;AAC/C,SAAK,sBAAsB;AAC3B,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,QAAA;AAClB,WAAK,eAAe;AAAA,IACtB;AAGA,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,aAAa;AAE3C,YAAM,aAAa,KAAK;AACxB,UAAI,YAAY;AACd,mBAAW,IAAI,cAAc;AAC7B,mBAAW,IAAI,oBAAoB;AAAA,MACrC;AAAA,IACF;AAGA,eAAW,CAAC,GAAG,KAAK,KAAK,iBAAiB;AACxC,WAAK,qBAAqB,GAAa;AAAA,IACzC;AAEA,SAAK,YAAY,MAAA;AAAA,EACnB;AAAA;AAAA,EAGA,MAAM,oBAAoB,WAA+C;AACvE,SAAK,iBAAiB;AAAA,EAExB;AAAA;AAAA,EAGA,MAAM,wBAAuC;AAC3C,SAAK,iBAAiB;AACtB,SAAK,kBAAkB,QAAQ,CAAC,UAAU;AACxC,YAAM,KAAA;AAAA,IACR,CAAC;AACD,SAAK,kBAAkB,MAAA;AAAA,EACzB;AAAA,EAEA,MAAM,kBAAkB,OAAyC;AAC/D,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,WAAW,MAAM,KAAK,QAAA;AAE5B,QAAI,OAAO;AAET,WAAK,kBAAkB,SAAS,uBAAuB;AAAA,QACrD,kBAAkB;AAAA,MAAA,CACnB;AAAA,IACH,OAAO;AAEL,WAAK,kBAAkB,MAAM,SAAS,2BAA2B;AAAA,QAC/D,eAAe;AAAA,QACf,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MAAA,CACN;AAAA,IACH;AAGA,UAAM,KAAK,OAAO,QAAQ,KAAK,eAAe;AAAA,EAChD;AAAA,EAEA,MAAM,sBAAqC;AACzC,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,iBAAiB;AACzC;AAAA,IACF;AAEA,UAAM,KAAK,OAAO,UAAU,KAAK,eAAe;AAChD,SAAK,gBAAgB,MAAA;AACrB,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,kBAAsC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAgB;AAEtB,SAAK,kBAAkB,QAAQ,CAAC,UAAU;AAEvC,YAAc,KAAA;AAAA,IACjB,CAAC;AACD,SAAK,kBAAkB,MAAA;AAGvB,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,aAAa;AAE3C,YAAM,aAAa,KAAK;AACxB,UAAI,YAAY;AACd,mBAAW,IAAI,cAAc;AAC7B,mBAAW,IAAI,oBAAoB;AAAA,MACrC;AAAA,IACF;AACA,SAAK,YAAY,MAAA;AAGjB,eAAW,CAAC,GAAG,KAAK,KAAK,iBAAiB;AACxC,WAAK,qBAAqB,GAAa;AAAA,IACzC;AAGA,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,QAAA;AAClB,WAAK,eAAe;AAAA,IACtB;AAGA,QAAI,KAAK,iBAAiB;AAEvB,WAAK,gBAAwB,MAAA;AAC9B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AACF;"}
package/dist/index5.js CHANGED
@@ -7,7 +7,10 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
7
7
  return ConnectionState2;
8
8
  })(ConnectionState || {});
9
9
  function isLiveKitConfig(config) {
10
- return "url" in config && "roomName" in config;
10
+ return "url" in config && "token" in config;
11
+ }
12
+ function isLiveKitPrepareConfig(config) {
13
+ return "url" in config;
11
14
  }
12
15
  function isAgoraConfig(config) {
13
16
  return "appId" in config && "channel" in config;
@@ -15,6 +18,7 @@ function isAgoraConfig(config) {
15
18
  export {
16
19
  ConnectionState,
17
20
  isAgoraConfig,
18
- isLiveKitConfig
21
+ isLiveKitConfig,
22
+ isLiveKitPrepareConfig
19
23
  };
20
24
  //# sourceMappingURL=index5.js.map