Skip to content

Commit 047356d

Browse files
authored
Merge pull request #436 from lichon/main
add webrtc stream
2 parents a86eda7 + 4e16783 commit 047356d

7 files changed

Lines changed: 978 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ edition = "2018"
66

77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

9+
[features]
10+
default = []
11+
webrtc = ["dep:webrtc"]
12+
913
[dependencies]
1014
# new flexi_logger failed on rustc 1.75
1115
flexi_logger = { version = "0.27", features = ["async"] }
@@ -61,6 +65,7 @@ rustls-pki-types = "1.11"
6165
rustls-native-certs = "0.8"
6266
webpki-roots = "1.0.4"
6367
async-recursion = "1.1"
68+
webrtc = { version = "0.14.0", optional = true }
6469

6570
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
6671
mac_address = "1.1"
@@ -70,6 +75,10 @@ machine-uid = { git = "https://github.com/rustdesk-org/machine-uid" }
7075
[build-dependencies]
7176
protobuf-codegen = { version = "3.7" }
7277

78+
[dev-dependencies]
79+
clap = "4.5.51"
80+
webrtc = "0.14.0"
81+
7382
[target.'cfg(target_os = "windows")'.dependencies]
7483
winapi = { version = "0.3", features = [
7584
"winuser",

examples/webrtc.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
extern crate hbb_common;
2+
3+
#[cfg(feature = "webrtc")]
4+
use hbb_common::webrtc::WebRTCStream;
5+
6+
use std::io::Write;
7+
use anyhow::Result;
8+
use bytes::Bytes;
9+
use clap::{Arg, Command};
10+
use tokio::time::Duration;
11+
12+
#[cfg(not(feature = "webrtc"))]
13+
#[tokio::main]
14+
async fn main() -> Result<()> {
15+
println!(
16+
"The webrtc feature is not enabled. \
17+
Please enable the webrtc feature to run this example."
18+
);
19+
Ok(())
20+
}
21+
22+
#[cfg(feature = "webrtc")]
23+
#[tokio::main]
24+
async fn main() -> Result<()> {
25+
let app = Command::new("webrtc-stream")
26+
.about("An example of webrtc stream using hbb_common and webrtc-rs")
27+
.arg(
28+
Arg::new("debug")
29+
.long("debug")
30+
.short('d')
31+
.action(clap::ArgAction::SetTrue)
32+
.help("Prints debug log information"),
33+
)
34+
.arg(
35+
Arg::new("offer")
36+
.long("offer")
37+
.short('o')
38+
.help("set offer from other endpoint"),
39+
);
40+
41+
let matches = app.clone().get_matches();
42+
43+
let debug = matches.contains_id("debug");
44+
if debug {
45+
println!("Debug log enabled");
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(Some("hbb_common"), log::LevelFilter::Debug)
59+
.init();
60+
}
61+
62+
let remote_endpoint = if let Some(endpoint) = matches.get_one::<String>("offer") {
63+
endpoint.to_string()
64+
} else {
65+
"".to_string()
66+
};
67+
68+
let webrtc_stream = WebRTCStream::new(&remote_endpoint, false, 30000).await?;
69+
// Print the offer to be sent to the other peer
70+
let local_endpoint = webrtc_stream.get_local_endpoint().await?;
71+
72+
if remote_endpoint.is_empty() {
73+
println!();
74+
// Wait for the answer to be pasted
75+
println!(
76+
"Start new terminal run: \n{} \ncopy remote endpoint and paste here",
77+
format!(
78+
"cargo r --features webrtc --example webrtc -- --offer {}",
79+
local_endpoint
80+
)
81+
);
82+
// readline blocking
83+
let line = std::io::stdin()
84+
.lines()
85+
.next()
86+
.ok_or_else(|| anyhow::anyhow!("No input received"))??;
87+
webrtc_stream.set_remote_endpoint(&line).await?;
88+
} else {
89+
println!(
90+
"Copy local endpoint and paste to the other peer: \n{}",
91+
local_endpoint
92+
);
93+
}
94+
95+
let s1 = webrtc_stream.clone();
96+
tokio::spawn(async move {
97+
let _ = read_loop(s1).await;
98+
});
99+
100+
let s2 = webrtc_stream.clone();
101+
tokio::spawn(async move {
102+
let _ = write_loop(s2).await;
103+
});
104+
105+
println!("Press ctrl-c to stop");
106+
tokio::select! {
107+
_ = tokio::signal::ctrl_c() => {
108+
println!();
109+
}
110+
};
111+
112+
Ok(())
113+
}
114+
115+
// read_loop shows how to read from the datachannel directly
116+
#[cfg(feature = "webrtc")]
117+
async fn read_loop(mut stream: WebRTCStream) -> Result<()> {
118+
loop {
119+
let Some(res) = stream.next().await else {
120+
println!("WebRTC stream closed; Exit the read_loop");
121+
return Ok(());
122+
};
123+
match res {
124+
Err(e) => {
125+
println!("WebRTC stream read error: {}; Exit the read_loop", e);
126+
return Ok(());
127+
}
128+
Ok(data) => {
129+
println!("Message from stream: {}", String::from_utf8(data.to_vec())?);
130+
}
131+
}
132+
}
133+
}
134+
135+
// write_loop shows how to write to the webrtc stream directly
136+
#[cfg(feature = "webrtc")]
137+
async fn write_loop(mut stream: WebRTCStream) -> Result<()> {
138+
let mut result = Result::<()>::Ok(());
139+
while result.is_ok() {
140+
let timeout = tokio::time::sleep(Duration::from_secs(5));
141+
tokio::pin!(timeout);
142+
143+
tokio::select! {
144+
_ = timeout.as_mut() =>{
145+
let message = webrtc::peer_connection::math_rand_alpha(15);
146+
result = stream.send_bytes(Bytes::from(message.clone())).await;
147+
println!("Sent '{message}' {}", result.is_ok());
148+
}
149+
};
150+
}
151+
println!("WebRTC stream write failed; Exit the write_loop");
152+
153+
Ok(())
154+
}

src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,6 +2547,7 @@ pub mod keys {
25472547
pub const OPTION_TRACKPAD_SPEED: &str = "trackpad-speed";
25482548
pub const OPTION_REGISTER_DEVICE: &str = "register-device";
25492549
pub const OPTION_RELAY_SERVER: &str = "relay-server";
2550+
pub const OPTION_ICE_SERVERS: &str = "ice-servers";
25502551
pub const OPTION_DISABLE_UDP: &str = "disable-udp";
25512552
pub const OPTION_ALLOW_INSECURE_TLS_FALLBACK: &str = "allow-insecure-tls-fallback";
25522553
pub const OPTION_SHOW_VIRTUAL_MOUSE: &str = "show-virtual-mouse";
@@ -2743,6 +2744,7 @@ pub mod keys {
27432744
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
27442745
OPTION_ENABLE_TRUSTED_DEVICES,
27452746
OPTION_RELAY_SERVER,
2747+
OPTION_ICE_SERVERS,
27462748
OPTION_DISABLE_UDP,
27472749
OPTION_ALLOW_INSECURE_TLS_FALLBACK,
27482750
];

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ pub mod fingerprint;
5959
pub use flexi_logger;
6060
pub mod stream;
6161
pub mod websocket;
62+
#[cfg(feature = "webrtc")]
63+
pub mod webrtc;
6264
#[cfg(any(target_os = "android", target_os = "ios"))]
6365
pub use rustls_platform_verifier;
6466
pub use stream::Stream;

src/socket_client.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(feature = "webrtc")]
2+
use crate::webrtc::{self, is_webrtc_endpoint};
13
use crate::{
24
config::{Config, NetworkType},
35
tcp::FramedStream,
@@ -129,6 +131,12 @@ pub async fn connect_tcp<
129131
target: T,
130132
ms_timeout: u64,
131133
) -> ResultType<crate::Stream> {
134+
#[cfg(feature = "webrtc")]
135+
if is_webrtc_endpoint(&target.to_string()) {
136+
return Ok(Stream::WebRTC(
137+
webrtc::WebRTCStream::new(&target.to_string(), false, ms_timeout).await?,
138+
));
139+
}
132140
let target_str = check_ws(&target.to_string());
133141
if is_ws_endpoint(&target_str) {
134142
return Ok(Stream::WebSocket(

src/stream.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use crate::{config, tcp, websocket, ResultType};
2+
#[cfg(feature = "webrtc")]
3+
use crate::webrtc;
24
use sodiumoxide::crypto::secretbox::Key;
35
use std::net::SocketAddr;
46
use tokio::net::TcpStream;
57

68
// support Websocket and tcp.
79
pub enum Stream {
10+
#[cfg(feature = "webrtc")]
11+
WebRTC(webrtc::WebRTCStream),
812
WebSocket(websocket::WsFramedStream),
913
Tcp(tcp::FramedStream),
1014
}
@@ -13,6 +17,8 @@ impl Stream {
1317
#[inline]
1418
pub fn set_send_timeout(&mut self, ms: u64) {
1519
match self {
20+
#[cfg(feature = "webrtc")]
21+
Stream::WebRTC(s) => s.set_send_timeout(ms),
1622
Stream::WebSocket(s) => s.set_send_timeout(ms),
1723
Stream::Tcp(s) => s.set_send_timeout(ms),
1824
}
@@ -21,6 +27,8 @@ impl Stream {
2127
#[inline]
2228
pub fn set_raw(&mut self) {
2329
match self {
30+
#[cfg(feature = "webrtc")]
31+
Stream::WebRTC(s) => s.set_raw(),
2432
Stream::WebSocket(s) => s.set_raw(),
2533
Stream::Tcp(s) => s.set_raw(),
2634
}
@@ -29,6 +37,8 @@ impl Stream {
2937
#[inline]
3038
pub async fn send_bytes(&mut self, bytes: bytes::Bytes) -> ResultType<()> {
3139
match self {
40+
#[cfg(feature = "webrtc")]
41+
Stream::WebRTC(s) => s.send_bytes(bytes).await,
3242
Stream::WebSocket(s) => s.send_bytes(bytes).await,
3343
Stream::Tcp(s) => s.send_bytes(bytes).await,
3444
}
@@ -37,6 +47,8 @@ impl Stream {
3747
#[inline]
3848
pub async fn send_raw(&mut self, bytes: Vec<u8>) -> ResultType<()> {
3949
match self {
50+
#[cfg(feature = "webrtc")]
51+
Stream::WebRTC(s) => s.send_raw(bytes).await,
4052
Stream::WebSocket(s) => s.send_raw(bytes).await,
4153
Stream::Tcp(s) => s.send_raw(bytes).await,
4254
}
@@ -45,6 +57,8 @@ impl Stream {
4557
#[inline]
4658
pub fn set_key(&mut self, key: Key) {
4759
match self {
60+
#[cfg(feature = "webrtc")]
61+
Stream::WebRTC(s) => s.set_key(key),
4862
Stream::WebSocket(s) => s.set_key(key),
4963
Stream::Tcp(s) => s.set_key(key),
5064
}
@@ -53,6 +67,8 @@ impl Stream {
5367
#[inline]
5468
pub fn is_secured(&self) -> bool {
5569
match self {
70+
#[cfg(feature = "webrtc")]
71+
Stream::WebRTC(s) => s.is_secured(),
5672
Stream::WebSocket(s) => s.is_secured(),
5773
Stream::Tcp(s) => s.is_secured(),
5874
}
@@ -64,6 +80,8 @@ impl Stream {
6480
timeout: u64,
6581
) -> Option<Result<bytes::BytesMut, std::io::Error>> {
6682
match self {
83+
#[cfg(feature = "webrtc")]
84+
Stream::WebRTC(s) => s.next_timeout(timeout).await,
6785
Stream::WebSocket(s) => s.next_timeout(timeout).await,
6886
Stream::Tcp(s) => s.next_timeout(timeout).await,
6987
}
@@ -87,6 +105,8 @@ impl Stream {
87105
#[inline]
88106
pub async fn send(&mut self, msg: &impl protobuf::Message) -> ResultType<()> {
89107
match self {
108+
#[cfg(feature = "webrtc")]
109+
Self::WebRTC(s) => s.send(msg).await,
90110
Self::WebSocket(ws) => ws.send(msg).await,
91111
Self::Tcp(tcp) => tcp.send(msg).await,
92112
}
@@ -96,6 +116,8 @@ impl Stream {
96116
#[inline]
97117
pub async fn next(&mut self) -> Option<Result<bytes::BytesMut, std::io::Error>> {
98118
match self {
119+
#[cfg(feature = "webrtc")]
120+
Self::WebRTC(s) => s.next().await,
99121
Self::WebSocket(ws) => ws.next().await,
100122
Self::Tcp(tcp) => tcp.next().await,
101123
}
@@ -104,6 +126,8 @@ impl Stream {
104126
#[inline]
105127
pub fn local_addr(&self) -> SocketAddr {
106128
match self {
129+
#[cfg(feature = "webrtc")]
130+
Self::WebRTC(s) => s.local_addr(),
107131
Self::WebSocket(ws) => ws.local_addr(),
108132
Self::Tcp(tcp) => tcp.local_addr(),
109133
}
@@ -113,4 +137,13 @@ impl Stream {
113137
pub fn from(stream: TcpStream, stream_addr: SocketAddr) -> Self {
114138
Self::Tcp(tcp::FramedStream::from(stream, stream_addr))
115139
}
140+
141+
#[inline]
142+
#[cfg(feature = "webrtc")]
143+
pub fn get_webrtc_stream(&self) -> Option<webrtc::WebRTCStream> {
144+
match self {
145+
Self::WebRTC(s) => Some(s.clone()),
146+
_ => None,
147+
}
148+
}
116149
}

0 commit comments

Comments
 (0)