rtc/lib.rs
1//! # RTC - Sans-I/O WebRTC Implementation
2//!
3//! A Rust implementation of the [WebRTC specification](https://www.w3.org/TR/webrtc/) using a
4//! **sans-I/O architecture**. This crate provides full WebRTC functionality while giving you
5//! complete control over networking, threading, and async runtime integration.
6//!
7//! ## What is Sans-I/O?
8//!
9//! Sans-I/O (without I/O) is a design pattern that separates protocol logic from I/O operations.
10//! Instead of the library performing network reads and writes directly, **you** provide the
11//! network data and handle the output. This gives you:
12//!
13//! - **Runtime Independence**: Works with tokio, async-std, smol, or blocking I/O
14//! - **Full Control**: You control threading, scheduling, and I/O multiplexing
15//! - **Testability**: Protocol logic can be tested without real network I/O
16//! - **Flexibility**: Easy integration with existing networking code
17//!
18//! ## Quick Start
19//!
20//! ```no_run
21//! use rtc::peer_connection::RTCPeerConnectionBuilder;
22//! use rtc::peer_connection::configuration::RTCConfigurationBuilder;
23//! use rtc::peer_connection::transport::RTCIceServer;
24//! use rtc::peer_connection::sdp::RTCSessionDescription;
25//! use rtc::peer_connection::transport::{CandidateConfig, CandidateHostConfig, RTCIceCandidate};
26//!
27//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
28//! // 1. Create a peer connection with ICE servers
29//! let mut pc = RTCPeerConnectionBuilder::new()
30//! .with_configuration(
31//! RTCConfigurationBuilder::new()
32//! .with_ice_servers(vec![RTCIceServer {
33//! urls: vec!["stun:stun.l.google.com:19302".to_string()],
34//! ..Default::default()
35//! }])
36//! .build()
37//! )
38//! .build()?;
39//!
40//! // 2. Create an offer
41//! let offer = pc.create_offer(None)?;
42//! pc.set_local_description(offer.clone())?;
43//!
44//! // Send offer to remote peer via your signaling channel
45//! // signaling.send(offer.sdp)?;
46//!
47//! // 3. Receive answer from remote peer
48//! // let answer_sdp = signaling.receive()?;
49//! # let answer_sdp = String::new();
50//! let answer = RTCSessionDescription::answer(answer_sdp)?;
51//! pc.set_remote_description(answer)?;
52//!
53//! // 4. Add local ICE candidate
54//! # use std::net::{IpAddr, Ipv4Addr};
55//! let candidate = CandidateHostConfig {
56//! base_config: CandidateConfig {
57//! network: "udp".to_owned(),
58//! address: "192.168.1.100".to_string(),
59//! port: 8080,
60//! component: 1,
61//! ..Default::default()
62//! },
63//! ..Default::default()
64//! }
65//! .new_candidate_host()?;
66//! let local_candidate_init = RTCIceCandidate::from(&candidate).to_json()?;
67//! pc.add_local_candidate(local_candidate_init)?;
68//!
69//! // 5. Event loop - see complete example below
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! ## Complete Event Loop with All API Calls
75//!
76//! This example demonstrates the full sans-I/O event loop pattern with all key API methods:
77//!
78//! ```no_run
79//! use rtc::peer_connection::RTCPeerConnectionBuilder;
80//! use rtc::peer_connection::configuration::{RTCConfigurationBuilder, media_engine::MediaEngine};
81//! use rtc::peer_connection::transport::RTCIceServer;
82//! use rtc::peer_connection::event::{RTCPeerConnectionEvent, RTCTrackEvent};
83//! use rtc::peer_connection::state::{RTCPeerConnectionState, RTCIceConnectionState};
84//! use rtc::peer_connection::message::RTCMessage;
85//! use rtc::shared::{TaggedBytesMut, TransportContext, TransportProtocol};
86//! use rtc::sansio::Protocol;
87//! use std::time::{Duration, Instant};
88//! use tokio::net::UdpSocket;
89//! use bytes::BytesMut;
90//!
91//! # #[tokio::main]
92//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
93//! // Configure media codecs
94//! let media_engine = MediaEngine::default();
95//!
96//! // Create peer connection
97//! let mut pc = RTCPeerConnectionBuilder::new()
98//! .with_configuration(
99//! RTCConfigurationBuilder::new()
100//! .with_ice_servers(vec![RTCIceServer {
101//! urls: vec!["stun:stun.l.google.com:19302".to_string()],
102//! ..Default::default()
103//! }])
104//! .build()
105//! )
106//! .with_media_engine(media_engine)
107//! .build()?;
108//!
109//! // Bind UDP socket for network I/O
110//! let socket = UdpSocket::bind("0.0.0.0:0").await?;
111//! let local_addr = socket.local_addr()?;
112//!
113//! let mut buf = vec![0u8; 2000];
114//! const DEFAULT_TIMEOUT: Duration = Duration::from_secs(86400);
115//!
116//! // Main event loop
117//! loop {
118//! // 1. poll_write() - Get outgoing network packets
119//! while let Some(msg) = pc.poll_write() {
120//! socket.send_to(&msg.message, msg.transport.peer_addr).await?;
121//! }
122//!
123//! // 2. poll_event() - Process connection state changes and events
124//! while let Some(event) = pc.poll_event() {
125//! match event {
126//! RTCPeerConnectionEvent::OnIceConnectionStateChangeEvent(state) => {
127//! println!("ICE Connection State: {state}");
128//! if state == RTCIceConnectionState::Failed {
129//! break;
130//! }
131//! }
132//! RTCPeerConnectionEvent::OnConnectionStateChangeEvent(state) => {
133//! println!("Connection State: {state}");
134//! if state == RTCPeerConnectionState::Failed {
135//! return Ok(());
136//! }
137//! }
138//! RTCPeerConnectionEvent::OnDataChannel(dc_event) => {
139//! println!("Data channel event: {:?}", dc_event);
140//! }
141//! RTCPeerConnectionEvent::OnTrack(track_event) => {
142//! match track_event {
143//! RTCTrackEvent::OnOpen(init) => {
144//! println!("Track opened: track_id={}, receiver_id={:?}",
145//! init.track_id, init.receiver_id);
146//! }
147//! RTCTrackEvent::OnClose(track_id) => {
148//! println!("Track closed: {track_id}");
149//! }
150//! _ => {}
151//! }
152//! }
153//! _ => {}
154//! }
155//! }
156//!
157//! // 3. poll_read() - Get incoming application messages (RTP/RTCP/data)
158//! while let Some(message) = pc.poll_read() {
159//! match message {
160//! RTCMessage::RtpPacket(track_id, rtp_packet) => {
161//! println!("Received RTP packet on track {track_id}");
162//! // Process RTP packet
163//! }
164//! RTCMessage::RtcpPacket(receiver_id, rtcp_packets) => {
165//! println!("Received RTCP packets on receiver {:?}", receiver_id);
166//! // Process RTCP packets
167//! }
168//! RTCMessage::DataChannelMessage(channel_id, message) => {
169//! println!("Received data channel message on channel {:?}", channel_id);
170//! // Process data channel message
171//! }
172//! }
173//! }
174//!
175//! // 4. poll_timeout() - Get next timer deadline
176//! let timeout = pc.poll_timeout()
177//! .unwrap_or(Instant::now() + DEFAULT_TIMEOUT);
178//! let delay = timeout.saturating_duration_since(Instant::now());
179//!
180//! // Handle immediate timeout
181//! if delay.is_zero() {
182//! // 6. handle_timeout() - Notify about timer expiration
183//! pc.handle_timeout(Instant::now())?;
184//! continue;
185//! }
186//!
187//! // Wait for events using tokio::select!
188//! let timer = tokio::time::sleep(delay);
189//! tokio::pin!(timer);
190//!
191//! tokio::select! {
192//! biased;
193//!
194//! // Timer expired
195//! _ = timer => {
196//! pc.handle_timeout(Instant::now())?;
197//! }
198//! // Received network packet
199//! Ok((n, peer_addr)) = socket.recv_from(&mut buf) => {
200//! // 5. handle_read() - Feed incoming network packets
201//! pc.handle_read(TaggedBytesMut {
202//! now: Instant::now(),
203//! transport: TransportContext {
204//! local_addr,
205//! peer_addr,
206//! ecn: None,
207//! transport_protocol: TransportProtocol::UDP,
208//! },
209//! message: BytesMut::from(&buf[..n]),
210//! })?;
211//! }
212//! // Ctrl-C to exit
213//! _ = tokio::signal::ctrl_c() => {
214//! break;
215//! }
216//! }
217//! }
218//!
219//! pc.close()?;
220//! # Ok(())
221//! # }
222//! ```
223//!
224//! ## Core API Methods
225//!
226//! ### Sans-I/O Event Loop Methods
227//!
228//! The event loop uses these six core methods:
229//!
230//! 1. **`poll_write()`** - Get outgoing network packets to send via UDP
231//! 2. **`poll_event()`** - Process connection state changes and notifications
232//! 3. **`poll_read()`** - Get incoming application messages (RTP, RTCP, data)
233//! 4. **`poll_timeout()`** - Get next timer deadline for retransmissions/keepalives
234//! 5. **`handle_read()`** - Feed incoming network packets into the connection
235//! 6. **`handle_timeout()`** - Notify about timer expiration
236//!
237//! Additional methods for external control:
238//!
239//! - **`handle_write()`** - Queue application messages (RTP/RTCP/data) for sending
240//! - **`handle_event()`** - Inject external events into the connection
241//!
242//! ### Signaling with Complete Example
243//!
244//! WebRTC requires an external signaling channel to exchange offers, answers, and ICE
245//! candidates. This example shows the complete offer/answer flow:
246//!
247//! ```no_run
248//! use rtc::peer_connection::RTCPeerConnectionBuilder;
249//! use rtc::peer_connection::configuration::RTCConfigurationBuilder;
250//! use rtc::peer_connection::transport::RTCIceServer;
251//! use rtc::peer_connection::sdp::RTCSessionDescription;
252//! use rtc::peer_connection::transport::{CandidateConfig, CandidateHostConfig, RTCIceCandidate};
253//!
254//! # fn send_to_remote_peer(_: &str) {}
255//! # fn receive_from_remote_peer() -> String { String::new() }
256//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
257//! // Offerer side - creates the offer
258//! let mut offerer = RTCPeerConnectionBuilder::new()
259//! .with_configuration(
260//! RTCConfigurationBuilder::new()
261//! .with_ice_servers(vec![RTCIceServer {
262//! urls: vec!["stun:stun.l.google.com:19302".to_string()],
263//! ..Default::default()
264//! }])
265//! .build()
266//! )
267//! .build()?;
268//!
269//! // 1. Create offer
270//! let offer = offerer.create_offer(None)?;
271//!
272//! // 2. Set local description
273//! offerer.set_local_description(offer.clone())?;
274//!
275//! // 3. Add local ICE candidate
276//! let candidate = CandidateHostConfig {
277//! base_config: CandidateConfig {
278//! network: "udp".to_owned(),
279//! address: "192.168.1.100".to_string(),
280//! port: 8080,
281//! component: 1,
282//! ..Default::default()
283//! },
284//! ..Default::default()
285//! }
286//! .new_candidate_host()?;
287//! offerer.add_local_candidate(RTCIceCandidate::from(&candidate).to_json()?)?;
288//!
289//! // 4. Send offer to remote peer (your signaling channel)
290//! send_to_remote_peer(&serde_json::to_string(&offer)?);
291//!
292//! // --- On answerer side ---
293//! let mut answerer = RTCPeerConnectionBuilder::new()
294//! .with_configuration(
295//! RTCConfigurationBuilder::new()
296//! .with_ice_servers(vec![RTCIceServer {
297//! urls: vec!["stun:stun.l.google.com:19302".to_string()],
298//! ..Default::default()
299//! }])
300//! .build()
301//! )
302//! .build()?;
303//!
304//! // 5. Receive and set remote description
305//! let offer_json = receive_from_remote_peer();
306//! let remote_offer: RTCSessionDescription = serde_json::from_str(&offer_json)?;
307//! answerer.set_remote_description(remote_offer)?;
308//!
309//! // 6. Create answer
310//! let answer = answerer.create_answer(None)?;
311//!
312//! // 7. Set local description
313//! answerer.set_local_description(answer.clone())?;
314//!
315//! // 8. Send answer back to offerer
316//! send_to_remote_peer(&serde_json::to_string(&answer)?);
317//!
318//! // --- Back on offerer side ---
319//! // 9. Receive and set remote description
320//! let answer_json = receive_from_remote_peer();
321//! let remote_answer: RTCSessionDescription = serde_json::from_str(&answer_json)?;
322//! offerer.set_remote_description(remote_answer)?;
323//!
324//! // Now both peers are connected!
325//! # Ok(())
326//! # }
327//! ```
328//!
329//! ## Module Organization
330//!
331//! ### [`peer_connection`]
332//!
333//! Core WebRTC peer connection implementation:
334//!
335//! - **[`RTCPeerConnection`](peer_connection::RTCPeerConnection)** - Peer connection interface
336//! - **[`certificate`](peer_connection::certificate)** - Peer connection certficiate
337//! - **[`configuration`](peer_connection::configuration)** - Peer connection configuration
338//! - **[`interceptor_registry`](peer_connection::configuration::interceptor_registry)** - NACK, TWCC, RTCP Reports configuration
339//! - **[`media_engine`](peer_connection::configuration::media_engine)** - Codec and RTP extension configuration
340//! - **[`setting_engine`](peer_connection::configuration::setting_engine)** - Low-level transport settings
341//! - **[`event`](peer_connection::event)** - Peer connection events
342//! - **[`message`](peer_connection::message)** - RTP/RTCP Packets and Application messages
343//! - **[`sdp`](peer_connection::sdp)** - SDP offer/answer types
344//! - **[`state`](peer_connection::state)** - Peer connection state types
345//! - **[`transport`](peer_connection::transport)** - ICE, DTLS, SCTP transport types
346//!
347//! ### [`data_channel`]
348//!
349//! WebRTC data channels for arbitrary data transfer:
350//!
351//! - **[`RTCDataChannel`](data_channel::RTCDataChannel)** - Data channel interface
352//! - **[`RTCDataChannelInit`](data_channel::RTCDataChannelInit)** - Channel configuration
353//! - **[`RTCDataChannelMessage`](data_channel::RTCDataChannelMessage)** - Data channel messages
354//!
355//! ### [`rtp_transceiver`]
356//!
357//! RTP media transmission and reception:
358//!
359//! - **[`RTCRtpSender`](rtp_transceiver::rtp_sender::RTCRtpSender)** - Media sender
360//! - **[`RTCRtpReceiver`](rtp_transceiver::rtp_receiver::RTCRtpReceiver)** - Media receiver
361//!
362//! ### [`media_stream`]
363//!
364//! Media track management:
365//!
366//! - **[`MediaStreamTrack`](media_stream::track::MediaStreamTrack)** - Audio/video track
367//!
368//! ## Features
369//!
370//! - ✅ **ICE (Interactive Connectivity Establishment)** - NAT traversal with STUN/TURN
371//! - ✅ **DTLS (Datagram Transport Layer Security)** - Encryption for media and data
372//! - ✅ **SCTP (Stream Control Transmission Protocol)** - Reliable data channels
373//! - ✅ **RTP/RTCP** - Real-time media transport and control
374//! - ✅ **SDP (Session Description Protocol)** - Offer/answer negotiation
375//! - ✅ **Data Channels** - Bidirectional peer-to-peer data transfer
376//! - ✅ **Media Tracks** - Audio/video transmission
377//! - ✅ **Trickle ICE** - Progressive candidate gathering
378//! - ✅ **ICE Restart** - Connection recovery
379//! - ✅ **Simulcast & SVC** - Scalable video coding
380//!
381//! ## Working Examples
382//!
383//! The crate includes comprehensive examples in the `examples/` directory:
384//!
385//! - **data-channels-offer-answer** - Complete data channel setup with signaling
386//! - **save-to-disk-vpx** - Receive and save VP8/VP9 video to disk
387//! - **play-from-disk-vpx** - Send VP8/VP9 video from disk
388//! - **rtp-forwarder** - Forward RTP streams between peers
389//! - **simulcast** - Multiple quality streams
390//! - **trickle-ice** - Progressive ICE candidate exchange
391//!
392//! See the `examples/` directory for complete, runnable code.
393//!
394//! ## Common Patterns
395//!
396//! ### Configuring Interceptors (NACK, TWCC, RTCP Reports)
397//!
398//! Interceptors process RTP/RTCP packets as they flow through the media pipeline.
399//! Use the [`interceptor_registry`](peer_connection::configuration::interceptor_registry) module
400//! to configure packet loss recovery, congestion control, and quality monitoring:
401//!
402//! ```no_run
403//! use rtc::peer_connection::RTCPeerConnectionBuilder;
404//! use rtc::peer_connection::configuration::RTCConfigurationBuilder;
405//! use rtc::peer_connection::configuration::media_engine::MediaEngine;
406//! use rtc::peer_connection::configuration::interceptor_registry::register_default_interceptors;
407//! use rtc::interceptor::Registry;
408//!
409//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
410//! // Create media engine with default codecs
411//! let mut media_engine = MediaEngine::default();
412//!
413//! // Create interceptor registry with default interceptors:
414//! // - NACK: Packet loss recovery for video
415//! // - RTCP Reports: Sender/Receiver quality statistics
416//! // - TWCC Receiver: Congestion control feedback
417//! let registry = Registry::new();
418//! let registry = register_default_interceptors(registry, &mut media_engine)?;
419//!
420//! // Build peer connection with interceptors
421//! let mut pc = RTCPeerConnectionBuilder::new()
422//! .with_media_engine(media_engine)
423//! .with_interceptor_registry(registry)
424//! .build()?;
425//! # Ok(())
426//! # }
427//! ```
428//!
429//! For custom interceptor configuration:
430//!
431//! ```no_run
432//! use rtc::peer_connection::RTCPeerConnectionBuilder;
433//! use rtc::peer_connection::configuration::RTCConfigurationBuilder;
434//! use rtc::peer_connection::configuration::media_engine::MediaEngine;
435//! use rtc::peer_connection::configuration::interceptor_registry::{
436//! configure_nack,
437//! configure_rtcp_reports,
438//! configure_twcc,
439//! };
440//! use rtc::interceptor::Registry;
441//!
442//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
443//! let mut media_engine = MediaEngine::default();
444//! let registry = Registry::new();
445//!
446//! // Configure individual interceptors as needed
447//! let registry = configure_nack(registry, &mut media_engine); // Packet loss recovery
448//! let registry = configure_rtcp_reports(registry); // SR/RR statistics
449//! let registry = configure_twcc(registry, &mut media_engine)?; // Full TWCC (sender + receiver)
450//!
451//! let mut pc = RTCPeerConnectionBuilder::new()
452//! .with_media_engine(media_engine)
453//! .with_interceptor_registry(registry)
454//! .build()?;
455//! # Ok(())
456//! # }
457//! ```
458//!
459//! #### Type-erasing the interceptor chain
460//!
461//! [`RTCPeerConnection`](peer_connection::RTCPeerConnection) is generic over its
462//! interceptor chain, and a chain's type spells out its entire composition
463//! (`TwccReceiverInterceptor<SenderReportInterceptor<…>>`). That type propagates into
464//! everything that *holds* a peer connection, which is a problem when the chain is chosen
465//! at runtime or when connections have to live in your own structs and collections.
466//!
467//! [`Registry::boxed`](interceptor::Registry::boxed) erases the chain to
468//! [`BoxedInterceptor`](interceptor::BoxedInterceptor), giving every connection the same
469//! concrete type no matter how it was built:
470//!
471//! ```no_run
472//! use rtc::interceptor::{BoxedInterceptor, Registry};
473//! use rtc::peer_connection::configuration::interceptor_registry::register_default_interceptors;
474//! use rtc::peer_connection::configuration::media_engine::MediaEngine;
475//! use rtc::peer_connection::{RTCPeerConnection, RTCPeerConnectionBuilder};
476//!
477//! // No type parameter: this struct does not have to know the chain.
478//! struct Session {
479//! peer_connection: RTCPeerConnection<BoxedInterceptor>,
480//! }
481//!
482//! # fn example(with_nack: bool) -> Result<(), Box<dyn std::error::Error>> {
483//! let mut media_engine = MediaEngine::default();
484//! let registry = register_default_interceptors(Registry::new(), &mut media_engine)?;
485//!
486//! let mut sessions: Vec<Session> = Vec::new();
487//! sessions.push(Session {
488//! peer_connection: RTCPeerConnectionBuilder::new()
489//! .with_media_engine(media_engine)
490//! .with_interceptor_registry(registry.boxed())
491//! .build()?,
492//! });
493//! # Ok(())
494//! # }
495//! ```
496//!
497//! The cost is one virtual call per chain entry point (`handle_read`, `poll_write`,
498//! `handle_timeout`, …); the chain's interior remains statically dispatched and inlined.
499//! Static dispatch is still the default — keep the generic form when the chain is fixed at
500//! compile time.
501//!
502//! ### Creating and Using Data Channels
503//!
504//! ```no_run
505//! use rtc::peer_connection::RTCPeerConnection;
506//! use rtc::peer_connection::configuration::RTCConfiguration;
507//! use rtc::data_channel::RTCDataChannelInit;
508//! use rtc::peer_connection::event::RTCPeerConnectionEvent;
509//! use rtc::peer_connection::message::RTCMessage;
510//! use rtc::sansio::Protocol;
511//! use bytes::BytesMut;
512//!
513//! # fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
514//! // Create data channel with ordered, reliable delivery
515//! let init = RTCDataChannelInit {
516//! ordered: true,
517//! max_retransmits: None,
518//! ..Default::default()
519//! };
520//!
521//! let mut dc = pc.create_data_channel("my-channel", Some(init))?;
522//! let channel_id = dc.id();
523//!
524//! // Send text message
525//! dc.send_text("Hello, WebRTC!")?;
526//!
527//! // Send binary message
528//! dc.send(BytesMut::from(&[0x01, 0x02, 0x03, 0x04][..]))?;
529//!
530//! // Later, retrieve the data channel by ID
531//! if let Some(mut dc) = pc.data_channel(channel_id) {
532//! dc.send_text("Another message")?;
533//! }
534//!
535//! // Receive messages in event loop
536//! while let Some(message) = pc.poll_read() {
537//! if let RTCMessage::DataChannelMessage(channel_id, msg) = message {
538//! if msg.is_string {
539//! let text = String::from_utf8_lossy(&msg.data);
540//! println!("Received text: {text}");
541//! } else {
542//! println!("Received binary: {} bytes", msg.data.len());
543//! }
544//! }
545//! }
546//! # Ok(())
547//! # }
548//! ```
549//!
550//! ### Adding Media Tracks with Codecs
551//!
552//! ```no_run
553//! use rtc::peer_connection::RTCPeerConnection;
554//! use rtc::media_stream::MediaStreamTrack;
555//! use rtc::rtp_transceiver::rtp_sender::{RTCRtpCodec, RTCRtpCodecParameters, RtpCodecKind};
556//! use rtc::rtp_transceiver::rtp_sender::{RTCRtpEncodingParameters, RTCRtpCodingParameters};
557//! use rtc::peer_connection::configuration::media_engine::{MIME_TYPE_VP8, MIME_TYPE_OPUS};
558//!
559//! # fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
560//! // Configure VP8 video codec
561//! let video_codec = RTCRtpCodec {
562//! mime_type: MIME_TYPE_VP8.to_owned(),
563//! clock_rate: 90000,
564//! channels: 0,
565//! sdp_fmtp_line: "".to_owned(),
566//! rtcp_feedback: vec![],
567//! };
568//!
569//! // Create video track
570//! let video_track = MediaStreamTrack::new(
571//! "stream-id".to_string(),
572//! "video-track-id".to_string(),
573//! "video-label".to_string(),
574//! RtpCodecKind::Video,
575//! vec![RTCRtpEncodingParameters {
576//! rtp_coding_parameters: RTCRtpCodingParameters {
577//! ssrc: Some(rand::random::<u32>()),
578//! ..Default::default()
579//! },
580//! codec: video_codec.clone(),
581//! ..Default::default()
582//! }],
583//! );
584//!
585//! // Add track to peer connection
586//! let sender_id = pc.add_track(video_track)?;
587//!
588//! // Send RTP packets
589//! if let Some(mut sender) = pc.rtp_sender(sender_id) {
590//! // sender.write_rtp(rtp_packet)?;
591//! }
592//! # Ok(())
593//! # }
594//! ```
595//!
596//! ### Receiving Media Tracks
597//!
598//! ```no_run
599//! use rtc::peer_connection::RTCPeerConnection;
600//! use rtc::peer_connection::event::{RTCPeerConnectionEvent, RTCTrackEvent};
601//! use rtc::peer_connection::message::RTCMessage;
602//! use rtc::sansio::Protocol;
603//! use std::collections::HashMap;
604//!
605//! # fn example(mut pc: RTCPeerConnection) -> Result<(), Box<dyn std::error::Error>> {
606//! // Track mapping for received tracks
607//! let mut track_to_receiver = HashMap::new();
608//!
609//! // Handle track events
610//! while let Some(event) = pc.poll_event() {
611//! if let RTCPeerConnectionEvent::OnTrack(track_event) = event {
612//! match track_event {
613//! RTCTrackEvent::OnOpen(init) => {
614//! println!("New track: track_id={}, receiver_id={:?}",
615//! init.track_id, init.receiver_id);
616//! track_to_receiver.insert(init.track_id.clone(), init.receiver_id);
617//! }
618//! RTCTrackEvent::OnClose(track_id) => {
619//! println!("Track closed: {track_id}");
620//! track_to_receiver.remove(&track_id);
621//! }
622//! _ => {}
623//! }
624//! }
625//! }
626//!
627//! // Receive RTP packets
628//! while let Some(message) = pc.poll_read() {
629//! if let RTCMessage::RtpPacket(track_id, rtp_packet) = message {
630//! println!("RTP packet on track {}: {} bytes",
631//! track_id, rtp_packet.payload.len());
632//!
633//! // Access receiver to get track metadata
634//! if let Some(&receiver_id) = track_to_receiver.get(&track_id) {
635//! if let Some(receiver) = pc.rtp_receiver(receiver_id) {
636//! let track = receiver.track();
637//! let ssrcs: Vec<u32> = track.ssrcs().collect();
638//! println!(" SSRCs: {:?}, Kind: {:?}", ssrcs, track.kind());
639//! }
640//! }
641//! }
642//! }
643//! # Ok(())
644//! # }
645//! ```
646//!
647//! ### Sending RTCP Packets (e.g., PLI for keyframes)
648//!
649//! ```no_run
650//! use rtc::peer_connection::RTCPeerConnection;
651//! use rtc::rtp_transceiver::RTCRtpReceiverId;
652//! use rtc::rtcp::payload_feedbacks::picture_loss_indication::PictureLossIndication;
653//!
654//! # fn example(mut pc: RTCPeerConnection, receiver_id: RTCRtpReceiverId, media_ssrc: u32)
655//! # -> Result<(), Box<dyn std::error::Error>> {
656//! // Request keyframe by sending Picture Loss Indication (PLI)
657//! if let Some(mut receiver) = pc.rtp_receiver(receiver_id) {
658//! receiver.write_rtcp(vec![Box::new(PictureLossIndication {
659//! sender_ssrc: 0,
660//! media_ssrc,
661//! })])?;
662//! }
663//! # Ok(())
664//! # }
665//! ```
666//!
667//! ## Specification Compliance
668//!
669//! This implementation follows these specifications:
670//!
671//! - [W3C WebRTC 1.0] - Main WebRTC API specification
672//! - [RFC 8829] - JSEP: JavaScript Session Establishment Protocol
673//! - [RFC 8866] - SDP: Session Description Protocol
674//! - [RFC 8445] - ICE: Interactive Connectivity Establishment
675//! - [RFC 6347] - DTLS: Datagram Transport Layer Security
676//! - [RFC 8831] - WebRTC Data Channels
677//! - [RFC 3550] - RTP: Real-time Transport Protocol
678//!
679//! [W3C WebRTC 1.0]: https://www.w3.org/TR/webrtc/
680//! [RFC 8829]: https://datatracker.ietf.org/doc/html/rfc8829
681//! [RFC 8866]: https://datatracker.ietf.org/doc/html/rfc8866
682//! [RFC 8445]: https://datatracker.ietf.org/doc/html/rfc8445
683//! [RFC 6347]: https://datatracker.ietf.org/doc/html/rfc6347
684//! [RFC 8831]: https://datatracker.ietf.org/doc/html/rfc8831
685//! [RFC 3550]: https://datatracker.ietf.org/doc/html/rfc3550
686//!
687//! ## Further Reading
688//!
689//! - [Sans-I/O Approach](https://sans-io.readthedocs.io/) - Detailed explanation of sans-I/O design
690//! - [WebRTC for the Curious](https://webrtcforthecurious.com/) - Comprehensive WebRTC guide
691//! - [MDN WebRTC API](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) - Browser WebRTC documentation
692
693#![doc(
694 html_logo_url = "https://raw.githubusercontent.com/webrtc-rs/webrtc-rs.github.io/master/res/rtc.png"
695)]
696#![warn(rust_2018_idioms)]
697#![warn(missing_docs)]
698#![allow(dead_code)]
699
700pub use {
701 datachannel, dtls, ice, interceptor, mdns, media, rtcp, rtp, sansio, sctp, sdp, shared, srtp,
702 stun, turn,
703};
704
705pub mod data_channel;
706pub mod media_stream;
707pub mod peer_connection;
708pub mod rtp_transceiver;
709pub mod statistics;
710
711#[cfg(all(feature = "aws-lc-rs", feature = "ring"))]
712compile_error!("At most one of the features \"aws-lc-rs\" and \"ring\" can be enabled.");
713#[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
714compile_error!("At least one of the features \"aws-lc-rs\" and \"ring\" must be enabled.");
715#[cfg(feature = "aws-lc-rs")]
716extern crate aws_lc_rs as ring;