Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pixelflux/src/encoders/nvenc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1968,7 +1968,7 @@ mod gpu_tests {
fn gpu_resolution_reconfigure_roundtrip() {
let mut s = settings(1280, 720, 60.0);
let t0 = std::time::Instant::now();
let mut enc = NvencEncoder::new(&s, ptr::null(), None).expect("NVENC init");
let mut enc = NvencEncoder::new(&s, ptr::null()).expect("NVENC init");
let init_ms = t0.elapsed().as_secs_f64() * 1000.0;

let mut stream: Vec<u8> = Vec::new();
Expand Down Expand Up @@ -2049,7 +2049,7 @@ mod gpu_tests {
let mut s = settings(1280, 720, 60.0);
s.video_cbr_mode = true;
s.video_bitrate_kbps = 4000;
let mut enc = NvencEncoder::new(&s, ptr::null(), None).expect("NVENC init");
let mut enc = NvencEncoder::new(&s, ptr::null()).expect("NVENC init");
let f720 = frame(1280, 720, 10);
for i in 0..3u64 {
enc.encode_cpu_argb(&f720, 1280 * 4, i, 25, i == 0)
Expand Down Expand Up @@ -2082,7 +2082,7 @@ mod gpu_tests {
}
let s = settings(1920, 1080, 60.0);
let before = used_mb();
let mut enc = NvencEncoder::new(&s, ptr::null(), None).expect("init");
let mut enc = NvencEncoder::new(&s, ptr::null()).expect("init");
let f = frame(1920, 1080, 5);
for i in 0..3u64 {
enc.encode_cpu_argb(&f, 1920 * 4, i, 25, i == 0).expect("encode");
Expand All @@ -2097,7 +2097,7 @@ mod gpu_tests {
#[ignore]
fn gpu_init_above_default_headroom() {
let s = settings(2160, 4096, 30.0);
let mut enc = NvencEncoder::new(&s, ptr::null(), None).expect("NVENC init portrait 4K");
let mut enc = NvencEncoder::new(&s, ptr::null()).expect("NVENC init portrait 4K");
assert_eq!(enc.init_params.maxEncodeWidth, 4096);
assert_eq!(enc.init_params.maxEncodeHeight, 4096);
let f = frame(2160, 4096, 20);
Expand Down
22 changes: 11 additions & 11 deletions pixelflux/src/encoders/oh264.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ mod tests {
target_fps: 30.0,
..Default::default()
};
let mut enc = Openh264Encoder::new(&s, None).expect("openh264 init");
let mut enc = Openh264Encoder::new(&s).expect("openh264 init");
let stride = 128 * 4;
let idr = enc.encode_host_argb(&busy_frame(128, 96, 0), stride, 0, true, false).expect("encode idr");
assert!(idr.len() > 10, "IDR frame should produce output");
Expand Down Expand Up @@ -489,7 +489,7 @@ mod tests {
omit_stripe_headers: true,
..Default::default()
};
let mut enc = Openh264Encoder::new(&s, None).expect("openh264 init");
let mut enc = Openh264Encoder::new(&s).expect("openh264 init");
let out = enc.encode_host_argb(&busy_frame(128, 96, 0), 128 * 4, 0, true, false).expect("encode");
assert!(
out.starts_with(&[0, 0, 0, 1]) || out.starts_with(&[0, 0, 1]),
Expand All @@ -511,7 +511,7 @@ mod tests {
target_fps: 30.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let _ = e.encode_host_argb(&busy_frame(w, h, 0), stride, 0, true, false).unwrap();
(1..24).map(|t| e.encode_host_argb(&busy_frame(w, h, t), stride, t as u64, false, false).unwrap().len()).sum()
};
Expand All @@ -533,7 +533,7 @@ mod tests {
target_fps: 30.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let _ = e.encode_host_argb(&busy_frame(w, h, 0), stride, 0, true, false).unwrap();
let high: usize =
(1..24).map(|t| e.encode_host_argb(&busy_frame(w, h, t), stride, t as u64, false, false).unwrap().len()).sum();
Expand All @@ -557,7 +557,7 @@ mod tests {
target_fps: 30.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let _ = e.encode_host_argb(&busy_frame(w, h, 0), stride, 0, true, false).unwrap();
let low: usize =
(1..24).map(|t| e.encode_host_argb(&busy_frame(w, h, t), stride, t as u64, false, false).unwrap().len()).sum();
Expand All @@ -584,7 +584,7 @@ mod tests {
target_fps: 30.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let mut total = e.encode_host_argb(&gradient_frame(w, h, 0), stride, 0, true, false).unwrap().len();
total += (1..24)
.map(|t| e.encode_host_argb(&gradient_frame(w, h, t), stride, t as u64, false, false).unwrap().len())
Expand Down Expand Up @@ -614,7 +614,7 @@ mod tests {
};
let frame = busy_frame(w, h, 0);
for rgba in [false, true] {
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let out = e.encode_host_argb(&frame, stride, 0, true, rgba).expect("encode");
assert!(out.len() > 10, "rgba_input={rgba} must produce output");
assert_eq!(out[0], 0x04, "rgba_input={rgba} output must carry the wire header");
Expand Down Expand Up @@ -642,7 +642,7 @@ mod slice_tests {
video_cbr_mode: true,
..Default::default()
};
let mut enc = Openh264Encoder::new(&s, None).expect("encoder");
let mut enc = Openh264Encoder::new(&s).expect("encoder");
let frame = vec![0x80u8; 640 * 480 * 4];
let out = enc.encode_host_argb(&frame, 640 * 4, 0, true, false).expect("encode");
let mut nals = 0;
Expand Down Expand Up @@ -705,7 +705,7 @@ mod slice_tests {
target_fps: 60.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
let mut total = 0usize;
let mut idrs = 0usize;
for t in 0..60u64 {
Expand Down Expand Up @@ -752,7 +752,7 @@ mod slice_tests {
target_fps: 60.0,
..Default::default()
};
let mut e = Openh264Encoder::new(&s, None).unwrap();
let mut e = Openh264Encoder::new(&s).unwrap();
unsafe {
let mut p: openh264_sys2::SEncParamExt = std::mem::zeroed();
let ret = e.encoder.raw_api().get_option(
Expand Down Expand Up @@ -798,7 +798,7 @@ mod rebuild_cost {
..Default::default()
};
let t = std::time::Instant::now();
let mut e = Openh264Encoder::new(&s, None).expect("init");
let mut e = Openh264Encoder::new(&s).expect("init");
let init_ms = t.elapsed().as_secs_f64() * 1000.0;
let frame = vec![128u8; 1920 * 1080 * 4];
let t = std::time::Instant::now();
Expand Down
24 changes: 13 additions & 11 deletions pixelflux/src/encoders/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rayon::prelude::*;
use smithay::utils::{Physical, Rectangle};
use std::ffi::CString;
use std::ptr;
use std::sync::Arc;
use yuv::{BufferStoreMut, YuvConversionMode, YuvPlanarImageMut, YuvRange, YuvStandardMatrix};

/// Upper bound on the horizontal stripes the CPU encoder splits a frame into, so the
Expand Down Expand Up @@ -580,13 +581,14 @@ impl StripeState {
///
/// # Fields
///
/// * `data` - Compressed payload (JPEG or H.264 NAL units).
/// * `data` - Compressed payload (JPEG or H.264 NAL units). `Arc`-shared so the recording
/// tap can retain the frame for its client queues without copying the bytes.
/// * `data_type` - Codec tag: **1 = JPEG**, **2 = H.264**.
/// * `stripe_y_start` - Y pixel coordinate of the stripe's top edge within the frame.
/// * `stripe_height` - Height of the stripe in pixels.
/// * `frame_id` - Frame sequence number this stripe belongs to.
pub struct EncodedStripe {
pub data: Vec<u8>,
pub data: Arc<Vec<u8>>,
pub data_type: i32,
pub stripe_y_start: i32,
pub stripe_height: i32,
Expand Down Expand Up @@ -932,7 +934,7 @@ pub fn encode_cpu(
std::mem::take(&mut stripe_state.packet_buf)
};
Some(EncodedStripe {
data,
data: Arc::new(data),
data_type: 1,
stripe_y_start: y_start as i32,
stripe_height: actual_height as i32,
Expand Down Expand Up @@ -1025,7 +1027,7 @@ pub fn encode_cpu(
&mut stripe_state.packet_buf,
) {
Some(EncodedStripe {
data: std::mem::take(&mut stripe_state.packet_buf),
data: Arc::new(std::mem::take(&mut stripe_state.packet_buf)),
data_type: 2,
stripe_y_start: y_start as i32,
stripe_height: actual_height as i32,
Expand Down Expand Up @@ -1132,14 +1134,14 @@ mod tests {
(w, h).into(),
)];
let dirty = super::encode_cpu(
&mut stripes, &pixels, w, h, &full, &settings, 0, false, false, None, false,
&mut stripes, &pixels, w, h, &full, &settings, 0, false, false, false,
);
assert!(!dirty.is_empty(), "damaged frame must encode");

let mut fired_at = None;
for frame in 1..=20u16 {
let out = super::encode_cpu(
&mut stripes, &pixels, w, h, &[], &settings, frame, false, false, None, false,
&mut stripes, &pixels, w, h, &[], &settings, frame, false, false, false,
);
if !out.is_empty() {
assert!(fired_at.is_none(), "paint-over must fire exactly once");
Expand Down Expand Up @@ -1177,14 +1179,14 @@ mod tests {
};
let mut stripes = Vec::new();
let first = super::encode_cpu(
&mut stripes, &static_px, w, h, &[], &settings, 0, false, true, None, false,
&mut stripes, &static_px, w, h, &[], &settings, 0, false, true, false,
);
assert!(!first.is_empty(), "first frame hashes as changed and encodes");

let mut fired_at = None;
for frame in 1..=20u16 {
let out = super::encode_cpu(
&mut stripes, &static_px, w, h, &[], &settings, frame, false, true, None, false,
&mut stripes, &static_px, w, h, &[], &settings, frame, false, true, false,
);
if !out.is_empty() {
assert!(fired_at.is_none(), "paint-over must fire exactly once while static");
Expand All @@ -1194,7 +1196,7 @@ mod tests {
assert_eq!(fired_at, Some(settings.paint_over_trigger_frames as u16));

let woke = super::encode_cpu(
&mut stripes, &changed_px, w, h, &[], &settings, 21, false, true, None, false,
&mut stripes, &changed_px, w, h, &[], &settings, 21, false, true, false,
);
assert!(!woke.is_empty(), "content change after idle must encode");
}
Expand Down Expand Up @@ -1313,7 +1315,7 @@ mod qp_bound_sweep {
let mut out = Vec::new();
enc.encode_with_headers(
&y, &u, &v, W as i32, (W / 2) as i32, (W / 2) as i32,
i as i64, i == 0, &[], true, &mut out, None,
i as i64, i == 0, &[], true, &mut out,
);
out
})
Expand All @@ -1336,7 +1338,7 @@ mod qp_bound_sweep {
video_max_qp: max_qp,
..Default::default()
};
let mut enc = Openh264Encoder::new(&s, None).expect("oh264 init");
let mut enc = Openh264Encoder::new(&s).expect("oh264 init");
(0..FRAMES)
.map(|i| {
let y = text_luma(i);
Expand Down
36 changes: 19 additions & 17 deletions pixelflux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ pub struct RustCaptureSettings {
/// infinite GOP, 3 when scheduled keyframes are enabled.
pub video_vbv_multiplier: f64,
/// Seconds between scheduled recovery keyframes; `<= 0` keeps the GOP infinite
/// (IDRs only on demand: client join / reset, recording cadence).
/// (IDRs only on demand: client join / reset, recorder connect).
pub keyframe_interval_s: f64,
/// Rate-controlled (CBR) QP clamp: `video_max_qp` bounds the quality FLOOR (screen text stays
/// legible under motion at the cost of overshooting impossible targets), `video_min_qp` bounds
Expand Down Expand Up @@ -496,9 +496,6 @@ pub(crate) fn extract_settings(settings: &Bound<'_, PyAny>) -> PyResult<RustCapt
.getattr("recording_socket")
.ok()
.and_then(|v| v.extract::<String>().ok())
.filter(|s| !s.is_empty())
.or_else(|| std::env::var("PIXELFLUX_RECORDING_SOCKET").ok().filter(|s| !s.is_empty()))
.or_else(|| std::env::var("SELKIES_RECORDING_SOCKET").ok().filter(|s| !s.is_empty()))
.unwrap_or_default(),
omit_stripe_headers: settings
.getattr("omit_stripe_headers")
Expand Down Expand Up @@ -1183,7 +1180,7 @@ fn wayland_encode_loop(pool: &WlFramePool, cfg: WlEncodeConfig) -> Option<GpuEnc
};
match result {
Ok(data) if !data.is_empty() => out.push(EncodedStripe {
data,
data: Arc::new(data),
data_type: 2,
stripe_y_start: 0,
stripe_height: height,
Expand All @@ -1207,7 +1204,7 @@ fn wayland_encode_loop(pool: &WlFramePool, cfg: WlEncodeConfig) -> Option<GpuEnc
let stride = (width * 4) as usize;
match enc.encode_host_argb(&f.buf, stride, f.frame_id as u64, force_idr, cfg.use_gpu) {
Ok(data) if !data.is_empty() => out.push(EncodedStripe {
data,
data: Arc::new(data),
data_type: 2,
stripe_y_start: 0,
stripe_height: height,
Expand Down Expand Up @@ -1708,12 +1705,17 @@ fn run_wayland_thread(
crate::recording_sink::RecordingSink::try_bind(&settings.recording_socket);

if let Some(output) = state.outputs.first() {
let current_mode = output.current_mode().unwrap();
let current_w = current_mode.size.w;
let current_h = current_mode.size.h;
let current_scale = output.current_scale().fractional_scale();
let current_refresh = current_mode.refresh;
// Never panic the compositor thread: an output momentarily
// without a current mode falls back to the requested geometry so
// the reconfigure below is a no-op for size/refresh instead of
// unwrap-panicking, which would drop every Wayland client with no
// recovery short of a process restart.
let target_refresh = (settings.target_fps * 1000.0).round() as i32;
let (current_w, current_h, current_refresh) = match output.current_mode() {
Some(m) => (m.size.w, m.size.h, m.refresh),
None => (settings.width, settings.height, target_refresh),
};
let current_scale = output.current_scale().fractional_scale();

let scale = settings.scale.max(0.1);
let logical_width = (settings.width as f64 / scale).round() as i32;
Expand Down Expand Up @@ -2858,7 +2860,7 @@ fn run_wayland_thread(
state.encode_stats.stripes.fetch_add(1, Ordering::Relaxed);
if let Some(ref tx) = state.deliver_tx {
let stripes = vec![EncodedStripe {
data, data_type: 2, stripe_y_start: 0,
data: Arc::new(data), data_type: 2, stripe_y_start: 0,
stripe_height: height, frame_id: state.frame_counter as i32,
}];
if let Some(ref socket) = state.recording_sink {
Expand Down Expand Up @@ -2954,7 +2956,7 @@ fn run_wayland_thread(
/// four stripe-metadata ints as Python attributes.
#[pyclass]
struct StripeFrame {
data: Vec<u8>,
data: Arc<Vec<u8>>,
#[pyo3(get, set)]
data_type: i32,
#[pyo3(get, set)]
Expand All @@ -2966,10 +2968,10 @@ struct StripeFrame {
}

impl StripeFrame {
/// Hot-path constructor: MOVES the encoded buffer in (no copy) and carries stripe
/// Hot-path constructor: shares the encoder's buffer by `Arc` (no copy) and carries stripe
/// metadata as attributes, so the consumer can read it without parsing a header
/// (required for omit_stripe_headers).
fn new_owned_meta(data: Vec<u8>, data_type: i32, stripe_y_start: i32, stripe_height: i32, frame_id: i32) -> Self {
fn new_owned_meta(data: Arc<Vec<u8>>, data_type: i32, stripe_y_start: i32, stripe_height: i32, frame_id: i32) -> Self {
Self { data, data_type, stripe_y_start, stripe_height, frame_id }
}
}
Expand All @@ -2981,7 +2983,7 @@ impl StripeFrame {
#[new]
#[pyo3(signature = (data, data_type = 0, stripe_y_start = 0, stripe_height = 0, frame_id = 0))]
fn new(data: Vec<u8>, data_type: i32, stripe_y_start: i32, stripe_height: i32, frame_id: i32) -> Self {
Self { data, data_type, stripe_y_start, stripe_height, frame_id }
Self { data: Arc::new(data), data_type, stripe_y_start, stripe_height, frame_id }
}

fn __len__(&self) -> usize {
Expand Down Expand Up @@ -3210,7 +3212,7 @@ fn stripe_frame_from_buffer(
stripe_height: i32,
frame_id: i32,
) -> StripeFrame {
StripeFrame::new_owned_meta(data, data_type, stripe_y_start, stripe_height, frame_id)
StripeFrame::new_owned_meta(Arc::new(data), data_type, stripe_y_start, stripe_height, frame_id)
}

/// Capture configuration read by `start_capture` (each field by attribute name via
Expand Down
2 changes: 1 addition & 1 deletion pixelflux/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl X11Pipeline {
match res {
Ok(data) if !data.is_empty() => {
vec![EncodedStripe {
data,
data: Arc::new(data),
data_type: 2,
stripe_y_start: 0,
stripe_height: height,
Expand Down
Loading