@furious.luke/argus-js 0.5.5 → 0.5.7
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/README.md +54 -0
- package/dist/index.cjs +304 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +153 -1
- package/dist/index.d.ts +153 -1
- package/dist/index.js +304 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/signaling.ts","../src/publisher.ts","../src/capture.ts"],"sourcesContent":["export { Publisher } from \"./publisher\";\nexport type {\n SignalMessage,\n TrackType,\n VideoTrackType,\n TrackLabel,\n PublisherCallbacks,\n PublisherOptions,\n GatewayReadyInfo,\n PublisherRecoveryEvent,\n PublisherRecoveryState,\n PublisherRecoveryAction,\n PublisherRecoveryFailureReason,\n TurnTransportPolicy,\n AssistantTextEvent,\n UserTextResultEvent,\n} from \"./types\";\nexport { captureCamera, captureScreen, captureMicrophone } from \"./capture\";\nexport type {\n CaptureCameraOptions,\n CaptureScreenOptions,\n CaptureMicrophoneOptions,\n} from \"./capture\";\n","import type { SignalMessage } from \"./types\";\n\n/**\n * Wraps an open WebSocket to the Argus gateway and handles the JSON message\n * envelope: incoming text frames are parsed into {@link SignalMessage}s and\n * outgoing messages are serialised.\n *\n * This is an internal helper. The {@link Publisher} opens the socket itself\n * (racing all candidate gateways) and hands the winner here, so this class only\n * ever adopts an already-open socket rather than dialing one.\n *\n * @internal\n */\nexport class SignalingChannel {\n private ws: WebSocket;\n\n /** Fired for every incoming JSON message. */\n onMessage: ((msg: SignalMessage) => void) | null = null;\n /** Fired when the underlying WebSocket closes. */\n onClose: (() => void) | null = null;\n /** Fired when an error occurs on the WebSocket. */\n onError: ((err: Error) => void) | null = null;\n\n private constructor(ws: WebSocket) {\n this.ws = ws;\n }\n\n /**\n * Adopts an already-open WebSocket (e.g. the winner of a gateway race),\n * routing its events through the channel's callbacks. Any handlers previously\n * attached to the socket are replaced.\n */\n static wrap(ws: WebSocket): SignalingChannel {\n const ch = new SignalingChannel(ws);\n ws.onmessage = (ev: MessageEvent) => {\n const msg = parseSignal(ev.data);\n if (msg) ch.onMessage?.(msg);\n };\n ws.onerror = () => ch.onError?.(new Error(\"WebSocket error\"));\n ws.onclose = () => ch.onClose?.();\n return ch;\n }\n\n /** Sends a JSON message when the socket is open and reports whether it was sent. */\n send(msg: SignalMessage): boolean {\n if (this.ws.readyState !== WebSocket.OPEN) return false;\n this.ws.send(JSON.stringify(msg));\n return true;\n }\n\n /** Closes the underlying WebSocket. */\n close(): void {\n this.ws.close();\n }\n}\n\n/** Parses a WebSocket text frame into a SignalMessage, or null if malformed. */\nfunction parseSignal(data: unknown): SignalMessage | null {\n try {\n return JSON.parse(data as string) as SignalMessage;\n } catch {\n return null;\n }\n}\n","import { SignalingChannel } from \"./signaling\";\nimport type {\n GatewayReadyInfo,\n PublisherOptions,\n PublisherRecoveryAction,\n PublisherRecoveryEvent,\n SignalMessage,\n TrackLabel,\n TrackType,\n VideoTrackType,\n} from \"./types\";\n\nfunction selectGatewayTURNURLs(\n advertised: string[],\n policy: \"all\" | \"udp\" | \"tls\" = \"all\",\n): string[] {\n if (policy === \"all\") return advertised;\n\n const selected = advertised.filter((raw) => {\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n return false;\n }\n const transport = (parsed.searchParams.get(\"transport\") ?? \"\").toLowerCase();\n if (policy === \"tls\") {\n return parsed.protocol.toLowerCase() === \"turns:\" && (transport === \"\" || transport === \"tcp\");\n }\n return parsed.protocol.toLowerCase() === \"turn:\" && (transport === \"\" || transport === \"udp\");\n });\n if (selected.length === 0) {\n throw new Error(`gateway advertised no TURN URLs for required ${policy} transport`);\n }\n return selected;\n}\n\nconst defaultSignalingReconnectTimeoutMs = 20_000;\nconst defaultGatewayHandshakeTimeoutMs = 20_000;\nconst defaultPeerConnectionTimeoutMs = 30_000;\nconst initialGatewayAttemptTimeoutMs = 3_000;\nconst defaultGatewayFailoverTimeoutMs = 8_000;\n// The gateway reaps a standby socket that has been sent `accepted` but not\n// `proceed` after its own deadline (argus/gateway standbyProceedDeadline, 30s).\n// The failover window is capped well below that so a `proceed` sent on failover\n// still reaches a standby before the gateway drops it — a larger configured value\n// would let standbys disappear before the browser ever fails over to them.\nconst maxGatewayFailoverTimeoutMs = 20_000;\n// A gateway that is transiently unable to serve replies `unavailable` (rather than\n// letting the socket look like a dead gateway); the browser reopens that URL after\n// the gateway's hinted backoff, clamped to this range and bounded overall by the\n// gateway-handshake deadline.\nconst defaultGatewayRetryBackoffMs = 3_000;\nconst minGatewayRetryBackoffMs = 250;\nconst maxGatewayRetryBackoffMs = 5_000;\n// A stream that keeps losing the placement compare-and-set would otherwise chase\n// redirects forever; bound the self-heal to a couple of hops.\nconst maxPlacementRedirects = 2;\nconst signalingResumeAttemptTimeoutMs = 3_000;\nconst signalingResumeMaxBackoffMs = 3_000;\nconst senderRestartPauseMs = 100;\nconst senderRecoveryWaitMs = 4_000;\nconst iceRecoveryWaitMs = 8_000;\n// A healthy signaling connection should answer well within this window. When a\n// reconnect timeout is longer, answer waiting is extended past it so a buffered\n// answer can still be applied after signaling resumes.\nconst minimumNegotiationAnswerTimeoutMs = 15_000;\nconst negotiationReconnectGraceMs = 5_000;\nconst minimumIntentionalTrackEndRetentionMs = 35_000;\nconst maxUserTextBytes = 4 * 1024;\nconst maxRetainedICECandidates = 64;\n\nclass ReportedPublisherError extends Error {\n constructor(message: string, readonly fatal = false) {\n super(message);\n }\n}\nclass NegotiationTimeoutError extends Error {}\nclass SenderRestoreError extends Error {}\nclass PublisherStoppedError extends Error {}\n\ninterface TrackRecoveryState {\n generation: number;\n recovering: boolean;\n required: boolean;\n action: PublisherRecoveryAction | null;\n}\n\ninterface SignalingWaiter {\n resolve: (channel: SignalingChannel) => void;\n reject: (err: Error) => void;\n}\n\ninterface InitialTrack {\n track: MediaStreamTrack;\n stream: MediaStream;\n type: TrackType;\n watchForRecovery: boolean;\n}\n\ninterface NegotiationChange {\n // labels is evaluated after setLocalDescription, not at stage time: a newly\n // added transceiver has no mid until then, and labels are keyed on the mid.\n labels?: () => TrackLabel[];\n commit?: () => void | Promise<void>;\n rollback?: () => void | Promise<void>;\n discard?: () => void | Promise<void>;\n}\n\ntype NegotiationMutationResult = void | false | NegotiationChange;\ntype NegotiationMutation = () => NegotiationMutationResult | Promise<NegotiationMutationResult>;\ntype OfferSignalMessage = Extract<SignalMessage, { type: \"offer\" }>;\ntype ICECandidateSignalMessage = Extract<SignalMessage, { type: \"ice_candidate\" }>;\ntype ICEPathSignalMessage = Extract<SignalMessage, { type: \"ice_path\" }>;\n\n/**\n * Publisher establishes a browser WebRTC session with an Argus media server.\n * The session may start with camera, microphone, or only its reliable text data\n * channel. Given the `gateway_urls` and `token` from a join-token response, it\n * races the candidate gateways to the fastest one, completes the two-phase\n * signaling handshake, and manages offer/answer exchange, ICE candidate\n * trickling, and track (re)negotiation.\n *\n * After {@link Publisher.start} resolves, {@link Publisher.frameReadToken} holds\n * the token your application server needs to fetch frames for this stream.\n *\n * @example Publish the default camera:\n * ```ts\n * const pub = new Publisher({\n * gatewayURLs: joinResp.gateway_urls,\n * token: joinResp.token,\n * callbacks: { onConnected: () => console.log(\"live!\") },\n * });\n *\n * const stream = await navigator.mediaDevices.getUserMedia({ video: true });\n * await pub.start(stream);\n * ```\n */\nexport class Publisher {\n private opts: PublisherOptions;\n private sig: SignalingChannel | null = null;\n private pc: RTCPeerConnection | null = null;\n private hasAnswer = false;\n private pendingRemoteCandidates: RTCIceCandidateInit[] = [];\n // Setting a local description starts ICE gathering. Candidates may therefore\n // arrive before the corresponding offer has crossed the signaling socket.\n // Hold them until sendOffer confirms that the offer was sent, then trickle\n // them in order. This keeps startup fast without allowing candidate/offer\n // reordering at the media server.\n private pendingLocalCandidates: ICECandidateSignalMessage[] = [];\n private localCandidateOfferSent = false;\n // WebSocket.send() only proves local queueing. Retain the current ICE\n // generation so a replacement signaling socket can replay candidates whose\n // delivery on the old socket was ambiguous.\n private retainedLocalCandidates: ICECandidateSignalMessage[] = [];\n private retainedLocalCandidateKeys = new Set<string>();\n private localCandidateGeneration: string | null = null;\n // Both peers replay after reconnect. Receiving the same candidate must be\n // idempotent, whether it is still buffered behind an answer or already\n // applied to the peer connection.\n private remoteCandidateKeys = new Set<string>();\n private remoteCandidateOrder: string[] = [];\n private remoteCandidateGeneration: string | null = null;\n private readToken: string | null = null;\n private gatewayURL: string | null = null;\n private lastReportedICEPath: string | null = null;\n private watchedICETransports = new WeakSet<RTCIceTransport>();\n private stopped = true;\n // Every start/stop boundary advances lifecycleGeneration. Async work captures\n // the generation it belongs to and may never mutate or terminate a later run.\n private lifecycleGeneration = 0;\n private runAbort: AbortController | null = null;\n private peerConnectionTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnecting = false;\n private reconnectGeneration = 0;\n private resumeSocket: WebSocket | null = null;\n private signalingWaiters = new Set<SignalingWaiter>();\n private pendingOffer: { message: OfferSignalMessage; sent: boolean } | null = null;\n private recoverySequence = 0;\n private recoveryStates = new Map<TrackType, TrackRecoveryState>();\n // ICE restart applies to the whole peer connection. Keep one attempt shared\n // by every track currently recovering so simultaneous camera/screen stalls do\n // not create duplicate ICE offers.\n private iceRestartSequence = 0;\n private iceRestartAttempt: { id: number; promise: Promise<void> } | null = null;\n private trackEndHandlers = new Map<MediaStreamTrack, EventListener>();\n // published is the source of truth for the live video tracks and their logical\n // types. It drives the track labels sent on every offer, per-track recovery\n // reporting, and add/remove of individual tracks. At most one track per type is\n // kept (publishing a second track of a type replaces the first).\n private published = new Map<MediaStreamTrack, TrackType>();\n private publishedStreams = new Map<MediaStreamTrack, MediaStream>();\n // Own the active sender for each logical type. Active source replacement uses\n // replaceTrack on that sender. Unpublish removes the mapping because addTrack\n // may later reuse any compatible inactive transceiver, not necessarily the one\n // that previously carried the same logical type.\n private typeSenders = new Map<TrackType, RTCRtpSender>();\n // intentionalTrackEnds holds the browser track ids removed by publish/unpublish\n // whose server-side `media_track_ended` has not yet arrived. Correlating by the\n // track id (the generation identity the server echoes in track_id) keeps a\n // delayed end for an old screen track from being mistaken for failure of a\n // newly-published screen track.\n private intentionalTrackEnds = new Map<\n string,\n { type: TrackType; timer: ReturnType<typeof setTimeout> }\n >();\n // negotiationChain serializes every offer/answer exchange, including recovery.\n // User operations do not resolve until their answer is applied, so no caller\n // or recovery timer can create a second offer while one is outstanding.\n private negotiationChain: Promise<void> = Promise.resolve();\n // pendingAnswer resolves the one in-flight negotiation once its matching answer\n // arrives, or rejects it on timeout/teardown. All offers, including recovery,\n // pass through negotiationChain, so this slot is never intentionally replaced.\n private pendingAnswer: {\n id: string;\n resolve: (sdp: string) => void;\n reject: (err: Error) => void;\n } | null = null;\n // negotiationSeq stamps each offer with a monotonically increasing id.\n private negotiationSeq = 0;\n private textChannel: RTCDataChannel | null = null;\n private speechEnabled = false;\n private speechPending = false;\n private speechTransceiver: RTCRtpTransceiver | null = null;\n private microphoneTransceiver: RTCRtpTransceiver | null = null;\n\n constructor(opts: PublisherOptions) {\n this.opts = opts;\n }\n\n /** The read token used for frame fetches and signaling resume in the selected region. */\n get frameReadToken(): string | null { return this.readToken; }\n\n /**\n * The signaling gateway URL that won the initial race, or null before start.\n * Relay this with frameReadToken so the application server can reach the same\n * region for frame reads and change-notification subscriptions.\n */\n get selectedGatewayURL(): string | null { return this.gatewayURL; }\n\n /** Requests the persistent outbound `speech` track. This is explicit user\n * opt-in and renegotiates only once; the track remains silent between turns. */\n async enableSpeech(): Promise<void> {\n if (this.speechEnabled) return;\n await this.enqueueNegotiation(() => {\n const pc = this.pc;\n if (!pc || this.speechEnabled || this.speechPending) return false;\n if (!this.speechTransceiver) {\n this.speechTransceiver = pc.addTransceiver(\"audio\", { direction: \"recvonly\" });\n } else {\n this.speechTransceiver.direction = \"recvonly\";\n }\n this.speechPending = true;\n return {\n commit: () => {\n this.speechPending = false;\n this.speechEnabled = true;\n },\n rollback: () => {\n this.speechPending = false;\n },\n };\n });\n }\n\n /** Sends typed input over the reliable ordered Argus text channel. */\n sendUserText(messageId: string, text: string): void {\n if (!messageId || !text.trim()) throw new Error(\"messageId and text are required\");\n if (new TextEncoder().encode(text).byteLength > maxUserTextBytes) {\n throw new Error(\"text must not exceed 4 KiB\");\n }\n if (!this.textChannel || this.textChannel.readyState !== \"open\") {\n throw new Error(\"Argus text channel is not open\");\n }\n this.textChannel.send(JSON.stringify({ type: \"user_text\", message_id: messageId, text }));\n }\n\n /**\n * Starts the publisher: races all gateways to find the fastest, completes\n * the two-phase handshake, creates the peer connection, and sends the SDP\n * offer. Resolves when the offer has been sent (not when ICE completes —\n * use onConnected for that).\n *\n * The stream's single video track is published under `type` (default `\"camera\"`),\n * declared to the server so reads and change notifications can address them by\n * type. Add or remove further tracks live with {@link Publisher.publish} and\n * {@link Publisher.unpublish}.\n */\n async start(stream: MediaStream, type: VideoTrackType = \"camera\"): Promise<void> {\n const track = this.requireSingleVideoTrack(stream);\n await this.startSession({ track, stream, type, watchForRecovery: true });\n }\n\n /**\n * Starts the publisher with a microphone track and no video — a fully valid\n * audio-only stream, the natural starting point for a voice agent. Exactly one\n * audio track must be present in `stream`. Video can be added later with\n * {@link Publisher.publish}; a stream carries at most one microphone track.\n *\n * Like {@link Publisher.start} it races the gateways, completes the handshake,\n * and sends the offer; it resolves once the offer is sent. The microphone is not\n * subject to the video recovery ladder — a mic that stops simply ends\n * transcription for the stream.\n */\n async startAudioOnly(stream: MediaStream): Promise<void> {\n const track = this.requireSingleAudioTrack(stream);\n await this.startSession({ track, stream, type: \"audio\", watchForRecovery: false });\n }\n\n /**\n * Starts a WebRTC session with only the ordered `argus.text` data channel.\n * This is the natural entry point for a typed, text-only agent: it requests no\n * camera or microphone permission and publishes no media. Camera, screen, or\n * microphone tracks can be added later with {@link Publisher.publish} or\n * {@link Publisher.publishMicrophone}; {@link Publisher.enableSpeech} can add\n * the optional inbound speech track independently.\n */\n async startTextOnly(): Promise<void> {\n await this.startSession(null);\n }\n\n /**\n * Shared startup for video, audio-only, and text-only entry points: race the\n * gateways, build the peer connection and text channel, optionally add an\n * initial media track, and send the first offer.\n */\n private async startSession(initialTrack: InitialTrack | null): Promise<void> {\n if (!this.stopped || this.pc || this.published.size > 0) {\n throw new Error(\"publisher already started\");\n }\n const trackKind: \"video\" | \"audio\" | null = initialTrack\n ? (initialTrack.type === \"audio\" ? \"audio\" : \"video\")\n : null;\n const generation = ++this.lifecycleGeneration;\n const runAbort = new AbortController();\n this.runAbort = runAbort;\n // A publisher run owns its negotiation queue. Old queue continuations may\n // still unwind after stop(), but their captured generation prevents them\n // from adopting this fresh chain or peer connection.\n this.negotiationChain = Promise.resolve();\n this.stopped = false;\n this.recoveryStates.clear();\n this.typeSenders.clear();\n this.lastReportedICEPath = null;\n this.watchedICETransports = new WeakSet<RTCIceTransport>();\n // Record the startup track for offer labels and teardown, but do not watch\n // it for recovery until startup succeeds. If capture ends while startup is\n // awaiting the gateway or ICE, start() must reject rather than report a\n // recovery transition for a session that never became live.\n if (initialTrack) {\n this.published.set(initialTrack.track, initialTrack.type);\n this.publishedStreams.set(initialTrack.track, initialTrack.stream);\n }\n let startupWS: WebSocket | null = null;\n\n try {\n // Race all gateways; returns winning WebSocket + TURN/read-token info\n const { ws, readyInfo, gatewayURL } = await this.raceGateways(runAbort.signal);\n startupWS = ws;\n this.assertActiveRun(generation);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n this.gatewayURL = gatewayURL;\n\n // Store read token for caller\n if (readyInfo.read_token) {\n this.readToken = readyInfo.read_token;\n }\n\n // Build ICE servers: extra servers from opts + the gateway's bounded TURN\n // selection. Each selected relay supplies UDP and TCP URLs under one\n // credential; browser ICE may gather from either selected server.\n const iceServers: RTCIceServer[] = [...(this.opts.iceServers ?? [])];\n const advertisedTURNURLs = readyInfo.turn_urls ?? [];\n if (advertisedTURNURLs.length > 0 || this.opts.turnTransportPolicy !== undefined) {\n const turnURLs = selectGatewayTURNURLs(\n advertisedTURNURLs,\n this.opts.turnTransportPolicy,\n );\n if (turnURLs.length > 0) {\n iceServers.push({\n urls: turnURLs,\n username: readyInfo.turn_username,\n credential: readyInfo.turn_credential,\n });\n }\n }\n\n const pc = new RTCPeerConnection({\n iceServers,\n iceTransportPolicy: this.opts.iceTransportPolicy,\n });\n this.pc = pc;\n\n this.textChannel = pc.createDataChannel(\"argus.text\", { ordered: true });\n this.textChannel.onmessage = (event) => this.handleTextMessage(event.data);\n pc.ontrack = (event) => {\n if (!this.isActiveRun(generation, pc) || event.track.kind !== \"audio\") return;\n this.opts.callbacks?.onSpeechTrack?.(event.track, event.streams);\n };\n\n pc.onicecandidate = (ev) => {\n if (!this.isActiveRun(generation, pc) || !ev.candidate) return;\n this.handleLocalICECandidate(ev.candidate);\n };\n\n pc.onconnectionstatechange = () => {\n if (!this.isActiveRun(generation, pc)) return;\n const state = pc.connectionState;\n if (state) this.opts.callbacks?.onConnectionStateChange?.(state);\n if (state === \"connected\") {\n this.clearPeerConnectionTimeout();\n void this.reportSelectedICEPath(pc);\n this.opts.callbacks?.onConnected?.();\n } else if (state === \"failed\") {\n this.clearPeerConnectionTimeout();\n this.terminateWithError(new Error(\"WebRTC connection failed\"), true, generation);\n }\n };\n\n if (initialTrack) {\n const sender = initialTrack.type === \"audio\"\n ? this.addMicrophoneTrack(pc, initialTrack.track, initialTrack.stream)\n : pc.addTrack(initialTrack.track, initialTrack.stream);\n if (initialTrack.type !== \"audio\") this.preferVideoCodecs(pc, sender);\n this.typeSenders.set(\n initialTrack.type,\n sender,\n );\n }\n\n // Adopt the winning socket before setting the local description so ICE\n // candidates can be trickled instead of blocking startup on complete\n // gathering. handleLocalICECandidate still holds candidates until the\n // offer itself has been sent.\n this.installSignaling(ws);\n startupWS = null; // installSignaling/stop now owns this socket.\n\n const offer = await pc.createOffer();\n this.assertActiveRun(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n this.beginLocalCandidateBatch();\n await pc.setLocalDescription(offer);\n this.assertActiveRun(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n\n const local = pc.localDescription;\n if (!local) throw new Error(\"local description missing\");\n\n // Seed the negotiation chain with the initial offer's answer. start() resolves\n // once the offer is sent (its documented contract — use onConnected for media),\n // but any publish()/unpublish() queues behind this so it cannot emit a second\n // offer before the initial answer lands or apply that answer to its own offer.\n const id = this.nextNegotiationId();\n const { answered } = await this.sendOffer({\n type: \"offer\",\n sdp: local.sdp,\n sdp_type: \"offer\",\n negotiation_id: id,\n tracks: this.buildTrackLabels(),\n speech_enabled: (this.speechEnabled || this.speechPending) || undefined,\n });\n this.releaseLocalCandidateBatch();\n this.assertActiveRun(generation, pc);\n this.armPeerConnectionTimeout(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n // Only video participates in the media-recovery ladder; a microphone that\n // stops simply ends transcription and is not \"recovered\".\n if (initialTrack?.watchForRecovery) this.watchTrack(initialTrack.track);\n else if (initialTrack) this.watchMicrophone(initialTrack.track);\n const initial = answered.then(async (sdp) => {\n this.assertActiveRun(generation, pc);\n await pc.setRemoteDescription(\n new RTCSessionDescription({ type: \"answer\", sdp }),\n );\n this.assertActiveRun(generation, pc);\n this.applyAnswered(pc);\n });\n this.negotiationChain = initial.catch((err) => {\n if (this.isActiveRun(generation, pc)) {\n const reported = err instanceof Error ? err : new Error(String(err));\n const alreadyReported =\n err instanceof ReportedPublisherError && err.fatal;\n this.terminateWithError(reported, !alreadyReported, generation);\n }\n throw err;\n });\n // start() intentionally resolves once the offer is sent, so observe the\n // background initial-answer promise here to avoid an unhandled rejection.\n void this.negotiationChain.catch(() => {});\n } catch (err) {\n startupWS?.close();\n if (generation === this.lifecycleGeneration) this.stop();\n throw err;\n }\n }\n\n /**\n * Adds the single video track from `stream` to the live session under `type`,\n * renegotiating so the media server begins ingesting them. Use this to add a\n * track after {@link Publisher.start} — for example to begin a screen share on\n * top of a live camera.\n *\n * Exactly one video track must be present in `stream`. If a track of `type` is already\n * live it is removed and replaced (a \"screen\" published while another \"screen\"\n * is live supersedes it).\n */\n async publish(stream: MediaStream, type: VideoTrackType): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n const track = this.requireSingleVideoTrack(stream);\n\n // The track mutation runs inside the queued negotiation so it is atomic with\n // its offer: back-to-back publish/unpublish calls each mutate the peer\n // connection at the head of their own turn, not all up front.\n await this.enqueueNegotiation(() => this.stagePublish(track, stream, type));\n }\n\n /**\n * Removes the live track(s) of the given type, stops their local capture, and\n * renegotiates so the media server ends ingestion for that track. A no-op if\n * no track of that type is published.\n */\n async unpublish(type: VideoTrackType): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n await this.enqueueNegotiation(() => {\n if (this.tracksOfType(type).length === 0) {\n // Nothing to remove by the time this turn runs; skip the offer.\n return false;\n }\n return this.stageUnpublish(type);\n });\n }\n\n /**\n * Adds the microphone (audio) track from `stream` to the live session and\n * renegotiates, so the media server begins transcribing it. Exactly one audio\n * track must be present in `stream`. Publishing a microphone while one is\n * already live replaces it.\n *\n * The audio track feeds server-side speech-to-text; its transcripts are\n * delivered to the customer server over the change-notification subscription,\n * not to the browser. Audio is not subject to the video recovery ladder — a\n * mic that stops is simply removed.\n */\n async publishMicrophone(stream: MediaStream): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n const track = this.requireSingleAudioTrack(stream);\n await this.enqueueNegotiation(() => this.stagePublishAudio(track, stream));\n }\n\n /**\n * Removes the live microphone track, stops its local capture, and\n * renegotiates so the media server ends transcription. A no-op if no\n * microphone is published.\n */\n async unpublishMicrophone(): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n await this.enqueueNegotiation(() => {\n if (this.tracksOfType(\"audio\").length === 0) return false;\n return this.stageUnpublish(\"audio\");\n });\n }\n\n /**\n * Replaces the published track of a single type with a new stream and\n * renegotiates in place — e.g. to swap to a freshly reacquired screen share\n * after {@link PublisherCallbacks.onRecoveryRequired}. Defaults to the\n * `\"camera\"` type. This is a convenience over {@link Publisher.publish}, which\n * it delegates to (publishing one track per type replaces any existing track\n * of that type).\n */\n async replaceStream(stream: MediaStream, type: VideoTrackType = \"camera\"): Promise<void> {\n await this.publish(stream, type);\n }\n\n /** Stops publishing and tears down the peer connection. */\n stop(): void {\n this.lifecycleGeneration++;\n this.runAbort?.abort();\n this.runAbort = null;\n this.clearPeerConnectionTimeout();\n this.stopped = true;\n this.cancelAllMediaRecovery();\n this.reconnectGeneration++;\n this.reconnecting = false;\n this.resumeSocket?.close();\n this.resumeSocket = null;\n this.sig?.close();\n this.sig = null;\n this.rejectSignalingWaiters(new Error(\"publisher stopped\"));\n\n this.unwatchStreamTracks();\n this.stopPublishedTracks();\n this.clearIntentionalTrackEnds();\n this.rejectPendingAnswer(new Error(\"publisher stopped\"));\n this.pendingOffer = null;\n\n this.pc?.close();\n this.pc = null;\n this.textChannel = null;\n this.speechEnabled = false;\n this.speechPending = false;\n this.speechTransceiver = null;\n this.microphoneTransceiver = null;\n this.typeSenders.clear();\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = false;\n this.clearRetainedICECandidates();\n this.readToken = null;\n this.gatewayURL = null;\n this.lastReportedICEPath = null;\n }\n\n // rejectPendingAnswer fails any in-flight negotiation so a queued\n // publish()/unpublish() rejects promptly instead of hanging until timeout.\n private rejectPendingAnswer(err: Error): void {\n const pending = this.pendingAnswer;\n if (pending) {\n this.pendingAnswer = null;\n pending.reject(err);\n }\n }\n\n /** Returns the current RTCPeerConnection, or null if not started. */\n get peerConnection(): RTCPeerConnection | null {\n return this.pc;\n }\n\n /** Returns true if the peer connection is in the \"connected\" state. */\n get isConnected(): boolean {\n return this.pc?.connectionState === \"connected\";\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n // raceGateways opens every candidate gateway at once, then decides in two\n // separate moments. SELECTION: the first socket to deliver `accepted` (a cheap,\n // control-plane-free acknowledgement) is chosen on network path; the browser\n // sends `proceed` on that one only and keeps the rest as standbys. PLACEMENT:\n // the selected region does its control-plane work and returns `ready`. If the\n // selection dies (socket close/error → immediately) or stalls past the failover\n // deadline (a hung-but-open socket), the browser abandons it — closing the\n // socket cancels that region's placement server-side — and selects the\n // next-fastest standby. A `placement_redirect` points the browser at the region\n // that already holds the stream so a mistimed failover self-heals.\n private raceGateways(signal: AbortSignal): Promise<{ ws: WebSocket; readyInfo: GatewayReadyInfo; gatewayURL: string }> {\n return new Promise((resolve, reject) => {\n const { gatewayURLs, token } = this.opts;\n if (gatewayURLs.length === 0) {\n reject(new Error(\"no gateway URLs provided\"));\n return;\n }\n\n const sockets: WebSocket[] = [];\n const attemptTimers = new Map<WebSocket, ReturnType<typeof setTimeout>>();\n // Pending reopen timers for URLs a gateway asked us to retry (`unavailable`).\n // A URL with a scheduled reopen is still in the running, so the race is not\n // exhausted while any of these are outstanding.\n const reopenTimers = new Set<ReturnType<typeof setTimeout>>();\n // Sockets that have delivered `accepted` but were not selected — the\n // failover pool, in acknowledgement (fastest-first) order.\n const standbys: WebSocket[] = [];\n let selected: WebSocket | null = null;\n let failoverTimer: ReturnType<typeof setTimeout> | null = null;\n let redirects = 0;\n let settled = false;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const failoverMs = Math.min(\n maxGatewayFailoverTimeoutMs,\n Math.max(0, this.opts.gatewayFailoverTimeoutMs ?? defaultGatewayFailoverTimeoutMs),\n );\n\n const clearTimeoutTimer = () => {\n if (timeoutTimer !== null) clearTimeout(timeoutTimer);\n timeoutTimer = null;\n };\n const clearFailoverTimer = () => {\n if (failoverTimer !== null) clearTimeout(failoverTimer);\n failoverTimer = null;\n };\n const clearReopenTimers = () => {\n for (const timer of reopenTimers) clearTimeout(timer);\n reopenTimers.clear();\n };\n const clearAttemptTimer = (socket: WebSocket) => {\n const timer = attemptTimers.get(socket);\n if (timer !== undefined) clearTimeout(timer);\n attemptTimers.delete(socket);\n };\n const detach = (socket: WebSocket) => {\n clearAttemptTimer(socket);\n socket.onmessage = null;\n socket.onerror = null;\n socket.onclose = null;\n };\n const dropStandby = (socket: WebSocket) => {\n const i = standbys.indexOf(socket);\n if (i !== -1) standbys.splice(i, 1);\n };\n\n const closeAll = (except?: WebSocket) => {\n for (const s of sockets) {\n if (s !== except) {\n detach(s);\n s.close();\n }\n }\n };\n\n const win = (ws: WebSocket, readyInfo: GatewayReadyInfo, gatewayURL: string) => {\n settled = true;\n clearTimeoutTimer();\n clearFailoverTimer();\n clearReopenTimers();\n signal.removeEventListener(\"abort\", abort);\n closeAll(ws);\n resolve({ ws, readyInfo, gatewayURL });\n };\n const fail = (err: Error) => {\n settled = true;\n clearTimeoutTimer();\n clearFailoverTimer();\n clearReopenTimers();\n signal.removeEventListener(\"abort\", abort);\n closeAll();\n reject(err);\n };\n // No socket is still able to become a selection: every one is closed, nothing\n // is selected, no standby is waiting, and no retry (`unavailable`) is pending.\n const checkExhausted = () => {\n if (settled || selected !== null || standbys.length > 0 || reopenTimers.size > 0) return;\n if (sockets.every(s => s.readyState === WebSocket.CLOSED || s.readyState === WebSocket.CLOSING)) {\n fail(new Error(\"all gateways failed to connect\"));\n }\n };\n\n // select commits the browser to one region: send `proceed` and start the\n // failover deadline. Only the selected socket's `ready`/`placement_redirect`\n // are acted on.\n const select = (ws: WebSocket) => {\n selected = ws;\n dropStandby(ws);\n try {\n ws.send(JSON.stringify({ type: \"proceed\" }));\n } catch {\n socketDown(ws);\n return;\n }\n clearFailoverTimer();\n failoverTimer = setTimeout(() => failover(ws), failoverMs);\n };\n\n // failover abandons the current selection and moves to the next-fastest\n // standby. Closing the abandoned socket cancels its placement server-side.\n const failover = (deadSocket: WebSocket) => {\n if (settled || deadSocket !== selected) return;\n clearFailoverTimer();\n detach(deadSocket);\n deadSocket.close();\n selected = null;\n const next = standbys.shift();\n if (next) {\n select(next);\n } else {\n checkExhausted();\n }\n };\n\n // redirect self-heals to the region that already holds the stream: tear the\n // whole race down and open a single socket to the named gateway.\n const redirect = (gatewayURL: string) => {\n if (settled) return;\n if (redirects >= maxPlacementRedirects) {\n fail(new Error(\"too many placement redirects\"));\n return;\n }\n redirects++;\n clearFailoverTimer();\n closeAll();\n standbys.length = 0;\n selected = null;\n // A gateway-supplied redirect URL may be malformed; a construction failure\n // must reject the race, not throw out of this socket callback and leave the\n // race pending until the handshake deadline.\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n // retryUnavailable drops a socket whose gateway reported transient\n // unavailability and reopens the same URL after the hinted backoff, so a\n // temporary capacity or health blip does not count the region out. The\n // overall handshake deadline bounds how long this repeats.\n const retryUnavailable = (ws: WebSocket, gatewayURL: string, retryAfterMs?: number) => {\n detach(ws);\n ws.close();\n const delay = Math.min(\n maxGatewayRetryBackoffMs,\n Math.max(minGatewayRetryBackoffMs, retryAfterMs ?? defaultGatewayRetryBackoffMs),\n );\n const timer = setTimeout(() => {\n reopenTimers.delete(timer);\n if (settled) return;\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n }, delay);\n reopenTimers.add(timer);\n };\n\n const socketDown = (ws: WebSocket) => {\n if (settled) return;\n clearAttemptTimer(ws);\n dropStandby(ws);\n if (ws === selected) {\n // A definitive death of the selection fails over immediately, without\n // waiting out the deadline.\n failover(ws);\n } else {\n checkExhausted();\n }\n };\n\n const abort = () => {\n if (settled) return;\n fail(new PublisherStoppedError(\"publisher stopped\"));\n };\n if (signal.aborted) {\n abort();\n return;\n }\n signal.addEventListener(\"abort\", abort, { once: true });\n\n const timeoutMs = Math.max(\n 0,\n this.opts.gatewayHandshakeTimeoutMs ?? defaultGatewayHandshakeTimeoutMs,\n );\n timeoutTimer = setTimeout(() => {\n if (settled) return;\n fail(new Error(`gateway handshake timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const openGateway = (gatewayURL: string) => {\n if (settled) return;\n\n const u = new URL(gatewayURL);\n u.searchParams.set(\"token\", token);\n const ws = new WebSocket(u.toString());\n sockets.push(ws);\n\n let accepted = false;\n const attemptTimer = setTimeout(() => {\n attemptTimers.delete(ws);\n if (settled || accepted) return;\n\n // A WebSocket can remain CONNECTING until the browser's TCP timeout\n // expires. Retire that flow and create a fresh one; a new source port\n // gives load-balanced paths another chance without extending the\n // caller's overall gateway-handshake deadline. A socket that has already\n // acknowledged (selected or standby) is never reopened.\n detach(ws);\n ws.close();\n\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n }, initialGatewayAttemptTimeoutMs);\n attemptTimers.set(ws, attemptTimer);\n\n ws.onmessage = (ev: MessageEvent) => {\n if (settled) return;\n let msg: SignalMessage;\n try {\n msg = JSON.parse(ev.data as string) as SignalMessage;\n } catch {\n return; // ignore malformed\n }\n if (!accepted) {\n if (msg.type === \"unavailable\") {\n // The region cannot serve right now; reopen this URL after a backoff\n // instead of treating the socket as a dead gateway.\n retryUnavailable(ws, gatewayURL, msg.retry_after_ms);\n return;\n }\n if (msg.type !== \"accepted\") return;\n accepted = true;\n clearAttemptTimer(ws);\n // First acknowledgement wins selection; the rest wait as standbys.\n if (selected === null) select(ws);\n else standbys.push(ws);\n return;\n }\n // Post-acknowledgement messages are meaningful only from the selection.\n if (ws !== selected) return;\n if (msg.type === \"ready\") {\n win(ws, msg as GatewayReadyInfo, gatewayURL);\n } else if (msg.type === \"placement_redirect\" && msg.gateway_url) {\n redirect(msg.gateway_url);\n }\n };\n\n ws.onerror = () => socketDown(ws);\n ws.onclose = () => socketDown(ws);\n };\n\n try {\n for (const gatewayURL of gatewayURLs) {\n openGateway(gatewayURL);\n }\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n\n private installSignaling(ws: WebSocket): SignalingChannel {\n const channel = SignalingChannel.wrap(ws);\n const generation = this.lifecycleGeneration;\n this.sig = channel;\n channel.onMessage = (msg) => {\n if (this.sig !== channel || !this.isActiveRun(generation)) return;\n this.handleSignal(msg);\n };\n channel.onClose = () => {\n if (this.sig !== channel || this.stopped) return;\n this.sig = null;\n void this.resumeSignaling();\n };\n // Browsers normally follow an error event with close. Recovery begins from\n // close so a single transport failure cannot start two retry loops.\n channel.onError = () => {};\n this.resolveSignalingWaiters(channel);\n\n // An open WebSocket only acknowledges local queueing, not server receipt.\n // Replay the outstanding offer before its candidates; the media server\n // deduplicates the offer by negotiation_id and candidates by their content.\n const pending = this.pendingOffer;\n if (pending?.sent) {\n try {\n channel.send(pending.message);\n } catch {\n // onclose/sendWhenSignalingAvailable will drive another resume attempt.\n }\n }\n // Candidates captured for an offer that has not been sent remain in the\n // normal local batch. Replaying them here would put them ahead of that\n // offer when sendOffer wakes on this new channel.\n if (this.localCandidateOfferSent) {\n for (const candidate of this.retainedLocalCandidates) {\n try {\n channel.send(candidate);\n } catch {\n // A later resume replays the complete bounded set again.\n break;\n }\n }\n }\n return channel;\n }\n\n private awaitSignaling(): Promise<SignalingChannel> {\n if (this.sig) return Promise.resolve(this.sig);\n if (this.stopped) return Promise.reject(new Error(\"publisher stopped\"));\n return new Promise<SignalingChannel>((resolve, reject) => {\n this.signalingWaiters.add({ resolve, reject });\n });\n }\n\n private resolveSignalingWaiters(channel: SignalingChannel): void {\n const waiters = [...this.signalingWaiters];\n this.signalingWaiters.clear();\n for (const waiter of waiters) waiter.resolve(channel);\n }\n\n private rejectSignalingWaiters(err: Error): void {\n const waiters = [...this.signalingWaiters];\n this.signalingWaiters.clear();\n for (const waiter of waiters) waiter.reject(err);\n }\n\n private async sendWhenSignalingAvailable(msg: SignalMessage): Promise<void> {\n while (!this.stopped) {\n const channel = await this.awaitSignaling();\n try {\n if (channel.send(msg)) return;\n } catch {\n // Treat a synchronous transport failure exactly like a closed socket.\n }\n\n // The channel closed between selection and send. Invalidate it and begin\n // resume immediately; a later close event is ignored by its identity check.\n if (this.sig === channel) {\n this.sig = null;\n void this.resumeSignaling();\n }\n }\n throw new Error(\"publisher stopped\");\n }\n\n private async sendOffer(message: OfferSignalMessage): Promise<{ answered: Promise<string> }> {\n if (this.pendingOffer) {\n throw new Error(\"another negotiation offer is already pending\");\n }\n const pending = { message, sent: false };\n this.pendingOffer = pending;\n try {\n await this.sendWhenSignalingAvailable(message);\n pending.sent = true;\n const id = message.negotiation_id;\n if (!id) throw new Error(\"negotiation offer is missing an id\");\n const answered = this.awaitAnswer(id);\n void answered.then(\n () => { if (this.pendingOffer === pending) this.pendingOffer = null; },\n () => { if (this.pendingOffer === pending) this.pendingOffer = null; },\n );\n return { answered };\n } catch (err) {\n if (this.pendingOffer === pending) this.pendingOffer = null;\n throw err;\n }\n }\n\n private async resumeSignaling(): Promise<void> {\n if (this.reconnecting || this.stopped) return;\n if (!this.gatewayURL || !this.readToken) {\n this.terminateWithError(new Error(\"signaling closed and cannot be resumed\"));\n return;\n }\n\n this.reconnecting = true;\n const generation = ++this.reconnectGeneration;\n const timeout = this.opts.signalingReconnectTimeoutMs ?? defaultSignalingReconnectTimeoutMs;\n const deadline = Date.now() + Math.max(0, timeout);\n let backoffMs = 0;\n\n while (!this.stopped && generation === this.reconnectGeneration && Date.now() <= deadline) {\n if (backoffMs > 0) {\n const waitMs = Math.min(backoffMs, Math.max(0, deadline - Date.now()));\n if (waitMs === 0) break;\n await this.wait(waitMs);\n if (this.stopped || generation !== this.reconnectGeneration) return;\n }\n\n const remaining = deadline - Date.now();\n if (remaining < 0) break;\n try {\n const ws = await this.openResumeSocket(Math.min(signalingResumeAttemptTimeoutMs, Math.max(1, remaining)));\n if (this.stopped || generation !== this.reconnectGeneration) {\n ws.close();\n return;\n }\n this.resumeSocket = null;\n this.reconnecting = false;\n this.installSignaling(ws);\n return;\n } catch {\n backoffMs = backoffMs === 0 ? 250 : Math.min(backoffMs * 2, signalingResumeMaxBackoffMs);\n }\n }\n\n if (!this.stopped && generation === this.reconnectGeneration) {\n this.reconnecting = false;\n this.terminateWithError(new Error(\"unable to resume signaling with the selected gateway\"));\n }\n }\n\n private openResumeSocket(timeoutMs: number): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const u = new URL(this.gatewayURL!);\n u.searchParams.set(\"token\", this.readToken!);\n const ws = new WebSocket(u.toString());\n this.resumeSocket = ws;\n let settled = false;\n const timer = setTimeout(() => fail(), timeoutMs);\n\n const fail = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (this.resumeSocket === ws) this.resumeSocket = null;\n ws.onmessage = null;\n ws.onerror = null;\n ws.onclose = null;\n ws.close();\n reject(new Error(\"signaling resume attempt failed\"));\n };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(ev.data as string);\n if (msg.type !== \"resumed\" || settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(ws);\n } catch {\n // Ignore malformed messages while waiting for the resume acknowledgement.\n }\n };\n ws.onerror = fail;\n ws.onclose = fail;\n });\n }\n\n private wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private isActiveRun(generation: number, pc?: RTCPeerConnection): boolean {\n return (\n generation === this.lifecycleGeneration &&\n !this.stopped &&\n !this.runAbort?.signal.aborted &&\n (!pc || this.pc === pc)\n );\n }\n\n private assertActiveRun(generation: number, pc?: RTCPeerConnection): void {\n if (!this.isActiveRun(generation, pc)) {\n throw new PublisherStoppedError(\"publisher stopped\");\n }\n }\n\n private armPeerConnectionTimeout(generation: number, pc: RTCPeerConnection): void {\n this.clearPeerConnectionTimeout();\n if (pc.connectionState === \"connected\") return;\n const timeoutMs = Math.max(\n 0,\n this.opts.peerConnectionTimeoutMs ?? defaultPeerConnectionTimeoutMs,\n );\n const timer = setTimeout(() => {\n if (this.peerConnectionTimer !== timer) return;\n this.peerConnectionTimer = null;\n if (!this.isActiveRun(generation, pc) || pc.connectionState === \"connected\") return;\n this.terminateWithError(\n new Error(`WebRTC connection timed out after ${timeoutMs}ms`),\n true,\n generation,\n );\n }, timeoutMs);\n this.peerConnectionTimer = timer;\n }\n\n private clearPeerConnectionTimeout(): void {\n if (this.peerConnectionTimer !== null) clearTimeout(this.peerConnectionTimer);\n this.peerConnectionTimer = null;\n }\n\n private terminateWithError(\n err: Error,\n notify = true,\n generation = this.lifecycleGeneration,\n ): void {\n if (generation !== this.lifecycleGeneration) return;\n this.lifecycleGeneration++;\n this.runAbort?.abort();\n this.runAbort = null;\n this.clearPeerConnectionTimeout();\n this.stopped = true;\n this.cancelAllMediaRecovery();\n this.reconnectGeneration++;\n this.resumeSocket?.close();\n this.resumeSocket = null;\n this.sig?.close();\n this.sig = null;\n this.rejectSignalingWaiters(err);\n this.pc?.close();\n this.pc = null;\n this.textChannel = null;\n this.speechEnabled = false;\n this.speechPending = false;\n this.speechTransceiver = null;\n this.microphoneTransceiver = null;\n this.typeSenders.clear();\n this.unwatchStreamTracks();\n this.stopPublishedTracks();\n this.clearIntentionalTrackEnds();\n this.rejectPendingAnswer(new Error(\"publisher terminated\"));\n this.pendingOffer = null;\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = false;\n this.clearRetainedICECandidates();\n this.readToken = null;\n this.gatewayURL = null;\n this.lastReportedICEPath = null;\n if (notify) this.opts.callbacks?.onError?.(err);\n }\n\n private handleSignal(msg: SignalMessage): void {\n switch (msg.type) {\n case \"answer\": {\n if (!this.pc) return;\n const pending = this.pendingAnswer;\n if (!pending) break;\n // Correlate the answer with the outstanding offer by negotiation id. An\n // answer whose id does not match the current pending offer is for a\n // superseded offer (or arrived after a timeout) and must be dropped — this\n // is what prevents an answer being applied to the wrong offer. Older\n // servers omit the id; then there is only ever one outstanding offer, so\n // an unlabelled answer is accepted.\n if (msg.negotiation_id && msg.negotiation_id !== pending.id) break;\n this.pendingAnswer = null;\n pending.resolve(msg.sdp);\n break;\n }\n\n case \"ice_candidate\": {\n if (!this.pc) return;\n if (!this.retainRemoteCandidate(msg)) return;\n const init: RTCIceCandidateInit = {\n candidate: msg.candidate,\n sdpMid: msg.sdp_mid ?? null,\n sdpMLineIndex: msg.sdp_mline_index ?? null,\n usernameFragment: msg.username_fragment ?? null,\n };\n if (this.hasAnswer) {\n this.pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n } else {\n this.pendingRemoteCandidates.push(init);\n }\n break;\n }\n\n case \"connection_state\": {\n // The server echoes the peer connection state; the onconnectionstatechange\n // handler above already covers this, but callers can also react via\n // onConnectionStateChange.\n break;\n }\n\n case \"media_track_ended\": {\n // The microphone is not subject to the video recovery ladder: a mic that\n // stops simply ends transcription for the stream, so never attempt sender/\n // ICE recovery for it.\n if (msg.track === \"audio\") break;\n // A media_track_ended for a track intentionally removed by publish or\n // unpublish is expected, not a capture failure. New servers identify the\n // physical track; the type-only fallback keeps compatibility with older\n // servers but cannot distinguish two generations of the same type.\n if (this.consumeIntentionalTrackEnd(msg.track, msg.track_id)) break;\n void this.beginMediaRecovery(msg.track);\n break;\n }\n\n case \"media_stall\": {\n // Audio is never \"recovered\" (see media_track_ended above).\n if (msg.track === \"audio\") break;\n void this.beginMediaRecovery(msg.track);\n break;\n }\n\n case \"media_resumed\": {\n this.completeMediaRecovery(msg.track);\n break;\n }\n\n case \"error\": {\n const err = new ReportedPublisherError(msg.error, msg.fatal === true);\n const pending = this.pendingAnswer;\n let matchedPending = false;\n if (\n pending &&\n (!msg.negotiation_id || msg.negotiation_id === pending.id)\n ) {\n matchedPending = true;\n this.pendingAnswer = null;\n pending.reject(err);\n }\n if (err.fatal) this.opts.callbacks?.onError?.(err);\n if (err.fatal && !matchedPending) {\n this.terminateWithError(err, false);\n }\n break;\n }\n\n case \"resumed\":\n break;\n }\n }\n\n private async beginMediaRecovery(trackType: TrackType): Promise<void> {\n const state = this.recoveryState(trackType);\n if (this.stopped || state.recovering || state.required) return;\n\n const liveTracks = this.tracksOfType(trackType).filter(\n (track) => track.readyState !== \"ended\",\n );\n if (liveTracks.length === 0) {\n this.failMediaRecovery(trackType, \"capture_ended\");\n return;\n }\n\n state.recovering = true;\n state.generation = ++this.recoverySequence;\n const generation = state.generation;\n state.action = \"sender_restart\";\n this.emitRecoveryTransition({ state: \"recovering\", track: trackType, action: \"sender_restart\" });\n this.sendRecoveryDiagnostic(\"recovery_started\", trackType, \"sender_restart\");\n\n await this.restartSenders(trackType, liveTracks, generation);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n // This observation window begins only after the sender-restart offer has\n // reached the queue head and completed. Time spent behind another offer is\n // not evidence that sender recovery failed.\n await this.wait(senderRecoveryWaitMs);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n state.action = \"ice_restart\";\n this.emitRecoveryTransition({ state: \"recovering\", track: trackType, action: \"ice_restart\" });\n this.sendRecoveryDiagnostic(\"recovery_retry\", trackType, \"ice_restart\");\n try {\n await this.sharedIceRestart();\n } catch {\n // Media may still resume without a successful answer, so retain the\n // observation window before requiring host intervention.\n }\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n // As above, start the deadline only after the shared ICE attempt has run.\n await this.wait(iceRecoveryWaitMs);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n this.failMediaRecovery(trackType, \"automatic_recovery_failed\");\n }\n\n private async restartSenders(\n trackType: TrackType,\n tracks: MediaStreamTrack[],\n generation: number,\n ): Promise<void> {\n const lifecycleGeneration = this.lifecycleGeneration;\n try {\n await this.enqueueNegotiation(async () => {\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n const live = new Set(tracks);\n const senders = this.pc?.getSenders().filter(\n (sender) => sender.track && live.has(sender.track),\n ) ?? [];\n if (senders.length === 0) return false;\n\n const originals = senders.map((sender) => ({ sender, track: sender.track! }));\n try {\n await Promise.all(originals.map(({ sender }) => sender.replaceTrack(null)));\n await this.wait(senderRestartPauseMs);\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n await Promise.all(originals.map(({ sender, track }) => sender.replaceTrack(track)));\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n return;\n } finally {\n // Cancellation or a partial replaceTrack failure must never leave a\n // still-published sender detached.\n const restored = await Promise.allSettled(originals.map(async ({ sender, track }) => {\n if (\n sender.track === null &&\n this.published.has(track) &&\n track.readyState !== \"ended\"\n ) {\n await sender.replaceTrack(track);\n }\n }));\n if (restored.some((result) => result.status === \"rejected\")) {\n throw new SenderRestoreError(\"failed to restore a detached media sender\");\n }\n }\n });\n } catch (err) {\n if (\n err instanceof SenderRestoreError &&\n lifecycleGeneration === this.lifecycleGeneration &&\n !this.stopped\n ) {\n this.terminateWithError(err, true, lifecycleGeneration);\n return;\n }\n // ICE restart is the second stage and may still recover a sender reset or\n // renegotiation failure, so do not fail the stream at this stage.\n }\n }\n\n /**\n * Adds a recovery renegotiation to the same queue as user operations. The\n * recovery ladder awaits its completion before starting the stage observation\n * window. If recovery has completed by the time this reaches the head of the\n * queue, it is skipped.\n */\n /** Returns the peer-wide ICE attempt shared by all active track recoveries. */\n private sharedIceRestart(): Promise<void> {\n if (this.iceRestartAttempt) return this.iceRestartAttempt.promise;\n const id = ++this.iceRestartSequence;\n const promise = this.enqueueNegotiation(\n () => {\n if (\n this.iceRestartAttempt?.id !== id ||\n !this.hasActiveMediaRecovery()\n ) return false;\n },\n { iceRestart: true },\n );\n const attempt = { id, promise };\n this.iceRestartAttempt = attempt;\n // Coalesce only overlapping attempts. Retaining a completed promise while\n // another track remains in its observation window would make a later stall\n // incorrectly reuse an ICE restart that happened before that stall.\n void promise.finally(() => {\n if (this.iceRestartAttempt === attempt) this.iceRestartAttempt = null;\n }).catch(() => {});\n return promise;\n }\n\n /**\n * Serializes a renegotiation onto the shared chain: it waits for any prior\n * negotiation to finish (its answer applied), sends a fresh offer, and resolves\n * only once this offer's answer has been applied. This prevents overlapping\n * offers and answers being applied to the wrong offer.\n */\n private enqueueNegotiation(\n mutate: NegotiationMutation,\n opts: { iceRestart?: boolean } = {},\n ): Promise<void> {\n const generation = this.lifecycleGeneration;\n const run = this.negotiationChain\n .catch(() => {\n // A failed prior negotiation must not permanently break the chain; the\n // caller that owned it already saw the rejection.\n })\n .then(() => {\n this.assertActiveRun(generation);\n return this.negotiateOnce(mutate, opts, generation);\n });\n // Keep the chain alive regardless of this negotiation's outcome.\n this.negotiationChain = run.catch(() => {});\n return run;\n }\n\n private async negotiateOnce(\n mutate: NegotiationMutation,\n opts: { iceRestart?: boolean },\n generation: number,\n ): Promise<void> {\n const pc = this.pc;\n if (!pc || !this.isActiveRun(generation, pc)) {\n throw new PublisherStoppedError(\"publisher stopped\");\n }\n if (!this.runAbort?.signal) throw new PublisherStoppedError(\"publisher stopped\");\n const previousHasAnswer = this.hasAnswer;\n const previousRemoteCandidates = this.pendingRemoteCandidates;\n const previousLocalCandidateOfferSent = this.localCandidateOfferSent;\n const previousRetainedLocalCandidates = [...this.retainedLocalCandidates];\n const previousRetainedLocalCandidateKeys = new Set(this.retainedLocalCandidateKeys);\n const previousLocalCandidateGeneration = this.localCandidateGeneration;\n\n // Do not mutate tracks while signaling is unavailable: if reconnection\n // ultimately fails, the previously published state remains intact.\n await this.awaitSignaling();\n this.assertActiveRun(generation, pc);\n\n let change: NegotiationChange | undefined;\n let localOfferSet = false;\n let answerReceived = false;\n try {\n // Apply the staged track change at the head of this turn, atomic with the\n // offer it produces. A callback returning false skips negotiation.\n const result = await mutate();\n this.assertActiveRun(generation, pc);\n if (result === false) return;\n if (result && typeof result === \"object\") change = result;\n\n if (opts.iceRestart) pc.restartIce?.();\n const offer = await pc.createOffer(opts.iceRestart ? { iceRestart: true } : undefined);\n this.assertActiveRun(generation, pc);\n this.beginLocalCandidateBatch();\n await pc.setLocalDescription(offer);\n localOfferSet = true;\n this.assertActiveRun(generation, pc);\n\n const local = pc.localDescription;\n if (!local) throw new Error(\"local description missing\");\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n\n const id = this.nextNegotiationId();\n const { answered } = await this.sendOffer({\n type: \"offer\",\n sdp: local.sdp,\n sdp_type: \"offer\",\n negotiation_id: id,\n tracks: change?.labels?.() ?? this.buildTrackLabels(),\n speech_enabled: (this.speechEnabled || this.speechPending) || undefined,\n });\n this.releaseLocalCandidateBatch();\n const sdp = await answered;\n this.assertActiveRun(generation, pc);\n answerReceived = true;\n await pc.setRemoteDescription(new RTCSessionDescription({ type: \"answer\", sdp }));\n this.assertActiveRun(generation, pc);\n this.applyAnswered(pc);\n await change?.commit?.();\n } catch (err) {\n let rollbackFailed = false;\n if (localOfferSet && this.pc === pc && pc.signalingState === \"have-local-offer\") {\n try {\n await pc.setLocalDescription({ type: \"rollback\" });\n } catch {\n rollbackFailed = true;\n }\n }\n try {\n await change?.rollback?.();\n } catch {\n rollbackFailed = true;\n }\n\n // Once an answer was received, or delivery timed out, the server's state\n // is ambiguous. Tear down coherently instead of continuing divergent.\n const ambiguous =\n rollbackFailed ||\n answerReceived ||\n err instanceof NegotiationTimeoutError ||\n err instanceof SenderRestoreError ||\n (err instanceof ReportedPublisherError && err.fatal);\n if (this.isActiveRun(generation, pc) && ambiguous) {\n try {\n await change?.discard?.();\n } catch {\n // The peer is terminal regardless; teardown below owns all committed\n // captures and the discard hook is best-effort for staged inputs.\n }\n const failure = err instanceof Error ? err : new Error(String(err));\n this.terminateWithError(\n failure,\n !(err instanceof ReportedPublisherError),\n generation,\n );\n } else if (this.isActiveRun(generation, pc)) {\n // A pre-application rejection leaves the previous remote description\n // authoritative. Restore candidate routing and flush candidates that\n // arrived while the rejected offer was outstanding.\n const buffered = this.pendingRemoteCandidates;\n this.hasAnswer = previousHasAnswer;\n this.pendingRemoteCandidates = previousRemoteCandidates;\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = previousLocalCandidateOfferSent;\n this.retainedLocalCandidates = previousRetainedLocalCandidates;\n this.retainedLocalCandidateKeys = previousRetainedLocalCandidateKeys;\n this.localCandidateGeneration = previousLocalCandidateGeneration;\n if (previousHasAnswer) {\n for (const init of buffered) {\n pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n }\n }\n }\n throw err;\n }\n }\n\n /**\n * Registers interest in the answer for the offer identified by `id` and returns\n * a promise for its SDP. A second pending answer is an invariant violation: all\n * offer creation, including recovery, must pass through negotiationChain.\n */\n private nextNegotiationId(): string {\n return `n${++this.negotiationSeq}`;\n }\n\n private handleTextMessage(data: unknown): void {\n if (typeof data !== \"string\") return;\n try {\n const message = JSON.parse(data) as {\n type?: string; utterance_id?: string; message_id?: string; text?: string; reason?: string;\n };\n if (message.type === \"assistant_text\" && message.utterance_id && message.text) {\n this.opts.callbacks?.onAssistantText?.({ utteranceId: message.utterance_id, text: message.text });\n } else if (message.type === \"assistant_text_finished\" && message.utterance_id) {\n this.opts.callbacks?.onAssistantTextFinished?.({ utteranceId: message.utterance_id });\n } else if ((message.type === \"user_text_accepted\" || message.type === \"user_text_rejected\") && message.message_id) {\n this.opts.callbacks?.onUserTextResult?.({\n messageId: message.message_id,\n accepted: message.type === \"user_text_accepted\",\n reason: message.reason,\n });\n }\n } catch {\n // Unknown or malformed application messages are ignored; they do not\n // compromise the WebRTC transport.\n }\n }\n\n private awaitAnswer(id: string): Promise<string> {\n if (this.pendingAnswer) {\n return Promise.reject(new Error(\"another negotiation is already awaiting an answer\"));\n }\n return new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pendingAnswer?.id === id) {\n this.pendingAnswer = null;\n reject(new NegotiationTimeoutError(\"timed out waiting for renegotiation answer\"));\n }\n }, this.negotiationAnswerTimeoutMs());\n this.pendingAnswer = {\n id,\n resolve: (sdp) => {\n clearTimeout(timer);\n resolve(sdp);\n },\n reject: (err) => {\n clearTimeout(timer);\n reject(err);\n },\n };\n });\n }\n\n private negotiationAnswerTimeoutMs(): number {\n const reconnectTimeout =\n this.opts.signalingReconnectTimeoutMs ?? defaultSignalingReconnectTimeoutMs;\n return Math.max(\n minimumNegotiationAnswerTimeoutMs,\n Math.max(0, reconnectTimeout) + negotiationReconnectGraceMs,\n );\n }\n\n private handleLocalICECandidate(candidate: RTCIceCandidate): void {\n const message: ICECandidateSignalMessage = {\n type: \"ice_candidate\",\n candidate: candidate.candidate,\n sdp_mid: candidate.sdpMid ?? undefined,\n sdp_mline_index: candidate.sdpMLineIndex ?? undefined,\n username_fragment: candidate.usernameFragment ?? undefined,\n };\n if (!this.retainLocalCandidate(message)) return;\n if (!this.localCandidateOfferSent) {\n this.pendingLocalCandidates.push(message);\n return;\n }\n void this.sendWhenSignalingAvailable(message).catch(() => {\n // stop()/fatal teardown owns the terminal error; an obsolete candidate\n // does not need a second user-facing failure.\n });\n }\n\n private candidateKey(candidate: ICECandidateSignalMessage): string {\n return JSON.stringify([\n candidate.candidate,\n candidate.sdp_mid ?? null,\n candidate.sdp_mline_index ?? null,\n candidate.username_fragment ?? null,\n ]);\n }\n\n private retainLocalCandidate(candidate: ICECandidateSignalMessage): boolean {\n const generation = candidate.username_fragment;\n if (generation) {\n if (this.localCandidateGeneration && this.localCandidateGeneration !== generation) {\n this.retainedLocalCandidates = [];\n this.retainedLocalCandidateKeys.clear();\n }\n this.localCandidateGeneration = generation;\n }\n const key = this.candidateKey(candidate);\n if (this.retainedLocalCandidateKeys.has(key)) return false;\n if (this.retainedLocalCandidates.length === maxRetainedICECandidates) {\n const evicted = this.retainedLocalCandidates.shift();\n if (evicted) this.retainedLocalCandidateKeys.delete(this.candidateKey(evicted));\n }\n this.retainedLocalCandidates.push(candidate);\n this.retainedLocalCandidateKeys.add(key);\n return true;\n }\n\n private retainRemoteCandidate(candidate: ICECandidateSignalMessage): boolean {\n const generation = candidate.username_fragment;\n if (generation) {\n if (this.remoteCandidateGeneration && this.remoteCandidateGeneration !== generation) {\n this.remoteCandidateKeys.clear();\n this.remoteCandidateOrder = [];\n }\n this.remoteCandidateGeneration = generation;\n }\n const key = this.candidateKey(candidate);\n if (this.remoteCandidateKeys.has(key)) return false;\n if (this.remoteCandidateOrder.length === maxRetainedICECandidates) {\n const evicted = this.remoteCandidateOrder.shift();\n if (evicted) this.remoteCandidateKeys.delete(evicted);\n }\n this.remoteCandidateOrder.push(key);\n this.remoteCandidateKeys.add(key);\n return true;\n }\n\n private clearRetainedICECandidates(): void {\n this.retainedLocalCandidates = [];\n this.retainedLocalCandidateKeys.clear();\n this.localCandidateGeneration = null;\n this.remoteCandidateKeys.clear();\n this.remoteCandidateOrder = [];\n this.remoteCandidateGeneration = null;\n }\n\n private beginLocalCandidateBatch(): void {\n this.localCandidateOfferSent = false;\n this.pendingLocalCandidates = [];\n }\n\n private releaseLocalCandidateBatch(): void {\n this.localCandidateOfferSent = true;\n const candidates = this.pendingLocalCandidates;\n this.pendingLocalCandidates = [];\n for (const candidate of candidates) {\n void this.sendWhenSignalingAvailable(candidate).catch(() => {\n // stop()/fatal teardown owns the terminal error.\n });\n }\n }\n\n // applyAnswered flushes ICE candidates buffered before the answer landed.\n private applyAnswered(pc: RTCPeerConnection): void {\n if (this.pc !== pc) return;\n this.hasAnswer = true;\n for (const init of this.pendingRemoteCandidates) {\n pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n }\n this.pendingRemoteCandidates = [];\n this.watchSelectedICEPairChanges(pc);\n void this.reportSelectedICEPath(pc);\n }\n\n private watchSelectedICEPairChanges(pc: RTCPeerConnection): void {\n try {\n const dtlsTransports = [\n pc.sctp?.transport,\n ...pc.getSenders().map((sender) => sender.transport),\n ...pc.getReceivers().map((receiver) => receiver.transport),\n ];\n for (const dtls of dtlsTransports) {\n const ice = dtls?.iceTransport;\n if (!ice || this.watchedICETransports.has(ice)) continue;\n this.watchedICETransports.add(ice);\n ice.addEventListener(\"selectedcandidatepairchange\", () => {\n void this.reportSelectedICEPath(pc);\n });\n }\n } catch {\n // Path reporting is diagnostic-only. A browser with partial transport\n // introspection must still be able to publish normally; the connected\n // state callback will retain the one-shot getStats fallback.\n }\n }\n\n private async reportSelectedICEPath(pc: RTCPeerConnection): Promise<void> {\n if (this.pc !== pc || this.stopped) return;\n\n let stats: RTCStatsReport;\n try {\n stats = await pc.getStats();\n } catch {\n return;\n }\n if (this.pc !== pc || this.stopped) return;\n\n let selectedPairID: string | undefined;\n let selectedPair: Record<string, unknown> | undefined;\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (value.type === \"transport\" && typeof value.selectedCandidatePairId === \"string\") {\n selectedPairID = value.selectedCandidatePairId;\n }\n });\n if (selectedPairID) {\n selectedPair = stats.get(selectedPairID) as unknown as Record<string, unknown> | undefined;\n }\n if (!selectedPair) {\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (\n !selectedPair &&\n value.type === \"candidate-pair\" &&\n value.state === \"succeeded\" &&\n value.nominated === true\n ) {\n selectedPair = value;\n }\n });\n }\n if (!selectedPair) return;\n\n const localID = selectedPair.localCandidateId;\n const remoteID = selectedPair.remoteCandidateId;\n if (typeof localID !== \"string\") return;\n const local = stats.get(localID) as unknown as Record<string, unknown> | undefined;\n const remote = typeof remoteID === \"string\"\n ? stats.get(remoteID) as unknown as Record<string, unknown> | undefined\n : undefined;\n if (!local || typeof local.candidateType !== \"string\") return;\n\n const message: ICEPathSignalMessage = {\n type: \"ice_path\",\n local_candidate_type: local.candidateType,\n local_protocol: typeof local.protocol === \"string\" ? local.protocol : undefined,\n remote_candidate_type: typeof remote?.candidateType === \"string\" ? remote.candidateType : undefined,\n remote_protocol: typeof remote?.protocol === \"string\" ? remote.protocol : undefined,\n relay_protocol: typeof local.relayProtocol === \"string\" ? local.relayProtocol : undefined,\n turn_url: typeof local.url === \"string\" ? local.url : undefined,\n };\n const fingerprint = JSON.stringify(message);\n if (fingerprint === this.lastReportedICEPath) return;\n this.lastReportedICEPath = fingerprint;\n try {\n await this.sendWhenSignalingAvailable(message);\n } catch {\n // Signaling teardown owns the terminal error. A later selected-pair event\n // will report the current path again if the connection remains active.\n if (this.lastReportedICEPath === fingerprint) {\n this.lastReportedICEPath = null;\n }\n }\n }\n\n private completeMediaRecovery(trackType: TrackType): void {\n const state = this.recoveryStates.get(trackType);\n if (!state?.recovering) return;\n const action = state.action ?? undefined;\n this.cancelMediaRecovery(trackType);\n this.emitRecoveryTransition({ state: \"recovered\", track: trackType, action });\n }\n\n private failMediaRecovery(\n trackType: TrackType,\n reason: \"capture_ended\" | \"automatic_recovery_failed\",\n ): void {\n const state = this.recoveryState(trackType);\n if (this.stopped || state.required) return;\n state.required = true;\n const action = state.action ?? undefined;\n this.cancelMediaRecovery(trackType);\n const event: PublisherRecoveryEvent = { state: \"failed\", track: trackType, action, reason };\n this.emitRecoveryTransition(event);\n this.opts.callbacks?.onRecoveryRequired?.(event);\n this.sendRecoveryDiagnostic(\"recovery_failed\", trackType, action, reason);\n }\n\n private recoveryState(trackType: TrackType): TrackRecoveryState {\n let state = this.recoveryStates.get(trackType);\n if (!state) {\n state = { generation: 0, recovering: false, required: false, action: null };\n this.recoveryStates.set(trackType, state);\n }\n return state;\n }\n\n private cancelMediaRecovery(trackType: TrackType): void {\n const state = this.recoveryState(trackType);\n state.generation = ++this.recoverySequence;\n state.recovering = false;\n state.action = null;\n this.clearSharedIceRestartIfIdle();\n }\n\n private cancelAllMediaRecovery(): void {\n for (const trackType of this.recoveryStates.keys()) {\n this.cancelMediaRecovery(trackType);\n }\n }\n\n private isCurrentRecovery(trackType: TrackType, generation: number): boolean {\n const state = this.recoveryStates.get(trackType);\n return !this.stopped && !!state?.recovering && state.generation === generation;\n }\n\n private hasActiveMediaRecovery(): boolean {\n for (const state of this.recoveryStates.values()) {\n if (state.recovering) return true;\n }\n return false;\n }\n\n private clearSharedIceRestartIfIdle(): void {\n if (!this.hasActiveMediaRecovery()) this.iceRestartAttempt = null;\n }\n\n private emitRecoveryTransition(event: PublisherRecoveryEvent): void {\n this.opts.callbacks?.onRecoveryStateChange?.(event);\n }\n\n private sendRecoveryDiagnostic(\n event: \"recovery_started\" | \"recovery_retry\" | \"recovery_failed\",\n track: TrackType,\n action?: PublisherRecoveryAction,\n reason?: \"capture_ended\" | \"automatic_recovery_failed\",\n ): void {\n this.sig?.send({ type: \"recovery_event\", event, track, action, reason });\n }\n\n // -------------------------------------------------------------------------\n // Published-track bookkeeping\n // -------------------------------------------------------------------------\n\n private requireSingleVideoTrack(stream: MediaStream): MediaStreamTrack {\n const tracks = stream.getVideoTracks();\n if (tracks.length !== 1) {\n throw new Error(\n `expected exactly one video track, received ${tracks.length}`,\n );\n }\n this.requireLiveVideoTrack(tracks[0]);\n return tracks[0];\n }\n\n private requireLiveVideoTrack(track: MediaStreamTrack): void {\n if (track.readyState === \"ended\") {\n throw new Error(\"video track has already ended\");\n }\n }\n\n private requireLiveTrack(track: MediaStreamTrack, kind: \"video\" | \"audio\"): void {\n if (track.readyState === \"ended\") {\n throw new Error(`${kind} track has already ended`);\n }\n }\n\n private requireSingleAudioTrack(stream: MediaStream): MediaStreamTrack {\n const tracks = stream.getAudioTracks();\n if (tracks.length !== 1) {\n throw new Error(\n `expected exactly one audio track, received ${tracks.length}`,\n );\n }\n if (tracks[0].readyState === \"ended\") {\n throw new Error(\"audio track has already ended\");\n }\n return tracks[0];\n }\n\n /**\n * Stages the microphone track onto the peer connection. Unlike video, audio\n * has no recovery ladder and no SSIM/frame semantics, so this simply adds (or\n * replaces) the single audio sender and declares the updated labels.\n */\n private async stagePublishAudio(\n track: MediaStreamTrack,\n stream: MediaStream,\n ): Promise<NegotiationMutationResult> {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n if (track.readyState === \"ended\") throw new Error(\"audio track has already ended\");\n\n const type: TrackType = \"audio\";\n if (this.published.get(track) === type) return false;\n\n const previous = this.tracksOfType(type).map((oldTrack) => ({\n track: oldTrack,\n stream: this.publishedStreams.get(oldTrack)!,\n }));\n const typeSender = this.typeSenders.get(type) ?? null;\n\n let addedSender: RTCRtpSender | null = null;\n let replacedSender: RTCRtpSender | null = null;\n if (previous.length > 0) {\n if (previous.length !== 1 || !typeSender || typeSender.track !== previous[0].track) {\n throw new SenderRestoreError(\"published audio sender is unavailable\");\n }\n replacedSender = typeSender;\n await replacedSender.replaceTrack(track);\n } else {\n if (typeSender?.track) {\n throw new SenderRestoreError(\"inactive audio sender still has a track\");\n }\n if (\n this.microphoneTransceiver &&\n this.microphoneTransceiver.sender.track === null\n ) {\n addedSender = this.microphoneTransceiver.sender;\n await addedSender.replaceTrack(track);\n this.microphoneTransceiver.direction = \"sendonly\";\n } else {\n addedSender = this.addMicrophoneTrack(pc, track, stream);\n }\n this.typeSenders.set(type, addedSender);\n }\n\n return {\n labels: () => this.labelsReplacingType(type, track),\n commit: () => {\n for (const { track: oldTrack } of previous) {\n this.unwatchTrack(oldTrack);\n this.published.delete(oldTrack);\n this.publishedStreams.delete(oldTrack);\n oldTrack.stop();\n }\n // A microphone has lifecycle observation but never enters watchTrack's\n // video recovery ladder.\n this.published.set(track, type);\n this.publishedStreams.set(track, stream);\n this.watchMicrophone(track);\n },\n rollback: async () => {\n if (this.pc !== pc) return;\n if (addedSender && pc.getSenders().includes(addedSender)) {\n pc.removeTrack(addedSender);\n if (typeSender) this.typeSenders.set(type, typeSender);\n else this.typeSenders.delete(type);\n }\n if (replacedSender && previous.length > 0) {\n const oldTrack = previous[0].track;\n if (oldTrack.readyState !== \"ended\") {\n await replacedSender.replaceTrack(oldTrack);\n }\n } else if (replacedSender) {\n pc.removeTrack(replacedSender);\n }\n },\n discard: () => track.stop(),\n };\n }\n\n /**\n * Gives the microphone its own sendonly transceiver. addTrack() may reuse an\n * existing compatible recvonly transceiver, which would collapse the\n * microphone and assistant speech roles when speech was enabled first.\n * addTransceiver() always creates a distinct m-line and its direction keeps\n * the server's outbound speech sender on the dedicated speech transceiver.\n */\n private addMicrophoneTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack,\n stream: MediaStream,\n ): RTCRtpSender {\n const transceiver = pc.addTransceiver(track, {\n direction: \"sendonly\",\n streams: [stream],\n });\n this.microphoneTransceiver = transceiver;\n return transceiver.sender;\n }\n\n /**\n * Reorders the codecs a video sender offers so the browser prefers the\n * configured codecs (VP9 by default). This is what actually controls the wire\n * format: the browser is the offerer and sends its own top-of-offer codec, and\n * the media server (Pion) answers by mirroring the offer's codec order — so the\n * server's own registration order has no say. Floating VP9 to the front of the\n * offer is therefore the lever. Codecs not in the preference keep their native\n * order behind it, so anything VP9 can't satisfy still negotiates.\n *\n * Best-effort: browsers lacking `getCapabilities`/`setCodecPreferences` (older\n * Safari) keep their default order, and any failure is swallowed — codec\n * preference is an optimization, never a requirement for publishing.\n */\n private preferVideoCodecs(pc: RTCPeerConnection, sender: RTCRtpSender): void {\n const preferred = this.opts.preferredVideoCodecs ?? [\"video/VP9\"];\n if (preferred.length === 0) return;\n if (sender.track && sender.track.kind !== \"video\") return;\n // `typeof RTCRtpSender` guards environments where the global is absent\n // entirely (test harness, non-WebRTC runtimes) — a bare member access there\n // would throw a ReferenceError.\n if (typeof RTCRtpSender === \"undefined\" || typeof RTCRtpSender.getCapabilities !== \"function\") return;\n if (typeof pc.getTransceivers !== \"function\") return;\n\n const caps = RTCRtpSender.getCapabilities(\"video\");\n if (!caps?.codecs) return;\n const transceiver = pc.getTransceivers().find((t) => t.sender === sender);\n if (!transceiver || typeof transceiver.setCodecPreferences !== \"function\") return;\n\n const rank = (mimeType: string): number => {\n const idx = preferred.findIndex((p) => p.toLowerCase() === mimeType.toLowerCase());\n return idx === -1 ? preferred.length : idx;\n };\n // Stable reorder: preferred codecs first in the configured order; everything\n // else — including RTX, which the browser re-associates by its apt fmtp\n // regardless of position — keeps its original order.\n const ordered = caps.codecs\n .map((codec, index) => ({ codec, index }))\n .sort((a, b) => rank(a.codec.mimeType) - rank(b.codec.mimeType) || a.index - b.index)\n .map((entry) => entry.codec);\n\n try {\n transceiver.setCodecPreferences(ordered);\n } catch {\n // Ignore: an unsupported preference just leaves the native order in place.\n }\n }\n\n /** Registers one physical video track under its logical type and source stream. */\n private registerTrack(track: MediaStreamTrack, stream: MediaStream, type: TrackType): void {\n this.published.set(track, type);\n this.publishedStreams.set(track, stream);\n this.watchTrack(track);\n }\n\n /** The live video tracks currently published under the given type. */\n private tracksOfType(type: TrackType): MediaStreamTrack[] {\n const out: MediaStreamTrack[] = [];\n for (const [track, tt] of this.published) {\n if (tt === type) out.push(track);\n }\n return out;\n }\n\n /** Builds the id → type label array declared to the server on every offer. */\n private buildTrackLabels(): TrackLabel[] {\n const labels: TrackLabel[] = [];\n for (const [track, type] of this.published) {\n const mid = this.midForTrack(track);\n if (mid !== null) labels.push({ mid, id: track.id, type });\n }\n return labels;\n }\n\n private labelsReplacingType(type: TrackType, replacement?: MediaStreamTrack): TrackLabel[] {\n const labels: TrackLabel[] = [];\n for (const [track, publishedType] of this.published) {\n if (publishedType === type) continue;\n const mid = this.midForTrack(track);\n if (mid !== null) labels.push({ mid, id: track.id, type: publishedType });\n }\n if (replacement) {\n const mid = this.midForTrack(replacement);\n if (mid !== null) labels.push({ mid, id: replacement.id, type });\n }\n return labels;\n }\n\n /**\n * The negotiated mid of the transceiver currently sending `track`, or null if\n * none is found or it has not been negotiated yet. The mid is the identifier\n * both peers agree on; it is assigned once setLocalDescription runs, which the\n * publisher always does before sending an offer's labels.\n */\n private midForTrack(track: MediaStreamTrack): string | null {\n const transceiver = this.pc\n ?.getTransceivers()\n .find((candidate) => candidate.sender.track === track);\n return transceiver?.mid ?? null;\n }\n\n private async stagePublish(\n track: MediaStreamTrack,\n stream: MediaStream,\n type: VideoTrackType,\n ): Promise<NegotiationMutationResult> {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n this.requireLiveVideoTrack(track);\n\n const alreadyPublishedAs = this.published.get(track);\n if (alreadyPublishedAs === type) return false;\n if (alreadyPublishedAs) {\n throw new Error(`video track is already published as ${alreadyPublishedAs}`);\n }\n\n const previous = this.tracksOfType(type).map((oldTrack) => ({\n track: oldTrack,\n stream: this.publishedStreams.get(oldTrack)!,\n }));\n const typeSender = this.typeSenders.get(type) ?? null;\n\n let addedSender: RTCRtpSender | null = null;\n let replacedSender: RTCRtpSender | null = null;\n if (previous.length > 0) {\n if (\n previous.length !== 1 ||\n !typeSender ||\n typeSender.track !== previous[0].track\n ) {\n throw new SenderRestoreError(`published ${type} sender is unavailable`);\n }\n // Preserve the negotiated transceiver. removeTrack()+addTrack() is not a\n // reversible replacement: browsers may allocate a new m-line, breaking\n // rollback and growing SDP after every source change.\n replacedSender = typeSender;\n await replacedSender.replaceTrack(track);\n } else {\n if (typeSender?.track) {\n throw new SenderRestoreError(`inactive ${type} sender still has a track`);\n }\n // replaceTrack alone does not change an inactive transceiver back to a\n // sending direction. addTrack performs that transition and is specified\n // to reuse an eligible inactive transceiver when one is available.\n addedSender = pc.addTrack(track, stream);\n this.preferVideoCodecs(pc, addedSender);\n this.typeSenders.set(type, addedSender);\n }\n\n return {\n labels: () => this.labelsReplacingType(type, track),\n commit: () => {\n this.cancelMediaRecovery(type);\n this.recoveryState(type).required = false;\n for (const { track: oldTrack } of previous) {\n this.unwatchTrack(oldTrack);\n this.published.delete(oldTrack);\n this.publishedStreams.delete(oldTrack);\n oldTrack.stop();\n }\n this.registerTrack(track, stream, type);\n if (track.readyState === \"ended\") {\n this.failMediaRecovery(type, \"capture_ended\");\n }\n },\n rollback: async () => {\n if (this.pc !== pc) return;\n if (addedSender && pc.getSenders().includes(addedSender)) {\n pc.removeTrack(addedSender);\n if (typeSender) this.typeSenders.set(type, typeSender);\n else this.typeSenders.delete(type);\n }\n if (replacedSender && previous.length > 0) {\n const oldTrack = previous[0].track;\n if (oldTrack.readyState !== \"ended\") {\n await replacedSender.replaceTrack(oldTrack);\n }\n } else if (replacedSender) {\n pc.removeTrack(replacedSender);\n }\n },\n discard: () => track.stop(),\n };\n }\n\n private stageUnpublish(type: TrackType): NegotiationChange {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n const previous = this.tracksOfType(type).map((track) => ({\n track,\n stream: this.publishedStreams.get(track)!,\n }));\n const typeSender = this.typeSenders.get(type);\n if (\n previous.length !== 1 ||\n !typeSender ||\n typeSender.track !== previous[0].track\n ) {\n throw new SenderRestoreError(`published ${type} sender is unavailable`);\n }\n\n // The pending end is keyed on the browser track id — the generation identity\n // the server echoes in track_id. (The mid is unsuitable: it is reused when a\n // track of the same type is republished on the same m-line.)\n for (const { track } of previous) this.expectIntentionalTrackEnd(track.id, type);\n pc.removeTrack(typeSender);\n\n return {\n labels: () => this.labelsReplacingType(type),\n commit: () => {\n this.cancelMediaRecovery(type);\n this.recoveryState(type).required = false;\n this.typeSenders.delete(type);\n for (const { track } of previous) {\n this.unwatchTrack(track);\n this.published.delete(track);\n this.publishedStreams.delete(track);\n track.stop();\n }\n },\n rollback: () => {\n if (this.pc !== pc) return;\n this.forgetIntentionalTrackEnds(previous.map(({ track }) => track.id));\n return Promise.all(previous.map(async ({ track }) => {\n if (track.readyState === \"ended\") return;\n await typeSender.replaceTrack(track);\n })).then(() => undefined);\n },\n };\n }\n\n /** Stops every published local track and clears the published map. */\n private stopPublishedTracks(): void {\n for (const track of this.published.keys()) {\n track.stop();\n }\n this.published.clear();\n this.publishedStreams.clear();\n }\n\n private expectIntentionalTrackEnd(trackID: string, type: TrackType): void {\n const prior = this.intentionalTrackEnds.get(trackID);\n if (prior) clearTimeout(prior.timer);\n const retentionMs = Math.max(\n minimumIntentionalTrackEndRetentionMs,\n this.negotiationAnswerTimeoutMs(),\n );\n const timer = setTimeout(() => {\n const current = this.intentionalTrackEnds.get(trackID);\n if (current?.timer === timer) this.intentionalTrackEnds.delete(trackID);\n }, retentionMs);\n this.intentionalTrackEnds.set(trackID, { type, timer });\n }\n\n private forgetIntentionalTrackEnds(trackIDs: string[]): void {\n for (const trackID of trackIDs) {\n const expected = this.intentionalTrackEnds.get(trackID);\n if (!expected) continue;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(trackID);\n }\n }\n\n private consumeIntentionalTrackEnd(type: TrackType, trackID?: string): boolean {\n if (trackID) {\n const expected = this.intentionalTrackEnds.get(trackID);\n if (!expected || expected.type !== type) return false;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(trackID);\n return true;\n }\n\n // Older media servers did not identify the physical track. Consume the\n // oldest expected end of this type as a best-effort compatibility fallback.\n for (const [id, expected] of this.intentionalTrackEnds) {\n if (expected.type !== type) continue;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(id);\n return true;\n }\n return false;\n }\n\n private clearIntentionalTrackEnds(): void {\n for (const expected of this.intentionalTrackEnds.values()) {\n clearTimeout(expected.timer);\n }\n this.intentionalTrackEnds.clear();\n }\n\n /**\n * Watches a track's \"ended\" event so an involuntary capture stop (the user\n * revokes a screen share, a device unplugs) reports as a recovery failure for\n * that track's actual type. Intentional removals unwatch first.\n */\n private watchTrack(track: MediaStreamTrack): void {\n if (this.trackEndHandlers.has(track)) return;\n const handler: EventListener = () => {\n const type = this.published.get(track) ?? \"camera\";\n this.failMediaRecovery(type, \"capture_ended\");\n };\n track.addEventListener(\"ended\", handler);\n this.trackEndHandlers.set(track, handler);\n }\n\n /**\n * Watches a microphone only for lifecycle removal. An ended microphone is not\n * recovered like video; it is negotiated away so the media server can flush\n * the utterance and release transcription resources.\n */\n private watchMicrophone(track: MediaStreamTrack): void {\n if (this.trackEndHandlers.has(track)) return;\n const generation = this.lifecycleGeneration;\n const handler: EventListener = () => {\n if (this.published.get(track) !== \"audio\") return;\n void this.enqueueNegotiation(() => {\n // Recheck identity when this turn reaches the head of the queue. A user\n // may already be replacing this ended source; its delayed removal must\n // not unpublish that committed replacement.\n if (this.published.get(track) !== \"audio\") return false;\n return this.stageUnpublish(\"audio\");\n }).catch((err) => {\n if (!this.isActiveRun(generation)) return;\n const failure = err instanceof Error ? err : new Error(String(err));\n this.terminateWithError(failure, true, generation);\n });\n };\n track.addEventListener(\"ended\", handler);\n this.trackEndHandlers.set(track, handler);\n // The track may have ended while its publish negotiation was awaiting an\n // answer, before the committed source acquired this listener.\n if (track.readyState === \"ended\") handler(new Event(\"ended\"));\n }\n\n private unwatchTrack(track: MediaStreamTrack): void {\n const handler = this.trackEndHandlers.get(track);\n if (!handler) return;\n track.removeEventListener(\"ended\", handler);\n this.trackEndHandlers.delete(track);\n }\n\n private unwatchStreamTracks(): void {\n for (const [track, handler] of this.trackEndHandlers) {\n track.removeEventListener(\"ended\", handler);\n }\n this.trackEndHandlers.clear();\n }\n\n}\n","/**\n * Options for {@link captureCamera}.\n *\n * These are merged shallowly over the library defaults: any field you provide\n * replaces the default for that field entirely (e.g. passing `video` overrides\n * the default video constraints rather than merging into them). Omit a field to\n * keep its default.\n */\nexport interface CaptureCameraOptions {\n /**\n * Video constraints, or `true`/`false`. Defaults to a modest resolution and\n * frame rate (see {@link captureCamera}). Set `false` to disable video.\n */\n video?: MediaTrackConstraints | boolean;\n /**\n * Audio constraints, or `true`/`false`. Defaults to `false` — Argus is a\n * video-frame streaming system, so audio is off unless you ask for it.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`. Pass another window's `navigator.mediaDevices`\n * (e.g. a Document Picture-in-Picture window's) so the browser's permission\n * prompt and transient-activation check are anchored to that window rather\n * than the one holding the global `navigator`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Options for {@link captureScreen}.\n *\n * These are merged shallowly over the library defaults: any field you provide\n * replaces the default for that field entirely. Omit a field to keep its\n * default.\n */\nexport interface CaptureScreenOptions {\n /**\n * Video constraints, or `true`. Defaults to a capped width and a low frame\n * rate (see {@link captureScreen}).\n */\n video?: MediaTrackConstraints | boolean;\n /**\n * Audio constraints, or `true`/`false`. Defaults to `false` — screen shares\n * rarely need audio for a video-frame streaming system.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`. Pass another window's `navigator.mediaDevices`\n * (e.g. a Document Picture-in-Picture window's) so the screen picker and its\n * transient-activation check are anchored to that window rather than the one\n * holding the global `navigator`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Captures the user's camera via `navigator.mediaDevices.getUserMedia`,\n * applying sensible defaults for a video-frame streaming system.\n *\n * Defaults:\n * - `video`: `{ width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } }`\n * — a modest resolution that keeps upload bandwidth reasonable. Cameras are\n * rarely the 4k bandwidth problem that screen capture is, so this is an\n * `ideal` (a hint) rather than a hard cap.\n * - `audio`: `false` — this is a video-frame streaming system.\n *\n * Any option you pass replaces the corresponding default outright (shallow\n * merge), so pass a full `video` constraints object if you want to tweak it.\n *\n * @example\n * ```ts\n * const stream = await captureCamera();\n * await publisher.start(stream);\n * ```\n *\n * @example Front camera with audio:\n * ```ts\n * const stream = await captureCamera({\n * video: { facingMode: \"user\" },\n * audio: true,\n * });\n * ```\n */\nexport async function captureCamera(\n opts: CaptureCameraOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n video: opts.video ?? {\n width: { ideal: 1280 },\n height: { ideal: 720 },\n frameRate: { ideal: 30 },\n },\n audio: opts.audio ?? false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n}\n\n/**\n * Captures a screen / window / tab via\n * `navigator.mediaDevices.getDisplayMedia`, applying sensible defaults tuned to\n * avoid the HiDPI/Retina bandwidth trap.\n *\n * Defaults:\n * - `video`: `{ width: { max: 1920 }, frameRate: { ideal: 5, max: 10 } }`.\n * The `width` is a **`max`, not an `ideal`**: on a 2x-Retina display the\n * browser would otherwise capture at native resolution (often 3456px+ /\n * effectively 4k), wasting upload bandwidth and downstream decode cost for no\n * visible benefit. Capping the max roughly halves a 2x-Retina share while\n * leaving smaller displays untouched. Screen content is mostly static, so the\n * low frame rate saves further bandwidth.\n * - `audio`: `false`.\n *\n * IMPORTANT — do NOT add `resizeMode: \"none\"` here. That value forbids the\n * browser from downscaling the source, which turns the `width: { max: 1920 }`\n * cap into a no-op on exactly the Retina displays it targets. By omitting\n * `resizeMode` we let the user agent scale to satisfy the constraint (its\n * default behaviour), which is the entire point of this helper. It is tempting\n * to add `resizeMode: \"none\"` back for \"sharpness\" — don't.\n *\n * Any option you pass replaces the corresponding default outright (shallow\n * merge), so pass a full `video` constraints object if you want to tweak it.\n *\n * @example\n * ```ts\n * const stream = await captureScreen();\n * await publisher.start(stream);\n * ```\n */\nexport async function captureScreen(\n opts: CaptureScreenOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n video: opts.video ?? {\n width: { max: 1920 },\n frameRate: { ideal: 5, max: 10 },\n },\n audio: opts.audio ?? false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getDisplayMedia(constraints);\n}\n\n/**\n * Options for {@link captureMicrophone}.\n */\nexport interface CaptureMicrophoneOptions {\n /**\n * Audio constraints, or `true`. Defaults to enabling the browser's echo\n * cancellation, noise suppression, and auto gain control — the settings that\n * give a speech-to-text engine the cleanest signal.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Captures the user's microphone via `navigator.mediaDevices.getUserMedia`,\n * applying defaults tuned for speech-to-text. Pair with\n * {@link Publisher.publishMicrophone} to add transcription to a stream.\n *\n * Defaults:\n * - `audio`: `{ echoCancellation: true, noiseSuppression: true, autoGainControl: true }`.\n *\n * @example\n * ```ts\n * const mic = await captureMicrophone();\n * await publisher.publishMicrophone(mic);\n * ```\n */\nexport async function captureMicrophone(\n opts: CaptureMicrophoneOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n audio: opts.audio ?? {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n video: false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/signaling.ts","../src/publisher.ts","../src/capture.ts"],"sourcesContent":["export { Publisher } from \"./publisher\";\nexport type {\n SignalMessage,\n TrackType,\n VideoTrackType,\n TrackLabel,\n PublisherCallbacks,\n PublisherOptions,\n GatewayReadyInfo,\n PublisherRecoveryEvent,\n PublisherRecoveryState,\n PublisherRecoveryAction,\n PublisherRecoveryFailureReason,\n TurnTransportPolicy,\n AssistantTextEvent,\n UserTextResultEvent,\n SpeechQualityEvent,\n ConnectionQualityLevel,\n ConnectionStatsSample,\n ConnectionQuality,\n ConnectionQualityThresholds,\n} from \"./types\";\nexport { captureCamera, captureScreen, captureMicrophone } from \"./capture\";\nexport type {\n CaptureCameraOptions,\n CaptureScreenOptions,\n CaptureMicrophoneOptions,\n} from \"./capture\";\n","import type { SignalMessage } from \"./types\";\n\n/**\n * Wraps an open WebSocket to the Argus gateway and handles the JSON message\n * envelope: incoming text frames are parsed into {@link SignalMessage}s and\n * outgoing messages are serialised.\n *\n * This is an internal helper. The {@link Publisher} opens the socket itself\n * (racing all candidate gateways) and hands the winner here, so this class only\n * ever adopts an already-open socket rather than dialing one.\n *\n * @internal\n */\nexport class SignalingChannel {\n private ws: WebSocket;\n\n /** Fired for every incoming JSON message. */\n onMessage: ((msg: SignalMessage) => void) | null = null;\n /** Fired when the underlying WebSocket closes. */\n onClose: (() => void) | null = null;\n /** Fired when an error occurs on the WebSocket. */\n onError: ((err: Error) => void) | null = null;\n\n private constructor(ws: WebSocket) {\n this.ws = ws;\n }\n\n /**\n * Adopts an already-open WebSocket (e.g. the winner of a gateway race),\n * routing its events through the channel's callbacks. Any handlers previously\n * attached to the socket are replaced.\n */\n static wrap(ws: WebSocket): SignalingChannel {\n const ch = new SignalingChannel(ws);\n ws.onmessage = (ev: MessageEvent) => {\n const msg = parseSignal(ev.data);\n if (msg) ch.onMessage?.(msg);\n };\n ws.onerror = () => ch.onError?.(new Error(\"WebSocket error\"));\n ws.onclose = () => ch.onClose?.();\n return ch;\n }\n\n /** Sends a JSON message when the socket is open and reports whether it was sent. */\n send(msg: SignalMessage): boolean {\n if (this.ws.readyState !== WebSocket.OPEN) return false;\n this.ws.send(JSON.stringify(msg));\n return true;\n }\n\n /** Closes the underlying WebSocket. */\n close(): void {\n this.ws.close();\n }\n}\n\n/** Parses a WebSocket text frame into a SignalMessage, or null if malformed. */\nfunction parseSignal(data: unknown): SignalMessage | null {\n try {\n return JSON.parse(data as string) as SignalMessage;\n } catch {\n return null;\n }\n}\n","import { SignalingChannel } from \"./signaling\";\nimport type {\n ConnectionQualityLevel,\n ConnectionQualityThresholds,\n ConnectionStatsSample,\n GatewayReadyInfo,\n PublisherOptions,\n PublisherRecoveryAction,\n PublisherRecoveryEvent,\n SignalMessage,\n TrackLabel,\n TrackType,\n VideoTrackType,\n} from \"./types\";\n\nfunction selectGatewayTURNURLs(\n advertised: string[],\n policy: \"all\" | \"udp\" | \"tls\" = \"all\",\n): string[] {\n if (policy === \"all\") return advertised;\n\n const selected = advertised.filter((raw) => {\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n return false;\n }\n const transport = (parsed.searchParams.get(\"transport\") ?? \"\").toLowerCase();\n if (policy === \"tls\") {\n return parsed.protocol.toLowerCase() === \"turns:\" && (transport === \"\" || transport === \"tcp\");\n }\n return parsed.protocol.toLowerCase() === \"turn:\" && (transport === \"\" || transport === \"udp\");\n });\n if (selected.length === 0) {\n throw new Error(`gateway advertised no TURN URLs for required ${policy} transport`);\n }\n return selected;\n}\n\n// Connection-quality levels ordered worst-last; the index is the severity used\n// to compare and to map a computed severity back to a level.\nconst qualityLevels: readonly ConnectionQualityLevel[] = [\"good\", \"fair\", \"poor\", \"critical\"];\nfunction qualitySeverity(level: ConnectionQualityLevel): number {\n return qualityLevels.indexOf(level);\n}\n\n// A monotonic timestamp for stats windowing; falls back to Date.now() where the\n// performance clock is unavailable.\nfunction nowMs(): number {\n return typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n}\n\n// Relative severity of RTCOutboundRtpStreamStats.qualityLimitationReason, used to\n// keep the worst reason when several outbound tracks report different ones.\nconst limitationSeverity: Record<string, number> = { none: 0, other: 1, cpu: 2, bandwidth: 3 };\nfunction worseLimitation(current: string | null, next: string): string {\n if (current === null) return next;\n return (limitationSeverity[next] ?? 1) > (limitationSeverity[current] ?? 1) ? next : current;\n}\n\n// Fallback lookup for the selected candidate pair when the transport report does\n// not name one: the nominated, succeeded pair.\nfunction findNominatedCandidatePair(stats: RTCStatsReport): Record<string, unknown> | undefined {\n let found: Record<string, unknown> | undefined;\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (\n !found &&\n value.type === \"candidate-pair\" &&\n value.state === \"succeeded\" &&\n value.nominated === true\n ) {\n found = value;\n }\n });\n return found;\n}\n\nconst defaultSignalingReconnectTimeoutMs = 20_000;\nconst defaultGatewayHandshakeTimeoutMs = 20_000;\nconst defaultPeerConnectionTimeoutMs = 30_000;\nconst initialGatewayAttemptTimeoutMs = 3_000;\nconst defaultGatewayFailoverTimeoutMs = 8_000;\n// The gateway reaps a standby socket that has been sent `accepted` but not\n// `proceed` after its own deadline (argus/gateway standbyProceedDeadline, 30s).\n// The failover window is capped well below that so a `proceed` sent on failover\n// still reaches a standby before the gateway drops it — a larger configured value\n// would let standbys disappear before the browser ever fails over to them.\nconst maxGatewayFailoverTimeoutMs = 20_000;\n// A gateway that is transiently unable to serve replies `unavailable` (rather than\n// letting the socket look like a dead gateway); the browser reopens that URL after\n// the gateway's hinted backoff, clamped to this range and bounded overall by the\n// gateway-handshake deadline.\nconst defaultGatewayRetryBackoffMs = 3_000;\nconst minGatewayRetryBackoffMs = 250;\nconst maxGatewayRetryBackoffMs = 5_000;\n// A stream that keeps losing the placement compare-and-set would otherwise chase\n// redirects forever; bound the self-heal to a couple of hops.\nconst maxPlacementRedirects = 2;\nconst signalingResumeAttemptTimeoutMs = 3_000;\nconst signalingResumeMaxBackoffMs = 3_000;\nconst senderRestartPauseMs = 100;\nconst senderRecoveryWaitMs = 4_000;\nconst iceRecoveryWaitMs = 8_000;\n// A healthy signaling connection should answer well within this window. When a\n// reconnect timeout is longer, answer waiting is extended past it so a buffered\n// answer can still be applied after signaling resumes.\nconst minimumNegotiationAnswerTimeoutMs = 15_000;\nconst negotiationReconnectGraceMs = 5_000;\nconst minimumIntentionalTrackEndRetentionMs = 35_000;\nconst maxUserTextBytes = 4 * 1024;\nconst maxRetainedICECandidates = 64;\nconst defaultConnectionStatsIntervalMs = 2_000;\nconst defaultConnectionQualityDebounceSamples = 2;\n// Loss is the primary axis; RTT and jitter can only worsen the level, never\n// improve it. Consumers override any subset via connectionQualityThresholds.\nconst defaultConnectionQualityThresholds: ConnectionQualityThresholds = {\n fairLossRatio: 0.02,\n poorLossRatio: 0.05,\n criticalLossRatio: 0.12,\n fairRttMs: 300,\n poorRttMs: 600,\n criticalRttMs: 1_000,\n fairJitterMs: 50,\n poorJitterMs: 150,\n};\n\nclass ReportedPublisherError extends Error {\n constructor(message: string, readonly fatal = false) {\n super(message);\n }\n}\nclass NegotiationTimeoutError extends Error {}\nclass SenderRestoreError extends Error {}\nclass PublisherStoppedError extends Error {}\n\ninterface TrackRecoveryState {\n generation: number;\n recovering: boolean;\n required: boolean;\n action: PublisherRecoveryAction | null;\n}\n\ninterface SignalingWaiter {\n resolve: (channel: SignalingChannel) => void;\n reject: (err: Error) => void;\n}\n\ninterface InitialTrack {\n track: MediaStreamTrack;\n stream: MediaStream;\n type: TrackType;\n watchForRecovery: boolean;\n}\n\ninterface NegotiationChange {\n // labels is evaluated after setLocalDescription, not at stage time: a newly\n // added transceiver has no mid until then, and labels are keyed on the mid.\n labels?: () => TrackLabel[];\n commit?: () => void | Promise<void>;\n rollback?: () => void | Promise<void>;\n discard?: () => void | Promise<void>;\n}\n\ntype NegotiationMutationResult = void | false | NegotiationChange;\ntype NegotiationMutation = () => NegotiationMutationResult | Promise<NegotiationMutationResult>;\ntype OfferSignalMessage = Extract<SignalMessage, { type: \"offer\" }>;\ntype ICECandidateSignalMessage = Extract<SignalMessage, { type: \"ice_candidate\" }>;\ntype ICEPathSignalMessage = Extract<SignalMessage, { type: \"ice_path\" }>;\n\n/**\n * Publisher establishes a browser WebRTC session with an Argus media server.\n * The session may start with camera, microphone, or only its reliable text data\n * channel. Given the `gateway_urls` and `token` from a join-token response, it\n * races the candidate gateways to the fastest one, completes the two-phase\n * signaling handshake, and manages offer/answer exchange, ICE candidate\n * trickling, and track (re)negotiation.\n *\n * After {@link Publisher.start} resolves, {@link Publisher.frameReadToken} holds\n * the token your application server needs to fetch frames for this stream.\n *\n * @example Publish the default camera:\n * ```ts\n * const pub = new Publisher({\n * gatewayURLs: joinResp.gateway_urls,\n * token: joinResp.token,\n * callbacks: { onConnected: () => console.log(\"live!\") },\n * });\n *\n * const stream = await navigator.mediaDevices.getUserMedia({ video: true });\n * await pub.start(stream);\n * ```\n */\nexport class Publisher {\n private opts: PublisherOptions;\n private sig: SignalingChannel | null = null;\n private pc: RTCPeerConnection | null = null;\n private hasAnswer = false;\n private pendingRemoteCandidates: RTCIceCandidateInit[] = [];\n // Setting a local description starts ICE gathering. Candidates may therefore\n // arrive before the corresponding offer has crossed the signaling socket.\n // Hold them until sendOffer confirms that the offer was sent, then trickle\n // them in order. This keeps startup fast without allowing candidate/offer\n // reordering at the media server.\n private pendingLocalCandidates: ICECandidateSignalMessage[] = [];\n private localCandidateOfferSent = false;\n // WebSocket.send() only proves local queueing. Retain the current ICE\n // generation so a replacement signaling socket can replay candidates whose\n // delivery on the old socket was ambiguous.\n private retainedLocalCandidates: ICECandidateSignalMessage[] = [];\n private retainedLocalCandidateKeys = new Set<string>();\n private localCandidateGeneration: string | null = null;\n // Both peers replay after reconnect. Receiving the same candidate must be\n // idempotent, whether it is still buffered behind an answer or already\n // applied to the peer connection.\n private remoteCandidateKeys = new Set<string>();\n private remoteCandidateOrder: string[] = [];\n private remoteCandidateGeneration: string | null = null;\n private readToken: string | null = null;\n private gatewayURL: string | null = null;\n private lastReportedICEPath: string | null = null;\n private watchedICETransports = new WeakSet<RTCIceTransport>();\n // Periodic getStats() polling for connection-quality assessment. The timer runs\n // only while connected; lastStatsSample anchors the windowed loss/bitrate deltas\n // (loss is tracked per outbound SSRC so it can be paired to the remote's report),\n // and currentQualityLevel/qualityDowngradeStreak drive the debounced classifier.\n private connectionStatsTimer: ReturnType<typeof setInterval> | null = null;\n // Guards against overlapping getStats() calls: if one outlives the interval,\n // ticks are skipped until it resolves so samples never complete out of order.\n private statsSampleInFlight = false;\n private lastStatsSample:\n | {\n timestamp: number;\n // Transport-level cumulative bytesSent, summed across transport reports. It\n // survives stream/SSRC churn, so it drives send bitrate when available; null\n // when no transport report carries it (then per-stream bytes are used).\n transportBytes: number | null;\n // Per-stream counters, keyed by the outbound stats-object id (not the numeric\n // SSRC): a recycled SSRC yields a new stats object with a new id, so id-keying\n // treats it as a new stream instead of diffing two unrelated objects. fractionLost\n // is the remote's own loss fraction for its last RR interval (the primary loss\n // signal, aligned by construction); expected (distinct media packets sent,\n // excluding retransmissions) and lost back the windowed fallback and the\n // cross-stream traffic weight; bytesSent drives the send bitrate. Deltas are\n // taken only over ids present in both samples, so streams appearing or\n // disappearing (which reset aggregate counters, per the spec) neither inflate\n // loss nor dip the bitrate.\n streams: Map<\n string,\n { expected: number; lost: number | null; fractionLost: number | null; bytesSent: number }\n >;\n }\n | null = null;\n private currentQualityLevel: ConnectionQualityLevel | null = null;\n private qualityDowngradeStreak = 0;\n private stopped = true;\n // Every start/stop boundary advances lifecycleGeneration. Async work captures\n // the generation it belongs to and may never mutate or terminate a later run.\n private lifecycleGeneration = 0;\n private runAbort: AbortController | null = null;\n private peerConnectionTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnecting = false;\n private reconnectGeneration = 0;\n private resumeSocket: WebSocket | null = null;\n private signalingWaiters = new Set<SignalingWaiter>();\n private pendingOffer: { message: OfferSignalMessage; sent: boolean } | null = null;\n private recoverySequence = 0;\n private recoveryStates = new Map<TrackType, TrackRecoveryState>();\n // ICE restart applies to the whole peer connection. Keep one attempt shared\n // by every track currently recovering so simultaneous camera/screen stalls do\n // not create duplicate ICE offers.\n private iceRestartSequence = 0;\n private iceRestartAttempt: { id: number; promise: Promise<void> } | null = null;\n private trackEndHandlers = new Map<MediaStreamTrack, EventListener>();\n // published is the source of truth for the live video tracks and their logical\n // types. It drives the track labels sent on every offer, per-track recovery\n // reporting, and add/remove of individual tracks. At most one track per type is\n // kept (publishing a second track of a type replaces the first).\n private published = new Map<MediaStreamTrack, TrackType>();\n private publishedStreams = new Map<MediaStreamTrack, MediaStream>();\n // Own the active sender for each logical type. Active source replacement uses\n // replaceTrack on that sender. Unpublish removes the mapping because addTrack\n // may later reuse any compatible inactive transceiver, not necessarily the one\n // that previously carried the same logical type.\n private typeSenders = new Map<TrackType, RTCRtpSender>();\n // intentionalTrackEnds holds the browser track ids removed by publish/unpublish\n // whose server-side `media_track_ended` has not yet arrived. Correlating by the\n // track id (the generation identity the server echoes in track_id) keeps a\n // delayed end for an old screen track from being mistaken for failure of a\n // newly-published screen track.\n private intentionalTrackEnds = new Map<\n string,\n { type: TrackType; timer: ReturnType<typeof setTimeout> }\n >();\n // negotiationChain serializes every offer/answer exchange, including recovery.\n // User operations do not resolve until their answer is applied, so no caller\n // or recovery timer can create a second offer while one is outstanding.\n private negotiationChain: Promise<void> = Promise.resolve();\n // pendingAnswer resolves the one in-flight negotiation once its matching answer\n // arrives, or rejects it on timeout/teardown. All offers, including recovery,\n // pass through negotiationChain, so this slot is never intentionally replaced.\n private pendingAnswer: {\n id: string;\n resolve: (sdp: string) => void;\n reject: (err: Error) => void;\n } | null = null;\n // negotiationSeq stamps each offer with a monotonically increasing id.\n private negotiationSeq = 0;\n private textChannel: RTCDataChannel | null = null;\n private speechEnabled = false;\n private speechPending = false;\n private speechTransceiver: RTCRtpTransceiver | null = null;\n private microphoneTransceiver: RTCRtpTransceiver | null = null;\n\n constructor(opts: PublisherOptions) {\n this.opts = opts;\n }\n\n /** The read token used for frame fetches and signaling resume in the selected region. */\n get frameReadToken(): string | null { return this.readToken; }\n\n /**\n * The signaling gateway URL that won the initial race, or null before start.\n * Relay this with frameReadToken so the application server can reach the same\n * region for frame reads and change-notification subscriptions.\n */\n get selectedGatewayURL(): string | null { return this.gatewayURL; }\n\n /** Requests the persistent outbound `speech` track. This is explicit user\n * opt-in and renegotiates only once; the track remains silent between turns. */\n async enableSpeech(): Promise<void> {\n if (this.speechEnabled) return;\n await this.enqueueNegotiation(() => {\n const pc = this.pc;\n if (!pc || this.speechEnabled || this.speechPending) return false;\n if (!this.speechTransceiver) {\n this.speechTransceiver = pc.addTransceiver(\"audio\", { direction: \"recvonly\" });\n } else {\n this.speechTransceiver.direction = \"recvonly\";\n }\n this.speechPending = true;\n return {\n commit: () => {\n this.speechPending = false;\n this.speechEnabled = true;\n },\n rollback: () => {\n this.speechPending = false;\n },\n };\n });\n }\n\n /** Sends typed input over the reliable ordered Argus text channel. */\n sendUserText(messageId: string, text: string): void {\n if (!messageId || !text.trim()) throw new Error(\"messageId and text are required\");\n if (new TextEncoder().encode(text).byteLength > maxUserTextBytes) {\n throw new Error(\"text must not exceed 4 KiB\");\n }\n if (!this.textChannel || this.textChannel.readyState !== \"open\") {\n throw new Error(\"Argus text channel is not open\");\n }\n this.textChannel.send(JSON.stringify({ type: \"user_text\", message_id: messageId, text }));\n }\n\n /**\n * Starts the publisher: races all gateways to find the fastest, completes\n * the two-phase handshake, creates the peer connection, and sends the SDP\n * offer. Resolves when the offer has been sent (not when ICE completes —\n * use onConnected for that).\n *\n * The stream's single video track is published under `type` (default `\"camera\"`),\n * declared to the server so reads and change notifications can address them by\n * type. Add or remove further tracks live with {@link Publisher.publish} and\n * {@link Publisher.unpublish}.\n */\n async start(stream: MediaStream, type: VideoTrackType = \"camera\"): Promise<void> {\n const track = this.requireSingleVideoTrack(stream);\n await this.startSession({ track, stream, type, watchForRecovery: true });\n }\n\n /**\n * Starts the publisher with a microphone track and no video — a fully valid\n * audio-only stream, the natural starting point for a voice agent. Exactly one\n * audio track must be present in `stream`. Video can be added later with\n * {@link Publisher.publish}; a stream carries at most one microphone track.\n *\n * Like {@link Publisher.start} it races the gateways, completes the handshake,\n * and sends the offer; it resolves once the offer is sent. The microphone is not\n * subject to the video recovery ladder — a mic that stops simply ends\n * transcription for the stream.\n */\n async startAudioOnly(stream: MediaStream): Promise<void> {\n const track = this.requireSingleAudioTrack(stream);\n await this.startSession({ track, stream, type: \"audio\", watchForRecovery: false });\n }\n\n /**\n * Starts a WebRTC session with only the ordered `argus.text` data channel.\n * This is the natural entry point for a typed, text-only agent: it requests no\n * camera or microphone permission and publishes no media. Camera, screen, or\n * microphone tracks can be added later with {@link Publisher.publish} or\n * {@link Publisher.publishMicrophone}; {@link Publisher.enableSpeech} can add\n * the optional inbound speech track independently.\n */\n async startTextOnly(): Promise<void> {\n await this.startSession(null);\n }\n\n /**\n * Shared startup for video, audio-only, and text-only entry points: race the\n * gateways, build the peer connection and text channel, optionally add an\n * initial media track, and send the first offer.\n */\n private async startSession(initialTrack: InitialTrack | null): Promise<void> {\n if (!this.stopped || this.pc || this.published.size > 0) {\n throw new Error(\"publisher already started\");\n }\n const trackKind: \"video\" | \"audio\" | null = initialTrack\n ? (initialTrack.type === \"audio\" ? \"audio\" : \"video\")\n : null;\n const generation = ++this.lifecycleGeneration;\n const runAbort = new AbortController();\n this.runAbort = runAbort;\n // A publisher run owns its negotiation queue. Old queue continuations may\n // still unwind after stop(), but their captured generation prevents them\n // from adopting this fresh chain or peer connection.\n this.negotiationChain = Promise.resolve();\n this.stopped = false;\n this.recoveryStates.clear();\n this.typeSenders.clear();\n this.lastReportedICEPath = null;\n this.watchedICETransports = new WeakSet<RTCIceTransport>();\n // Record the startup track for offer labels and teardown, but do not watch\n // it for recovery until startup succeeds. If capture ends while startup is\n // awaiting the gateway or ICE, start() must reject rather than report a\n // recovery transition for a session that never became live.\n if (initialTrack) {\n this.published.set(initialTrack.track, initialTrack.type);\n this.publishedStreams.set(initialTrack.track, initialTrack.stream);\n }\n let startupWS: WebSocket | null = null;\n\n try {\n // Race all gateways; returns winning WebSocket + TURN/read-token info\n const { ws, readyInfo, gatewayURL } = await this.raceGateways(runAbort.signal);\n startupWS = ws;\n this.assertActiveRun(generation);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n this.gatewayURL = gatewayURL;\n\n // Store read token for caller\n if (readyInfo.read_token) {\n this.readToken = readyInfo.read_token;\n }\n\n // Build ICE servers: extra servers from opts + the gateway's bounded TURN\n // selection. Each selected relay supplies UDP and TCP URLs under one\n // credential; browser ICE may gather from either selected server.\n const iceServers: RTCIceServer[] = [...(this.opts.iceServers ?? [])];\n const advertisedTURNURLs = readyInfo.turn_urls ?? [];\n if (advertisedTURNURLs.length > 0 || this.opts.turnTransportPolicy !== undefined) {\n const turnURLs = selectGatewayTURNURLs(\n advertisedTURNURLs,\n this.opts.turnTransportPolicy,\n );\n if (turnURLs.length > 0) {\n iceServers.push({\n urls: turnURLs,\n username: readyInfo.turn_username,\n credential: readyInfo.turn_credential,\n });\n }\n }\n\n const pc = new RTCPeerConnection({\n iceServers,\n iceTransportPolicy: this.opts.iceTransportPolicy,\n });\n this.pc = pc;\n\n this.textChannel = pc.createDataChannel(\"argus.text\", { ordered: true });\n this.textChannel.onmessage = (event) => this.handleTextMessage(event.data);\n pc.ontrack = (event) => {\n if (!this.isActiveRun(generation, pc) || event.track.kind !== \"audio\") return;\n this.opts.callbacks?.onSpeechTrack?.(event.track, event.streams);\n };\n\n pc.onicecandidate = (ev) => {\n if (!this.isActiveRun(generation, pc) || !ev.candidate) return;\n this.handleLocalICECandidate(ev.candidate);\n };\n\n pc.onconnectionstatechange = () => {\n if (!this.isActiveRun(generation, pc)) return;\n const state = pc.connectionState;\n if (state) this.opts.callbacks?.onConnectionStateChange?.(state);\n if (state === \"connected\") {\n this.clearPeerConnectionTimeout();\n void this.reportSelectedICEPath(pc);\n // startConnectionStatsLoop re-baselines, so a reconnect after a\n // transient ICE disconnect resumes with a fresh quality assessment.\n this.startConnectionStatsLoop(generation, pc);\n this.opts.callbacks?.onConnected?.();\n } else if (state === \"failed\") {\n this.clearPeerConnectionTimeout();\n this.terminateWithError(new Error(\"WebRTC connection failed\"), true, generation);\n } else {\n // \"connecting\" | \"disconnected\" | \"closed\" | \"new\": quality polling is\n // meaningful only while connected, so pause it (and stop wasting\n // getStats calls) until the connection returns to \"connected\".\n this.stopConnectionStatsLoop();\n }\n };\n\n if (initialTrack) {\n const sender = initialTrack.type === \"audio\"\n ? this.addMicrophoneTrack(pc, initialTrack.track, initialTrack.stream)\n : pc.addTrack(initialTrack.track, initialTrack.stream);\n if (initialTrack.type !== \"audio\") this.preferVideoCodecs(pc, sender);\n this.typeSenders.set(\n initialTrack.type,\n sender,\n );\n }\n\n // Adopt the winning socket before setting the local description so ICE\n // candidates can be trickled instead of blocking startup on complete\n // gathering. handleLocalICECandidate still holds candidates until the\n // offer itself has been sent.\n this.installSignaling(ws);\n startupWS = null; // installSignaling/stop now owns this socket.\n\n const offer = await pc.createOffer();\n this.assertActiveRun(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n this.beginLocalCandidateBatch();\n await pc.setLocalDescription(offer);\n this.assertActiveRun(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n\n const local = pc.localDescription;\n if (!local) throw new Error(\"local description missing\");\n\n // Seed the negotiation chain with the initial offer's answer. start() resolves\n // once the offer is sent (its documented contract — use onConnected for media),\n // but any publish()/unpublish() queues behind this so it cannot emit a second\n // offer before the initial answer lands or apply that answer to its own offer.\n const id = this.nextNegotiationId();\n const { answered } = await this.sendOffer({\n type: \"offer\",\n sdp: local.sdp,\n sdp_type: \"offer\",\n negotiation_id: id,\n tracks: this.buildTrackLabels(),\n speech_enabled: (this.speechEnabled || this.speechPending) || undefined,\n });\n this.releaseLocalCandidateBatch();\n this.assertActiveRun(generation, pc);\n this.armPeerConnectionTimeout(generation, pc);\n if (initialTrack && trackKind) this.requireLiveTrack(initialTrack.track, trackKind);\n // Only video participates in the media-recovery ladder; a microphone that\n // stops simply ends transcription and is not \"recovered\".\n if (initialTrack?.watchForRecovery) this.watchTrack(initialTrack.track);\n else if (initialTrack) this.watchMicrophone(initialTrack.track);\n const initial = answered.then(async (sdp) => {\n this.assertActiveRun(generation, pc);\n await pc.setRemoteDescription(\n new RTCSessionDescription({ type: \"answer\", sdp }),\n );\n this.assertActiveRun(generation, pc);\n this.applyAnswered(pc);\n });\n this.negotiationChain = initial.catch((err) => {\n if (this.isActiveRun(generation, pc)) {\n const reported = err instanceof Error ? err : new Error(String(err));\n const alreadyReported =\n err instanceof ReportedPublisherError && err.fatal;\n this.terminateWithError(reported, !alreadyReported, generation);\n }\n throw err;\n });\n // start() intentionally resolves once the offer is sent, so observe the\n // background initial-answer promise here to avoid an unhandled rejection.\n void this.negotiationChain.catch(() => {});\n } catch (err) {\n startupWS?.close();\n if (generation === this.lifecycleGeneration) this.stop();\n throw err;\n }\n }\n\n /**\n * Adds the single video track from `stream` to the live session under `type`,\n * renegotiating so the media server begins ingesting them. Use this to add a\n * track after {@link Publisher.start} — for example to begin a screen share on\n * top of a live camera.\n *\n * Exactly one video track must be present in `stream`. If a track of `type` is already\n * live it is removed and replaced (a \"screen\" published while another \"screen\"\n * is live supersedes it).\n */\n async publish(stream: MediaStream, type: VideoTrackType): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n const track = this.requireSingleVideoTrack(stream);\n\n // The track mutation runs inside the queued negotiation so it is atomic with\n // its offer: back-to-back publish/unpublish calls each mutate the peer\n // connection at the head of their own turn, not all up front.\n await this.enqueueNegotiation(() => this.stagePublish(track, stream, type));\n }\n\n /**\n * Removes the live track(s) of the given type, stops their local capture, and\n * renegotiates so the media server ends ingestion for that track. A no-op if\n * no track of that type is published.\n */\n async unpublish(type: VideoTrackType): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n await this.enqueueNegotiation(() => {\n if (this.tracksOfType(type).length === 0) {\n // Nothing to remove by the time this turn runs; skip the offer.\n return false;\n }\n return this.stageUnpublish(type);\n });\n }\n\n /**\n * Adds the microphone (audio) track from `stream` to the live session and\n * renegotiates, so the media server begins transcribing it. Exactly one audio\n * track must be present in `stream`. Publishing a microphone while one is\n * already live replaces it.\n *\n * The audio track feeds server-side speech-to-text; its transcripts are\n * delivered to the customer server over the change-notification subscription,\n * not to the browser. Audio is not subject to the video recovery ladder — a\n * mic that stops is simply removed.\n */\n async publishMicrophone(stream: MediaStream): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n const track = this.requireSingleAudioTrack(stream);\n await this.enqueueNegotiation(() => this.stagePublishAudio(track, stream));\n }\n\n /**\n * Removes the live microphone track, stops its local capture, and\n * renegotiates so the media server ends transcription. A no-op if no\n * microphone is published.\n */\n async unpublishMicrophone(): Promise<void> {\n if (!this.pc) throw new Error(\"publisher not started\");\n await this.enqueueNegotiation(() => {\n if (this.tracksOfType(\"audio\").length === 0) return false;\n return this.stageUnpublish(\"audio\");\n });\n }\n\n /**\n * Replaces the published track of a single type with a new stream and\n * renegotiates in place — e.g. to swap to a freshly reacquired screen share\n * after {@link PublisherCallbacks.onRecoveryRequired}. Defaults to the\n * `\"camera\"` type. This is a convenience over {@link Publisher.publish}, which\n * it delegates to (publishing one track per type replaces any existing track\n * of that type).\n */\n async replaceStream(stream: MediaStream, type: VideoTrackType = \"camera\"): Promise<void> {\n await this.publish(stream, type);\n }\n\n /** Stops publishing and tears down the peer connection. */\n stop(): void {\n this.lifecycleGeneration++;\n this.runAbort?.abort();\n this.runAbort = null;\n this.clearPeerConnectionTimeout();\n this.stopConnectionStatsLoop();\n this.stopped = true;\n this.cancelAllMediaRecovery();\n this.reconnectGeneration++;\n this.reconnecting = false;\n this.resumeSocket?.close();\n this.resumeSocket = null;\n this.sig?.close();\n this.sig = null;\n this.rejectSignalingWaiters(new Error(\"publisher stopped\"));\n\n this.unwatchStreamTracks();\n this.stopPublishedTracks();\n this.clearIntentionalTrackEnds();\n this.rejectPendingAnswer(new Error(\"publisher stopped\"));\n this.pendingOffer = null;\n\n this.pc?.close();\n this.pc = null;\n this.textChannel = null;\n this.speechEnabled = false;\n this.speechPending = false;\n this.speechTransceiver = null;\n this.microphoneTransceiver = null;\n this.typeSenders.clear();\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = false;\n this.clearRetainedICECandidates();\n this.readToken = null;\n this.gatewayURL = null;\n this.lastReportedICEPath = null;\n }\n\n // rejectPendingAnswer fails any in-flight negotiation so a queued\n // publish()/unpublish() rejects promptly instead of hanging until timeout.\n private rejectPendingAnswer(err: Error): void {\n const pending = this.pendingAnswer;\n if (pending) {\n this.pendingAnswer = null;\n pending.reject(err);\n }\n }\n\n /** Returns the current RTCPeerConnection, or null if not started. */\n get peerConnection(): RTCPeerConnection | null {\n return this.pc;\n }\n\n /** Returns true if the peer connection is in the \"connected\" state. */\n get isConnected(): boolean {\n return this.pc?.connectionState === \"connected\";\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n // raceGateways opens every candidate gateway at once, then decides in two\n // separate moments. SELECTION: the first socket to deliver `accepted` (a cheap,\n // control-plane-free acknowledgement) is chosen on network path; the browser\n // sends `proceed` on that one only and keeps the rest as standbys. PLACEMENT:\n // the selected region does its control-plane work and returns `ready`. If the\n // selection dies (socket close/error → immediately) or stalls past the failover\n // deadline (a hung-but-open socket), the browser abandons it — closing the\n // socket cancels that region's placement server-side — and selects the\n // next-fastest standby. A `placement_redirect` points the browser at the region\n // that already holds the stream so a mistimed failover self-heals.\n private raceGateways(signal: AbortSignal): Promise<{ ws: WebSocket; readyInfo: GatewayReadyInfo; gatewayURL: string }> {\n return new Promise((resolve, reject) => {\n const { gatewayURLs, token } = this.opts;\n if (gatewayURLs.length === 0) {\n reject(new Error(\"no gateway URLs provided\"));\n return;\n }\n\n const sockets: WebSocket[] = [];\n const attemptTimers = new Map<WebSocket, ReturnType<typeof setTimeout>>();\n // Pending reopen timers for URLs a gateway asked us to retry (`unavailable`).\n // A URL with a scheduled reopen is still in the running, so the race is not\n // exhausted while any of these are outstanding.\n const reopenTimers = new Set<ReturnType<typeof setTimeout>>();\n // Sockets that have delivered `accepted` but were not selected — the\n // failover pool, in acknowledgement (fastest-first) order.\n const standbys: WebSocket[] = [];\n let selected: WebSocket | null = null;\n let failoverTimer: ReturnType<typeof setTimeout> | null = null;\n let redirects = 0;\n let settled = false;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n const failoverMs = Math.min(\n maxGatewayFailoverTimeoutMs,\n Math.max(0, this.opts.gatewayFailoverTimeoutMs ?? defaultGatewayFailoverTimeoutMs),\n );\n\n const clearTimeoutTimer = () => {\n if (timeoutTimer !== null) clearTimeout(timeoutTimer);\n timeoutTimer = null;\n };\n const clearFailoverTimer = () => {\n if (failoverTimer !== null) clearTimeout(failoverTimer);\n failoverTimer = null;\n };\n const clearReopenTimers = () => {\n for (const timer of reopenTimers) clearTimeout(timer);\n reopenTimers.clear();\n };\n const clearAttemptTimer = (socket: WebSocket) => {\n const timer = attemptTimers.get(socket);\n if (timer !== undefined) clearTimeout(timer);\n attemptTimers.delete(socket);\n };\n const detach = (socket: WebSocket) => {\n clearAttemptTimer(socket);\n socket.onmessage = null;\n socket.onerror = null;\n socket.onclose = null;\n };\n const dropStandby = (socket: WebSocket) => {\n const i = standbys.indexOf(socket);\n if (i !== -1) standbys.splice(i, 1);\n };\n\n const closeAll = (except?: WebSocket) => {\n for (const s of sockets) {\n if (s !== except) {\n detach(s);\n s.close();\n }\n }\n };\n\n const win = (ws: WebSocket, readyInfo: GatewayReadyInfo, gatewayURL: string) => {\n settled = true;\n clearTimeoutTimer();\n clearFailoverTimer();\n clearReopenTimers();\n signal.removeEventListener(\"abort\", abort);\n closeAll(ws);\n resolve({ ws, readyInfo, gatewayURL });\n };\n const fail = (err: Error) => {\n settled = true;\n clearTimeoutTimer();\n clearFailoverTimer();\n clearReopenTimers();\n signal.removeEventListener(\"abort\", abort);\n closeAll();\n reject(err);\n };\n // No socket is still able to become a selection: every one is closed, nothing\n // is selected, no standby is waiting, and no retry (`unavailable`) is pending.\n const checkExhausted = () => {\n if (settled || selected !== null || standbys.length > 0 || reopenTimers.size > 0) return;\n if (sockets.every(s => s.readyState === WebSocket.CLOSED || s.readyState === WebSocket.CLOSING)) {\n fail(new Error(\"all gateways failed to connect\"));\n }\n };\n\n // select commits the browser to one region: send `proceed` and start the\n // failover deadline. Only the selected socket's `ready`/`placement_redirect`\n // are acted on.\n const select = (ws: WebSocket) => {\n selected = ws;\n dropStandby(ws);\n try {\n ws.send(JSON.stringify({ type: \"proceed\" }));\n } catch {\n socketDown(ws);\n return;\n }\n clearFailoverTimer();\n failoverTimer = setTimeout(() => failover(ws), failoverMs);\n };\n\n // failover abandons the current selection and moves to the next-fastest\n // standby. Closing the abandoned socket cancels its placement server-side.\n const failover = (deadSocket: WebSocket) => {\n if (settled || deadSocket !== selected) return;\n clearFailoverTimer();\n detach(deadSocket);\n deadSocket.close();\n selected = null;\n const next = standbys.shift();\n if (next) {\n select(next);\n } else {\n checkExhausted();\n }\n };\n\n // redirect self-heals to the region that already holds the stream: tear the\n // whole race down and open a single socket to the named gateway.\n const redirect = (gatewayURL: string) => {\n if (settled) return;\n if (redirects >= maxPlacementRedirects) {\n fail(new Error(\"too many placement redirects\"));\n return;\n }\n redirects++;\n clearFailoverTimer();\n closeAll();\n standbys.length = 0;\n selected = null;\n // A gateway-supplied redirect URL may be malformed; a construction failure\n // must reject the race, not throw out of this socket callback and leave the\n // race pending until the handshake deadline.\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n // retryUnavailable drops a socket whose gateway reported transient\n // unavailability and reopens the same URL after the hinted backoff, so a\n // temporary capacity or health blip does not count the region out. The\n // overall handshake deadline bounds how long this repeats.\n const retryUnavailable = (ws: WebSocket, gatewayURL: string, retryAfterMs?: number) => {\n detach(ws);\n ws.close();\n const delay = Math.min(\n maxGatewayRetryBackoffMs,\n Math.max(minGatewayRetryBackoffMs, retryAfterMs ?? defaultGatewayRetryBackoffMs),\n );\n const timer = setTimeout(() => {\n reopenTimers.delete(timer);\n if (settled) return;\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n }, delay);\n reopenTimers.add(timer);\n };\n\n const socketDown = (ws: WebSocket) => {\n if (settled) return;\n clearAttemptTimer(ws);\n dropStandby(ws);\n if (ws === selected) {\n // A definitive death of the selection fails over immediately, without\n // waiting out the deadline.\n failover(ws);\n } else {\n checkExhausted();\n }\n };\n\n const abort = () => {\n if (settled) return;\n fail(new PublisherStoppedError(\"publisher stopped\"));\n };\n if (signal.aborted) {\n abort();\n return;\n }\n signal.addEventListener(\"abort\", abort, { once: true });\n\n const timeoutMs = Math.max(\n 0,\n this.opts.gatewayHandshakeTimeoutMs ?? defaultGatewayHandshakeTimeoutMs,\n );\n timeoutTimer = setTimeout(() => {\n if (settled) return;\n fail(new Error(`gateway handshake timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const openGateway = (gatewayURL: string) => {\n if (settled) return;\n\n const u = new URL(gatewayURL);\n u.searchParams.set(\"token\", token);\n const ws = new WebSocket(u.toString());\n sockets.push(ws);\n\n let accepted = false;\n const attemptTimer = setTimeout(() => {\n attemptTimers.delete(ws);\n if (settled || accepted) return;\n\n // A WebSocket can remain CONNECTING until the browser's TCP timeout\n // expires. Retire that flow and create a fresh one; a new source port\n // gives load-balanced paths another chance without extending the\n // caller's overall gateway-handshake deadline. A socket that has already\n // acknowledged (selected or standby) is never reopened.\n detach(ws);\n ws.close();\n\n try {\n openGateway(gatewayURL);\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n }, initialGatewayAttemptTimeoutMs);\n attemptTimers.set(ws, attemptTimer);\n\n ws.onmessage = (ev: MessageEvent) => {\n if (settled) return;\n let msg: SignalMessage;\n try {\n msg = JSON.parse(ev.data as string) as SignalMessage;\n } catch {\n return; // ignore malformed\n }\n if (!accepted) {\n if (msg.type === \"unavailable\") {\n // The region cannot serve right now; reopen this URL after a backoff\n // instead of treating the socket as a dead gateway.\n retryUnavailable(ws, gatewayURL, msg.retry_after_ms);\n return;\n }\n if (msg.type !== \"accepted\") return;\n accepted = true;\n clearAttemptTimer(ws);\n // First acknowledgement wins selection; the rest wait as standbys.\n if (selected === null) select(ws);\n else standbys.push(ws);\n return;\n }\n // Post-acknowledgement messages are meaningful only from the selection.\n if (ws !== selected) return;\n if (msg.type === \"ready\") {\n win(ws, msg as GatewayReadyInfo, gatewayURL);\n } else if (msg.type === \"placement_redirect\" && msg.gateway_url) {\n redirect(msg.gateway_url);\n }\n };\n\n ws.onerror = () => socketDown(ws);\n ws.onclose = () => socketDown(ws);\n };\n\n try {\n for (const gatewayURL of gatewayURLs) {\n openGateway(gatewayURL);\n }\n } catch (err) {\n fail(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n\n private installSignaling(ws: WebSocket): SignalingChannel {\n const channel = SignalingChannel.wrap(ws);\n const generation = this.lifecycleGeneration;\n this.sig = channel;\n channel.onMessage = (msg) => {\n if (this.sig !== channel || !this.isActiveRun(generation)) return;\n this.handleSignal(msg);\n };\n channel.onClose = () => {\n if (this.sig !== channel || this.stopped) return;\n this.sig = null;\n void this.resumeSignaling();\n };\n // Browsers normally follow an error event with close. Recovery begins from\n // close so a single transport failure cannot start two retry loops.\n channel.onError = () => {};\n this.resolveSignalingWaiters(channel);\n\n // An open WebSocket only acknowledges local queueing, not server receipt.\n // Replay the outstanding offer before its candidates; the media server\n // deduplicates the offer by negotiation_id and candidates by their content.\n const pending = this.pendingOffer;\n if (pending?.sent) {\n try {\n channel.send(pending.message);\n } catch {\n // onclose/sendWhenSignalingAvailable will drive another resume attempt.\n }\n }\n // Candidates captured for an offer that has not been sent remain in the\n // normal local batch. Replaying them here would put them ahead of that\n // offer when sendOffer wakes on this new channel.\n if (this.localCandidateOfferSent) {\n for (const candidate of this.retainedLocalCandidates) {\n try {\n channel.send(candidate);\n } catch {\n // A later resume replays the complete bounded set again.\n break;\n }\n }\n }\n return channel;\n }\n\n private awaitSignaling(): Promise<SignalingChannel> {\n if (this.sig) return Promise.resolve(this.sig);\n if (this.stopped) return Promise.reject(new Error(\"publisher stopped\"));\n return new Promise<SignalingChannel>((resolve, reject) => {\n this.signalingWaiters.add({ resolve, reject });\n });\n }\n\n private resolveSignalingWaiters(channel: SignalingChannel): void {\n const waiters = [...this.signalingWaiters];\n this.signalingWaiters.clear();\n for (const waiter of waiters) waiter.resolve(channel);\n }\n\n private rejectSignalingWaiters(err: Error): void {\n const waiters = [...this.signalingWaiters];\n this.signalingWaiters.clear();\n for (const waiter of waiters) waiter.reject(err);\n }\n\n private async sendWhenSignalingAvailable(msg: SignalMessage): Promise<void> {\n while (!this.stopped) {\n const channel = await this.awaitSignaling();\n try {\n if (channel.send(msg)) return;\n } catch {\n // Treat a synchronous transport failure exactly like a closed socket.\n }\n\n // The channel closed between selection and send. Invalidate it and begin\n // resume immediately; a later close event is ignored by its identity check.\n if (this.sig === channel) {\n this.sig = null;\n void this.resumeSignaling();\n }\n }\n throw new Error(\"publisher stopped\");\n }\n\n private async sendOffer(message: OfferSignalMessage): Promise<{ answered: Promise<string> }> {\n if (this.pendingOffer) {\n throw new Error(\"another negotiation offer is already pending\");\n }\n const pending = { message, sent: false };\n this.pendingOffer = pending;\n try {\n await this.sendWhenSignalingAvailable(message);\n pending.sent = true;\n const id = message.negotiation_id;\n if (!id) throw new Error(\"negotiation offer is missing an id\");\n const answered = this.awaitAnswer(id);\n void answered.then(\n () => { if (this.pendingOffer === pending) this.pendingOffer = null; },\n () => { if (this.pendingOffer === pending) this.pendingOffer = null; },\n );\n return { answered };\n } catch (err) {\n if (this.pendingOffer === pending) this.pendingOffer = null;\n throw err;\n }\n }\n\n private async resumeSignaling(): Promise<void> {\n if (this.reconnecting || this.stopped) return;\n if (!this.gatewayURL || !this.readToken) {\n this.terminateWithError(new Error(\"signaling closed and cannot be resumed\"));\n return;\n }\n\n this.reconnecting = true;\n const generation = ++this.reconnectGeneration;\n const timeout = this.opts.signalingReconnectTimeoutMs ?? defaultSignalingReconnectTimeoutMs;\n const deadline = Date.now() + Math.max(0, timeout);\n let backoffMs = 0;\n\n while (!this.stopped && generation === this.reconnectGeneration && Date.now() <= deadline) {\n if (backoffMs > 0) {\n const waitMs = Math.min(backoffMs, Math.max(0, deadline - Date.now()));\n if (waitMs === 0) break;\n await this.wait(waitMs);\n if (this.stopped || generation !== this.reconnectGeneration) return;\n }\n\n const remaining = deadline - Date.now();\n if (remaining < 0) break;\n try {\n const ws = await this.openResumeSocket(Math.min(signalingResumeAttemptTimeoutMs, Math.max(1, remaining)));\n if (this.stopped || generation !== this.reconnectGeneration) {\n ws.close();\n return;\n }\n this.resumeSocket = null;\n this.reconnecting = false;\n this.installSignaling(ws);\n return;\n } catch {\n backoffMs = backoffMs === 0 ? 250 : Math.min(backoffMs * 2, signalingResumeMaxBackoffMs);\n }\n }\n\n if (!this.stopped && generation === this.reconnectGeneration) {\n this.reconnecting = false;\n this.terminateWithError(new Error(\"unable to resume signaling with the selected gateway\"));\n }\n }\n\n private openResumeSocket(timeoutMs: number): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const u = new URL(this.gatewayURL!);\n u.searchParams.set(\"token\", this.readToken!);\n const ws = new WebSocket(u.toString());\n this.resumeSocket = ws;\n let settled = false;\n const timer = setTimeout(() => fail(), timeoutMs);\n\n const fail = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (this.resumeSocket === ws) this.resumeSocket = null;\n ws.onmessage = null;\n ws.onerror = null;\n ws.onclose = null;\n ws.close();\n reject(new Error(\"signaling resume attempt failed\"));\n };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(ev.data as string);\n if (msg.type !== \"resumed\" || settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(ws);\n } catch {\n // Ignore malformed messages while waiting for the resume acknowledgement.\n }\n };\n ws.onerror = fail;\n ws.onclose = fail;\n });\n }\n\n private wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private isActiveRun(generation: number, pc?: RTCPeerConnection): boolean {\n return (\n generation === this.lifecycleGeneration &&\n !this.stopped &&\n !this.runAbort?.signal.aborted &&\n (!pc || this.pc === pc)\n );\n }\n\n private assertActiveRun(generation: number, pc?: RTCPeerConnection): void {\n if (!this.isActiveRun(generation, pc)) {\n throw new PublisherStoppedError(\"publisher stopped\");\n }\n }\n\n private armPeerConnectionTimeout(generation: number, pc: RTCPeerConnection): void {\n this.clearPeerConnectionTimeout();\n if (pc.connectionState === \"connected\") return;\n const timeoutMs = Math.max(\n 0,\n this.opts.peerConnectionTimeoutMs ?? defaultPeerConnectionTimeoutMs,\n );\n const timer = setTimeout(() => {\n if (this.peerConnectionTimer !== timer) return;\n this.peerConnectionTimer = null;\n if (!this.isActiveRun(generation, pc) || pc.connectionState === \"connected\") return;\n this.terminateWithError(\n new Error(`WebRTC connection timed out after ${timeoutMs}ms`),\n true,\n generation,\n );\n }, timeoutMs);\n this.peerConnectionTimer = timer;\n }\n\n private clearPeerConnectionTimeout(): void {\n if (this.peerConnectionTimer !== null) clearTimeout(this.peerConnectionTimer);\n this.peerConnectionTimer = null;\n }\n\n private terminateWithError(\n err: Error,\n notify = true,\n generation = this.lifecycleGeneration,\n ): void {\n if (generation !== this.lifecycleGeneration) return;\n this.lifecycleGeneration++;\n this.runAbort?.abort();\n this.runAbort = null;\n this.clearPeerConnectionTimeout();\n this.stopConnectionStatsLoop();\n this.stopped = true;\n this.cancelAllMediaRecovery();\n this.reconnectGeneration++;\n this.resumeSocket?.close();\n this.resumeSocket = null;\n this.sig?.close();\n this.sig = null;\n this.rejectSignalingWaiters(err);\n this.pc?.close();\n this.pc = null;\n this.textChannel = null;\n this.speechEnabled = false;\n this.speechPending = false;\n this.speechTransceiver = null;\n this.microphoneTransceiver = null;\n this.typeSenders.clear();\n this.unwatchStreamTracks();\n this.stopPublishedTracks();\n this.clearIntentionalTrackEnds();\n this.rejectPendingAnswer(new Error(\"publisher terminated\"));\n this.pendingOffer = null;\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = false;\n this.clearRetainedICECandidates();\n this.readToken = null;\n this.gatewayURL = null;\n this.lastReportedICEPath = null;\n if (notify) this.opts.callbacks?.onError?.(err);\n }\n\n private handleSignal(msg: SignalMessage): void {\n switch (msg.type) {\n case \"answer\": {\n if (!this.pc) return;\n const pending = this.pendingAnswer;\n if (!pending) break;\n // Correlate the answer with the outstanding offer by negotiation id. An\n // answer whose id does not match the current pending offer is for a\n // superseded offer (or arrived after a timeout) and must be dropped — this\n // is what prevents an answer being applied to the wrong offer. Older\n // servers omit the id; then there is only ever one outstanding offer, so\n // an unlabelled answer is accepted.\n if (msg.negotiation_id && msg.negotiation_id !== pending.id) break;\n this.pendingAnswer = null;\n pending.resolve(msg.sdp);\n break;\n }\n\n case \"ice_candidate\": {\n if (!this.pc) return;\n if (!this.retainRemoteCandidate(msg)) return;\n const init: RTCIceCandidateInit = {\n candidate: msg.candidate,\n sdpMid: msg.sdp_mid ?? null,\n sdpMLineIndex: msg.sdp_mline_index ?? null,\n usernameFragment: msg.username_fragment ?? null,\n };\n if (this.hasAnswer) {\n this.pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n } else {\n this.pendingRemoteCandidates.push(init);\n }\n break;\n }\n\n case \"connection_state\": {\n // The server echoes the peer connection state; the onconnectionstatechange\n // handler above already covers this, but callers can also react via\n // onConnectionStateChange.\n break;\n }\n\n case \"media_track_ended\": {\n // The microphone is not subject to the video recovery ladder: a mic that\n // stops simply ends transcription for the stream, so never attempt sender/\n // ICE recovery for it.\n if (msg.track === \"audio\") break;\n // A media_track_ended for a track intentionally removed by publish or\n // unpublish is expected, not a capture failure. New servers identify the\n // physical track; the type-only fallback keeps compatibility with older\n // servers but cannot distinguish two generations of the same type.\n if (this.consumeIntentionalTrackEnd(msg.track, msg.track_id)) break;\n void this.beginMediaRecovery(msg.track);\n break;\n }\n\n case \"media_stall\": {\n // Audio is never \"recovered\" (see media_track_ended above).\n if (msg.track === \"audio\") break;\n void this.beginMediaRecovery(msg.track);\n break;\n }\n\n case \"media_resumed\": {\n this.completeMediaRecovery(msg.track);\n break;\n }\n\n case \"speech_slow\": {\n this.opts.callbacks?.onSpeechQualityChange?.({\n degraded: true,\n realtimeFactor: msg.realtime_factor,\n });\n break;\n }\n\n case \"speech_recovered\": {\n this.opts.callbacks?.onSpeechQualityChange?.({ degraded: false });\n break;\n }\n\n case \"error\": {\n const err = new ReportedPublisherError(msg.error, msg.fatal === true);\n const pending = this.pendingAnswer;\n let matchedPending = false;\n if (\n pending &&\n (!msg.negotiation_id || msg.negotiation_id === pending.id)\n ) {\n matchedPending = true;\n this.pendingAnswer = null;\n pending.reject(err);\n }\n if (err.fatal) this.opts.callbacks?.onError?.(err);\n if (err.fatal && !matchedPending) {\n this.terminateWithError(err, false);\n }\n break;\n }\n\n case \"resumed\":\n break;\n }\n }\n\n private async beginMediaRecovery(trackType: TrackType): Promise<void> {\n const state = this.recoveryState(trackType);\n if (this.stopped || state.recovering || state.required) return;\n\n const liveTracks = this.tracksOfType(trackType).filter(\n (track) => track.readyState !== \"ended\",\n );\n if (liveTracks.length === 0) {\n this.failMediaRecovery(trackType, \"capture_ended\");\n return;\n }\n\n state.recovering = true;\n state.generation = ++this.recoverySequence;\n const generation = state.generation;\n state.action = \"sender_restart\";\n this.emitRecoveryTransition({ state: \"recovering\", track: trackType, action: \"sender_restart\" });\n this.sendRecoveryDiagnostic(\"recovery_started\", trackType, \"sender_restart\");\n\n await this.restartSenders(trackType, liveTracks, generation);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n // This observation window begins only after the sender-restart offer has\n // reached the queue head and completed. Time spent behind another offer is\n // not evidence that sender recovery failed.\n await this.wait(senderRecoveryWaitMs);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n state.action = \"ice_restart\";\n this.emitRecoveryTransition({ state: \"recovering\", track: trackType, action: \"ice_restart\" });\n this.sendRecoveryDiagnostic(\"recovery_retry\", trackType, \"ice_restart\");\n try {\n await this.sharedIceRestart();\n } catch {\n // Media may still resume without a successful answer, so retain the\n // observation window before requiring host intervention.\n }\n if (!this.isCurrentRecovery(trackType, generation)) return;\n\n // As above, start the deadline only after the shared ICE attempt has run.\n await this.wait(iceRecoveryWaitMs);\n if (!this.isCurrentRecovery(trackType, generation)) return;\n this.failMediaRecovery(trackType, \"automatic_recovery_failed\");\n }\n\n private async restartSenders(\n trackType: TrackType,\n tracks: MediaStreamTrack[],\n generation: number,\n ): Promise<void> {\n const lifecycleGeneration = this.lifecycleGeneration;\n try {\n await this.enqueueNegotiation(async () => {\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n const live = new Set(tracks);\n const senders = this.pc?.getSenders().filter(\n (sender) => sender.track && live.has(sender.track),\n ) ?? [];\n if (senders.length === 0) return false;\n\n const originals = senders.map((sender) => ({ sender, track: sender.track! }));\n try {\n await Promise.all(originals.map(({ sender }) => sender.replaceTrack(null)));\n await this.wait(senderRestartPauseMs);\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n await Promise.all(originals.map(({ sender, track }) => sender.replaceTrack(track)));\n if (!this.isCurrentRecovery(trackType, generation)) return false;\n return;\n } finally {\n // Cancellation or a partial replaceTrack failure must never leave a\n // still-published sender detached.\n const restored = await Promise.allSettled(originals.map(async ({ sender, track }) => {\n if (\n sender.track === null &&\n this.published.has(track) &&\n track.readyState !== \"ended\"\n ) {\n await sender.replaceTrack(track);\n }\n }));\n if (restored.some((result) => result.status === \"rejected\")) {\n throw new SenderRestoreError(\"failed to restore a detached media sender\");\n }\n }\n });\n } catch (err) {\n if (\n err instanceof SenderRestoreError &&\n lifecycleGeneration === this.lifecycleGeneration &&\n !this.stopped\n ) {\n this.terminateWithError(err, true, lifecycleGeneration);\n return;\n }\n // ICE restart is the second stage and may still recover a sender reset or\n // renegotiation failure, so do not fail the stream at this stage.\n }\n }\n\n /**\n * Adds a recovery renegotiation to the same queue as user operations. The\n * recovery ladder awaits its completion before starting the stage observation\n * window. If recovery has completed by the time this reaches the head of the\n * queue, it is skipped.\n */\n /** Returns the peer-wide ICE attempt shared by all active track recoveries. */\n private sharedIceRestart(): Promise<void> {\n if (this.iceRestartAttempt) return this.iceRestartAttempt.promise;\n const id = ++this.iceRestartSequence;\n const promise = this.enqueueNegotiation(\n () => {\n if (\n this.iceRestartAttempt?.id !== id ||\n !this.hasActiveMediaRecovery()\n ) return false;\n },\n { iceRestart: true },\n );\n const attempt = { id, promise };\n this.iceRestartAttempt = attempt;\n // Coalesce only overlapping attempts. Retaining a completed promise while\n // another track remains in its observation window would make a later stall\n // incorrectly reuse an ICE restart that happened before that stall.\n void promise.finally(() => {\n if (this.iceRestartAttempt === attempt) this.iceRestartAttempt = null;\n }).catch(() => {});\n return promise;\n }\n\n /**\n * Serializes a renegotiation onto the shared chain: it waits for any prior\n * negotiation to finish (its answer applied), sends a fresh offer, and resolves\n * only once this offer's answer has been applied. This prevents overlapping\n * offers and answers being applied to the wrong offer.\n */\n private enqueueNegotiation(\n mutate: NegotiationMutation,\n opts: { iceRestart?: boolean } = {},\n ): Promise<void> {\n const generation = this.lifecycleGeneration;\n const run = this.negotiationChain\n .catch(() => {\n // A failed prior negotiation must not permanently break the chain; the\n // caller that owned it already saw the rejection.\n })\n .then(() => {\n this.assertActiveRun(generation);\n return this.negotiateOnce(mutate, opts, generation);\n });\n // Keep the chain alive regardless of this negotiation's outcome.\n this.negotiationChain = run.catch(() => {});\n return run;\n }\n\n private async negotiateOnce(\n mutate: NegotiationMutation,\n opts: { iceRestart?: boolean },\n generation: number,\n ): Promise<void> {\n const pc = this.pc;\n if (!pc || !this.isActiveRun(generation, pc)) {\n throw new PublisherStoppedError(\"publisher stopped\");\n }\n if (!this.runAbort?.signal) throw new PublisherStoppedError(\"publisher stopped\");\n const previousHasAnswer = this.hasAnswer;\n const previousRemoteCandidates = this.pendingRemoteCandidates;\n const previousLocalCandidateOfferSent = this.localCandidateOfferSent;\n const previousRetainedLocalCandidates = [...this.retainedLocalCandidates];\n const previousRetainedLocalCandidateKeys = new Set(this.retainedLocalCandidateKeys);\n const previousLocalCandidateGeneration = this.localCandidateGeneration;\n\n // Do not mutate tracks while signaling is unavailable: if reconnection\n // ultimately fails, the previously published state remains intact.\n await this.awaitSignaling();\n this.assertActiveRun(generation, pc);\n\n let change: NegotiationChange | undefined;\n let localOfferSet = false;\n let answerReceived = false;\n try {\n // Apply the staged track change at the head of this turn, atomic with the\n // offer it produces. A callback returning false skips negotiation.\n const result = await mutate();\n this.assertActiveRun(generation, pc);\n if (result === false) return;\n if (result && typeof result === \"object\") change = result;\n\n if (opts.iceRestart) pc.restartIce?.();\n const offer = await pc.createOffer(opts.iceRestart ? { iceRestart: true } : undefined);\n this.assertActiveRun(generation, pc);\n this.beginLocalCandidateBatch();\n await pc.setLocalDescription(offer);\n localOfferSet = true;\n this.assertActiveRun(generation, pc);\n\n const local = pc.localDescription;\n if (!local) throw new Error(\"local description missing\");\n this.hasAnswer = false;\n this.pendingRemoteCandidates = [];\n\n const id = this.nextNegotiationId();\n const { answered } = await this.sendOffer({\n type: \"offer\",\n sdp: local.sdp,\n sdp_type: \"offer\",\n negotiation_id: id,\n tracks: change?.labels?.() ?? this.buildTrackLabels(),\n speech_enabled: (this.speechEnabled || this.speechPending) || undefined,\n });\n this.releaseLocalCandidateBatch();\n const sdp = await answered;\n this.assertActiveRun(generation, pc);\n answerReceived = true;\n await pc.setRemoteDescription(new RTCSessionDescription({ type: \"answer\", sdp }));\n this.assertActiveRun(generation, pc);\n this.applyAnswered(pc);\n await change?.commit?.();\n } catch (err) {\n let rollbackFailed = false;\n if (localOfferSet && this.pc === pc && pc.signalingState === \"have-local-offer\") {\n try {\n await pc.setLocalDescription({ type: \"rollback\" });\n } catch {\n rollbackFailed = true;\n }\n }\n try {\n await change?.rollback?.();\n } catch {\n rollbackFailed = true;\n }\n\n // Once an answer was received, or delivery timed out, the server's state\n // is ambiguous. Tear down coherently instead of continuing divergent.\n const ambiguous =\n rollbackFailed ||\n answerReceived ||\n err instanceof NegotiationTimeoutError ||\n err instanceof SenderRestoreError ||\n (err instanceof ReportedPublisherError && err.fatal);\n if (this.isActiveRun(generation, pc) && ambiguous) {\n try {\n await change?.discard?.();\n } catch {\n // The peer is terminal regardless; teardown below owns all committed\n // captures and the discard hook is best-effort for staged inputs.\n }\n const failure = err instanceof Error ? err : new Error(String(err));\n this.terminateWithError(\n failure,\n !(err instanceof ReportedPublisherError),\n generation,\n );\n } else if (this.isActiveRun(generation, pc)) {\n // A pre-application rejection leaves the previous remote description\n // authoritative. Restore candidate routing and flush candidates that\n // arrived while the rejected offer was outstanding.\n const buffered = this.pendingRemoteCandidates;\n this.hasAnswer = previousHasAnswer;\n this.pendingRemoteCandidates = previousRemoteCandidates;\n this.pendingLocalCandidates = [];\n this.localCandidateOfferSent = previousLocalCandidateOfferSent;\n this.retainedLocalCandidates = previousRetainedLocalCandidates;\n this.retainedLocalCandidateKeys = previousRetainedLocalCandidateKeys;\n this.localCandidateGeneration = previousLocalCandidateGeneration;\n if (previousHasAnswer) {\n for (const init of buffered) {\n pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n }\n }\n }\n throw err;\n }\n }\n\n /**\n * Registers interest in the answer for the offer identified by `id` and returns\n * a promise for its SDP. A second pending answer is an invariant violation: all\n * offer creation, including recovery, must pass through negotiationChain.\n */\n private nextNegotiationId(): string {\n return `n${++this.negotiationSeq}`;\n }\n\n private handleTextMessage(data: unknown): void {\n if (typeof data !== \"string\") return;\n try {\n const message = JSON.parse(data) as {\n type?: string; utterance_id?: string; message_id?: string; text?: string; reason?: string;\n };\n if (message.type === \"assistant_text\" && message.utterance_id && message.text) {\n this.opts.callbacks?.onAssistantText?.({ utteranceId: message.utterance_id, text: message.text });\n } else if (message.type === \"assistant_text_finished\" && message.utterance_id) {\n this.opts.callbacks?.onAssistantTextFinished?.({ utteranceId: message.utterance_id });\n } else if ((message.type === \"user_text_accepted\" || message.type === \"user_text_rejected\") && message.message_id) {\n this.opts.callbacks?.onUserTextResult?.({\n messageId: message.message_id,\n accepted: message.type === \"user_text_accepted\",\n reason: message.reason,\n });\n }\n } catch {\n // Unknown or malformed application messages are ignored; they do not\n // compromise the WebRTC transport.\n }\n }\n\n private awaitAnswer(id: string): Promise<string> {\n if (this.pendingAnswer) {\n return Promise.reject(new Error(\"another negotiation is already awaiting an answer\"));\n }\n return new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pendingAnswer?.id === id) {\n this.pendingAnswer = null;\n reject(new NegotiationTimeoutError(\"timed out waiting for renegotiation answer\"));\n }\n }, this.negotiationAnswerTimeoutMs());\n this.pendingAnswer = {\n id,\n resolve: (sdp) => {\n clearTimeout(timer);\n resolve(sdp);\n },\n reject: (err) => {\n clearTimeout(timer);\n reject(err);\n },\n };\n });\n }\n\n private negotiationAnswerTimeoutMs(): number {\n const reconnectTimeout =\n this.opts.signalingReconnectTimeoutMs ?? defaultSignalingReconnectTimeoutMs;\n return Math.max(\n minimumNegotiationAnswerTimeoutMs,\n Math.max(0, reconnectTimeout) + negotiationReconnectGraceMs,\n );\n }\n\n private handleLocalICECandidate(candidate: RTCIceCandidate): void {\n const message: ICECandidateSignalMessage = {\n type: \"ice_candidate\",\n candidate: candidate.candidate,\n sdp_mid: candidate.sdpMid ?? undefined,\n sdp_mline_index: candidate.sdpMLineIndex ?? undefined,\n username_fragment: candidate.usernameFragment ?? undefined,\n };\n if (!this.retainLocalCandidate(message)) return;\n if (!this.localCandidateOfferSent) {\n this.pendingLocalCandidates.push(message);\n return;\n }\n void this.sendWhenSignalingAvailable(message).catch(() => {\n // stop()/fatal teardown owns the terminal error; an obsolete candidate\n // does not need a second user-facing failure.\n });\n }\n\n private candidateKey(candidate: ICECandidateSignalMessage): string {\n return JSON.stringify([\n candidate.candidate,\n candidate.sdp_mid ?? null,\n candidate.sdp_mline_index ?? null,\n candidate.username_fragment ?? null,\n ]);\n }\n\n private retainLocalCandidate(candidate: ICECandidateSignalMessage): boolean {\n const generation = candidate.username_fragment;\n if (generation) {\n if (this.localCandidateGeneration && this.localCandidateGeneration !== generation) {\n this.retainedLocalCandidates = [];\n this.retainedLocalCandidateKeys.clear();\n }\n this.localCandidateGeneration = generation;\n }\n const key = this.candidateKey(candidate);\n if (this.retainedLocalCandidateKeys.has(key)) return false;\n if (this.retainedLocalCandidates.length === maxRetainedICECandidates) {\n const evicted = this.retainedLocalCandidates.shift();\n if (evicted) this.retainedLocalCandidateKeys.delete(this.candidateKey(evicted));\n }\n this.retainedLocalCandidates.push(candidate);\n this.retainedLocalCandidateKeys.add(key);\n return true;\n }\n\n private retainRemoteCandidate(candidate: ICECandidateSignalMessage): boolean {\n const generation = candidate.username_fragment;\n if (generation) {\n if (this.remoteCandidateGeneration && this.remoteCandidateGeneration !== generation) {\n this.remoteCandidateKeys.clear();\n this.remoteCandidateOrder = [];\n }\n this.remoteCandidateGeneration = generation;\n }\n const key = this.candidateKey(candidate);\n if (this.remoteCandidateKeys.has(key)) return false;\n if (this.remoteCandidateOrder.length === maxRetainedICECandidates) {\n const evicted = this.remoteCandidateOrder.shift();\n if (evicted) this.remoteCandidateKeys.delete(evicted);\n }\n this.remoteCandidateOrder.push(key);\n this.remoteCandidateKeys.add(key);\n return true;\n }\n\n private clearRetainedICECandidates(): void {\n this.retainedLocalCandidates = [];\n this.retainedLocalCandidateKeys.clear();\n this.localCandidateGeneration = null;\n this.remoteCandidateKeys.clear();\n this.remoteCandidateOrder = [];\n this.remoteCandidateGeneration = null;\n }\n\n private beginLocalCandidateBatch(): void {\n this.localCandidateOfferSent = false;\n this.pendingLocalCandidates = [];\n }\n\n private releaseLocalCandidateBatch(): void {\n this.localCandidateOfferSent = true;\n const candidates = this.pendingLocalCandidates;\n this.pendingLocalCandidates = [];\n for (const candidate of candidates) {\n void this.sendWhenSignalingAvailable(candidate).catch(() => {\n // stop()/fatal teardown owns the terminal error.\n });\n }\n }\n\n // applyAnswered flushes ICE candidates buffered before the answer landed.\n private applyAnswered(pc: RTCPeerConnection): void {\n if (this.pc !== pc) return;\n this.hasAnswer = true;\n for (const init of this.pendingRemoteCandidates) {\n pc.addIceCandidate(init).catch(() => {\n /* ignore stale candidates */\n });\n }\n this.pendingRemoteCandidates = [];\n this.watchSelectedICEPairChanges(pc);\n void this.reportSelectedICEPath(pc);\n }\n\n private watchSelectedICEPairChanges(pc: RTCPeerConnection): void {\n try {\n const dtlsTransports = [\n pc.sctp?.transport,\n ...pc.getSenders().map((sender) => sender.transport),\n ...pc.getReceivers().map((receiver) => receiver.transport),\n ];\n for (const dtls of dtlsTransports) {\n const ice = dtls?.iceTransport;\n if (!ice || this.watchedICETransports.has(ice)) continue;\n this.watchedICETransports.add(ice);\n ice.addEventListener(\"selectedcandidatepairchange\", () => {\n void this.reportSelectedICEPath(pc);\n });\n }\n } catch {\n // Path reporting is diagnostic-only. A browser with partial transport\n // introspection must still be able to publish normally; the connected\n // state callback will retain the one-shot getStats fallback.\n }\n }\n\n // startConnectionStatsLoop begins periodic getStats() sampling once the peer\n // connection is connected. It is a no-op when polling is disabled or no\n // consumer is listening, and it re-baselines on each call so a reconnect after\n // an ICE restart starts a fresh quality assessment.\n private startConnectionStatsLoop(generation: number, pc: RTCPeerConnection): void {\n const intervalMs = Math.max(\n 0,\n this.opts.connectionStatsIntervalMs ?? defaultConnectionStatsIntervalMs,\n );\n const listening =\n !!this.opts.callbacks?.onConnectionStats || !!this.opts.callbacks?.onConnectionQualityChange;\n if (intervalMs === 0 || !listening) return;\n\n this.stopConnectionStatsLoop();\n const timer = setInterval(() => {\n // A stale timer from a superseded run must never fire callbacks.\n if (this.connectionStatsTimer !== timer) return;\n if (!this.isActiveRun(generation, pc)) {\n this.stopConnectionStatsLoop();\n return;\n }\n // Skip this tick if the previous getStats() is still pending, so slow\n // reports cannot overlap and mutate the window out of completion order.\n if (this.statsSampleInFlight) return;\n this.statsSampleInFlight = true;\n void this.sampleConnectionQuality(generation, pc, timer);\n }, intervalMs);\n this.connectionStatsTimer = timer;\n }\n\n private stopConnectionStatsLoop(): void {\n if (this.connectionStatsTimer !== null) clearInterval(this.connectionStatsTimer);\n this.connectionStatsTimer = null;\n this.statsSampleInFlight = false;\n this.lastStatsSample = null;\n // A null current level makes the next sample commit as the baseline, emitting\n // the initial quality when a (re)connection's loop starts.\n this.currentQualityLevel = null;\n this.qualityDowngradeStreak = 0;\n }\n\n private async sampleConnectionQuality(\n generation: number,\n pc: RTCPeerConnection,\n timer: ReturnType<typeof setInterval>,\n ): Promise<void> {\n let stats: RTCStatsReport | null = null;\n try {\n stats = await pc.getStats();\n } catch {\n // Fall through to release the in-flight guard below.\n }\n // If the loop was stopped or restarted while this call was outstanding, the\n // timer identity no longer matches; drop the result without touching the new\n // loop's in-flight flag or window.\n if (this.connectionStatsTimer !== timer) return;\n this.statsSampleInFlight = false;\n // Emit only while connected: the timer is paused on state change, and this\n // guards the case where a disconnect fired no connectionstatechange event.\n if (!stats || !this.isActiveRun(generation, pc) || pc.connectionState !== \"connected\") {\n return;\n }\n const sample = this.buildStatsSample(stats);\n this.opts.callbacks?.onConnectionStats?.(sample);\n // The onConnectionStats callback may have stopped the publisher or triggered a\n // reconnect; re-check before mutating quality state or emitting a change, so a\n // stale sample cannot fire onConnectionQualityChange after teardown.\n if (\n this.connectionStatsTimer !== timer ||\n !this.isActiveRun(generation, pc) ||\n pc.connectionState !== \"connected\"\n ) {\n return;\n }\n this.updateConnectionQuality(sample);\n }\n\n // buildStatsSample derives one sample from a getStats() report. Loss is a mean of\n // per-stream loss fractions weighted by each stream's packets sent this window, so\n // every stream carrying traffic contributes. A stream uses its remote fractionLost\n // when present — the remote's loss ratio over its RR interval, self-aligned and\n // excluding retransmissions (which ride a separate SSRC); dividing the remote's\n // Δ(packetsLost) by the local Δ(packetsSent) would instead misalign an RTCP-timed\n // numerator with a continuously-updated denominator — and otherwise falls back to\n // its own windowed Δ(packetsLost) / Δ(distinct media packets sent), a denominator\n // that excludes retransmissions (packetsSent - retransmittedPacketsSent). Mixing the\n // two per stream keeps a lossy stream from being dropped when a sibling has fractionLost.\n //\n // Deltas are taken only over outbound stats-object ids present in BOTH this and the\n // previous sample. A track replace/unpublish (or a recycled SSRC) deletes the old\n // stats object and creates a new one with a new id; counting a vanished stream's\n // missing tail, or a fresh object's cumulative total as an interval delta, would\n // corrupt loss. Excluding the symmetric difference baselines new objects (they count\n // from their next sample) and drops departed ones, so continuous streams still measure\n // loss even when an SSRC number is reused across distinct stats objects.\n private buildStatsSample(stats: RTCStatsReport): ConnectionStatsSample {\n const timestamp = nowMs();\n let nackCount = 0;\n let pliCount = 0;\n let haveOutbound = false;\n // qualityLimitationReason is a video-only stat; audio outbound reports omit\n // it, so it stays null for audio-only sessions rather than reporting \"none\".\n let haveOutboundVideo = false;\n let rttSeconds: number | null = null;\n let jitterSeconds: number | null = null;\n let limitationReason: string | null = null;\n let selectedPairID: string | undefined;\n let transportBytes: number | null = null;\n // Outbound counters keyed by the outbound stats-object id, plus an SSRC → id\n // index so a remote-inbound report can still pair to its stream when it omits\n // localId (it carries the media SSRC either way).\n const streams = new Map<\n string,\n { expected: number; lost: number | null; fractionLost: number | null; bytesSent: number }\n >();\n const ssrcToId = new Map<number, string>();\n\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n switch (value.type) {\n case \"outbound-rtp\": {\n haveOutbound = true;\n if (value.kind === \"video\") haveOutboundVideo = true;\n const ssrc = typeof value.ssrc === \"number\" ? value.ssrc : NaN;\n const sent = typeof value.packetsSent === \"number\" ? value.packetsSent : 0;\n const retransmitted =\n typeof value.retransmittedPacketsSent === \"number\" ? value.retransmittedPacketsSent : 0;\n const bytesSent = typeof value.bytesSent === \"number\" ? value.bytesSent : 0;\n if (typeof value.nackCount === \"number\") nackCount += value.nackCount;\n if (typeof value.pliCount === \"number\") pliCount += value.pliCount;\n if (typeof value.qualityLimitationReason === \"string\") {\n limitationReason = worseLimitation(limitationReason, value.qualityLimitationReason);\n }\n if (typeof value.id === \"string\") {\n const expected = Math.max(0, sent - retransmitted);\n streams.set(value.id, { expected, lost: null, fractionLost: null, bytesSent });\n if (!Number.isNaN(ssrc)) ssrcToId.set(ssrc, value.id);\n }\n break;\n }\n case \"remote-inbound-rtp\": {\n if (typeof value.roundTripTime === \"number\") {\n rttSeconds = Math.max(rttSeconds ?? 0, value.roundTripTime);\n }\n if (typeof value.jitter === \"number\") {\n jitterSeconds = Math.max(jitterSeconds ?? 0, value.jitter);\n }\n break;\n }\n case \"transport\": {\n if (typeof value.selectedCandidatePairId === \"string\") {\n selectedPairID = value.selectedCandidatePairId;\n }\n if (typeof value.bytesSent === \"number\") {\n transportBytes = (transportBytes ?? 0) + value.bytesSent;\n }\n break;\n }\n default:\n break;\n }\n });\n\n // Attach each remote-inbound report's loss stats to its paired outbound stream.\n // localId names the outbound stats object directly; fall back to the SSRC index.\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (value.type !== \"remote-inbound-rtp\") return;\n const outboundId =\n typeof value.localId === \"string\" && streams.has(value.localId)\n ? value.localId\n : typeof value.ssrc === \"number\"\n ? ssrcToId.get(value.ssrc)\n : undefined;\n if (outboundId === undefined) return;\n const stream = streams.get(outboundId);\n if (!stream) return;\n const lost = typeof value.packetsLost === \"number\" ? value.packetsLost : 0;\n stream.lost = (stream.lost ?? 0) + lost;\n // fractionLost is the remote's loss fraction over its last RR interval — the\n // primary loss signal, clamped since duplicates can push it negative.\n if (typeof value.fractionLost === \"number\" && Number.isFinite(value.fractionLost)) {\n stream.fractionLost = Math.min(1, Math.max(0, value.fractionLost));\n }\n });\n\n const pair =\n (selectedPairID\n ? (stats.get(selectedPairID) as unknown as Record<string, unknown> | undefined)\n : undefined) ?? findNominatedCandidatePair(stats);\n const availableOutgoingBitrate =\n pair && typeof pair.availableOutgoingBitrate === \"number\"\n ? pair.availableOutgoingBitrate\n : null;\n // The candidate pair's RTT is a fallback when no remote-inbound report carries one.\n if (rttSeconds === null && pair && typeof pair.currentRoundTripTime === \"number\") {\n rttSeconds = pair.currentRoundTripTime;\n }\n\n const prev = this.lastStatsSample;\n // Loss is a traffic-weighted mean of each stream's per-window loss fraction,\n // weighted by the packets it sent. Each stream uses its remote fractionLost when\n // present (the remote's own loss ratio, aligned in time and free of the RTCP-vs-poll\n // skew that dividing remote Δlost by local Δsent introduces) and otherwise the\n // windowed Δ(packetsLost) / Δ(distinct media packets sent). Both kinds of stream\n // contribute, so a lossy stream is never dropped just because a sibling reported\n // fractionLost.\n let weightedFractionSum = 0;\n let weightSum = 0;\n let intersectionBytesDelta = 0;\n let hadStreamOverlap = false;\n streams.forEach((current, id) => {\n const before = prev?.streams.get(id);\n if (!before) return; // New stream: baseline it now, measure from next sample.\n hadStreamOverlap = true;\n // bytesSent can dip (mid-interval resets), so clamp.\n intersectionBytesDelta += Math.max(0, current.bytesSent - before.bytesSent);\n const deltaExpected = Math.max(0, current.expected - before.expected);\n if (deltaExpected <= 0) return; // no traffic weight this window\n let fraction: number | null = null;\n if (current.fractionLost !== null) {\n fraction = current.fractionLost;\n } else if (current.lost !== null && before.lost !== null) {\n // packetsLost can dip on duplicates, so clamp.\n fraction = Math.min(1, Math.max(0, current.lost - before.lost) / deltaExpected);\n }\n if (fraction === null) return; // no loss signal for this stream this window\n weightedFractionSum += fraction * deltaExpected;\n weightSum += deltaExpected;\n });\n const deltaSeconds = prev ? (timestamp - prev.timestamp) / 1000 : 0;\n this.lastStatsSample = { timestamp, transportBytes, streams };\n\n const lossRatio = weightSum > 0 ? Math.min(1, weightedFractionSum / weightSum) : 0;\n\n // Send bitrate prefers the transport-level counter, which survives stream/SSRC\n // churn. Only when the transport report omits bytesSent does it fall back to the\n // per-stream byte deltas — and if the whole stream set was replaced (no overlap),\n // those span nothing, so the bitrate is unknown (null) rather than a bogus 0.\n let sendBitrate: number | null = null;\n if (prev && deltaSeconds > 0) {\n if (transportBytes !== null && prev.transportBytes !== null) {\n sendBitrate = (Math.max(0, transportBytes - prev.transportBytes) * 8) / deltaSeconds;\n } else if (hadStreamOverlap) {\n sendBitrate = (intersectionBytesDelta * 8) / deltaSeconds;\n }\n }\n\n return {\n timestamp,\n lossRatio,\n rttMs: rttSeconds !== null ? rttSeconds * 1000 : null,\n jitterMs: jitterSeconds !== null ? jitterSeconds * 1000 : null,\n availableOutgoingBitrate,\n sendBitrate,\n qualityLimitationReason: haveOutboundVideo ? (limitationReason ?? \"none\") : null,\n nackCount: haveOutbound ? nackCount : null,\n pliCount: haveOutbound ? pliCount : null,\n };\n }\n\n private resolveQualityThresholds(): ConnectionQualityThresholds {\n return { ...defaultConnectionQualityThresholds, ...(this.opts.connectionQualityThresholds ?? {}) };\n }\n\n // classifyQuality maps a sample to a level. Loss is the primary axis; RTT,\n // jitter, and a bandwidth-limited encoder can only raise severity.\n private classifyQuality(sample: ConnectionStatsSample): ConnectionQualityLevel {\n const t = this.resolveQualityThresholds();\n let severity = 0;\n\n if (sample.lossRatio >= t.criticalLossRatio) severity = Math.max(severity, 3);\n else if (sample.lossRatio >= t.poorLossRatio) severity = Math.max(severity, 2);\n else if (sample.lossRatio >= t.fairLossRatio) severity = Math.max(severity, 1);\n\n if (sample.rttMs !== null) {\n if (sample.rttMs >= t.criticalRttMs) severity = Math.max(severity, 3);\n else if (sample.rttMs >= t.poorRttMs) severity = Math.max(severity, 2);\n else if (sample.rttMs >= t.fairRttMs) severity = Math.max(severity, 1);\n }\n if (sample.jitterMs !== null) {\n if (sample.jitterMs >= t.poorJitterMs) severity = Math.max(severity, 2);\n else if (sample.jitterMs >= t.fairJitterMs) severity = Math.max(severity, 1);\n }\n if (sample.qualityLimitationReason === \"bandwidth\") severity = Math.max(severity, 1);\n\n return qualityLevels[severity];\n }\n\n // updateConnectionQuality commits level transitions with hysteresis: an\n // improvement is reported on the first better sample, while a degradation must\n // persist for connectionQualityDebounceSamples consecutive samples to commit,\n // so a single blip does not flap the reported level.\n private updateConnectionQuality(sample: ConnectionStatsSample): void {\n const candidate = this.classifyQuality(sample);\n const current = this.currentQualityLevel;\n\n if (current === null || candidate === current) {\n this.qualityDowngradeStreak = 0;\n if (candidate !== current) {\n this.currentQualityLevel = candidate;\n this.opts.callbacks?.onConnectionQualityChange?.({ level: candidate, sample });\n }\n return;\n }\n\n if (qualitySeverity(candidate) < qualitySeverity(current)) {\n this.qualityDowngradeStreak = 0;\n this.currentQualityLevel = candidate;\n this.opts.callbacks?.onConnectionQualityChange?.({ level: candidate, sample });\n return;\n }\n\n const needed = Math.max(\n 1,\n this.opts.connectionQualityDebounceSamples ?? defaultConnectionQualityDebounceSamples,\n );\n this.qualityDowngradeStreak += 1;\n if (this.qualityDowngradeStreak >= needed) {\n this.qualityDowngradeStreak = 0;\n this.currentQualityLevel = candidate;\n this.opts.callbacks?.onConnectionQualityChange?.({ level: candidate, sample });\n }\n }\n\n private async reportSelectedICEPath(pc: RTCPeerConnection): Promise<void> {\n if (this.pc !== pc || this.stopped) return;\n\n let stats: RTCStatsReport;\n try {\n stats = await pc.getStats();\n } catch {\n return;\n }\n if (this.pc !== pc || this.stopped) return;\n\n let selectedPairID: string | undefined;\n let selectedPair: Record<string, unknown> | undefined;\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (value.type === \"transport\" && typeof value.selectedCandidatePairId === \"string\") {\n selectedPairID = value.selectedCandidatePairId;\n }\n });\n if (selectedPairID) {\n selectedPair = stats.get(selectedPairID) as unknown as Record<string, unknown> | undefined;\n }\n if (!selectedPair) {\n stats.forEach((report) => {\n const value = report as unknown as Record<string, unknown>;\n if (\n !selectedPair &&\n value.type === \"candidate-pair\" &&\n value.state === \"succeeded\" &&\n value.nominated === true\n ) {\n selectedPair = value;\n }\n });\n }\n if (!selectedPair) return;\n\n const localID = selectedPair.localCandidateId;\n const remoteID = selectedPair.remoteCandidateId;\n if (typeof localID !== \"string\") return;\n const local = stats.get(localID) as unknown as Record<string, unknown> | undefined;\n const remote = typeof remoteID === \"string\"\n ? stats.get(remoteID) as unknown as Record<string, unknown> | undefined\n : undefined;\n if (!local || typeof local.candidateType !== \"string\") return;\n\n const message: ICEPathSignalMessage = {\n type: \"ice_path\",\n local_candidate_type: local.candidateType,\n local_protocol: typeof local.protocol === \"string\" ? local.protocol : undefined,\n remote_candidate_type: typeof remote?.candidateType === \"string\" ? remote.candidateType : undefined,\n remote_protocol: typeof remote?.protocol === \"string\" ? remote.protocol : undefined,\n relay_protocol: typeof local.relayProtocol === \"string\" ? local.relayProtocol : undefined,\n turn_url: typeof local.url === \"string\" ? local.url : undefined,\n };\n const fingerprint = JSON.stringify(message);\n if (fingerprint === this.lastReportedICEPath) return;\n this.lastReportedICEPath = fingerprint;\n try {\n await this.sendWhenSignalingAvailable(message);\n } catch {\n // Signaling teardown owns the terminal error. A later selected-pair event\n // will report the current path again if the connection remains active.\n if (this.lastReportedICEPath === fingerprint) {\n this.lastReportedICEPath = null;\n }\n }\n }\n\n private completeMediaRecovery(trackType: TrackType): void {\n const state = this.recoveryStates.get(trackType);\n if (!state?.recovering) return;\n const action = state.action ?? undefined;\n this.cancelMediaRecovery(trackType);\n this.emitRecoveryTransition({ state: \"recovered\", track: trackType, action });\n }\n\n private failMediaRecovery(\n trackType: TrackType,\n reason: \"capture_ended\" | \"automatic_recovery_failed\",\n ): void {\n const state = this.recoveryState(trackType);\n if (this.stopped || state.required) return;\n state.required = true;\n const action = state.action ?? undefined;\n this.cancelMediaRecovery(trackType);\n const event: PublisherRecoveryEvent = { state: \"failed\", track: trackType, action, reason };\n this.emitRecoveryTransition(event);\n this.opts.callbacks?.onRecoveryRequired?.(event);\n this.sendRecoveryDiagnostic(\"recovery_failed\", trackType, action, reason);\n }\n\n private recoveryState(trackType: TrackType): TrackRecoveryState {\n let state = this.recoveryStates.get(trackType);\n if (!state) {\n state = { generation: 0, recovering: false, required: false, action: null };\n this.recoveryStates.set(trackType, state);\n }\n return state;\n }\n\n private cancelMediaRecovery(trackType: TrackType): void {\n const state = this.recoveryState(trackType);\n state.generation = ++this.recoverySequence;\n state.recovering = false;\n state.action = null;\n this.clearSharedIceRestartIfIdle();\n }\n\n private cancelAllMediaRecovery(): void {\n for (const trackType of this.recoveryStates.keys()) {\n this.cancelMediaRecovery(trackType);\n }\n }\n\n private isCurrentRecovery(trackType: TrackType, generation: number): boolean {\n const state = this.recoveryStates.get(trackType);\n return !this.stopped && !!state?.recovering && state.generation === generation;\n }\n\n private hasActiveMediaRecovery(): boolean {\n for (const state of this.recoveryStates.values()) {\n if (state.recovering) return true;\n }\n return false;\n }\n\n private clearSharedIceRestartIfIdle(): void {\n if (!this.hasActiveMediaRecovery()) this.iceRestartAttempt = null;\n }\n\n private emitRecoveryTransition(event: PublisherRecoveryEvent): void {\n this.opts.callbacks?.onRecoveryStateChange?.(event);\n }\n\n private sendRecoveryDiagnostic(\n event: \"recovery_started\" | \"recovery_retry\" | \"recovery_failed\",\n track: TrackType,\n action?: PublisherRecoveryAction,\n reason?: \"capture_ended\" | \"automatic_recovery_failed\",\n ): void {\n this.sig?.send({ type: \"recovery_event\", event, track, action, reason });\n }\n\n // -------------------------------------------------------------------------\n // Published-track bookkeeping\n // -------------------------------------------------------------------------\n\n private requireSingleVideoTrack(stream: MediaStream): MediaStreamTrack {\n const tracks = stream.getVideoTracks();\n if (tracks.length !== 1) {\n throw new Error(\n `expected exactly one video track, received ${tracks.length}`,\n );\n }\n this.requireLiveVideoTrack(tracks[0]);\n return tracks[0];\n }\n\n private requireLiveVideoTrack(track: MediaStreamTrack): void {\n if (track.readyState === \"ended\") {\n throw new Error(\"video track has already ended\");\n }\n }\n\n private requireLiveTrack(track: MediaStreamTrack, kind: \"video\" | \"audio\"): void {\n if (track.readyState === \"ended\") {\n throw new Error(`${kind} track has already ended`);\n }\n }\n\n private requireSingleAudioTrack(stream: MediaStream): MediaStreamTrack {\n const tracks = stream.getAudioTracks();\n if (tracks.length !== 1) {\n throw new Error(\n `expected exactly one audio track, received ${tracks.length}`,\n );\n }\n if (tracks[0].readyState === \"ended\") {\n throw new Error(\"audio track has already ended\");\n }\n return tracks[0];\n }\n\n /**\n * Stages the microphone track onto the peer connection. Unlike video, audio\n * has no recovery ladder and no SSIM/frame semantics, so this simply adds (or\n * replaces) the single audio sender and declares the updated labels.\n */\n private async stagePublishAudio(\n track: MediaStreamTrack,\n stream: MediaStream,\n ): Promise<NegotiationMutationResult> {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n if (track.readyState === \"ended\") throw new Error(\"audio track has already ended\");\n\n const type: TrackType = \"audio\";\n if (this.published.get(track) === type) return false;\n\n const previous = this.tracksOfType(type).map((oldTrack) => ({\n track: oldTrack,\n stream: this.publishedStreams.get(oldTrack)!,\n }));\n const typeSender = this.typeSenders.get(type) ?? null;\n\n let addedSender: RTCRtpSender | null = null;\n let replacedSender: RTCRtpSender | null = null;\n if (previous.length > 0) {\n if (previous.length !== 1 || !typeSender || typeSender.track !== previous[0].track) {\n throw new SenderRestoreError(\"published audio sender is unavailable\");\n }\n replacedSender = typeSender;\n await replacedSender.replaceTrack(track);\n } else {\n if (typeSender?.track) {\n throw new SenderRestoreError(\"inactive audio sender still has a track\");\n }\n if (\n this.microphoneTransceiver &&\n this.microphoneTransceiver.sender.track === null\n ) {\n addedSender = this.microphoneTransceiver.sender;\n await addedSender.replaceTrack(track);\n this.microphoneTransceiver.direction = \"sendonly\";\n } else {\n addedSender = this.addMicrophoneTrack(pc, track, stream);\n }\n this.typeSenders.set(type, addedSender);\n }\n\n return {\n labels: () => this.labelsReplacingType(type, track),\n commit: () => {\n for (const { track: oldTrack } of previous) {\n this.unwatchTrack(oldTrack);\n this.published.delete(oldTrack);\n this.publishedStreams.delete(oldTrack);\n oldTrack.stop();\n }\n // A microphone has lifecycle observation but never enters watchTrack's\n // video recovery ladder.\n this.published.set(track, type);\n this.publishedStreams.set(track, stream);\n this.watchMicrophone(track);\n },\n rollback: async () => {\n if (this.pc !== pc) return;\n if (addedSender && pc.getSenders().includes(addedSender)) {\n pc.removeTrack(addedSender);\n if (typeSender) this.typeSenders.set(type, typeSender);\n else this.typeSenders.delete(type);\n }\n if (replacedSender && previous.length > 0) {\n const oldTrack = previous[0].track;\n if (oldTrack.readyState !== \"ended\") {\n await replacedSender.replaceTrack(oldTrack);\n }\n } else if (replacedSender) {\n pc.removeTrack(replacedSender);\n }\n },\n discard: () => track.stop(),\n };\n }\n\n /**\n * Gives the microphone its own sendonly transceiver. addTrack() may reuse an\n * existing compatible recvonly transceiver, which would collapse the\n * microphone and assistant speech roles when speech was enabled first.\n * addTransceiver() always creates a distinct m-line and its direction keeps\n * the server's outbound speech sender on the dedicated speech transceiver.\n */\n private addMicrophoneTrack(\n pc: RTCPeerConnection,\n track: MediaStreamTrack,\n stream: MediaStream,\n ): RTCRtpSender {\n const transceiver = pc.addTransceiver(track, {\n direction: \"sendonly\",\n streams: [stream],\n });\n this.microphoneTransceiver = transceiver;\n return transceiver.sender;\n }\n\n /**\n * Reorders the codecs a video sender offers so the browser prefers the\n * configured codecs (VP9 by default). This is what actually controls the wire\n * format: the browser is the offerer and sends its own top-of-offer codec, and\n * the media server (Pion) answers by mirroring the offer's codec order — so the\n * server's own registration order has no say. Floating VP9 to the front of the\n * offer is therefore the lever. Codecs not in the preference keep their native\n * order behind it, so anything VP9 can't satisfy still negotiates.\n *\n * Best-effort: browsers lacking `getCapabilities`/`setCodecPreferences` (older\n * Safari) keep their default order, and any failure is swallowed — codec\n * preference is an optimization, never a requirement for publishing.\n */\n private preferVideoCodecs(pc: RTCPeerConnection, sender: RTCRtpSender): void {\n const preferred = this.opts.preferredVideoCodecs ?? [\"video/VP9\"];\n if (preferred.length === 0) return;\n if (sender.track && sender.track.kind !== \"video\") return;\n // `typeof RTCRtpSender` guards environments where the global is absent\n // entirely (test harness, non-WebRTC runtimes) — a bare member access there\n // would throw a ReferenceError.\n if (typeof RTCRtpSender === \"undefined\" || typeof RTCRtpSender.getCapabilities !== \"function\") return;\n if (typeof pc.getTransceivers !== \"function\") return;\n\n const caps = RTCRtpSender.getCapabilities(\"video\");\n if (!caps?.codecs) return;\n const transceiver = pc.getTransceivers().find((t) => t.sender === sender);\n if (!transceiver || typeof transceiver.setCodecPreferences !== \"function\") return;\n\n const rank = (mimeType: string): number => {\n const idx = preferred.findIndex((p) => p.toLowerCase() === mimeType.toLowerCase());\n return idx === -1 ? preferred.length : idx;\n };\n // Stable reorder: preferred codecs first in the configured order; everything\n // else — including RTX, which the browser re-associates by its apt fmtp\n // regardless of position — keeps its original order.\n const ordered = caps.codecs\n .map((codec, index) => ({ codec, index }))\n .sort((a, b) => rank(a.codec.mimeType) - rank(b.codec.mimeType) || a.index - b.index)\n .map((entry) => entry.codec);\n\n try {\n transceiver.setCodecPreferences(ordered);\n } catch {\n // Ignore: an unsupported preference just leaves the native order in place.\n }\n }\n\n /** Registers one physical video track under its logical type and source stream. */\n private registerTrack(track: MediaStreamTrack, stream: MediaStream, type: TrackType): void {\n this.published.set(track, type);\n this.publishedStreams.set(track, stream);\n this.watchTrack(track);\n }\n\n /** The live video tracks currently published under the given type. */\n private tracksOfType(type: TrackType): MediaStreamTrack[] {\n const out: MediaStreamTrack[] = [];\n for (const [track, tt] of this.published) {\n if (tt === type) out.push(track);\n }\n return out;\n }\n\n /** Builds the id → type label array declared to the server on every offer. */\n private buildTrackLabels(): TrackLabel[] {\n const labels: TrackLabel[] = [];\n for (const [track, type] of this.published) {\n const mid = this.midForTrack(track);\n if (mid !== null) labels.push({ mid, id: track.id, type });\n }\n return labels;\n }\n\n private labelsReplacingType(type: TrackType, replacement?: MediaStreamTrack): TrackLabel[] {\n const labels: TrackLabel[] = [];\n for (const [track, publishedType] of this.published) {\n if (publishedType === type) continue;\n const mid = this.midForTrack(track);\n if (mid !== null) labels.push({ mid, id: track.id, type: publishedType });\n }\n if (replacement) {\n const mid = this.midForTrack(replacement);\n if (mid !== null) labels.push({ mid, id: replacement.id, type });\n }\n return labels;\n }\n\n /**\n * The negotiated mid of the transceiver currently sending `track`, or null if\n * none is found or it has not been negotiated yet. The mid is the identifier\n * both peers agree on; it is assigned once setLocalDescription runs, which the\n * publisher always does before sending an offer's labels.\n */\n private midForTrack(track: MediaStreamTrack): string | null {\n const transceiver = this.pc\n ?.getTransceivers()\n .find((candidate) => candidate.sender.track === track);\n return transceiver?.mid ?? null;\n }\n\n private async stagePublish(\n track: MediaStreamTrack,\n stream: MediaStream,\n type: VideoTrackType,\n ): Promise<NegotiationMutationResult> {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n this.requireLiveVideoTrack(track);\n\n const alreadyPublishedAs = this.published.get(track);\n if (alreadyPublishedAs === type) return false;\n if (alreadyPublishedAs) {\n throw new Error(`video track is already published as ${alreadyPublishedAs}`);\n }\n\n const previous = this.tracksOfType(type).map((oldTrack) => ({\n track: oldTrack,\n stream: this.publishedStreams.get(oldTrack)!,\n }));\n const typeSender = this.typeSenders.get(type) ?? null;\n\n let addedSender: RTCRtpSender | null = null;\n let replacedSender: RTCRtpSender | null = null;\n if (previous.length > 0) {\n if (\n previous.length !== 1 ||\n !typeSender ||\n typeSender.track !== previous[0].track\n ) {\n throw new SenderRestoreError(`published ${type} sender is unavailable`);\n }\n // Preserve the negotiated transceiver. removeTrack()+addTrack() is not a\n // reversible replacement: browsers may allocate a new m-line, breaking\n // rollback and growing SDP after every source change.\n replacedSender = typeSender;\n await replacedSender.replaceTrack(track);\n } else {\n if (typeSender?.track) {\n throw new SenderRestoreError(`inactive ${type} sender still has a track`);\n }\n // replaceTrack alone does not change an inactive transceiver back to a\n // sending direction. addTrack performs that transition and is specified\n // to reuse an eligible inactive transceiver when one is available.\n addedSender = pc.addTrack(track, stream);\n this.preferVideoCodecs(pc, addedSender);\n this.typeSenders.set(type, addedSender);\n }\n\n return {\n labels: () => this.labelsReplacingType(type, track),\n commit: () => {\n this.cancelMediaRecovery(type);\n this.recoveryState(type).required = false;\n for (const { track: oldTrack } of previous) {\n this.unwatchTrack(oldTrack);\n this.published.delete(oldTrack);\n this.publishedStreams.delete(oldTrack);\n oldTrack.stop();\n }\n this.registerTrack(track, stream, type);\n if (track.readyState === \"ended\") {\n this.failMediaRecovery(type, \"capture_ended\");\n }\n },\n rollback: async () => {\n if (this.pc !== pc) return;\n if (addedSender && pc.getSenders().includes(addedSender)) {\n pc.removeTrack(addedSender);\n if (typeSender) this.typeSenders.set(type, typeSender);\n else this.typeSenders.delete(type);\n }\n if (replacedSender && previous.length > 0) {\n const oldTrack = previous[0].track;\n if (oldTrack.readyState !== \"ended\") {\n await replacedSender.replaceTrack(oldTrack);\n }\n } else if (replacedSender) {\n pc.removeTrack(replacedSender);\n }\n },\n discard: () => track.stop(),\n };\n }\n\n private stageUnpublish(type: TrackType): NegotiationChange {\n const pc = this.pc;\n if (!pc) throw new Error(\"publisher not started\");\n const previous = this.tracksOfType(type).map((track) => ({\n track,\n stream: this.publishedStreams.get(track)!,\n }));\n const typeSender = this.typeSenders.get(type);\n if (\n previous.length !== 1 ||\n !typeSender ||\n typeSender.track !== previous[0].track\n ) {\n throw new SenderRestoreError(`published ${type} sender is unavailable`);\n }\n\n // The pending end is keyed on the browser track id — the generation identity\n // the server echoes in track_id. (The mid is unsuitable: it is reused when a\n // track of the same type is republished on the same m-line.)\n for (const { track } of previous) this.expectIntentionalTrackEnd(track.id, type);\n pc.removeTrack(typeSender);\n\n return {\n labels: () => this.labelsReplacingType(type),\n commit: () => {\n this.cancelMediaRecovery(type);\n this.recoveryState(type).required = false;\n this.typeSenders.delete(type);\n for (const { track } of previous) {\n this.unwatchTrack(track);\n this.published.delete(track);\n this.publishedStreams.delete(track);\n track.stop();\n }\n },\n rollback: () => {\n if (this.pc !== pc) return;\n this.forgetIntentionalTrackEnds(previous.map(({ track }) => track.id));\n return Promise.all(previous.map(async ({ track }) => {\n if (track.readyState === \"ended\") return;\n await typeSender.replaceTrack(track);\n })).then(() => undefined);\n },\n };\n }\n\n /** Stops every published local track and clears the published map. */\n private stopPublishedTracks(): void {\n for (const track of this.published.keys()) {\n track.stop();\n }\n this.published.clear();\n this.publishedStreams.clear();\n }\n\n private expectIntentionalTrackEnd(trackID: string, type: TrackType): void {\n const prior = this.intentionalTrackEnds.get(trackID);\n if (prior) clearTimeout(prior.timer);\n const retentionMs = Math.max(\n minimumIntentionalTrackEndRetentionMs,\n this.negotiationAnswerTimeoutMs(),\n );\n const timer = setTimeout(() => {\n const current = this.intentionalTrackEnds.get(trackID);\n if (current?.timer === timer) this.intentionalTrackEnds.delete(trackID);\n }, retentionMs);\n this.intentionalTrackEnds.set(trackID, { type, timer });\n }\n\n private forgetIntentionalTrackEnds(trackIDs: string[]): void {\n for (const trackID of trackIDs) {\n const expected = this.intentionalTrackEnds.get(trackID);\n if (!expected) continue;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(trackID);\n }\n }\n\n private consumeIntentionalTrackEnd(type: TrackType, trackID?: string): boolean {\n if (trackID) {\n const expected = this.intentionalTrackEnds.get(trackID);\n if (!expected || expected.type !== type) return false;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(trackID);\n return true;\n }\n\n // Older media servers did not identify the physical track. Consume the\n // oldest expected end of this type as a best-effort compatibility fallback.\n for (const [id, expected] of this.intentionalTrackEnds) {\n if (expected.type !== type) continue;\n clearTimeout(expected.timer);\n this.intentionalTrackEnds.delete(id);\n return true;\n }\n return false;\n }\n\n private clearIntentionalTrackEnds(): void {\n for (const expected of this.intentionalTrackEnds.values()) {\n clearTimeout(expected.timer);\n }\n this.intentionalTrackEnds.clear();\n }\n\n /**\n * Watches a track's \"ended\" event so an involuntary capture stop (the user\n * revokes a screen share, a device unplugs) reports as a recovery failure for\n * that track's actual type. Intentional removals unwatch first.\n */\n private watchTrack(track: MediaStreamTrack): void {\n if (this.trackEndHandlers.has(track)) return;\n const handler: EventListener = () => {\n const type = this.published.get(track) ?? \"camera\";\n this.failMediaRecovery(type, \"capture_ended\");\n };\n track.addEventListener(\"ended\", handler);\n this.trackEndHandlers.set(track, handler);\n }\n\n /**\n * Watches a microphone only for lifecycle removal. An ended microphone is not\n * recovered like video; it is negotiated away so the media server can flush\n * the utterance and release transcription resources.\n */\n private watchMicrophone(track: MediaStreamTrack): void {\n if (this.trackEndHandlers.has(track)) return;\n const generation = this.lifecycleGeneration;\n const handler: EventListener = () => {\n if (this.published.get(track) !== \"audio\") return;\n void this.enqueueNegotiation(() => {\n // Recheck identity when this turn reaches the head of the queue. A user\n // may already be replacing this ended source; its delayed removal must\n // not unpublish that committed replacement.\n if (this.published.get(track) !== \"audio\") return false;\n return this.stageUnpublish(\"audio\");\n }).catch((err) => {\n if (!this.isActiveRun(generation)) return;\n const failure = err instanceof Error ? err : new Error(String(err));\n this.terminateWithError(failure, true, generation);\n });\n };\n track.addEventListener(\"ended\", handler);\n this.trackEndHandlers.set(track, handler);\n // The track may have ended while its publish negotiation was awaiting an\n // answer, before the committed source acquired this listener.\n if (track.readyState === \"ended\") handler(new Event(\"ended\"));\n }\n\n private unwatchTrack(track: MediaStreamTrack): void {\n const handler = this.trackEndHandlers.get(track);\n if (!handler) return;\n track.removeEventListener(\"ended\", handler);\n this.trackEndHandlers.delete(track);\n }\n\n private unwatchStreamTracks(): void {\n for (const [track, handler] of this.trackEndHandlers) {\n track.removeEventListener(\"ended\", handler);\n }\n this.trackEndHandlers.clear();\n }\n\n}\n","/**\n * Options for {@link captureCamera}.\n *\n * These are merged shallowly over the library defaults: any field you provide\n * replaces the default for that field entirely (e.g. passing `video` overrides\n * the default video constraints rather than merging into them). Omit a field to\n * keep its default.\n */\nexport interface CaptureCameraOptions {\n /**\n * Video constraints, or `true`/`false`. Defaults to a modest resolution and\n * frame rate (see {@link captureCamera}). Set `false` to disable video.\n */\n video?: MediaTrackConstraints | boolean;\n /**\n * Audio constraints, or `true`/`false`. Defaults to `false` — Argus is a\n * video-frame streaming system, so audio is off unless you ask for it.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`. Pass another window's `navigator.mediaDevices`\n * (e.g. a Document Picture-in-Picture window's) so the browser's permission\n * prompt and transient-activation check are anchored to that window rather\n * than the one holding the global `navigator`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Options for {@link captureScreen}.\n *\n * These are merged shallowly over the library defaults: any field you provide\n * replaces the default for that field entirely. Omit a field to keep its\n * default.\n */\nexport interface CaptureScreenOptions {\n /**\n * Video constraints, or `true`. Defaults to a capped width and a low frame\n * rate (see {@link captureScreen}).\n */\n video?: MediaTrackConstraints | boolean;\n /**\n * Audio constraints, or `true`/`false`. Defaults to `false` — screen shares\n * rarely need audio for a video-frame streaming system.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`. Pass another window's `navigator.mediaDevices`\n * (e.g. a Document Picture-in-Picture window's) so the screen picker and its\n * transient-activation check are anchored to that window rather than the one\n * holding the global `navigator`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Captures the user's camera via `navigator.mediaDevices.getUserMedia`,\n * applying sensible defaults for a video-frame streaming system.\n *\n * Defaults:\n * - `video`: `{ width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } }`\n * — a modest resolution that keeps upload bandwidth reasonable. Cameras are\n * rarely the 4k bandwidth problem that screen capture is, so this is an\n * `ideal` (a hint) rather than a hard cap.\n * - `audio`: `false` — this is a video-frame streaming system.\n *\n * Any option you pass replaces the corresponding default outright (shallow\n * merge), so pass a full `video` constraints object if you want to tweak it.\n *\n * @example\n * ```ts\n * const stream = await captureCamera();\n * await publisher.start(stream);\n * ```\n *\n * @example Front camera with audio:\n * ```ts\n * const stream = await captureCamera({\n * video: { facingMode: \"user\" },\n * audio: true,\n * });\n * ```\n */\nexport async function captureCamera(\n opts: CaptureCameraOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n video: opts.video ?? {\n width: { ideal: 1280 },\n height: { ideal: 720 },\n frameRate: { ideal: 30 },\n },\n audio: opts.audio ?? false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n}\n\n/**\n * Captures a screen / window / tab via\n * `navigator.mediaDevices.getDisplayMedia`, applying sensible defaults tuned to\n * avoid the HiDPI/Retina bandwidth trap.\n *\n * Defaults:\n * - `video`: `{ width: { max: 1920 }, frameRate: { ideal: 5, max: 10 } }`.\n * The `width` is a **`max`, not an `ideal`**: on a 2x-Retina display the\n * browser would otherwise capture at native resolution (often 3456px+ /\n * effectively 4k), wasting upload bandwidth and downstream decode cost for no\n * visible benefit. Capping the max roughly halves a 2x-Retina share while\n * leaving smaller displays untouched. Screen content is mostly static, so the\n * low frame rate saves further bandwidth.\n * - `audio`: `false`.\n *\n * IMPORTANT — do NOT add `resizeMode: \"none\"` here. That value forbids the\n * browser from downscaling the source, which turns the `width: { max: 1920 }`\n * cap into a no-op on exactly the Retina displays it targets. By omitting\n * `resizeMode` we let the user agent scale to satisfy the constraint (its\n * default behaviour), which is the entire point of this helper. It is tempting\n * to add `resizeMode: \"none\"` back for \"sharpness\" — don't.\n *\n * Any option you pass replaces the corresponding default outright (shallow\n * merge), so pass a full `video` constraints object if you want to tweak it.\n *\n * @example\n * ```ts\n * const stream = await captureScreen();\n * await publisher.start(stream);\n * ```\n */\nexport async function captureScreen(\n opts: CaptureScreenOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n video: opts.video ?? {\n width: { max: 1920 },\n frameRate: { ideal: 5, max: 10 },\n },\n audio: opts.audio ?? false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getDisplayMedia(constraints);\n}\n\n/**\n * Options for {@link captureMicrophone}.\n */\nexport interface CaptureMicrophoneOptions {\n /**\n * Audio constraints, or `true`. Defaults to enabling the browser's echo\n * cancellation, noise suppression, and auto gain control — the settings that\n * give a speech-to-text engine the cleanest signal.\n */\n audio?: MediaTrackConstraints | boolean;\n /**\n * The `MediaDevices` instance to capture from. Defaults to the global\n * `navigator.mediaDevices`.\n */\n mediaDevices?: MediaDevices;\n}\n\n/**\n * Captures the user's microphone via `navigator.mediaDevices.getUserMedia`,\n * applying defaults tuned for speech-to-text. Pair with\n * {@link Publisher.publishMicrophone} to add transcription to a stream.\n *\n * Defaults:\n * - `audio`: `{ echoCancellation: true, noiseSuppression: true, autoGainControl: true }`.\n *\n * @example\n * ```ts\n * const mic = await captureMicrophone();\n * await publisher.publishMicrophone(mic);\n * ```\n */\nexport async function captureMicrophone(\n opts: CaptureMicrophoneOptions = {},\n): Promise<MediaStream> {\n const constraints: MediaStreamConstraints = {\n audio: opts.audio ?? {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n video: false,\n };\n const mediaDevices = opts.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n}\n"],"mappings":"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","names":[]}
|