Track is a handle onto one named media slot the model declared — not something you construct
yourself. Ask for it by name with reactor.track(name),
or find it by filtering reactor.tracks when you don’t know the name:
pushFrame sends, onFrame receives, one name for video and audio alike — the track
already knows its kind.
A handle, not an owner: it holds the client weakly, so a track parked in a view model cannot
keep the session alive for its lifetime. Registering a handler after the
Reactor that owns it has
been released throws ReactorError.invalidState rather than silently never firing.Properties
name
Signature
kind
Signature
.video or .audio, or nil before the session has declared its tracks.
direction
Signature
.sendonly or .recvonly, or nil before the session has declared them. .sendonly is from this
client’s point of view: this client sends, the model receives.
mid
Signature
paused
Signature
true would go on
claiming otherwise.
published
Signature
It is cleared whenever the status leaves
.ready. A reconnect resumes recvonly tracks and
nothing else, so a slot published before one is not published after it — publish again. See
Reactor/reconnect().Sending
publish()
Activates this sendonly slot, so the model has something to receive on.
Signature
.ready.
Example
unpublish()
Deactivates the slot. Synchronous, unlike the other track methods — there is no round trip, only a
local state change and a fire-and-forget notification.
Signature
pushFrame(_:)
Pushes a frame into this sendonly track. The overload — and what else is needed — follows from the
track’s kind. Each tab shows the signature for that track kind.
- Video
- Audio
For
TrackKind.video.Signature
A second overload takes an
UnsafeRawBufferPointer for a caller who already holds the pixels as a
buffer and wants to push without copying:Signature
Example
ReactorError.invalidState on a recvonly track, before publish(), on a
session that has left .ready, or on an overload that does not match this track’s kind (BGRA into
an audio track, or PCM into a video one); ReactorError.badRequest on a BGRA buffer whose length
does not match the dimensions, or PCM whose sample count does not divide by channels.
Receiving
onFrame(_:)
Receives decoded frames from this track, copied so they can be kept. kind determines the
frame type the handler takes.
- Video
- Audio
For
TrackKind.video.Signature
Example
AudioFrame). Blocking here is the backpressure. Handing frames to a queue instead
trades a bounded drop for unbounded latency and memory.
Throws ReactorError.invalidState when this track sends rather than receives, or when the frame
type does not match this track’s kind — both would otherwise be a handler that never fires.
onRawFrame(_:)
Receives frames without copying them.
Signature
onFrame(_:) — but the buffers belong to the library
and are gone when the handler returns. For a renderer that uploads straight to a texture, this is
the version that copies nothing it does not need to.
VideoFrame
Definition
String
The track this arrived on. Every recvonly video track decodes into one callback, so on a session
with several this is what tells them apart.
Data
BGRA pixels — blue, green, red, alpha —
width * height * 4 bytes.UInt64
The sender’s frame counter, or 0 when the frame carried no metadata trailer.
UInt64
When the sender says it captured this frame, in microseconds on the sender’s own clock. 0 when
the frame carried no trailer. Differences between stamps from one sender are what this supports; it
is not comparable with a local clock.
Data?
The bytes the sender tagged this frame with, if any.
nil when the frame carried no trailer, and
also when the far end never declared that it writes tags — no published model attaches one today.onRawFrame(_:) hands the same fields, borrowed, as RawVideoFrame:
Definition
AudioFrame
Definition
samples.count total across all channels. Copied, unlike video — audio
arrives in short buffers, roughly 10 ms each, and the queue behind it keeps its backlog rather than
dropping, because there the queue is the jitter buffer and a hole in it is audible.
Pausing
pause()
Signature
resume()
Signature
TrackList
The tracks a session declares, in declaration order, filterable — for discovery, and for a caller
who would rather not hardcode a name. Returned by reactor.tracks.
Signature
Example
one() throws ReactorError.notFound when the list is empty and ReactorError.conflict when it
holds more than one — a filter that matched several and a caller that wanted one is a question with
no answer, and picking the first would answer it wrongly and silently.
TrackKind and TrackDirection
Definition
String-backed, so TrackKind(rawValue: "video") and .video.rawValue round-trip through the wire
spelling directly.