2025-06-21 05:21:57 -05:00
|
|
|
|
use gstreamer as gst;
|
|
|
|
|
|
use gstreamer_app as gst_app;
|
|
|
|
|
|
use gst::prelude::*;
|
2025-06-23 07:18:26 -05:00
|
|
|
|
use lesavka_common::lesavka::VideoPacket;
|
2025-06-26 17:26:28 -05:00
|
|
|
|
use winit::{
|
|
|
|
|
|
event_loop::EventLoop,
|
|
|
|
|
|
window::{Window, WindowAttributes},
|
|
|
|
|
|
};
|
2025-06-21 05:21:57 -05:00
|
|
|
|
|
|
|
|
|
|
pub struct MonitorWindow {
|
2025-06-26 17:26:28 -05:00
|
|
|
|
id: u32,
|
2025-06-21 05:21:57 -05:00
|
|
|
|
_window: Window,
|
2025-06-26 17:26:28 -05:00
|
|
|
|
src: gst_app::AppSrc,
|
2025-06-21 05:21:57 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MonitorWindow {
|
|
|
|
|
|
pub fn new(id: u32, el: &EventLoop<()>) -> anyhow::Result<Self> {
|
2025-06-26 17:26:28 -05:00
|
|
|
|
gst::init()?; // idempotent
|
2025-06-21 05:21:57 -05:00
|
|
|
|
|
2025-06-26 17:26:28 -05:00
|
|
|
|
/*────────────────────── window ──────────────────────*/
|
2025-06-24 23:48:06 -05:00
|
|
|
|
let window = el.create_window(
|
|
|
|
|
|
WindowAttributes::default()
|
|
|
|
|
|
.with_title(format!("Lesavka‑monitor‑{id}"))
|
2025-06-26 17:26:28 -05:00
|
|
|
|
.with_decorations(false),
|
2025-06-24 23:48:06 -05:00
|
|
|
|
)?;
|
2025-06-21 05:21:57 -05:00
|
|
|
|
|
2025-06-26 17:26:28 -05:00
|
|
|
|
/*────────────────────── pipeline ────────────────────*/
|
2025-06-26 16:17:31 -05:00
|
|
|
|
let caps = gst::Caps::builder("video/x-h264")
|
|
|
|
|
|
.field("stream-format", &"byte-stream")
|
2025-06-26 17:26:28 -05:00
|
|
|
|
.field("alignment", &"au")
|
2025-06-26 16:17:31 -05:00
|
|
|
|
.build();
|
|
|
|
|
|
|
2025-06-26 17:26:28 -05:00
|
|
|
|
// Optional HW decode with VA‑API if LESAVKA_HW_DEC is set.
|
2025-06-26 14:05:23 -05:00
|
|
|
|
let desc = if std::env::var_os("LESAVKA_HW_DEC").is_some() {
|
2025-06-26 17:26:28 -05:00
|
|
|
|
concat!(
|
|
|
|
|
|
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! ",
|
|
|
|
|
|
"capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! ",
|
|
|
|
|
|
"queue max-size-buffers=0 max-size-bytes=0 max-size-time=0 leaky=downstream ! ",
|
|
|
|
|
|
"h264parse ! vaapih264dec low-latency=true ! videoconvert ! ",
|
2025-06-27 14:01:29 -05:00
|
|
|
|
"direct-render-synchronised-videosink ! autovideosink sync=false",
|
2025-06-26 17:26:28 -05:00
|
|
|
|
)
|
2025-06-25 16:23:50 -05:00
|
|
|
|
} else {
|
2025-06-26 17:26:28 -05:00
|
|
|
|
concat!(
|
|
|
|
|
|
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! ",
|
|
|
|
|
|
"capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! ",
|
|
|
|
|
|
"queue max-size-buffers=0 max-size-bytes=0 max-size-time=0 leaky=downstream ! ",
|
|
|
|
|
|
"h264parse ! decodebin ! videoconvert ! autovideosink sync=false",
|
|
|
|
|
|
)
|
2025-06-25 16:23:50 -05:00
|
|
|
|
};
|
2025-06-24 23:48:06 -05:00
|
|
|
|
|
|
|
|
|
|
let pipeline = gst::parse::launch(desc)?
|
|
|
|
|
|
.downcast::<gst::Pipeline>()
|
2025-06-26 17:26:28 -05:00
|
|
|
|
.expect("pipeline down‑cast");
|
2025-06-26 16:17:31 -05:00
|
|
|
|
|
2025-06-26 17:26:28 -05:00
|
|
|
|
let src = pipeline
|
|
|
|
|
|
.by_name("src")
|
|
|
|
|
|
.expect("appsink")
|
|
|
|
|
|
.downcast::<gst_app::AppSrc>()
|
|
|
|
|
|
.expect("appsink down‑cast");
|
2025-06-24 23:48:06 -05:00
|
|
|
|
|
2025-06-26 16:17:31 -05:00
|
|
|
|
src.set_caps(Some(&caps));
|
2025-06-27 14:01:29 -05:00
|
|
|
|
src.set_format(gst::Format::Undefined); // running‑time PTS
|
2025-06-26 17:26:28 -05:00
|
|
|
|
src.set_property("blocksize", &0u32); // whole AU per buffer
|
2025-06-24 23:48:06 -05:00
|
|
|
|
src.set_latency(gst::ClockTime::NONE, gst::ClockTime::NONE);
|
2025-06-26 16:17:31 -05:00
|
|
|
|
|
2025-06-21 05:21:57 -05:00
|
|
|
|
pipeline.set_state(gst::State::Playing)?;
|
2025-06-24 23:48:06 -05:00
|
|
|
|
|
2025-06-21 05:21:57 -05:00
|
|
|
|
Ok(Self { id, _window: window, src })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-26 17:26:28 -05:00
|
|
|
|
/// Push one encoded access‑unit into the local pipeline.
|
2025-06-21 05:21:57 -05:00
|
|
|
|
pub fn push_packet(&self, pkt: VideoPacket) {
|
2025-06-27 14:01:29 -05:00
|
|
|
|
let buf = gst::Buffer::from_slice(pkt.data); // no PTS manipulation
|
|
|
|
|
|
let _ = self.src.push_buffer(buf);
|
2025-06-21 05:21:57 -05:00
|
|
|
|
}
|
2025-06-27 14:01:29 -05:00
|
|
|
|
|
2025-06-21 05:21:57 -05:00
|
|
|
|
}
|