video updates
This commit is contained in:
parent
83f5d7124d
commit
da22370468
@ -75,7 +75,7 @@ impl LesavkaClientApp {
|
|||||||
let suicide = async {
|
let suicide = async {
|
||||||
if self.dev_mode {
|
if self.dev_mode {
|
||||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||||
warn!("dev‑mode timeout");
|
warn!("💀 dev‑mode timeout 💀");
|
||||||
// self.aggregator.keyboards.dev.ungrab();
|
// self.aggregator.keyboards.dev.ungrab();
|
||||||
// self.aggregator.mice.dev.ungrab();
|
// self.aggregator.mice.dev.ungrab();
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
@ -189,23 +189,34 @@ impl LesavkaClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*──────────────── monitor stream ────────────────*/
|
/*──────────────── monitor stream ────────────────*/
|
||||||
async fn video_loop(ep: Channel, tx: tokio::sync::mpsc::UnboundedSender<VideoPacket>) {
|
async fn video_loop(
|
||||||
loop {
|
ep: Channel,
|
||||||
let mut cli = RelayClient::new(ep.clone());
|
tx: tokio::sync::mpsc::UnboundedSender<VideoPacket>,
|
||||||
for monitor_id in 0..=1 {
|
) {
|
||||||
|
for monitor_id in 0..=1 {
|
||||||
|
let tx = tx.clone();
|
||||||
|
let ep = ep.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let mut cli = RelayClient::new(ep.clone());
|
||||||
let req = MonitorRequest { id: monitor_id, max_bitrate: 6_000 };
|
let req = MonitorRequest { id: monitor_id, max_bitrate: 6_000 };
|
||||||
if let Ok(mut stream) = cli.capture_video(Request::new(req)).await {
|
match cli.capture_video(Request::new(req)).await {
|
||||||
while let Some(pkt) = stream.get_mut().message().await.transpose() {
|
Ok(mut stream) => {
|
||||||
match pkt {
|
while let Some(pkt) = stream.get_mut().message().await.transpose() {
|
||||||
Ok(p) => { let _ = tx.send(p); }
|
match pkt {
|
||||||
Err(e) => { error!("video {monitor_id}: {e}"); break }
|
Ok(p) => { let _ = tx.send(p); }
|
||||||
|
Err(e) => { error!("video {monitor_id}: {e}"); break }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => error!("video {monitor_id}: {e}"),
|
||||||
}
|
}
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
async fn delay() { tokio::time::sleep(Duration::from_secs(1)).await; }
|
async fn delay() { tokio::time::sleep(Duration::from_secs(1)).await; }
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use tracing::{debug, error, warn, trace};
|
|||||||
|
|
||||||
use lesavka_common::lesavka::MouseReport;
|
use lesavka_common::lesavka::MouseReport;
|
||||||
|
|
||||||
const SEND_INTERVAL: Duration = Duration::from_micros(250);
|
const SEND_INTERVAL: Duration = Duration::from_micros(50);
|
||||||
|
|
||||||
pub struct MouseAggregator {
|
pub struct MouseAggregator {
|
||||||
dev: Device,
|
dev: Device,
|
||||||
|
|||||||
@ -24,26 +24,43 @@ impl MonitorWindow {
|
|||||||
.with_decorations(false)
|
.with_decorations(false)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// appsrc -> decode -> convert -> autovideosink
|
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() {
|
let desc = if std::env::var_os("LESAVKA_HW_DEC").is_some() {
|
||||||
"appsrc name=src is-live=true format=time do-timestamp=true ! queue ! \
|
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
|
||||||
h264parse ! vaapih264dec low-latency=true ! videoconvert ! \
|
capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! \
|
||||||
autovideosink sync=false"
|
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 {
|
} else {
|
||||||
// fallback
|
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
|
||||||
"appsrc name=src is-live=true format=time do-timestamp=true ! \
|
capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! \
|
||||||
queue ! h264parse ! decodebin ! videoconvert ! autovideosink sync=false"
|
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)?
|
let pipeline = gst::parse::launch(desc)?
|
||||||
.downcast::<gst::Pipeline>()
|
.downcast::<gst::Pipeline>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let src = pipeline
|
let src = pipeline.by_name("src").unwrap()
|
||||||
.by_name("src").unwrap()
|
|
||||||
.downcast::<gst_app::AppSrc>().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);
|
src.set_latency(gst::ClockTime::NONE, gst::ClockTime::NONE);
|
||||||
|
|
||||||
pipeline.set_state(gst::State::Playing)?;
|
pipeline.set_state(gst::State::Playing)?;
|
||||||
|
|
||||||
Ok(Self { id, _window: window, src })
|
Ok(Self { id, _window: window, src })
|
||||||
|
|||||||
@ -327,6 +327,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
if let Err(e) = Server::builder()
|
if let Err(e) = Server::builder()
|
||||||
.tcp_nodelay(true)
|
.tcp_nodelay(true)
|
||||||
|
.max_frame_size(Some(256 * 1024))
|
||||||
.add_service(RelayServer::new(handler))
|
.add_service(RelayServer::new(handler))
|
||||||
.serve(([0, 0, 0, 0], 50051).into())
|
.serve(([0, 0, 0, 0], 50051).into())
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -15,10 +15,13 @@ pub async fn spawn_camera(
|
|||||||
) -> anyhow::Result<ReceiverStream<Result<VideoPacket, Status>>> {
|
) -> anyhow::Result<ReceiverStream<Result<VideoPacket, Status>>> {
|
||||||
gst::init().context("gst init")?;
|
gst::init().context("gst init")?;
|
||||||
|
|
||||||
// v4l2src → H.264 already, we only parse & relay
|
// IMPORTANT: keep one AU per buffer, include regular SPS/PPS
|
||||||
let desc = format!(
|
let desc = format!(
|
||||||
"v4l2src device={dev} io-mode=auto ! queue ! h264parse config-interval=-1 ! \
|
"v4l2src device={dev} io-mode=dmabuf ! \
|
||||||
appsink name=sink emit-signals=true sync=false"
|
video/x-h264,alignment=au,stream-format=byte-stream ! \
|
||||||
|
queue max-size-buffers=0 max-size-bytes=0 max-size-time=0 ! \
|
||||||
|
h264parse config-interval=1 ! \
|
||||||
|
appsink name=sink emit-signals=true sync=false drop=true"
|
||||||
);
|
);
|
||||||
|
|
||||||
let pipeline = gst::parse::launch(&desc)?
|
let pipeline = gst::parse::launch(&desc)?
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user