Skip to main content
A 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:
One type covers both directions and both kinds, because the operations are the same operations either way: 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.
Calling a method the track’s kind or direction does not allow throws, on purpose: pushFrame on a recvonly track, onFrame on a sendonly one. Each of those would otherwise reach the native layer, find nothing to do, and return — so a caller pushing at 30fps would see a model receiving nothing and no reason why.

Properties

name

Signature
The declared name. Never changes.

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
The SDP media id, once the track has been received. Read from the session rather than cached: it is reported as tracks arrive and is renegotiated on a reconnect.

paused

Signature
Whether this track is paused right now. Read from the session, not cached, so it stays right across a reconnect — recvonly tracks resume automatically once connected, and a cached true would go on claiming otherwise.

published

Signature
Whether this sendonly slot is activated. Kept by the SDK rather than read back, because the session does not record it: publish is a request and unpublish a notification, and neither leaves anything to query.
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
Publishing is what puts a sender behind the slot: pushing before it would drop the frame, and this SDK refuses rather than letting it. Throws on a recvonly track, and on a session that is not .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
Throws when the notification could not be made; the track then stays published, so a retry is possible.

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.
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
Throws 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.
For TrackKind.video.
Signature
Example
Runs inline on the library’s delivery thread, deliberately: while it runs, the library keeps only the newest video frame and drops what arrives in between — audio is queued instead (see 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.
Hold the returned Subscription. It cancels — and stops delivering — as soon as nothing references it.

onRawFrame(_:)

Receives frames without copying them.
Signature
Same thread, same backpressure as 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
RawVideoFrame’s buffers are borrowed for the duration of the handler and no longer. Copy anything you keep — retaining a pointer here is a use-after-free that reproduces under load and not in tests.

AudioFrame

Definition
Interleaved 16-bit PCM, 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
Stops this track. Nothing is generated while paused, which on a video track is visible only as a frozen frame.

resume()

Signature
Starts it again.

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
Filters chain in either order, and it iterates like an array:
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.