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