feat(m2): real desktop capture in the video stream (portal → Moonlight)
Wire M0's portal desktop capture into the GameStream video plane: with LUMEN_VIDEO_SOURCE=portal the stream captures the headless wlroots desktop (PipeWire RGB) instead of the synthetic pattern, opens NVENC from the first captured frame's format/size, and streams it. Verified live: a stock Moonlight client shows the real 5120×1440 desktop at ~42 fps (release build). - capture.rs: FastSyntheticCapturer (cheap fill pattern, real-time at 5K) so both sources share the Capturer trait - stream.rs: source select (portal | synthetic), encoder opened from the first frame, wall-clock 90 kHz RTP timestamps (correct under a variable capture rate) Note: the CPU-copy RGB→rgb0 path caps ~42 fps at 5K (single-threaded); dmabuf zero-copy is the deferred optimization (plan §9). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,49 @@ impl Capturer for SyntheticCapturer {
|
||||
}
|
||||
}
|
||||
|
||||
/// A cheap moving test pattern (BGRx) for the streaming path: a pulsing field + a white band
|
||||
/// sweeping down, generated with whole-buffer `fill`s so it stays real-time even at 5K.
|
||||
pub struct FastSyntheticCapturer {
|
||||
width: u32,
|
||||
height: u32,
|
||||
frame_idx: u64,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl FastSyntheticCapturer {
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
assert!(width > 0 && height > 0);
|
||||
FastSyntheticCapturer {
|
||||
width,
|
||||
height,
|
||||
frame_idx: 0,
|
||||
buf: vec![0u8; width as usize * height as usize * 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Capturer for FastSyntheticCapturer {
|
||||
fn next_frame(&mut self) -> Result<CapturedFrame> {
|
||||
let (w, h) = (self.width as usize, self.height as usize);
|
||||
let row = w * 4;
|
||||
let shade = (self.frame_idx % 256) as u8;
|
||||
self.buf.fill(shade);
|
||||
let band_h = (h / 20).max(1);
|
||||
let band_y = (self.frame_idx as usize * 6) % h;
|
||||
for y in band_y..(band_y + band_h).min(h) {
|
||||
self.buf[y * row..(y + 1) * row].fill(0xff);
|
||||
}
|
||||
self.frame_idx += 1;
|
||||
Ok(CapturedFrame {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
pts_ns: 0,
|
||||
format: PixelFormat::Bgrx,
|
||||
cpu_bytes: self.buf.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a live capturer for a client-sized monitor via the xdg ScreenCast portal
|
||||
/// (`ashpd`) → PipeWire (`pipewire`). Implemented in the `linux` submodule.
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
Reference in New Issue
Block a user