@unith-ai/core-client 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.umd.js","sources":["../src/utils/worklet-loader.ts","../src/utils/audio.ts","../src/types/event.ts","../src/modules/audio.ts","../src/modules/av.ts","../src/modules/environment.ts","../src/modules/event.ts","../src/modules/connection.ts","../src/modules/idle-video.ts","../src/modules/monitor.ts","../src/modules/sync.ts","../src/utils/storage.ts","../src/types/vp8.ts","../src/modules/user.ts","../src/modules/vp8.ts","../src/modules/video.ts","../src/utils/sync.ts","../src/index.ts"],"sourcesContent":["const URLCache = new Map<string, string>();\n\nexport function createWorkletModuleLoader(name: string, sourceCode: string) {\n return async (worklet: AudioWorklet) => {\n const url = URLCache.get(name);\n if (url) {\n return worklet.addModule(url);\n }\n\n const blob = new Blob([sourceCode], { type: \"application/javascript\" });\n const blobURL = URL.createObjectURL(blob);\n try {\n await worklet.addModule(blobURL);\n URLCache.set(name, blobURL);\n return;\n } catch {\n URL.revokeObjectURL(blobURL);\n }\n\n try {\n // Attempting to start a conversation in Safari inside an iframe will\n // throw a CORS error because the blob:// protocol is considered\n // cross-origin. In such cases, fall back to using a base64 data URL:\n const base64 = btoa(sourceCode);\n const moduleURL = `data:application/javascript;base64,${base64}`;\n await worklet.addModule(moduleURL);\n URLCache.set(name, moduleURL);\n } catch (error) {\n throw new Error(\n `Failed to load the ${name} worklet module. Make sure the browser supports AudioWorklets.`\n );\n }\n };\n}\n","/*\n * ulaw decoding logic taken from the wavefile library\n * https://github.com/rochars/wavefile/blob/master/lib/codecs/mulaw.js\n */\n\nimport { createWorkletModuleLoader } from \"./worklet-loader\";\n\nexport const loadAudioConcatProcessor = createWorkletModuleLoader(\n \"audio-concat-processor\",\n // language=JavaScript\n `\nconst decodeTable = [0,132,396,924,1980,4092,8316,16764];\n\nexport function decodeSample(muLawSample) {\n let sign;\n let exponent;\n let mantissa;\n let sample;\n muLawSample = ~muLawSample;\n sign = (muLawSample & 0x80);\n exponent = (muLawSample >> 4) & 0x07;\n mantissa = muLawSample & 0x0F;\n sample = decodeTable[exponent] + (mantissa << (exponent+3));\n if (sign !== 0) sample = -sample;\n\n return sample;\n}\n\nclass AudioConcatProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffers = [];\n this.cursor = 0;\n this.currentBuffer = null;\n this.currentBufferTimestamp = null;\n this.currentBufferStartSample = 0;\n this.wasInterrupted = false;\n this.playing = false;\n this.finished = false;\n\n this.playbackRate = 1.0;\n this.adjustUntilSample = null;\n this.sampleCounter = 0;\n this.format = { sampleRate: 48000 };\n\n // Time synchronization properties\n this.playbackStartTime = null;\n this.totalSamplesPlayed = 0;\n \n // Drift correction parameters\n this.maxCorrectionRate = 0.1; // Maximum 10% speed adjustment\n this.correctionSmoothness = 0.95; // How smoothly to apply corrections (0-1)\n this.targetPlaybackRate = 1.0;\n\n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case \"setFormat\":\n this.format = data.format;\n break;\n\n case \"buffer\":\n this.wasInterrupted = false;\n \n const bufferData = this.format.encoding === \"ulaw\"\n ? new Uint8Array(data.buffer)\n : new Int16Array(data.buffer);\n \n // Store buffer with its timestamp\n this.buffers.push({\n buffer: bufferData,\n timestamp: data.timestamp_ms\n });\n break;\n \n case \"startPlayback\":\n this.playing = true;\n this.playbackStartTime = currentTime;\n this.totalSamplesPlayed = 0;\n this.sampleCounter = 0;\n break;\n \n case \"stopPlayback\":\n this.playing = false;\n this.finished = true;\n \n // Discard all queued buffers\n this.buffers = [];\n \n // Clear current buffer and reset cursor\n this.currentBuffer = null;\n this.currentBufferTimestamp = null;\n this.cursor = 0;\n \n // Reset playback rate adjustments\n this.playbackRate = 1.0;\n this.adjustUntilSample = null;\n this.targetPlaybackRate = 1.0;\n \n // Notify that playback has stopped\n this.port.postMessage({ \n type: \"process\", \n finished: true,\n stopped: true\n });\n break;\n\n case \"adjustPlaybackRate\":\n this.playbackRate = data.rate;\n this.adjustUntilSample = this.sampleCounter + Math.floor(this.format.sampleRate * data.duration);\n break;\n \n case \"reset\":\n this.playing = false;\n this.finished = false;\n this.currentBuffer = null;\n this.currentBufferTimestamp = null;\n this.currentBufferStartSample = 0;\n this.cursor = 0;\n this.buffers = [];\n this.playbackStartTime = null;\n this.totalSamplesPlayed = 0;\n this.targetPlaybackRate = 1.0;\n break;\n\n case \"interrupt\":\n this.wasInterrupted = true;\n break;\n\n case \"clearInterrupted\":\n if (this.wasInterrupted) {\n this.wasInterrupted = false;\n this.buffers = [];\n this.currentBuffer = null;\n this.currentBufferTimestamp = null;\n }\n break;\n }\n };\n }\n\n // Calculate the expected timestamp based on samples played\n getExpectedTimestamp() {\n if (!this.playbackStartTime) return 0;\n \n // Convert samples to milliseconds\n const samplesPlayedMs = (this.totalSamplesPlayed / this.format.sampleRate) * 1000;\n return samplesPlayedMs;\n }\n\n // Calculate the actual timestamp of current playback position\n getCurrentActualTimestamp() {\n if (!this.currentBufferTimestamp) return 0;\n \n // Current position within the buffer in samples\n const samplesIntoBuffer = Math.floor(this.cursor);\n \n // Convert buffer position to milliseconds (assuming buffer covers specific time duration)\n // Each buffer typically represents a fixed time duration\n const bufferDurationMs = (this.currentBuffer.length / this.format.sampleRate) * 1000;\n const progressThroughBuffer = samplesIntoBuffer / this.currentBuffer.length;\n \n return this.currentBufferTimestamp + (progressThroughBuffer * bufferDurationMs);\n }\n\n // Calculate timing drift and adjust playback rate accordingly\n calculateDriftCorrection() {\n if (!this.currentBufferTimestamp || !this.playbackStartTime) {\n return 1.0;\n }\n\n const expectedTimestamp = this.getExpectedTimestamp();\n const actualTimestamp = this.getCurrentActualTimestamp();\n \n // Calculate drift in milliseconds\n const drift = actualTimestamp - expectedTimestamp;\n \n // Convert drift to a playback rate adjustment\n // Positive drift means we're ahead - slow down\n // Negative drift means we're behind - speed up\n let correctionFactor = 1.0;\n \n if (Math.abs(drift) > 10) { // Only correct if drift > 10ms\n // Calculate correction rate based on drift\n // More drift = more correction, but capped at maxCorrectionRate\n const driftRatio = Math.min(Math.abs(drift) / 1000, this.maxCorrectionRate);\n \n if (drift > 0) {\n // We're ahead, slow down\n correctionFactor = 1.0 - driftRatio;\n } else {\n // We're behind, speed up\n correctionFactor = 1.0 + driftRatio;\n }\n \n // Apply smoothing to avoid jarring rate changes\n this.targetPlaybackRate = this.targetPlaybackRate * this.correctionSmoothness + \n correctionFactor * (1 - this.correctionSmoothness);\n } else {\n // Gradually return to normal speed when drift is small\n this.targetPlaybackRate = this.targetPlaybackRate * this.correctionSmoothness + \n 1.0 * (1 - this.correctionSmoothness);\n }\n\n return this.targetPlaybackRate;\n }\n\n process(_, outputs, parameters) {\n const output = outputs[0][0];\n\n if (!this.playing) {\n output.fill(0);\n return true;\n }\n\n let finished = false;\n\n for (let i = 0; i < output.length; i++) {\n // If no buffer is ready, get the next one\n if (!this.currentBuffer) {\n if (this.buffers.length === 0) {\n finished = true;\n break;\n }\n \n const bufferData = this.buffers.shift();\n this.currentBuffer = bufferData.buffer;\n this.currentBufferTimestamp = bufferData.timestamp;\n this.currentBufferStartSample = this.totalSamplesPlayed;\n this.cursor = 0;\n }\n\n // Calculate drift correction for timing synchronization\n const driftCorrectedRate = this.calculateDriftCorrection();\n \n // Apply manual playback rate adjustments if active\n let finalPlaybackRate = driftCorrectedRate;\n if (this.adjustUntilSample !== null && this.sampleCounter < this.adjustUntilSample) {\n finalPlaybackRate *= this.playbackRate;\n } else if (this.adjustUntilSample !== null) {\n this.playbackRate = 1.0;\n this.adjustUntilSample = null;\n }\n\n const idx = Math.floor(this.cursor);\n const nextIdx = Math.min(idx + 1, this.currentBuffer.length - 1);\n\n let s1 = this.currentBuffer[idx];\n let s2 = this.currentBuffer[nextIdx];\n if (this.format.encoding === \"ulaw\") {\n s1 = decodeSample(s1);\n s2 = decodeSample(s2);\n }\n\n const frac = this.cursor - idx;\n const interpolated = s1 * (1 - frac) + s2 * frac;\n output[i] = interpolated / 32768;\n\n this.cursor += finalPlaybackRate;\n this.sampleCounter++;\n this.totalSamplesPlayed++;\n\n if (this.cursor >= this.currentBuffer.length) {\n this.currentBuffer = null;\n this.currentBufferTimestamp = null;\n }\n }\n\n if (this.finished !== finished) {\n this.finished = finished;\n this.port.postMessage({ \n type: \"process\", \n finished,\n // Optional: send timing info for debugging\n timing: {\n expectedTimestamp: this.getExpectedTimestamp(),\n actualTimestamp: this.getCurrentActualTimestamp(),\n playbackRate: this.targetPlaybackRate\n }\n });\n }\n\n return true;\n }\n}\nregisterProcessor(\"audio-concat-processor\", AudioConcatProcessor);\n`\n);\n\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binaryString = window.atob(base64);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\nexport function detectAudioFormat(base64Data: string): {\n hasHeader: boolean;\n headerSize: number;\n format: string;\n shouldSlice: boolean;\n} {\n // Convert base64 to ArrayBuffer\n const binaryString = window.atob(base64Data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n // Check for common audio file headers\n const header = binaryString.slice(0, 12);\n\n // WAV file detection\n if (header.slice(0, 4) === \"RIFF\" && header.slice(8, 12) === \"WAVE\") {\n return {\n hasHeader: true,\n headerSize: 44, // Standard WAV header size\n format: \"WAV\",\n shouldSlice: true,\n };\n }\n\n // Alternative WAV detection using bytes\n if (\n bytes[0] === 0x52 &&\n bytes[1] === 0x49 &&\n bytes[2] === 0x46 &&\n bytes[3] === 0x46\n ) {\n // RIFF header found\n if (\n bytes[8] === 0x57 &&\n bytes[9] === 0x41 &&\n bytes[10] === 0x56 &&\n bytes[11] === 0x45\n ) {\n // WAVE format\n return {\n hasHeader: true,\n headerSize: 44,\n format: \"WAV\",\n shouldSlice: true,\n };\n }\n }\n\n // OGG file detection\n if (header.slice(0, 4) === \"OggS\") {\n return {\n hasHeader: true,\n headerSize: 0, // Variable header size, needs different handling\n format: \"OGG\",\n shouldSlice: false, // Don't use simple slice for OGG\n };\n }\n\n // MP3 file detection (ID3 tags or frame sync)\n if (header.slice(0, 3) === \"ID3\") {\n return {\n hasHeader: true,\n headerSize: 0, // Variable size, needs parsing\n format: \"MP3\",\n shouldSlice: false,\n };\n }\n\n // MP3 frame sync detection (0xFF 0xFB, 0xFF 0xFA, etc.)\n if (bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0) {\n return {\n hasHeader: false,\n headerSize: 0,\n format: \"MP3_RAW\",\n shouldSlice: false,\n };\n }\n\n // Raw PCM detection heuristics\n // Check if data looks like 16-bit little-endian PCM\n const int16Array = new Int16Array(bytes.buffer);\n const sampleCount = int16Array.length;\n\n // Statistical analysis to determine if it's raw PCM\n let zeroCount = 0;\n let reasonableValueCount = 0;\n const maxReasonableValue = 32767; // Max for 16-bit signed\n\n for (let i = 0; i < Math.min(sampleCount, 100); i++) {\n const value = Math.abs(int16Array[i]);\n if (value === 0) zeroCount++;\n if (value <= maxReasonableValue) reasonableValueCount++;\n }\n\n const samplesChecked = Math.min(sampleCount, 100);\n const zeroRatio = zeroCount / samplesChecked;\n const reasonableRatio = reasonableValueCount / samplesChecked;\n\n // If most values are reasonable and not too many zeros, likely raw PCM\n if (reasonableRatio > 0.9 && zeroRatio < 0.8) {\n return {\n hasHeader: false,\n headerSize: 0,\n format: \"RAW_PCM\",\n shouldSlice: false,\n };\n }\n\n // Default: assume raw PCM if no other format detected\n return {\n hasHeader: false,\n headerSize: 0,\n format: \"UNKNOWN_RAW\",\n shouldSlice: false,\n };\n}\n","export enum EventType {\n TEXT = \"text\",\n CONVERSATION_END = \"conversation_end\",\n JOIN = \"join\",\n ERROR = \"error\",\n TIME_OUT = \"timeout\",\n UNITH_NLP_EXCEPTION = \"unith_nlp_exception\",\n ANALYTICS = \"analytics-userFeedback\",\n CHOICE = \"choice\",\n TIMEOUT_WARNING = \"timeout_warning\",\n KEEP_SESSION = \"keep_session\",\n RESPONSE = \"response\",\n STREAMING = \"streaming\",\n PING = \"ping\",\n PONG = \"pong\",\n BINARY = \"binary\",\n}\n\nexport enum StreamingEventType {\n VIDEO_FRAME = \"video_frame\",\n AUDIO_FRAME = \"audio_frame\",\n METADATA = \"metadata\",\n ERROR = \"error\",\n CACHE= 'cache'\n}\nexport interface Message {\n id: number;\n timestamp: string;\n speaker: string;\n text: string;\n isSent: boolean;\n user_id: string;\n username?: string;\n event: EventType.TEXT | EventType.KEEP_SESSION;\n //not needed for now\n session_id?: string;\n visible: boolean;\n}\n\nexport type JoinEventData = {\n granted: boolean;\n};\n\nexport type GlobalEventData = {\n event: EventType;\n user_id: string;\n username: string;\n type?: string;\n};\n\nexport type BinaryEventData = {\n data: ArrayBuffer;\n};\n\nexport type SpeakerType = \"user\" | \"backend\";\n\nexport type TextEventData = {\n id: string;\n timestamp: Date;\n speaker: SpeakerType;\n text: string;\n isSent: boolean;\n session_id: string;\n visible: boolean;\n is_last?: boolean;\n part_order?: number;\n input_message_id?: string;\n suggestions: string[];\n has_error?: boolean;\n error_code?: number;\n error_message?: string;\n media?: string[];\n video?: string;\n stream_id?: string;\n};\n\n// Add other event data types as needed\nexport type ConversationEndEventData = {\n reason: string;\n duration: number;\n};\n\nexport type TimeoutEventData = {\n remaining_time: number;\n};\n\nexport type AnalyticsEventData = {\n feedback_type: string;\n rating: number;\n comment?: string;\n};\n\nexport type ChoiceEventData = {\n choices: string[];\n selected_choice?: string;\n};\n\nexport type StreamingEventData = {\n message?: string;\n metadata_type?: string;\n session_id?: string;\n event_timestamp_ms: number;\n type: StreamingEventType;\n video_url?: string;\n text?: string;\n error_type?:\n | \"resource_exhausted\"\n | \"deadline_exceeded\"\n | \"inactivity_timeout\";\n format?: \"jpeg\" | \"wav\";\n frame_data?: string;\n height?: number;\n width?: string;\n sample_rate?: string;\n duration_ms?: number;\n is_keyframe?: boolean;\n};\n\nexport type PingEventData = {\n event: EventType.PING;\n timestamp: string;\n id: string;\n};\n\nexport type PongEventData = {\n type: EventType.PONG;\n timestamp: string;\n id: string;\n};\n\nexport type IncomingSocketEvent<T extends EventType = EventType> =\n T extends EventType.BINARY\n ? GlobalEventData & BinaryEventData\n : T extends EventType.JOIN\n ? GlobalEventData & JoinEventData\n : T extends EventType.TEXT\n ? GlobalEventData & TextEventData\n : T extends EventType.RESPONSE\n ? GlobalEventData & TextEventData\n : T extends EventType.CONVERSATION_END\n ? GlobalEventData & ConversationEndEventData\n : T extends EventType.STREAMING\n ? GlobalEventData & StreamingEventData\n : T extends EventType.TIME_OUT\n ? GlobalEventData & TimeoutEventData\n : T extends EventType.TIMEOUT_WARNING\n ? GlobalEventData & TimeoutEventData\n : T extends EventType.KEEP_SESSION\n ? GlobalEventData & JoinEventData\n : T extends EventType.ANALYTICS\n ? GlobalEventData & AnalyticsEventData\n : T extends EventType.PING\n ? GlobalEventData & PingEventData\n : T extends EventType.PONG\n ? GlobalEventData & PongEventData\n : T extends EventType.CHOICE\n ? GlobalEventData & ChoiceEventData\n : GlobalEventData;\n\nexport type AnyIncomingSocketEvent =\n | IncomingSocketEvent<EventType.JOIN>\n | IncomingSocketEvent<EventType.TEXT>\n | IncomingSocketEvent<EventType.RESPONSE>\n | IncomingSocketEvent<EventType.STREAMING>\n | IncomingSocketEvent<EventType.BINARY>\n | IncomingSocketEvent<EventType.CONVERSATION_END>\n | IncomingSocketEvent<EventType.TIME_OUT>\n | IncomingSocketEvent<EventType.TIMEOUT_WARNING>\n | IncomingSocketEvent<EventType.KEEP_SESSION>\n | IncomingSocketEvent<EventType.ANALYTICS>\n | IncomingSocketEvent<EventType.CHOICE>\n | IncomingSocketEvent<EventType.PING>\n | IncomingSocketEvent<EventType.PONG>\n | (GlobalEventData & {\n event: Exclude<\n EventType,\n | EventType.JOIN\n | EventType.TEXT\n | EventType.CONVERSATION_END\n | EventType.TIME_OUT\n | EventType.TIMEOUT_WARNING\n | EventType.ANALYTICS\n | EventType.CHOICE\n | EventType.RESPONSE\n | EventType.STREAMING\n | EventType.PING\n | EventType.KEEP_SESSION\n | EventType.BINARY\n >;\n });\n\nexport type DisconnectionDetails = {\n reason: \"error\" | \"user\";\n message?: string;\n context?: Event;\n};\n\nexport type OnDisconnectCallback = (details: DisconnectionDetails) => void;\nexport type OnMessageCallback = (event: AnyIncomingSocketEvent) => void;\nexport type OnPingPongCallback = (event: PongEventData) => void;\n\n// Type guard functions\nexport function isJoinEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.JOIN> {\n return event.event === EventType.JOIN;\n}\n\nexport function isTextEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.TEXT> {\n return event.event === EventType.TEXT;\n}\n\nexport function isResponseEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.RESPONSE> {\n return event.event === EventType.RESPONSE;\n}\n\nexport function isStreamingEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.STREAMING> {\n return event.event === EventType.STREAMING;\n}\nexport function isStreamingErrorEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.STREAMING> {\n return (\n event.event === EventType.STREAMING &&\n event.type === StreamingEventType.ERROR\n );\n}\nexport function isBinaryEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.BINARY> {\n return event.event === EventType.BINARY;\n}\n\nexport function isConversationEndEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.CONVERSATION_END> {\n return event.event === EventType.CONVERSATION_END;\n}\n\nexport function isPongEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.PONG> {\n return event.type === EventType.PONG;\n}\n\nexport function isTimeoutWarningEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.TIMEOUT_WARNING> {\n return event.event === EventType.TIMEOUT_WARNING;\n}\n\nexport function isTimeoutEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.TIME_OUT> {\n return event.event === EventType.TIME_OUT;\n}\n\nexport function isKeepSessionEvent(\n event: AnyIncomingSocketEvent\n): event is IncomingSocketEvent<EventType.KEEP_SESSION> {\n return event.event === EventType.KEEP_SESSION;\n}\n","import { AudioFormatConfig } from \"../types/audio\";\nimport { loadAudioConcatProcessor } from \"../utils/audio\";\nexport class AudioOutput {\n private _isMuted: boolean = false;\n\n public static async createAudioOutput({\n sampleRate,\n format,\n }: AudioFormatConfig): Promise<AudioOutput> {\n let audioContext: AudioContext | null = null;\n try {\n audioContext = new AudioContext({ sampleRate });\n const audioAnalyser = audioContext.createAnalyser();\n const audioGain = audioContext.createGain();\n audioGain.connect(audioAnalyser);\n audioAnalyser.connect(audioContext.destination);\n\n // load the audio\n await loadAudioConcatProcessor(audioContext.audioWorklet);\n\n const worklet = new AudioWorkletNode(\n audioContext,\n \"audio-concat-processor\"\n );\n worklet.port.postMessage({ type: \"setFormat\", format });\n worklet.connect(audioGain);\n\n // resume the audio context\n // audioContext.resume();\n\n const audioOutput = new AudioOutput(\n audioContext,\n audioAnalyser,\n audioGain,\n worklet\n );\n\n // Add iOS compatibility handling\n await audioOutput.ensureIOSCompatibility();\n\n return audioOutput;\n } catch (error) {\n audioContext?.close();\n throw error;\n }\n }\n\n private async ensureIOSCompatibility() {\n // Create a silent audio buffer and play it to \"prime\" the audio system\n if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {\n const buffer = this.context.createBuffer(1, 1, this.context.sampleRate);\n const source = this.context.createBufferSource();\n source.buffer = buffer;\n source.connect(this.context.destination);\n source.start();\n }\n }\n\n private constructor(\n public readonly context: AudioContext,\n public readonly analyser: AnalyserNode,\n public readonly gain: GainNode,\n public readonly worklet: AudioWorkletNode\n ) {}\n\n public async getOutputDevice(): Promise<MediaDeviceInfo | null> {\n try {\n // Get the sinkId from the AudioContext\n const sinkId = (this.context as any).sinkId || \"\";\n\n // Enumerate all devices\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioOutputs = devices.filter(\n device => device.kind === \"audiooutput\"\n );\n\n // Find the device matching the current sinkId\n // Empty string means default device\n if (sinkId === \"\") {\n return (\n audioOutputs.find(d => d.deviceId === \"default\") ||\n audioOutputs[0] ||\n null\n );\n }\n\n return audioOutputs.find(d => d.deviceId === sinkId) || null;\n } catch (error) {\n console.error(\"Error getting output device:\", error);\n return null;\n }\n }\n\n // Get all available output devices\n public async getAvailableOutputDevices(): Promise<MediaDeviceInfo[]> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.filter(device => device.kind === \"audiooutput\");\n } catch (error) {\n console.error(\"Error enumerating devices:\", error);\n return [];\n }\n }\n\n public mute(): void {\n this.gain.gain.value = 0;\n this._isMuted = true;\n }\n\n // Unmute the audio output\n public unmute(): void {\n this.gain.gain.value = 1;\n this._isMuted = false;\n }\n\n // Toggle mute state\n public toggleMute(): boolean {\n if (this._isMuted) {\n this.unmute();\n } else {\n this.mute();\n }\n return this._isMuted;\n }\n\n public async close() {\n await this.context.close();\n }\n}\n","import { AudioOutput } from \"./audio\";\nimport { SyncController } from \"./sync\";\nimport { VideoOutput } from \"./video\";\n\nexport class AVController {\n private syncController: SyncController;\n private audioOutput: AudioOutput;\n private videoOutput: VideoOutput;\n\n initialized: boolean = false; // has the AVController been initialized\n isPlaying: boolean = false; // is playing audio and video\n isPlayingAudio: boolean = false; // is playing audio only\n isPlayingVideo: boolean = false; // is playing video only\n isStoppingAV: boolean = false;\n\n constructor(\n syncConfig: SyncController,\n audioOutput: AudioOutput,\n videoOutput: VideoOutput\n ) {\n this.syncController = syncConfig;\n this.audioOutput = audioOutput;\n this.videoOutput = videoOutput;\n\n this.videoOutput.setEventCallbacks({\n onIdleVideoShown: () => {\n // this.isPlayingVideo = true;\n },\n onIdleVideoHidden: () => {\n //this means stream has ended. Let's stop streaming.\n this.isPlayingVideo = false;\n if (!this.isPlayingAudio) {\n this.isPlaying = false;\n }\n this.videoOutput.stopBufferMonitoring();\n },\n });\n }\n\n public updatePlayingState(state: boolean) {\n this.isPlaying = state;\n this.isPlayingAudio = state;\n this.isPlayingVideo = state;\n }\n\n public toggleStoppingVideo(stopping: boolean) {\n this.isStoppingAV = stopping;\n }\n\n public async startPlayback(init = false) {\n if (this.isPlaying) return;\n if (init) {\n this.initialized = init;\n }\n await this.audioOutput.context.resume();\n this.updatePlayingState(true);\n\n this.videoOutput.startStreaming(init);\n this.audioOutput.worklet.port.postMessage({\n type: \"startPlayback\",\n });\n }\n\n public handleAudioWorkletMessage = (event: {\n type: string;\n finished: boolean;\n }) => {\n if (event.type === \"process\") {\n this.isPlayingAudio = false;\n if (!this.isPlayingVideo) {\n this.isPlaying = false;\n }\n this.audioOutput.worklet.port.postMessage({\n type: \"reset\",\n });\n }\n };\n\n public async playAudioVideo() {\n if (!this.initialized) return;\n if (this.isPlaying || this.isStoppingAV) return;\n // start playback if video frames is up to 4\n // we're assuming that if video frames are available, audio is also available.\n const videoBufferLength = this.videoOutput.getBufferLength();\n this.startPlayback(true);\n if (videoBufferLength >= 6) {\n this.startPlayback();\n }\n }\n}\n","import { Environment } from \"../types/environment\";\n\nexport function getApiBase(environment: Environment): string {\n switch (environment) {\n case \"production\":\n return \"https://chat-api.unith.ai\";\n case \"staging\":\n return \"https://chat-api.stg.unith.live\";\n case \"development\":\n return \"https://chat-development-origin.api.unith.live\";\n default:\n return \"https://chat-api.unith.ai\";\n }\n}\n\nexport function getStreamBase(environment: Environment): string {\n switch (environment) {\n case \"production\":\n return \"wss://stream-api.unith.ai/stream-hub\";\n case \"staging\":\n return \"wss://stream-api.stg.unith.live/stream-hub\";\n case \"development\":\n return \"wss://stream-api.dev.unith.live/stream-hub\";\n default:\n return \"wss://stream-api.unith.ai/stream-hub\";\n }\n}\n","import { AnyIncomingSocketEvent } from \"../types/event\";\n\n\nexport function isValidSocketEvent(event: any): event is AnyIncomingSocketEvent {\n return !!event.event;\n}","import { WebsocketConnectionConfig } from \"../types/connection\";\nimport {\n DisconnectionDetails,\n AnyIncomingSocketEvent,\n OnDisconnectCallback,\n OnMessageCallback,\n isJoinEvent,\n Message,\n isPongEvent,\n OnPingPongCallback,\n PingEventData,\n PongEventData,\n EventType,\n isStreamingErrorEvent,\n} from \"../types/event\";\nimport { getStreamBase } from \"./environment\";\nimport { isValidSocketEvent } from \"./event\";\n\nexport class Connection {\n public static async create(\n config: WebsocketConnectionConfig\n ): Promise<Connection> {\n let socket: WebSocket | null = null;\n\n const streamBase = getStreamBase(config.environment);\n try {\n const tempUrl = `${streamBase}/${config.orgId}/${config.headId}`;\n socket = new WebSocket(tempUrl);\n const userId = await new Promise<string>((resolve, reject) => {\n socket!.addEventListener(\n \"open\",\n () => {\n const initMessage = {\n token: config.token,\n api_key: config.apiKey,\n text_only: config.mode === \"chat\",\n format: config.streamType, // Request VP8 format for WebCodecs\n quality: config.quality,\n crop: config.crop,\n };\n socket!.send(JSON.stringify(initMessage));\n },\n { once: true }\n );\n\n socket!.addEventListener(\"close\", reject);\n socket!.addEventListener(\"error\", reject);\n socket!.addEventListener(\n \"message\",\n (event: MessageEvent) => {\n const message = JSON.parse(event.data);\n\n if (!isValidSocketEvent(message)) {\n console.log(\"invalid socket event\");\n return;\n }\n\n if (isJoinEvent(message)) {\n if (message.granted) {\n config.onJoin?.();\n resolve(message.user_id);\n }\n } else {\n if (isStreamingErrorEvent(message)) {\n reject({\n type: \"connection\",\n message:\n \"We are currently at full capacity. Please try again later.\",\n });\n }\n }\n },\n { once: true }\n );\n });\n return new Connection(socket, userId);\n } catch (error) {\n socket?.close();\n throw error;\n }\n }\n\n private queue: AnyIncomingSocketEvent[] = [];\n private disconnectionDetails: DisconnectionDetails | null = null;\n private onDisconnectCallback: OnDisconnectCallback | null = null;\n private onMessageCallback: OnMessageCallback | null = null;\n private onPingPongCallback: OnPingPongCallback | null = null;\n\n private constructor(\n public readonly socket: WebSocket,\n public readonly userId: string\n ) {\n this.socket.addEventListener(\"error\", event => {\n //only disconnect once per session or allow reconnection ??\n this.disconnect({\n reason: \"error\",\n message: \"The connection was closed due to a websocket error.\",\n context: event,\n });\n });\n\n this.socket.addEventListener(\"close\", event => {\n this.disconnect({\n reason: \"error\",\n message: event.reason || \"The connection was closed by the server.\",\n context: event,\n });\n });\n\n this.socket.addEventListener(\"message\", event => {\n try {\n //check if binary event\n let parsedEvent;\n if (event.data instanceof Blob || event.data instanceof ArrayBuffer) {\n parsedEvent = {\n event: EventType.BINARY,\n user_id: \"\",\n username: \"\",\n data: event.data,\n };\n } else {\n parsedEvent = JSON.parse(event.data);\n }\n\n if (isPongEvent(parsedEvent)) {\n //discard pong events if callback isn't set yet.\n //todo: add a new queue for ping/pong events\n if (!this.onPingPongCallback) return;\n this.onPingPongCallback(parsedEvent as PongEventData);\n return;\n }\n\n if (!isValidSocketEvent(parsedEvent)) {\n return;\n }\n\n if (this.onMessageCallback) {\n this.onMessageCallback(parsedEvent);\n } else {\n this.queue.push(parsedEvent);\n }\n } catch (_) {}\n });\n }\n\n private disconnect(details: DisconnectionDetails) {\n if (!this.disconnectionDetails) {\n this.disconnectionDetails = details;\n this.onDisconnectCallback?.(details);\n }\n }\n\n public close() {\n this.socket.close();\n }\n\n public sendMessage(message: Message) {\n this.socket.send(JSON.stringify(message));\n }\n\n public sendPingEvent(message: PingEventData) {\n // console.log(message)\n if (!this.onPingPongCallback) {\n console.warn(\"Ping event sent without a callback set.\");\n return;\n }\n this.socket.send(JSON.stringify(message));\n }\n\n public onPingPong(callback: OnPingPongCallback) {\n this.onPingPongCallback = callback;\n }\n\n public onMessage(callback: OnMessageCallback) {\n this.onMessageCallback = callback;\n const queue = this.queue;\n this.queue = [];\n\n if (queue.length > 0) {\n // Make sure the queue is flushed after the constructors finishes and\n // classes are initialized.\n queueMicrotask(() => {\n queue.forEach(callback);\n });\n }\n }\n\n public onDisconnect(callback: OnDisconnectCallback) {\n this.onDisconnectCallback = callback;\n const details = this.disconnectionDetails;\n if (details) {\n // Make sure the event is triggered after the constructors finishes and\n // classes are initialized.\n queueMicrotask(() => {\n callback(details);\n });\n }\n }\n}\n","import { ObjectOfStrings } from \"../types/idle-video\";\n\nexport class IdleVideo {\n constructor(\n public readonly idleVideoSource: string,\n public readonly videoId: string\n ) {}\n\n public static async getIdleVideo(\n apiUrl: string,\n orgId: string,\n headId: string\n ): Promise<IdleVideo> {\n //todo: catch errors in getIdleVideoId\n const videoId = await IdleVideo.getIdleVideoId(apiUrl, orgId, headId);\n const url = `${apiUrl}/api/v1/idle/${orgId}/${headId}/${videoId}`;\n const response = await fetch(url);\n if (response.status >= 200 && response.status <= 299) {\n const idleVideoString = (await response.json()) as string;\n if (idleVideoString) {\n return new IdleVideo(idleVideoString, videoId);\n } else {\n throw new Error(\n \"No idle video found for the specified orgId and headId.\"\n );\n }\n } else {\n const errorMessage = await response.text();\n const error = new Error(\n `An error occurred retrieving idle video: ${response.status} ${response.statusText}. Response: ${errorMessage}`\n );\n (error as Error).name = \"IdleVideoError\";\n throw error;\n }\n }\n\n public async getAvatarSrc(\n apiUrl: string,\n orgId: string,\n headId: string\n ): Promise<string> {\n //todo: catch errors in getIdleVideoId\n const url = `${apiUrl}/api/v1/avatar/${orgId}/${headId}/${this.videoId}`;\n const response = await fetch(url);\n if (response.status >= 200 && response.status <= 299) {\n const avatarSrc = (await response.json()) as string;\n if (avatarSrc) {\n return avatarSrc;\n } else {\n throw new Error(\n \"No avatar image found for the specified orgId and headId.\"\n );\n }\n } else {\n const errorMessage = await response.text();\n const error = new Error(\n `An error occurred retrieving avatar: ${response.status} ${response.statusText}. Response: ${errorMessage}`\n );\n (error as Error).name = \"AvatarError\";\n throw error;\n }\n }\n\n private static async getIdleVideoId(\n apiUrl: string,\n orgId: string,\n headId: string\n ): Promise<string> {\n const url = `${apiUrl}/api/v1/videos/${orgId}/${headId}`;\n const response = await fetch(url);\n if (response.status >= 200 && response.status <= 299) {\n const videos = (await response.json()) as Array<ObjectOfStrings>;\n if (videos.length > 0) {\n return videos[0].id;\n } else {\n throw new Error(\n \"No idle video found for the specified orgId and headId.\"\n );\n }\n } else {\n const errorMessage = await response.text();\n const error = new Error(\n `An error occurred retrieving idle video: ${response.status} ${response.statusText}. Response: ${errorMessage}`\n );\n (error as Error).name = \"IdleVideoError\";\n throw error;\n }\n }\n}\n","import { EventType, PingEventData, PongEventData } from \"../types/event\";\nimport { Connection } from \"./connection\";\n\ntype LatencyStatus = \"good\" | \"moderate\" | \"poor\";\n\ninterface LatencyStats {\n rtt: number;\n average: number;\n status: LatencyStatus;\n}\n\ntype OnUpdateCallback = (stats: LatencyStats) => void;\n\nexport class LatencyMonitor {\n private intervalId: any = null;\n private pingInterval: number;\n private timeout: number;\n private lastPingTimestamp: string = \"0\";\n private history: number[] = [];\n private maxHistory: number;\n private onUpdate?: OnUpdateCallback;\n\n constructor(\n public connection: Connection,\n options?: {\n pingInterval?: number;\n timeout?: number;\n maxHistory?: number;\n onUpdate?: OnUpdateCallback;\n }\n ) {\n this.pingInterval = options?.pingInterval || 4000;\n this.timeout = options?.timeout || 2000;\n this.maxHistory = options?.maxHistory || 3;\n this.onUpdate = options?.onUpdate;\n this.handleMessage = this.handleMessage.bind(this);\n this.connection.onPingPong(this.handleMessage);\n }\n\n start() {\n if (this.intervalId) return;\n\n this.intervalId = setInterval(() => {\n this.sendPing();\n }, this.pingInterval);\n }\n\n stop() {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n\n destroy() {\n this.stop();\n }\n\n private sendPing() {\n const ts = performance.now().toString();\n this.lastPingTimestamp = ts;\n const pingPayload: PingEventData = {\n event: EventType.PING,\n timestamp: this.lastPingTimestamp,\n id: \"0\",\n };\n\n this.connection.sendPingEvent(pingPayload);\n\n //todo: Handle timeout\n // setTimeout(() => {\n // if (\n // performance.now() - this.lastPingTimestamp >= this.timeout\n // ) {\n // this.recordLatency(this.timeout);\n // }\n // }, this.timeout);\n }\n\n private handleMessage(event: PongEventData) {\n try {\n if (event.timestamp === this.lastPingTimestamp) {\n const rtt = performance.now() - parseFloat(event.timestamp);\n this.recordLatency(rtt);\n }\n } catch (e) {\n console.log(\"logging irrelevant message\");\n // ignore malformed or messages that aren't within the worse possible threshold\n }\n }\n\n private recordLatency(rtt: number) {\n this.history.push(rtt);\n if (this.history.length > this.maxHistory) {\n this.history.shift(); // keep only recent entries\n }\n\n // console.log(this.history);\n\n const average =\n this.history.reduce((a, b) => a + b, 0) / this.history.length;\n const status = this.classifyLatency(average);\n\n const stats: LatencyStats = { rtt, average, status };\n\n if (this.onUpdate) {\n this.onUpdate(stats);\n }\n }\n\n private classifyLatency(ms: number): LatencyStatus {\n if (ms < 100) return \"good\";\n if (ms < 250) return \"moderate\";\n return \"poor\";\n }\n}\n","import { DriftSample, SyncConfig, TimingInfo } from \"../types/sync\";\n\nexport class SyncController {\n private config: SyncConfig;\n private driftHistory: DriftSample[] = [];\n private correctionInProgress: boolean = false;\n private lastAudioTiming: TimingInfo | null = null;\n private lastVideoTiming: TimingInfo | null = null;\n\n constructor(config: SyncConfig) {\n this.config = config;\n }\n\n public updateAudioTime(relativeTime: number): void {\n this.lastAudioTiming = {\n timestamp: performance.now(),\n relativeTime: relativeTime,\n };\n }\n\n public updateVideoTime(relativeTime: number): void {\n this.lastVideoTiming = {\n timestamp: performance.now(),\n relativeTime: relativeTime,\n };\n }\n\n public resetTiming(): void {\n this.lastAudioTiming = null;\n this.lastVideoTiming = null;\n }\n\n public async checkSync(): Promise<void> {\n if (this.correctionInProgress) {\n console.warn(\"Sync correction already in progress\");\n return;\n }\n if (!this.lastAudioTiming || !this.lastVideoTiming) {\n console.warn(\"Insufficient timing data for sync check\");\n return;\n }\n\n const drift = Math.abs(\n this.lastAudioTiming.relativeTime - this.lastVideoTiming.relativeTime\n );\n\n this.recordDrift(drift);\n\n if (drift > this.config.tolerance) {\n // Handle drift correction\n console.log(`Drift detected: ${drift}ms`);\n } else {\n console.log(\"No significant drift detected\");\n }\n }\n\n private recordDrift(drift: number): void {\n this.driftHistory.push({\n timestamp: Date.now(),\n drift,\n audioTime: this.lastAudioTiming!.relativeTime,\n videoTime: this.lastVideoTiming!.relativeTime,\n });\n\n // Maintain history length\n if (this.driftHistory.length > this.config.historyLength) {\n this.driftHistory.shift();\n }\n }\n}\n","const storage = window.localStorage;\nconst location = window.location.origin + window.location.pathname;\n\nexport const getFromStorage = (\n key: string,\n orgId: string,\n headId: string\n): string | null | undefined => {\n if (typeof storage !== \"undefined\") {\n const prefix = `chat:${location}:${orgId}:${headId}:`;\n return storage.getItem(`${prefix}${key}`);\n }\n};\n\nexport const setToStorage = (\n key: string,\n val: string,\n orgId: string,\n headId: string\n): void => {\n if (typeof storage !== \"undefined\") {\n const prefix = `chat:${location}:${orgId}:${headId}:`;\n storage.setItem(`${prefix}${key}`, val);\n return;\n }\n};\n\nexport const setCookie = (key: string, val: string): void => {\n if (typeof storage !== \"undefined\") {\n storage.setItem(key, val);\n return;\n }\n};\n\nexport const getCookie = (key: string): string | null | undefined => {\n if (typeof storage !== \"undefined\") {\n return storage.getItem(key);\n }\n};\n","export interface FrameData {\n data: Uint8Array<ArrayBuffer>;\n timestamp: number;\n isKeyframe: boolean;\n sequenceId: number;\n size: number;\n}\n\nexport interface VideoMetrics {\n framesRendered: number;\n framesDropped: number;\n actualFps: number;\n bufferSize: number;\n lastRenderTime: number;\n decoderQueueSize: number;\n}\n\nexport enum VideoState {\n INITIALIZING = \"initializing\",\n READY = \"ready\",\n PLAYING = \"playing\",\n PAUSED = \"paused\",\n INTERRUPTED = \"interrupted\",\n DESTROYED = \"destroyed\",\n}\n\nexport interface VideoTransitionConfig {\n enabled: boolean;\n type: VideoTransitionType;\n}\n\nexport enum VideoTransitionType {\n NONE = \"none\",\n CROSSFADE = \"crossfade\",\n FADEIN = \"fadein\",\n FADEOUT = \"fadeout\",\n}\n\nexport interface VideoFrameType {\n timeStamp: number;\n isKeyframe?: boolean;\n}\n\nexport interface VideoFormatConfig {\n width: number;\n height: number;\n frameRate: number;\n format: \"jpeg\" | \"webp\" | \"png\";\n backgroundColor?: string;\n antialias?: boolean;\n resolution?: number;\n maxBufferSize?: number;\n enableAdaptiveQuality?: boolean;\n}\n","import {\n ApiAsrTokenType,\n ApiErrorType,\n AuthTokenType,\n HeadType,\n TokenResponseType,\n} from \"../types/User\";\nimport { AsrTokenType } from \"../utils/microphone\";\nimport { getFromStorage, setToStorage } from \"../utils/storage\";\n\nexport class User {\n public static async loginUser(\n username: string,\n password: string,\n apiUrl: string,\n orgId: string,\n headId: string\n ): Promise<User> {\n const formData = new FormData();\n formData.append(\"username\", username);\n formData.append(\"password\", password);\n try {\n const url = `${apiUrl}/token`;\n const response = await fetch(url, {\n method: \"POST\",\n body: formData,\n });\n if (response.status >= 200 && response.status <= 299) {\n const user = (await response.json()) as TokenResponseType;\n return new User(\n user.access_token,\n user.token_type,\n user.user_id,\n username,\n password,\n orgId,\n headId,\n apiUrl\n );\n } else {\n const errorMessage = await response.text();\n const error = new Error(\n `An error occurred: ${response.status} ${response.statusText}. Response: ${errorMessage}`\n );\n (error as ApiErrorType).response = {\n status: response.status,\n status_code: response.status,\n data: JSON.parse(errorMessage),\n };\n throw error;\n }\n } catch (error: any) {\n if (error?.response?.data?.detail) {\n throw new Error(error?.response?.data?.detail);\n } else {\n throw new Error(JSON.stringify(error));\n }\n }\n }\n\n private EXPIRATION_OFFSET = 15 * 60 * 1000;\n public accessToken: string = \"\";\n public tokenType: string = \"\";\n public sessionId: number = 0;\n\n private constructor(\n accessToken: string,\n tokenType: string,\n public readonly id: string,\n public username: string,\n public readonly password: string,\n public readonly orgId: string,\n public readonly headId: string,\n public readonly apiBase: string\n ) {\n this.accessToken = accessToken;\n this.tokenType = tokenType;\n const existingSession = getFromStorage(\"session_id\", orgId, headId);\n if (existingSession) {\n this.sessionId = parseInt(existingSession);\n this.sessionId += 1; // Increment session ID to avoid conflicts\n }\n setToStorage(\"session_id\", this.sessionId.toString(), orgId, headId);\n }\n\n public async getHeadDetails(apiKey: string): Promise<HeadType> {\n const url = `${this.apiBase}/api/v1/head/${this.orgId}/${this.headId}/${apiKey}`;\n try {\n const response = await fetch(url);\n if (response.status >= 200 && response.status <= 299) {\n const headInfo = (await response.json()) as HeadType;\n return headInfo;\n } else {\n const errorMessage = await response.text();\n const error = new Error(\n `An error occurred: ${response.status} ${response.statusText}. Response: ${errorMessage}`\n );\n (error as ApiErrorType).response = {\n status: response.status,\n status_code: response.status,\n data: JSON.parse(errorMessage),\n };\n throw error;\n }\n } catch (error: any) {\n if (error?.response?.data?.detail) {\n throw new Error(error?.response?.data?.detail);\n } else {\n throw new Error(JSON.stringify(error));\n }\n }\n }\n\n public async getAuthToken(\n username: string,\n password: string\n ): Promise<TokenResponseType> {\n const url = `${this.apiBase}/token`;\n let access_token: TokenResponseType;\n\n const formData = new FormData();\n formData.append(\"username\", username);\n formData.append(\"password\", password);\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n body: formData,\n });\n access_token = (await response.json()) as TokenResponseType;\n } catch (error: any) {\n if (error?.response?.data?.detail) {\n throw new Error(error?.response?.data?.detail);\n } else {\n throw new Error(JSON.stringify(error));\n }\n }\n\n return access_token;\n }\n\n public async getApiAsrToken(auth_jwt: string): Promise<ApiAsrTokenType> {\n const url = `${this.apiBase}/api/v1/asr_token`;\n let token: string, region: string | undefined;\n\n try {\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${auth_jwt}` },\n });\n const data = (await response.json()) as ApiAsrTokenType;\n token = data.token;\n region = data.region;\n } catch (error: any) {\n if (error?.response?.data?.detail) {\n throw new Error(error?.response?.data?.detail);\n } else {\n throw new Error(JSON.stringify(error));\n }\n }\n return { token, region };\n }\n\n public async getAccessToken() {\n let access_token = getFromStorage(\n \"access_token\",\n this.orgId,\n this.headId\n ) as string;\n let user_id = getFromStorage(\"user_id\", this.orgId, this.headId);\n let session_id =\n parseInt(getFromStorage(\"session_id\", this.orgId, this.headId) || \"\") ||\n 0;\n\n // decode jwt and multiply to get the elapsed time in miliseconds\n // from 1st of January 1970 at 00:00 (UTC/GMT)\n let will_expire = true;\n if (\n // This means we saved undefined as the token in the past,\n // and we need to check for this case\n access_token !== \"undefined\" &&\n !!access_token &&\n !!user_id &&\n typeof session_id === \"number\"\n ) {\n let token_data: AuthTokenType = JSON.parse(\n window.atob(access_token.split(\".\")[1])\n ) as AuthTokenType;\n const exp = token_data.exp * 1000;\n\n // check if the token is about to expire (expiration time in\n // less than EXPIRATION_OFFSET milliseconds)\n const expiration_time = new Date().getTime() + this.EXPIRATION_OFFSET;\n will_expire = expiration_time > exp;\n }\n if (will_expire) {\n // Get a token:\n const response = await this.getAuthToken(this.username, this.password);\n if (!response) {\n throw new Error(\"Could not renew authentication token\");\n }\n access_token = response.access_token;\n user_id = response.user_id;\n // admin = response.admin;\n\n // TODO: only increment on user request\n session_id++;\n\n setToStorage(\"access_token\", access_token, this.orgId, this.headId);\n setToStorage(\"user_id\", user_id, this.orgId, this.headId);\n setToStorage(\n \"session_id\",\n session_id.toString(),\n this.orgId,\n this.headId\n );\n }\n const token_data = JSON.parse(\n window.atob(access_token.split(\".\")[1])\n ) as AuthTokenType;\n\n this.username = token_data.username;\n\n return { access_token, user_id, session_id };\n }\n\n public async getAsrToken() {\n let token = getFromStorage(\"asr_token\", this.orgId, this.headId);\n let region = getFromStorage(\"region\", this.orgId, this.headId);\n const { access_token } = await this.getAccessToken();\n\n let will_expire = true;\n\n if (!!token && !!region) {\n const decoded_token = JSON.parse(\n window.atob(token.split(\".\")[1])\n ) as AsrTokenType;\n const exp = decoded_token.exp * 1000;\n\n // check if the token is about to expire (expiration time in\n // less than EXPIRATION_OFFSET milliseconds)\n const expiration_time = new Date().getTime() + this.EXPIRATION_OFFSET;\n will_expire = expiration_time > exp;\n }\n\n if (will_expire) {\n // Get a token:\n const response = await this.getApiAsrToken(access_token);\n if (!response) {\n return { token: \"\", region: \"\" };\n }\n\n token = response.token;\n region = response.region;\n\n setToStorage(\"asr_token\", token, this.orgId, this.headId);\n setToStorage(\"asr_region\", region, this.orgId, this.headId);\n }\n return { token, region };\n }\n\n // public getToken(): string {\n // const accessToken = this.accessToken || getFromStorage(\"access_token\", this.orgId, this.headId);\n // const userId\n // }\n}\n","import { FrameData, VideoState, VideoFormatConfig } from \"../types/vp8\";\n\nexport class Vp8VideoOutput {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n private container: HTMLElement;\n private config: VideoFormatConfig;\n private resizeObserver: ResizeObserver | null = null;\n\n // WebCodecs VideoDecoder\n private decoder: VideoDecoder | null = null;\n\n // State management\n private state: VideoState = VideoState.INITIALIZING;\n private isProcessingFrame: boolean = false;\n private isStreaming: boolean = false;\n private startTime: number = 0;\n\n // Frame management\n //todo change framebuffer data structure to a deque to allow for o(1) operations\n private frameBuffer: FrameData[] = [];\n private currentSequenceId: number = 0;\n\n // Animation and timing\n private animationFrameId: number | null = null;\n private renderLoop: boolean = false;\n\n // FPS calculation\n private fpsCounter: { count: number; lastTime: number } = {\n count: 0,\n lastTime: 0,\n };\n\n // Context loss handling\n private handleContextLoss: (event: Event) => void = () => {};\n private handleContextRestore: (event: Event) => void = () => {};\n\n private constructor(\n canvas: HTMLCanvasElement,\n ctx: CanvasRenderingContext2D,\n container: HTMLElement,\n config: VideoFormatConfig\n ) {\n this.canvas = canvas;\n this.ctx = ctx;\n this.container = container;\n this.config = {\n maxBufferSize: 1000,\n enableAdaptiveQuality: false,\n ...config,\n };\n\n this.setupContextLossHandling();\n this.setupResizeHandling();\n this.initializeDecoder();\n }\n\n public static async create(\n container: HTMLElement,\n config: VideoFormatConfig\n ): Promise<Vp8VideoOutput> {\n // Check WebCodecs support\n if (!(\"VideoDecoder\" in window)) {\n throw new Error(\n \"WebCodecs VideoDecoder API is not supported in this browser\"\n );\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = config.width;\n canvas.height = config.height;\n canvas.style.position = \"absolute\";\n canvas.style.top = \"0\";\n canvas.style.left = \"0\";\n canvas.style.backgroundColor =\n config.backgroundColor?.toString() || \"#000000\";\n canvas.style.maxWidth = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.zIndex = \"0\";\n canvas.style.pointerEvents = \"none\";\n canvas.style.objectFit = \"cover\"; // Cover the entire container\n canvas.style.imageRendering = \"pixelated\"; // Ensure pixelated rendering for VP8\n\n const ctx = canvas.getContext(\"2d\", {\n alpha: false,\n desynchronized: true,\n });\n\n if (!ctx) {\n throw new Error(\"Failed to get 2D canvas context\");\n }\n\n ctx.imageSmoothingEnabled = true;\n container.appendChild(canvas);\n\n return new Vp8VideoOutput(canvas, ctx, container, config);\n }\n\n private async initializeDecoder(): Promise<void> {\n try {\n // Test decoder creation\n const testDecoder = new VideoDecoder({\n output: () => {},\n error: () => {},\n });\n testDecoder.close();\n\n // Create the actual decoder\n this.decoder = new VideoDecoder({\n output: (frame: VideoFrame) => {\n this.renderVideoFrame(frame);\n },\n error: (error: DOMException) => {\n console.error(\"VP8 Decoder error:\", error.message);\n },\n });\n\n // Configure for VP8\n await this.decoder.configure({\n codec: \"vp8\",\n codedWidth: this.config.width,\n codedHeight: this.config.height,\n });\n\n //todo: send event to indicate decoder is ready\n // this.state = VideoState.READY;\n // this.startRenderingStreamingVideo();\n } catch (error: any) {\n throw new Error(`Failed to initialize VP8 decoder: ${error.message}`);\n }\n }\n\n private setupResizeHandling(): void {\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n if (entry.target === this.container) {\n this.handleResize();\n }\n }\n });\n\n this.resizeObserver.observe(this.container);\n\n // Also handle window resize as fallback\n window.addEventListener(\"resize\", this.handleResize);\n }\n\n private handleResize = (): void => {\n const containerRect = this.container.getBoundingClientRect();\n const containerWidth = containerRect.width;\n const containerHeight = containerRect.height;\n\n // Update canvas size to match container\n this.canvas.width = containerWidth;\n this.canvas.height = containerHeight;\n\n // Update canvas style dimensions (already set to 100% but ensure consistency)\n this.canvas.style.width = `${containerWidth}px`;\n this.canvas.style.height = `${containerHeight}px`;\n\n // Re-enable smoothing after resize\n this.ctx.imageSmoothingEnabled = true;\n };\n\n public getBufferLength(): number {\n return this.frameBuffer.length;\n }\n\n public getStreamingStatus(): boolean {\n return this.isStreaming;\n }\n\n public async addFrame(\n frameData: Uint8Array<ArrayBuffer>,\n timestamp: number,\n isKeyframe: boolean = false\n ): Promise<void> {\n if (this.state === VideoState.DESTROYED) {\n throw new Error(\"Cannot add frame to destroyed video output\");\n }\n\n const frame: FrameData = {\n data: frameData,\n timestamp: timestamp,\n isKeyframe,\n sequenceId: this.currentSequenceId++,\n size: frameData.byteLength,\n };\n\n // Buffer management with adaptive quality\n if (this.frameBuffer.length >= (this.config.maxBufferSize || 1000)) {\n if (this.config.enableAdaptiveQuality) {\n if (isKeyframe) {\n const oldestNonKeyframeIndex = this.frameBuffer.findIndex(\n f => !f.isKeyframe\n );\n if (oldestNonKeyframeIndex !== -1) {\n this.frameBuffer.splice(oldestNonKeyframeIndex, 1);\n } else {\n this.frameBuffer.shift();\n }\n } else {\n return;\n }\n } else {\n this.frameBuffer.shift();\n }\n }\n\n this.frameBuffer.push(frame);\n }\n\n public async toggleStream(status: boolean): Promise<void> {\n this.isStreaming = status;\n }\n\n public clearFrame(): void {\n if (this.state === VideoState.DESTROYED) {\n throw new Error(\"Cannot clear frame from destroyed video output\");\n }\n\n // Stop the render loop\n this.renderLoop = false;\n\n if (this.animationFrameId) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n\n this.frameBuffer = [];\n this.isProcessingFrame = false;\n\n this.currentSequenceId = 0;\n this.startTime = 0;\n\n this.fpsCounter = {\n count: 0,\n lastTime: 0,\n };\n\n // Flush and reset decoder\n if (this.decoder && this.decoder.state === \"configured\") {\n try {\n this.decoder.flush();\n } catch (error) {\n console.warn(\"Error flushing decoder:\", error);\n }\n }\n\n // Clear canvas\n this.clearCanvas();\n this.state = VideoState.READY;\n }\n\n public interrupt(fadeOut: boolean = false): void {\n if (this.state === VideoState.DESTROYED) return;\n\n this.state = VideoState.INTERRUPTED;\n this.frameBuffer = [];\n this.isProcessingFrame = false;\n\n // Flush decoder\n if (this.decoder && this.decoder.state === \"configured\") {\n try {\n this.decoder.flush();\n } catch (error) {\n console.warn(\"Error flushing decoder:\", error);\n }\n }\n\n if (fadeOut) {\n this.fadeOutCanvas().then(() => {\n this.clearCanvas();\n });\n } else {\n this.clearCanvas();\n }\n }\n\n public destroy(): void {\n if (this.state === VideoState.DESTROYED) return;\n\n this.state = VideoState.DESTROYED;\n this.renderLoop = false;\n\n // Stop render loop\n if (this.animationFrameId) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n\n // Close decoder\n if (this.decoder) {\n this.decoder.close();\n this.decoder = null;\n }\n\n // Clear buffers\n this.frameBuffer = [];\n\n // Remove event listeners\n this.canvas.removeEventListener(\"webglcontextlost\", this.handleContextLoss);\n this.canvas.removeEventListener(\n \"webglcontextrestored\",\n this.handleContextRestore\n );\n\n // Remove canvas from DOM\n if (this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n\n this.isProcessingFrame = false;\n }\n\n public getState(): VideoState {\n return this.state;\n }\n\n public startRenderingStreamingVideo(init: boolean): void {\n if (!init) return;\n this.state = VideoState.READY;\n this.renderLoop = true;\n //log start time.\n this.startTime = performance.now();\n this.render();\n }\n\n private render(): void {\n if (!this.renderLoop || this.state === VideoState.DESTROYED) {\n return;\n }\n const nextFrameTimeStamp = this.frameBuffer[0]?.timestamp || 0;\n if (this.frameBuffer.length > 0 && this.frameBuffer[0].sequenceId === 0) {\n this.startTime = performance.now();\n }\n const interval = performance.now() - this.startTime;\n const intervalDiff = interval - nextFrameTimeStamp;\n\n // Check if it's time to process next frame\n if (\n interval >= nextFrameTimeStamp &&\n this.frameBuffer.length > 0 &&\n !this.isProcessingFrame\n ) {\n this.processNextFrame(interval, intervalDiff);\n }\n\n //todo: find way to stop the loop when no frames are available\n this.animationFrameId = requestAnimationFrame(() => this.render());\n }\n\n private async processNextFrame(\n intervalTimestamp: number,\n excessDrift: number\n ): Promise<void> {\n if (\n this.isProcessingFrame ||\n this.frameBuffer.length === 0 ||\n !this.decoder\n )\n return;\n if (this.decoder.state !== \"configured\") return;\n\n this.isProcessingFrame = true;\n\n if (\n excessDrift > 540 &&\n this.frameBuffer[this.frameBuffer.length - 1].timestamp <\n intervalTimestamp\n ) {\n // handle buffering cases\n //case 1: video is still in early phases (< 10 frames played) we should show loader until enough frames in the current timestamp interval are available\n // console.log(\"Buffering here\");\n }\n let frame = this.frameBuffer.shift()!;\n\n try {\n await this.decodeVP8Frame(frame);\n } catch (error) {\n console.error(\"Frame processing failed:\", error);\n } finally {\n this.isProcessingFrame = false;\n }\n }\n\n private async decodeVP8Frame(frame: FrameData): Promise<void> {\n try {\n // Create EncodedVideoChunk\n const chunk = new EncodedVideoChunk({\n type: frame.isKeyframe ? \"key\" : \"delta\",\n timestamp: frame.timestamp * 1000, // Convert to microseconds\n data: frame.data,\n });\n\n // Decode the frame\n this.decoder!.decode(chunk);\n } catch (error: any) {\n throw new Error(`VP8 decode failed: ${error.message}`);\n }\n }\n\n private renderVideoFrame(videoFrame: VideoFrame): void {\n try {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Calculate aspect ratios\n const canvasAspect = this.canvas.width / this.canvas.height;\n const videoAspect = videoFrame.displayWidth / videoFrame.displayHeight;\n\n let drawWidth, drawHeight, offsetX, offsetY;\n\n // Implement object-fit: cover behavior\n if (videoAspect > canvasAspect) {\n // Video is wider - fit to height, crop sides\n drawHeight = this.canvas.height;\n drawWidth = drawHeight * videoAspect;\n offsetX = (this.canvas.width - drawWidth) / 2;\n offsetY = 0;\n } else {\n // Video is taller - fit to width, crop top/bottom\n drawWidth = this.canvas.width;\n drawHeight = drawWidth / videoAspect;\n offsetX = 0;\n offsetY = (this.canvas.height - drawHeight) / 2;\n }\n\n this.ctx.drawImage(videoFrame, offsetX, offsetY, drawWidth, drawHeight);\n\n videoFrame.close();\n } catch (error) {\n console.error(\"Error rendering video frame:\", error);\n }\n }\n\n private clearCanvas(): void {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n\n private async fadeOutCanvas(): Promise<void> {\n return new Promise(resolve => {\n let opacity = 1;\n const fadeStep = () => {\n opacity -= 0.05;\n this.canvas.style.opacity = opacity.toString();\n\n if (opacity <= 0) {\n this.canvas.style.opacity = \"1\";\n resolve();\n } else {\n requestAnimationFrame(fadeStep);\n }\n };\n fadeStep();\n });\n }\n\n private setupContextLossHandling(): void {\n this.handleContextLoss = (event: Event) => {\n event.preventDefault();\n console.warn(\"Canvas context lost\");\n this.state = VideoState.PAUSED;\n };\n\n this.handleContextRestore = () => {\n if (this.state === VideoState.PAUSED) {\n this.state = VideoState.PLAYING;\n }\n };\n\n this.canvas.addEventListener(\"webglcontextlost\", this.handleContextLoss);\n this.canvas.addEventListener(\n \"webglcontextrestored\",\n this.handleContextRestore\n );\n }\n}\n","import { Vp8VideoOutput } from \"./vp8\";\nimport { IdleVideoConfig } from \"../types/idle-video\";\nimport {\n VideoFormatConfig,\n VideoTransitionConfig,\n VideoTransitionType,\n} from \"../types/vp8\";\n\nexport interface VideoEffectConfig {\n background?: BackgroundConfig;\n}\n\nexport interface BackgroundConfig {\n type: \"color\" | \"image\";\n value: number | string;\n}\n\nexport class VideoOutput {\n private videoOutput: Vp8VideoOutput;\n private container: HTMLElement;\n private idleVideo: HTMLVideoElement | null = null;\n private cachedVideo: HTMLVideoElement | null = null;\n private idleVideoConfig: IdleVideoConfig | null = null;\n\n //transitions\n private videoTransitionConfig: VideoTransitionType | null = null;\n private readonly CROSSFADE_DURATION = 500;\n private isTransitioning: boolean = false;\n\n // track state\n private isShowingIdleVideo: boolean = true;\n private bufferCheckAnimationId: number | null = null;\n private lastBufferCheckTime: number = 0;\n private sessionStarted: boolean = false;\n\n //cached video\n private isShowingCachedVideo: boolean = false;\n\n //events\n private onIdleVideoShown?: () => void;\n private onIdleVideoHidden?: () => void;\n\n private onSpeakingStartCallback: (() => void) | null = null;\n private onSpeakingEndCallback: (() => void) | null = null;\n\n public static async createVideoOutput(\n container: HTMLElement,\n config: VideoFormatConfig & {\n effects?: VideoEffectConfig;\n idleVideo: IdleVideoConfig;\n transition: VideoTransitionType;\n }\n ) {\n let videoOutput: Vp8VideoOutput;\n videoOutput = await Vp8VideoOutput.create(container, config);\n\n const videoOutputInstance = new VideoOutput(\n videoOutput,\n container,\n config.transition,\n config.effects\n );\n await videoOutputInstance.setupIdleVideo(config.idleVideo, config);\n await videoOutputInstance.setupCachedVideo(config);\n\n return videoOutputInstance;\n }\n\n private constructor(\n videoOutput: Vp8VideoOutput,\n container: HTMLElement,\n transitionsConfig: VideoTransitionType,\n effectsConfig?: VideoEffectConfig\n ) {\n this.videoOutput = videoOutput;\n this.container = container;\n this.videoTransitionConfig = transitionsConfig;\n }\n\n private async setupIdleVideo(\n idleVideoConfig: IdleVideoConfig,\n videoConfig: VideoFormatConfig\n ): Promise<void> {\n this.idleVideoConfig = idleVideoConfig;\n\n this.idleVideo = document.createElement(\"video\");\n this.idleVideo.src = idleVideoConfig.src;\n this.idleVideo.width = videoConfig.width;\n this.idleVideo.height = videoConfig.height;\n this.idleVideo.style.position = \"absolute\";\n this.idleVideo.style.top = \"0\";\n this.idleVideo.style.left = \"0\";\n this.idleVideo.style.width = \"100%\";\n this.idleVideo.style.height = \"100%\";\n this.idleVideo.style.objectFit = \"cover\";\n this.idleVideo.style.maxWidth = \"100%\";\n this.idleVideo.style.backgroundColor =\n videoConfig.backgroundColor || \"#000000\";\n this.idleVideo.style.zIndex = \"1\";\n this.idleVideo.style.pointerEvents = \"none\";\n this.idleVideo.style.opacity = \"1\";\n this.idleVideo.muted = true;\n this.idleVideo.loop = true;\n this.idleVideo.controls = false;\n this.idleVideo.playsInline = true;\n this.idleVideo.preload = \"auto\";\n\n const containerStyle = getComputedStyle(this.container);\n if (containerStyle.position === \"static\") {\n this.container.style.position = \"relative\";\n }\n\n this.container.appendChild(this.idleVideo);\n\n try {\n await this.idleVideo.load();\n await this.idleVideo.play();\n } catch (error) {\n console.error(\"Failed to load idle video:\", error);\n }\n }\n\n private async setupCachedVideo(\n videoConfig: VideoFormatConfig\n ): Promise<void> {\n this.cachedVideo = document.createElement(\"video\");\n this.cachedVideo.src = \"\";\n this.cachedVideo.width = videoConfig.width;\n this.cachedVideo.height = videoConfig.height;\n this.cachedVideo.style.position = \"absolute\";\n this.cachedVideo.style.top = \"0\";\n this.cachedVideo.style.left = \"0\";\n this.cachedVideo.style.width = \"100%\";\n this.cachedVideo.style.height = \"100%\";\n this.cachedVideo.style.objectFit = \"cover\";\n this.cachedVideo.style.maxWidth = \"100%\";\n // this.cachedVideo.style.backgroundColor =\n // videoConfig.backgroundColor?.toString() || \"#000000\";\n this.cachedVideo.style.zIndex = \"2\"; // Above idle video\n this.cachedVideo.style.pointerEvents = \"none\";\n this.cachedVideo.style.opacity = \"0\";\n this.cachedVideo.style.transition = `opacity ${this.CROSSFADE_DURATION}ms ease-in-out`;\n this.cachedVideo.controls = false;\n this.cachedVideo.playsInline = true;\n this.cachedVideo.preload = \"auto\";\n\n // Add to container but keep invisible\n this.container.appendChild(this.cachedVideo);\n }\n\n public onSpeakingStart(speakingStartCallback: () => void): void {\n this.onSpeakingStartCallback = speakingStartCallback;\n }\n\n public onSpeakingEnd(speakingEndCallback: () => void): void {\n this.onSpeakingEndCallback = speakingEndCallback;\n }\n\n public startStreaming(init = false): void {\n this.sessionStarted = true;\n this.startBufferMonitoring();\n this.videoOutput.startRenderingStreamingVideo(init);\n }\n\n public stopBufferMonitoring(): void {\n if (this.bufferCheckAnimationId) {\n cancelAnimationFrame(this.bufferCheckAnimationId);\n this.bufferCheckAnimationId = null;\n }\n this.lastBufferCheckTime = 0;\n this.sessionStarted = false;\n }\n\n public getBufferLength(): number {\n return this.videoOutput.getBufferLength();\n }\n\n public startBufferMonitoring(): void {\n if (this.bufferCheckAnimationId) return;\n\n this.lastBufferCheckTime = 0;\n\n const checkBuffer = (timestamp: number) => {\n if (timestamp - this.lastBufferCheckTime >= 100) {\n if (this.sessionStarted && this.videoOutput.getBufferLength() > 0) {\n // this.hideIdleVideo();\n this.hideIdleVideoBeforeStream();\n } else if (\n !this.videoOutput.getStreamingStatus() &&\n this.videoOutput.getBufferLength() === 0\n ) {\n // this.showIdleVideo();\n this.showIdleVideoAfterStream();\n }\n this.lastBufferCheckTime = timestamp;\n }\n this.bufferCheckAnimationId = requestAnimationFrame(checkBuffer);\n };\n\n this.bufferCheckAnimationId = requestAnimationFrame(checkBuffer);\n }\n\n public toggleCacheVideoMute(): void {\n if (this.cachedVideo) {\n this.cachedVideo.muted = !this.cachedVideo.muted;\n }\n }\n\n public async playCachedVideo(cachedVideoUrl: string): Promise<void> {\n if (\n this.isShowingCachedVideo ||\n !this.cachedVideo ||\n !cachedVideoUrl ||\n this.isTransitioning\n )\n return;\n\n this.isShowingCachedVideo = true;\n\n try {\n // Reset cached video state\n this.cachedVideo.src = cachedVideoUrl;\n this.cachedVideo.style.opacity = \"0\";\n\n // Wait for video to load\n await new Promise<void>((resolve, reject) => {\n const onLoad = () => resolve();\n const onError = () => reject(new Error(\"Video failed to load\"));\n\n this.cachedVideo!.addEventListener(\"loadeddata\", onLoad, {\n once: true,\n });\n this.cachedVideo!.addEventListener(\"error\", onError, { once: true });\n this.cachedVideo!.load();\n });\n // Start playing cached video\n this.crossfadeFromIdleToCached();\n await this.cachedVideo.play();\n // Listen for video end\n this.cachedVideo.addEventListener(\n \"ended\",\n async () => {\n // Crossfade back to idle when cached video ends\n // if (this.videoTransitionConfig === VideoTransitionType.CROSSFADE) {\n // await this.crossfadeFromCachedToIdle();\n // } else {\n // // Instant switch back\n // if (this.cachedVideo) {\n // this.cachedVideo.style.opacity = \"0\";\n // }\n // if (this.idleVideo) {\n // this.idleVideo.style.opacity = \"1\";\n // }\n // }\n await this.crossfadeFromCachedToIdle();\n await this.cleanupCachedVideo();\n await this.showIdleVideo();\n },\n { once: true }\n );\n } catch (error) {\n console.error(\"Failed to play cached video:\", error);\n this.cachedVideo.style.opacity = \"0\";\n this.isShowingCachedVideo = false;\n await this.showIdleVideo();\n }\n }\n\n private async crossfadeFromIdleToCached(): Promise<void> {\n if (!this.idleVideo || !this.cachedVideo || this.isTransitioning) return;\n\n this.isTransitioning = true;\n\n // Ensure idle video stays at 100% opacity\n this.idleVideo.style.opacity = \"1\";\n\n // Trigger reflow to ensure transition works\n void this.cachedVideo.offsetWidth;\n\n // Fade in cached video from 0 to 100% ON TOP of idle video\n this.cachedVideo.style.opacity = \"1\";\n\n // Wait for transition to complete\n await new Promise(resolve => setTimeout(resolve, this.CROSSFADE_DURATION));\n\n // After transition, fade out idle underneath\n if (this.idleVideo) {\n this.idleVideo.style.opacity = \"0\";\n }\n\n this.isShowingIdleVideo = false;\n this.onIdleVideoHidden?.();\n this.isTransitioning = false;\n }\n\n private async crossfadeFromCachedToIdle(): Promise<void> {\n if (!this.idleVideo || !this.cachedVideo || this.isTransitioning) return;\n\n this.isTransitioning = true;\n\n // Ensure idle video is ready and at full opacity\n this.idleVideo.style.opacity = \"1\";\n\n if (this.idleVideo.paused) {\n try {\n await this.idleVideo.play();\n } catch (error) {\n console.error(\"Failed to play idle video during crossfade:\", error);\n }\n }\n\n // Trigger reflow\n void this.cachedVideo.offsetWidth;\n\n // Fade out cached video from 100% to 0% (revealing idle underneath)\n this.cachedVideo.style.opacity = \"0\";\n\n // Wait for transition to complete\n await new Promise(resolve => setTimeout(resolve, this.CROSSFADE_DURATION));\n\n this.isShowingIdleVideo = true;\n this.onSpeakingEndCallback?.();\n this.onIdleVideoShown?.();\n this.isTransitioning = false;\n }\n\n private async cleanupCachedVideo(): Promise<void> {\n if (this.cachedVideo) {\n this.cachedVideo.pause();\n this.cachedVideo.currentTime = 0;\n this.cachedVideo.src = \"\";\n this.cachedVideo.style.opacity = \"0\";\n }\n this.isShowingCachedVideo = false;\n }\n\n private hideIdleVideo(): void {\n if (!this.idleVideo || !this.isShowingIdleVideo) return;\n this.onSpeakingStartCallback?.();\n this.isShowingIdleVideo = false;\n this.idleVideo.style.opacity = \"0\";\n this.onIdleVideoHidden?.();\n }\n\n private hideIdleVideoBeforeStream(): void {\n if (!this.idleVideo || !this.isShowingIdleVideo) return;\n this.onSpeakingStartCallback?.();\n this.idleVideo.style.transition = `opacity ${this.CROSSFADE_DURATION}ms ease-in-out`;\n this.isShowingIdleVideo = false;\n this.idleVideo.style.opacity = \"0\";\n this.onIdleVideoHidden?.();\n }\n\n public setEventCallbacks(callbacks: {\n onIdleVideoShown?: () => void;\n onIdleVideoHidden?: () => void;\n }): void {\n this.onIdleVideoShown = callbacks.onIdleVideoShown;\n this.onIdleVideoHidden = callbacks.onIdleVideoHidden;\n }\n\n public getStreamingStatus(): boolean {\n return this.videoOutput.getStreamingStatus();\n }\n\n public async showIdleVideo(): Promise<void> {\n if (!this.idleVideo || this.isShowingIdleVideo) return;\n\n this.onSpeakingEndCallback?.();\n this.onIdleVideoShown?.();\n this.isShowingIdleVideo = true;\n this.idleVideo.style.opacity = \"1\";\n\n try {\n if (this.idleVideo.paused) {\n await this.idleVideo.play();\n }\n } catch (error) {\n console.error(\"failed to play idle video:\", error);\n }\n }\n\n public async showIdleVideoAfterStream(): Promise<void> {\n if (!this.idleVideo || this.isShowingIdleVideo) return;\n\n this.onSpeakingEndCallback?.();\n this.onIdleVideoShown?.();\n this.isShowingIdleVideo = true;\n this.idleVideo.style.opacity = \"1\";\n\n try {\n if (this.idleVideo.paused) {\n await this.idleVideo.play();\n }\n } catch (error) {\n console.error(\"failed to play idle video:\", error);\n }\n }\n\n // Proxy methods to video output\n public async addFrame(\n frameData: Blob | ArrayBuffer,\n timeStamp: number,\n isKeyframe?: boolean\n ): Promise<void> {\n const uint8Array =\n frameData instanceof Blob\n ? new Uint8Array(await frameData.arrayBuffer())\n : new Uint8Array(frameData);\n\n return this.videoOutput.addFrame(uint8Array, timeStamp, isKeyframe);\n }\n\n public clearFrame(): void {\n this.showIdleVideo();\n return this.videoOutput.clearFrame();\n }\n\n public async toggleStream(status: boolean): Promise<void> {\n return this.videoOutput.toggleStream(status);\n }\n\n public interrupt(fadeOut?: boolean): void {\n this.videoOutput.interrupt(fadeOut);\n }\n\n public destroy(): void {\n if (this.bufferCheckAnimationId) {\n cancelAnimationFrame(this.bufferCheckAnimationId);\n this.bufferCheckAnimationId = null;\n }\n if (this.idleVideo) {\n this.idleVideo.pause();\n if (this.idleVideo.parentNode) {\n this.idleVideo.parentNode.removeChild(this.idleVideo);\n }\n this.idleVideo = null;\n }\n if (this.cachedVideo) {\n this.cachedVideo.pause();\n if (this.cachedVideo.parentNode) {\n this.cachedVideo.parentNode.removeChild(this.cachedVideo);\n }\n this.cachedVideo = null;\n }\n this.videoOutput.destroy();\n }\n}\n","import { SyncConfig } from \"../types/sync\";\n\nexport const DEFAULT_SYNC_CONFIG: SyncConfig = {\n tolerance: 40, // ms\n softCorrectionThreshold: 100, // ms\n hardCorrectionThreshold: 200, // ms\n historyLength: 50,\n maxCorrectionRate: 0.02, // 2% max playback rate adjustment\n correctionCoolDown: 500, // ms\n correctionFadeTime: 1000, // ms\n minAudioBuffer: 100, // ms\n maxAudioBuffer: 500, // ms\n minVideoBuffer: 3, // frames\n maxVideoBuffer: 10, // frames\n};\n","import { AudioOutput } from \"./modules/audio\";\nimport { AVController } from \"./modules/av\";\nimport { Connection } from \"./modules/connection\";\nimport { getApiBase } from \"./modules/environment\";\nimport { IdleVideo } from \"./modules/idle-video\";\nimport { LatencyMonitor } from \"./modules/monitor\";\nimport { SyncController } from \"./modules/sync\";\nimport { User } from \"./modules/user\";\nimport { VideoOutput } from \"./modules/video\";\nimport { AudioFormatConfig, ResultType, STATUS_TYPES } from \"./types/audio\";\nimport type {\n ConversationOptions,\n ConversationEvents,\n Status,\n DigitalHumanOptions,\n HeadOptions,\n VideoHtmlElement,\n Language,\n} from \"./types/Conversation\";\nimport { Environment } from \"./types/environment\";\nimport {\n DisconnectionDetails,\n AnyIncomingSocketEvent,\n EventType,\n IncomingSocketEvent,\n isJoinEvent,\n isTextEvent,\n Message,\n isResponseEvent,\n isStreamingEvent,\n isTimeoutWarningEvent,\n isTimeoutEvent,\n isKeepSessionEvent,\n isBinaryEvent,\n TextEventData,\n GlobalEventData,\n} from \"./types/event\";\nimport { HeadType } from \"./types/User\";\nimport { VideoFrameType, VideoTransitionType } from \"./types/vp8\";\nimport { base64ToArrayBuffer, detectAudioFormat } from \"./utils/audio\";\nimport { DEFAULT_SYNC_CONFIG } from \"./utils/sync\";\nexport * from \"./types/event\";\nexport * from \"./types/Conversation\";\nexport * from \"./types/connection\";\nexport * from \"./types/User\";\nexport * from \"./types/environment\";\nexport { VideoTransitionType } from \"./types/vp8\";\nexport type Options = ConversationOptions & ConversationEvents;\nexport type HeadConfigOptions = HeadOptions & Partial<DigitalHumanOptions>;\nexport type PartialOptions = HeadConfigOptions &\n VideoHtmlElement &\n Partial<ConversationEvents>;\nexport class Conversation {\n private status: Status = \"connecting\";\n protected volume: number = 1;\n private sessionStarted: boolean = false;\n\n public syncController: SyncController;\n public avController: AVController;\n private monitor: LatencyMonitor | null = null;\n\n private videoFrameQueue: VideoFrameType[] = [];\n\n private cachedResponseQueue: {\n video_url: string;\n textEventData: GlobalEventData & TextEventData;\n }[] = [];\n\n private static getFullOptions(partialOptions: PartialOptions): Options {\n return {\n username: \"anonymous\",\n password: \"Password1\",\n environment: \"production\",\n mode: \"default\",\n apiKey: \"\",\n frameRate: 30,\n streamType: \"jpg\",\n quality: \"high\",\n crop: false,\n showIdle: false,\n onStatusChange: () => {},\n onConnect: () => {},\n onDisconnect: () => {},\n onText: () => {},\n onJoin: () => {},\n onResponse: () => {},\n onStreaming: () => {},\n onMuteStatusChange: () => {},\n onSpeakingStart: () => {},\n onSpeakingEnd: () => {},\n onStoppingEnd: () => {},\n onTimeout: () => {},\n onTimeoutWarning: () => {},\n onKeepSession: () => {},\n onError: () => {},\n ...partialOptions,\n };\n }\n\n /**\n * Starts a digital human conversation.\n * @param options - The options for the conversation.\n * @returns A promise that resolves to a Conversation instance.\n */\n public static async startDigitalHuman(\n options: PartialOptions\n ): Promise<Conversation> {\n const fullOptions = Conversation.getFullOptions(options);\n const {\n username,\n password,\n environment,\n orgId,\n headId,\n mode,\n apiKey,\n allowWakeLock,\n frameRate,\n streamType,\n language,\n quality,\n crop,\n showIdle,\n fadeTransitionsType,\n onStatusChange,\n } = fullOptions;\n onStatusChange({ status: \"connecting\" });\n let user: User | null = null;\n let connection: Connection | null = null;\n let audioInputStream: MediaStream | null = null;\n let videoOutput: VideoOutput | null = null;\n let audioOutput: AudioOutput | null = null;\n const defaultAudioOutputFormat: AudioFormatConfig = {\n sampleRate: 16000,\n format: \"pcm\", // we'll convert from wav to pcm in event handler\n };\n\n //lets use wakelock to stop screen from locking or dimming when the Digital human is active.\n let wakeLock: WakeLockSentinel | null = null;\n if (allowWakeLock ?? true) {\n try {\n wakeLock = await navigator.wakeLock.request(\"screen\");\n } catch (e) {\n //swallow error\n }\n }\n const apiBase = getApiBase(environment);\n try {\n user = await User.loginUser(username, password, apiBase, orgId, headId);\n\n const idleVideo = await IdleVideo.getIdleVideo(apiBase, orgId, headId);\n //TODO: Implement a way to dynamically add a delay before connecting\n\n let headInfo = await user.getHeadDetails(apiKey);\n\n headInfo.avatarSrc = await idleVideo.getAvatarSrc(apiBase, orgId, headId);\n const languageToUse: Language =\n language || headInfo.language || navigator.language;\n headInfo.language = languageToUse;\n connection = await Connection.create({\n environment,\n orgId,\n headId,\n token: user.accessToken, // use direct token for first time. subsequent connections will use the getToken() incase the token is expired.\n mode,\n apiKey,\n language: languageToUse,\n streamType,\n crop,\n quality,\n });\n const videoelement = options.element;\n\n let microphoneAccess = false;\n try {\n audioInputStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n });\n\n audioInputStream.getTracks().forEach(track => {\n track.stop();\n });\n audioInputStream = null;\n microphoneAccess = true;\n } catch (error) {\n //swallow error\n microphoneAccess = false;\n }\n [audioOutput, videoOutput] = await Promise.all([\n AudioOutput.createAudioOutput(defaultAudioOutputFormat),\n VideoOutput.createVideoOutput(videoelement, {\n width: videoelement.getBoundingClientRect().width,\n height: videoelement.getBoundingClientRect().height,\n frameRate: frameRate ?? 30,\n backgroundColor: \"transparent\",\n format: \"jpeg\",\n idleVideo: {\n src: idleVideo.idleVideoSource,\n enabled: showIdle,\n },\n transition: fadeTransitionsType ?? VideoTransitionType.NONE,\n }),\n ]);\n return new Conversation(\n fullOptions,\n microphoneAccess,\n connection,\n idleVideo,\n wakeLock,\n user,\n audioOutput,\n videoOutput,\n headInfo\n );\n } catch (error) {\n onStatusChange({ status: \"disconnected\" });\n connection?.close();\n await audioOutput?.close();\n await videoOutput?.destroy();\n\n try {\n await wakeLock?.release();\n wakeLock = null;\n } catch (e) {}\n throw error;\n }\n }\n\n /**\n * This retrieves the background video to use for widget mode & welcome screen\n * @param options PartialOptions\n * @returns Video link to use as widget background\n */\n public static async getBackgroundVideo(options: {\n orgId: string;\n headId: string;\n environment: Environment;\n }) {\n const { environment, orgId, headId } = options;\n const apiBase = getApiBase(environment);\n const idleVideo = await IdleVideo.getIdleVideo(\n apiBase ?? \"https://chat-origin.api.unith.live\",\n orgId,\n headId\n );\n\n return idleVideo.idleVideoSource;\n }\n\n public constructor(\n public options: Options,\n public microphoneAccess: boolean,\n public connection: Connection,\n public idleVideo: IdleVideo,\n public wakeLock: WakeLockSentinel | null,\n public user: User,\n public audioOutput: AudioOutput,\n public videoOutput: VideoOutput,\n public headInfo: HeadType\n ) {\n //log npm version\n console.log(\"version: \", 1.3);\n this.syncController = new SyncController(DEFAULT_SYNC_CONFIG);\n this.avController = new AVController(\n this.syncController,\n this.audioOutput,\n this.videoOutput\n );\n this.options.onConnect({\n userId: connection.userId,\n headInfo,\n microphoneAccess,\n });\n this.connection.onDisconnect(this.endSessionWithDetails);\n this.connection.onMessage(this.onMessage);\n this.videoOutput.onSpeakingStart(this.options.onSpeakingStart);\n this.videoOutput.onSpeakingEnd(this.options.onSpeakingEnd);\n this.updateStatus(\"connected\");\n this.audioOutput.worklet.port.onmessage = this.onOutputWorkletMessage;\n this.handleIOSSilentMode();\n this.startLatencyMonitoring();\n }\n private startLatencyMonitoring() {\n this.monitor = new LatencyMonitor(this.connection, {\n onUpdate: latency => {\n console.log(\"Latency updated:\", latency);\n },\n });\n\n // this.monitor.start();\n }\n\n private handleIOSSilentMode() {\n if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {\n // Listen for audio context state changes\n this.audioOutput.context.addEventListener(\"statechange\", () => {\n if (this.audioOutput.context.state === \"suspended\") {\n console.warn(\n \"Audio context suspended - likely due to iOS silent mode\"\n );\n this.audioOutput.context.resume();\n }\n });\n }\n }\n\n private onOutputWorkletMessage = ({ data }: MessageEvent): void => {\n this.avController.handleAudioWorkletMessage(data);\n };\n\n private handleJoinEvent = async (\n event: IncomingSocketEvent<EventType.JOIN>\n ) => {\n if (event.granted) {\n this.options.onJoin(event);\n } else {\n console.warn(\"Join event not granted:\", event);\n }\n };\n private handleTextEvent = async (\n event: IncomingSocketEvent<EventType.TEXT>\n ) => {\n this.options.onText(event);\n };\n\n private handleResponseEvent = async (\n event: IncomingSocketEvent<EventType.RESPONSE>\n ) => {\n this.options.onResponse(event);\n };\n\n private handleStreamError = (\n event: IncomingSocketEvent<EventType.STREAMING>\n ) => {\n const errorType = event.error_type;\n\n // Scenario 1: error is undefined or not part of the errors we're handling\n // We'll always reload the widget in this case.\n if (\n !errorType ||\n ![\n \"resource_exhausted\",\n \"deadline_exceeded\",\n \"inactivity_timeout\",\n ].includes(errorType)\n ) {\n console.log(\"unknown or non handled error\");\n this.options.onError({\n message: \"A connection error occurred. Please try again.\",\n endConversation: true,\n type: \"toast\",\n });\n return;\n }\n\n if (errorType === \"resource_exhausted\") {\n console.log(\"resource exhausted error\");\n // There's 2 cases here:\n // 1. If this is the first time a user is asking a question, it means the BE can't handle this request,\n // we should show 'heavy traffic banner' on the FE. Then do a WS reconnection.\n // 2. If this is not the first time, i.e this happens unexpectedly during a conversation (n+1),\n // we can show 'heavy traffic message and tell the user to try resending the message.\n if (this.avController.isPlaying) {\n // Case 2\n this.options.onError({\n message:\n \"We are experiencing heavy traffic and we can't deliver the response, try again\",\n endConversation: false,\n type: \"toast\",\n });\n } else {\n this.options.onError({\n message:\n \"The system is experiencing heavy traffic. Please try again later.\",\n endConversation: true,\n type: \"toast\",\n });\n return;\n }\n } else if (errorType === \"deadline_exceeded\") {\n // We'll show banner 'heavy traffic', connection will remain open and user can try resending message.\n this.options.onError({\n message:\n \"We are experiencing heavy traffic and we can't deliver the response, try again\",\n endConversation: false,\n type: \"toast\",\n });\n } else if (errorType === \"inactivity_timeout\") {\n // Use the message string to identify inactivity timeout in the widget\n this.options.onTimeout();\n return;\n }\n };\n\n private handleStreamingEvent = async (\n event: IncomingSocketEvent<EventType.STREAMING>\n ) => {\n if (event.type === \"error\") {\n this.handleStreamError(event);\n return;\n }\n\n if (event.type === \"cache\") {\n const textEvent: IncomingSocketEvent<EventType.TEXT> = {\n id: event.session_id || Math.random().toString(36).substring(2),\n timestamp: new Date(),\n isSent: true,\n visible: true,\n event: EventType.TEXT,\n user_id: event.user_id,\n username: event.username,\n text: event.text ?? \"\",\n speaker: \"backend\",\n session_id: event.session_id || \"\",\n suggestions: [],\n };\n // if user hasn't interacted with DH, add cached video to queue\n if (!this.sessionStarted) {\n this.cachedResponseQueue.push({\n video_url: event.video_url || \"\",\n textEventData: textEvent,\n });\n } else {\n await this.videoOutput.playCachedVideo(event.video_url || \"\");\n this.handleTextEvent(textEvent);\n this.options.onStreaming(event);\n }\n return;\n }\n\n if (event.type === \"metadata\") {\n if (event.metadata_type === \"start\" || event.metadata_type === \"end\") {\n this.avController.isStoppingAV = false;\n // this.options.onStoppingEnd(); //why??\n this.videoOutput.toggleStream(event.metadata_type === \"start\");\n if (event.metadata_type === \"start\") {\n // Reset frame queue\n this.videoFrameQueue = [];\n this.syncController.resetTiming();\n }\n }\n }\n\n if (event.type === \"audio_frame\") {\n if (this.avController.isStoppingAV) return;\n this.handleAudioFrame(event);\n // this.syncController.checkSync();\n }\n\n if (event.type === \"video_frame\") {\n if (this.avController.isStoppingAV) return;\n this.handleVideoFrame(event);\n // this.syncController.checkSync();\n }\n\n this.avController.playAudioVideo();\n this.options.onStreaming(event);\n };\n\n private handleVideoFrame = async (\n event: IncomingSocketEvent<EventType.STREAMING>\n ) => {\n if (!event.frame_data) {\n // in binary mode, we expect this to be absent\n // console.warn(\"Video frame data is missing in the event:\", event);\n this.videoFrameQueue.push({\n timeStamp: event.event_timestamp_ms,\n isKeyframe: event.is_keyframe ?? false,\n });\n return;\n }\n };\n\n private handleBinaryData = async (\n event: IncomingSocketEvent<EventType.BINARY>\n ) => {\n if (this.videoFrameQueue.length > 0) {\n const frame = this.videoFrameQueue.shift()!;\n this.updateStatus(\"connected\");\n this.videoOutput.addFrame(event.data, frame.timeStamp, frame.isKeyframe);\n this.syncController.updateVideoTime(frame.timeStamp);\n }\n };\n\n private handleAudioFrame = async (\n event: IncomingSocketEvent<EventType.STREAMING>\n ) => {\n if (!event.frame_data) {\n console.warn(\"Audio frame data is missing in the event:\", event);\n return;\n }\n // const detection = detectAudioFormat(event.frame_data);\n\n const buffer = base64ToArrayBuffer(event.frame_data);\n let pcmData: Int16Array;\n\n pcmData = new Int16Array(buffer);\n\n this.audioOutput.gain.gain.value = this.volume;\n this.audioOutput.worklet.port.postMessage({ type: \"clearInterrupted\" });\n\n //todo: save timestamp for AV syncing...\n // console.log(\"Sending audio frame to worklet:\", pcmData);\n this.audioOutput.worklet.port.postMessage({\n type: \"buffer\",\n buffer: pcmData,\n time_stamp: event.event_timestamp_ms,\n });\n\n this.syncController.updateAudioTime(event.event_timestamp_ms);\n };\n\n private onMessage = async (event: AnyIncomingSocketEvent) => {\n switch (event.event) {\n case EventType.JOIN: {\n if (isJoinEvent(event)) {\n this.handleJoinEvent(event);\n }\n return;\n }\n case EventType.TEXT: {\n if (isTextEvent(event)) {\n const textEvent = event as IncomingSocketEvent<EventType.TEXT>;\n textEvent.timestamp = new Date();\n textEvent.isSent = true;\n textEvent.visible = true;\n this.handleTextEvent(textEvent);\n }\n return;\n }\n case EventType.RESPONSE: {\n if (isResponseEvent(event)) {\n const responseEvent =\n event as IncomingSocketEvent<EventType.RESPONSE>;\n responseEvent.timestamp = new Date();\n responseEvent.speaker = \"backend\";\n responseEvent.isSent = true;\n this.handleResponseEvent(responseEvent);\n }\n return;\n }\n case EventType.STREAMING: {\n // console.log(\"streaming event\");\n // if (isStreamingErrorEvent(event)) {\n // this.options.onError({\n // message:\n // \"We are currently at full capacity. Please try again later.\",\n // endConversation: true,\n // type: \"modal\",\n // });\n // return;\n // } else\n if (isStreamingEvent(event)) {\n this.handleStreamingEvent(event);\n }\n return;\n }\n\n case EventType.BINARY: {\n if (isBinaryEvent(event)) {\n this.handleBinaryData(event);\n }\n }\n case EventType.TIMEOUT_WARNING: {\n if (isTimeoutWarningEvent(event)) {\n this.options.onTimeoutWarning(event);\n }\n return;\n }\n case EventType.TIME_OUT: {\n if (isTimeoutEvent(event)) {\n this.options.onTimeout(event);\n }\n return;\n }\n case EventType.KEEP_SESSION: {\n if (isKeepSessionEvent(event)) {\n this.options.onKeepSession(event);\n }\n return;\n }\n default: {\n console.warn(\"Unhandled event type:\", event.event);\n return;\n }\n }\n };\n\n private endSessionWithDetails = async (details: DisconnectionDetails) => {\n if (this.status !== \"connected\" && this.status !== \"connecting\") return;\n this.sessionStarted = false;\n this.updateStatus(\"disconnecting\");\n await this.handleEndSession();\n this.updateStatus(\"disconnected\");\n this.monitor?.stop();\n this.options.onDisconnect(details);\n };\n\n public getUserId() {\n return this.connection.userId;\n }\n\n private async handleEndSession() {\n this.connection.close();\n }\n private updateStatus(status: Status) {\n // console.log(\"this.up\", this.options)\n if (status !== this.status) {\n this.status = status;\n this.options.onStatusChange({ status });\n }\n }\n\n /**\n * To stop current response, we'll do the following:\n * 1. set a flag that'll prevent adding new audio & video events to their respective queues\n * 2. clear video queue & switch state to idle\n * 3. clear audio queue\n * 4. send an event if all expected response from BE has been received. If not, FE will keep status in 'stopping' mode\n */\n public stopCurrentResponse() {\n this.avController.toggleStoppingVideo(true);\n this.videoOutput.clearFrame();\n this.audioOutput.worklet.port.postMessage({\n type: \"stopPlayback\",\n });\n if (!this.videoOutput.getStreamingStatus()) {\n this.options.onStoppingEnd();\n this.avController.toggleStoppingVideo(false);\n }\n }\n\n public async toggleMuteStatus() {\n this.volume = this.volume === 0 ? 1 : 0;\n this.audioOutput.toggleMute();\n this.videoOutput.toggleCacheVideoMute();\n\n this.options.onMuteStatusChange({ isMuted: this.volume === 0 });\n return this.volume;\n }\n\n public async startSession() {\n this.sessionStarted = true;\n if (this.audioOutput.context.state === \"suspended\") {\n await this.audioOutput.context.resume();\n }\n await this.avController.startPlayback(true);\n // check if video exists in queue and play it.\n if (this.cachedResponseQueue.length) {\n const lastResponse =\n this.cachedResponseQueue[this.cachedResponseQueue.length - 1];\n // add a small delay to allow AV playback to start\n await new Promise(resolve => setTimeout(resolve, 50));\n await this.videoOutput.playCachedVideo(lastResponse.video_url);\n this.handleTextEvent(lastResponse.textEventData);\n }\n return this.connection;\n }\n\n public async initializeMicrophone(\n onSpeechResult: (result: ResultType) => void,\n onError: (e: string) => void,\n onStatusChange: (status: STATUS_TYPES) => void\n ) {\n return await this.user.getAsrToken();\n }\n\n public endSession() {\n return this.endSessionWithDetails({ reason: \"user\" });\n }\n\n public sendMessage(message: Message) {\n if (!this.connection) {\n throw new Error(\"Connection not established\");\n }\n message.session_id = `${message.user_id}::${this.user.orgId}::${this.user.headId}::${this.user.sessionId\n .toString()\n .padStart(5, \"0\")}`;\n return this.connection.sendMessage(message);\n }\n\n public keepSession(message: Message) {\n if (!this.connection) {\n throw new Error(\"Connection not established\");\n }\n message.session_id = `${this.user.id}::${this.user.orgId}::${this.user.headId}::${this.user.sessionId\n .toString()\n .padStart(5, \"0\")}`;\n return this.connection.sendMessage(message);\n }\n}\n"],"names":["name","sourceCode","URLCache","Map","loadAudioConcatProcessor","worklet","_exit","_temp2","_result","_catch","moduleURL","base64","btoa","Promise","resolve","addModule","then","set","Error","url","get","blob","Blob","type","blobURL","URL","createObjectURL","_temp","revokeObjectURL","e","reject","EventType","StreamingEventType","AudioOutput","context","analyser","gain","this","_isMuted","createAudioOutput","_ref","sampleRate","format","audioContext","audioAnalyser","AudioContext","createAnalyser","audioGain","createGain","connect","destination","audioWorklet","AudioWorkletNode","port","postMessage","audioOutput","ensureIOSCompatibility","error","_audioContext","close","_proto","prototype","_this","test","navigator","userAgent","buffer","createBuffer","source","createBufferSource","start","getOutputDevice","_this2","sinkId","mediaDevices","enumerateDevices","devices","audioOutputs","filter","device","kind","find","d","deviceId","console","getAvailableOutputDevices","mute","value","unmute","toggleMute","AVController","syncConfig","videoOutput","syncController","initialized","isPlaying","isPlayingAudio","isPlayingVideo","isStoppingAV","handleAudioWorkletMessage","event","setEventCallbacks","onIdleVideoShown","onIdleVideoHidden","stopBufferMonitoring","updatePlayingState","state","toggleStoppingVideo","stopping","startPlayback","init","resume","startStreaming","playAudioVideo","_this3","videoBufferLength","getBufferLength","isJoinEvent","JOIN","isTextEvent","TEXT","isResponseEvent","RESPONSE","isStreamingEvent","STREAMING","isStreamingErrorEvent","ERROR","isBinaryEvent","BINARY","isPongEvent","PONG","isTimeoutWarningEvent","TIMEOUT_WARNING","isTimeoutEvent","TIME_OUT","isKeepSessionEvent","KEEP_SESSION","getApiBase","environment","isValidSocketEvent","Connection","socket","userId","queue","disconnectionDetails","onDisconnectCallback","onMessageCallback","onPingPongCallback","addEventListener","disconnect","reason","message","parsedEvent","data","ArrayBuffer","user_id","username","JSON","parse","push","_","create","config","streamBase","getStreamBase","WebSocket","orgId","headId","send","stringify","token","api_key","apiKey","text_only","mode","streamType","quality","crop","once","granted","onJoin","log","_socket","details","_this$onDisconnectCal","call","sendMessage","sendPingEvent","warn","onPingPong","callback","onMessage","length","queueMicrotask","forEach","onDisconnect","IdleVideo","idleVideoSource","videoId","getIdleVideo","apiUrl","getIdleVideoId","fetch","response","status","json","idleVideoString","text","errorMessage","statusText","getAvatarSrc","avatarSrc","videos","id","LatencyMonitor","connection","options","intervalId","pingInterval","timeout","lastPingTimestamp","history","maxHistory","onUpdate","handleMessage","bind","setInterval","sendPing","stop","clearInterval","destroy","ts","performance","now","toString","PING","timestamp","rtt","parseFloat","recordLatency","shift","average","reduce","a","b","classifyLatency","ms","SyncController","driftHistory","correctionInProgress","lastAudioTiming","lastVideoTiming","updateAudioTime","relativeTime","updateVideoTime","resetTiming","checkSync","drift","Math","abs","recordDrift","tolerance","Date","audioTime","videoTime","historyLength","storage","window","localStorage","location","origin","pathname","getFromStorage","key","getItem","setToStorage","val","setItem","VideoState","VideoTransitionType","User","accessToken","tokenType","password","apiBase","EXPIRATION_OFFSET","sessionId","existingSession","parseInt","loginUser","formData","FormData","append","method","body","user","access_token","token_type","status_code","_error$response","_error$response2","detail","getHeadDetails","_error$response3","_error$response4","getAuthToken","_response$json","_error$response5","_error$response6","getApiAsrToken","auth_jwt","_temp3","region","_result2","_exit2","headers","Authorization","_error$response7","_error$response8","getAccessToken","_temp5","_result3","token_data","atob","split","_this4","session_id","will_expire","exp","expiration_time","getTime","_temp4","getAsrToken","_this5","_exit4","_temp7","_result4","_temp6","_token$region","Vp8VideoOutput","canvas","ctx","container","resizeObserver","decoder","INITIALIZING","isProcessingFrame","isStreaming","startTime","frameBuffer","currentSequenceId","animationFrameId","renderLoop","fpsCounter","count","lastTime","handleContextLoss","handleContextRestore","handleResize","containerRect","getBoundingClientRect","containerWidth","width","containerHeight","height","style","imageSmoothingEnabled","_extends","maxBufferSize","enableAdaptiveQuality","setupContextLossHandling","setupResizeHandling","initializeDecoder","_config$backgroundCol","document","createElement","position","top","left","backgroundColor","maxWidth","zIndex","pointerEvents","objectFit","imageRendering","getContext","alpha","desynchronized","appendChild","VideoDecoder","output","frame","renderVideoFrame","configure","codec","codedWidth","codedHeight","ResizeObserver","entries","_step","_iterator","_createForOfIteratorHelperLoose","done","target","observe","getStreamingStatus","addFrame","frameData","isKeyframe","DESTROYED","sequenceId","size","byteLength","oldestNonKeyframeIndex","findIndex","f","splice","toggleStream","clearFrame","cancelAnimationFrame","flush","clearCanvas","READY","interrupt","fadeOut","_this6","INTERRUPTED","fadeOutCanvas","removeEventListener","parentNode","removeChild","getState","startRenderingStreamingVideo","render","_this$frameBuffer$","_this7","nextFrameTimeStamp","interval","processNextFrame","requestAnimationFrame","intervalTimestamp","excessDrift","_this8","decodeVP8Frame","_finallyRethrows","_wasThrown","chunk","EncodedVideoChunk","decode","videoFrame","clearRect","drawWidth","drawHeight","offsetX","offsetY","videoAspect","displayWidth","displayHeight","drawImage","_this0","opacity","fadeStep","_this1","preventDefault","PAUSED","PLAYING","VideoOutput","transitionsConfig","effectsConfig","idleVideo","cachedVideo","idleVideoConfig","videoTransitionConfig","CROSSFADE_DURATION","isTransitioning","isShowingIdleVideo","bufferCheckAnimationId","lastBufferCheckTime","sessionStarted","isShowingCachedVideo","onSpeakingStartCallback","onSpeakingEndCallback","createVideoOutput","_Vp8VideoOutput$creat","videoOutputInstance","transition","effects","setupIdleVideo","setupCachedVideo","videoConfig","src","muted","loop","controls","playsInline","preload","getComputedStyle","load","play","onSpeakingStart","speakingStartCallback","onSpeakingEnd","speakingEndCallback","startBufferMonitoring","checkBuffer","hideIdleVideoBeforeStream","showIdleVideoAfterStream","toggleCacheVideoMute","playCachedVideo","cachedVideoUrl","crossfadeFromIdleToCached","crossfadeFromCachedToIdle","cleanupCachedVideo","showIdleVideo","setTimeout","paused","pause","currentTime","hideIdleVideo","_this$onSpeakingStart","_this$onIdleVideoHidd","_this$onSpeakingStart2","_this$onIdleVideoHidd2","callbacks","_this9","timeStamp","_temp9","_frameData$arrayBuffe","uint8Array","_temp8","Uint8Array","arrayBuffer","DEFAULT_SYNC_CONFIG","softCorrectionThreshold","hardCorrectionThreshold","maxCorrectionRate","correctionCoolDown","correctionFadeTime","minAudioBuffer","maxAudioBuffer","minVideoBuffer","maxVideoBuffer","Conversation","microphoneAccess","wakeLock","headInfo","volume","avController","monitor","videoFrameQueue","cachedResponseQueue","onOutputWorkletMessage","handleJoinEvent","handleTextEvent","onText","handleResponseEvent","onResponse","handleStreamError","errorType","error_type","includes","onError","endConversation","onTimeout","handleStreamingEvent","metadata_type","handleAudioFrame","handleVideoFrame","onStreaming","_event$text","textEvent","random","substring","isSent","visible","speaker","suggestions","video_url","textEventData","_event$is_keyframe","frame_data","event_timestamp_ms","is_keyframe","handleBinaryData","updateStatus","pcmData","binaryString","len","bytes","i","charCodeAt","base64ToArrayBuffer","Int16Array","time_stamp","responseEvent","onTimeoutWarning","onKeepSession","endSessionWithDetails","handleEndSession","_this0$monitor","onConnect","onmessage","handleIOSSilentMode","startLatencyMonitoring","getFullOptions","partialOptions","frameRate","showIdle","onStatusChange","onMuteStatusChange","onStoppingEnd","startDigitalHuman","_temp1","_User$loginUser","_idleVideo$getAvatarS","languageToUse","language","_Connection$create","all","defaultAudioOutputFormat","videoelement","enabled","fadeTransitionsType","NONE","_Promise$all","fullOptions","element","getUserMedia","audio","_navigator$mediaDevic","getTracks","track","_connection","_audioOutput","_videoOutput","_wakeLock","release","allowWakeLock","_temp0","request","_navigator$wakeLock$r","getBackgroundVideo","latency","getUserId","stopCurrentResponse","toggleMuteStatus","_this11","isMuted","startSession","_temp13","_this12","_temp11","_temp10","lastResponse","_temp12","initializeMicrophone","onSpeechResult","endSession","padStart","keepSession","CONVERSATION_END"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { AudioFormatConfig } from "../types/audio";
2
+ export declare class AudioOutput {
3
+ readonly context: AudioContext;
4
+ readonly analyser: AnalyserNode;
5
+ readonly gain: GainNode;
6
+ readonly worklet: AudioWorkletNode;
7
+ private _isMuted;
8
+ static createAudioOutput({ sampleRate, format, }: AudioFormatConfig): Promise<AudioOutput>;
9
+ private ensureIOSCompatibility;
10
+ private constructor();
11
+ getOutputDevice(): Promise<MediaDeviceInfo | null>;
12
+ getAvailableOutputDevices(): Promise<MediaDeviceInfo[]>;
13
+ mute(): void;
14
+ unmute(): void;
15
+ toggleMute(): boolean;
16
+ close(): Promise<void>;
17
+ }
@@ -0,0 +1,22 @@
1
+ import { AudioOutput } from "./audio";
2
+ import { SyncController } from "./sync";
3
+ import { VideoOutput } from "./video";
4
+ export declare class AVController {
5
+ private syncController;
6
+ private audioOutput;
7
+ private videoOutput;
8
+ initialized: boolean;
9
+ isPlaying: boolean;
10
+ isPlayingAudio: boolean;
11
+ isPlayingVideo: boolean;
12
+ isStoppingAV: boolean;
13
+ constructor(syncConfig: SyncController, audioOutput: AudioOutput, videoOutput: VideoOutput);
14
+ updatePlayingState(state: boolean): void;
15
+ toggleStoppingVideo(stopping: boolean): void;
16
+ startPlayback(init?: boolean): Promise<void>;
17
+ handleAudioWorkletMessage: (event: {
18
+ type: string;
19
+ finished: boolean;
20
+ }) => void;
21
+ playAudioVideo(): Promise<void>;
22
+ }
@@ -0,0 +1,20 @@
1
+ import { WebsocketConnectionConfig } from "../types/connection";
2
+ import { OnDisconnectCallback, OnMessageCallback, Message, OnPingPongCallback, PingEventData } from "../types/event";
3
+ export declare class Connection {
4
+ readonly socket: WebSocket;
5
+ readonly userId: string;
6
+ static create(config: WebsocketConnectionConfig): Promise<Connection>;
7
+ private queue;
8
+ private disconnectionDetails;
9
+ private onDisconnectCallback;
10
+ private onMessageCallback;
11
+ private onPingPongCallback;
12
+ private constructor();
13
+ private disconnect;
14
+ close(): void;
15
+ sendMessage(message: Message): void;
16
+ sendPingEvent(message: PingEventData): void;
17
+ onPingPong(callback: OnPingPongCallback): void;
18
+ onMessage(callback: OnMessageCallback): void;
19
+ onDisconnect(callback: OnDisconnectCallback): void;
20
+ }
@@ -0,0 +1,3 @@
1
+ import { Environment } from "../types/environment";
2
+ export declare function getApiBase(environment: Environment): string;
3
+ export declare function getStreamBase(environment: Environment): string;
@@ -0,0 +1,2 @@
1
+ import { AnyIncomingSocketEvent } from "../types/event";
2
+ export declare function isValidSocketEvent(event: any): event is AnyIncomingSocketEvent;
@@ -0,0 +1,8 @@
1
+ export declare class IdleVideo {
2
+ readonly idleVideoSource: string;
3
+ readonly videoId: string;
4
+ constructor(idleVideoSource: string, videoId: string);
5
+ static getIdleVideo(apiUrl: string, orgId: string, headId: string): Promise<IdleVideo>;
6
+ getAvatarSrc(apiUrl: string, orgId: string, headId: string): Promise<string>;
7
+ private static getIdleVideoId;
8
+ }
@@ -0,0 +1,32 @@
1
+ import { Connection } from "./connection";
2
+ type LatencyStatus = "good" | "moderate" | "poor";
3
+ interface LatencyStats {
4
+ rtt: number;
5
+ average: number;
6
+ status: LatencyStatus;
7
+ }
8
+ type OnUpdateCallback = (stats: LatencyStats) => void;
9
+ export declare class LatencyMonitor {
10
+ connection: Connection;
11
+ private intervalId;
12
+ private pingInterval;
13
+ private timeout;
14
+ private lastPingTimestamp;
15
+ private history;
16
+ private maxHistory;
17
+ private onUpdate?;
18
+ constructor(connection: Connection, options?: {
19
+ pingInterval?: number;
20
+ timeout?: number;
21
+ maxHistory?: number;
22
+ onUpdate?: OnUpdateCallback;
23
+ });
24
+ start(): void;
25
+ stop(): void;
26
+ destroy(): void;
27
+ private sendPing;
28
+ private handleMessage;
29
+ private recordLatency;
30
+ private classifyLatency;
31
+ }
32
+ export {};
@@ -0,0 +1,14 @@
1
+ import { SyncConfig } from "../types/sync";
2
+ export declare class SyncController {
3
+ private config;
4
+ private driftHistory;
5
+ private correctionInProgress;
6
+ private lastAudioTiming;
7
+ private lastVideoTiming;
8
+ constructor(config: SyncConfig);
9
+ updateAudioTime(relativeTime: number): void;
10
+ updateVideoTime(relativeTime: number): void;
11
+ resetTiming(): void;
12
+ checkSync(): Promise<void>;
13
+ private recordDrift;
14
+ }
@@ -0,0 +1,27 @@
1
+ import { ApiAsrTokenType, HeadType, TokenResponseType } from "../types/User";
2
+ export declare class User {
3
+ readonly id: string;
4
+ username: string;
5
+ readonly password: string;
6
+ readonly orgId: string;
7
+ readonly headId: string;
8
+ readonly apiBase: string;
9
+ static loginUser(username: string, password: string, apiUrl: string, orgId: string, headId: string): Promise<User>;
10
+ private EXPIRATION_OFFSET;
11
+ accessToken: string;
12
+ tokenType: string;
13
+ sessionId: number;
14
+ private constructor();
15
+ getHeadDetails(apiKey: string): Promise<HeadType>;
16
+ getAuthToken(username: string, password: string): Promise<TokenResponseType>;
17
+ getApiAsrToken(auth_jwt: string): Promise<ApiAsrTokenType>;
18
+ getAccessToken(): Promise<{
19
+ access_token: string;
20
+ user_id: string | null | undefined;
21
+ session_id: number;
22
+ }>;
23
+ getAsrToken(): Promise<{
24
+ token: string | null | undefined;
25
+ region: string | null | undefined;
26
+ }>;
27
+ }