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
+27 -6
View File
@@ -641,7 +641,8 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "ecolor"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e479a7fa3f23d4e794f8b2f8b3568dd4e47886ad1b12c9c095e141cb591eb63"
dependencies = [
"bytemuck",
]
@@ -649,7 +650,8 @@ dependencies = [
[[package]]
name = "egui"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3aef8ec3ae1b772f340170c65bf27d5b8c28f543a0116c844d2ac08d01123e7"
dependencies = [
"ahash 0.8.3",
"epaint",
@@ -660,7 +662,8 @@ dependencies = [
[[package]]
name = "egui-wgpu"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33caaedd8283779c787298af23d8754a7e88421ff32e89ad0040c855fc0b0224"
dependencies = [
"bytemuck",
"epaint",
@@ -674,7 +677,8 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a49155fd4a0a4fb21224407a91de0030847972ef90fc64edb63621caea61cb2"
dependencies = [
"arboard",
"egui",
@@ -695,7 +699,8 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "emath"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3857d743a6e0741cdd60b622a74c7a36ea75f5f8f11b793b41d905d2c9721a4b"
dependencies = [
"bytemuck",
]
@@ -725,7 +730,8 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.22.0"
source = "git+https://github.com/emilk/egui#307565efa55158cfa6b82d2e8fdc4c4914b954ed"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09333964d4d57f40a85338ba3ca5ed4716070ab184dcfed966b35491c5c64f3b"
dependencies = [
"ab_glyph",
"ahash 0.8.3",
@@ -2016,10 +2022,13 @@ version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"backtrace",
"cfg-if",
"libc",
"petgraph",
"redox_syscall 0.2.16",
"smallvec",
"thread-id",
"windows-sys 0.45.0",
]
@@ -2753,6 +2762,17 @@ dependencies = [
"syn 2.0.16",
]
[[package]]
name = "thread-id"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee93aa2b8331c0fec9091548843f2c90019571814057da3b783f9de09349d73"
dependencies = [
"libc",
"redox_syscall 0.2.16",
"winapi",
]
[[package]]
name = "thread_local"
version = "1.1.7"
@@ -3420,6 +3440,7 @@ dependencies = [
"futures",
"image",
"livekit",
"log",
"parking_lot",
"tokio",
"tracing",
+5 -4
View File
@@ -11,8 +11,9 @@ livekit = { path = "../../livekit", version = "0.1.1" }
futures = "0.3"
wgpu = "0.16"
winit = "0.28"
parking_lot = { version = "0.12.1", features=["send_guard"] }
parking_lot = { version = "0.12.1", features=["deadlock_detection"] }
image = "0.24"
egui = { git = "https://github.com/emilk/egui" }
egui-wgpu = { git = "https://github.com/emilk/egui", features = ["winit"] }
egui-winit = { git = "https://github.com/emilk/egui" }
egui = "0.22"
egui-wgpu = { version = "0.22", features = ["winit"] }
egui-winit = "0.22"
log = "0.4"
+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()
},
)