@roboflow/inference-sdk 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +264 -243
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inference-api.d.ts +14 -0
- package/dist/inference-api.d.ts.map +1 -1
- package/dist/webrtc.d.ts +50 -0
- package/dist/webrtc.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/inference-api.ts","../src/streams.ts","../src/video-upload.ts","../src/webrtc.ts"],"sourcesContent":["/**\n * Base URL for the Roboflow API (used for TURN server configuration)\n * Can be overridden via environment variable in Node.js environments\n */\nconst RF_API_BASE_URL = typeof process !== \"undefined\" && process.env?.RF_API_BASE_URL\n ? process.env.RF_API_BASE_URL\n : \"https://api.roboflow.com\";\n\n/**\n * List of known Roboflow serverless API URLs where auto TURN config applies\n */\nconst ROBOFLOW_SERVERLESS_URLS = [\n \"https://serverless.roboflow.com\"\n];\n\nexport interface WebRTCWorkerConfig {\n imageInputName?: string;\n streamOutputNames?: string[];\n dataOutputNames?: string[];\n threadPoolWorkers?: number;\n /**\n * Workflow parameters to pass to the workflow execution\n */\n workflowsParameters?: Record<string, any>;\n /**\n * ICE servers for WebRTC connections (used for both client and server)\n */\n iceServers?: RTCIceServerConfig[];\n /**\n * Processing timeout in seconds (serverless only)\n * @default 600\n */\n processingTimeout?: number;\n /**\n * Requested compute plan (serverless only)\n * @example \"webrtc-gpu-small\"\n */\n requestedPlan?: string;\n /**\n * Requested region for processing (serverless only)\n * @example \"us\"\n */\n requestedRegion?: string;\n /**\n * Set to false for file upload mode (batch processing).\n * When false, server processes all frames sequentially instead of dropping frames.\n * @default true\n */\n realtimeProcessing?: boolean;\n}\n\n/**\n * ICE server configuration for WebRTC connections\n *\n * Use this to configure custom STUN/TURN servers for users behind\n * symmetric NAT or restrictive firewalls.\n */\nexport interface RTCIceServerConfig {\n urls: string[];\n username?: string;\n credential?: string;\n}\n\nexport interface WebRTCOffer {\n sdp: string;\n type: string;\n}\n\nexport type WorkflowSpec = Record<string, any>;\n\nexport interface WebRTCWorkerResponse {\n status?: string;\n sdp: string;\n type: string;\n context?: {\n request_id: string | null;\n pipeline_id: string | null;\n };\n}\n\nexport interface WebRTCParams {\n workflowSpec?: WorkflowSpec;\n workspaceName?: string;\n workflowId?: string;\n imageInputName?: string;\n streamOutputNames?: string[];\n dataOutputNames?: string[];\n threadPoolWorkers?: number;\n /**\n * Workflow parameters to pass to the workflow execution\n */\n workflowsParameters?: Record<string, any>;\n /**\n * ICE servers for WebRTC connections (used for both client and server)\n *\n * Use this to specify custom STUN/TURN servers for users behind\n * symmetric NAT or restrictive firewalls. The same configuration is\n * used for both the client-side RTCPeerConnection and sent to the\n * server via webrtc_config.\n *\n * @example\n * ```typescript\n * iceServers: [\n * { urls: [\"stun:stun.l.google.com:19302\"] },\n * { urls: [\"turn:turn.example.com:3478\"], username: \"user\", credential: \"pass\" }\n * ]\n * ```\n */\n iceServers?: RTCIceServerConfig[];\n /**\n * Processing timeout in seconds (serverless only)\n * @default 600\n */\n processingTimeout?: number;\n /**\n * Requested compute plan (serverless only)\n * @example \"webrtc-gpu-small\"\n */\n requestedPlan?: string;\n /**\n * Requested region for processing (serverless only)\n * @example \"us\"\n */\n requestedRegion?: string;\n /**\n * Set to false for file upload mode (batch processing).\n * When false, server processes all frames sequentially instead of dropping frames.\n * @default true\n */\n realtimeProcessing?: boolean;\n}\n\nexport interface Connector {\n connectWrtc(offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse>;\n /**\n * Fetch ICE servers (TURN configuration) for WebRTC connections\n * This should be called BEFORE creating the RTCPeerConnection to ensure\n * proper NAT traversal configuration.\n *\n * @returns Promise resolving to ICE server configuration, or null/undefined if not available\n */\n getIceServers?(): Promise<RTCIceServerConfig[] | null>;\n _apiKey?: string;\n _serverUrl?: string;\n}\n\nexport class InferenceHTTPClient {\n private apiKey: string;\n private serverUrl: string;\n\n /**\n * @private\n * Use InferenceHTTPClient.init() instead\n */\n private constructor(apiKey: string, serverUrl: string = \"https://serverless.roboflow.com\") {\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n }\n\n static init({ apiKey, serverUrl }: { apiKey: string; serverUrl?: string }): InferenceHTTPClient {\n if (!apiKey) {\n throw new Error(\"apiKey is required\");\n }\n return new InferenceHTTPClient(apiKey, serverUrl);\n }\n\n /**\n * Initialize a WebRTC worker pipeline\n *\n * @param params - Pipeline parameters\n * @param params.offer - WebRTC offer { sdp, type }\n * @param params.workflowSpec - Workflow specification\n * @param params.config - Additional configuration\n * @param params.config.imageInputName - Input image name (default: \"image\")\n * @param params.config.streamOutputNames - Output stream names for video (default: [])\n * @param params.config.dataOutputNames - Output data names (default: [\"string\"])\n * @param params.config.threadPoolWorkers - Thread pool workers (default: 4)\n * @returns Promise resolving to answer with SDP and pipeline ID\n *\n * @example\n * ```typescript\n * const answer = await client.initializeWebrtcWorker({\n * offer: { sdp, type },\n * workflowSpec: { ... },\n * config: {\n * imageInputName: \"image\",\n * streamOutputNames: [\"output_image\"]\n * }\n * });\n * ```\n */\n async initializeWebrtcWorker({\n offer,\n workflowSpec,\n workspaceName,\n workflowId,\n config = {}\n }: {\n offer: WebRTCOffer;\n workflowSpec?: WorkflowSpec;\n workspaceName?: string;\n workflowId?: string;\n config?: WebRTCWorkerConfig;\n }): Promise<WebRTCWorkerResponse> {\n if (!offer || !offer.sdp || !offer.type) {\n throw new Error(\"offer with sdp and type is required\");\n }\n\n // Validate that either workflowSpec OR (workspaceName + workflowId) is provided\n const hasWorkflowSpec = !!workflowSpec;\n const hasWorkspaceIdentifier = !!(workspaceName && workflowId);\n\n if (!hasWorkflowSpec && !hasWorkspaceIdentifier) {\n throw new Error(\"Either workflowSpec OR (workspaceName + workflowId) is required\");\n }\n if (hasWorkflowSpec && hasWorkspaceIdentifier) {\n throw new Error(\"Provide either workflowSpec OR (workspaceName + workflowId), not both\");\n }\n\n const {\n imageInputName = \"image\",\n streamOutputNames = [],\n dataOutputNames = [\"string\"],\n threadPoolWorkers = 4,\n workflowsParameters = {},\n iceServers,\n processingTimeout,\n requestedPlan,\n requestedRegion,\n realtimeProcessing = true\n } = config as any;\n\n // Build workflow_configuration based on what's provided\n const workflowConfiguration: any = {\n type: \"WorkflowConfiguration\",\n image_input_name: imageInputName,\n workflows_parameters: workflowsParameters,\n workflows_thread_pool_workers: threadPoolWorkers,\n cancel_thread_pool_tasks_on_exit: true,\n video_metadata_input_name: \"video_metadata\"\n };\n\n if (hasWorkflowSpec) {\n workflowConfiguration.workflow_specification = workflowSpec;\n } else {\n workflowConfiguration.workspace_name = workspaceName;\n workflowConfiguration.workflow_id = workflowId;\n }\n\n const payload: Record<string, any> = {\n workflow_configuration: workflowConfiguration,\n api_key: this.apiKey,\n webrtc_realtime_processing: realtimeProcessing,\n webrtc_offer: {\n sdp: offer.sdp,\n type: offer.type\n },\n webrtc_config: iceServers ? { iceServers } : null,\n stream_output: streamOutputNames,\n data_output: dataOutputNames\n };\n\n // Add serverless-specific fields if provided\n if (processingTimeout !== undefined) {\n payload.processing_timeout = processingTimeout;\n }\n if (requestedPlan !== undefined) {\n payload.requested_plan = requestedPlan;\n }\n if (requestedRegion !== undefined) {\n payload.requested_region = requestedRegion;\n }\n const response = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n throw new Error(`initialise_webrtc_worker failed (${response.status}): ${errorText}`);\n }\n\n const result = await response.json();\n\n return result;\n }\n\n async terminatePipeline({ pipelineId }: { pipelineId: string }): Promise<void> {\n if (!pipelineId) {\n throw new Error(\"pipelineId is required\");\n }\n\n await fetch(\n `${this.serverUrl}/inference_pipelines/${pipelineId}/terminate?api_key=${this.apiKey}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" }\n }\n );\n }\n\n /**\n * Fetch TURN server configuration from Roboflow API\n *\n * This automatically fetches TURN server credentials for improved WebRTC\n * connectivity through firewalls and NAT. Only applicable when using\n * Roboflow serverless infrastructure.\n *\n * @returns Promise resolving to ICE server configuration, or null if not applicable\n *\n * @example\n * ```typescript\n * const client = InferenceHTTPClient.init({ apiKey: \"your-api-key\" });\n * const iceServers = await client.fetchTurnConfig();\n * // Returns: [{ urls: [\"turn:...\"], username: \"...\", credential: \"...\" }]\n * ```\n */\n async fetchTurnConfig(): Promise<RTCIceServerConfig[] | null> {\n // // Only fetch TURN config for Roboflow serverless URLs\n if (!ROBOFLOW_SERVERLESS_URLS.includes(this.serverUrl)) {\n return null;\n }\n try {\n const response = await fetch(\n `${RF_API_BASE_URL}/webrtc_turn_config?api_key=${this.apiKey}`,\n {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n }\n );\n\n if (!response.ok) {\n console.warn(`[RFWebRTC] Failed to fetch TURN config (${response.status}), using defaults`);\n return null;\n }\n\n const turnConfig = await response.json();\n\n // Handle 3 formats:\n // 1. Single server object: { urls, username, credential }\n // 2. Array of servers: [{ urls, username, credential }, ...]\n // 3. Object with iceServers: { iceServers: [...] }\n let iceServersRaw: any[];\n\n if (Array.isArray(turnConfig)) {\n // Format 2: array of servers\n iceServersRaw = turnConfig;\n } else if (turnConfig.iceServers && Array.isArray(turnConfig.iceServers)) {\n // Format 3: object with iceServers array\n iceServersRaw = turnConfig.iceServers;\n } else if (turnConfig.urls) {\n // Format 1: single server object - wrap in array\n iceServersRaw = [turnConfig];\n } else {\n console.warn(\"[RFWebRTC] Invalid TURN config format, using defaults\");\n return null;\n }\n\n // Normalize the ICE servers format\n const iceServers: RTCIceServerConfig[] = iceServersRaw.map((server: any) => ({\n urls: Array.isArray(server.urls) ? server.urls : [server.urls],\n username: server.username,\n credential: server.credential\n }));\n\n return iceServers;\n } catch (err) {\n console.warn(\"[RFWebRTC] Error fetching TURN config:\", err);\n return null;\n }\n }\n}\n\n/**\n * Connectors for establishing WebRTC connections to Roboflow\n */\nexport const connectors = {\n /**\n * Create a connector that uses API key directly\n *\n * **WARNING**: If you use this in the frontend, it will expose your API key. \n * Use only for demos/testing.\n * For production, use withProxyUrl() with a backend proxy.\n *\n * @param apiKey - Roboflow API key\n * @param options - Additional options\n * @param options.serverUrl - Custom Roboflow server URL\n * @returns Connector with connectWrtc method\n *\n * @example\n * ```typescript\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const answer = await connector.connectWrtc(offer, wrtcParams);\n * ```\n */\n withApiKey(apiKey: string, options: { serverUrl?: string } = {}): Connector {\n const { serverUrl } = options;\n\n // Warn if running in browser context\n if (typeof window !== 'undefined') {\n console.warn(\n '[Security Warning] Using API key directly in browser will expose it. ' +\n 'Use connectors.withProxyUrl() for production. ' +\n 'See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key'\n );\n }\n\n const client = InferenceHTTPClient.init({ apiKey, serverUrl });\n\n return {\n connectWrtc: async (offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse> => {\n console.debug(\"wrtcParams\", wrtcParams);\n const answer = await client.initializeWebrtcWorker({\n offer,\n workflowSpec: wrtcParams.workflowSpec,\n workspaceName: wrtcParams.workspaceName,\n workflowId: wrtcParams.workflowId,\n config: {\n imageInputName: wrtcParams.imageInputName,\n streamOutputNames: wrtcParams.streamOutputNames,\n dataOutputNames: wrtcParams.dataOutputNames,\n threadPoolWorkers: wrtcParams.threadPoolWorkers,\n workflowsParameters: wrtcParams.workflowsParameters,\n iceServers: wrtcParams.iceServers,\n processingTimeout: wrtcParams.processingTimeout,\n requestedPlan: wrtcParams.requestedPlan,\n requestedRegion: wrtcParams.requestedRegion,\n realtimeProcessing: wrtcParams.realtimeProcessing\n }\n });\n\n return answer;\n },\n\n /**\n * Fetch TURN server configuration for improved WebRTC connectivity\n */\n getIceServers: async (): Promise<RTCIceServerConfig[] | null> => {\n return await client.fetchTurnConfig();\n },\n\n // Store apiKey for cleanup\n _apiKey: apiKey,\n _serverUrl: serverUrl\n };\n },\n\n /**\n * Create a connector that uses a backend proxy (recommended for production)\n *\n * Your backend receives the offer and wrtcParams, adds the secret API key,\n * and forwards to Roboflow. This keeps your API key secure.\n *\n * For improved WebRTC connectivity through firewalls, implement a separate\n * endpoint for TURN server configuration that calls `fetchTurnConfig()`.\n *\n * @param proxyUrl - Backend proxy endpoint URL for WebRTC initialization\n * @param options - Additional options\n * @param options.turnConfigUrl - Optional URL for fetching TURN server configuration\n * @returns Connector with connectWrtc and optional getIceServers methods\n *\n * @example\n * ```typescript\n * // Frontend: Create connector with TURN config endpoint\n * const connector = connectors.withProxyUrl('/api/init-webrtc', {\n * turnConfigUrl: '/api/turn-config'\n * });\n * ```\n *\n * @example\n * Backend implementation (Express) with TURN server support:\n * ```typescript\n * // Endpoint for TURN configuration (called first by SDK)\n * app.get('/api/turn-config', async (req, res) => {\n * const client = InferenceHTTPClient.init({\n * apiKey: process.env.ROBOFLOW_API_KEY\n * });\n * const iceServers = await client.fetchTurnConfig();\n * res.json({ iceServers });\n * });\n *\n * // Endpoint for WebRTC initialization\n * app.post('/api/init-webrtc', async (req, res) => {\n * const { offer, wrtcParams } = req.body;\n * const client = InferenceHTTPClient.init({\n * apiKey: process.env.ROBOFLOW_API_KEY\n * });\n *\n * const answer = await client.initializeWebrtcWorker({\n * offer,\n * workflowSpec: wrtcParams.workflowSpec,\n * workspaceName: wrtcParams.workspaceName,\n * workflowId: wrtcParams.workflowId,\n * config: {\n * imageInputName: wrtcParams.imageInputName,\n * streamOutputNames: wrtcParams.streamOutputNames,\n * dataOutputNames: wrtcParams.dataOutputNames,\n * threadPoolWorkers: wrtcParams.threadPoolWorkers,\n * workflowsParameters: wrtcParams.workflowsParameters,\n * iceServers: wrtcParams.iceServers,\n * processingTimeout: wrtcParams.processingTimeout,\n * requestedPlan: wrtcParams.requestedPlan,\n * requestedRegion: wrtcParams.requestedRegion\n * }\n * });\n *\n * res.json(answer);\n * });\n * ```\n */\n withProxyUrl(proxyUrl: string, options: { turnConfigUrl?: string } = {}): Connector {\n const { turnConfigUrl } = options;\n\n return {\n connectWrtc: async (offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse> => {\n const response = await fetch(proxyUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n offer,\n wrtcParams\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n throw new Error(`Proxy request failed (${response.status}): ${errorText}`);\n }\n\n return await response.json();\n },\n\n /**\n * Fetch TURN server configuration from the proxy backend\n * Only available if turnConfigUrl was provided\n */\n getIceServers: turnConfigUrl\n ? async (): Promise<RTCIceServerConfig[] | null> => {\n try {\n const response = await fetch(turnConfigUrl, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n\n if (!response.ok) {\n console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${response.status})`);\n return null;\n }\n\n const data = await response.json();\n return data.iceServers || null;\n } catch (err) {\n console.warn(\"[RFWebRTC] Error fetching TURN config from proxy:\", err);\n return null;\n }\n }\n : undefined\n };\n }\n};\n","/**\n * Get a camera stream with the given constraints.\n *\n * @param constraints - MediaStreamConstraints for getUserMedia\n * @returns Promise that resolves to MediaStream\n *\n * @example\n * ```typescript\n * const stream = await useCamera({\n * video: {\n * facingMode: { ideal: \"user\" },\n * width: { ideal: 1280 },\n * height: { ideal: 720 },\n * frameRate: { ideal: 30 }\n * },\n * audio: false\n * });\n * ```\n */\nexport async function useCamera(constraints: MediaStreamConstraints = { video: true }): Promise<MediaStream> {\n try {\n console.log(\"[RFStreams] requesting with\", constraints);\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n console.log(\"[RFStreams] got stream\", stream.getVideoTracks().map(t => ({ id: t.id, label: t.label })));\n return stream;\n } catch (err) {\n console.warn(\"[RFStreams] failed, falling back\", err);\n const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });\n console.log(\"[RFStreams] fallback stream\", stream.getVideoTracks().map(t => ({ id: t.id, label: t.label })));\n return stream;\n }\n}\n \nexport function stopStream(stream: MediaStream | null | undefined): void {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n console.log(\"[RFStreams] Stream stopped\");\n }\n}\n","/**\n * Video file upload via WebRTC datachannel\n *\n * This module provides the FileUploader class for chunked file uploads\n * through WebRTC datachannels with backpressure handling.\n */\n\n/**\n * Configuration constants for file upload (matching Python SDK)\n */\nconst CHUNK_SIZE = 49152; // 49KB - safe for WebRTC\nconst BUFFER_LIMIT = 262144; // 256KB - backpressure threshold\nconst POLL_INTERVAL = 10; // 10ms buffer check interval\n\n/**\n * Helper to sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * FileUploader handles chunked file upload with backpressure\n *\n * Uploads files through a WebRTC datachannel in 49KB chunks with\n * intelligent backpressure handling to prevent overwhelming the network.\n */\nexport class FileUploader {\n private file: File;\n private channel: RTCDataChannel;\n private totalChunks: number;\n private cancelled: boolean = false;\n\n constructor(file: File, channel: RTCDataChannel) {\n this.file = file;\n this.channel = channel;\n this.totalChunks = Math.ceil(file.size / CHUNK_SIZE);\n }\n\n /**\n * Cancel the upload\n */\n cancel(): void {\n this.cancelled = true;\n }\n\n /**\n * Upload the file in chunks with backpressure handling\n *\n * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)\n */\n async upload(onProgress?: (bytesUploaded: number, totalBytes: number) => void): Promise<void> {\n const totalBytes = this.file.size;\n\n for (let chunkIndex = 0; chunkIndex < this.totalChunks; chunkIndex++) {\n // Check for cancellation\n if (this.cancelled) {\n throw new Error(\"Upload cancelled\");\n }\n\n // Check channel state\n if (this.channel.readyState !== \"open\") {\n throw new Error(\"Video upload interrupted\");\n }\n\n // Read chunk from file\n const start = chunkIndex * CHUNK_SIZE;\n const end = Math.min(start + CHUNK_SIZE, totalBytes);\n const chunkBlob = this.file.slice(start, end);\n const chunkData = new Uint8Array(await chunkBlob.arrayBuffer());\n\n // Create message with 8-byte header (chunkIndex + totalChunks as uint32 LE)\n const message = new ArrayBuffer(8 + chunkData.length);\n const view = new DataView(message);\n view.setUint32(0, chunkIndex, true); // little-endian\n view.setUint32(4, this.totalChunks, true); // little-endian\n new Uint8Array(message, 8).set(chunkData);\n\n // Backpressure: wait for buffer to drain\n while (this.channel.bufferedAmount > BUFFER_LIMIT) {\n if (this.channel.readyState !== \"open\") {\n throw new Error(\"Video upload interrupted\");\n }\n await sleep(POLL_INTERVAL);\n }\n\n // Send chunk\n this.channel.send(message);\n\n // Report progress\n if (onProgress) {\n onProgress(end, totalBytes);\n }\n }\n }\n}\n","\nimport { InferenceHTTPClient, Connector, WebRTCParams, RTCIceServerConfig } from \"./inference-api\";\nimport { stopStream } from \"./streams\";\nimport { WebRTCOutputData } from \"./webrtc-types\";\nimport { FileUploader } from \"./video-upload\";\n\n// Re-export shared types\nexport type { WebRTCVideoMetadata, WebRTCOutputData } from \"./webrtc-types\";\n\n// Re-export FileUploader from video-upload\nexport { FileUploader } from \"./video-upload\";\n\n/**\n * Binary protocol header size (frame_id + chunk_index + total_chunks)\n * Each field is 4 bytes uint32 little-endian\n */\nconst HEADER_SIZE = 12;\n\n/**\n * Reassembles chunked binary messages from the datachannel\n */\nexport class ChunkReassembler {\n private pendingFrames: Map<number, {\n chunks: Map<number, Uint8Array>;\n totalChunks: number;\n }> = new Map();\n\n /**\n * Process an incoming chunk and return the complete message if all chunks received\n */\n processChunk(frameId: number, chunkIndex: number, totalChunks: number, payload: Uint8Array): Uint8Array | null {\n // Single chunk message - return immediately\n if (totalChunks === 1) {\n return payload;\n }\n\n // Multi-chunk message - accumulate\n if (!this.pendingFrames.has(frameId)) {\n this.pendingFrames.set(frameId, {\n chunks: new Map(),\n totalChunks\n });\n }\n\n const frame = this.pendingFrames.get(frameId)!;\n frame.chunks.set(chunkIndex, payload);\n\n // Check if all chunks received\n if (frame.chunks.size === totalChunks) {\n // Reassemble in order\n const totalLength = Array.from(frame.chunks.values()).reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (let i = 0; i < totalChunks; i++) {\n const chunk = frame.chunks.get(i)!;\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n this.pendingFrames.delete(frameId);\n return result;\n }\n\n return null;\n }\n\n /**\n * Clear all pending frames (for cleanup)\n */\n clear(): void {\n this.pendingFrames.clear();\n }\n}\n\n/**\n * Parse the binary header from a datachannel message\n */\nexport function parseBinaryHeader(buffer: ArrayBuffer): { frameId: number; chunkIndex: number; totalChunks: number; payload: Uint8Array } {\n const view = new DataView(buffer);\n const frameId = view.getUint32(0, true); // little-endian\n const chunkIndex = view.getUint32(4, true); // little-endian\n const totalChunks = view.getUint32(8, true); // little-endian\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n\n return { frameId, chunkIndex, totalChunks, payload };\n}\n\nexport interface UseStreamOptions {\n disableInputStreamDownscaling?: boolean;\n}\n\nexport interface UseStreamParams {\n source: MediaStream;\n connector: Connector;\n wrtcParams: WebRTCParams;\n onData?: (data: WebRTCOutputData) => void;\n options?: UseStreamOptions;\n}\n\nasync function waitForIceGathering(pc: RTCPeerConnection, timeoutMs = 6000): Promise<void> {\n if (pc.iceGatheringState === \"complete\") return;\n\n let hasSrflx = false;\n\n // Track if we get a good candidate (srflx = public IP via STUN)\n const candidateHandler = (event: RTCPeerConnectionIceEvent) => {\n if (event.candidate && event.candidate.type === \"srflx\") {\n hasSrflx = true;\n }\n };\n pc.addEventListener(\"icecandidate\", candidateHandler);\n\n try {\n await Promise.race([\n new Promise<void>(resolve => {\n const check = () => {\n if (pc.iceGatheringState === \"complete\") {\n pc.removeEventListener(\"icegatheringstatechange\", check);\n resolve();\n }\n };\n pc.addEventListener(\"icegatheringstatechange\", check);\n }),\n new Promise<void>((resolve, reject) => {\n setTimeout(() => {\n if (!hasSrflx) {\n console.error(\"[ICE] timeout with NO srflx candidate! Connection may fail.\");\n reject(new Error(\"ICE gathering timeout without srflx candidate\"));\n } else {\n resolve();\n }\n }, timeoutMs);\n })\n ]);\n } finally {\n pc.removeEventListener(\"icecandidate\", candidateHandler);\n }\n}\n\nfunction setupRemoteStreamListener(pc: RTCPeerConnection): Promise<MediaStream> {\n return new Promise((resolve) => {\n pc.addEventListener(\"track\", (event: RTCTrackEvent) => {\n if (event.streams && event.streams[0]) {\n resolve(event.streams[0]);\n }\n });\n });\n}\n\nconst DEFAULT_ICE_SERVERS: RTCIceServerConfig[] = [\n { urls: [\"stun:stun.l.google.com:19302\"] }\n];\n\nasync function preparePeerConnection(\n localStream?: MediaStream,\n file?: File,\n customIceServers?: RTCIceServerConfig[]\n): Promise<{\n pc: RTCPeerConnection;\n offer: RTCSessionDescriptionInit;\n remoteStreamPromise: Promise<MediaStream>;\n dataChannel: RTCDataChannel;\n uploadChannel?: RTCDataChannel;\n}> {\n if (!localStream && !file || (localStream && file)) {\n throw new Error(\"Either localStream or file must be provided, but not both\");\n }\n const iceServers = customIceServers ?? DEFAULT_ICE_SERVERS;\n\n const pc = new RTCPeerConnection({\n iceServers: iceServers as RTCIceServer[]\n });\n\n // Add transceiver for receiving remote video (BEFORE adding tracks - order matters!)\n try {\n pc.addTransceiver(\"video\", { direction: \"recvonly\" });\n } catch (err) {\n console.warn(\"[RFWebRTC] Could not add transceiver:\", err);\n }\n\n if (localStream) {\n // Add local tracks\n localStream.getVideoTracks().forEach(track => {\n try {\n // @ts-ignore - contentHint is not in all TypeScript definitions\n track.contentHint = \"detail\";\n } catch (e) {\n // Ignore if contentHint not supported\n }\n pc.addTrack(track, localStream);\n });\n }\n\n // Setup remote stream listener\n const remoteStreamPromise = setupRemoteStreamListener(pc);\n\n // Create control datachannel (named \"inference\" to match Python SDK)\n const dataChannel = pc.createDataChannel(\"inference\", {\n ordered: true\n });\n\n // Create upload datachannel for file uploads\n let uploadChannel: RTCDataChannel | undefined;\n if (file) {\n uploadChannel = pc.createDataChannel(\"video_upload\");\n }\n\n // Create offer\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n // Wait for ICE gathering\n await waitForIceGathering(pc);\n\n return {\n pc,\n offer: pc.localDescription!,\n remoteStreamPromise,\n dataChannel,\n uploadChannel\n };\n}\n\n/**\n * Disable input stream downscaling\n * @private\n */\nasync function disableInputStreamDownscaling(pc: RTCPeerConnection): Promise<void> {\n const sender = pc.getSenders().find(s => s.track && s.track.kind === \"video\");\n if (!sender) return;\n\n const params = sender.getParameters();\n params.encodings = params.encodings || [{}];\n params.encodings[0].scaleResolutionDownBy = 1;\n\n try {\n await sender.setParameters(params);\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to set encoding parameters:\", err);\n }\n}\n\n/**\n * Helper to wait for datachannel to open\n */\nfunction waitForChannelOpen(channel: RTCDataChannel, timeoutMs = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n if (channel.readyState === \"open\") {\n resolve();\n return;\n }\n\n const openHandler = () => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n clearTimeout(timeout);\n resolve();\n };\n\n const errorHandler = () => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n clearTimeout(timeout);\n reject(new Error(\"Datachannel error\"));\n };\n\n const timeout = setTimeout(() => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n reject(new Error(\"Datachannel open timeout\"));\n }, timeoutMs);\n\n channel.addEventListener(\"open\", openHandler);\n channel.addEventListener(\"error\", errorHandler);\n });\n}\n\n/**\n * WebRTC Connection object\n *\n * Represents an active WebRTC connection to Roboflow for streaming inference\n * or file-based batch processing.\n */\nexport class RFWebRTCConnection {\n private pc: RTCPeerConnection;\n private _localStream?: MediaStream;\n private remoteStreamPromise: Promise<MediaStream>;\n private pipelineId: string | null;\n private apiKey: string | null;\n private dataChannel: RTCDataChannel;\n private reassembler: ChunkReassembler;\n private uploadChannel?: RTCDataChannel;\n private uploader?: FileUploader;\n private onComplete?: () => void;\n\n /** @private */\n constructor(\n pc: RTCPeerConnection,\n remoteStreamPromise: Promise<MediaStream>,\n pipelineId: string | null,\n apiKey: string | null,\n dataChannel: RTCDataChannel,\n options?: {\n localStream?: MediaStream;\n uploadChannel?: RTCDataChannel;\n onData?: (data: any) => void;\n onComplete?: () => void;\n }\n ) {\n this.pc = pc;\n this._localStream = options?.localStream;\n this.remoteStreamPromise = remoteStreamPromise;\n this.pipelineId = pipelineId;\n this.apiKey = apiKey;\n this.dataChannel = dataChannel;\n this.reassembler = new ChunkReassembler();\n this.uploadChannel = options?.uploadChannel;\n this.onComplete = options?.onComplete;\n\n // Set binary mode for datachannel\n this.dataChannel.binaryType = \"arraybuffer\";\n\n const onData = options?.onData;\n\n // Setup data channel event listeners\n if (onData) {\n this.dataChannel.addEventListener(\"message\", (messageEvent: MessageEvent) => {\n try {\n // Handle binary protocol with chunking\n if (messageEvent.data instanceof ArrayBuffer) {\n const { frameId, chunkIndex, totalChunks, payload } = parseBinaryHeader(messageEvent.data);\n const completePayload = this.reassembler.processChunk(frameId, chunkIndex, totalChunks, payload);\n\n if (completePayload) {\n // Decode UTF-8 JSON payload\n const decoder = new TextDecoder(\"utf-8\");\n const jsonString = decoder.decode(completePayload);\n const data = JSON.parse(jsonString);\n onData(data);\n }\n } else {\n // Fallback for string messages (shouldn't happen with new protocol)\n const data = JSON.parse(messageEvent.data);\n onData(data);\n }\n } catch (err) {\n console.error(\"[RFWebRTC] Failed to parse data channel message:\", err);\n }\n });\n\n this.dataChannel.addEventListener(\"error\", (error) => {\n console.error(\"[RFWebRTC] Data channel error:\", error);\n });\n }\n\n // Handle channel close - call onComplete when processing finishes\n this.dataChannel.addEventListener(\"close\", () => {\n this.reassembler.clear();\n if (this.onComplete) {\n this.onComplete();\n }\n });\n }\n\n /**\n * Get the remote stream (processed video from Roboflow)\n *\n * @returns Promise resolving to the remote MediaStream\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * const remoteStream = await conn.remoteStream();\n * videoElement.srcObject = remoteStream;\n * ```\n */\n async remoteStream(): Promise<MediaStream> {\n return await this.remoteStreamPromise;\n }\n\n /**\n * Get the local stream (original camera)\n *\n * @returns The local MediaStream, or undefined if using file upload mode\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * const localStream = conn.localStream();\n * if (localStream) {\n * videoElement.srcObject = localStream;\n * }\n * ```\n */\n localStream(): MediaStream | undefined {\n return this._localStream;\n }\n\n /**\n * Cleanup and close connection\n *\n * Terminates the pipeline on Roboflow, closes the peer connection,\n * and stops the local media stream (if applicable).\n *\n * @returns Promise that resolves when cleanup is complete\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * // ... use connection ...\n * await conn.cleanup(); // Clean up when done\n * ```\n */\n async cleanup(): Promise<void> {\n // Cancel any ongoing upload\n if (this.uploader) {\n this.uploader.cancel();\n }\n\n // Clear pending chunks\n this.reassembler.clear();\n\n // Terminate pipeline\n if (this.pipelineId && this.apiKey) {\n try {\n const client = InferenceHTTPClient.init({ apiKey: this.apiKey });\n await client.terminatePipeline({ pipelineId: this.pipelineId });\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to terminate pipeline:\", err);\n }\n }\n\n // Close peer connection\n if (this.pc && this.pc.connectionState !== \"closed\") {\n this.pc.close();\n }\n\n // Stop local stream if present\n if (this._localStream) {\n stopStream(this._localStream);\n }\n }\n\n /**\n * Start uploading a file through the connection\n *\n * @param file - The file to upload\n * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)\n * @returns Promise that resolves when upload is complete\n * @throws Error if no upload channel is available\n *\n * @example\n * ```typescript\n * await connection.startUpload(videoFile, (uploaded, total) => {\n * console.log(`Upload progress: ${(uploaded / total * 100).toFixed(1)}%`);\n * });\n * ```\n */\n async startUpload(file: File, onProgress?: (bytesUploaded: number, totalBytes: number) => void): Promise<void> {\n if (!this.uploadChannel) {\n throw new Error(\"No upload channel available. This connection was not created for file uploads.\");\n }\n\n // Wait for upload channel to open\n await waitForChannelOpen(this.uploadChannel);\n\n this.uploader = new FileUploader(file, this.uploadChannel);\n await this.uploader.upload(onProgress);\n }\n\n /**\n * Cancel any ongoing file upload\n */\n cancelUpload(): void {\n if (this.uploader) {\n this.uploader.cancel();\n }\n }\n\n /**\n * Reconfigure pipeline outputs at runtime\n *\n * Dynamically change stream and data outputs without restarting the connection.\n * Set a field to `null` to leave it unchanged, or to `null` value to enable all outputs,\n * or to `[]` to disable/auto-detect.\n *\n * @param config - Output configuration\n * @param config.streamOutput - Stream output names (null = unchanged, [] = auto-detect, [\"name\"] = specific output)\n * @param config.dataOutput - Data output names (null = unchanged, [] = disable, [\"name\"] = specific outputs, null value = all outputs)\n *\n * @example\n * ```typescript\n * // Change to different stream output\n * connection.reconfigureOutputs({\n * streamOutput: [\"annotated_image\"],\n * dataOutput: null // unchanged\n * });\n *\n * // Enable all data outputs\n * connection.reconfigureOutputs({\n * streamOutput: null, // unchanged\n * dataOutput: null // null value = all outputs\n * });\n *\n * // Disable all data outputs\n * connection.reconfigureOutputs({\n * streamOutput: null, // unchanged\n * dataOutput: [] // empty array = disable\n * });\n * ```\n */\n reconfigureOutputs(config: { streamOutput?: string[] | null; dataOutput?: string[] | null }): void {\n const message: any = {};\n\n if (config.streamOutput !== undefined) {\n message.stream_output = config.streamOutput;\n }\n\n if (config.dataOutput !== undefined) {\n message.data_output = config.dataOutput;\n }\n\n this.sendData(message);\n }\n\n /**\n * Send data through the data channel\n * @private\n */\n private sendData(data: any): void {\n if (this.dataChannel.readyState !== \"open\") {\n console.warn(\"[RFWebRTC] Data channel is not open. Current state:\", this.dataChannel.readyState);\n return;\n }\n\n try {\n const message = typeof data === \"string\" ? data : JSON.stringify(data);\n this.dataChannel.send(message);\n } catch (err) {\n console.error(\"[RFWebRTC] Failed to send data:\", err);\n }\n }\n}\n\n/**\n * Internal base function for establishing WebRTC connection\n * Used by both useStream and useVideoFile\n * @private\n */\ninterface BaseUseStreamParams {\n source: MediaStream | File;\n connector: Connector;\n wrtcParams: WebRTCParams;\n onData?: (data: WebRTCOutputData) => void;\n onComplete?: () => void;\n onFileUploadProgress?: (bytesUploaded: number, totalBytes: number) => void;\n options?: UseStreamOptions;\n}\n\nasync function baseUseStream({\n source,\n connector,\n wrtcParams,\n onData,\n onComplete,\n onFileUploadProgress,\n options = {}\n}: BaseUseStreamParams): Promise<RFWebRTCConnection> {\n // Validate connector\n if (!connector || typeof connector.connectWrtc !== \"function\") {\n throw new Error(\"connector must have a connectWrtc method\");\n }\n\n const isFile = source instanceof File;\n const localStream = isFile ? undefined : source;\n const file = isFile ? source : undefined;\n\n // Step 1: Determine ICE servers to use\n // Priority: 1) User-provided in wrtcParams, 2) From connector.getIceServers(), 3) Defaults\n let iceServers = wrtcParams.iceServers;\n if ((!iceServers || iceServers.length === 0) && connector.getIceServers) {\n try {\n const turnConfig = await connector.getIceServers();\n if (turnConfig && turnConfig.length > 0) {\n iceServers = turnConfig;\n console.log(\"[RFWebRTC] Using TURN servers from connector\");\n }\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to fetch TURN config, using defaults:\", err);\n }\n }\n\n // Step 2: Prepare peer connection and create offer\n const { pc, offer, remoteStreamPromise, dataChannel, uploadChannel } = await preparePeerConnection(\n localStream,\n file,\n iceServers\n );\n\n // Update wrtcParams with resolved iceServers so server also uses them\n // For file uploads, default to batch mode (realtimeProcessing: false)\n const resolvedWrtcParams = {\n ...wrtcParams,\n iceServers: iceServers,\n realtimeProcessing: wrtcParams.realtimeProcessing ?? !isFile\n };\n\n // Step 3: Call connector.connectWrtc to exchange SDP and get answer\n const answer = await connector.connectWrtc(\n { sdp: offer.sdp!, type: offer.type! },\n resolvedWrtcParams\n );\n\n // API returns sdp and type at root level\n const sdpAnswer = { sdp: answer.sdp, type: answer.type } as RTCSessionDescriptionInit;\n\n if (!sdpAnswer?.sdp || !sdpAnswer?.type) {\n console.error(\"[RFWebRTC] Invalid answer from server:\", answer);\n throw new Error(\"connector.connectWrtc must return answer with sdp and type\");\n }\n\n const pipelineId = answer?.context?.pipeline_id || null;\n\n // Step 4: Set remote description\n await pc.setRemoteDescription(sdpAnswer);\n\n // Step 5: Wait for connection to establish\n await new Promise<void>((resolve, reject) => {\n const checkState = () => {\n if (pc.connectionState === \"connected\") {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n resolve();\n } else if (pc.connectionState === \"failed\") {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n reject(new Error(\"WebRTC connection failed\"));\n }\n };\n\n pc.addEventListener(\"connectionstatechange\", checkState);\n checkState(); // Check immediately in case already connected\n\n // Timeout after 30 seconds\n setTimeout(() => {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n reject(new Error(\"WebRTC connection timeout after 30s\"));\n }, 30000);\n });\n\n // Step 6: Optimize quality for MediaStream (disable downsampling by default)\n if (localStream) {\n const shouldDisableDownscaling = options.disableInputStreamDownscaling !== false;\n if (shouldDisableDownscaling) {\n await disableInputStreamDownscaling(pc);\n }\n }\n\n // Get apiKey from connector if available (for cleanup)\n const apiKey = connector._apiKey || null;\n\n // Step 7: Create connection object\n const connection = new RFWebRTCConnection(\n pc,\n remoteStreamPromise,\n pipelineId,\n apiKey,\n dataChannel,\n {\n localStream,\n uploadChannel,\n onData,\n onComplete\n }\n );\n\n // Step 8: Start file upload if applicable (runs in background)\n if (file && uploadChannel) {\n connection.startUpload(file, onFileUploadProgress).catch(err => {\n console.error(\"[RFWebRTC] Upload error:\", err);\n });\n }\n\n return connection;\n}\n\n/**\n * Main function to establish WebRTC streaming connection\n *\n * Creates a WebRTC connection to Roboflow for real-time inference on video streams.\n *\n * @param params - Connection parameters\n * @returns Promise resolving to RFWebRTCConnection\n *\n * @example\n * ```typescript\n * import { useStream } from 'inferencejs/webrtc';\n * import { connectors } from 'inferencejs/api';\n * import { useCamera } from 'inferencejs/streams';\n *\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const stream = await useCamera({ video: { facingMode: { ideal: \"environment\" } } });\n * const conn = await useStream({\n * source: stream,\n * connector,\n * wrtcParams: {\n * workflowSpec: {\n * // Your workflow specification\n * },\n * imageInputName: \"image\",\n * streamOutputNames: [\"output\"],\n * dataOutputNames: [\"predictions\"]\n * },\n * onData: (data) => {\n * console.log(\"Inference results:\", data);\n * }\n * });\n *\n * const remoteStream = await conn.remoteStream();\n * videoElement.srcObject = remoteStream;\n * ```\n */\nexport async function useStream({\n source,\n connector,\n wrtcParams,\n onData,\n options = {}\n}: UseStreamParams): Promise<RFWebRTCConnection> {\n if (source instanceof File) {\n throw new Error(\"useStream requires a MediaStream. Use useVideoFile for File uploads.\");\n }\n\n return baseUseStream({\n source,\n connector,\n wrtcParams,\n onData,\n options\n });\n}\n\n/**\n * Parameters for useVideoFile function\n */\nexport interface UseVideoFileParams {\n /** The video file to upload */\n file: File;\n /** Connector for WebRTC signaling */\n connector: Connector;\n /** WebRTC parameters for the workflow */\n wrtcParams: WebRTCParams;\n /** Callback for inference results */\n onData?: (data: WebRTCOutputData) => void;\n /** Callback for upload progress */\n onUploadProgress?: (bytesUploaded: number, totalBytes: number) => void;\n /** Callback when processing completes (datachannel closes) */\n onComplete?: () => void;\n}\n\n/**\n * Upload a video file for batch inference processing\n *\n * Creates a WebRTC connection to Roboflow for uploading a video file\n * and receiving inference results. The file is uploaded via datachannel\n * with intelligent backpressure handling.\n *\n * @param params - Connection parameters\n * @returns Promise resolving to RFWebRTCConnection\n *\n * @example\n * ```typescript\n * import { connectors, webrtc } from '@roboflow/inference-sdk';\n *\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const connection = await webrtc.useVideoFile({\n * file: videoFile,\n * connector,\n * wrtcParams: {\n * workflowSpec: { ... },\n * imageInputName: \"image\",\n * dataOutputNames: [\"predictions\"]\n * },\n * onData: (data) => {\n * console.log(\"Inference results:\", data);\n * if (data.processing_complete) {\n * console.log(\"Processing complete!\");\n * }\n * },\n * onUploadProgress: (uploaded, total) => {\n * console.log(`Upload: ${(uploaded / total * 100).toFixed(1)}%`);\n * }\n * });\n *\n * // When done\n * await connection.cleanup();\n * ```\n */\nexport async function useVideoFile({\n file,\n connector,\n wrtcParams,\n onData,\n onUploadProgress,\n onComplete\n}: UseVideoFileParams): Promise<RFWebRTCConnection> {\n return baseUseStream({\n source: file,\n connector,\n wrtcParams: {\n ...wrtcParams,\n realtimeProcessing: wrtcParams.realtimeProcessing ?? true\n },\n onData,\n onComplete,\n onFileUploadProgress: onUploadProgress\n });\n}\n"],"names":["RF_API_BASE_URL","_a","ROBOFLOW_SERVERLESS_URLS","InferenceHTTPClient","apiKey","serverUrl","__publicField","offer","workflowSpec","workspaceName","workflowId","config","hasWorkflowSpec","hasWorkspaceIdentifier","imageInputName","streamOutputNames","dataOutputNames","threadPoolWorkers","workflowsParameters","iceServers","processingTimeout","requestedPlan","requestedRegion","realtimeProcessing","workflowConfiguration","payload","response","errorText","pipelineId","turnConfig","iceServersRaw","server","err","connectors","options","client","wrtcParams","proxyUrl","turnConfigUrl","useCamera","constraints","stream","t","stopStream","track","CHUNK_SIZE","BUFFER_LIMIT","POLL_INTERVAL","sleep","ms","resolve","FileUploader","file","channel","onProgress","totalBytes","chunkIndex","start","end","chunkBlob","chunkData","message","view","HEADER_SIZE","ChunkReassembler","frameId","totalChunks","frame","totalLength","sum","chunk","result","offset","i","parseBinaryHeader","buffer","waitForIceGathering","pc","timeoutMs","hasSrflx","candidateHandler","event","check","reject","setupRemoteStreamListener","DEFAULT_ICE_SERVERS","preparePeerConnection","localStream","customIceServers","remoteStreamPromise","dataChannel","uploadChannel","disableInputStreamDownscaling","sender","s","params","waitForChannelOpen","openHandler","errorHandler","timeout","RFWebRTCConnection","onData","messageEvent","completePayload","jsonString","data","error","baseUseStream","source","connector","onComplete","onFileUploadProgress","isFile","resolvedWrtcParams","answer","sdpAnswer","checkState","connection","useStream","useVideoFile","onUploadProgress"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/inference-api.ts","../src/streams.ts","../src/video-upload.ts","../src/webrtc.ts"],"sourcesContent":["/**\n * Base URL for the Roboflow API (used for TURN server configuration)\n * Can be overridden via environment variable in Node.js environments\n */\nconst RF_API_BASE_URL = typeof process !== \"undefined\" && process.env?.RF_API_BASE_URL\n ? process.env.RF_API_BASE_URL\n : \"https://api.roboflow.com\";\n\n/**\n * List of known Roboflow serverless API URLs where auto TURN config applies\n */\nconst ROBOFLOW_SERVERLESS_URLS = [\n \"https://serverless.roboflow.com\"\n];\n\nexport interface WebRTCWorkerConfig {\n imageInputName?: string;\n streamOutputNames?: string[];\n dataOutputNames?: string[];\n threadPoolWorkers?: number;\n /**\n * Workflow parameters to pass to the workflow execution\n */\n workflowsParameters?: Record<string, any>;\n /**\n * ICE servers for WebRTC connections (used for both client and server)\n */\n iceServers?: RTCIceServerConfig[];\n /**\n * Processing timeout in seconds (serverless only)\n * @default 600\n */\n processingTimeout?: number;\n /**\n * Requested compute plan (serverless only)\n * @example \"webrtc-gpu-small\"\n */\n requestedPlan?: string;\n /**\n * Requested region for processing (serverless only)\n * @example \"us\"\n */\n requestedRegion?: string;\n /**\n * Set to false for file upload mode (batch processing).\n * When false, server processes all frames sequentially instead of dropping frames.\n * @default true\n */\n realtimeProcessing?: boolean;\n /**\n * RTSP URL for server-side video capture.\n * When provided, the server captures video from this RTSP stream instead of receiving\n * video from the client. Supports credentials in URL format: rtsp://user:pass@host/stream\n * @example \"rtsp://camera.local/stream\"\n */\n rtspUrl?: string;\n}\n\n/**\n * ICE server configuration for WebRTC connections\n *\n * Use this to configure custom STUN/TURN servers for users behind\n * symmetric NAT or restrictive firewalls.\n */\nexport interface RTCIceServerConfig {\n urls: string[];\n username?: string;\n credential?: string;\n}\n\nexport interface WebRTCOffer {\n sdp: string;\n type: string;\n}\n\nexport type WorkflowSpec = Record<string, any>;\n\nexport interface WebRTCWorkerResponse {\n status?: string;\n sdp: string;\n type: string;\n context?: {\n request_id: string | null;\n pipeline_id: string | null;\n };\n}\n\nexport interface WebRTCParams {\n workflowSpec?: WorkflowSpec;\n workspaceName?: string;\n workflowId?: string;\n imageInputName?: string;\n streamOutputNames?: string[];\n dataOutputNames?: string[];\n threadPoolWorkers?: number;\n /**\n * Workflow parameters to pass to the workflow execution\n */\n workflowsParameters?: Record<string, any>;\n /**\n * ICE servers for WebRTC connections (used for both client and server)\n *\n * Use this to specify custom STUN/TURN servers for users behind\n * symmetric NAT or restrictive firewalls. The same configuration is\n * used for both the client-side RTCPeerConnection and sent to the\n * server via webrtc_config.\n *\n * @example\n * ```typescript\n * iceServers: [\n * { urls: [\"stun:stun.l.google.com:19302\"] },\n * { urls: [\"turn:turn.example.com:3478\"], username: \"user\", credential: \"pass\" }\n * ]\n * ```\n */\n iceServers?: RTCIceServerConfig[];\n /**\n * Processing timeout in seconds (serverless only)\n * @default 600\n */\n processingTimeout?: number;\n /**\n * Requested compute plan (serverless only)\n * @example \"webrtc-gpu-small\"\n */\n requestedPlan?: string;\n /**\n * Requested region for processing (serverless only)\n * @example \"us\"\n */\n requestedRegion?: string;\n /**\n * Set to false for file upload mode (batch processing).\n * When false, server processes all frames sequentially instead of dropping frames.\n * @default true\n */\n realtimeProcessing?: boolean;\n /**\n * RTSP URL for server-side video capture.\n * When provided, the server captures video from this RTSP stream instead of receiving\n * video from the client. Supports credentials in URL format: rtsp://user:pass@host/stream\n * @example \"rtsp://camera.local/stream\"\n */\n rtspUrl?: string;\n}\n\nexport interface Connector {\n connectWrtc(offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse>;\n /**\n * Fetch ICE servers (TURN configuration) for WebRTC connections\n * This should be called BEFORE creating the RTCPeerConnection to ensure\n * proper NAT traversal configuration.\n *\n * @returns Promise resolving to ICE server configuration, or null/undefined if not available\n */\n getIceServers?(): Promise<RTCIceServerConfig[] | null>;\n _apiKey?: string;\n _serverUrl?: string;\n}\n\nexport class InferenceHTTPClient {\n private apiKey: string;\n private serverUrl: string;\n\n /**\n * @private\n * Use InferenceHTTPClient.init() instead\n */\n private constructor(apiKey: string, serverUrl: string = \"https://serverless.roboflow.com\") {\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n }\n\n static init({ apiKey, serverUrl }: { apiKey: string; serverUrl?: string }): InferenceHTTPClient {\n if (!apiKey) {\n throw new Error(\"apiKey is required\");\n }\n return new InferenceHTTPClient(apiKey, serverUrl);\n }\n\n /**\n * Initialize a WebRTC worker pipeline\n *\n * @param params - Pipeline parameters\n * @param params.offer - WebRTC offer { sdp, type }\n * @param params.workflowSpec - Workflow specification\n * @param params.config - Additional configuration\n * @param params.config.imageInputName - Input image name (default: \"image\")\n * @param params.config.streamOutputNames - Output stream names for video (default: [])\n * @param params.config.dataOutputNames - Output data names (default: [\"string\"])\n * @param params.config.threadPoolWorkers - Thread pool workers (default: 4)\n * @returns Promise resolving to answer with SDP and pipeline ID\n *\n * @example\n * ```typescript\n * const answer = await client.initializeWebrtcWorker({\n * offer: { sdp, type },\n * workflowSpec: { ... },\n * config: {\n * imageInputName: \"image\",\n * streamOutputNames: [\"output_image\"]\n * }\n * });\n * ```\n */\n async initializeWebrtcWorker({\n offer,\n workflowSpec,\n workspaceName,\n workflowId,\n config = {}\n }: {\n offer: WebRTCOffer;\n workflowSpec?: WorkflowSpec;\n workspaceName?: string;\n workflowId?: string;\n config?: WebRTCWorkerConfig;\n }): Promise<WebRTCWorkerResponse> {\n if (!offer || !offer.sdp || !offer.type) {\n throw new Error(\"offer with sdp and type is required\");\n }\n\n // Validate that either workflowSpec OR (workspaceName + workflowId) is provided\n const hasWorkflowSpec = !!workflowSpec;\n const hasWorkspaceIdentifier = !!(workspaceName && workflowId);\n\n if (!hasWorkflowSpec && !hasWorkspaceIdentifier) {\n throw new Error(\"Either workflowSpec OR (workspaceName + workflowId) is required\");\n }\n if (hasWorkflowSpec && hasWorkspaceIdentifier) {\n throw new Error(\"Provide either workflowSpec OR (workspaceName + workflowId), not both\");\n }\n\n const {\n imageInputName = \"image\",\n streamOutputNames = [],\n dataOutputNames = [\"string\"],\n threadPoolWorkers = 4,\n workflowsParameters = {},\n iceServers,\n processingTimeout,\n requestedPlan,\n requestedRegion,\n realtimeProcessing = true,\n rtspUrl\n } = config as any;\n\n // Build workflow_configuration based on what's provided\n const workflowConfiguration: any = {\n type: \"WorkflowConfiguration\",\n image_input_name: imageInputName,\n workflows_parameters: workflowsParameters,\n workflows_thread_pool_workers: threadPoolWorkers,\n cancel_thread_pool_tasks_on_exit: true,\n video_metadata_input_name: \"video_metadata\"\n };\n\n if (hasWorkflowSpec) {\n workflowConfiguration.workflow_specification = workflowSpec;\n } else {\n workflowConfiguration.workspace_name = workspaceName;\n workflowConfiguration.workflow_id = workflowId;\n }\n\n const payload: Record<string, any> = {\n workflow_configuration: workflowConfiguration,\n api_key: this.apiKey,\n webrtc_realtime_processing: realtimeProcessing,\n webrtc_offer: {\n sdp: offer.sdp,\n type: offer.type\n },\n webrtc_config: iceServers ? { iceServers } : null,\n stream_output: streamOutputNames,\n data_output: dataOutputNames\n };\n\n // Add serverless-specific fields if provided\n if (processingTimeout !== undefined) {\n payload.processing_timeout = processingTimeout;\n }\n if (requestedPlan !== undefined) {\n payload.requested_plan = requestedPlan;\n }\n if (requestedRegion !== undefined) {\n payload.requested_region = requestedRegion;\n }\n // Add RTSP URL for server-side video capture\n if (rtspUrl) {\n payload.rtsp_url = rtspUrl;\n }\n const response = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n throw new Error(`initialise_webrtc_worker failed (${response.status}): ${errorText}`);\n }\n\n const result = await response.json();\n\n return result;\n }\n\n async terminatePipeline({ pipelineId }: { pipelineId: string }): Promise<void> {\n if (!pipelineId) {\n throw new Error(\"pipelineId is required\");\n }\n\n await fetch(\n `${this.serverUrl}/inference_pipelines/${pipelineId}/terminate?api_key=${this.apiKey}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" }\n }\n );\n }\n\n /**\n * Fetch TURN server configuration from Roboflow API\n *\n * This automatically fetches TURN server credentials for improved WebRTC\n * connectivity through firewalls and NAT. Only applicable when using\n * Roboflow serverless infrastructure.\n *\n * @returns Promise resolving to ICE server configuration, or null if not applicable\n *\n * @example\n * ```typescript\n * const client = InferenceHTTPClient.init({ apiKey: \"your-api-key\" });\n * const iceServers = await client.fetchTurnConfig();\n * // Returns: [{ urls: [\"turn:...\"], username: \"...\", credential: \"...\" }]\n * ```\n */\n async fetchTurnConfig(): Promise<RTCIceServerConfig[] | null> {\n // // Only fetch TURN config for Roboflow serverless URLs\n if (!ROBOFLOW_SERVERLESS_URLS.includes(this.serverUrl)) {\n return null;\n }\n try {\n const response = await fetch(\n `${RF_API_BASE_URL}/webrtc_turn_config?api_key=${this.apiKey}`,\n {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n }\n );\n\n if (!response.ok) {\n console.warn(`[RFWebRTC] Failed to fetch TURN config (${response.status}), using defaults`);\n return null;\n }\n\n const turnConfig = await response.json();\n\n // Handle 3 formats:\n // 1. Single server object: { urls, username, credential }\n // 2. Array of servers: [{ urls, username, credential }, ...]\n // 3. Object with iceServers: { iceServers: [...] }\n let iceServersRaw: any[];\n\n if (Array.isArray(turnConfig)) {\n // Format 2: array of servers\n iceServersRaw = turnConfig;\n } else if (turnConfig.iceServers && Array.isArray(turnConfig.iceServers)) {\n // Format 3: object with iceServers array\n iceServersRaw = turnConfig.iceServers;\n } else if (turnConfig.urls) {\n // Format 1: single server object - wrap in array\n iceServersRaw = [turnConfig];\n } else {\n console.warn(\"[RFWebRTC] Invalid TURN config format, using defaults\");\n return null;\n }\n\n // Normalize the ICE servers format\n const iceServers: RTCIceServerConfig[] = iceServersRaw.map((server: any) => ({\n urls: Array.isArray(server.urls) ? server.urls : [server.urls],\n username: server.username,\n credential: server.credential\n }));\n\n return iceServers;\n } catch (err) {\n console.warn(\"[RFWebRTC] Error fetching TURN config:\", err);\n return null;\n }\n }\n}\n\n/**\n * Connectors for establishing WebRTC connections to Roboflow\n */\nexport const connectors = {\n /**\n * Create a connector that uses API key directly\n *\n * **WARNING**: If you use this in the frontend, it will expose your API key. \n * Use only for demos/testing.\n * For production, use withProxyUrl() with a backend proxy.\n *\n * @param apiKey - Roboflow API key\n * @param options - Additional options\n * @param options.serverUrl - Custom Roboflow server URL\n * @returns Connector with connectWrtc method\n *\n * @example\n * ```typescript\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const answer = await connector.connectWrtc(offer, wrtcParams);\n * ```\n */\n withApiKey(apiKey: string, options: { serverUrl?: string } = {}): Connector {\n const { serverUrl } = options;\n\n // Warn if running in browser context\n if (typeof window !== 'undefined') {\n console.warn(\n '[Security Warning] Using API key directly in browser will expose it. ' +\n 'Use connectors.withProxyUrl() for production. ' +\n 'See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key'\n );\n }\n\n const client = InferenceHTTPClient.init({ apiKey, serverUrl });\n\n return {\n connectWrtc: async (offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse> => {\n console.debug(\"wrtcParams\", wrtcParams);\n const answer = await client.initializeWebrtcWorker({\n offer,\n workflowSpec: wrtcParams.workflowSpec,\n workspaceName: wrtcParams.workspaceName,\n workflowId: wrtcParams.workflowId,\n config: {\n imageInputName: wrtcParams.imageInputName,\n streamOutputNames: wrtcParams.streamOutputNames,\n dataOutputNames: wrtcParams.dataOutputNames,\n threadPoolWorkers: wrtcParams.threadPoolWorkers,\n workflowsParameters: wrtcParams.workflowsParameters,\n iceServers: wrtcParams.iceServers,\n processingTimeout: wrtcParams.processingTimeout,\n requestedPlan: wrtcParams.requestedPlan,\n requestedRegion: wrtcParams.requestedRegion,\n realtimeProcessing: wrtcParams.realtimeProcessing,\n rtspUrl: wrtcParams.rtspUrl\n }\n });\n\n return answer;\n },\n\n /**\n * Fetch TURN server configuration for improved WebRTC connectivity\n */\n getIceServers: async (): Promise<RTCIceServerConfig[] | null> => {\n return await client.fetchTurnConfig();\n },\n\n // Store apiKey for cleanup\n _apiKey: apiKey,\n _serverUrl: serverUrl\n };\n },\n\n /**\n * Create a connector that uses a backend proxy (recommended for production)\n *\n * Your backend receives the offer and wrtcParams, adds the secret API key,\n * and forwards to Roboflow. This keeps your API key secure.\n *\n * For improved WebRTC connectivity through firewalls, implement a separate\n * endpoint for TURN server configuration that calls `fetchTurnConfig()`.\n *\n * @param proxyUrl - Backend proxy endpoint URL for WebRTC initialization\n * @param options - Additional options\n * @param options.turnConfigUrl - Optional URL for fetching TURN server configuration\n * @returns Connector with connectWrtc and optional getIceServers methods\n *\n * @example\n * ```typescript\n * // Frontend: Create connector with TURN config endpoint\n * const connector = connectors.withProxyUrl('/api/init-webrtc', {\n * turnConfigUrl: '/api/turn-config'\n * });\n * ```\n *\n * @example\n * Backend implementation (Express) with TURN server support:\n * ```typescript\n * // Endpoint for TURN configuration (called first by SDK)\n * app.get('/api/turn-config', async (req, res) => {\n * const client = InferenceHTTPClient.init({\n * apiKey: process.env.ROBOFLOW_API_KEY\n * });\n * const iceServers = await client.fetchTurnConfig();\n * res.json({ iceServers });\n * });\n *\n * // Endpoint for WebRTC initialization\n * app.post('/api/init-webrtc', async (req, res) => {\n * const { offer, wrtcParams } = req.body;\n * const client = InferenceHTTPClient.init({\n * apiKey: process.env.ROBOFLOW_API_KEY\n * });\n *\n * const answer = await client.initializeWebrtcWorker({\n * offer,\n * workflowSpec: wrtcParams.workflowSpec,\n * workspaceName: wrtcParams.workspaceName,\n * workflowId: wrtcParams.workflowId,\n * config: {\n * imageInputName: wrtcParams.imageInputName,\n * streamOutputNames: wrtcParams.streamOutputNames,\n * dataOutputNames: wrtcParams.dataOutputNames,\n * threadPoolWorkers: wrtcParams.threadPoolWorkers,\n * workflowsParameters: wrtcParams.workflowsParameters,\n * iceServers: wrtcParams.iceServers,\n * processingTimeout: wrtcParams.processingTimeout,\n * requestedPlan: wrtcParams.requestedPlan,\n * requestedRegion: wrtcParams.requestedRegion\n * }\n * });\n *\n * res.json(answer);\n * });\n * ```\n */\n withProxyUrl(proxyUrl: string, options: { turnConfigUrl?: string } = {}): Connector {\n const { turnConfigUrl } = options;\n\n return {\n connectWrtc: async (offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse> => {\n const response = await fetch(proxyUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n offer,\n wrtcParams\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n throw new Error(`Proxy request failed (${response.status}): ${errorText}`);\n }\n\n return await response.json();\n },\n\n /**\n * Fetch TURN server configuration from the proxy backend\n * Only available if turnConfigUrl was provided\n */\n getIceServers: turnConfigUrl\n ? async (): Promise<RTCIceServerConfig[] | null> => {\n try {\n const response = await fetch(turnConfigUrl, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n\n if (!response.ok) {\n console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${response.status})`);\n return null;\n }\n\n const data = await response.json();\n return data.iceServers || null;\n } catch (err) {\n console.warn(\"[RFWebRTC] Error fetching TURN config from proxy:\", err);\n return null;\n }\n }\n : undefined\n };\n }\n};\n","/**\n * Get a camera stream with the given constraints.\n *\n * @param constraints - MediaStreamConstraints for getUserMedia\n * @returns Promise that resolves to MediaStream\n *\n * @example\n * ```typescript\n * const stream = await useCamera({\n * video: {\n * facingMode: { ideal: \"user\" },\n * width: { ideal: 1280 },\n * height: { ideal: 720 },\n * frameRate: { ideal: 30 }\n * },\n * audio: false\n * });\n * ```\n */\nexport async function useCamera(constraints: MediaStreamConstraints = { video: true }): Promise<MediaStream> {\n try {\n console.log(\"[RFStreams] requesting with\", constraints);\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n console.log(\"[RFStreams] got stream\", stream.getVideoTracks().map(t => ({ id: t.id, label: t.label })));\n return stream;\n } catch (err) {\n console.warn(\"[RFStreams] failed, falling back\", err);\n const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });\n console.log(\"[RFStreams] fallback stream\", stream.getVideoTracks().map(t => ({ id: t.id, label: t.label })));\n return stream;\n }\n}\n \nexport function stopStream(stream: MediaStream | null | undefined): void {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n console.log(\"[RFStreams] Stream stopped\");\n }\n}\n","/**\n * Video file upload via WebRTC datachannel\n *\n * This module provides the FileUploader class for chunked file uploads\n * through WebRTC datachannels with backpressure handling.\n */\n\n/**\n * Configuration constants for file upload (matching Python SDK)\n */\nconst CHUNK_SIZE = 49152; // 49KB - safe for WebRTC\nconst BUFFER_LIMIT = 262144; // 256KB - backpressure threshold\nconst POLL_INTERVAL = 10; // 10ms buffer check interval\n\n/**\n * Helper to sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * FileUploader handles chunked file upload with backpressure\n *\n * Uploads files through a WebRTC datachannel in 49KB chunks with\n * intelligent backpressure handling to prevent overwhelming the network.\n */\nexport class FileUploader {\n private file: File;\n private channel: RTCDataChannel;\n private totalChunks: number;\n private cancelled: boolean = false;\n\n constructor(file: File, channel: RTCDataChannel) {\n this.file = file;\n this.channel = channel;\n this.totalChunks = Math.ceil(file.size / CHUNK_SIZE);\n }\n\n /**\n * Cancel the upload\n */\n cancel(): void {\n this.cancelled = true;\n }\n\n /**\n * Upload the file in chunks with backpressure handling\n *\n * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)\n */\n async upload(onProgress?: (bytesUploaded: number, totalBytes: number) => void): Promise<void> {\n const totalBytes = this.file.size;\n\n for (let chunkIndex = 0; chunkIndex < this.totalChunks; chunkIndex++) {\n // Check for cancellation\n if (this.cancelled) {\n throw new Error(\"Upload cancelled\");\n }\n\n // Check channel state\n if (this.channel.readyState !== \"open\") {\n throw new Error(\"Video upload interrupted\");\n }\n\n // Read chunk from file\n const start = chunkIndex * CHUNK_SIZE;\n const end = Math.min(start + CHUNK_SIZE, totalBytes);\n const chunkBlob = this.file.slice(start, end);\n const chunkData = new Uint8Array(await chunkBlob.arrayBuffer());\n\n // Create message with 8-byte header (chunkIndex + totalChunks as uint32 LE)\n const message = new ArrayBuffer(8 + chunkData.length);\n const view = new DataView(message);\n view.setUint32(0, chunkIndex, true); // little-endian\n view.setUint32(4, this.totalChunks, true); // little-endian\n new Uint8Array(message, 8).set(chunkData);\n\n // Backpressure: wait for buffer to drain\n while (this.channel.bufferedAmount > BUFFER_LIMIT) {\n if (this.channel.readyState !== \"open\") {\n throw new Error(\"Video upload interrupted\");\n }\n await sleep(POLL_INTERVAL);\n }\n\n // Send chunk\n this.channel.send(message);\n\n // Report progress\n if (onProgress) {\n onProgress(end, totalBytes);\n }\n }\n }\n}\n","\nimport { InferenceHTTPClient, Connector, WebRTCParams, RTCIceServerConfig } from \"./inference-api\";\nimport { stopStream } from \"./streams\";\nimport { WebRTCOutputData } from \"./webrtc-types\";\nimport { FileUploader } from \"./video-upload\";\n\n// Re-export shared types\nexport type { WebRTCVideoMetadata, WebRTCOutputData } from \"./webrtc-types\";\n\n// Re-export FileUploader from video-upload\nexport { FileUploader } from \"./video-upload\";\n\n/**\n * Binary protocol header size (frame_id + chunk_index + total_chunks)\n * Each field is 4 bytes uint32 little-endian\n */\nconst HEADER_SIZE = 12;\n\n/**\n * Reassembles chunked binary messages from the datachannel\n */\nexport class ChunkReassembler {\n private pendingFrames: Map<number, {\n chunks: Map<number, Uint8Array>;\n totalChunks: number;\n }> = new Map();\n\n /**\n * Process an incoming chunk and return the complete message if all chunks received\n */\n processChunk(frameId: number, chunkIndex: number, totalChunks: number, payload: Uint8Array): Uint8Array | null {\n // Single chunk message - return immediately\n if (totalChunks === 1) {\n return payload;\n }\n\n // Multi-chunk message - accumulate\n if (!this.pendingFrames.has(frameId)) {\n this.pendingFrames.set(frameId, {\n chunks: new Map(),\n totalChunks\n });\n }\n\n const frame = this.pendingFrames.get(frameId)!;\n frame.chunks.set(chunkIndex, payload);\n\n // Check if all chunks received\n if (frame.chunks.size === totalChunks) {\n // Reassemble in order\n const totalLength = Array.from(frame.chunks.values()).reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (let i = 0; i < totalChunks; i++) {\n const chunk = frame.chunks.get(i)!;\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n this.pendingFrames.delete(frameId);\n return result;\n }\n\n return null;\n }\n\n /**\n * Clear all pending frames (for cleanup)\n */\n clear(): void {\n this.pendingFrames.clear();\n }\n}\n\n/**\n * Parse the binary header from a datachannel message\n */\nexport function parseBinaryHeader(buffer: ArrayBuffer): { frameId: number; chunkIndex: number; totalChunks: number; payload: Uint8Array } {\n const view = new DataView(buffer);\n const frameId = view.getUint32(0, true); // little-endian\n const chunkIndex = view.getUint32(4, true); // little-endian\n const totalChunks = view.getUint32(8, true); // little-endian\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n\n return { frameId, chunkIndex, totalChunks, payload };\n}\n\nexport interface UseStreamOptions {\n disableInputStreamDownscaling?: boolean;\n}\n\nexport interface UseStreamParams {\n source: MediaStream;\n connector: Connector;\n wrtcParams: WebRTCParams;\n onData?: (data: WebRTCOutputData) => void;\n options?: UseStreamOptions;\n}\n\nasync function waitForIceGathering(pc: RTCPeerConnection, timeoutMs = 6000): Promise<void> {\n if (pc.iceGatheringState === \"complete\") return;\n\n let hasSrflx = false;\n\n // Track if we get a good candidate (srflx = public IP via STUN)\n const candidateHandler = (event: RTCPeerConnectionIceEvent) => {\n if (event.candidate && event.candidate.type === \"srflx\") {\n hasSrflx = true;\n }\n };\n pc.addEventListener(\"icecandidate\", candidateHandler);\n\n try {\n await Promise.race([\n new Promise<void>(resolve => {\n const check = () => {\n if (pc.iceGatheringState === \"complete\") {\n pc.removeEventListener(\"icegatheringstatechange\", check);\n resolve();\n }\n };\n pc.addEventListener(\"icegatheringstatechange\", check);\n }),\n new Promise<void>((resolve, reject) => {\n setTimeout(() => {\n if (!hasSrflx) {\n console.error(\"[ICE] timeout with NO srflx candidate! Connection may fail.\");\n reject(new Error(\"ICE gathering timeout without srflx candidate\"));\n } else {\n resolve();\n }\n }, timeoutMs);\n })\n ]);\n } finally {\n pc.removeEventListener(\"icecandidate\", candidateHandler);\n }\n}\n\nfunction setupRemoteStreamListener(pc: RTCPeerConnection): Promise<MediaStream> {\n return new Promise((resolve) => {\n pc.addEventListener(\"track\", (event: RTCTrackEvent) => {\n if (event.streams && event.streams[0]) {\n resolve(event.streams[0]);\n }\n });\n });\n}\n\nconst DEFAULT_ICE_SERVERS: RTCIceServerConfig[] = [\n { urls: [\"stun:stun.l.google.com:19302\"] }\n];\n\nasync function preparePeerConnection(\n localStream?: MediaStream,\n file?: File,\n customIceServers?: RTCIceServerConfig[],\n rtspUrl?: string\n): Promise<{\n pc: RTCPeerConnection;\n offer: RTCSessionDescriptionInit;\n remoteStreamPromise: Promise<MediaStream>;\n dataChannel: RTCDataChannel;\n uploadChannel?: RTCDataChannel;\n}> {\n // Validate: exactly one source type must be provided\n const hasLocalStream = !!localStream;\n const hasFile = !!file;\n const hasRtspUrl = !!rtspUrl;\n const sourceCount = [hasLocalStream, hasFile, hasRtspUrl].filter(Boolean).length;\n\n if (sourceCount !== 1) {\n throw new Error(\"Exactly one of localStream, file, or rtspUrl must be provided\");\n }\n\n const iceServers = customIceServers ?? DEFAULT_ICE_SERVERS;\n\n const pc = new RTCPeerConnection({\n iceServers: iceServers as RTCIceServer[]\n });\n\n // Add transceiver for receiving remote video (BEFORE adding tracks - order matters!)\n try {\n pc.addTransceiver(\"video\", { direction: \"recvonly\" });\n } catch (err) {\n console.warn(\"[RFWebRTC] Could not add transceiver:\", err);\n }\n\n if (localStream) {\n // Add local tracks\n localStream.getVideoTracks().forEach(track => {\n try {\n // @ts-ignore - contentHint is not in all TypeScript definitions\n track.contentHint = \"detail\";\n } catch (e) {\n // Ignore if contentHint not supported\n }\n pc.addTrack(track, localStream);\n });\n }\n // Note: For RTSP, no local tracks are added (receive-only mode)\n\n // Setup remote stream listener\n const remoteStreamPromise = setupRemoteStreamListener(pc);\n\n // Create control datachannel (named \"inference\" to match Python SDK)\n const dataChannel = pc.createDataChannel(\"inference\", {\n ordered: true\n });\n\n // Create upload datachannel for file uploads (not needed for RTSP)\n let uploadChannel: RTCDataChannel | undefined;\n if (file) {\n uploadChannel = pc.createDataChannel(\"video_upload\");\n }\n\n // Create offer\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n // Wait for ICE gathering\n await waitForIceGathering(pc);\n\n return {\n pc,\n offer: pc.localDescription!,\n remoteStreamPromise,\n dataChannel,\n uploadChannel\n };\n}\n\n/**\n * Disable input stream downscaling\n * @private\n */\nasync function disableInputStreamDownscaling(pc: RTCPeerConnection): Promise<void> {\n const sender = pc.getSenders().find(s => s.track && s.track.kind === \"video\");\n if (!sender) return;\n\n const params = sender.getParameters();\n params.encodings = params.encodings || [{}];\n params.encodings[0].scaleResolutionDownBy = 1;\n\n try {\n await sender.setParameters(params);\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to set encoding parameters:\", err);\n }\n}\n\n/**\n * Helper to wait for datachannel to open\n */\nfunction waitForChannelOpen(channel: RTCDataChannel, timeoutMs = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n if (channel.readyState === \"open\") {\n resolve();\n return;\n }\n\n const openHandler = () => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n clearTimeout(timeout);\n resolve();\n };\n\n const errorHandler = () => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n clearTimeout(timeout);\n reject(new Error(\"Datachannel error\"));\n };\n\n const timeout = setTimeout(() => {\n channel.removeEventListener(\"open\", openHandler);\n channel.removeEventListener(\"error\", errorHandler);\n reject(new Error(\"Datachannel open timeout\"));\n }, timeoutMs);\n\n channel.addEventListener(\"open\", openHandler);\n channel.addEventListener(\"error\", errorHandler);\n });\n}\n\n/**\n * WebRTC Connection object\n *\n * Represents an active WebRTC connection to Roboflow for streaming inference\n * or file-based batch processing.\n */\nexport class RFWebRTCConnection {\n private pc: RTCPeerConnection;\n private _localStream?: MediaStream;\n private remoteStreamPromise: Promise<MediaStream>;\n private pipelineId: string | null;\n private apiKey: string | null;\n private dataChannel: RTCDataChannel;\n private reassembler: ChunkReassembler;\n private uploadChannel?: RTCDataChannel;\n private uploader?: FileUploader;\n private onComplete?: () => void;\n\n /** @private */\n constructor(\n pc: RTCPeerConnection,\n remoteStreamPromise: Promise<MediaStream>,\n pipelineId: string | null,\n apiKey: string | null,\n dataChannel: RTCDataChannel,\n options?: {\n localStream?: MediaStream;\n uploadChannel?: RTCDataChannel;\n onData?: (data: any) => void;\n onComplete?: () => void;\n }\n ) {\n this.pc = pc;\n this._localStream = options?.localStream;\n this.remoteStreamPromise = remoteStreamPromise;\n this.pipelineId = pipelineId;\n this.apiKey = apiKey;\n this.dataChannel = dataChannel;\n this.reassembler = new ChunkReassembler();\n this.uploadChannel = options?.uploadChannel;\n this.onComplete = options?.onComplete;\n\n // Set binary mode for datachannel\n this.dataChannel.binaryType = \"arraybuffer\";\n\n const onData = options?.onData;\n\n // Setup data channel event listeners\n if (onData) {\n this.dataChannel.addEventListener(\"message\", (messageEvent: MessageEvent) => {\n try {\n // Handle binary protocol with chunking\n if (messageEvent.data instanceof ArrayBuffer) {\n const { frameId, chunkIndex, totalChunks, payload } = parseBinaryHeader(messageEvent.data);\n const completePayload = this.reassembler.processChunk(frameId, chunkIndex, totalChunks, payload);\n\n if (completePayload) {\n // Decode UTF-8 JSON payload\n const decoder = new TextDecoder(\"utf-8\");\n const jsonString = decoder.decode(completePayload);\n const data = JSON.parse(jsonString);\n onData(data);\n }\n } else {\n // Fallback for string messages (shouldn't happen with new protocol)\n const data = JSON.parse(messageEvent.data);\n onData(data);\n }\n } catch (err) {\n console.error(\"[RFWebRTC] Failed to parse data channel message:\", err);\n }\n });\n\n this.dataChannel.addEventListener(\"error\", (error) => {\n console.error(\"[RFWebRTC] Data channel error:\", error);\n });\n }\n\n // Handle channel close - call onComplete when processing finishes\n this.dataChannel.addEventListener(\"close\", () => {\n this.reassembler.clear();\n if (this.onComplete) {\n this.onComplete();\n }\n });\n }\n\n /**\n * Get the remote stream (processed video from Roboflow)\n *\n * @returns Promise resolving to the remote MediaStream\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * const remoteStream = await conn.remoteStream();\n * videoElement.srcObject = remoteStream;\n * ```\n */\n async remoteStream(): Promise<MediaStream> {\n return await this.remoteStreamPromise;\n }\n\n /**\n * Get the local stream (original camera)\n *\n * @returns The local MediaStream, or undefined if using file upload mode\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * const localStream = conn.localStream();\n * if (localStream) {\n * videoElement.srcObject = localStream;\n * }\n * ```\n */\n localStream(): MediaStream | undefined {\n return this._localStream;\n }\n\n /**\n * Cleanup and close connection\n *\n * Terminates the pipeline on Roboflow, closes the peer connection,\n * and stops the local media stream (if applicable).\n *\n * @returns Promise that resolves when cleanup is complete\n *\n * @example\n * ```typescript\n * const conn = await useStream({ ... });\n * // ... use connection ...\n * await conn.cleanup(); // Clean up when done\n * ```\n */\n async cleanup(): Promise<void> {\n // Cancel any ongoing upload\n if (this.uploader) {\n this.uploader.cancel();\n }\n\n // Clear pending chunks\n this.reassembler.clear();\n\n // Terminate pipeline\n if (this.pipelineId && this.apiKey) {\n try {\n const client = InferenceHTTPClient.init({ apiKey: this.apiKey });\n await client.terminatePipeline({ pipelineId: this.pipelineId });\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to terminate pipeline:\", err);\n }\n }\n\n // Close peer connection\n if (this.pc && this.pc.connectionState !== \"closed\") {\n this.pc.close();\n }\n\n // Stop local stream if present\n if (this._localStream) {\n stopStream(this._localStream);\n }\n }\n\n /**\n * Start uploading a file through the connection\n *\n * @param file - The file to upload\n * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)\n * @returns Promise that resolves when upload is complete\n * @throws Error if no upload channel is available\n *\n * @example\n * ```typescript\n * await connection.startUpload(videoFile, (uploaded, total) => {\n * console.log(`Upload progress: ${(uploaded / total * 100).toFixed(1)}%`);\n * });\n * ```\n */\n async startUpload(file: File, onProgress?: (bytesUploaded: number, totalBytes: number) => void): Promise<void> {\n if (!this.uploadChannel) {\n throw new Error(\"No upload channel available. This connection was not created for file uploads.\");\n }\n\n // Wait for upload channel to open\n await waitForChannelOpen(this.uploadChannel);\n\n this.uploader = new FileUploader(file, this.uploadChannel);\n await this.uploader.upload(onProgress);\n }\n\n /**\n * Cancel any ongoing file upload\n */\n cancelUpload(): void {\n if (this.uploader) {\n this.uploader.cancel();\n }\n }\n\n /**\n * Reconfigure pipeline outputs at runtime\n *\n * Dynamically change stream and data outputs without restarting the connection.\n * Set a field to `null` to leave it unchanged, or to `null` value to enable all outputs,\n * or to `[]` to disable/auto-detect.\n *\n * @param config - Output configuration\n * @param config.streamOutput - Stream output names (null = unchanged, [] = auto-detect, [\"name\"] = specific output)\n * @param config.dataOutput - Data output names (null = unchanged, [] = disable, [\"name\"] = specific outputs, null value = all outputs)\n *\n * @example\n * ```typescript\n * // Change to different stream output\n * connection.reconfigureOutputs({\n * streamOutput: [\"annotated_image\"],\n * dataOutput: null // unchanged\n * });\n *\n * // Enable all data outputs\n * connection.reconfigureOutputs({\n * streamOutput: null, // unchanged\n * dataOutput: null // null value = all outputs\n * });\n *\n * // Disable all data outputs\n * connection.reconfigureOutputs({\n * streamOutput: null, // unchanged\n * dataOutput: [] // empty array = disable\n * });\n * ```\n */\n reconfigureOutputs(config: { streamOutput?: string[] | null; dataOutput?: string[] | null }): void {\n const message: any = {};\n\n if (config.streamOutput !== undefined) {\n message.stream_output = config.streamOutput;\n }\n\n if (config.dataOutput !== undefined) {\n message.data_output = config.dataOutput;\n }\n\n this.sendData(message);\n }\n\n /**\n * Send data through the data channel\n * @private\n */\n private sendData(data: any): void {\n if (this.dataChannel.readyState !== \"open\") {\n console.warn(\"[RFWebRTC] Data channel is not open. Current state:\", this.dataChannel.readyState);\n return;\n }\n\n try {\n const message = typeof data === \"string\" ? data : JSON.stringify(data);\n this.dataChannel.send(message);\n } catch (err) {\n console.error(\"[RFWebRTC] Failed to send data:\", err);\n }\n }\n}\n\n/**\n * Internal base function for establishing WebRTC connection\n * Used by both useStream and useVideoFile\n * @private\n */\ninterface BaseUseStreamParams {\n source?: MediaStream | File;\n rtspUrl?: string;\n connector: Connector;\n wrtcParams: WebRTCParams;\n onData?: (data: WebRTCOutputData) => void;\n onComplete?: () => void;\n onFileUploadProgress?: (bytesUploaded: number, totalBytes: number) => void;\n options?: UseStreamOptions;\n}\n\nasync function baseUseStream({\n source,\n rtspUrl,\n connector,\n wrtcParams,\n onData,\n onComplete,\n onFileUploadProgress,\n options = {}\n}: BaseUseStreamParams): Promise<RFWebRTCConnection> {\n // Validate connector\n if (!connector || typeof connector.connectWrtc !== \"function\") {\n throw new Error(\"connector must have a connectWrtc method\");\n }\n\n // Determine source type\n const isRtsp = !!rtspUrl;\n const isFile = !isRtsp && source instanceof File;\n const localStream = !isRtsp && !isFile && source ? (source as MediaStream) : undefined;\n const file = isFile ? (source as File) : undefined;\n\n // Step 1: Determine ICE servers to use\n // Priority: 1) User-provided in wrtcParams, 2) From connector.getIceServers(), 3) Defaults\n let iceServers = wrtcParams.iceServers;\n if ((!iceServers || iceServers.length === 0) && connector.getIceServers) {\n try {\n const turnConfig = await connector.getIceServers();\n if (turnConfig && turnConfig.length > 0) {\n iceServers = turnConfig;\n console.log(\"[RFWebRTC] Using TURN servers from connector\");\n }\n } catch (err) {\n console.warn(\"[RFWebRTC] Failed to fetch TURN config, using defaults:\", err);\n }\n }\n\n // Step 2: Prepare peer connection and create offer\n const { pc, offer, remoteStreamPromise, dataChannel, uploadChannel } = await preparePeerConnection(\n localStream,\n file,\n iceServers,\n rtspUrl\n );\n\n // Update wrtcParams with resolved iceServers so server also uses them\n // For file uploads, default to batch mode (realtimeProcessing: false)\n // For RTSP, default to realtime processing\n const resolvedWrtcParams = {\n ...wrtcParams,\n iceServers: iceServers,\n realtimeProcessing: wrtcParams.realtimeProcessing ?? !isFile,\n rtspUrl: rtspUrl\n };\n\n // Step 3: Call connector.connectWrtc to exchange SDP and get answer\n const answer = await connector.connectWrtc(\n { sdp: offer.sdp!, type: offer.type! },\n resolvedWrtcParams\n );\n\n // API returns sdp and type at root level\n const sdpAnswer = { sdp: answer.sdp, type: answer.type } as RTCSessionDescriptionInit;\n\n if (!sdpAnswer?.sdp || !sdpAnswer?.type) {\n console.error(\"[RFWebRTC] Invalid answer from server:\", answer);\n throw new Error(\"connector.connectWrtc must return answer with sdp and type\");\n }\n\n const pipelineId = answer?.context?.pipeline_id || null;\n\n // Step 4: Set remote description\n await pc.setRemoteDescription(sdpAnswer);\n\n // Step 5: Wait for connection to establish\n await new Promise<void>((resolve, reject) => {\n const checkState = () => {\n if (pc.connectionState === \"connected\") {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n resolve();\n } else if (pc.connectionState === \"failed\") {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n reject(new Error(\"WebRTC connection failed\"));\n }\n };\n\n pc.addEventListener(\"connectionstatechange\", checkState);\n checkState(); // Check immediately in case already connected\n\n // Timeout after 30 seconds\n setTimeout(() => {\n pc.removeEventListener(\"connectionstatechange\", checkState);\n reject(new Error(\"WebRTC connection timeout after 30s\"));\n }, 30000);\n });\n\n // Step 6: Optimize quality for MediaStream (disable downsampling by default)\n if (localStream) {\n const shouldDisableDownscaling = options.disableInputStreamDownscaling !== false;\n if (shouldDisableDownscaling) {\n await disableInputStreamDownscaling(pc);\n }\n }\n\n // Get apiKey from connector if available (for cleanup)\n const apiKey = connector._apiKey || null;\n\n // Step 7: Create connection object\n const connection = new RFWebRTCConnection(\n pc,\n remoteStreamPromise,\n pipelineId,\n apiKey,\n dataChannel,\n {\n localStream,\n uploadChannel,\n onData,\n onComplete\n }\n );\n\n // Step 8: Start file upload if applicable (runs in background)\n if (file && uploadChannel) {\n connection.startUpload(file, onFileUploadProgress).catch(err => {\n console.error(\"[RFWebRTC] Upload error:\", err);\n });\n }\n\n return connection;\n}\n\n/**\n * Main function to establish WebRTC streaming connection\n *\n * Creates a WebRTC connection to Roboflow for real-time inference on video streams.\n *\n * @param params - Connection parameters\n * @returns Promise resolving to RFWebRTCConnection\n *\n * @example\n * ```typescript\n * import { useStream } from 'inferencejs/webrtc';\n * import { connectors } from 'inferencejs/api';\n * import { useCamera } from 'inferencejs/streams';\n *\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const stream = await useCamera({ video: { facingMode: { ideal: \"environment\" } } });\n * const conn = await useStream({\n * source: stream,\n * connector,\n * wrtcParams: {\n * workflowSpec: {\n * // Your workflow specification\n * },\n * imageInputName: \"image\",\n * streamOutputNames: [\"output\"],\n * dataOutputNames: [\"predictions\"]\n * },\n * onData: (data) => {\n * console.log(\"Inference results:\", data);\n * }\n * });\n *\n * const remoteStream = await conn.remoteStream();\n * videoElement.srcObject = remoteStream;\n * ```\n */\nexport async function useStream({\n source,\n connector,\n wrtcParams,\n onData,\n options = {}\n}: UseStreamParams): Promise<RFWebRTCConnection> {\n if (source instanceof File) {\n throw new Error(\"useStream requires a MediaStream. Use useVideoFile for File uploads.\");\n }\n\n return baseUseStream({\n source,\n connector,\n wrtcParams,\n onData,\n options\n });\n}\n\n/**\n * Parameters for useVideoFile function\n */\nexport interface UseVideoFileParams {\n /** The video file to upload */\n file: File;\n /** Connector for WebRTC signaling */\n connector: Connector;\n /** WebRTC parameters for the workflow */\n wrtcParams: WebRTCParams;\n /** Callback for inference results */\n onData?: (data: WebRTCOutputData) => void;\n /** Callback for upload progress */\n onUploadProgress?: (bytesUploaded: number, totalBytes: number) => void;\n /** Callback when processing completes (datachannel closes) */\n onComplete?: () => void;\n}\n\n/**\n * Upload a video file for batch inference processing\n *\n * Creates a WebRTC connection to Roboflow for uploading a video file\n * and receiving inference results. The file is uploaded via datachannel\n * with intelligent backpressure handling.\n *\n * @param params - Connection parameters\n * @returns Promise resolving to RFWebRTCConnection\n *\n * @example\n * ```typescript\n * import { connectors, webrtc } from '@roboflow/inference-sdk';\n *\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const connection = await webrtc.useVideoFile({\n * file: videoFile,\n * connector,\n * wrtcParams: {\n * workflowSpec: { ... },\n * imageInputName: \"image\",\n * dataOutputNames: [\"predictions\"]\n * },\n * onData: (data) => {\n * console.log(\"Inference results:\", data);\n * if (data.processing_complete) {\n * console.log(\"Processing complete!\");\n * }\n * },\n * onUploadProgress: (uploaded, total) => {\n * console.log(`Upload: ${(uploaded / total * 100).toFixed(1)}%`);\n * }\n * });\n *\n * // When done\n * await connection.cleanup();\n * ```\n */\nexport async function useVideoFile({\n file,\n connector,\n wrtcParams,\n onData,\n onUploadProgress,\n onComplete\n}: UseVideoFileParams): Promise<RFWebRTCConnection> {\n return baseUseStream({\n source: file,\n connector,\n wrtcParams: {\n ...wrtcParams,\n realtimeProcessing: wrtcParams.realtimeProcessing ?? true\n },\n onData,\n onComplete,\n onFileUploadProgress: onUploadProgress\n });\n}\n\n/**\n * Parameters for useRtspStream function\n */\nexport interface UseRtspStreamParams {\n /** RTSP URL for server-side video capture (e.g., \"rtsp://camera.local/stream\") */\n rtspUrl: string;\n /** Connector for WebRTC signaling */\n connector: Connector;\n /** WebRTC parameters for the workflow */\n wrtcParams: WebRTCParams;\n /** Callback for inference results */\n onData?: (data: WebRTCOutputData) => void;\n}\n\n/**\n * Connect to an RTSP stream for inference processing\n *\n * Creates a WebRTC connection where the server captures video from an RTSP URL\n * and sends processed video back to the client. This is a receive-only mode -\n * no video is sent from the browser to the server.\n *\n * @param params - Connection parameters\n * @returns Promise resolving to RFWebRTCConnection\n *\n * @example\n * ```typescript\n * import { connectors, webrtc } from '@roboflow/inference-sdk';\n *\n * const connector = connectors.withApiKey(\"your-api-key\");\n * const connection = await webrtc.useRtspStream({\n * rtspUrl: \"rtsp://camera.local/stream\",\n * connector,\n * wrtcParams: {\n * workflowSpec: { ... },\n * imageInputName: \"image\",\n * dataOutputNames: [\"predictions\"]\n * },\n * onData: (data) => {\n * console.log(\"Inference results:\", data);\n * }\n * });\n *\n * // Get processed video stream from server\n * const remoteStream = await connection.remoteStream();\n * videoElement.srcObject = remoteStream;\n *\n * // When done\n * await connection.cleanup();\n * ```\n */\nexport async function useRtspStream({\n rtspUrl,\n connector,\n wrtcParams,\n onData\n}: UseRtspStreamParams): Promise<RFWebRTCConnection> {\n // Validate RTSP URL format\n if (!rtspUrl.startsWith(\"rtsp://\") && !rtspUrl.startsWith(\"rtsps://\")) {\n throw new Error(\"Invalid RTSP URL: must start with rtsp:// or rtsps://\");\n }\n\n return baseUseStream({\n rtspUrl,\n connector,\n wrtcParams,\n onData\n });\n}\n"],"names":["RF_API_BASE_URL","_a","ROBOFLOW_SERVERLESS_URLS","InferenceHTTPClient","apiKey","serverUrl","__publicField","offer","workflowSpec","workspaceName","workflowId","config","hasWorkflowSpec","hasWorkspaceIdentifier","imageInputName","streamOutputNames","dataOutputNames","threadPoolWorkers","workflowsParameters","iceServers","processingTimeout","requestedPlan","requestedRegion","realtimeProcessing","rtspUrl","workflowConfiguration","payload","response","errorText","pipelineId","turnConfig","iceServersRaw","server","err","connectors","options","client","wrtcParams","proxyUrl","turnConfigUrl","useCamera","constraints","stream","t","stopStream","track","CHUNK_SIZE","BUFFER_LIMIT","POLL_INTERVAL","sleep","ms","resolve","FileUploader","file","channel","onProgress","totalBytes","chunkIndex","start","end","chunkBlob","chunkData","message","view","HEADER_SIZE","ChunkReassembler","frameId","totalChunks","frame","totalLength","sum","chunk","result","offset","parseBinaryHeader","buffer","waitForIceGathering","pc","timeoutMs","hasSrflx","candidateHandler","event","check","reject","setupRemoteStreamListener","DEFAULT_ICE_SERVERS","preparePeerConnection","localStream","customIceServers","remoteStreamPromise","dataChannel","uploadChannel","disableInputStreamDownscaling","sender","s","params","waitForChannelOpen","openHandler","errorHandler","timeout","RFWebRTCConnection","onData","messageEvent","completePayload","jsonString","data","error","baseUseStream","source","connector","onComplete","onFileUploadProgress","isRtsp","isFile","resolvedWrtcParams","answer","sdpAnswer","checkState","connection","useStream","useVideoFile","onUploadProgress","useRtspStream"],"mappings":"oZAIA,MAAMA,EAAkB,OAAO,QAAY,OAAeC,EAAA,QAAQ,MAAR,MAAAA,EAAa,iBACnE,QAAQ,IAAI,gBACZ,2BAKEC,EAA2B,CAC/B,iCACF,EAmJO,MAAMC,CAAoB,CAQvB,YAAYC,EAAgBC,EAAoB,kCAAmC,CAPnFC,EAAA,eACAA,EAAA,kBAON,KAAK,OAASF,EACd,KAAK,UAAYC,CACnB,CAEA,OAAO,KAAK,CAAE,OAAAD,EAAQ,UAAAC,GAA0E,CAC9F,GAAI,CAACD,EACH,MAAM,IAAI,MAAM,oBAAoB,EAEtC,OAAO,IAAID,EAAoBC,EAAQC,CAAS,CAClD,CA2BA,MAAM,uBAAuB,CAC3B,MAAAE,EACA,aAAAC,EACA,cAAAC,EACA,WAAAC,EACA,OAAAC,EAAS,CAAA,CAAC,EAOsB,CAChC,GAAI,CAACJ,GAAS,CAACA,EAAM,KAAO,CAACA,EAAM,KACjC,MAAM,IAAI,MAAM,qCAAqC,EAIvD,MAAMK,EAAkB,CAAC,CAACJ,EACpBK,EAAyB,CAAC,EAAEJ,GAAiBC,GAEnD,GAAI,CAACE,GAAmB,CAACC,EACvB,MAAM,IAAI,MAAM,iEAAiE,EAEnF,GAAID,GAAmBC,EACrB,MAAM,IAAI,MAAM,uEAAuE,EAGzF,KAAM,CACJ,eAAAC,EAAiB,QACjB,kBAAAC,EAAoB,CAAA,EACpB,gBAAAC,EAAkB,CAAC,QAAQ,EAC3B,kBAAAC,EAAoB,EACpB,oBAAAC,EAAsB,CAAA,EACtB,WAAAC,EACA,kBAAAC,EACA,cAAAC,EACA,gBAAAC,EACA,mBAAAC,EAAqB,GACrB,QAAAC,CAAA,EACEb,EAGEc,EAA6B,CACjC,KAAM,wBACN,iBAAkBX,EAClB,qBAAsBI,EACtB,8BAA+BD,EAC/B,iCAAkC,GAClC,0BAA2B,gBAAA,EAGzBL,EACFa,EAAsB,uBAAyBjB,GAE/CiB,EAAsB,eAAiBhB,EACvCgB,EAAsB,YAAcf,GAGtC,MAAMgB,EAA+B,CACnC,uBAAwBD,EACxB,QAAS,KAAK,OACd,2BAA4BF,EAC5B,aAAc,CACZ,IAAKhB,EAAM,IACX,KAAMA,EAAM,IAAA,EAEd,cAAeY,EAAa,CAAE,WAAAA,CAAA,EAAe,KAC7C,cAAeJ,EACf,YAAaC,CAAA,EAIXI,IAAsB,SACxBM,EAAQ,mBAAqBN,GAE3BC,IAAkB,SACpBK,EAAQ,eAAiBL,GAEvBC,IAAoB,SACtBI,EAAQ,iBAAmBJ,GAGzBE,IACFE,EAAQ,SAAWF,GAErB,MAAMG,EAAW,MAAM,MAAM,GAAG,KAAK,SAAS,4BAA6B,CACzE,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAUD,CAAO,CAAA,CAC7B,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,OAAO,MAAM,IAAM,EAAE,EACtD,MAAM,IAAI,MAAM,oCAAoCA,EAAS,MAAM,MAAMC,CAAS,EAAE,CACtF,CAIA,OAFe,MAAMD,EAAS,KAAA,CAGhC,CAEA,MAAM,kBAAkB,CAAE,WAAAE,GAAqD,CAC7E,GAAI,CAACA,EACH,MAAM,IAAI,MAAM,wBAAwB,EAG1C,MAAM,MACJ,GAAG,KAAK,SAAS,wBAAwBA,CAAU,sBAAsB,KAAK,MAAM,GACpF,CACE,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,CAAmB,CAChD,CAEJ,CAkBA,MAAM,iBAAwD,CAE5D,GAAI,CAAC3B,EAAyB,SAAS,KAAK,SAAS,EACnD,OAAO,KAET,GAAI,CACF,MAAMyB,EAAW,MAAM,MACrB,GAAG3B,CAAe,+BAA+B,KAAK,MAAM,GAC5D,CACE,OAAQ,MACR,QAAS,CAAE,eAAgB,kBAAA,CAAmB,CAChD,EAGF,GAAI,CAAC2B,EAAS,GACZ,eAAQ,KAAK,2CAA2CA,EAAS,MAAM,mBAAmB,EACnF,KAGT,MAAMG,EAAa,MAAMH,EAAS,KAAA,EAMlC,IAAII,EAEJ,GAAI,MAAM,QAAQD,CAAU,EAE1BC,EAAgBD,UACPA,EAAW,YAAc,MAAM,QAAQA,EAAW,UAAU,EAErEC,EAAgBD,EAAW,mBAClBA,EAAW,KAEpBC,EAAgB,CAACD,CAAU,MAE3B,gBAAQ,KAAK,uDAAuD,EAC7D,KAUT,OANyCC,EAAc,IAAKC,IAAiB,CAC3E,KAAM,MAAM,QAAQA,EAAO,IAAI,EAAIA,EAAO,KAAO,CAACA,EAAO,IAAI,EAC7D,SAAUA,EAAO,SACjB,WAAYA,EAAO,UAAA,EACnB,CAGJ,OAASC,EAAK,CACZ,eAAQ,KAAK,yCAA0CA,CAAG,EACnD,IACT,CACF,CACF,CAKO,MAAMC,EAAa,CAmBxB,WAAW9B,EAAgB+B,EAAkC,GAAe,CAC1E,KAAM,CAAE,UAAA9B,GAAc8B,EAGlB,OAAO,OAAW,KACpB,QAAQ,KACN,sMAAA,EAMJ,MAAMC,EAASjC,EAAoB,KAAK,CAAE,OAAAC,EAAQ,UAAAC,EAAW,EAE7D,MAAO,CACL,YAAa,MAAOE,EAAoB8B,KACtC,QAAQ,MAAM,aAAcA,CAAU,EACvB,MAAMD,EAAO,uBAAuB,CACjD,MAAA7B,EACA,aAAc8B,EAAW,aACzB,cAAeA,EAAW,cAC1B,WAAYA,EAAW,WACvB,OAAQ,CACN,eAAgBA,EAAW,eAC3B,kBAAmBA,EAAW,kBAC9B,gBAAiBA,EAAW,gBAC5B,kBAAmBA,EAAW,kBAC9B,oBAAqBA,EAAW,oBAChC,WAAYA,EAAW,WACvB,kBAAmBA,EAAW,kBAC9B,cAAeA,EAAW,cAC1B,gBAAiBA,EAAW,gBAC5B,mBAAoBA,EAAW,mBAC/B,QAASA,EAAW,OAAA,CACtB,CACD,GAQH,cAAe,SACN,MAAMD,EAAO,gBAAA,EAItB,QAAShC,EACT,WAAYC,CAAA,CAEhB,EAiEA,aAAaiC,EAAkBH,EAAsC,GAAe,CAClF,KAAM,CAAE,cAAAI,GAAkBJ,EAE1B,MAAO,CACL,YAAa,MAAO5B,EAAoB8B,IAA4D,CAClG,MAAMV,EAAW,MAAM,MAAMW,EAAU,CACrC,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAU,CACnB,MAAA/B,EACA,WAAA8B,CAAA,CACD,CAAA,CACF,EAED,GAAI,CAACV,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,OAAO,MAAM,IAAM,EAAE,EACtD,MAAM,IAAI,MAAM,yBAAyBA,EAAS,MAAM,MAAMC,CAAS,EAAE,CAC3E,CAEA,OAAO,MAAMD,EAAS,KAAA,CACxB,EAMA,cAAeY,EACX,SAAkD,CAChD,GAAI,CACF,MAAMZ,EAAW,MAAM,MAAMY,EAAe,CAC1C,OAAQ,MACR,QAAS,CAAE,eAAgB,kBAAA,CAAmB,CAC/C,EAED,OAAKZ,EAAS,IAKD,MAAMA,EAAS,KAAA,GAChB,YAAc,MALxB,QAAQ,KAAK,sDAAsDA,EAAS,MAAM,GAAG,EAC9E,KAKX,OAASM,EAAK,CACZ,eAAQ,KAAK,oDAAqDA,CAAG,EAC9D,IACT,CACF,EACA,MAAA,CAER,CACF,ECjjBA,eAAsBO,EAAUC,EAAsC,CAAE,MAAO,IAA8B,CAC3G,GAAI,CACF,QAAQ,IAAI,8BAA+BA,CAAW,EACtD,MAAMC,EAAS,MAAM,UAAU,aAAa,aAAaD,CAAW,EACpE,eAAQ,IAAI,yBAA0BC,EAAO,eAAA,EAAiB,IAAI,IAAM,CAAE,GAAI,EAAE,GAAI,MAAO,EAAE,KAAA,EAAQ,CAAC,EAC/FA,CACT,OAAST,EAAK,CACZ,QAAQ,KAAK,mCAAoCA,CAAG,EACpD,MAAMS,EAAS,MAAM,UAAU,aAAa,aAAa,CAAE,MAAO,GAAM,MAAO,GAAO,EACtF,eAAQ,IAAI,8BAA+BA,EAAO,eAAA,EAAiB,IAAIC,IAAM,CAAE,GAAIA,EAAE,GAAI,MAAOA,EAAE,KAAA,EAAQ,CAAC,EACpGD,CACT,CACF,CAEO,SAASE,EAAWF,EAA8C,CACnEA,IACFA,EAAO,YAAY,QAAQG,GAASA,EAAM,MAAM,EAChD,QAAQ,IAAI,4BAA4B,EAE5C,6HC5BMC,EAAa,MACbC,EAAe,OACfC,EAAgB,GAKtB,SAASC,EAAMC,EAA2B,CACxC,OAAO,IAAI,QAAQC,GAAW,WAAWA,EAASD,CAAE,CAAC,CACvD,CAQO,MAAME,CAAa,CAMxB,YAAYC,EAAYC,EAAyB,CALzChD,EAAA,aACAA,EAAA,gBACAA,EAAA,oBACAA,EAAA,iBAAqB,IAG3B,KAAK,KAAO+C,EACZ,KAAK,QAAUC,EACf,KAAK,YAAc,KAAK,KAAKD,EAAK,KAAOP,CAAU,CACrD,CAKA,QAAe,CACb,KAAK,UAAY,EACnB,CAOA,MAAM,OAAOS,EAAiF,CAC5F,MAAMC,EAAa,KAAK,KAAK,KAE7B,QAASC,EAAa,EAAGA,EAAa,KAAK,YAAaA,IAAc,CAEpE,GAAI,KAAK,UACP,MAAM,IAAI,MAAM,kBAAkB,EAIpC,GAAI,KAAK,QAAQ,aAAe,OAC9B,MAAM,IAAI,MAAM,0BAA0B,EAI5C,MAAMC,EAAQD,EAAaX,EACrBa,EAAM,KAAK,IAAID,EAAQZ,EAAYU,CAAU,EAC7CI,EAAY,KAAK,KAAK,MAAMF,EAAOC,CAAG,EACtCE,EAAY,IAAI,WAAW,MAAMD,EAAU,aAAa,EAGxDE,EAAU,IAAI,YAAY,EAAID,EAAU,MAAM,EAC9CE,EAAO,IAAI,SAASD,CAAO,EAMjC,IALAC,EAAK,UAAU,EAAGN,EAAY,EAAI,EAClCM,EAAK,UAAU,EAAG,KAAK,YAAa,EAAI,EACxC,IAAI,WAAWD,EAAS,CAAC,EAAE,IAAID,CAAS,EAGjC,KAAK,QAAQ,eAAiBd,GAAc,CACjD,GAAI,KAAK,QAAQ,aAAe,OAC9B,MAAM,IAAI,MAAM,0BAA0B,EAE5C,MAAME,EAAMD,CAAa,CAC3B,CAGA,KAAK,QAAQ,KAAKc,CAAO,EAGrBP,GACFA,EAAWI,EAAKH,CAAU,CAE9B,CACF,CACF,CC/EA,MAAMQ,EAAc,GAKb,MAAMC,CAAiB,CAAvB,cACG3D,EAAA,yBAGC,KAKT,aAAa4D,EAAiBT,EAAoBU,EAAqBzC,EAAwC,CAE7G,GAAIyC,IAAgB,EAClB,OAAOzC,EAIJ,KAAK,cAAc,IAAIwC,CAAO,GACjC,KAAK,cAAc,IAAIA,EAAS,CAC9B,WAAY,IACZ,YAAAC,CAAA,CACD,EAGH,MAAMC,EAAQ,KAAK,cAAc,IAAIF,CAAO,EAI5C,GAHAE,EAAM,OAAO,IAAIX,EAAY/B,CAAO,EAGhC0C,EAAM,OAAO,OAASD,EAAa,CAErC,MAAME,EAAc,MAAM,KAAKD,EAAM,OAAO,OAAA,CAAQ,EAAE,OAAO,CAACE,EAAKC,IAAUD,EAAMC,EAAM,OAAQ,CAAC,EAC5FC,EAAS,IAAI,WAAWH,CAAW,EACzC,IAAII,EAAS,EAEb,QAAS,EAAI,EAAG,EAAIN,EAAa,IAAK,CACpC,MAAMI,EAAQH,EAAM,OAAO,IAAI,CAAC,EAChCI,EAAO,IAAID,EAAOE,CAAM,EACxBA,GAAUF,EAAM,MAClB,CAEA,YAAK,cAAc,OAAOL,CAAO,EAC1BM,CACT,CAEA,OAAO,IACT,CAKA,OAAc,CACZ,KAAK,cAAc,MAAA,CACrB,CACF,CAKO,SAASE,EAAkBC,EAAwG,CACxI,MAAMZ,EAAO,IAAI,SAASY,CAAM,EAC1BT,EAAUH,EAAK,UAAU,EAAG,EAAI,EAChCN,EAAaM,EAAK,UAAU,EAAG,EAAI,EACnCI,EAAcJ,EAAK,UAAU,EAAG,EAAI,EACpCrC,EAAU,IAAI,WAAWiD,EAAQX,CAAW,EAElD,MAAO,CAAE,QAAAE,EAAS,WAAAT,EAAY,YAAAU,EAAa,QAAAzC,CAAA,CAC7C,CAcA,eAAekD,EAAoBC,EAAuBC,EAAY,IAAqB,CACzF,GAAID,EAAG,oBAAsB,WAAY,OAEzC,IAAIE,EAAW,GAGf,MAAMC,EAAoBC,GAAqC,CACzDA,EAAM,WAAaA,EAAM,UAAU,OAAS,UAC9CF,EAAW,GAEf,EACAF,EAAG,iBAAiB,eAAgBG,CAAgB,EAEpD,GAAI,CACF,MAAM,QAAQ,KAAK,CACjB,IAAI,QAAc7B,GAAW,CAC3B,MAAM+B,EAAQ,IAAM,CACdL,EAAG,oBAAsB,aAC3BA,EAAG,oBAAoB,0BAA2BK,CAAK,EACvD/B,EAAA,EAEJ,EACA0B,EAAG,iBAAiB,0BAA2BK,CAAK,CACtD,CAAC,EACD,IAAI,QAAc,CAAC/B,EAASgC,IAAW,CACrC,WAAW,IAAM,CACVJ,EAIH5B,EAAA,GAHA,QAAQ,MAAM,6DAA6D,EAC3EgC,EAAO,IAAI,MAAM,+CAA+C,CAAC,EAIrE,EAAGL,CAAS,CACd,CAAC,CAAA,CACF,CACH,QAAA,CACED,EAAG,oBAAoB,eAAgBG,CAAgB,CACzD,CACF,CAEA,SAASI,EAA0BP,EAA6C,CAC9E,OAAO,IAAI,QAAS1B,GAAY,CAC9B0B,EAAG,iBAAiB,QAAUI,GAAyB,CACjDA,EAAM,SAAWA,EAAM,QAAQ,CAAC,GAClC9B,EAAQ8B,EAAM,QAAQ,CAAC,CAAC,CAE5B,CAAC,CACH,CAAC,CACH,CAEA,MAAMI,EAA4C,CAChD,CAAE,KAAM,CAAC,8BAA8B,CAAA,CACzC,EAEA,eAAeC,EACbC,EACAlC,EACAmC,EACAhE,EAOC,CAOD,GAFoB,CAHG,CAAC,CAAC+D,EACT,CAAC,CAAClC,EACC,CAAC,CAAC7B,CACmC,EAAE,OAAO,OAAO,EAAE,SAEtD,EAClB,MAAM,IAAI,MAAM,+DAA+D,EAGjF,MAAML,EAAaqE,GAAoBH,EAEjCR,EAAK,IAAI,kBAAkB,CAC/B,WAAA1D,CAAA,CACD,EAGD,GAAI,CACF0D,EAAG,eAAe,QAAS,CAAE,UAAW,WAAY,CACtD,OAAS5C,EAAK,CACZ,QAAQ,KAAK,wCAAyCA,CAAG,CAC3D,CAEIsD,GAEFA,EAAY,eAAA,EAAiB,QAAQ1C,GAAS,CAC5C,GAAI,CAEFA,EAAM,YAAc,QACtB,MAAY,CAEZ,CACAgC,EAAG,SAAShC,EAAO0C,CAAW,CAChC,CAAC,EAKH,MAAME,EAAsBL,EAA0BP,CAAE,EAGlDa,EAAcb,EAAG,kBAAkB,YAAa,CACpD,QAAS,EAAA,CACV,EAGD,IAAIc,EACAtC,IACFsC,EAAgBd,EAAG,kBAAkB,cAAc,GAIrD,MAAMtE,EAAQ,MAAMsE,EAAG,YAAA,EACvB,aAAMA,EAAG,oBAAoBtE,CAAK,EAGlC,MAAMqE,EAAoBC,CAAE,EAErB,CACL,GAAAA,EACA,MAAOA,EAAG,iBACV,oBAAAY,EACA,YAAAC,EACA,cAAAC,CAAA,CAEJ,CAMA,eAAeC,EAA8Bf,EAAsC,CACjF,MAAMgB,EAAShB,EAAG,WAAA,EAAa,KAAKiB,GAAKA,EAAE,OAASA,EAAE,MAAM,OAAS,OAAO,EAC5E,GAAI,CAACD,EAAQ,OAEb,MAAME,EAASF,EAAO,cAAA,EACtBE,EAAO,UAAYA,EAAO,WAAa,CAAC,CAAA,CAAE,EAC1CA,EAAO,UAAU,CAAC,EAAE,sBAAwB,EAE5C,GAAI,CACF,MAAMF,EAAO,cAAcE,CAAM,CACnC,OAAS9D,EAAK,CACZ,QAAQ,KAAK,gDAAiDA,CAAG,CACnE,CACF,CAKA,SAAS+D,EAAmB1C,EAAyBwB,EAAY,IAAsB,CACrF,OAAO,IAAI,QAAQ,CAAC3B,EAASgC,IAAW,CACtC,GAAI7B,EAAQ,aAAe,OAAQ,CACjCH,EAAA,EACA,MACF,CAEA,MAAM8C,EAAc,IAAM,CACxB3C,EAAQ,oBAAoB,OAAQ2C,CAAW,EAC/C3C,EAAQ,oBAAoB,QAAS4C,CAAY,EACjD,aAAaC,CAAO,EACpBhD,EAAA,CACF,EAEM+C,EAAe,IAAM,CACzB5C,EAAQ,oBAAoB,OAAQ2C,CAAW,EAC/C3C,EAAQ,oBAAoB,QAAS4C,CAAY,EACjD,aAAaC,CAAO,EACpBhB,EAAO,IAAI,MAAM,mBAAmB,CAAC,CACvC,EAEMgB,EAAU,WAAW,IAAM,CAC/B7C,EAAQ,oBAAoB,OAAQ2C,CAAW,EAC/C3C,EAAQ,oBAAoB,QAAS4C,CAAY,EACjDf,EAAO,IAAI,MAAM,0BAA0B,CAAC,CAC9C,EAAGL,CAAS,EAEZxB,EAAQ,iBAAiB,OAAQ2C,CAAW,EAC5C3C,EAAQ,iBAAiB,QAAS4C,CAAY,CAChD,CAAC,CACH,CAQO,MAAME,CAAmB,CAa9B,YACEvB,EACAY,EACA5D,EACAzB,EACAsF,EACAvD,EAMA,CAxBM7B,EAAA,WACAA,EAAA,qBACAA,EAAA,4BACAA,EAAA,mBACAA,EAAA,eACAA,EAAA,oBACAA,EAAA,oBACAA,EAAA,sBACAA,EAAA,iBACAA,EAAA,mBAgBN,KAAK,GAAKuE,EACV,KAAK,aAAe1C,GAAA,YAAAA,EAAS,YAC7B,KAAK,oBAAsBsD,EAC3B,KAAK,WAAa5D,EAClB,KAAK,OAASzB,EACd,KAAK,YAAcsF,EACnB,KAAK,YAAc,IAAIzB,EACvB,KAAK,cAAgB9B,GAAA,YAAAA,EAAS,cAC9B,KAAK,WAAaA,GAAA,YAAAA,EAAS,WAG3B,KAAK,YAAY,WAAa,cAE9B,MAAMkE,EAASlE,GAAA,YAAAA,EAAS,OAGpBkE,IACF,KAAK,YAAY,iBAAiB,UAAYC,GAA+B,CAC3E,GAAI,CAEF,GAAIA,EAAa,gBAAgB,YAAa,CAC5C,KAAM,CAAE,QAAApC,EAAS,WAAAT,EAAY,YAAAU,EAAa,QAAAzC,GAAYgD,EAAkB4B,EAAa,IAAI,EACnFC,EAAkB,KAAK,YAAY,aAAarC,EAAST,EAAYU,EAAazC,CAAO,EAE/F,GAAI6E,EAAiB,CAGnB,MAAMC,EADU,IAAI,YAAY,OAAO,EACZ,OAAOD,CAAe,EAC3CE,EAAO,KAAK,MAAMD,CAAU,EAClCH,EAAOI,CAAI,CACb,CACF,KAAO,CAEL,MAAMA,EAAO,KAAK,MAAMH,EAAa,IAAI,EACzCD,EAAOI,CAAI,CACb,CACF,OAASxE,EAAK,CACZ,QAAQ,MAAM,mDAAoDA,CAAG,CACvE,CACF,CAAC,EAED,KAAK,YAAY,iBAAiB,QAAUyE,GAAU,CACpD,QAAQ,MAAM,iCAAkCA,CAAK,CACvD,CAAC,GAIH,KAAK,YAAY,iBAAiB,QAAS,IAAM,CAC/C,KAAK,YAAY,MAAA,EACb,KAAK,YACP,KAAK,WAAA,CAET,CAAC,CACH,CAcA,MAAM,cAAqC,CACzC,OAAO,MAAM,KAAK,mBACpB,CAgBA,aAAuC,CACrC,OAAO,KAAK,YACd,CAiBA,MAAM,SAAyB,CAU7B,GARI,KAAK,UACP,KAAK,SAAS,OAAA,EAIhB,KAAK,YAAY,MAAA,EAGb,KAAK,YAAc,KAAK,OAC1B,GAAI,CAEF,MADevG,EAAoB,KAAK,CAAE,OAAQ,KAAK,OAAQ,EAClD,kBAAkB,CAAE,WAAY,KAAK,WAAY,CAChE,OAAS8B,EAAK,CACZ,QAAQ,KAAK,2CAA4CA,CAAG,CAC9D,CAIE,KAAK,IAAM,KAAK,GAAG,kBAAoB,UACzC,KAAK,GAAG,MAAA,EAIN,KAAK,cACPW,EAAW,KAAK,YAAY,CAEhC,CAiBA,MAAM,YAAYS,EAAYE,EAAiF,CAC7G,GAAI,CAAC,KAAK,cACR,MAAM,IAAI,MAAM,gFAAgF,EAIlG,MAAMyC,EAAmB,KAAK,aAAa,EAE3C,KAAK,SAAW,IAAI5C,EAAaC,EAAM,KAAK,aAAa,EACzD,MAAM,KAAK,SAAS,OAAOE,CAAU,CACvC,CAKA,cAAqB,CACf,KAAK,UACP,KAAK,SAAS,OAAA,CAElB,CAkCA,mBAAmB5C,EAAgF,CACjG,MAAMmD,EAAe,CAAA,EAEjBnD,EAAO,eAAiB,SAC1BmD,EAAQ,cAAgBnD,EAAO,cAG7BA,EAAO,aAAe,SACxBmD,EAAQ,YAAcnD,EAAO,YAG/B,KAAK,SAASmD,CAAO,CACvB,CAMQ,SAAS2C,EAAiB,CAChC,GAAI,KAAK,YAAY,aAAe,OAAQ,CAC1C,QAAQ,KAAK,sDAAuD,KAAK,YAAY,UAAU,EAC/F,MACF,CAEA,GAAI,CACF,MAAM3C,EAAU,OAAO2C,GAAS,SAAWA,EAAO,KAAK,UAAUA,CAAI,EACrE,KAAK,YAAY,KAAK3C,CAAO,CAC/B,OAAS7B,EAAK,CACZ,QAAQ,MAAM,kCAAmCA,CAAG,CACtD,CACF,CACF,CAkBA,eAAe0E,EAAc,CAC3B,OAAAC,EACA,QAAApF,EACA,UAAAqF,EACA,WAAAxE,EACA,OAAAgE,EACA,WAAAS,EACA,qBAAAC,EACA,QAAA5E,EAAU,CAAA,CACZ,EAAqD,OAEnD,GAAI,CAAC0E,GAAa,OAAOA,EAAU,aAAgB,WACjD,MAAM,IAAI,MAAM,0CAA0C,EAI5D,MAAMG,EAAS,CAAC,CAACxF,EACXyF,EAAS,CAACD,GAAUJ,aAAkB,KACtCrB,EAAc,CAACyB,GAAU,CAACC,GAAUL,EAAUA,EAAyB,OACvEvD,EAAO4D,EAAUL,EAAkB,OAIzC,IAAIzF,EAAakB,EAAW,WAC5B,IAAK,CAAClB,GAAcA,EAAW,SAAW,IAAM0F,EAAU,cACxD,GAAI,CACF,MAAM/E,EAAa,MAAM+E,EAAU,cAAA,EAC/B/E,GAAcA,EAAW,OAAS,IACpCX,EAAaW,EACb,QAAQ,IAAI,8CAA8C,EAE9D,OAASG,EAAK,CACZ,QAAQ,KAAK,0DAA2DA,CAAG,CAC7E,CAIF,KAAM,CAAE,GAAA4C,EAAI,MAAAtE,EAAO,oBAAAkF,EAAqB,YAAAC,EAAa,cAAAC,CAAA,EAAkB,MAAML,EAC3EC,EACAlC,EACAlC,EACAK,CAAA,EAMI0F,EAAqB,CACzB,GAAG7E,EACH,WAAAlB,EACA,mBAAoBkB,EAAW,oBAAsB,CAAC4E,EACtD,QAAAzF,CAAA,EAII2F,EAAS,MAAMN,EAAU,YAC7B,CAAE,IAAKtG,EAAM,IAAM,KAAMA,EAAM,IAAA,EAC/B2G,CAAA,EAIIE,EAAY,CAAE,IAAKD,EAAO,IAAK,KAAMA,EAAO,IAAA,EAElD,GAAI,EAACC,GAAA,MAAAA,EAAW,MAAO,EAACA,GAAA,MAAAA,EAAW,MACjC,cAAQ,MAAM,yCAA0CD,CAAM,EACxD,IAAI,MAAM,4DAA4D,EAG9E,MAAMtF,IAAa5B,EAAAkH,GAAA,YAAAA,EAAQ,UAAR,YAAAlH,EAAiB,cAAe,KAGnD,MAAM4E,EAAG,qBAAqBuC,CAAS,EAGvC,MAAM,IAAI,QAAc,CAACjE,EAASgC,IAAW,CAC3C,MAAMkC,EAAa,IAAM,CACnBxC,EAAG,kBAAoB,aACzBA,EAAG,oBAAoB,wBAAyBwC,CAAU,EAC1DlE,EAAA,GACS0B,EAAG,kBAAoB,WAChCA,EAAG,oBAAoB,wBAAyBwC,CAAU,EAC1DlC,EAAO,IAAI,MAAM,0BAA0B,CAAC,EAEhD,EAEAN,EAAG,iBAAiB,wBAAyBwC,CAAU,EACvDA,EAAA,EAGA,WAAW,IAAM,CACfxC,EAAG,oBAAoB,wBAAyBwC,CAAU,EAC1DlC,EAAO,IAAI,MAAM,qCAAqC,CAAC,CACzD,EAAG,GAAK,CACV,CAAC,EAGGI,GAC+BpD,EAAQ,gCAAkC,IAEzE,MAAMyD,EAA8Bf,CAAE,EAK1C,MAAMzE,EAASyG,EAAU,SAAW,KAG9BS,EAAa,IAAIlB,EACrBvB,EACAY,EACA5D,EACAzB,EACAsF,EACA,CACE,YAAAH,EACA,cAAAI,EACA,OAAAU,EACA,WAAAS,CAAA,CACF,EAIF,OAAIzD,GAAQsC,GACV2B,EAAW,YAAYjE,EAAM0D,CAAoB,EAAE,MAAM9E,GAAO,CAC9D,QAAQ,MAAM,2BAA4BA,CAAG,CAC/C,CAAC,EAGIqF,CACT,CAsCA,eAAsBC,GAAU,CAC9B,OAAAX,EACA,UAAAC,EACA,WAAAxE,EACA,OAAAgE,EACA,QAAAlE,EAAU,CAAA,CACZ,EAAiD,CAC/C,GAAIyE,aAAkB,KACpB,MAAM,IAAI,MAAM,sEAAsE,EAGxF,OAAOD,EAAc,CACnB,OAAAC,EACA,UAAAC,EACA,WAAAxE,EACA,OAAAgE,EACA,QAAAlE,CAAA,CACD,CACH,CA0DA,eAAsBqF,GAAa,CACjC,KAAAnE,EACA,UAAAwD,EACA,WAAAxE,EACA,OAAAgE,EACA,iBAAAoB,EACA,WAAAX,CACF,EAAoD,CAClD,OAAOH,EAAc,CACnB,OAAQtD,EACR,UAAAwD,EACA,WAAY,CACV,GAAGxE,EACH,mBAAoBA,EAAW,oBAAsB,EAAA,EAEvD,OAAAgE,EACA,WAAAS,EACA,qBAAsBW,CAAA,CACvB,CACH,CAoDA,eAAsBC,GAAc,CAClC,QAAAlG,EACA,UAAAqF,EACA,WAAAxE,EACA,OAAAgE,CACF,EAAqD,CAEnD,GAAI,CAAC7E,EAAQ,WAAW,SAAS,GAAK,CAACA,EAAQ,WAAW,UAAU,EAClE,MAAM,IAAI,MAAM,uDAAuD,EAGzE,OAAOmF,EAAc,CACnB,QAAAnF,EACA,UAAAqF,EACA,WAAAxE,EACA,OAAAgE,CAAA,CACD,CACH"}
|
package/dist/inference-api.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ export interface WebRTCWorkerConfig {
|
|
|
32
32
|
* @default true
|
|
33
33
|
*/
|
|
34
34
|
realtimeProcessing?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* RTSP URL for server-side video capture.
|
|
37
|
+
* When provided, the server captures video from this RTSP stream instead of receiving
|
|
38
|
+
* video from the client. Supports credentials in URL format: rtsp://user:pass@host/stream
|
|
39
|
+
* @example "rtsp://camera.local/stream"
|
|
40
|
+
*/
|
|
41
|
+
rtspUrl?: string;
|
|
35
42
|
}
|
|
36
43
|
/**
|
|
37
44
|
* ICE server configuration for WebRTC connections
|
|
@@ -108,6 +115,13 @@ export interface WebRTCParams {
|
|
|
108
115
|
* @default true
|
|
109
116
|
*/
|
|
110
117
|
realtimeProcessing?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* RTSP URL for server-side video capture.
|
|
120
|
+
* When provided, the server captures video from this RTSP stream instead of receiving
|
|
121
|
+
* video from the client. Supports credentials in URL format: rtsp://user:pass@host/stream
|
|
122
|
+
* @example "rtsp://camera.local/stream"
|
|
123
|
+
*/
|
|
124
|
+
rtspUrl?: string;
|
|
111
125
|
}
|
|
112
126
|
export interface Connector {
|
|
113
127
|
connectWrtc(offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inference-api.d.ts","sourceRoot":"","sources":["../src/inference-api.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1C;;OAEG;IACH,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAClC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"inference-api.d.ts","sourceRoot":"","sources":["../src/inference-api.ts"],"names":[],"mappings":"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"}
|
package/dist/webrtc.d.ts
CHANGED
|
@@ -258,4 +258,54 @@ export interface UseVideoFileParams {
|
|
|
258
258
|
* ```
|
|
259
259
|
*/
|
|
260
260
|
export declare function useVideoFile({ file, connector, wrtcParams, onData, onUploadProgress, onComplete }: UseVideoFileParams): Promise<RFWebRTCConnection>;
|
|
261
|
+
/**
|
|
262
|
+
* Parameters for useRtspStream function
|
|
263
|
+
*/
|
|
264
|
+
export interface UseRtspStreamParams {
|
|
265
|
+
/** RTSP URL for server-side video capture (e.g., "rtsp://camera.local/stream") */
|
|
266
|
+
rtspUrl: string;
|
|
267
|
+
/** Connector for WebRTC signaling */
|
|
268
|
+
connector: Connector;
|
|
269
|
+
/** WebRTC parameters for the workflow */
|
|
270
|
+
wrtcParams: WebRTCParams;
|
|
271
|
+
/** Callback for inference results */
|
|
272
|
+
onData?: (data: WebRTCOutputData) => void;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Connect to an RTSP stream for inference processing
|
|
276
|
+
*
|
|
277
|
+
* Creates a WebRTC connection where the server captures video from an RTSP URL
|
|
278
|
+
* and sends processed video back to the client. This is a receive-only mode -
|
|
279
|
+
* no video is sent from the browser to the server.
|
|
280
|
+
*
|
|
281
|
+
* @param params - Connection parameters
|
|
282
|
+
* @returns Promise resolving to RFWebRTCConnection
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* import { connectors, webrtc } from './index.ts';
|
|
287
|
+
*
|
|
288
|
+
* const connector = connectors.withApiKey("your-api-key");
|
|
289
|
+
* const connection = await webrtc.useRtspStream({
|
|
290
|
+
* rtspUrl: "rtsp://camera.local/stream",
|
|
291
|
+
* connector,
|
|
292
|
+
* wrtcParams: {
|
|
293
|
+
* workflowSpec: { ... },
|
|
294
|
+
* imageInputName: "image",
|
|
295
|
+
* dataOutputNames: ["predictions"]
|
|
296
|
+
* },
|
|
297
|
+
* onData: (data) => {
|
|
298
|
+
* console.log("Inference results:", data);
|
|
299
|
+
* }
|
|
300
|
+
* });
|
|
301
|
+
*
|
|
302
|
+
* // Get processed video stream from server
|
|
303
|
+
* const remoteStream = await connection.remoteStream();
|
|
304
|
+
* videoElement.srcObject = remoteStream;
|
|
305
|
+
*
|
|
306
|
+
* // When done
|
|
307
|
+
* await connection.cleanup();
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export declare function useRtspStream({ rtspUrl, connector, wrtcParams, onData }: UseRtspStreamParams): Promise<RFWebRTCConnection>;
|
|
261
311
|
//# sourceMappingURL=webrtc.d.ts.map
|
package/dist/webrtc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,SAAS,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AAEnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIlD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAGN;IAEf;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAqC9G;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAQxI;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;
|
|
1
|
+
{"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,SAAS,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AAEnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIlD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAGN;IAEf;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAqC9G;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAQxI;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AA6LD;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,UAAU,CAAC,CAAa;IAEhC,eAAe;gBAEb,EAAE,EAAE,iBAAiB,EACrB,mBAAmB,EAAE,OAAO,CAAC,WAAW,CAAC,EACzC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,WAAW,EAAE,cAAc,EAC3B,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,aAAa,CAAC,EAAE,cAAc,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;KACzB;IAyDH;;;;;;;;;;;OAWG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAI1C;;;;;;;;;;;;;OAaG;IACH,WAAW,IAAI,WAAW,GAAG,SAAS;IAItC;;;;;;;;;;;;;;OAcG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B9B;;;;;;;;;;;;;;OAcG;IACG,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9G;;OAEG;IACH,YAAY,IAAI,IAAI;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,kBAAkB,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAclG;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAajB;AAqJD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,SAAS,CAAC,EAC9B,MAAM,EACN,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAY,EACb,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAY/C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,qCAAqC;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,yCAAyC;IACzC,UAAU,EAAE,YAAY,CAAC;IACzB,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvE,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,YAAY,CAAC,EACjC,IAAI,EACJ,SAAS,EACT,UAAU,EACV,MAAM,EACN,gBAAgB,EAChB,UAAU,EACX,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAYlD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,yCAAyC;IACzC,UAAU,EAAE,YAAY,CAAC;IACzB,qCAAqC;IACrC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,aAAa,CAAC,EAClC,OAAO,EACP,SAAS,EACT,UAAU,EACV,MAAM,EACP,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAYnD"}
|