Skip to content

Commit f94e377

Browse files
author
Yuji Sugiura
authored
[Examples] Add WebRTC DataChannel example (#2131)
* Add WebRTC DataChannel example * Add guide * Fix format * Use webpack to build example
1 parent 6b5f734 commit f94e377

File tree

9 files changed

+288
-0
lines changed

9 files changed

+288
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ members = [
8181
"examples/wasm2js",
8282
"examples/webaudio",
8383
"examples/webgl",
84+
"examples/webrtc_datachannel",
8485
"examples/websockets",
8586
"examples/webxr",
8687
"examples/without-a-bundler",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "webrtc_datachannel"
3+
version = "0.1.0"
4+
authors = ["The wasm-bindgen Developers"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["cdylib"]
9+
10+
[dependencies]
11+
wasm-bindgen = "0.2.62"
12+
js-sys = "0.3"
13+
wasm-bindgen-futures = "0.4.12"
14+
15+
[dependencies.web-sys]
16+
version = "0.3.22"
17+
features = [
18+
"MessageEvent",
19+
"RtcPeerConnection",
20+
"RtcSignalingState",
21+
"RtcSdpType",
22+
"RtcSessionDescriptionInit",
23+
"RtcPeerConnectionIceEvent",
24+
"RtcIceCandidate",
25+
"RtcDataChannel",
26+
"RtcDataChannelEvent",
27+
]

examples/webrtc_datachannel/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# WebRTC DataChannel Example
2+
3+
[View documentation for this example online][dox] or [View compiled example
4+
online][compiled]
5+
6+
[compiled]: https://rustwasm.github.io/wasm-bindgen/exbuild/webrtc_datachannel/
7+
[dox]: https://rustwasm.github.io/wasm-bindgen/examples/webrtc_datachannel.html
8+
9+
You can build the example locally with:
10+
11+
```
12+
$ npm run serve
13+
```
14+
15+
and then visiting http://localhost:8080 in a browser should run the example!
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>WebRTC DataChannel example</title>
6+
</head>
7+
<body>
8+
<p>Open DevTools and check the Console.</p>
9+
</body>
10+
</html>

examples/webrtc_datachannel/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
window.addEventListener('load', async () => {
2+
await import('./pkg');
3+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"scripts": {
3+
"build": "webpack",
4+
"serve": "webpack-dev-server"
5+
},
6+
"devDependencies": {
7+
"@wasm-tool/wasm-pack-plugin": "1.0.1",
8+
"text-encoding": "^0.7.0",
9+
"html-webpack-plugin": "^3.2.0",
10+
"webpack": "^4.29.4",
11+
"webpack-cli": "^3.1.1",
12+
"webpack-dev-server": "^3.1.0"
13+
}
14+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use js_sys::Reflect;
2+
use wasm_bindgen::prelude::*;
3+
use wasm_bindgen::JsCast;
4+
use wasm_bindgen_futures::JsFuture;
5+
use web_sys::{
6+
MessageEvent, RtcDataChannelEvent, RtcPeerConnection, RtcPeerConnectionIceEvent, RtcSdpType,
7+
RtcSessionDescriptionInit,
8+
};
9+
10+
macro_rules! console_log {
11+
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
12+
}
13+
macro_rules! console_warn {
14+
($($t:tt)*) => (warn(&format_args!($($t)*).to_string()))
15+
}
16+
17+
#[wasm_bindgen]
18+
extern "C" {
19+
#[wasm_bindgen(js_namespace = console)]
20+
fn log(s: &str);
21+
#[wasm_bindgen(js_namespace = console)]
22+
fn warn(s: &str);
23+
}
24+
25+
#[wasm_bindgen(start)]
26+
pub async fn start() -> Result<(), JsValue> {
27+
/*
28+
* Set up PeerConnections
29+
* pc1 <=> pc2
30+
*
31+
*/
32+
let pc1 = RtcPeerConnection::new()?;
33+
console_log!("pc1 created: state {:?}", pc1.signaling_state());
34+
let pc2 = RtcPeerConnection::new()?;
35+
console_log!("pc2 created: state {:?}", pc2.signaling_state());
36+
37+
/*
38+
* Create DataChannel on pc1 to negotiate
39+
* Message will be shonw here after connection established
40+
*
41+
*/
42+
let dc1 = pc1.create_data_channel("my-data-channel");
43+
console_log!("dc1 created: label {:?}", dc1.label());
44+
45+
let dc1_clone = dc1.clone();
46+
let onmessage_callback =
47+
Closure::wrap(
48+
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
49+
Some(message) => {
50+
console_warn!("{:?}", message);
51+
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
52+
}
53+
None => {}
54+
}) as Box<dyn FnMut(MessageEvent)>,
55+
);
56+
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
57+
onmessage_callback.forget();
58+
59+
/*
60+
* If negotiaion has done, this closure will be called
61+
*
62+
*/
63+
let ondatachannel_callback = Closure::wrap(Box::new(move |ev: RtcDataChannelEvent| {
64+
let dc2 = ev.channel();
65+
console_log!("pc2.ondatachannel!: {:?}", dc2.label());
66+
67+
let onmessage_callback =
68+
Closure::wrap(
69+
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
70+
Some(message) => console_warn!("{:?}", message),
71+
None => {}
72+
}) as Box<dyn FnMut(MessageEvent)>,
73+
);
74+
dc2.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
75+
onmessage_callback.forget();
76+
77+
dc2.send_with_str("Ping from pc2.dc!").unwrap();
78+
}) as Box<dyn FnMut(RtcDataChannelEvent)>);
79+
pc2.set_ondatachannel(Some(ondatachannel_callback.as_ref().unchecked_ref()));
80+
ondatachannel_callback.forget();
81+
82+
/*
83+
* Handle ICE candidate each other
84+
*
85+
*/
86+
let pc2_clone = pc2.clone();
87+
let onicecandidate_callback1 =
88+
Closure::wrap(
89+
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
90+
Some(candidate) => {
91+
console_log!("pc1.onicecandidate: {:#?}", candidate.candidate());
92+
let _ =
93+
pc2_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
94+
}
95+
None => {}
96+
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
97+
);
98+
pc1.set_onicecandidate(Some(onicecandidate_callback1.as_ref().unchecked_ref()));
99+
onicecandidate_callback1.forget();
100+
101+
let pc1_clone = pc1.clone();
102+
let onicecandidate_callback2 =
103+
Closure::wrap(
104+
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
105+
Some(candidate) => {
106+
console_log!("pc2.onicecandidate: {:#?}", candidate.candidate());
107+
let _ =
108+
pc1_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
109+
}
110+
None => {}
111+
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
112+
);
113+
pc2.set_onicecandidate(Some(onicecandidate_callback2.as_ref().unchecked_ref()));
114+
onicecandidate_callback2.forget();
115+
116+
/*
117+
* Send OFFER from pc1 to pc2
118+
*
119+
*/
120+
let offer = JsFuture::from(pc1.create_offer()).await?;
121+
let offer_sdp = Reflect::get(&offer, &JsValue::from_str("sdp"))?
122+
.as_string()
123+
.unwrap();
124+
console_log!("pc1: offer {:?}", offer_sdp);
125+
126+
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
127+
offer_obj.sdp(&offer_sdp);
128+
let sld_promise = pc1.set_local_description(&offer_obj);
129+
JsFuture::from(sld_promise).await?;
130+
console_log!("pc1: state {:?}", pc1.signaling_state());
131+
132+
/*
133+
* Receive OFFER from pc1
134+
* Create and send ANSWER from pc2 to pc1
135+
*
136+
*/
137+
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
138+
offer_obj.sdp(&offer_sdp);
139+
let srd_promise = pc2.set_remote_description(&offer_obj);
140+
JsFuture::from(srd_promise).await?;
141+
console_log!("pc2: state {:?}", pc2.signaling_state());
142+
143+
let answer = JsFuture::from(pc2.create_answer()).await?;
144+
let answer_sdp = Reflect::get(&answer, &JsValue::from_str("sdp"))?
145+
.as_string()
146+
.unwrap();
147+
console_log!("pc2: answer {:?}", answer_sdp);
148+
149+
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
150+
answer_obj.sdp(&answer_sdp);
151+
let sld_promise = pc2.set_local_description(&answer_obj);
152+
JsFuture::from(sld_promise).await?;
153+
console_log!("pc2: state {:?}", pc2.signaling_state());
154+
155+
/*
156+
* Receive ANSWER from pc2
157+
*
158+
*/
159+
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
160+
answer_obj.sdp(&answer_sdp);
161+
let srd_promise = pc1.set_remote_description(&answer_obj);
162+
JsFuture::from(srd_promise).await?;
163+
console_log!("pc1: state {:?}", pc1.signaling_state());
164+
165+
Ok(())
166+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const path = require('path');
2+
const HtmlWebpackPlugin = require('html-webpack-plugin');
3+
const webpack = require('webpack');
4+
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
5+
6+
module.exports = {
7+
entry: './index.js',
8+
output: {
9+
path: path.resolve(__dirname, 'dist'),
10+
filename: 'index.js',
11+
},
12+
plugins: [
13+
new HtmlWebpackPlugin({
14+
template: 'index.html'
15+
}),
16+
new WasmPackPlugin({
17+
crateDirectory: path.resolve(__dirname, ".")
18+
}),
19+
// Have this example work in Edge which doesn't ship `TextEncoder` or
20+
// `TextDecoder` at this time.
21+
new webpack.ProvidePlugin({
22+
TextDecoder: ['text-encoding', 'TextDecoder'],
23+
TextEncoder: ['text-encoding', 'TextEncoder']
24+
})
25+
],
26+
mode: 'development'
27+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# WebRTC DataChannel Example
2+
3+
[View full source code][code] or [view the compiled example online][online]
4+
5+
[online]: https://rustwasm.github.io/wasm-bindgen/exbuild/webrtc_datachannel/
6+
[code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webrtc_datachannel/
7+
8+
This example creates 2 peer connections and 2 data channels in single browser tab.
9+
Send ping/pong between `peer1.dc` and `peer2.dc`.
10+
11+
## `Cargo.toml`
12+
13+
The `Cargo.toml` enables features necessary to use WebRTC DataChannel and its negotiation.
14+
15+
```toml
16+
{{#include ../../../examples/webrtc_datachannel/Cargo.toml}}
17+
```
18+
19+
## `src/lib.rs`
20+
21+
The Rust code connects WebRTC data channel.
22+
23+
```rust
24+
{{#include ../../../examples/webrtc_datachannel/src/lib.rs}}
25+
```

0 commit comments

Comments
 (0)