webrtc

Async-friendly WebRTC implementation in Rust

5,105 stars Rust #async#rtc#rust#webrtc
RAW Doc

[](https://webrtc.rs)

Overview

WebRTC.rs is an async-friendly WebRTC implementation in Rust, originally inspired by and largely rewriting the Pion
stack. The async webrtc crate is a clean, ergonomic, runtime-agnostic rewrite on top of a Sans-I/O core; it ships with
Tokio and smol runtime backends, and any other runtime can be plugged in by implementing one trait.

Architecture:

  • rtc: Sans-I/O protocol core with complete WebRTC stack (95%+ W3C API
    compliance)
  • webrtc (this crate): a thin async layer over rtc:
    • PeerConnection — the user-facing async API handle; all operations (create offers/answers, add tracks,
      create data channels) are async
    • PeerConnectionDriver — an internal background event loop, spawned automatically, that owns the sockets,
      drives the Sans-I/O rtc core, handles timeouts, and dispatches events
    • Runtime — a trait abstracting timers, task spawning, and sockets, so the crate is runtime-agnostic

📖 Learn more: Read
our architecture blog post for design
details and roadmap.

Getting Started

Trying the 0.21 pre-release? Cargo does not select pre-release versions from a plain
"0.21" requirement, so name it in full:

toml
[dependencies]
webrtc = "0.21.0-alpha.1"

Feature flags:

Feature Default Description
runtime-tokio Timers, task spawning and sockets via Tokio
runtime-smol The same, via smol
runtime-mock MockRuntime, a deterministic virtual-clock runtime for tests (no I/O)
crypto-ring The ring-based crypto provider
crypto-aws-lc-rs The aws-lc-rs-based crypto provider

The runtime features are additive: each one only makes a built-in runtime available, so enabling several is safe
and a single process can drive different connections on different runtimes.

The crypto features work the same way. Enabling both compiles both providers, and ring stays the default selection —
so a dependency that turns on crypto-aws-lc-rs cannot silently change which one your application runs. Building with
neither compiles no provider, and you supply your own.

Bringing your own runtime. The built-ins are not privileged — implement webrtc::runtime::Runtime and pass it per
connection with with_runtime, with no #[cfg] edits and no fork. See
the custom-runtime example, which runs the full stack on async-executor + async-io with
--no-default-features (neither Tokio nor smol compiled in).

Choosing a crypto provider. Same story: pass one per connection through SettingEngine, which also means two
connections in one process can use different providers.

rust
use std::sync::Arc;
use webrtc::peer_connection::crypto;
use webrtc::peer_connection::SettingEngineBuilder;

let setting_engine = SettingEngineBuilder::new().with_crypto_provider(Arc::new(crypto::providers::AwsLcRsProvider::new()));

Applications needing a FIPS-validated module, an HSM, or a platform backend implement
crypto::RTCCryptoProvider and pass it the same way; rtc-crypto's conformance suite validates
an implementation against the same RFC vectors the built-ins pass. No cryptography happens in this
crate — it forwards the provider to rtc.

Build a peer connection and create an offer:

text
/* Detailed source-code truncated for AI context efficiency. */

build() returns an opaque impl PeerConnection. PeerConnection is an object-safe trait, so when you need to store
the connection in a struct or share it across tasks, wrap it:

rust,ignore
let pc: Arc<dyn PeerConnection> = Arc::new(pc);

Either way no runtime or interceptor type parameters leak into your own types.

Next steps: browse the API docs or the
37 runnable examples — data channels, media playback,
simulcast, ICE restart, insertable streams, and more.

The architecture

Runtime independence

  • Runtime-agnostic via a Quinn-style Runtime abstraction (timers, task spawning, sockets, DNS)
  • Feature flags: runtime-tokio (default) and runtime-smol, additive rather than mutually exclusive
  • Any third-party runtime works today: implement Runtime, inject it per connection with with_runtime.
    The custom-runtime example does exactly that on async-executor + async-io, with neither
    built-in runtime compiled in
  • runtime-mock gives tests a deterministic virtual clock, so timing-dependent behaviour is testable instantly and
    without sockets

Clean event handling

  • One trait-based event handler (PeerConnectionEventHandler) for every event, with default no-op methods so you
    implement only what you need — no per-event callback registration
  • Centralized state: the handler is one shared Arc<MyHandler>. Methods take &self, so mutable handler state
    goes behind a single lock rather than being captured per closure

Sans-I/O foundation

  • Protocol logic completely separate from I/O (via the rtc core)
  • Deterministic testing without real network I/O
  • A thin async driver (PeerConnection handle + background PeerConnectionDriver) over the core

Release lines

  • v0.21.x — in development, currently v0.21.0-alpha.1. The pre-1.0 line, working towards a stable public
    API; see what's in 0.21 below. APIs may change between alphas.
  • v0.20.x — the current stable line, and the recommended choice for production today.

While the version is 0.x, a minor bump may carry breaking changes
(see Semantic Versioning). Once 1.0 ships, that stops being true — which is the whole
point of the 0.21 work.

What's in 0.21

v0.21 is the run-up to 1.0, whose goal is a stable public API — not every feature, but an API that will
not break under you. Work so far:

Extensibility, before the freeze — public enums are #[non_exhaustive] and the traits this crate alone
implements are sealed, so variants and methods can be added later without a major bump. This cannot be done
after 1.0, which is why it came first.

Crypto provider selectioncrypto-ring (default) and crypto-aws-lc-rs features, chosen per peer
connection, so two connections in one process can use different providers. Build with neither and supply your
own. No cryptography happens in this crate; a CI check enforces that.

Deterministic time — the Sans-I/O core no longer reads a clock. Time is an input, threaded from
Runtime::now(), so runtime-mock's virtual clock genuinely drives ICE timeouts, DTLS retransmits and SCTP
RTO. Advancing a mock clock by 30 s now produces real protocol transitions instead of nothing.

No silent drops on data channels — a slow consumer used to lose messages on a reliable channel once
the internal hand-off queue filled. The driver now keeps them and stops pulling from the core, so back-pressure
reaches SCTP's receive window and the peer is throttled instead. Media is unaffected by a stalled data channel.

Track the remaining work in The path to webrtc 1.0.

How to Provide Feedback

We welcome your input as v0.20.x and v0.21.x grow:

New projects: start on v0.20, or on v0.21.0-alpha.1 if you want the pre-1.0 API and can absorb changes
between alphas.
Hit a gap? Open an issue — reports of what is missing directly shape what we prioritise before 1.0.

Building and Testing

bash
# Update rtc submodule first
git submodule update --init --recursive

# Build the library
cargo build

# Run tests
cargo test

# Build documentation
cargo doc --open

# Run examples
cargo run --example data-channels

Semantic Versioning

This project follows Semantic Versioning:

  • Patch (0.x.Y): Bug fixes and internal improvements with no public API changes.
  • Minor (0.X.0): Backwards-compatible additions or deprecations to the public API.
  • Major (X.0.0): Breaking changes to the public API.

While the version is 0.x, the minor version acts as the major — i.e., a minor bump may include breaking changes. Once
1.0.0 is released, full semver stability guarantees apply.

Pre-release versions are published with the following suffixes, in order of increasing stability:

  • -alpha.N: Early preview. API is unstable and may change significantly.
  • -beta.N: Feature-complete for the release. API may still have minor changes.
  • -rc.N: Release candidate. No further API changes are expected unless critical issues are found.

For example: 1.0.0-alpha.11.0.0-beta.11.0.0-rc.11.0.0.

Open Source License

Dual licensing under both MIT and Apache-2.0 is the currently accepted standard by the Rust language community and has
been used for both the compiler and many public libraries since (
see https://doc.rust-lang.org/1.6.0/complement-project-faq.html#why-dual-mitasl2-license). In order to match the
community standards, webrtc-rs is using the dual MIT+Apache-2.0 license.

Contributing

Contributors or Pull Requests are Welcome!!!