Skip to main content
A model can receive live video and audio from the client — webcam, screen share, microphone. You declare the tracks you expect, and the runtime hands you a buffer per track to read from.

Declaring input tracks

Subclass Input with one field per track, then annotate it on your model:
The field names are the track identifiers your client publishes to. The runtime binds a live buffer to each one, reachable as self.input.webcam and self.input.mic. The attribute name is up to you — input is the convention, but the runtime finds the holder by its type, not its name.

Frames

Every read returns a list of InputFrame: Video data is (height, width, 3) uint8 RGB. Audio data is (1, samples) int16 mono at 48 kHz.

Reading without blocking

try_read(n=1) returns n frames, or None when fewer than n have arrived. It never waits, and when it returns None it consumes nothing — the frames already buffered are still there for the next call. This is the right default for a generation loop, because it lets the model keep producing when the client’s camera stalls:

Reading with a wait

await read(n) parks until n frames are available, then returns them. Use it when a forward pass genuinely cannot proceed without input:
Pass timeout in seconds for a bounded wait, which raises TimeoutError if it elapses:
An indefinite read() parks until a frame arrives or the track closes. Closing the track is what releases it, so prefer try_read() or a timeout in a loop that also needs to notice a client leaving.

BufferClosed

Reads raise BufferClosed once a track is closed. Catch it around the session loop:

Newest frames or oldest

Reads take a mode that decides which frames you get:
LATEST is right for video: if your model falls behind, you want the current frame, not a stale one. It clears the backlog so you never accumulate lag. FIFO is right for audio: dropping chunks leaves audible gaps and sample-level discontinuities, so consume them in order. A common pattern is to drain every queued chunk into a backlog each tick:
Each buffer holds 128 frames and evicts the oldest when full, so a model that never reads a track will not grow memory without bound.

Clearing between clients

Buffers persist across connections and across sessions, until you clear them. When a new client joins, frames from the previous one can still be queued, so clear the tracks you care about:
clear() drops buffered frames and leaves the track open for reading.

Full example: video to video

Next

Audio

Emit audio tracks and keep them in sync with video.

The Run Loop

Emitting frames, batches, and frame rates.