Skip to main content
A Track is a handle onto one named media slot the model declared — not something you construct yourself. Obtain one from reactor.track(name) when you know its name, or by filtering reactor.tracks — a TrackList — when you don’t:
One type covers both directions and both kinds — video and audio — because the operations are the same operations either way: push_frame() sends, on_frame() receives, pause()/resume() control a recvonly track. There is no push_video_frame / push_audio_frame split at the Track level, and no on_video_frame / on_audio_frame split: the track already knows its kind.
Push a frame, receive one, or pause/resume a recvonly track through this object. publish_track() / unpublish_track() also stay on Reactor directly — publish_track() hands back this same Track. See Reactor.track() for how to obtain one.
Calling a method the track’s direction doesn’t allow raises, on purpose — push_frame() on a recvonly track, on_frame() on a sendonly one, pause()/resume() on a sendonly track, and so on.

Properties

name

Signature
The declared name of this track. Never changes.

kind

Signature
TrackKind.VIDEO or TrackKind.AUDIO, or None before the session has declared its tracks.

direction

Signature
TrackDirection.SENDONLY or TrackDirection.RECVONLY, or None before the session has declared its tracks.

mid

Signature
The SDP media id, once the track has been received. None until then, and renegotiated across a reconnect.

paused

Signature
Whether this track is currently paused. Read live from the session — a recvonly track is resumed automatically on reconnect, so this always reflects the current state rather than a cached one.

published

Signature
Whether this sendonly slot is currently activated. Always False for a recvonly track — there is nothing to activate on a slot that only receives. Unlike paused, this is not read live from the session: the session itself doesn’t record it (publish_track is a control request and unpublish_track a notification, neither leaves anything to query), so the Track tracks it locally. It goes back to False on its own when the connection leaves ready — see the note on publish() below.

Sending (sendonly tracks)

publish()

Activates this sendonly slot, so frames pushed into it go on the wire.
Signature
Until this returns, push_frame() raises — an unpublished slot has no sender behind it, so the frames would be accepted and dropped rather than sent. Returns the track, so getting one and activating it can be a single line:
Example
The activation lasts as long as the session does, not the connection: a reconnect resumes recvonly tracks automatically but does not restore a sendonly track’s publish, so publish again for anything you were sending after reactor.reconnect(). published says which side of that you’re on.

unpublish()

Deactivates this sendonly slot. Synchronous, unlike the other track methods — it never touches the network, only a local status check and a fire-and-forget notification.
Signature
Unlike every other operation on Track, a failure here is logged, not raised — see the underlying Reactor.unpublish_track(). Unpublish is commonly the last call in a finally block; raising there would replace whatever exception was already propagating instead of adding to it. Check the logs (reactor_sdk at WARNING) if a track seems to have stayed published.
Calling it on a slot that is already not published does nothing — not even a notification to the session — deliberately: this is what that same finally block often calls after the failure that ended the session already cleared the publish, and raising there would replace the exception on its way out with this one.

push_frame()

Pushes one frame into this sendonly track. What data may be — and what else is needed — follows from the track’s kind, which is why there is one method and not two.
Signature
Raises InvalidStateError if the track isn’t published yet — an unpublished slot has no sender behind it, so a pushed frame would be accepted and dropped rather than sent. Call publish() first.
Video. An RGB numpy array of shape (height, width, 3) — exactly what on_frame() delivers, needing nothing else:
Example
Or raw BGRA bytes, which need the dimensions the array would have carried:
Example
A (height, width, 4) array is taken as BGRA already and sent untouched. Pass user_data to tag the frame; it reaches the model as that frame’s metadata, dropped unless the model declared that it reads tags. See Frame Metadata for the full round-trip pattern. Audio. Interleaved 16-bit PCM, as bytes or as a numpy int16 array. samples_per_channel is worked out from the length when not given:
Example
An argument the track’s kind has no use for is refused rather than silently ignored, when ignoring it would throw away something the caller meant. user_data on an audio track raises TypeError — the wire format has no metadata trailer for audio, so a tag passed there would simply vanish. sample_rate on a video track is merely redundant and is let through.

Receiving (recvonly tracks)

on_frame()

Registers a handler for this track’s frames, converted to a NumPy array. Usable as a decorator.
Signature
Only this track’s frames reach it — every media handler in this SDK is scoped to one track. On a video track, the handler is given as many of (frame, frame_id, timestamp_us, user_data) as it declares parameters for, frame being an RGB array of shape (height, width, 3):
Example
On an audio track, as many of (frame, sample_rate, num_channels), frame being an int16 array of shape (samples, channels):
Example
Requires numpy, which is not a hard dependency of this package. Use on_raw_frame() for the same frames without the conversion. Registering before the session has declared its tracks is allowed — the direction is checked once it’s known, and a handler that turns out to be on a sendonly track simply never fires (logged once as a warning).

on_raw_frame()

Registers a handler for this track’s frames as raw bytes — no NumPy conversion, no NumPy dependency. Usable as a decorator.
Signature
The same routing as on_frame(), without the conversion — every argument the frame arrived with is passed straight through:
Example
Every argument is passed — the handler must take them all.

off_frame()

Unregisters a handler registered with on_frame() or on_raw_frame().
Signature

Pausing (recvonly tracks)

pause()

Stops receiving this track. Frames stop arriving until resume().
Signature

resume()

Starts receiving this track again after pause().
Signature

Errors

Calling a method the track’s direction doesn’t support raises ValueError with a message naming the track, its actual direction, and what to call instead. Calling push_frame() or pause()/resume() before the session has declared any tracks raises RuntimeError telling you to wait for READY or register handlers first. See Reactor.track() for when tracks become resolvable.