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