rtc 0.5.1

Sans-I/O WebRTC implementation in Rust
Documentation

Overview

RTC is a pure Rust implementation of WebRTC using a sans-I/O architecture. Unlike traditional WebRTC libraries, RTC separates protocol logic from I/O operations, giving you complete control over networking, threading, and async runtime integration.

What is Sans-I/O?

Sans-I/O (without I/O) is a design pattern where the library handles protocol logic but you control all I/O operations. Instead of the library performing network reads and writes directly, you feed it network data and it tells you what to send.

Benefits:

  • ๐Ÿš€ Runtime Independent - Works with tokio, async-std, smol, or blocking I/O
  • ๐ŸŽฏ Full Control - You control threading, scheduling, and I/O multiplexing
  • ๐Ÿงช Testable - Protocol logic can be tested without real network I/O
  • ๐Ÿ”Œ Flexible - Easy integration with existing networking code

Sans-I/O Event Loop Pattern

The sans-I/O architecture uses a simple event loop with six core methods:

Core API Methods

  1. poll_write() - Get outgoing network packets to send via UDP
  2. poll_event() - Process connection state changes and notifications
  3. poll_read() - Get incoming application messages (RTP, RTCP, data)
  4. poll_timeout() - Get next timer deadline for retransmissions/keepalives
  5. handle_read() - Feed incoming network packets into the connection
  6. handle_timeout() - Notify about timer expiration

Additional methods for external control:

  • handle_write() - Queue application messages (RTP/RTCP/data) for sending
  • handle_event() - Inject external events into the connection

Event Loop Example

use rtc::peer_connection::RTCPeerConnection;
use rtc::peer_connection::configuration::RTCConfigurationBuilder;
use rtc::peer_connection::event::{RTCPeerConnectionEvent, RTCTrackEvent};
use rtc::peer_connection::state::RTCPeerConnectionState;
use rtc::peer_connection::message::RTCMessage;
use rtc::peer_connection::sdp::RTCSessionDescription;
use rtc::shared::{TaggedBytesMut, TransportContext, TransportProtocol};
use rtc::sansio::Protocol;
use std::time::{Duration, Instant};
use tokio::net::UdpSocket;
use bytes::BytesMut;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Setup peer connection
    let config = RTCConfigurationBuilder::new().build();
    let mut pc = RTCPeerConnection::new(config)?;

    // Signaling: Create offer and set local description
    let offer = pc.create_offer(None)?;
    pc.set_local_description(offer.clone())?;

    // TODO: Send offer.sdp to remote peer via your signaling channel
    // signaling_channel.send_offer(&offer.sdp).await?;

    // TODO: Receive answer from remote peer via your signaling channel
    // let answer_sdp = signaling_channel.receive_answer().await?;
    // let answer = RTCSessionDescription::answer(answer_sdp)?;
    // pc.set_remote_description(answer)?;

    // Bind UDP socket
    let socket = UdpSocket::bind("0.0.0.0:0").await?;
    let local_addr = socket.local_addr()?;
    let mut buf = vec![0u8; 2000];

    'EventLoop: loop {
        // 1. Send outgoing packets
        while let Some(msg) = pc.poll_write() {
            socket.send_to(&msg.message, msg.transport.peer_addr).await?;
        }

        // 2. Handle events
        while let Some(event) = pc.poll_event() {
            match event {
                RTCPeerConnectionEvent::OnConnectionStateChangeEvent(state) => {
                    println!("Connection state: {state}");
                    if state == RTCPeerConnectionState::Failed {
                        return Ok(());
                    }
                }
                RTCPeerConnectionEvent::OnTrack(RTCTrackEvent::OnOpen(init)) => {
                    println!("New track: {}", init.track_id);
                }
                _ => {}
            }
        }

        // 3. Handle incoming messages
        while let Some(message) = pc.poll_read() {
            match message {
                RTCMessage::RtpPacket(track_id, packet) => {
                    println!("RTP packet on track {track_id}");
                }
                RTCMessage::DataChannelMessage(channel_id, msg) => {
                    println!("Data channel message");
                }
                _ => {}
            }
        }

        // 4. Handle timeouts
        let timeout = pc.poll_timeout()
            .unwrap_or(Instant::now() + Duration::from_secs(86400));
        let delay = timeout.saturating_duration_since(Instant::now());

        if delay.is_zero() {
            pc.handle_timeout(Instant::now())?;
            continue;
        }

        // 5. Multiplex I/O
        tokio::select! {
            _ = stop_rx.recv() => {
                break 'EventLoop,
            } 
            _ = tokio::time::sleep(delay) => {
                pc.handle_timeout(Instant::now())?;
            }
            Ok(message) = message_rx.recv() => {
                pc.handle_write(message)?;
            }
            Ok(event) = event_rx.recv() => {
                pc.handle_event(event)?;
            }
            Ok((n, peer_addr)) = socket.recv_from(&mut buf) => {
                pc.handle_read(TaggedBytesMut {
                    now: Instant::now(),
                    transport: TransportContext {
                        local_addr,
                        peer_addr,
                        ecn: None,
                        transport_protocol: TransportProtocol::UDP,
                    },
                    message: BytesMut::from(&buf[..n]),
                })?;
            }
        }
    }

    pc.close()?;

    Ok(())
}

