diff --git a/client/src/output/audio.rs b/client/src/output/audio.rs index 2e5124d..a555f2d 100644 --- a/client/src/output/audio.rs +++ b/client/src/output/audio.rs @@ -30,8 +30,26 @@ impl AudioOut { .expect("no src element") .downcast::() .expect("src not an AppSrc"); - + + src.set_caps(Some(&gst::Caps::builder("audio/mpeg") // mpeg‑4 AAC + .field("mpegversion", &4i32) + .field("stream-format", &"adts") + .build())); src.set_format(gst::Format::Time); + + { + let bus = pipeline.bus().expect("bus"); + std::thread::spawn(move || { + use gst::MessageView::*; + for msg in bus.iter_timed(gst::ClockTime::NONE) { + if let Error(e) = msg.view() { + error!("πŸ’₯ client‑audio: {} ({})", + e.error(), e.debug().unwrap_or_default()); + } + } + }); + } + pipeline.set_state(gst::State::Playing)?; Ok(Self { src }) diff --git a/server/src/audio.rs b/server/src/audio.rs index fd197dc..a6b2ff0 100644 --- a/server/src/audio.rs +++ b/server/src/audio.rs @@ -7,6 +7,7 @@ use gstreamer as gst; use gstreamer_app as gst_app; use gst::prelude::*; use gst::ElementFactory; +use gst::MessageView::*; use lesavka_common::lesavka::AudioPacket; use tokio_stream::wrappers::ReceiverStream; use tonic::Status; @@ -36,10 +37,10 @@ impl Drop for AudioStream { } /*───────────────────────────────────────────────────────────────────────────*/ -/* eye_ear() – capture from ALSA (β€œspeaker”) and push AAC AUs via gRPC */ +/* ear() – capture from ALSA (β€œspeaker”) and push AAC AUs via gRPC */ /*───────────────────────────────────────────────────────────────────────────*/ -pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result { +pub async fn ear(alsa_dev: &str, id: u32) -> anyhow::Result { // NB: one *logical* speaker β†’ id==0. A 2nd logical stream could be // added later (for multi‑channel) without changing the client. gst::init().context("gst init")?; @@ -65,15 +66,14 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result { let (tx, rx) = tokio::sync::mpsc::channel(8192); - let bus = pipeline.bus().expect("no bus"); + let bus = pipeline.bus().expect("bus"); std::thread::spawn(move || { for msg in bus.iter_timed(gst::ClockTime::NONE) { - use gst::MessageView::*; match msg.view() { Error(e) => error!("πŸ’₯ audio pipeline: {} ({})", - e.error(), e.debug().unwrap_or_default()), + e.error(), e.debug().unwrap_or_default()), Warning(w) => warn!("⚠️ audio pipeline: {} ({})", - w.error(), w.debug().unwrap_or_default()), + w.error(), w.debug().unwrap_or_default()), StateChanged(s) if s.current() == gst::State::Playing => debug!("🎢 audio pipeline PLAYING"), _ => {} @@ -93,7 +93,7 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result { std::sync::atomic::AtomicU64::new(0); let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n < 10 || n % 300 == 0 { - debug!("πŸ”Š eye‑ear #{n}: {}β€―bytes", map.len()); + debug!("🎧 ear #{n}: {}β€―bytes", map.len()); } let pts_us = buffer @@ -111,7 +111,7 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result { std::sync::atomic::AtomicU64::new(0); let d = DROPS.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if d % 300 == 0 { - warn!("πŸ”ŠπŸ’” dropped {d} audio AUs (client too slow)"); + warn!("πŸŽ§πŸ’” dropped {d} audio AUs (client too slow)"); } } Ok(gst::FlowSuccess::Ok) @@ -129,17 +129,17 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result { } fn build_pipeline_desc(dev: &str) -> anyhow::Result { - use gst::ElementFactory; // <- simpler probe + // choose the first encoder that exists on the system let enc = ["voaacenc", "avenc_aac", "fdkaacenc"] .into_iter() - .find(|&e| ElementFactory::find(e).is_some()) + .find(|e| ElementFactory::find(e).is_some()) // cheap run‑time probe .ok_or_else(|| anyhow::anyhow!("no AAC encoder plugin available"))?; Ok(format!( - // ➊ provide-clock=false lets the USB gadget be master - // βž‹ audioconvert+audioresample make sure caps match encoder + // 48β€―kHz stereo, floats, gadget is master β†’ provide‑clock=false "alsasrc device=\"{dev}\" provide-clock=false do-timestamp=true ! \ - audioconvert ! audioresample ! audio/x-raw,channels=2,rate=48000,format=F32LE ! \ + audioconvert ! audioresample ! \ + audio/x-raw,format=F32LE,channels=2,rate=48000 ! \ {enc} bitrate=192000 ! aacparse ! queue ! \ appsink name=asink emit-signals=true max-buffers=64 drop=true" )) diff --git a/server/src/main.rs b/server/src/main.rs index bfcc470..206b2c0 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -192,7 +192,7 @@ impl Relay for Handler { let dev = std::env::var("LESAVKA_ALSA_DEV") .unwrap_or_else(|_| "hw:UAC2Gadget,0".into()); - let s = audio::eye_ear(&dev, 0) + let s = audio::ear(&dev, 0) .await .map_err(|e| Status::internal(format!("{e:#}")))?;