1 use anyhow::Result;
2 use clap::{AppSettings, Arg, Command};
3 use std::io::Write;
4 use std::sync::Arc;
5 use tokio::time::Duration;
6 use webrtc::api::interceptor_registry::register_default_interceptors;
7 use webrtc::api::media_engine::MediaEngine;
8 use webrtc::api::APIBuilder;
9 use webrtc::data_channel::data_channel_message::DataChannelMessage;
10 use webrtc::data_channel::RTCDataChannel;
11 use webrtc::ice_transport::ice_server::RTCIceServer;
12 use webrtc::interceptor::registry::Registry;
13 use webrtc::peer_connection::configuration::RTCConfiguration;
14 use webrtc::peer_connection::math_rand_alpha;
15 use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState;
16 use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
17 
18 #[tokio::main]
main() -> Result<()>19 async fn main() -> Result<()> {
20     let mut app = Command::new("data-channels")
21         .version("0.1.0")
22         .author("Rain Liu <[email protected]>")
23         .about("An example of Data-Channels.")
24         .setting(AppSettings::DeriveDisplayOrder)
25         .subcommand_negates_reqs(true)
26         .arg(
27             Arg::new("FULLHELP")
28                 .help("Prints more detailed help information")
29                 .long("fullhelp"),
30         )
31         .arg(
32             Arg::new("debug")
33                 .long("debug")
34                 .short('d')
35                 .help("Prints debug log information"),
36         );
37 
38     let matches = app.clone().get_matches();
39 
40     if matches.is_present("FULLHELP") {
41         app.print_long_help().unwrap();
42         std::process::exit(0);
43     }
44 
45     let debug = matches.is_present("debug");
46     if debug {
47         env_logger::Builder::new()
48             .format(|buf, record| {
49                 writeln!(
50                     buf,
51                     "{}:{} [{}] {} - {}",
52                     record.file().unwrap_or("unknown"),
53                     record.line().unwrap_or(0),
54                     record.level(),
55                     chrono::Local::now().format("%H:%M:%S.%6f"),
56                     record.args()
57                 )
58             })
59             .filter(None, log::LevelFilter::Trace)
60             .init();
61     }
62 
63     // Everything below is the WebRTC-rs API! Thanks for using it ❤️.
64 
65     // Create a MediaEngine object to configure the supported codec
66     let mut m = MediaEngine::default();
67 
68     // Register default codecs
69     m.register_default_codecs()?;
70 
71     // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
72     // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
73     // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
74     // for each PeerConnection.
75     let mut registry = Registry::new();
76 
77     // Use the default set of Interceptors
78     registry = register_default_interceptors(registry, &mut m)?;
79 
80     // Create the API object with the MediaEngine
81     let api = APIBuilder::new()
82         .with_media_engine(m)
83         .with_interceptor_registry(registry)
84         .build();
85 
86     // Prepare the configuration
87     let config = RTCConfiguration {
88         ice_servers: vec![RTCIceServer {
89             urls: vec!["stun:stun.l.google.com:19302".to_owned()],
90             ..Default::default()
91         }],
92         ..Default::default()
93     };
94 
95     // Create a new RTCPeerConnection
96     let peer_connection = Arc::new(api.new_peer_connection(config).await?);
97 
98     let (done_tx, mut done_rx) = tokio::sync::mpsc::channel::<()>(1);
99 
100     // Set the handler for Peer connection state
101     // This will notify you when the peer has connected/disconnected
102     peer_connection.on_peer_connection_state_change(Box::new(move |s: RTCPeerConnectionState| {
103         println!("Peer Connection State has changed: {s}");
104 
105         if s == RTCPeerConnectionState::Failed {
106             // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
107             // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
108             // Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
109             println!("Peer Connection has gone to failed exiting");
110             let _ = done_tx.try_send(());
111         }
112 
113         Box::pin(async {})
114     }));
115 
116     // Register data channel creation handling
117     peer_connection
118         .on_data_channel(Box::new(move |d: Arc<RTCDataChannel>| {
119             let d_label = d.label().to_owned();
120             let d_id = d.id();
121             println!("New DataChannel {d_label} {d_id}");
122 
123             // Register channel opening handling
124             Box::pin(async move {
125                 let d2 = Arc::clone(&d);
126                 let d_label2 = d_label.clone();
127                 let d_id2 = d_id;
128                 d.on_open(Box::new(move || {
129                     println!("Data channel '{d_label2}'-'{d_id2}' open. Random messages will now be sent to any connected DataChannels every 5 seconds");
130 
131                     Box::pin(async move {
132                         let mut result = Result::<usize>::Ok(0);
133                         while result.is_ok() {
134                             let timeout = tokio::time::sleep(Duration::from_secs(5));
135                             tokio::pin!(timeout);
136 
137                             tokio::select! {
138                                 _ = timeout.as_mut() =>{
139                                     let message = math_rand_alpha(15);
140                                     println!("Sending '{message}'");
141                                     result = d2.send_text(message).await.map_err(Into::into);
142                                 }
143                             };
144                         }
145                     })
146                 }));
147 
148                 // Register text message handling
149                 d.on_message(Box::new(move |msg: DataChannelMessage| {
150                     let msg_str = String::from_utf8(msg.data.to_vec()).unwrap();
151                     println!("Message from DataChannel '{d_label}': '{msg_str}'");
152                     Box::pin(async {})
153                 }));
154             })
155         }));
156 
157     // Wait for the offer to be pasted
158     let line = signal::must_read_stdin()?;
159     let desc_data = signal::decode(line.as_str())?;
160     let offer = serde_json::from_str::<RTCSessionDescription>(&desc_data)?;
161 
162     // Set the remote SessionDescription
163     peer_connection.set_remote_description(offer).await?;
164 
165     // Create an answer
166     let answer = peer_connection.create_answer(None).await?;
167 
168     // Create channel that is blocked until ICE Gathering is complete
169     let mut gather_complete = peer_connection.gathering_complete_promise().await;
170 
171     // Sets the LocalDescription, and starts our UDP listeners
172     peer_connection.set_local_description(answer).await?;
173 
174     // Block until ICE Gathering is complete, disabling trickle ICE
175     // we do this because we only can exchange one signaling message
176     // in a production application you should exchange ICE Candidates via OnICECandidate
177     let _ = gather_complete.recv().await;
178 
179     // Output the answer in base64 so we can paste it in browser
180     if let Some(local_desc) = peer_connection.local_description().await {
181         let json_str = serde_json::to_string(&local_desc)?;
182         let b64 = signal::encode(&json_str);
183         println!("{b64}");
184     } else {
185         println!("generate local_description failed!");
186     }
187 
188     println!("Press ctrl-c to stop");
189     tokio::select! {
190         _ = done_rx.recv() => {
191             println!("received done signal!");
192         }
193         _ = tokio::signal::ctrl_c() => {
194             println!();
195         }
196     };
197 
198     peer_connection.close().await?;
199 
200     Ok(())
201 }
202