Features

  • โœ… ICE (Interactive Connectivity Establishment) - NAT traversal with STUN/TURN
  • โœ… DTLS (Datagram Transport Layer Security) - Encryption for media and data
  • โœ… SCTP (Stream Control Transmission Protocol) - Reliable data channels
  • โœ… RTP/RTCP - Real-time media transport and control
  • โœ… SDP (Session Description Protocol) - Offer/answer negotiation
  • โœ… Data Channels - Bidirectional peer-to-peer data transfer
  • โœ… Media Tracks - Audio/video transmission
  • โœ… Trickle ICE - Progressive candidate gathering
  • โœ… Simulcast & SVC - Scalable video coding

More Examples

The repository includes comprehensive examples demonstrating various use cases:

Run an example:

cargo run --example data-channels-offer --features examples

Architecture

RTC is built from composable crates, each implementing a specific protocol:

RTC Crates

Dependency Graph

Protocol Stack

Common Use Cases

Data Channels

use rtc::data_channel::RTCDataChannelInit;

fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
    // Create a data channel
    let init = RTCDataChannelInit {
        ordered: true,
        max_retransmits: None,
        ..Default::default()
    };
    let mut dc = pc.create_data_channel("my-channel", Some(init))?;

    // Send data
    dc.send_text("Hello, WebRTC!")?;
    Ok(())
}

Media Tracks

use rtc::media_stream::MediaStreamTrack;
use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RtpCodecKind};

fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
    // Create a video track
    let track = MediaStreamTrack::new(
        "stream-id".to_string(),
        "track-id".to_string(),
        "Camera".to_string(),
        RtpCodecKind::Video,
        None,
        12345, // SSRC
        RTCRtpCodec::default(),
    );

    // Add to peer connection
    let sender_id = pc.add_track(track)?;
    Ok(())
}

Signaling

WebRTC requires an external signaling channel (e.g., WebSocket, HTTP) to exchange offers and answers:

fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
    // Create and send offer
    let offer = pc.create_offer(None)?;
    pc.set_local_description(offer.clone())?;
    // Send offer.sdp via your signaling channel

    // Receive and apply answer
    // let answer = receive_answer_from_signaling()?;
    // pc.set_remote_description(answer)?;
    Ok(())
}

Specification Compliance

This implementation follows these specifications:

  • W3C WebRTC 1.0 - Main WebRTC API specification
  • RFC 8829 - JSEP: JavaScript Session Establishment Protocol
  • RFC 8866 - SDP: Session Description Protocol
  • RFC 8445 - ICE: Interactive Connectivity Establishment
  • RFC 6347 - DTLS: Datagram Transport Layer Security
  • RFC 8831 - WebRTC Data Channels
  • RFC 3550 - RTP: Real-time Transport Protocol

Documentation

Building and Testing

# Build the library
cargo build

# Run tests
cargo test

# Build documentation
cargo doc --open

# Run examples
cargo run --example data-channels-offer --features examples

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under either of:

at your option.

Acknowledgments

Special thanks to all contributors and the WebRTC-rs community for making this project possible.