AV Desc fix
This commit is contained in:
parent
14bad67a04
commit
6e107bb136
@ -6,6 +6,7 @@ use gstreamer_app as gst_app;
|
|||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use lesavka_common::lesavka::VideoPacket;
|
use lesavka_common::lesavka::VideoPacket;
|
||||||
use tracing::{error, info, warn, debug};
|
use tracing::{error, info, warn, debug};
|
||||||
|
use gstreamer_video::glib::Type;
|
||||||
|
|
||||||
/* ---------- pipeline ----------------------------------------------------
|
/* ---------- pipeline ----------------------------------------------------
|
||||||
* ┌────────────┐ H.264/AU ┌─────────┐ Decoded ┌─────────────┐
|
* ┌────────────┐ H.264/AU ┌─────────┐ Decoded ┌─────────────┐
|
||||||
@ -39,9 +40,14 @@ impl MonitorWindow {
|
|||||||
if let Some(sink) = pipeline.by_name("sink") {
|
if let Some(sink) = pipeline.by_name("sink") {
|
||||||
// glimagesink: title / fullscreen / force‑aspect‑ratio
|
// glimagesink: title / fullscreen / force‑aspect‑ratio
|
||||||
let title = format!("Lesavka‑eye‑{_id}");
|
let title = format!("Lesavka‑eye‑{_id}");
|
||||||
let _ = sink.set_property("title", &title); // only if supported
|
if sink.has_property("title", None::<Type>) {
|
||||||
if fullscreen {
|
sink.set_property("title", &title);
|
||||||
let _ = sink.set_property("fullscreen", &true); // ditto
|
}
|
||||||
|
if sink.has_property("window-title", None::<Type>) {
|
||||||
|
sink.set_property("window-title", &title);
|
||||||
|
}
|
||||||
|
if sink.has_property("fullscreen", None::<Type>) && fullscreen {
|
||||||
|
let _ = sink.set_property("fullscreen", &true);
|
||||||
}
|
}
|
||||||
let _ = sink.set_property("force-aspect-ratio", &true);
|
let _ = sink.set_property("force-aspect-ratio", &true);
|
||||||
}
|
}
|
||||||
@ -61,7 +67,6 @@ impl MonitorWindow {
|
|||||||
pipeline.set_state(gst::State::Playing)?;
|
pipeline.set_state(gst::State::Playing)?;
|
||||||
if let Some(sink) = pipeline.by_name("sink") {
|
if let Some(sink) = pipeline.by_name("sink") {
|
||||||
let title = format!("Lesavka‑eye‑{_id}");
|
let title = format!("Lesavka‑eye‑{_id}");
|
||||||
sink.set_property_from_str("window-title", &title);
|
|
||||||
sink.set_property("force-aspect-ratio", &true);
|
sink.set_property("force-aspect-ratio", &true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -107,6 +107,8 @@ ExecStart=/usr/local/bin/lesavka-server
|
|||||||
Restart=always
|
Restart=always
|
||||||
Environment=RUST_LOG=lesavka_server=trace,lesavka_server::audio=trace,lesavka_server::video=trace,lesavka_server::usb_gadget=info
|
Environment=RUST_LOG=lesavka_server=trace,lesavka_server::audio=trace,lesavka_server::video=trace,lesavka_server::usb_gadget=info
|
||||||
Environment=RUST_BACKTRACE=1
|
Environment=RUST_BACKTRACE=1
|
||||||
|
Environment=GST_DEBUG=3,v4l2src:6,tsdemux:6,parsebin:5
|
||||||
|
Environment=GST_DEBUG_DUMP_DOT_DIR=/tmp
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardError=append:/tmp/lesavka-server.stderr
|
StandardError=append:/tmp/lesavka-server.stderr
|
||||||
|
|||||||
@ -26,41 +26,51 @@ impl Stream for AudioStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn eye_ear(
|
// pub async fn eye_ear(
|
||||||
dev: &str,
|
// dev: &str,
|
||||||
id: u32,
|
// id: u32,
|
||||||
) -> anyhow::Result<AudioStream> {
|
// ) -> anyhow::Result<AudioStream> {
|
||||||
let ear = EAR_ID[id as usize];
|
// let ear = EAR_ID[id as usize];
|
||||||
gst::init().context("gst init")?;
|
// gst::init().context("gst init")?;
|
||||||
let desc = format!(
|
// let desc = format!(
|
||||||
"v4l2src device=\"{dev}\" io-mode=mmap do-timestamp=true ! queue ! \
|
// "v4l2src device=\"{dev}\" io-mode=mmap do-timestamp=true ! \
|
||||||
tsdemux name=d d.audio_0 ! queue ! \
|
// video/mpegts,systemstream=true,packetsize=188 ! \
|
||||||
aacparse ! queue ! \
|
// tsdemux name=d d.audio_0 ! queue ! \
|
||||||
appsink name=asink emit-signals=true max-buffers=64 drop=true"
|
// aacparse ! queue ! \
|
||||||
);
|
// appsink name=asink emit-signals=true max-buffers=64 drop=true"
|
||||||
let pipe: gst::Pipeline = gst::parse::launch(&desc)?
|
// );
|
||||||
.downcast()
|
// let pipe: gst::Pipeline = gst::parse::launch(&desc)?
|
||||||
.expect("pipeline");
|
// .downcast()
|
||||||
|
// .expect("pipeline");
|
||||||
|
|
||||||
let sink = pipe.by_name("asink").expect("asink").downcast::<gst_app::AppSink>().unwrap();
|
// let sink = pipe.by_name("asink").expect("asink").downcast::<gst_app::AppSink>().unwrap();
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(8192);
|
// let (tx, rx) = tokio::sync::mpsc::channel(8192);
|
||||||
|
|
||||||
sink.set_callbacks(
|
// sink.set_callbacks(
|
||||||
gst_app::AppSinkCallbacks::builder()
|
// gst_app::AppSinkCallbacks::builder()
|
||||||
.new_sample(move |s| {
|
// .new_sample(move |s| {
|
||||||
let samp = s.pull_sample().map_err(|_| gst::FlowError::Eos)?;
|
// let samp = s.pull_sample().map_err(|_| gst::FlowError::Eos)?;
|
||||||
let buf = samp.buffer().ok_or(gst::FlowError::Error)?;
|
// let buf = samp.buffer().ok_or(gst::FlowError::Error)?;
|
||||||
let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
|
// let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
|
||||||
let pts = buf.pts().unwrap_or(gst::ClockTime::ZERO).nseconds()/1_000;
|
// let pts = buf.pts().unwrap_or(gst::ClockTime::ZERO).nseconds()/1_000;
|
||||||
let pkt = AudioPacket { id, pts, data: map.as_slice().to_vec() };
|
// let pkt = AudioPacket { id, pts, data: map.as_slice().to_vec() };
|
||||||
debug!(target:"lesavka_server::audio",
|
// debug!(target:"lesavka_server::audio",
|
||||||
eye = id, bytes = pkt.data.len(), pts = pkt.pts,
|
// eye = id, bytes = pkt.data.len(), pts = pkt.pts,
|
||||||
"⬆️ pushed audio sample ear-{ear}");
|
// "⬆️ pushed audio sample ear-{ear}");
|
||||||
let _ = tx.try_send(Ok(pkt));
|
// let _ = tx.try_send(Ok(pkt));
|
||||||
Ok(gst::FlowSuccess::Ok)
|
// Ok(gst::FlowSuccess::Ok)
|
||||||
}).build()
|
// }).build()
|
||||||
);
|
// );
|
||||||
|
|
||||||
pipe.set_state(gst::State::Playing)?;
|
// pipe.set_state(gst::State::Playing)?;
|
||||||
Ok(AudioStream{ _pipe: pipe, inner: ReceiverStream::new(rx) })
|
// Ok(AudioStream{ _pipe: pipe, inner: ReceiverStream::new(rx) })
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub async fn eye_ear(_dev: &str, _id: u32) -> anyhow::Result<AudioStream> {
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
let (_tx, rx) = tokio::sync::mpsc::channel(1);
|
||||||
|
Ok(AudioStream {
|
||||||
|
_pipe: gst::Pipeline::new(),
|
||||||
|
inner: ReceiverStream::new(rx), // always empty
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -186,14 +186,15 @@ impl Relay for Handler {
|
|||||||
&self,
|
&self,
|
||||||
req: Request<MonitorRequest>,
|
req: Request<MonitorRequest>,
|
||||||
) -> Result<Response<Self::CaptureAudioStream>, Status> {
|
) -> Result<Response<Self::CaptureAudioStream>, Status> {
|
||||||
let id = req.into_inner().id;
|
// let id = req.into_inner().id;
|
||||||
let dev = match id { 0 => "/dev/lesavka_l_eye",
|
// let dev = match id { 0 => "/dev/lesavka_l_eye",
|
||||||
1 => "/dev/lesavka_r_eye",
|
// 1 => "/dev/lesavka_r_eye",
|
||||||
_ => return Err(Status::invalid_argument("monitor id must be 0 or 1")) };
|
// _ => return Err(Status::invalid_argument("monitor id must be 0 or 1")) };
|
||||||
let s = audio::eye_ear(dev, id)
|
// let s = audio::eye_ear(dev, id)
|
||||||
.await
|
// .await
|
||||||
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
// .map_err(|e| Status::internal(format!("{e:#}")))?;
|
||||||
Ok(Response::new(Box::pin(s)))
|
// Ok(Response::new(Box::pin(s)))
|
||||||
|
Err(Status::unimplemented("GC311 has no embedded audio"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*────────────── USB-reset RPC ───────────*/
|
/*────────────── USB-reset RPC ───────────*/
|
||||||
|
|||||||
@ -45,8 +45,10 @@ pub async fn eye_ball(
|
|||||||
gst::init().context("gst init")?;
|
gst::init().context("gst init")?;
|
||||||
|
|
||||||
let desc = format!(
|
let desc = format!(
|
||||||
"v4l2src device=\"{dev}\" io-mode=mmap do-timestamp=true ! queue ! \
|
// Elementary H‑264 stream coming straight from the UVC device –‑
|
||||||
tsdemux name=d d.video_0 ! queue ! \
|
// no MPEG‑TS container, so no tsdemux.
|
||||||
|
"v4l2src device=\"{dev}\" io-mode=mmap do-timestamp=true ! \
|
||||||
|
queue ! \
|
||||||
h264parse disable-passthrough=true config-interval=-1 ! \
|
h264parse disable-passthrough=true config-interval=-1 ! \
|
||||||
video/x-h264,stream-format=byte-stream,alignment=au ! \
|
video/x-h264,stream-format=byte-stream,alignment=au ! \
|
||||||
appsink name=sink emit-signals=true max-buffers=32 drop=true"
|
appsink name=sink emit-signals=true max-buffers=32 drop=true"
|
||||||
@ -78,31 +80,47 @@ pub async fn eye_ball(
|
|||||||
|
|
||||||
/* ----- BUS WATCH: show errors & warnings immediately --------------- */
|
/* ----- BUS WATCH: show errors & warnings immediately --------------- */
|
||||||
let bus = pipeline.bus().expect("bus");
|
let bus = pipeline.bus().expect("bus");
|
||||||
|
let src_pad = pipeline
|
||||||
|
.by_name("v4l2src1") // adapt: eye‑specific element name
|
||||||
|
.unwrap()
|
||||||
|
.static_pad("src")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
src_pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, |pad, info| {
|
||||||
|
if let Some(gst::PadProbeData::Event(ref ev)) = info.data {
|
||||||
|
if let gst::EventView::Caps(c) = ev.view() {
|
||||||
|
trace!(target:"lesavka_server::video",
|
||||||
|
?c, "🔍 new caps on {}", pad.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gst::PadProbeReturn::Ok
|
||||||
|
});
|
||||||
|
|
||||||
let eye_clone = eye.to_owned();
|
let eye_clone = eye.to_owned();
|
||||||
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::*;
|
use gst::MessageView::*;
|
||||||
match msg.view() {
|
match msg.view() {
|
||||||
Error(err) => {
|
Error(err) => {
|
||||||
tracing::error!(target:"lesavka_server::video",
|
error!(target:"lesavka_server::video",
|
||||||
eye = %eye_clone,
|
eye = %eye_clone,
|
||||||
"💥 pipeline error: {} ({})",
|
"💥 pipeline error: {} ({})",
|
||||||
err.error(), err.debug().unwrap_or_default());
|
err.error(), err.debug().unwrap_or_default());
|
||||||
}
|
}
|
||||||
Warning(w) => {
|
Warning(w) => {
|
||||||
tracing::warn!(target:"lesavka_server::video",
|
warn!(target:"lesavka_server::video",
|
||||||
eye = %eye_clone,
|
eye = %eye_clone,
|
||||||
"⚠️ pipeline warning: {} ({})",
|
"⚠️ pipeline warning: {} ({})",
|
||||||
w.error(), w.debug().unwrap_or_default());
|
w.error(), w.debug().unwrap_or_default());
|
||||||
}
|
}
|
||||||
Info(i) => {
|
Info(i) => {
|
||||||
tracing::info!(target:"lesavka_server::video",
|
info!(target:"lesavka_server::video",
|
||||||
eye = %eye_clone,
|
eye = %eye_clone,
|
||||||
"📌 pipeline info: {} ({})",
|
"📌 pipeline info: {} ({})",
|
||||||
i.error(), i.debug().unwrap_or_default());
|
i.error(), i.debug().unwrap_or_default());
|
||||||
}
|
}
|
||||||
StateChanged(s) if s.current() == gst::State::Playing => {
|
StateChanged(s) if s.current() == gst::State::Playing => {
|
||||||
tracing::info!(target:"lesavka_server::video",
|
info!(target:"lesavka_server::video",
|
||||||
eye = %eye_clone,
|
eye = %eye_clone,
|
||||||
"🎬 pipeline PLAYING");
|
"🎬 pipeline PLAYING");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user