Audio Fix
This commit is contained in:
parent
4522367a64
commit
f8528897de
@ -30,8 +30,26 @@ impl AudioOut {
|
|||||||
.expect("no src element")
|
.expect("no src element")
|
||||||
.downcast::<gst_app::AppSrc>()
|
.downcast::<gst_app::AppSrc>()
|
||||||
.expect("src not an AppSrc");
|
.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);
|
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)?;
|
pipeline.set_state(gst::State::Playing)?;
|
||||||
|
|
||||||
Ok(Self { src })
|
Ok(Self { src })
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use gstreamer as gst;
|
|||||||
use gstreamer_app as gst_app;
|
use gstreamer_app as gst_app;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gst::ElementFactory;
|
use gst::ElementFactory;
|
||||||
|
use gst::MessageView::*;
|
||||||
use lesavka_common::lesavka::AudioPacket;
|
use lesavka_common::lesavka::AudioPacket;
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
use tonic::Status;
|
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<AudioStream> {
|
pub async fn ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
||||||
// NB: one *logical* speaker → id==0. A 2nd logical stream could be
|
// NB: one *logical* speaker → id==0. A 2nd logical stream could be
|
||||||
// added later (for multi‑channel) without changing the client.
|
// added later (for multi‑channel) without changing the client.
|
||||||
gst::init().context("gst init")?;
|
gst::init().context("gst init")?;
|
||||||
@ -65,15 +66,14 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
|||||||
|
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(8192);
|
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 || {
|
std::thread::spawn(move || {
|
||||||
for msg in bus.iter_timed(gst::ClockTime::NONE) {
|
for msg in bus.iter_timed(gst::ClockTime::NONE) {
|
||||||
use gst::MessageView::*;
|
|
||||||
match msg.view() {
|
match msg.view() {
|
||||||
Error(e) => error!("💥 audio pipeline: {} ({})",
|
Error(e) => error!("💥 audio pipeline: {} ({})",
|
||||||
e.error(), e.debug().unwrap_or_default()),
|
e.error(), e.debug().unwrap_or_default()),
|
||||||
Warning(w) => warn!("⚠️ audio pipeline: {} ({})",
|
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 =>
|
StateChanged(s) if s.current() == gst::State::Playing =>
|
||||||
debug!("🎶 audio pipeline PLAYING"),
|
debug!("🎶 audio pipeline PLAYING"),
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -93,7 +93,7 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
|||||||
std::sync::atomic::AtomicU64::new(0);
|
std::sync::atomic::AtomicU64::new(0);
|
||||||
let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
if n < 10 || n % 300 == 0 {
|
if n < 10 || n % 300 == 0 {
|
||||||
debug!("🔊 eye‑ear #{n}: {} bytes", map.len());
|
debug!("🎧 ear #{n}: {} bytes", map.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
let pts_us = buffer
|
let pts_us = buffer
|
||||||
@ -111,7 +111,7 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
|||||||
std::sync::atomic::AtomicU64::new(0);
|
std::sync::atomic::AtomicU64::new(0);
|
||||||
let d = DROPS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
let d = DROPS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
if d % 300 == 0 {
|
if d % 300 == 0 {
|
||||||
warn!("🔊💔 dropped {d} audio AUs (client too slow)");
|
warn!("🎧💔 dropped {d} audio AUs (client too slow)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
@ -129,17 +129,17 @@ pub async fn eye_ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_pipeline_desc(dev: &str) -> anyhow::Result<String> {
|
fn build_pipeline_desc(dev: &str) -> anyhow::Result<String> {
|
||||||
use gst::ElementFactory; // <- simpler probe
|
// choose the first encoder that exists on the system
|
||||||
let enc = ["voaacenc", "avenc_aac", "fdkaacenc"]
|
let enc = ["voaacenc", "avenc_aac", "fdkaacenc"]
|
||||||
.into_iter()
|
.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_or_else(|| anyhow::anyhow!("no AAC encoder plugin available"))?;
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
// ➊ provide-clock=false lets the USB gadget be master
|
// 48 kHz stereo, floats, gadget is master → provide‑clock=false
|
||||||
// ➋ audioconvert+audioresample make sure caps match encoder
|
|
||||||
"alsasrc device=\"{dev}\" provide-clock=false do-timestamp=true ! \
|
"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 ! \
|
{enc} bitrate=192000 ! aacparse ! queue ! \
|
||||||
appsink name=asink emit-signals=true max-buffers=64 drop=true"
|
appsink name=asink emit-signals=true max-buffers=64 drop=true"
|
||||||
))
|
))
|
||||||
|
|||||||
@ -192,7 +192,7 @@ impl Relay for Handler {
|
|||||||
let dev = std::env::var("LESAVKA_ALSA_DEV")
|
let dev = std::env::var("LESAVKA_ALSA_DEV")
|
||||||
.unwrap_or_else(|_| "hw:UAC2Gadget,0".into());
|
.unwrap_or_else(|_| "hw:UAC2Gadget,0".into());
|
||||||
|
|
||||||
let s = audio::eye_ear(&dev, 0)
|
let s = audio::ear(&dev, 0)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user