74 lines
2.5 KiB
Rust
Raw Normal View History

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
2025-06-27 19:31:46 -05:00
const DESC: &str = concat!(
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! ",
"queue max-size-buffers=0 max-size-bytes=0 max-size-time=0 leaky=downstream ! ",
"capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! ",
"h264parse disable-passthrough=true ! decodebin ! ",
"queue ! videoconvert ! autovideosink sync=false"
);
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-27 22:51:50 -05:00
gst::init()?; // idempotent
2025-06-21 05:21:57 -05:00
2025-06-27 22:51:50 -05:00
/* ---------- Wayland / X11 window ------------- */
let window = el
.create_window(
WindowAttributes::default()
.with_title(format!("Lesavkamonitor{id}"))
.with_decorations(true),
)?;
2025-06-21 05:21:57 -05:00
2025-06-27 22:51:50 -05:00
/* ---------- GStreamer pipeline --------------- */
2025-06-26 16:17:31 -05:00
let caps = gst::Caps::builder("video/x-h264")
.field("stream-format", &"byte-stream")
2025-06-27 22:51:50 -05:00
.field("alignment", &"au")
2025-06-26 16:17:31 -05:00
.build();
2025-06-27 19:31:46 -05:00
let pipeline = gst::parse::launch(DESC)?
2025-06-24 23:48:06 -05:00
.downcast::<gst::Pipeline>()
2025-06-26 17:26:28 -05:00
.expect("pipeline downcast");
2025-06-26 16:17:31 -05:00
2025-06-26 17:26:28 -05:00
let src = pipeline
.by_name("src")
2025-06-27 22:51:50 -05:00
.expect("appsrc element not found")
2025-06-26 17:26:28 -05:00
.downcast::<gst_app::AppSrc>()
2025-06-27 22:51:50 -05:00
.expect("appsrc downcast");
2025-06-24 23:48:06 -05:00
2025-06-26 16:17:31 -05:00
src.set_caps(Some(&caps));
2025-06-27 22:51:50 -05:00
src.set_format(gst::Format::Time); // downstream clock
src.set_property("blocksize", &0u32); // one AU per buffer
// NOTE: set_property() and friends return (), so no `?`
src.set_property("do-timestamp", &true);
2025-06-26 16:17:31 -05:00
2025-06-27 22:51:50 -05:00
src.set_latency(gst::ClockTime::NONE, gst::ClockTime::NONE);
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-27 22:51:50 -05:00
/// Feed one H.264 accessunit into the pipeline.
2025-06-21 05:21:57 -05:00
pub fn push_packet(&self, pkt: VideoPacket) {
2025-06-27 22:51:50 -05:00
// Mutable so we can set the PTS:
let mut buf = gst::Buffer::from_slice(pkt.data);
if let Some(ref mut b) = buf.get_mut() {
2025-06-27 19:31:46 -05:00
b.set_pts(Some(gst::ClockTime::from_useconds(pkt.pts)));
}
2025-06-27 22:51:50 -05:00
let _ = self.src.push_buffer(buf); // ignore Eos / flushing
2025-06-21 05:21:57 -05:00
}
}