From ecec4a7dc93ec9fcef513b5496b3456b73ce700e Mon Sep 17 00:00:00 2001 From: "mgsloan@gmail.com" Date: Sun, 24 Nov 2024 17:54:00 -0700 Subject: [PATCH 1/4] Fix build of `wgpu_room` Before this change got `cannot create non-exhaustive struct using struct expression` error for construction of `RoomOptions` struct. --- examples/wgpu_room/src/service.rs | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/examples/wgpu_room/src/service.rs b/examples/wgpu_room/src/service.rs index 9d3a2627..0b1843c9 100644 --- a/examples/wgpu_room/src/service.rs +++ b/examples/wgpu_room/src/service.rs @@ -106,32 +106,19 @@ async fn service_task(inner: Arc, mut cmd_rx: mpsc::UnboundedRecei while let Some(event) = cmd_rx.recv().await { match event { - AsyncCmd::RoomConnect { - url, - token, - auto_subscribe, - enable_e2ee, - key, - } => { + AsyncCmd::RoomConnect { url, token, auto_subscribe, enable_e2ee, key } => { log::info!("connecting to room: {}", url); let key_provider = KeyProvider::with_shared_key(KeyProviderOptions::default(), key.into_bytes()); - let e2ee = enable_e2ee.then_some(E2eeOptions { - encryption_type: EncryptionType::Gcm, - key_provider, - }); - - let res = Room::connect( - &url, - &token, - RoomOptions { - auto_subscribe, - e2ee, - ..Default::default() - }, - ) - .await; + let e2ee = enable_e2ee + .then_some(E2eeOptions { encryption_type: EncryptionType::Gcm, key_provider }); + + let mut room_options: RoomOptions = Default::default(); + room_options.auto_subscribe = auto_subscribe; + room_options.e2ee = e2ee; + + let res = Room::connect(&url, &token, room_options).await; if let Ok((new_room, events)) = res { log::info!("connected to room: {}", new_room.name()); From 150195b2d2b376303ec20bd2cd8a23ce7099ee8e Mon Sep 17 00:00:00 2001 From: "mgsloan@gmail.com" Date: Sun, 24 Nov 2024 18:10:43 -0700 Subject: [PATCH 2/4] Fix `save_to_disk` + docs using old NativeAudioStream::new interface Broken when resampling was added in #412 --- README.md | 2 +- examples/save_to_disk/src/main.rs | 22 ++++------------------ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e305bd72..0f2fc420 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ match event { match track { RemoteTrack::Audio(audio_track) => { let rtc_track = audio_track.rtc_track(); - let mut audio_stream = NativeAudioStream::new(rtc_track); + let mut audio_stream = NativeAudioStream::new(rtc_track, 48000, 1); tokio::spawn(async move { // Receive the audio frames in a new task while let Some(audio_frame) = audio_stream.next().await { diff --git a/examples/save_to_disk/src/main.rs b/examples/save_to_disk/src/main.rs index d378dedc..908c931d 100644 --- a/examples/save_to_disk/src/main.rs +++ b/examples/save_to_disk/src/main.rs @@ -2,7 +2,6 @@ use bytes::{BufMut, BytesMut}; use futures::StreamExt; use livekit::prelude::*; use livekit::webrtc::audio_stream::native::NativeAudioStream; -use livekit::webrtc::native::audio_resampler; use std::env; use tokio::fs::File; use tokio::io::{AsyncWriteExt, BufWriter}; @@ -118,29 +117,16 @@ async fn record_track(audio_track: RemoteAudioTrack) -> Result<(), std::io::Erro println!("Recording track {:?}", audio_track.sid()); let rtc_track = audio_track.rtc_track(); - let header = WavHeader { - sample_rate: 48000, - bit_depth: 16, - num_channels: 2, - }; + let header = WavHeader { sample_rate: 48000, bit_depth: 16, num_channels: 2 }; - let mut resampler = audio_resampler::AudioResampler::default(); let mut wav_writer = WavWriter::create(FILE_PATH, header).await?; - let mut audio_stream = NativeAudioStream::new(rtc_track); + let mut audio_stream = + NativeAudioStream::new(rtc_track, header.sample_rate as i32, header.num_channels as i32); let max_record = 5 * header.sample_rate * header.num_channels; let mut sample_count = 0; 'recv_loop: while let Some(frame) = audio_stream.next().await { - let data = resampler.remix_and_resample( - &frame.data, - frame.samples_per_channel, - frame.num_channels, - frame.sample_rate, - header.num_channels, - header.sample_rate, - ); - - for sample in data { + for sample in frame.data.into_iter() { wav_writer.write_sample(*sample).await.unwrap(); sample_count += 1; From 1db318f0166b22043f54643740238afc6971f4da Mon Sep 17 00:00:00 2001 From: "mgsloan@gmail.com" Date: Sun, 24 Nov 2024 18:17:39 -0700 Subject: [PATCH 3/4] Update `Cargo.lock` files --- Cargo.lock | 2 +- examples/Cargo.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5630220..d907dc78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1642,7 +1642,7 @@ dependencies = [ [[package]] name = "livekit-ffi" -version = "0.12.2" +version = "0.12.3" dependencies = [ "console-subscriber", "dashmap", diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 06ad21b8..48008b4b 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -2185,7 +2185,7 @@ checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "livekit" -version = "0.6.0" +version = "0.7.0" dependencies = [ "chrono", "futures-util", @@ -2206,7 +2206,7 @@ dependencies = [ [[package]] name = "livekit-api" -version = "0.4.0" +version = "0.4.1" dependencies = [ "async-tungstenite", "base64", @@ -2231,7 +2231,7 @@ dependencies = [ [[package]] name = "livekit-protocol" -version = "0.3.5" +version = "0.3.6" dependencies = [ "futures-util", "livekit-runtime", @@ -2247,7 +2247,7 @@ dependencies = [ [[package]] name = "livekit-runtime" -version = "0.3.0" +version = "0.3.1" dependencies = [ "tokio", "tokio-stream", From 909a744b44cb7a3f4550bad9026c8681009413dd Mon Sep 17 00:00:00 2001 From: "mgsloan@gmail.com" Date: Sun, 24 Nov 2024 18:24:55 -0700 Subject: [PATCH 4/4] Run `cargo fmt` on examples --- examples/api/src/main.rs | 5 +- examples/mobile/src/lib.rs | 4 +- examples/save_to_disk/src/main.rs | 16 +-- examples/wgpu_room/src/app.rs | 158 +++++++++++---------------- examples/wgpu_room/src/logo_track.rs | 11 +- examples/wgpu_room/src/service.rs | 32 +----- examples/wgpu_room/src/video_grid.rs | 8 +- 7 files changed, 79 insertions(+), 155 deletions(-) diff --git a/examples/api/src/main.rs b/examples/api/src/main.rs index b45f0326..87c65854 100644 --- a/examples/api/src/main.rs +++ b/examples/api/src/main.rs @@ -4,10 +4,7 @@ use livekit_api::services::room::{CreateRoomOptions, RoomClient}; async fn main() { let room_service = RoomClient::new("http://localhost:7880").unwrap(); - let room = room_service - .create_room("my_room", CreateRoomOptions::default()) - .await - .unwrap(); + let room = room_service.create_room("my_room", CreateRoomOptions::default()).await.unwrap(); println!("Created room: {:?}", room); } diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index bee4af74..7eb190ac 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -70,9 +70,7 @@ pub mod android { #[no_mangle] pub extern "C" fn JNI_OnLoad(vm: JavaVM, _: *mut c_void) -> jint { android_logger::init_once( - Config::default() - .with_max_level(LevelFilter::Debug) - .with_tag("livekit-rustexample"), + Config::default().with_max_level(LevelFilter::Debug).with_tag("livekit-rustexample"), ); log::info!("JNI_OnLoad, initializing LiveKit"); diff --git a/examples/save_to_disk/src/main.rs b/examples/save_to_disk/src/main.rs index 908c931d..af2e77b6 100644 --- a/examples/save_to_disk/src/main.rs +++ b/examples/save_to_disk/src/main.rs @@ -30,11 +30,7 @@ impl WavWriter { let file = File::create(path).await?; let writer = BufWriter::new(file); - let mut wav_writer = WavWriter { - header, - data: BytesMut::new(), - writer, - }; + let mut wav_writer = WavWriter { header, data: BytesMut::new(), writer }; wav_writer.write_header()?; Ok(wav_writer) @@ -88,19 +84,13 @@ async fn main() { let url = env::var("LIVEKIT_URL").expect("LIVEKIT_URL is not set"); let token = env::var("LIVEKIT_TOKEN").expect("LIVEKIT_TOKEN is not set"); - let (room, mut rx) = Room::connect(&url, &token, RoomOptions::default()) - .await - .unwrap(); + let (room, mut rx) = Room::connect(&url, &token, RoomOptions::default()).await.unwrap(); println!("Connected to room: {} - {}", room.name(), String::from(room.sid().await)); while let Some(msg) = rx.recv().await { #[allow(clippy::single_match)] match msg { - RoomEvent::TrackSubscribed { - track, - publication: _, - participant: _, - } => { + RoomEvent::TrackSubscribed { track, publication: _, participant: _ } => { if let RemoteTrack::Audio(audio_track) = track { record_track(audio_track).await.unwrap(); break; diff --git a/examples/wgpu_room/src/app.rs b/examples/wgpu_room/src/app.rs index 99001f27..000b7140 100644 --- a/examples/wgpu_room/src/app.rs +++ b/examples/wgpu_room/src/app.rs @@ -47,10 +47,8 @@ impl LkApp { .and_then(|storage| eframe::get_value(storage, eframe::APP_KEY)) .unwrap_or_default(); - let async_runtime = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); + let async_runtime = + tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap(); Self { service: LkService::new(async_runtime.handle()), @@ -74,11 +72,7 @@ impl LkApp { UiCmd::RoomEvent { event } => { log::info!("{:?}", event); match event { - RoomEvent::TrackSubscribed { - track, - publication: _, - participant, - } => { + RoomEvent::TrackSubscribed { track, publication: _, participant } => { if let RemoteTrack::Video(ref video_track) = track { // Create a new VideoRenderer let video_renderer = VideoRenderer::new( @@ -92,19 +86,10 @@ impl LkApp { // TODO(theomonnom): Once we support media devices, we can play audio tracks here } } - RoomEvent::TrackUnsubscribed { - track, - publication: _, - participant, - } => { - self.video_renderers - .remove(&(participant.identity(), track.sid())); + RoomEvent::TrackUnsubscribed { track, publication: _, participant } => { + self.video_renderers.remove(&(participant.identity(), track.sid())); } - RoomEvent::LocalTrackPublished { - track, - publication: _, - participant, - } => { + RoomEvent::LocalTrackPublished { track, publication: _, participant } => { if let LocalTrack::Video(ref video_track) = track { // Also create a new VideoRenderer for local tracks let video_renderer = VideoRenderer::new( @@ -116,12 +101,8 @@ impl LkApp { .insert((participant.identity(), track.sid()), video_renderer); } } - RoomEvent::LocalTrackUnpublished { - publication, - participant, - } => { - self.video_renderers - .remove(&(participant.identity(), publication.sid())); + RoomEvent::LocalTrackUnpublished { publication, participant } => { + self.video_renderers.remove(&(participant.identity(), publication.sid())); } RoomEvent::Disconnected { reason: _ } => { self.video_renderers.clear(); @@ -240,10 +221,7 @@ impl LkApp { ui.label(format!("Name: {}", room.name())); //ui.label(format!("Sid: {}", String::from(room.sid().await))); ui.label(format!("ConnectionState: {:?}", room.connection_state())); - ui.label(format!( - "ParticipantCount: {:?}", - room.remote_participants().len() + 1 - )); + ui.label(format!("ParticipantCount: {:?}", room.remote_participants().len() + 1)); } egui::warn_if_debug_build(ui); @@ -262,10 +240,8 @@ impl LkApp { egui::ScrollArea::vertical().show(ui, |ui| { // Iterate with sorted keys to avoid flickers (Because this is a immediate mode UI) let participants = room.remote_participants(); - let mut sorted_participants = participants - .keys() - .cloned() - .collect::>(); + let mut sorted_participants = + participants.keys().cloned().collect::>(); sorted_participants.sort_by(|a, b| a.as_str().cmp(b.as_str())); for psid in sorted_participants { @@ -288,11 +264,7 @@ impl LkApp { } }); - ui.label(format!( - "{} - {:?}", - publication.name(), - publication.source() - )); + ui.label(format!("{} - {:?}", publication.name(), publication.source())); ui.horizontal(|ui| { if publication.is_muted() { @@ -307,9 +279,8 @@ impl LkApp { if publication.is_subscribed() { if ui.button("Unsubscribe").clicked() { - let _ = self - .service - .send(AsyncCmd::UnsubscribeTrack { publication }); + let _ = + self.service.send(AsyncCmd::UnsubscribeTrack { publication }); } } else if ui.button("Subscribe").clicked() { let _ = self.service.send(AsyncCmd::SubscribeTrack { publication }); @@ -334,46 +305,46 @@ impl LkApp { } egui::ScrollArea::vertical().show(ui, |ui| { - VideoGrid::new("default_grid") - .max_columns(6) - .show(ui, |ui| { - if show_videos { - // Draw participant videos - for ((participant_sid, _), video_renderer) in &self.video_renderers { - ui.video_frame(|ui| { - let room = room.as_ref().unwrap().clone(); - - if let Some(participant) = room.remote_participants().get(participant_sid) - { - draw_video( - participant.name().as_str(), - participant.is_speaking(), - video_renderer, - ui, - ); - } else { - draw_video( - room.local_participant().name().as_str(), - room.local_participant().is_speaking(), - video_renderer, - ui, - ); - } - }); - } - } else { - // Draw video skeletons when we're not connected - for _ in 0..5 { - ui.video_frame(|ui| { - egui::Frame::none() - .fill(ui.style().visuals.code_bg_color) - .show(ui, |ui| { - ui.allocate_space(ui.available_size()); - }); - }); - } + VideoGrid::new("default_grid").max_columns(6).show(ui, |ui| { + if show_videos { + // Draw participant videos + for ((participant_sid, _), video_renderer) in &self.video_renderers { + ui.video_frame(|ui| { + let room = room.as_ref().unwrap().clone(); + + if let Some(participant) = + room.remote_participants().get(participant_sid) + { + draw_video( + participant.name().as_str(), + participant.is_speaking(), + video_renderer, + ui, + ); + } else { + draw_video( + room.local_participant().name().as_str(), + room.local_participant().is_speaking(), + video_renderer, + ui, + ); + } + }); + } + } else { + // Draw video skeletons when we're not connected + for _ in 0..5 { + ui.video_frame(|ui| { + egui::Frame::none().fill(ui.style().visuals.code_bg_color).show( + ui, + |ui| { + ui.allocate_space(ui.available_size()); + }, + ); + }); } - }) + } + }) }); } } @@ -392,12 +363,12 @@ impl eframe::App for LkApp { self.top_panel(ui); }); - egui::SidePanel::left("left_panel") - .resizable(true) - .width_range(20.0..=360.0) - .show(ctx, |ui| { + egui::SidePanel::left("left_panel").resizable(true).width_range(20.0..=360.0).show( + ctx, + |ui| { self.left_panel(ui); - }); + }, + ); /*egui::TopBottomPanel::bottom("bottom_panel") .resizable(true) @@ -406,12 +377,12 @@ impl eframe::App for LkApp { self.bottom_panel(ui); });*/ - egui::SidePanel::right("right_panel") - .resizable(true) - .width_range(20.0..=360.0) - .show(ctx, |ui| { + egui::SidePanel::right("right_panel").resizable(true).width_range(20.0..=360.0).show( + ctx, + |ui| { self.right_panel(ui); - }); + }, + ); egui::CentralPanel::default().show(ctx, |ui| { self.central_panel(ui); @@ -427,8 +398,7 @@ fn draw_video(name: &str, speaking: bool, video_renderer: &VideoRenderer, ui: &m let inner_rect = rect.shrink(1.0); if speaking { - ui.painter() - .rect(rect, Rounding::none(), egui::Color32::GREEN, Stroke::NONE); + ui.painter().rect(rect, Rounding::none(), egui::Color32::GREEN, Stroke::NONE); } // Always draw a background in case we still didn't receive a frame diff --git a/examples/wgpu_room/src/logo_track.rs b/examples/wgpu_room/src/logo_track.rs index bcd34852..edb03465 100644 --- a/examples/wgpu_room/src/logo_track.rs +++ b/examples/wgpu_room/src/logo_track.rs @@ -83,11 +83,7 @@ impl LogoTrack { ) .await?; - let handle = TrackHandle { - close_tx, - task, - track, - }; + let handle = TrackHandle { close_tx, task, track }; self.handle = Some(handle); Ok(()) @@ -98,10 +94,7 @@ impl LogoTrack { let _ = handle.close_tx.send(()); let _ = handle.task.await; - self.room - .local_participant() - .unpublish_track(&handle.track.sid()) - .await?; + self.room.local_participant().unpublish_track(&handle.track.sid()).await?; } Ok(()) } diff --git a/examples/wgpu_room/src/service.rs b/examples/wgpu_room/src/service.rs index 0b1843c9..e81c0c08 100644 --- a/examples/wgpu_room/src/service.rs +++ b/examples/wgpu_room/src/service.rs @@ -13,25 +13,13 @@ use tokio::sync::mpsc::{self, error::SendError}; #[derive(Debug)] pub enum AsyncCmd { - RoomConnect { - url: String, - token: String, - auto_subscribe: bool, - enable_e2ee: bool, - key: String, - }, + RoomConnect { url: String, token: String, auto_subscribe: bool, enable_e2ee: bool, key: String }, RoomDisconnect, - SimulateScenario { - scenario: SimulateScenario, - }, + SimulateScenario { scenario: SimulateScenario }, ToggleLogo, ToggleSine, - SubscribeTrack { - publication: RemoteTrackPublication, - }, - UnsubscribeTrack { - publication: RemoteTrackPublication, - }, + SubscribeTrack { publication: RemoteTrackPublication }, + UnsubscribeTrack { publication: RemoteTrackPublication }, E2eeKeyRatchet, LogStats, } @@ -62,18 +50,10 @@ impl LkService { let (ui_tx, ui_rx) = mpsc::unbounded_channel(); let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); - let inner = Arc::new(ServiceInner { - ui_tx, - room: Default::default(), - }); + let inner = Arc::new(ServiceInner { ui_tx, room: Default::default() }); let handle = async_handle.spawn(service_task(inner.clone(), cmd_rx)); - Self { - cmd_tx, - ui_rx, - handle, - inner, - } + Self { cmd_tx, ui_rx, handle, inner } } pub fn room(&self) -> Option> { diff --git a/examples/wgpu_room/src/video_grid.rs b/examples/wgpu_room/src/video_grid.rs index cabc5490..aa57acda 100644 --- a/examples/wgpu_room/src/video_grid.rs +++ b/examples/wgpu_room/src/video_grid.rs @@ -61,16 +61,12 @@ impl VideoGrid { self.prev_state = prev_state.unwrap_or_default(); self.available_rect = ui.available_rect_before_wrap(); - ui.ctx() - .check_for_id_clash(self.id, self.available_rect, "VideoGrid"); + ui.ctx().check_for_id_clash(self.id, self.available_rect, "VideoGrid"); ui.allocate_ui_at_rect(self.available_rect, |ui| { ui.set_visible(!is_first_frame); - let mut ctx = VideoGridContext { - layout: &mut self, - ui, - }; + let mut ctx = VideoGridContext { layout: &mut self, ui }; let res = grid(&mut ctx); // Save the new state