mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-15 17:50:51 +00:00
[Examples] Add WebRTC DataChannel example (#2131)
* Add WebRTC DataChannel example * Add guide * Fix format * Use webpack to build example
This commit is contained in:
parent
6b5f7342a7
commit
f94e3772bb
@ -81,6 +81,7 @@ members = [
|
|||||||
"examples/wasm2js",
|
"examples/wasm2js",
|
||||||
"examples/webaudio",
|
"examples/webaudio",
|
||||||
"examples/webgl",
|
"examples/webgl",
|
||||||
|
"examples/webrtc_datachannel",
|
||||||
"examples/websockets",
|
"examples/websockets",
|
||||||
"examples/webxr",
|
"examples/webxr",
|
||||||
"examples/without-a-bundler",
|
"examples/without-a-bundler",
|
||||||
|
27
examples/webrtc_datachannel/Cargo.toml
Normal file
27
examples/webrtc_datachannel/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "webrtc_datachannel"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The wasm-bindgen Developers"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.62"
|
||||||
|
js-sys = "0.3"
|
||||||
|
wasm-bindgen-futures = "0.4.12"
|
||||||
|
|
||||||
|
[dependencies.web-sys]
|
||||||
|
version = "0.3.22"
|
||||||
|
features = [
|
||||||
|
"MessageEvent",
|
||||||
|
"RtcPeerConnection",
|
||||||
|
"RtcSignalingState",
|
||||||
|
"RtcSdpType",
|
||||||
|
"RtcSessionDescriptionInit",
|
||||||
|
"RtcPeerConnectionIceEvent",
|
||||||
|
"RtcIceCandidate",
|
||||||
|
"RtcDataChannel",
|
||||||
|
"RtcDataChannelEvent",
|
||||||
|
]
|
15
examples/webrtc_datachannel/README.md
Normal file
15
examples/webrtc_datachannel/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# WebRTC DataChannel Example
|
||||||
|
|
||||||
|
[View documentation for this example online][dox] or [View compiled example
|
||||||
|
online][compiled]
|
||||||
|
|
||||||
|
[compiled]: https://rustwasm.github.io/wasm-bindgen/exbuild/webrtc_datachannel/
|
||||||
|
[dox]: https://rustwasm.github.io/wasm-bindgen/examples/webrtc_datachannel.html
|
||||||
|
|
||||||
|
You can build the example locally with:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
and then visiting http://localhost:8080 in a browser should run the example!
|
10
examples/webrtc_datachannel/index.html
Normal file
10
examples/webrtc_datachannel/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>WebRTC DataChannel example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Open DevTools and check the Console.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
examples/webrtc_datachannel/index.js
Normal file
3
examples/webrtc_datachannel/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
window.addEventListener('load', async () => {
|
||||||
|
await import('./pkg');
|
||||||
|
});
|
14
examples/webrtc_datachannel/package.json
Normal file
14
examples/webrtc_datachannel/package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack",
|
||||||
|
"serve": "webpack-dev-server"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@wasm-tool/wasm-pack-plugin": "1.0.1",
|
||||||
|
"text-encoding": "^0.7.0",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"webpack": "^4.29.4",
|
||||||
|
"webpack-cli": "^3.1.1",
|
||||||
|
"webpack-dev-server": "^3.1.0"
|
||||||
|
}
|
||||||
|
}
|
166
examples/webrtc_datachannel/src/lib.rs
Normal file
166
examples/webrtc_datachannel/src/lib.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
use js_sys::Reflect;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
use web_sys::{
|
||||||
|
MessageEvent, RtcDataChannelEvent, RtcPeerConnection, RtcPeerConnectionIceEvent, RtcSdpType,
|
||||||
|
RtcSessionDescriptionInit,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! console_log {
|
||||||
|
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||||
|
}
|
||||||
|
macro_rules! console_warn {
|
||||||
|
($($t:tt)*) => (warn(&format_args!($($t)*).to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn warn(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
pub async fn start() -> Result<(), JsValue> {
|
||||||
|
/*
|
||||||
|
* Set up PeerConnections
|
||||||
|
* pc1 <=> pc2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let pc1 = RtcPeerConnection::new()?;
|
||||||
|
console_log!("pc1 created: state {:?}", pc1.signaling_state());
|
||||||
|
let pc2 = RtcPeerConnection::new()?;
|
||||||
|
console_log!("pc2 created: state {:?}", pc2.signaling_state());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create DataChannel on pc1 to negotiate
|
||||||
|
* Message will be shonw here after connection established
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let dc1 = pc1.create_data_channel("my-data-channel");
|
||||||
|
console_log!("dc1 created: label {:?}", dc1.label());
|
||||||
|
|
||||||
|
let dc1_clone = dc1.clone();
|
||||||
|
let onmessage_callback =
|
||||||
|
Closure::wrap(
|
||||||
|
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
|
||||||
|
Some(message) => {
|
||||||
|
console_warn!("{:?}", message);
|
||||||
|
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}) as Box<dyn FnMut(MessageEvent)>,
|
||||||
|
);
|
||||||
|
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||||
|
onmessage_callback.forget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If negotiaion has done, this closure will be called
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let ondatachannel_callback = Closure::wrap(Box::new(move |ev: RtcDataChannelEvent| {
|
||||||
|
let dc2 = ev.channel();
|
||||||
|
console_log!("pc2.ondatachannel!: {:?}", dc2.label());
|
||||||
|
|
||||||
|
let onmessage_callback =
|
||||||
|
Closure::wrap(
|
||||||
|
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
|
||||||
|
Some(message) => console_warn!("{:?}", message),
|
||||||
|
None => {}
|
||||||
|
}) as Box<dyn FnMut(MessageEvent)>,
|
||||||
|
);
|
||||||
|
dc2.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||||
|
onmessage_callback.forget();
|
||||||
|
|
||||||
|
dc2.send_with_str("Ping from pc2.dc!").unwrap();
|
||||||
|
}) as Box<dyn FnMut(RtcDataChannelEvent)>);
|
||||||
|
pc2.set_ondatachannel(Some(ondatachannel_callback.as_ref().unchecked_ref()));
|
||||||
|
ondatachannel_callback.forget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle ICE candidate each other
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let pc2_clone = pc2.clone();
|
||||||
|
let onicecandidate_callback1 =
|
||||||
|
Closure::wrap(
|
||||||
|
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
|
||||||
|
Some(candidate) => {
|
||||||
|
console_log!("pc1.onicecandidate: {:#?}", candidate.candidate());
|
||||||
|
let _ =
|
||||||
|
pc2_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
|
||||||
|
);
|
||||||
|
pc1.set_onicecandidate(Some(onicecandidate_callback1.as_ref().unchecked_ref()));
|
||||||
|
onicecandidate_callback1.forget();
|
||||||
|
|
||||||
|
let pc1_clone = pc1.clone();
|
||||||
|
let onicecandidate_callback2 =
|
||||||
|
Closure::wrap(
|
||||||
|
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
|
||||||
|
Some(candidate) => {
|
||||||
|
console_log!("pc2.onicecandidate: {:#?}", candidate.candidate());
|
||||||
|
let _ =
|
||||||
|
pc1_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
|
||||||
|
);
|
||||||
|
pc2.set_onicecandidate(Some(onicecandidate_callback2.as_ref().unchecked_ref()));
|
||||||
|
onicecandidate_callback2.forget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send OFFER from pc1 to pc2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let offer = JsFuture::from(pc1.create_offer()).await?;
|
||||||
|
let offer_sdp = Reflect::get(&offer, &JsValue::from_str("sdp"))?
|
||||||
|
.as_string()
|
||||||
|
.unwrap();
|
||||||
|
console_log!("pc1: offer {:?}", offer_sdp);
|
||||||
|
|
||||||
|
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||||||
|
offer_obj.sdp(&offer_sdp);
|
||||||
|
let sld_promise = pc1.set_local_description(&offer_obj);
|
||||||
|
JsFuture::from(sld_promise).await?;
|
||||||
|
console_log!("pc1: state {:?}", pc1.signaling_state());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive OFFER from pc1
|
||||||
|
* Create and send ANSWER from pc2 to pc1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||||||
|
offer_obj.sdp(&offer_sdp);
|
||||||
|
let srd_promise = pc2.set_remote_description(&offer_obj);
|
||||||
|
JsFuture::from(srd_promise).await?;
|
||||||
|
console_log!("pc2: state {:?}", pc2.signaling_state());
|
||||||
|
|
||||||
|
let answer = JsFuture::from(pc2.create_answer()).await?;
|
||||||
|
let answer_sdp = Reflect::get(&answer, &JsValue::from_str("sdp"))?
|
||||||
|
.as_string()
|
||||||
|
.unwrap();
|
||||||
|
console_log!("pc2: answer {:?}", answer_sdp);
|
||||||
|
|
||||||
|
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||||||
|
answer_obj.sdp(&answer_sdp);
|
||||||
|
let sld_promise = pc2.set_local_description(&answer_obj);
|
||||||
|
JsFuture::from(sld_promise).await?;
|
||||||
|
console_log!("pc2: state {:?}", pc2.signaling_state());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive ANSWER from pc2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||||||
|
answer_obj.sdp(&answer_sdp);
|
||||||
|
let srd_promise = pc1.set_remote_description(&answer_obj);
|
||||||
|
JsFuture::from(srd_promise).await?;
|
||||||
|
console_log!("pc1: state {:?}", pc1.signaling_state());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
27
examples/webrtc_datachannel/webpack.config.js
Normal file
27
examples/webrtc_datachannel/webpack.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './index.js',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'index.js',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'index.html'
|
||||||
|
}),
|
||||||
|
new WasmPackPlugin({
|
||||||
|
crateDirectory: path.resolve(__dirname, ".")
|
||||||
|
}),
|
||||||
|
// Have this example work in Edge which doesn't ship `TextEncoder` or
|
||||||
|
// `TextDecoder` at this time.
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
TextDecoder: ['text-encoding', 'TextDecoder'],
|
||||||
|
TextEncoder: ['text-encoding', 'TextEncoder']
|
||||||
|
})
|
||||||
|
],
|
||||||
|
mode: 'development'
|
||||||
|
};
|
25
guide/src/examples/webrtc_datachannel.md
Normal file
25
guide/src/examples/webrtc_datachannel.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# WebRTC DataChannel Example
|
||||||
|
|
||||||
|
[View full source code][code] or [view the compiled example online][online]
|
||||||
|
|
||||||
|
[online]: https://rustwasm.github.io/wasm-bindgen/exbuild/webrtc_datachannel/
|
||||||
|
[code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webrtc_datachannel/
|
||||||
|
|
||||||
|
This example creates 2 peer connections and 2 data channels in single browser tab.
|
||||||
|
Send ping/pong between `peer1.dc` and `peer2.dc`.
|
||||||
|
|
||||||
|
## `Cargo.toml`
|
||||||
|
|
||||||
|
The `Cargo.toml` enables features necessary to use WebRTC DataChannel and its negotiation.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
{{#include ../../../examples/webrtc_datachannel/Cargo.toml}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `src/lib.rs`
|
||||||
|
|
||||||
|
The Rust code connects WebRTC data channel.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#include ../../../examples/webrtc_datachannel/src/lib.rs}}
|
||||||
|
```
|
Loading…
x
Reference in New Issue
Block a user