Skip to content

feat(rt): add IVS transport to SDK#100

Draft
nagar-decart wants to merge 22 commits intomainfrom
nagar-decart/ivs-sdk-transport
Draft

feat(rt): add IVS transport to SDK#100
nagar-decart wants to merge 22 commits intomainfrom
nagar-decart/ivs-sdk-transport

Conversation

@nagar-decart
Copy link
Contributor

Summary

  • Add IVS (Amazon Interactive Video Service) transport layer to the realtime SDK
  • Includes stats collection, latency diagnostics, and observability

Status

🚧 Work in progress — do not merge

🤖 Generated with Claude Code

nagar-decart and others added 22 commits March 9, 2026 19:58
Add IVS (Interactive Video Service) as an alternative transport to WebRTC.
Users select transport via `transport: "ivs"` in connect options — both paths
return the same RealTimeClient interface.

New files: transport-manager.ts (shared interface), ivs-connection.ts,
ivs-manager.ts. Updated client.ts for transport selection, methods.ts to
accept the shared interface, and WebRTCManager to implement it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add transport dropdown (WebRTC/IVS) to the test page. When IVS is
selected, the IVS Web Broadcast SDK is loaded from CDN. The chosen
transport is passed to client.realtime.connect().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bouncer's IVS handler waits for `ivs_joined` as the first client
message after sending `ivs_stage_ready`. The message pump (which handles
set_image/prompt) only starts after `ivs_joined` is received.

Sending set_image before the stage handshake caused the bouncer to read
it instead of `ivs_joined`, rejecting with "Expected ivs_joined message".

Fix: reorder IVS connection phases so stage setup completes first, then
send initial image/prompt once the bouncer's message pump is running.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The IVS SDK's StageStrategy callbacks take (participant) not (stage, participant).
This caused TypeError: Cannot read properties of undefined (reading 'isLocal').

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
IVS SDK v1.14.0 does not pass participant to shouldPublishParticipant
or shouldSubscribeToParticipant. Use argument-free callbacks instead:
- publish: always true (only called for local publish-eligible participants)
- subscribe: always AUDIO_VIDEO (subscribe to all remote streams)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SDK uses pnpm — package-lock.json was generated by mistake.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add transport-aware subscribe flow so viewers can watch IVS sessions
without consuming inference server resources (SFU handles fan-out).

- Add optional transport field to subscribe token encoding
- Add subscribeIVS path: fetches viewer token from bouncer, creates
  subscribe-only IVS stage
- Export getIVSBroadcastClient and IVSBroadcastModule for reuse

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The realtime client's baseUrl is a WebSocket URL (wss://), but the
IVS subscribe endpoint is an HTTP GET. Convert the protocol before
calling fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The subscribe stage was subscribing to ALL non-local participants,
including the client's own camera feed. Now uses
client_publish_participant_id from bouncer's ivs_stage_ready message
to skip the client's publish participant and only receive the
server's processed output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use server_publish_participant_id from the subscribe-ivs response to only
subscribe to the server's inference output stream, preventing the viewer
from accidentally receiving the client's camera input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add requestRTCStats to IVS type declarations, store remote/local stage
streams in IVSConnection, and expose them via getter methods proxied
through IVSManager. Streams are cleared on cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move delta-tracking state and parse logic into a reusable StatsParser
class so it can be shared between WebRTCStatsCollector and the upcoming
IVSStatsCollector. No external API change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Polls IVS stage streams via requestRTCStats() at 1s intervals and
feeds merged RTCStatsReport into StatsParser, mirroring the
WebRTCStatsCollector pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire IVSStatsCollector into client.ts alongside WebRTC stats collection.
Both transports now get stats events, video stall detection, and telemetry
reporting. Add transport tag to telemetry reports for backend filtering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Required for IVSManager's public getRemoteStreams/getLocalStreams
methods to be type-checkable by downstream consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two new latency tracking approaches for real-time streams:
- Composite RTT: stitches client/server STUN RTTs + pipeline latency
- Pixel Marker: embeds seq number in output frame pixels for true E2E measurement

Both work across WebRTC and IVS transports, gated by client connect options.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate CompositeLatencyTracker + PixelLatencyProbe setup/teardown
into a single pluggable LatencyDiagnostics class, reducing ~35 lines of
inline wiring in client.ts to a simple instantiate/wire/stop pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
IVS publish stage was only sending video tracks and WHIP publish
was missing the audio output track, causing audio to be silently
dropped — a regression from existing WebRTC behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… start

Reject remoteStreamPromise when IVS subscribe stage disconnects during
setup, preventing the connect() flow from hanging forever. Store and
clear the latency diagnostics delayed-start timer on disconnect to avoid
restarting diagnostics after cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With high pipeline delays (17-22s, e.g. packet loss + high FPS),
probes were TTL-expired before the stamped frame reached the client,
causing the measurement to silently drop and report stale low values
instead of the actual delay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client burns binary seq marker into input frame pixels via canvas
processing → server reads and re-stamps → client reads output and
computes true round-trip latency through the video path.

Changes:
- pixel-latency-stamper.ts: canvas-based input frame stamper that wraps
  camera MediaStreamTrack (draw camera → stamp marker → captureStream)
- pixel-latency.ts: E2E mode (stamp input + read output), stats tracking
  (sent/received/lost/corrupted/outOfOrder), periodic e2e_latency_report
- latency-diagnostics.ts: createStamper() wraps localStream, deferred
  probe creation to start() for stamper availability
- client.ts: create stamper before manager.connect() to substitute the
  published stream when pixelMarker is enabled
- types.ts: add E2ELatencyReportMessage, update OutgoingMessage unions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant