feat: measure footage across segments and test X11 capture

Matroska output can stay buffered until a segment closes, so progress counts
bytes across all segments and the stall limit scales with segment length.
Unknown capture source types now fail instead of recording an empty
placeholder. The rig gains an Xwayland mode for Xcomposite window capture.
This commit is contained in:
2026-10-08 14:25:23 +02:00
parent 263e7a964e
commit 444758f9ec
4 changed files with 142 additions and 75 deletions
+17 -8
View File
@@ -129,7 +129,9 @@ unsafe extern "C" fn log_to_stderr(
args: VaList,
_: *mut c_void,
) {
if level >= sys::LOG_DEBUG as i32 && std::env::var_os("CLIPBIN_OBS_DEBUG").is_none() {
// LOG_* constants are u32 in the Linux bindings and i32 in the Windows ones.
let level = i64::from(level);
if level >= i64::from(sys::LOG_DEBUG) && std::env::var_os("CLIPBIN_OBS_DEBUG").is_none() {
return;
}
let mut message = Dstr {
@@ -141,9 +143,9 @@ unsafe extern "C" fn log_to_stderr(
// allocates message.array with bmalloc, released with bfree.
unsafe { dstr_vprintf(&mut message, format, args) };
let label = match level {
level if level <= sys::LOG_ERROR as i32 => "error",
level if level <= sys::LOG_WARNING as i32 => "warning",
level if level <= sys::LOG_INFO as i32 => "info",
level if level <= i64::from(sys::LOG_ERROR) => "error",
level if level <= i64::from(sys::LOG_WARNING) => "warning",
level if level <= i64::from(sys::LOG_INFO) => "info",
_ => "debug",
};
if let Some(text) = owned(message.array) {
@@ -757,7 +759,7 @@ impl Pipeline {
total_bytes: sys::obs_output_get_total_bytes(self.output),
lagged_frames: sys::obs_get_lagged_frames() as u64,
current_segment: None,
current_segment_bytes: 0,
written_bytes: 0,
disk_available_bytes: None,
}
}
@@ -824,11 +826,18 @@ impl Drop for Pipeline {
/// # Safety
/// Must run on the OBS thread.
unsafe fn create_source(spec: &SourceSpec, name: &CStr) -> Result<*mut sys::obs_source_t, String> {
let id = cstring(spec.id);
// OBS creates a placeholder for unknown source types instead of failing.
// SAFETY: looks up a registered type by id.
if unsafe { sys::obs_source_get_display_name(id.as_ptr()) }.is_null() {
return Err(format!(
"the {} capture source is not available (its OBS plugin did not register it)",
spec.id
));
}
let settings = Data::new(&spec.settings);
// SAFETY: arguments are valid; the caller owns the returned reference.
let source = unsafe {
sys::obs_source_create_private(cstring(spec.id).as_ptr(), name.as_ptr(), settings.0)
};
let source = unsafe { sys::obs_source_create_private(id.as_ptr(), name.as_ptr(), settings.0) };
if source.is_null() {
Err(format!(
"OBS could not create the {} source (is its plugin loaded?)",