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:
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.Properties
name
Signature
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
None until then, and renegotiated across a
reconnect.
paused
Signature
recvonly track is resumed
automatically on reconnect, so this always reflects the current state rather than a cached one.
published
Signature
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
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. Each tab shows the parameters relevant to that frame type.
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
- Audio
Signature
An RGB
numpy array of shape (height, width, 3) — exactly what
on_frame() delivers, needing nothing else:Example
Example
width * height * 4 bytes. A length that disagrees raises ValueError, because the
native side reads the dimensions and not the length.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.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.set_bitrate()
Bounds what this sender may spend, in bits per second. Needs a connected client, and raises on a
recvonly track.
Signature
This is the ceiling that caps a video encoder. With nothing set, WebRTC derives the maximum from the
frame size alone. That maximum is 2.5 Mbps for anything above 960x540, so 720p, 1080p and 4K all cap
there. A ceiling is permission rather than a target: the encoder still spends only what the
congestion controller allows.
Example
Reactor.set_bitrate() bounds the whole connection.
The two apply together, so the lower of them wins.
Receiving (recvonly tracks)
on_frame()
Registers a handler for this track’s frames, converted to a NumPy array. Usable as a decorator.
Only this track’s frames reach it — every media handler in this SDK is scoped to one track.
- Video
- Audio
Signature
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
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
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 raisesValueError 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.