72 lines
2.4 KiB
Rust
Raw Normal View History

2025-06-29 03:46:34 -05:00
// client/src/output/audio.rs
use gstreamer as gst;
use gstreamer_app as gst_app;
use lesavka_common::lesavka::AudioPacket;
use gst::prelude::*;
2025-06-29 11:44:16 -05:00
use tracing::{error, info, warn, debug};
2025-06-29 03:46:34 -05:00
pub struct AudioOut {
src: gst_app::AppSrc,
}
impl AudioOut {
pub fn new() -> anyhow::Result<Self> {
gst::init()?;
2025-06-29 22:39:17 -05:00
// Auto-audiosink picks PipeWire / Pulse etc.
2025-06-29 03:46:34 -05:00
const PIPE: &str =
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
queue leaky=downstream ! aacparse ! avdec_aac ! audioresample ! autoaudiosink";
2025-06-29 22:39:17 -05:00
// `parse_launch()` returns `gst::Element`; down-cast manually and
2025-06-29 03:46:34 -05:00
// map the error into anyhow ourselves (no `?` on the downcast).
let pipeline: gst::Pipeline = gst::parse::launch(PIPE)?
.downcast::<gst::Pipeline>()
.expect("not a pipeline");
let src: gst_app::AppSrc = pipeline
.by_name("src")
.expect("no src element")
.downcast::<gst_app::AppSrc>()
.expect("src not an AppSrc");
2025-06-30 02:42:20 -05:00
src.set_caps(Some(&gst::Caps::builder("audio/mpeg") // mpeg4 AAC
.field("mpegversion", &4i32)
.field("stream-format", &"adts")
.build()));
2025-06-29 03:46:34 -05:00
src.set_format(gst::Format::Time);
2025-06-30 02:42:20 -05:00
{
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!("💥 clientaudio: {} ({})",
e.error(), e.debug().unwrap_or_default());
}
}
});
}
2025-06-29 03:46:34 -05:00
pipeline.set_state(gst::State::Playing)?;
Ok(Self { src })
}
pub fn push(&self, pkt: AudioPacket) {
2025-06-29 11:44:16 -05:00
static CNT : std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(0);
let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if n % 300 == 0 || n < 10 {
debug!(bytes = pkt.data.len(), pts = pkt.pts, "⬇️ received audio AU");
}
2025-06-29 03:46:34 -05:00
let mut buf = gst::Buffer::from_slice(pkt.data);
buf.get_mut()
.unwrap()
.set_pts(Some(gst::ClockTime::from_useconds(pkt.pts)));
let _ = self.src.push_buffer(buf);
}
}