use callbacks on internal events (#100)

This commit is contained in:
Théo Monnom
2023-06-29 22:52:43 +02:00
committed by GitHub
parent efc334af8e
commit c7c82cc693
33 changed files with 1522 additions and 1447 deletions
+98 -39
View File
@@ -9,37 +9,38 @@ use futures::StreamExt;
use livekit::prelude::*;
use livekit::webrtc::audio_stream::native::NativeAudioStream;
use livekit::SimulateScenario;
use parking_lot::deadlock;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::thread;
use std::time::Duration;
use tokio::sync::Mutex as AsyncMutex;
use tokio::sync::{mpsc, oneshot};
// Useful default constants for developing
const DEFAULT_URL: &str = "ws://localhost:7880";
const DEFAULT_TOKEN : &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY";
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTM0MzcsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJ3ZWIiLCJuYmYiOjE2NzI2MTM0MzcsInN1YiI6IndlYiIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.DFTXt60n1kzGq4cSuOhbFBTQW2nd3rlcXKQ54sXsP8s
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
window::{WindowBuilder, WindowId},
};
struct Session {
struct SessionHandle {
room: Arc<Room>,
logo_track: LogoTrack,
sine_track: SineTrack,
logo_track: Arc<AsyncMutex<LogoTrack>>,
sine_track: Arc<AsyncMutex<SineTrack>>,
close_tx: oneshot::Sender<()>,
handle: tokio::task::JoinHandle<()>,
task_handle: tokio::task::JoinHandle<()>,
}
struct AppState {
session: Mutex<Option<Session>>,
session: Mutex<Option<SessionHandle>>,
connecting: AtomicBool,
}
@@ -54,11 +55,14 @@ struct App {
cmd_tx: mpsc::UnboundedSender<AsyncCmd>,
cmd_rx: mpsc::UnboundedReceiver<UiCmd>,
// UI State
// Ui State
lk_url: String,
lk_token: String,
connection_failure: Option<String>,
room_state: ConnectionState,
// Log events
events: Vec<String>,
}
pub fn run(rt: tokio::runtime::Runtime) {
@@ -97,8 +101,26 @@ pub fn run(rt: tokio::runtime::Runtime) {
lk_token: DEFAULT_TOKEN.to_owned(),
connection_failure: None,
room_state: ConnectionState::Connected,
events: Vec::new(),
};
// Create a background thread which checks for deadlocks every 10s
thread::spawn(move || loop {
thread::sleep(Duration::from_secs(10));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
log::error!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
log::error!("Deadlock #{}", i);
for t in threads {
log::error!("Thread Id {:#?}: \n{:#?}", t.thread_id(), t.backtrace());
}
}
});
// Async event loop
tokio::spawn(async move {
while let Some(event) = async_cmd_rx.recv().await {
@@ -112,19 +134,19 @@ pub fn run(rt: tokio::runtime::Runtime) {
let (close_tx, close_rx) = oneshot::channel();
let logo_track = LogoTrack::new(room.clone());
let sine_track = SineTrack::new(room.clone());
let handle = tokio::spawn(room_task(
let task_handle = tokio::spawn(room_task(
state.clone(),
room_events,
close_rx,
ui_cmd_tx.clone(),
));
*state.session.lock() = Some(Session {
*state.session.lock() = Some(SessionHandle {
room,
logo_track,
sine_track,
logo_track: Arc::new(AsyncMutex::new(logo_track)),
sine_track: Arc::new(AsyncMutex::new(sine_track)),
close_tx,
handle,
task_handle,
});
let _ = ui_cmd_tx.send(UiCmd::ConnectResult { result: Ok(()) });
@@ -135,20 +157,37 @@ pub fn run(rt: tokio::runtime::Runtime) {
state.connecting.store(false, Ordering::SeqCst);
}
AsyncCmd::RoomDisconnect => {
if let Some(session) = state.session.lock().take() {
let _ = session.room.close().await;
let _ = session.close_tx.send(());
let _ = session.handle.await;
let handle = state.session.lock().take();
if let Some(handle) = handle {
let _ = handle.room.close().await;
let _ = handle.close_tx.send(());
let _ = handle.task_handle.await;
}
}
AsyncCmd::SimulateScenario { scenario } => {
if let Some(session) = state.session.lock().as_ref() {
let _ = session.room.simulate_scenario(scenario).await;
let room = {
state
.session
.lock()
.as_ref()
.map(|handle| handle.room.clone())
};
if let Some(room) = room {
let _ = room.simulate_scenario(scenario).await;
}
}
AsyncCmd::ToggleLogo => {
if let Some(session) = state.session.lock().as_mut() {
let logo_track = &mut session.logo_track;
let logo_track = {
state
.session
.lock()
.as_ref()
.map(|handle| handle.logo_track.clone())
};
if let Some(logo_track) = logo_track {
let mut logo_track = logo_track.lock().await;
if !logo_track.is_published() {
logo_track.publish().await.unwrap();
} else {
@@ -157,8 +196,16 @@ pub fn run(rt: tokio::runtime::Runtime) {
}
}
AsyncCmd::ToggleSine => {
if let Some(session) = state.session.lock().as_mut() {
let sine_track = &mut session.sine_track;
let sine_track = {
state
.session
.lock()
.as_ref()
.map(|handle| handle.sine_track.clone())
};
if let Some(sine_track) = sine_track {
let mut sine_track = sine_track.lock().await;
if !sine_track.is_published() {
sine_track.publish().await.unwrap();
} else {
@@ -209,6 +256,8 @@ impl App {
}
}
UiCmd::RoomEvent { event } => {
self.events.push(format!("{:?}", event));
match event {
RoomEvent::TrackSubscribed {
track, participant, ..
@@ -229,7 +278,7 @@ impl App {
NativeAudioStream::new(audio_track.rtc_track());
while let Some(_frame) = stream.next().await {
// Received audio frames
// TODO(theomonnom): Play audio using the libwebrtc ADM playout devices
}
});
}
@@ -295,12 +344,10 @@ impl App {
fn ui(&mut self, ui: &mut egui::Ui) {
egui::TopBottomPanel::top("top_panel").show(ui.ctx(), |ui| {
egui::menu::bar(ui, |ui| {
ui.menu_button("Tools", |ui| {
if ui.button("Logs").clicked() {}
if ui.button("Profiler").clicked() {}
if ui.button("WebRTC Stats").clicked() {}
if ui.button("Events").clicked() {}
});
ui.menu_button(
"Tools",
|ui| if ui.button("WebRTC Stats (TODO)").clicked() {},
);
ui.menu_button("Simulate", |ui| {
if ui.button("SignalReconnect").clicked() {
let _ = self.cmd_tx.send(AsyncCmd::SimulateScenario {
@@ -399,21 +446,33 @@ impl App {
{
// Room Info
if let Some(session) = self.state.session.lock().as_ref() {
ui.label(format!("Name: {}", session.room.name()));
ui.label(format!("SID: {}", session.room.sid()));
if let Some(handle) = self.state.session.lock().as_ref() {
ui.label(format!("Name: {}", handle.room.name()));
ui.label(format!("SID: {}", handle.room.sid()));
ui.label(format!(
"ConnectionState: {:?}",
session.room.connection_state()
handle.room.connection_state()
));
ui.label(format!(
"ParticipantCount: {:?}",
session.room.participants().len() + 1
handle.room.participants().len() + 1
));
}
}
});
egui::TopBottomPanel::bottom("bottom_panel")
.default_height(128.0)
.show(ui.ctx(), |ui| {
ui.label("Events");
egui::ScrollArea::vertical().show(ui, |ui| {
for event in &self.events {
ui.label(event);
}
});
});
egui::CentralPanel::default().show(ui.ctx(), |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
VideoGrid::new("default_grid")
@@ -455,8 +514,8 @@ impl App {
}
let name =
self.state.session.lock().as_ref().and_then(|session| {
session
self.state.session.lock().as_ref().and_then(|handle| {
handle
.room
.participants()
.get(participant_sid)
+1 -1
View File
@@ -72,7 +72,7 @@ impl LogoTrack {
.publish_track(
LocalTrack::Video(track.clone()),
TrackPublishOptions {
source: TrackSource::Camera,
source: TrackSource::Camera, // Using camera so it is shown on everyone's screen (Unknown is sometimes not shown)
..Default::default()
},
)