feat: ✨ webrtc gui interaction
This commit is contained in:
parent
cde03367c3
commit
8391ef31ba
2 changed files with 66 additions and 64 deletions
|
@ -1,40 +1,41 @@
|
||||||
use leptos::{
|
use leptos::{
|
||||||
IntoView, ev,
|
IntoView, ev,
|
||||||
html::{ElementChild, button},
|
html::{ElementChild, button},
|
||||||
logging::log,
|
|
||||||
prelude::{OnAttribute, Read, Show, ShowProps, ToChildren},
|
prelude::{OnAttribute, Read, Show, ShowProps, ToChildren},
|
||||||
server::LocalResource,
|
server::LocalResource,
|
||||||
task::spawn_local,
|
|
||||||
};
|
};
|
||||||
use wasm_bindgen_futures::JsFuture;
|
|
||||||
use web_sys::HtmlAudioElement;
|
|
||||||
|
|
||||||
use crate::media::audio;
|
use crate::{media::audio, webrtc::WebRTC};
|
||||||
|
|
||||||
pub fn app() -> impl IntoView {
|
pub fn app() -> impl IntoView {
|
||||||
let audio_stream = LocalResource::new(|| audio());
|
let audio_stream = LocalResource::new(|| audio());
|
||||||
let props = ShowProps::builder()
|
let offer_props = ShowProps::builder()
|
||||||
.when(move || audio_stream.read().is_some())
|
.when(move || audio_stream.read().is_some())
|
||||||
.children(ToChildren::to_children(move || {
|
.children(ToChildren::to_children(move || {
|
||||||
let audio_element = HtmlAudioElement::new().unwrap();
|
|
||||||
let audio_stream = audio_stream.read();
|
|
||||||
let audio_stream = audio_stream.as_deref();
|
|
||||||
audio_element.set_src_object(audio_stream);
|
|
||||||
button()
|
button()
|
||||||
.on(ev::click, move |_| match audio_element.play() {
|
.on(ev::click, move |_| {
|
||||||
Ok(audio_element_play_promise) => {
|
WebRTC::init(Some(audio_stream), None, None);
|
||||||
log!("{}", "Play will");
|
LocalResource::new(|| WebRTC::offer());
|
||||||
spawn_local(async move {
|
|
||||||
JsFuture::from(audio_element_play_promise).await.ok();
|
|
||||||
});
|
|
||||||
log!("{}", "Play must");
|
|
||||||
}
|
|
||||||
Err(err_val) => log!("{:#?}", err_val),
|
|
||||||
})
|
})
|
||||||
.child("Happy Button")
|
.child("Offer")
|
||||||
.into_view()
|
.into_view()
|
||||||
}))
|
}))
|
||||||
.fallback(|| button().child("Sad Button"))
|
.fallback(|| button().child("Sad Offer Button"))
|
||||||
.build();
|
.build();
|
||||||
Show(props)
|
|
||||||
|
let answer_props = ShowProps::builder()
|
||||||
|
.when(move || audio_stream.read().is_some())
|
||||||
|
.children(ToChildren::to_children(move || {
|
||||||
|
button()
|
||||||
|
.on(ev::click, move |_| {
|
||||||
|
WebRTC::init(Some(audio_stream), None, None);
|
||||||
|
LocalResource::new(|| WebRTC::answer());
|
||||||
|
})
|
||||||
|
.child("Answer")
|
||||||
|
.into_view()
|
||||||
|
}))
|
||||||
|
.fallback(|| button().child("Sad Answer Button"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
(Show(offer_props), Show(answer_props))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use leptos::logging::log;
|
use leptos::{logging::log, prelude::Get, server::LocalResource};
|
||||||
use protocol::Error;
|
use protocol::Error;
|
||||||
use wasm_bindgen_futures::{JsFuture, spawn_local};
|
use wasm_bindgen_futures::{JsFuture, spawn_local};
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
|
@ -14,14 +14,11 @@ use crate::signal::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct WebRTC {
|
pub struct WebRTC {
|
||||||
audio_stream: Option<MediaStream>,
|
|
||||||
video_stream: Option<MediaStream>,
|
|
||||||
screen_stream: Option<MediaStream>,
|
|
||||||
peer_connection: RtcPeerConnection,
|
peer_connection: RtcPeerConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static WEBRTC:WebRTC = WebRTC::new().unwrap();
|
pub static WEBRTC:WebRTC = WebRTC::new().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebRTC {
|
impl WebRTC {
|
||||||
|
@ -51,27 +48,17 @@ impl WebRTC {
|
||||||
peer_connection.set_onicecandidate(Some(on_ice_candidate.as_ref().unchecked_ref()));
|
peer_connection.set_onicecandidate(Some(on_ice_candidate.as_ref().unchecked_ref()));
|
||||||
on_ice_candidate.forget();
|
on_ice_candidate.forget();
|
||||||
|
|
||||||
let webrtc = Self {
|
let webrtc = Self { peer_connection };
|
||||||
audio_stream: None,
|
|
||||||
video_stream: None,
|
|
||||||
screen_stream: None,
|
|
||||||
peer_connection,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(webrtc)
|
Ok(webrtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init(
|
pub fn init(
|
||||||
&mut self,
|
audio_stream: Option<LocalResource<MediaStream>>,
|
||||||
audio_stream: Option<MediaStream>,
|
video_stream: Option<LocalResource<MediaStream>>,
|
||||||
video_stream: Option<MediaStream>,
|
screen_stream: Option<LocalResource<MediaStream>>,
|
||||||
screen_stream: Option<MediaStream>,
|
|
||||||
) {
|
) {
|
||||||
self.audio_stream = audio_stream;
|
Self::add_streams(audio_stream, video_stream, screen_stream);
|
||||||
self.video_stream = video_stream;
|
|
||||||
self.screen_stream = screen_stream;
|
|
||||||
|
|
||||||
self.add_streams();
|
|
||||||
|
|
||||||
spawn_local(async {
|
spawn_local(async {
|
||||||
while let Ok(received_ice_candidate) = receive_ice_candidate() {
|
while let Ok(received_ice_candidate) = receive_ice_candidate() {
|
||||||
|
@ -98,20 +85,32 @@ impl WebRTC {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_streams(&mut self) {
|
fn add_streams(
|
||||||
if let Some(audio_stream) = &self.audio_stream {
|
audio_stream: Option<LocalResource<MediaStream>>,
|
||||||
self.peer_connection.add_stream(audio_stream);
|
video_stream: Option<LocalResource<MediaStream>>,
|
||||||
|
screen_stream: Option<LocalResource<MediaStream>>,
|
||||||
|
) {
|
||||||
|
WEBRTC.with(|webrtc| {
|
||||||
|
if let Some(audio_stream) = audio_stream {
|
||||||
|
if let Some(audio_stream) = audio_stream.get() {
|
||||||
|
webrtc.peer_connection.add_stream(&audio_stream);
|
||||||
}
|
}
|
||||||
if let Some(video_stream) = &self.video_stream {
|
|
||||||
self.peer_connection.add_stream(video_stream);
|
|
||||||
}
|
}
|
||||||
if let Some(screen_stream) = &self.screen_stream {
|
if let Some(video_stream) = video_stream {
|
||||||
self.peer_connection.add_stream(screen_stream);
|
if let Some(video_stream) = video_stream.get() {
|
||||||
|
webrtc.peer_connection.add_stream(&video_stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(screen_stream) = screen_stream {
|
||||||
|
if let Some(screen_stream) = screen_stream.get() {
|
||||||
|
webrtc.peer_connection.add_stream(&screen_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async fn offer(&mut self) -> Result<(), Error> {
|
pub async fn offer() -> Result<(), Error> {
|
||||||
let offer_promise = self.peer_connection.create_offer();
|
let offer_promise = WEBRTC.with(|webrtc| webrtc.peer_connection.create_offer());
|
||||||
match JsFuture::from(offer_promise)
|
match JsFuture::from(offer_promise)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::WebRTCOffer)
|
.map_err(|_| Error::WebRTCOffer)
|
||||||
|
@ -127,8 +126,8 @@ impl WebRTC {
|
||||||
Ok(offer_session_description_protocol) => {
|
Ok(offer_session_description_protocol) => {
|
||||||
let offer = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
let offer = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||||||
offer.set_sdp(&offer_session_description_protocol);
|
offer.set_sdp(&offer_session_description_protocol);
|
||||||
let set_local_description_promise =
|
let set_local_description_promise = WEBRTC
|
||||||
self.peer_connection.set_local_description(&offer);
|
.with(|webrtc| webrtc.peer_connection.set_local_description(&offer));
|
||||||
|
|
||||||
JsFuture::from(set_local_description_promise)
|
JsFuture::from(set_local_description_promise)
|
||||||
.await
|
.await
|
||||||
|
@ -140,8 +139,9 @@ impl WebRTC {
|
||||||
let answer = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
let answer = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||||||
answer.set_sdp(&received_answer.get_data());
|
answer.set_sdp(&received_answer.get_data());
|
||||||
|
|
||||||
let set_remote_description_promise =
|
let set_remote_description_promise = WEBRTC.with(|webrtc| {
|
||||||
self.peer_connection.set_remote_description(&answer);
|
webrtc.peer_connection.set_remote_description(&answer)
|
||||||
|
});
|
||||||
JsFuture::from(set_remote_description_promise)
|
JsFuture::from(set_remote_description_promise)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::WebRTCSetRemoteDescription)?;
|
.map_err(|_| Error::WebRTCSetRemoteDescription)?;
|
||||||
|
@ -164,18 +164,18 @@ impl WebRTC {
|
||||||
return Err(Error::WebRTCOffer);
|
return Err(Error::WebRTCOffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn answer(&mut self) -> Result<(), Error> {
|
pub async fn answer() -> Result<(), Error> {
|
||||||
if let Ok(received_offer) = receive_offer() {
|
if let Ok(received_offer) = receive_offer() {
|
||||||
let offer = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
let offer = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||||||
offer.set_sdp(&received_offer.get_data());
|
offer.set_sdp(&received_offer.get_data());
|
||||||
|
|
||||||
let set_remote_description_promise =
|
let set_remote_description_promise =
|
||||||
self.peer_connection.set_remote_description(&offer);
|
WEBRTC.with(|webrtc| webrtc.peer_connection.set_remote_description(&offer));
|
||||||
JsFuture::from(set_remote_description_promise)
|
JsFuture::from(set_remote_description_promise)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::WebRTCSetRemoteDescription)?;
|
.map_err(|_| Error::WebRTCSetRemoteDescription)?;
|
||||||
|
|
||||||
let answer_promise = self.peer_connection.create_answer();
|
let answer_promise = WEBRTC.with(|webrtc| webrtc.peer_connection.create_answer());
|
||||||
match JsFuture::from(answer_promise)
|
match JsFuture::from(answer_promise)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::WebRTCAnswer)
|
.map_err(|_| Error::WebRTCAnswer)
|
||||||
|
@ -192,8 +192,9 @@ impl WebRTC {
|
||||||
let answer = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
let answer = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||||||
answer.set_sdp(&answer_session_description_protocol);
|
answer.set_sdp(&answer_session_description_protocol);
|
||||||
|
|
||||||
let set_local_description_promise =
|
let set_local_description_promise = WEBRTC.with(|webrtc| {
|
||||||
self.peer_connection.set_local_description(&answer);
|
webrtc.peer_connection.set_local_description(&answer)
|
||||||
|
});
|
||||||
JsFuture::from(set_local_description_promise)
|
JsFuture::from(set_local_description_promise)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::WebRTCSetLocalDescription)?;
|
.map_err(|_| Error::WebRTCSetLocalDescription)?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue