feat: add KWin/KDE Plasma screen capture via xdg-desktop-portal ScreenCast + PipeWire
Add a second capture backend for compositors without wlr-screencopy (KWin, GNOME, etc.) using the xdg-desktop-portal ScreenCast interface and PipeWire DMA-BUF streaming. New files: - src/backend_detect.rs: auto-detect wlr-screencopy vs portal backend - src/cap_portal.rs: Portal session setup + PipeWire DMA-BUF thread - src/state_portal.rs: StatePortal encoder pipeline (DMA-BUF → VAAPI) Changes: - Cargo.toml: add ashpd 0.13, tokio 1, pipewire 0.9, libspa 0.9, crossbeam-channel 0.5 - src/args.rs: add --backend CLI flag - src/avhw.rs: extract create_encoder() from inline State code - src/main.rs: route to portal or wlr-screencopy based on backend - src/state.rs: fix params.destroy() on dup failure, cleanup in_flight_surface on copy fail, use create_encoder() - tests/integration_test.rs: add --backend flag tests
This commit is contained in:
+54
-15
@@ -7,7 +7,7 @@ use ffmpeg_next as ff;
|
||||
use ffmpeg_next::ffi;
|
||||
use ffmpeg_next::packet::Mut as _;
|
||||
|
||||
use crate::transform::Transform;
|
||||
use crate::transform::{transpose_if_transform_transposed, Transform};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AvHwDevCtx
|
||||
@@ -463,6 +463,46 @@ impl EncState {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared encoder creation (used by both wlr-screencopy and portal paths)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create a fully configured encoder with VAAPI hardware acceleration.
|
||||
///
|
||||
/// Convenience wrapper around [`EncState::new`] that computes default values
|
||||
/// for `bitrate` and `gop_size` when not provided, and handles encoder dimension
|
||||
/// transposition for rotated/transformed outputs.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_encoder(
|
||||
drm_device: &Path,
|
||||
output_path: &Path,
|
||||
width: u32,
|
||||
height: u32,
|
||||
fps: u32,
|
||||
transform: Transform,
|
||||
bitrate: Option<u64>,
|
||||
gop_size: Option<u32>,
|
||||
) -> Result<EncState> {
|
||||
let (enc_w, enc_h) =
|
||||
transpose_if_transform_transposed(transform, width as i32, height as i32);
|
||||
let actual_bitrate = bitrate.unwrap_or_else(|| {
|
||||
2 * (width as u64) * (height as u64) * (fps as u64) / 100
|
||||
});
|
||||
let actual_gop_size = gop_size.unwrap_or(fps);
|
||||
EncState::new(
|
||||
drm_device,
|
||||
output_path,
|
||||
width,
|
||||
height,
|
||||
enc_w as u32,
|
||||
enc_h as u32,
|
||||
actual_bitrate,
|
||||
actual_gop_size,
|
||||
fps,
|
||||
transform,
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Filter graph (inline)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -535,31 +575,30 @@ fn build_filter_graph(
|
||||
src_ctx.link(0, &mut scale_ctx, 0);
|
||||
|
||||
match transform {
|
||||
Transform::Normal90 | Transform::Normal270 => {
|
||||
Transform::Normal => {
|
||||
scale_ctx.link(0, &mut sink_ctx, 0);
|
||||
}
|
||||
other => {
|
||||
let transpose = ff::filter::find("transpose_vaapi")
|
||||
.ok_or_else(|| anyhow::anyhow!("filter 'transpose_vaapi' not found"))?;
|
||||
let dir_val = match transform {
|
||||
let dir_val = match other {
|
||||
Transform::Normal90 => "1",
|
||||
Transform::Normal180 => "4",
|
||||
Transform::Normal270 => "2",
|
||||
_ => unreachable!(),
|
||||
Transform::Flipped => "5",
|
||||
Transform::Flipped90 => "3",
|
||||
Transform::Flipped180 => "6",
|
||||
Transform::Flipped270 => "0",
|
||||
Transform::Normal => unreachable!(),
|
||||
};
|
||||
let mut trans_ctx = graph.add(&transpose, "transpose", &format!("dir={dir_val}"))?;
|
||||
// SAFETY: transpose_vaapi needs hw_device_ctx for VAAPI device access.
|
||||
let mut trans_ctx =
|
||||
graph.add(&transpose, "transpose", &format!("dir={dir_val}"))?;
|
||||
unsafe {
|
||||
(*trans_ctx.as_mut_ptr()).hw_device_ctx = hw_dev.ref_clone();
|
||||
}
|
||||
scale_ctx.link(0, &mut trans_ctx, 0);
|
||||
trans_ctx.link(0, &mut sink_ctx, 0);
|
||||
}
|
||||
Transform::Normal180 => {
|
||||
tracing::warn!(
|
||||
"Normal180 transform detected; rotation correction deferred to follow-up"
|
||||
);
|
||||
scale_ctx.link(0, &mut sink_ctx, 0);
|
||||
}
|
||||
_ => {
|
||||
scale_ctx.link(0, &mut sink_ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
graph
|
||||
|
||||
Reference in New Issue
Block a user