> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To build and serve your own model, start at /deploy/development/quickstart and /deploy/development/overview. Deploying is the default path: reactor init scaffolds a workspace, reactor auth login authenticates, and reactor model deploy registers the model, publishes the release with the weights/ folder, and activates it on Reactor's GPUs, in one command from that workspace. Docker must be running, because the publish step builds the image locally. Bump model.version in reactor.yaml before redeploying a change, because a release that already has an image is reactivated as it is. Deployment access is granted per account, so contact team@reactor.inc if a deploy is refused. Every key in reactor.yaml is documented at /deploy/platform/reactor-yaml. Model code imports reactor_runtime; Python client code imports reactor_sdk. The runtime overview explains the model interface. Running the model on your own machine with reactor run is optional and needs a GPU you attach with --gpus; /deploy/development/local-testing covers that loop and pairs a complete brightness model with a Python client test in a separate brightness-test workspace.
> Reactor hosts multiple models, each with its own connect slug (modelName) and command/event schema. The video model catalog — slug, typed SDK package, and links to its schema — is at /model-api-reference/overview. Robotics policy documentation starts at /robotics/overview; X-WAM observations, actions, and client integration are under /robotics/xwam/; Cosmos3 Nano Policy DROID is under /robotics/cosmos/nano-policy-droid/. Some models expose one slug per experience (e.g. HappyOyster); always take the slug from the model's own pages, never guess it.
> Fastest path to a working app: `npx create-reactor-app my-app --model=<slug>` scaffolds a complete app with secure auth wired up. Typed TypeScript SDKs are published as @reactor-models/<model>; Python uses the base reactor-sdk package.
> Auth: exchange an API key (rk_...) for a JWT via POST https://api.reactor.inc/tokens from your server. Never put the API key in client-side code.
> Append .md to any docs URL for clean Markdown. Search these docs via the MCP server at https://docs.reactor.inc/mcp.

# Track

> Receive and send named video and audio tracks with the Android SDK

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 [API reference](https://javadoc.io/doc/inc.reactor/reactor-sdk-android/1.1.0) lists every
operation.

The snippets below assume a connected `Reactor reactor` and imports from `inc.reactor.sdk.android`.

## Find a track

Look up a known name after connecting, or filter the declared tracks:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
val video = reactor.track("main_video")
val output = reactor.tracks
    .withKind(TrackKind.VIDEO)
    .withDirection(TrackDirection.RECVONLY)
    .one()
```

`tracks` is empty before the session declares its tracks and after disconnecting. It is a
`List<Track>`, so you can iterate or filter it like any list. `track(name)` throws
`InvalidStateException` if that name has not been declared, including before connecting, and the
message lists the names that were. `one()` requires exactly one match.

A track exposes `name`, `kind`, `direction`, and an optional `mid`.

## Receive frames

Register a handler on a receiving track. Name the parameter type: `onFrame` is overloaded for video
and audio, so an untyped lambda is ambiguous.

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
reactor.track("main_video").onFrame { frame: VideoFrame ->
    Log.i(TAG, "${frame.width}x${frame.height}")
}
```

Call `removeHandlers()` on the track when you no longer need it. Registering a video handler on an
audio track, or any frame handler on a sending track, throws `ReactorException`.

| Frame | Data | Metadata |
| - | - | - |
| `VideoFrame` | `pixels`: read-only BGRA `ByteBuffer`, `width * height * 4` bytes | `width`, `height`, `frameId`, `timestampUs`, `userData` |
| `AudioFrame` | `samples`: interleaved signed 16-bit PCM `ShortBuffer` | `sampleCount`, `sampleRate`, `channels` |

> [!WARNING]
>
> Frame memory is valid only while the handler runs. The native layer frees it as soon as the
> handler returns. To keep pixels or samples, call `VideoFrame.copyPixels()` or
> `AudioFrame.copySamples()` inside the handler.

Frame handlers run inline on the SDK's media delivery thread, not on the control-event dispatcher.
Blocking there is the backpressure: while a video handler runs, only the newest frame is kept, so a
slow handler drops frames instead of building a queue. Convert or draw the frame inside the handler,
and post only the result to the UI. `VideoRenderer` in the
[media module](https://docs.reactor.inc/sdk-reference/android/installation.md#optional-module) draws frames on a `SurfaceView`.

A video's `timestampUs` uses the sender's clock, so compare it only with timestamps from that same
sender. `userData` is a copy of the sender's tag and can outlive the handler.

## Send frames

Choose a sending track and call `publish()` before pushing data. Track names depend on the model;
this example finds its single video input:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
val input = reactor.tracks
    .withKind(TrackKind.VIDEO)
    .withDirection(TrackDirection.SENDONLY)
    .one()
input.publish()

val width = 640
val height = 480
val bgra = ByteBuffer.allocateDirect(width * height * 4)
// Fill bgra with B, G, R, A bytes from your source.
input.pushFrame(bgra, width, height)
input.unpublish()
```

Video input must be a **direct** `ByteBuffer` with exactly `width * height * 4` bytes remaining, in
B, G, R, A order. An optional `userData` tag follows the dimensions. `CameraCapture` in the
[media module](https://docs.reactor.inc/sdk-reference/android/installation.md#optional-module) produces frames in this format.

For audio, call `pushFrame(pcm, samplesPerChannel, sampleRate, channels)` on a published audio
input. `pcm` is a direct `ByteBuffer` of interleaved signed 16-bit samples, with exactly
`samplesPerChannel * channels * 2` bytes remaining. Supported rates are 8,000, 16,000, 24,000,
32,000, 44,100, and 48,000 Hz, with one or two channels.

`published` reports whether a sender is ready. `publishState` is `UNPUBLISHED`, `PUBLISHING`, or
`PUBLISHED`. Pushing before publication completes, after unpublishing, to an undeclared track, or to
a receiving track throws, naming the fix. Publication does not survive reconnect: call `publish()`
again before sending more frames.

## Pause and resume

`pause()` and `resume()` are `suspend` functions on a receiving track; `paused` reports the current
state. Nothing is generated for a paused track. On a sending track, `publish()` and `unpublish()`
start and stop sending instead.

The base SDK does not capture or play device media. The optional
[`reactor-sdk-android-media`](https://docs.reactor.inc/sdk-reference/android/installation#optional-module) module adds
camera, microphone, speaker, and rendering helpers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.