feat: Phase 1 MVP with audit fixes — Wayland screen capture + VAAPI encoding
Phase 1 MVP implementation of wl-webrtc: Wayland screen capture tool with hardware-accelerated VAAPI H.264 encoding and WebTransport output. Includes all 9 runtime bug fixes from code audit (fix-audit-issues plan): CRITICAL: - C2: h264_metadata BSF with repeat_sps/repeat_pps in encode pipeline - C4: FpsLimit wired as timing gate in on_copy_complete HIGH: - C3+A2: DRM device discovery via dmabuf feedback MainDevice event, unified resolve_drm_path() helper (CLI > compositor > auto > fallback) - H2: Separate physical_size (mm) from mode_size (pixels) in wl_output - H1+A3: Multi-output warning + named-output-not-found error MEDIUM: - M5: tv_sec u32->u64 to avoid Y2106 timestamp truncation - M4: Guard against SHM Buffer event (DMA-BUF only) Key components: - src/avhw.rs: FFmpeg VAAPI encoder + filter graph + BSF pipeline - src/state.rs: Wayland event loop + output negotiation + screencopy - src/cap_wlr_screencopy.rs: wlr-screencopy capture source - src/fps_limit.rs: Frame rate limiting with configurable target - src/transform.rs: Frame format conversion utilities
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
use anyhow::Result;
|
||||
use wayland_client::globals::GlobalList;
|
||||
use wayland_client::protocol::wl_buffer::WlBuffer;
|
||||
use wayland_client::protocol::wl_output::WlOutput;
|
||||
use wayland_client::QueueHandle;
|
||||
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1;
|
||||
|
||||
use crate::state::{CaptureSource, OutputInfo, State};
|
||||
|
||||
/// wlr-screencopy capture backend.
|
||||
///
|
||||
/// Holds the current in-flight frame protocol object. The
|
||||
/// `ZwlrScreencopyManagerV1` is stored separately in
|
||||
/// `State::EverythingButFmt` because binding it requires a `Dispatch`
|
||||
/// impl that lives in state.rs (T6b).
|
||||
pub struct CapWlrScreencopy {
|
||||
/// The active frame object for the current capture cycle.
|
||||
/// Set by Dispatch impls after `manager.capture_output()`, cleared
|
||||
/// by `on_done_with_frame()`.
|
||||
pub current_frame: Option<ZwlrScreencopyFrameV1>,
|
||||
}
|
||||
|
||||
impl CaptureSource for CapWlrScreencopy {
|
||||
/// Unit type: wlr-screencopy is fully asynchronous — `alloc_frame()`
|
||||
/// always returns `None`. The frame object is created by Dispatch
|
||||
/// impls calling `manager.capture_output()`, not by this method.
|
||||
type Frame = ();
|
||||
|
||||
fn new(
|
||||
_gm: &GlobalList,
|
||||
_output: &WlOutput,
|
||||
_output_info: &OutputInfo,
|
||||
_qh: &QueueHandle<State<Self>>,
|
||||
) -> Result<Self> {
|
||||
// Manager binding happens in state.rs during the ProbingOutputs →
|
||||
// EverythingButFmt stage transition (T6b). It requires a Dispatch
|
||||
// impl that doesn't exist yet, so we cannot call gm.bind() here.
|
||||
Ok(Self {
|
||||
current_frame: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_frame(&mut self) -> Option<Self::Frame> {
|
||||
// wlr-screencopy is asynchronous: the Dispatch impl creates a new
|
||||
// ZwlrScreencopyFrameV1 which triggers the buffer allocation flow
|
||||
// (buffer event → negotiate format → create DMA-BUF). This method
|
||||
// always returns None.
|
||||
None
|
||||
}
|
||||
|
||||
fn queue_copy(&mut self, buffer: &WlBuffer, _qh: &QueueHandle<State<Self>>) {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
frame.copy(buffer);
|
||||
} else {
|
||||
tracing::warn!("queue_copy: no current wlr-screencopy frame");
|
||||
}
|
||||
}
|
||||
|
||||
fn on_done_with_frame(&mut self, _frame: Self::Frame) {
|
||||
if let Some(frame) = self.current_frame.take() {
|
||||
frame.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user