76 lines
2.8 KiB
Rust
76 lines
2.8 KiB
Rust
// client/src/output/video.rs
|
||
|
||
use gstreamer as gst;
|
||
use gstreamer_app as gst_app;
|
||
use gst::prelude::*;
|
||
use gst_app::prelude::*;
|
||
use lesavka_common::lesavka::VideoPacket;
|
||
use winit::window::{Window, WindowAttributes};
|
||
use winit::event_loop::EventLoop;
|
||
|
||
pub struct MonitorWindow {
|
||
id: u32,
|
||
_window: Window,
|
||
src: gst_app::AppSrc,
|
||
}
|
||
|
||
impl MonitorWindow {
|
||
pub fn new(id: u32, el: &EventLoop<()>) -> anyhow::Result<Self> {
|
||
gst::init()?;
|
||
|
||
let window = el.create_window(
|
||
WindowAttributes::default()
|
||
.with_title(format!("Lesavka‑monitor‑{id}"))
|
||
.with_decorations(false)
|
||
)?;
|
||
|
||
let caps = gst::Caps::builder("video/x-h264")
|
||
.field("stream-format", &"byte-stream")
|
||
.field("alignment", &"au")
|
||
.build();
|
||
|
||
let desc = if std::env::var_os("LESAVKA_HW_DEC").is_some() {
|
||
"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 ! \
|
||
autovideosink sync=false max-lateness=-1"
|
||
} else {
|
||
"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 max-lateness=-1"
|
||
};
|
||
|
||
let pipeline = gst::parse::launch(desc)?
|
||
.downcast::<gst::Pipeline>()
|
||
.unwrap();
|
||
|
||
let src = pipeline.by_name("src").unwrap()
|
||
.downcast::<gst_app::AppSrc>().unwrap();
|
||
|
||
src.set_caps(Some(&caps));
|
||
|
||
// use the dedicated helpers from `AppSrcExt` instead of the generic
|
||
// `set_property`, which returns `()` (hence the earlier E0277).
|
||
src.set_format(gst::Format::Time); // timestamps are in running-time
|
||
|
||
// “blocksize=0” → deliver whole access-units (no chunking). The generic
|
||
// `set_property()` API returns a `Result<(), glib::BoolError>` so we just
|
||
// unwrap: this property always exists on AppSrc.
|
||
src.set_property("blocksize", &0u32);
|
||
src.set_latency(gst::ClockTime::NONE, gst::ClockTime::NONE);
|
||
|
||
pipeline.set_state(gst::State::Playing)?;
|
||
|
||
Ok(Self { id, _window: window, src })
|
||
}
|
||
|
||
pub fn push_packet(&self, pkt: VideoPacket) {
|
||
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);
|
||
}
|
||
}
|