screencapturekit/stream/output_trait.rs
1//! Output handler trait for stream callbacks
2//!
3//! Defines the interface for receiving captured frames and audio buffers.
4
5use crate::cm::CMSampleBuffer;
6
7use super::output_type::SCStreamOutputType;
8
9/// Trait for handling stream output
10///
11/// Implement this trait to receive callbacks when the stream captures frames or audio.
12///
13/// # Examples
14///
15/// ## Using a struct
16///
17/// ```
18/// use screencapturekit::stream::{
19/// output_trait::SCStreamOutputTrait,
20/// output_type::SCStreamOutputType,
21/// };
22/// use screencapturekit::cm::CMSampleBuffer;
23///
24/// struct MyHandler;
25///
26/// impl SCStreamOutputTrait for MyHandler {
27/// fn did_output_sample_buffer(&self, sample: CMSampleBuffer, of_type: SCStreamOutputType) {
28/// match of_type {
29/// SCStreamOutputType::Screen => {
30/// println!("Received video frame");
31/// }
32/// SCStreamOutputType::Audio => {
33/// println!("Received audio buffer");
34/// }
35/// SCStreamOutputType::Microphone => {
36/// println!("Received microphone audio");
37/// }
38/// }
39/// }
40/// }
41/// ```
42///
43/// ## Using a closure
44///
45/// Closures that match `Fn(CMSampleBuffer, SCStreamOutputType)` automatically
46/// implement this trait:
47///
48/// ```rust,no_run
49/// use screencapturekit::prelude::*;
50///
51/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
52/// # let content = SCShareableContent::get()?;
53/// # let display = &content.displays()[0];
54/// # let filter = SCContentFilter::create().with_display(display).with_excluding_windows(&[]).build();
55/// # let config = SCStreamConfiguration::default();
56/// let mut stream = SCStream::new(&filter, &config);
57///
58/// stream.add_output_handler(
59/// |_sample, _output_type| println!("Got frame!"),
60/// SCStreamOutputType::Screen
61/// );
62/// # Ok(())
63/// # }
64/// ```
65/// Trait implemented by handlers that receive captured samples from an
66/// [`SCStream`](crate::stream::SCStream).
67///
68/// # Concurrency
69///
70/// Handlers must be `Send + Sync`. `Send` is required because a handler
71/// registered on one thread is invoked on a `ScreenCaptureKit` dispatch
72/// queue, which runs on different OS threads over time. `Sync` is required
73/// because [`SCStream`] internally uses an `RwLock` to allow callbacks for
74/// independent output types (Screen / Audio / Microphone) to dispatch
75/// concurrently — without `Sync`, two threads could not concurrently
76/// invoke `&self` methods through the shared lock guard, even though
77/// `ScreenCaptureKit` serialises callbacks per output type in practice.
78///
79/// In practice, handlers that share state via `Arc<Mutex<…>>`, `Arc<RwLock<…>>`,
80/// or atomic primitives satisfy `Sync` automatically. Handlers that capture
81/// `Cell` / `RefCell` / `Rc` directly will not — wrap shared state in `Mutex`
82/// or `Arc<Mutex<…>>` instead.
83pub trait SCStreamOutputTrait: Send + Sync {
84 /// Called when a new sample buffer is available
85 ///
86 /// # Parameters
87 ///
88 /// - `sample_buffer`: The captured sample (video frame or audio buffer)
89 /// - `of_type`: Type of output (Screen, Audio, or Microphone)
90 fn did_output_sample_buffer(&self, sample_buffer: CMSampleBuffer, of_type: SCStreamOutputType);
91}
92
93/// Blanket implementation for closures
94///
95/// Any closure matching `Fn(CMSampleBuffer, SCStreamOutputType) + Send + Sync + 'static`
96/// can be used directly as an output handler. See the trait docs for
97/// the rationale behind the `Sync` bound.
98impl<F> SCStreamOutputTrait for F
99where
100 F: Fn(CMSampleBuffer, SCStreamOutputType) + Send + Sync + 'static,
101{
102 fn did_output_sample_buffer(&self, sample_buffer: CMSampleBuffer, of_type: SCStreamOutputType) {
103 self(sample_buffer, of_type);
104 }
105}