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