Expand description
§🦀 Official Rust SDK for Stream Video (Preview)
The getstream crate is Stream’s server-side Rust SDK for building rich video
applications and agents. It combines the Stream Video REST API with an SFU
WebRTC participant: manage users and calls, join calls from your backend, read
remote audio and video, transform it, and publish media back into the call.
§Quick links
- Stream Video
- Create a Stream account
- Stream Dashboard
- Video API documentation
- Rust API reference (docs.rs)
- Examples
- Contributing
§Features
- Create and manage Stream users and user authentication tokens.
- Create, query, update, end, and delete video calls.
- Manage call members, permissions, recording, transcription, captions, livestreaming, custom events, and reactions.
- Join a call as a server-side SFU participant with retry, reconnect, and migration handling.
- Subscribe globally or by participant session to remote audio, video, and screen-share tracks.
- Observe typed participant, connection-quality, pin, grant, and inbound-pause state from the SFU.
- Read Opus audio as PCM, decode VP8/VP9/H264 video as I420, or work with raw RTP packets.
- Resample and rechannel PCM, convert it to 32-bit float, raw bytes, WAV, or G.711, and slice it into chunks and sliding windows.
- Transform and republish audio or video through local tracks.
- Temporarily mute publications, publish screen-share audio, configure local video bitrate, and control SFU-side noise cancellation.
- Emit structured, secret-redacted diagnostics through
tracing.
§Crate map
crates.io has no organization namespace. This package is Stream’s official
Rust crate, published as getstream.
| Item | Role |
|---|---|
Stream | Server client: users, tokens, and webhook verification |
Call / VideoClient | Video REST and Call::join |
rtc | SFU participant, local/remote tracks, and PCM utilities |
models | REST request and response types |
ClientConfig | HTTP timeouts, retries, and payload limits |
webhook | Signature verification and typed events |
The wire-level rtc transport modules (proto, peer, sfu_ws, signal,
publisher, tracer, coordinator_ws) are public because they track Stream’s
SFU protocol, but they are exempt from compatibility guarantees.
§Requirements
The WebRTC media stack is part of every build. Install Rust 1.88 or newer, a C
compiler, CMake, pkg-config, and libvpx before building:
# macOS
brew install libvpx cmake pkg-config
# Debian / Ubuntu
sudo apt install libvpx-dev cmake pkg-config build-essentialprotoc, a system libopus package, and external media programs such as
ffmpeg are not required.
§Installation
cargo add getstream@0.1.0-preview.2 tokio tracingOr in Cargo.toml:
[dependencies]
getstream = "0.1.0-preview.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
tracing = "0.1"getstream = "0.1" will not match this preview. Cargo only selects a pre-release
when the version requirement includes one.
The API reference is published at docs.rs/getstream;
from a checkout, generate it locally with cargo doc --open.
This is a 0.x preview, so minor releases may contain breaking changes. The
wire-level rtc transport modules (proto, peer, sfu_ws, signal,
publisher, tracer, coordinator_ws) track Stream’s SFU protocol directly and
are exempt from compatibility guarantees at any version bump.
§Getting started
Set STREAM_API_KEY and STREAM_API_SECRET in the server environment. Never
ship the API secret to a browser or mobile client.
The following example creates a user and token, creates a call, joins it as a backend participant, sends a reaction, and leaves cleanly:
use getstream::models::{
CallRequest, GetOrCreateCallRequest, MemberRequest, SendVideoReactionRequest,
UserRequest,
};
use getstream::rtc::JoinCallData;
use getstream::Stream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Stream::from_env()?;
let user_id = "video-agent";
client
.upsert_users([UserRequest::new(user_id)])
.await?;
// Return this token to a trusted client that connects as `user_id`.
// The server-side SDK keeps STREAM_API_SECRET on the backend.
let _user_token = client.create_token(user_id)?;
let call = client.video().call("default", "agent-demo");
call.get_or_create(GetOrCreateCallRequest {
data: Some(CallRequest {
created_by_id: Some(user_id.to_owned()),
members: Some(vec![MemberRequest::new(user_id)]),
..Default::default()
}),
..Default::default()
})
.await?;
call.join(JoinCallData::new(user_id)).await?;
call.send_reaction(SendVideoReactionRequest {
reaction_type: "raise-hand".to_owned(),
emoji_code: Some("âś‹".to_owned()),
..Default::default()
})
.await?;
call.leave().await?;
Ok(())
}Call::join authenticates the backend participant internally. The token from
create_token is for a user connecting through another trusted client.
§Accessing and transforming media tracks
Remote tracks expose participant and codec metadata as well as decoded and raw
media. The example below subscribes to audio and video, doubles PCM amplitude,
downscales video to a 512-pixel longest edge, and republishes both streams. A
bounded channel absorbs short track-event bursts, and a JoinSet owns every
media task so shutdown can cancel and reap them.
use std::time::Duration;
use getstream::rtc::proto::models::TrackType;
use getstream::rtc::{
LocalAudioTrack, LocalVideoTrack, RemoteTrack, RtcResult, SubscriptionConfig,
};
use getstream::Call;
use tokio::sync::mpsc;
use tokio::task::JoinSet;
async fn transform_track(
track: RemoteTrack,
outbound_audio: LocalAudioTrack,
outbound_video: LocalVideoTrack,
) -> RtcResult<()> {
match track.track_type() {
TrackType::Audio | TrackType::ScreenShareAudio => {
while let Some(mut frame) = track.next_pcm().await {
for sample in &mut frame.samples {
*sample = sample.saturating_mul(2);
}
outbound_audio.write_pcm(frame).await?;
}
}
TrackType::Video | TrackType::ScreenShare => {
while let Some(frame) = track.next_video_frame().await {
let frame = frame.downscale_to_fit(512);
outbound_video
.write_i420(
&frame.data,
frame.width,
frame.height,
Duration::from_millis(33),
)
.await?;
}
}
TrackType::Unspecified => {}
}
Ok(())
}
async fn run_media_bridge(call: &Call) -> Result<(), Box<dyn std::error::Error>> {
call.update_subscriptions(SubscriptionConfig::audio_video())
.await?;
let outbound_audio = LocalAudioTrack::opus()?;
let outbound_video = LocalVideoTrack::vp9()?;
call.publish_audio(outbound_audio.clone()).await?;
call.publish_video(outbound_video.clone()).await?;
let (track_tx, mut track_rx) = mpsc::channel(16);
call.on_track(move |track| {
if track_tx.try_send(track).is_err() {
tracing::warn!("dropping track event because the media queue is full");
}
});
let mut track_tasks = JoinSet::new();
loop {
tokio::select! {
Some(track) = track_rx.recv() => {
let outbound_audio = outbound_audio.clone();
let outbound_video = outbound_video.clone();
track_tasks.spawn(transform_track(track, outbound_audio, outbound_video));
}
Some(result) = track_tasks.join_next(), if !track_tasks.is_empty() => {
result??;
}
_ = tokio::signal::ctrl_c() => break,
}
}
track_tasks.abort_all();
while track_tasks.join_next().await.is_some() {}
call.leave().await?;
Ok(())
}For a complete bridge with cancellation, barge-in, audio and video processing,
and deterministic cleanup, see gpt_realtime_bot.
For selective agents, use Call::update_subscription_targets with
SubscriptionTarget values instead of subscribing to every participant. A
temporary mute_track / unmute_track preserves the same local track and
sender; stop_publish remains terminal for that local track handle. The latest
SFU view is available synchronously through Call::call_state.
Layered publishing is opt-in. LocalVideoTrack::vp9_svc() provides camera SVC
with up to three spatial and temporal layers on one SSRC.
LocalVideoTrack::h264_simulcast() supports camera video and
LocalVideoTrack::vp8_simulcast() supports screen share with a q/h/f RID
ladder on one m-line. All three follow SFU quality updates. Feed raw I420 at the
full resolution announced by the SFU publish option; a mismatch is rejected
instead of advertising dimensions that are not sent. Pre-encoded samples and
forwarded RTP remain single-layer-only.
Tracks carry 48 kHz signed 16-bit PCM, which is rarely what a model or telephony
API wants. The rtc::pcm
module converts the rate and channel count, converts to 32-bit float, raw bytes,
WAV, or G.711, and slices audio into chunks and sliding windows.
§Examples
From the SDK checkout, create your local environment file and add credentials:
cp .env.example .env| Variable | Required by | Purpose |
|---|---|---|
STREAM_API_KEY | Both examples | Public Stream application key |
STREAM_API_SECRET | Both examples | Server-only Stream application secret |
OPENAI_API_KEY | gpt_realtime_bot | Server-side OpenAI Realtime authentication |
OPENAI_REALTIME_MODEL | gpt_realtime_bot | Realtime model; defaults to gpt-realtime |
EXAMPLE_BASE_URL | gpt_realtime_bot only | Base URL used for its printed browser join link |
Join a call as a backend participant:
cargo run --example join_callRun the Stream-to-OpenAI Realtime audio/video bridge:
cargo run --example gpt_realtime_botBoth examples load .env for local development. Never commit that file or
print the Stream API secret or OpenAI API key.
§Logging, retries, and security
The library emits structured events through
tracing and never installs a global subscriber.
Known secret fields are redacted, and HTTP bodies are excluded by default.
Applications must opt in to body logging carefully because custom payloads can
still contain sensitive information.
HTTP retries are opt-in and limited to idempotent reads on rate limits or transport failures. SFU joins use Stream’s reconnect and migration state machine rather than HTTP retry behavior.
Webhook verification must receive the exact raw request body and the
X-Signature header. Verify before parsing or deduplicating the event:
let event = client.parse_webhook(raw_body, x_signature)?;
tracing::info!(event_type = event.event_type(), "verified Stream webhook");H264 can be subject to patent obligations in some jurisdictions. Applications that distribute H264 functionality must assess their own requirements.
§Contributing
Contributions are welcome. See CONTRIBUTING.md for local setup, tests, benchmarks, and the checks to run before opening a pull request.
§License
Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
Licensed under the Stream License. You may not use this software except in compliance with that license. The software is distributed on an “AS IS” basis, without warranties or conditions of any kind.
Modules§
- client
- HTTP transport, JWT auth, opt-in retry, and tracing for the Stream API.
- error
- Typed errors for the Stream SDK.
- models
- Serde request/response models, hand-ported from getstream-go
models.go. - rtc
- SFU WebRTC participant support. Always compiled — there is no
webrtcCargo feature. - token
- JWT minting and verification (HS256), matching getstream-go’s token claims.
- video
- Video coordinator REST:
VideoClientandCall. - webhook
- Webhook signature verification (HMAC-SHA256) and typed event parsing.
Structs§
- ApiError
- The coordinator error envelope (
ErrorFromResponse). - Call
- A handle to a specific call (
<type>:<id>). Cheap to construct; no request is made until a method is invoked. Obtain viacrate::video::VideoClient::call. - Client
Config - Tunables for the HTTP client. Use
ClientConfig::defaultfor getstream-go defaults and override fields as needed before constructing acrate::Stream. - Network
Limits - Limits for payloads buffered by the HTTP and WebSocket transports.
- Retry
Config - Opt-in auto-retry policy. Disabled by default: exactly one attempt, errors
surface unchanged. When enabled, only
GET/HEADrequests failing with HTTP 429 or a transport error are retried, and never when the API marked the errorunrecoverable. Mirrors getstream-go’sWithRetry. - Stream
- The server-side Stream client. Holds the API key + secret and mints the server JWT used to authenticate REST calls.
- Token
Claims - Decoded user-token claims.
- Token
Options - Options for
crate::Stream::create_token_with. - Video
Client - Entry point for video endpoints. Obtain via
crate::Stream::video.
Enums§
- Error
- Top-level SDK error.
- Token
Error - JWT minting, parsing, verification, and operational-validation failures.
- Webhook
Error - Webhook verification / parsing failures.
- Webhook
Event - A parsed webhook event. Known video/call events are typed; anything else is
WebhookEvent::Unknownwith the raw payload preserved (forward-compat).
Constants§
- DEFAULT_
BASE_ URL - Default coordinator base URL (matches getstream-go’s
DefaultBaseURL). - ENV_
API_ KEY - Environment variable holding the Stream API key.
- ENV_
API_ SECRET - Environment variable holding the Stream API secret.
Functions§
- parse_
event - Parse a webhook payload into a typed
WebhookEventwithout verifying it. - verify_
signature - Verify a webhook
signature(hex HMAC-SHA256) against the exact rawbodyusingsecret. Comparison is constant-time for valid signature lengths.
Type Aliases§
- Result
- Convenience alias for SDK results.