Skip to main content
A Track is a named media stream declared by the model. Direction is from the client’s perspective: RECVONLY receives from the model, and SENDONLY sends to it. TrackKind is VIDEO or AUDIO. The Track Javadoc lists every operation. The snippets below assume a connected Reactor reactor and imports from inc.reactor.sdk.

Find a track

Look up a known name after connecting, or filter the declared tracks:
tracks() is empty before the session declares its tracks and after disconnecting. track(name) throws if that name has not been declared, including before connecting. TrackList.byName(name) instead returns an Optional<Track>. one() requires exactly one match and throws if there are zero or multiple matches. You can also iterate, call asList(), or use stream(). A track exposes name(), kind(), direction(), and an optional mid().

Receive frames

Register a handler on a receiving track. Specify the lambda parameter type: onFrame is overloaded for video and audio, so an untyped lambda is ambiguous.
Call frames.close() when you no longer need the handler. Registering a video handler on an audio track, or any frame handler on a sending track, throws ReactorException.
Frame memory is valid only while the callback runs. To keep pixels or samples, call VideoFrame.toByteArray() or AudioFrame.toShortArray() inside the callback. Reading the memory after the callback returns throws IllegalStateException.
Frame handlers run on a media delivery thread, independently of the control-event dispatcher. Copy media before handing it to another thread or a UI. Keep any application queue bounded: a slow video handler causes intermediate frames to be dropped in favor of the newest one. A video’s timestampUs() uses the sender’s clock, so compare it only with timestamps from that same sender. It is not a local wall-clock time. userData() returns an optional copy of the sender’s tag that can outlive the callback.

Send frames

Choose a sending track and await publish() before pushing data. Track names depend on the model; this example finds its single video input:
Video input must contain exactly width * height * 4 bytes in B, G, R, A order. An overload accepts userData and captureTimeUs after the dimensions. Use reactor.timeMicros() for an explicit capture time, or null for now. For audio, call pushFrame(short[] pcm, int sampleRate, int channels) on a published audio input. Samples are interleaved signed 16-bit PCM. Supported rates are 8,000, 16,000, 24,000, 32,000, 44,100, and 48,000 Hz, with one or two channels. The sample count must divide evenly by the channel count. isPublished() reports whether a sender is ready. publishState() returns UNPUBLISHED, PUBLISHING, or PUBLISHED. Pushing before publication completes, after unpublishing, or to a receiving track throws. Publication does not survive reconnect: await publish() again before sending more frames.

Pause and bitrate

pause() and resume() return CompletableFuture<Void>. isPaused() reports the current state. setBitrate(minBps, maxBps) sets track bitrate bounds in bits per second; negative values leave the corresponding bound unset. The base SDK does not capture or play device audio. Add the optional inc.reactor:reactor-sdk-audio module for microphone and speaker helpers; see Installation.