https://github.com/webrtc-rs/webrtc
Async-friendly WebRTC implementation in Rust
https://github.com/webrtc-rs/webrtc
async rtc rust webrtc
Last synced: 23 days ago
JSON representation
Async-friendly WebRTC implementation in Rust
- Host: GitHub
- URL: https://github.com/webrtc-rs/webrtc
- Owner: webrtc-rs
- License: apache-2.0
- Created: 2019-08-15T06:18:40.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2026-10-11T18:08:45.000Z (about 1 month ago)
- Last Synced: 2026-10-16T10:08:53.992Z (26 days ago)
- Topics: async, rtc, rust, webrtc
- Language: Rust
- Homepage: https://webrtc.rs
- Size: 24.1 MB
- Stars: 5,159
- Watchers: 72
- Forks: 523
- Open Issues: 23
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
- awesome-rust-list - WebRTC.rs - rs/webrtc?style=social"/> : A pure Rust implementation of WebRTC. [webrtc.rs](https://webrtc.rs/) (Image and Video Processing)
- awesome-webrtc - WebRTC.rs - Pure Rust implementation of WebRTC stack, which rewrites Pion stack in Rust. (Miscellaneous / Tool)
README
Async-friendly WebRTC implementation in Rust
Sponsored with ๐ by
Silver Sponsors:
Bronze Sponsors:
AdrianEddy
Table of Content
- [Overview](#overview)
- [Getting Started](#getting-started)
- [The architecture](#the-architecture)
- [Release lines](#release-lines)
- [What's in 0.21](#whats-in-021)
- [How to Provide Feedback](#how-to-provide-feedback)
- [Building and Testing](#building-and-testing)
- [Semantic Versioning](#semantic-versioning)
- [Open Source License](#open-source-license)
- [Contributing](#contributing)
## 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](https://github.com/webrtc-rs/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](https://webrtc.rs/blog/2026/01/31/async-friendly-webrtc-architecture.html) for design
details and roadmap.
## Getting Started
Add the crate:
```toml
[dependencies]
webrtc = "0.21.0"
```
**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](examples/custom-runtime), 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,ignore
use std::sync::Arc;
use webrtc::peer_connection::SettingEngineBuilder;
use webrtc::peer_connection::crypto;
let setting_engine = SettingEngineBuilder::new()
.with_crypto_provider(Arc::new(crypto::providers::AwsLcRsProvider::new()))
.build();
```
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:
```rust
use std::sync::Arc;
use webrtc::peer_connection::{
PeerConnection, PeerConnectionBuilder, PeerConnectionEventHandler,
RTCConfigurationBuilder, RTCIceServer, RTCPeerConnectionIceEvent, SettingEngineBuilder, crypto,
};
use webrtc::runtime::TokioRuntime;
// 1. Implement the PeerConnectionEventHandler trait to handle events
#[derive(Clone)]
struct MyHandler;
#[async_trait::async_trait]
impl PeerConnectionEventHandler for MyHandler {
async fn on_ice_candidate(&self, event: RTCPeerConnectionIceEvent) {
println!("New local ICE candidate gathered: {}", event.candidate);
}
}
#[tokio::main]
async fn main() -> Result<(), Box> {
// 2. Configure the peer connection
let config = RTCConfigurationBuilder::default()
.with_ice_servers(vec![RTCIceServer {
urls: vec!["stun:stun.l.google.com:19302".to_owned()],
..Default::default()
}])
.build();
// 3. Choose the crypto provider (optional).
// `crypto-ring` is on by default, so this states what you would get anyway โ but the
// choice is per connection, not per process, so two connections here could use
// different providers. Enable `crypto-aws-lc-rs` for `AwsLcRsProvider`, or build with
// neither feature and pass your own `RTCCryptoProvider`.
let setting_engine = SettingEngineBuilder::default()
.with_crypto_provider(Arc::new(crypto::providers::RingProvider::new()))
.build();
// 4. Build the PeerConnection โ the background driver starts here.
// The runtime is a value, injected per connection: swap `TokioRuntime` for
// `SmolRuntime`, or for your own `Runtime` impl, and nothing else changes.
// (Omit `with_runtime` entirely and `build()` uses the compiled-in default.)
let pc = PeerConnectionBuilder::new()
.with_configuration(config)
.with_setting_engine(setting_engine)
.with_runtime(Arc::new(TokioRuntime))
.with_handler(Arc::new(MyHandler))
.with_udp_addrs(vec!["0.0.0.0:0"])
.build()
.await?;
// 5. Create an SDP offer and set it as the local description
let offer = pc.create_offer(None).await?;
pc.set_local_description(offer).await?;
Ok(())
}
```
`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 = Arc::new(pc);
```
Either way no runtime or interceptor type parameters leak into your own types.
**Next steps:** browse the [API docs](https://docs.rs/webrtc) or the
[39 runnable examples](https://github.com/webrtc-rs/webrtc/tree/master/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](examples/custom-runtime) 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`. 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](https://github.com/webrtc-rs/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`** โ currently `v0.21.0`, and the recommended choice today. The pre-1.0 line, working towards a
stable public API; see [what's in 0.21](#whats-in-021) below.
- **`v0.20.x`** โ the previous line.
While the version is `0.x`, a minor bump may carry breaking changes
(see [Semantic Versioning](#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.
โ
**A provider-neutral cryptographic API** โ every cryptographic operation in the stack now goes through the
new `rtc-crypto` crate rather than a hard-wired backend. `RTCCryptoProvider` bundles an operations trait and a
CSPRNG; `crypto-ring` (default) and `crypto-aws-lc-rs` ship built in and are **additive** (enabling both builds,
and `ring` stays the default selection). The provider is chosen **per peer connection** โ there is no
process-global provider to install โ so two connections in one process can use different ones. Build with
neither feature and supply your own for OpenSSL, a FIPS-validated module, an HSM or a platform backend;
`rtc-crypto`'s conformance suite validates it against the same RFC vectors the built-ins pass. No cryptography
happens in this crate; a CI check enforces that.
โ
**Faster per-packet crypto** โ key schedules moved off the per-packet path. SRTP keys its MACs once per
context (~40% faster per RTCP packet), DTLS CBC keys its record MACs at epoch setup (~4-7% faster), AES-CTR is
batched (~9-10% faster on a 1200-byte payload), and the built-in CSPRNG no longer reads the OS per record โ
DTLS GCM record encryption went from 1.015 ยตs to 262 ns. Every per-packet benchmark is at parity with or
faster than the pre-migration baseline.
โ
**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.
โ
**Faster and more correct SCTP setup** โ the initial T1-init/T1-cookie RTO is now the 1 s that RFC 9260 ยง16
recommends, rather than the 3 s inherited from RFC 4960, so a lost first handshake packet is retried a second
in rather than three. And an inbound `DATA_CHANNEL_OPEN` that arrives before the local association reaches
`Established` is now held until it does, instead of announcing a channel as open over a transport that would
reject the first thing written to it.
โ ๏ธ **One behaviour change to note** โ `MediaEngine::register_default_codecs` no longer registers
`video/ulpfec`, because the receive path does not recover media from ULPFEC packets. This changes the default
offer's SDP: payload type 116 is gone. Register it explicitly with `MIME_TYPE_ULP_FEC` if you need it.
Track the remaining work in [The path to webrtc 1.0](https://github.com/webrtc-rs/webrtc/issues/836).
### How to Provide Feedback
We welcome your input as `v0.20.x` and `v0.21.x` grow:
- Review the [architecture blog post](https://webrtc.rs/blog/2026/01/31/async-friendly-webrtc-architecture.html)
- Join discussions on [GitHub Issues](https://github.com/webrtc-rs/webrtc/issues)
- Chat with us on [Discord](https://discord.gg/4Ju8UHdXMs)
**New projects:** start on `v0.21.0`.
**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](https://semver.org/):
- **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.1` โ `1.0.0-beta.1` โ `1.0.0-rc.1` โ `1.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 ). 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!!!