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, 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.numpy array of shape (height, width, 3) — exactly what
on_frame() delivers, needing nothing else:
Example
Example
(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
(frame, frame_id, timestamp_us, user_data)
as it declares parameters for, frame being an RGB array of shape (height, width, 3):
Example
(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
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.