1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Capture configuration
use crate::error::{CaptureError, Result};
/// Configuration for the capture process.
#[derive(Debug)]
pub struct CaptureConfig {
/// The config for video
pub video: VideoConfig,
/// The config for audio
pub audio: Option<AudioConfig>,
}
impl CaptureConfig {
/// Reject a configuration no backend can honour, before anything is started.
pub(crate) fn validate(&self) -> Result<()> {
// Every backend sends with `try_send`, which on a rendezvous channel only succeeds
// while a consumer happens to be parked in `recv`, so a capacity of 0 would drop
// almost everything instead of buffering it.
let zero = self.video.channel_capacity == 0
|| self.audio.as_ref().is_some_and(|a| a.channel_capacity == 0);
if zero {
return Err(CaptureError::ZeroChannelCapacity);
}
Ok(())
}
}
/// Configuration for the capture video.
#[derive(Debug)]
pub struct VideoConfig {
/// Capacity of the channel carrying video frames, at least 1.
///
/// Video and audio get a channel each: a frame is orders of magnitude larger than an
/// audio buffer, and dropping one is cheap where dropping audio is not.
pub channel_capacity: usize,
/// The window id to hide from capture:
/// - Windows: `HWND`
/// - macOS: `CGWindowID`, which `platform::window_id_from_ns_view` derives from the
/// `NSView` a window handle gives you.
/// - Linux: unsupported, a non-empty list is [`Unsupported::HideWindows`]. Neither
/// Wayland nor X11 lets a client opt a window out of a screencast.
///
/// [`Unsupported::HideWindows`]: crate::error::Unsupported::HideWindows
pub hide: Vec<isize>,
/// The target to capture.
///
/// Everything is supported everywhere except where noted:
/// - Linux: the XDG portal picker always makes the final choice, so `Primary` and
/// `Monitor` only restrict it to monitors (the index is *not* honoured), and
/// `Window`/`WindowName` are [`Unsupported::WindowTarget`].
/// - `Pick` blocks until the user chooses, so on Windows and macOS it must not be
/// called from the thread driving the UI, and a user who dismisses the picker gets
/// [`CaptureError::Cancelled`].
///
/// [`Unsupported::WindowTarget`]: crate::error::Unsupported::WindowTarget
/// [`CaptureError::Cancelled`]: crate::error::CaptureError::Cancelled
pub target: Target,
/// Cap on delivered frames per second, `None` to leave the source uncapped.
///
/// A cap, not a target: it cannot raise a rate the machine cannot sustain. The
/// platform's own knob is set where there is one, and frames that still arrive too
/// fast are dropped before being copied out of the capture buffer.
pub fps: Option<u32>,
}
/// Configuration for the capture audio.
#[derive(Debug)]
pub struct AudioConfig {
/// Capacity of the channel carrying audio frames, at least 1.
///
/// Worth setting far deeper than the video one: audio frames are small, and a gap is an
/// audible artefact rather than a frame the encoder can stretch over.
pub channel_capacity: usize,
}
/// Capture target.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Target {
/// Capture the primary monitor.
Primary,
/// Capture a specific monitor by its index.
Monitor(isize),
/// Capture a specific window by its window id:
/// - Windows: `HWND`
/// - macOS: `CGWindowID`
/// - Linux: unsupported, [`Unsupported::WindowTarget`]
///
/// [`Unsupported::WindowTarget`]: crate::error::Unsupported::WindowTarget
Window(isize),
/// Capture the first visible window whose title matches the given regex.
WindowName(String),
/// Let the user choose with the system picker.
///
/// The inner value is the `HWND` to present the picker from; it has to be a window this
/// process owns.
///
/// `create` blocks until the user has chosen, and the wait does not pump messages, so it
/// must not be called from the thread that owns that `HWND` (or from any other STA
/// thread) — doing so deadlocks.
#[cfg(target_os = "windows")]
Pick(isize),
/// Let the user choose with the system picker.
///
/// macOS needs 14.0 or newer, and `create` blocks until the user has chosen. The picker
/// answers on the main queue, so `create` must not be called from the main thread —
/// doing so deadlocks.
///
/// On Linux the portal's picker also lists this process's own windows, and nothing can
/// take them out. Make sure such a window has drawn before calling `create`: GNOME's
/// mutter has been seen to crash capturing one that had not.
#[cfg(not(target_os = "windows"))]
Pick,
}