pub struct AsyncSCStream { /* private fields */ }Expand description
Async wrapper for SCStream with integrated frame iteration
Provides async methods for stream lifecycle and frame iteration. Executor-agnostic - works with any async runtime.
§Examples
use screencapturekit::async_api::{AsyncSCShareableContent, AsyncSCStream};
use screencapturekit::stream::configuration::SCStreamConfiguration;
use screencapturekit::stream::content_filter::SCContentFilter;
use screencapturekit::stream::output_type::SCStreamOutputType;
let content = AsyncSCShareableContent::get().await?;
let display = &content.displays()[0];
let filter = SCContentFilter::create().with_display(display).with_excluding_windows(&[]).build();
let config = SCStreamConfiguration::new()
.with_width(1920)
.with_height(1080);
let stream = AsyncSCStream::new(&filter, &config, 30, SCStreamOutputType::Screen);
stream.start_capture().await?;
// Process frames asynchronously
while let Some(frame) = stream.next().await {
println!("Got frame!");
}Async wrapper for SCStream with integrated frame iteration.
§Back-pressure and frame loss
AsyncSCStream buffers samples in a bounded internal queue sized
by the buffer_capacity argument to AsyncSCStream::new. When the
queue is full and a new sample arrives from ScreenCaptureKit, the
oldest queued sample is dropped to make room — the stream is
lossy by design.
This is the right policy for real-time UI rendering, screen-share
previews, and live encoding: a slow consumer would rather see the
most recent frame than a stale one. It is the wrong policy for
lossless capture (e.g. saving every frame to disk for later
editing) — for that, use the synchronous SCStream
directly, where back-pressure is naturally enforced by Apple’s
queueDepth setting and your handler’s runtime.
To detect when frames are being dropped, watch buffered_count()
against buffer_capacity over time, or instrument your handler
with a per-frame timestamp delta and compare to your expected
frame interval.
Implementations§
Source§impl AsyncSCStream
impl AsyncSCStream
Sourcepub fn new(
filter: &SCContentFilter,
config: &SCStreamConfiguration,
buffer_capacity: usize,
output_type: SCStreamOutputType,
) -> Self
pub fn new( filter: &SCContentFilter, config: &SCStreamConfiguration, buffer_capacity: usize, output_type: SCStreamOutputType, ) -> Self
Create a new async stream
§Arguments
filter- Content filter specifying what to captureconfig- Stream configurationbuffer_capacity- Max frames to buffer (oldest dropped when full)output_type- Type of output (Screen, Audio, Microphone)
Sourcepub fn next(&self) -> NextSample<'_> ⓘ
pub fn next(&self) -> NextSample<'_> ⓘ
Get the next sample buffer asynchronously
Returns None when the stream is closed. For a multi-output stream
(see add_output_type) use
next_typed to also learn each sample’s
SCStreamOutputType.
Sourcepub fn next_typed(&self) -> NextSampleTyped<'_> ⓘ
pub fn next_typed(&self) -> NextSampleTyped<'_> ⓘ
Get the next sample buffer together with its output type.
Use this when the stream carries more than one output type (e.g. screen
and audio) and you need to tell the samples apart. Returns None when
the stream is closed.
Sourcepub fn frames(&self) -> SampleStream<'_>
pub fn frames(&self) -> SampleStream<'_>
Borrow the captured frames as a Stream of
CMSampleBuffers.
This unlocks the futures::StreamExt combinator ecosystem
(map, filter, take, for_each, zip, …) for processing frames:
use futures_util::StreamExt;
let frames: Vec<_> = stream.frames().take(30).collect().await;The returned stream borrows self; for a multi-output stream use
frames_typed to keep each sample’s output type.
Sourcepub fn frames_typed(&self) -> TypedSampleStream<'_>
pub fn frames_typed(&self) -> TypedSampleStream<'_>
Borrow the captured frames as a Stream of
(CMSampleBuffer, SCStreamOutputType) pairs.
Like frames but keeps each sample’s output type, so a
stream carrying both audio and video (see
add_output_type) can route samples with
StreamExt combinators:
use futures_util::StreamExt;
use screencapturekit::stream::output_type::SCStreamOutputType;
let audio: Vec<_> = stream
.frames_typed()
.filter(|(_, kind)| std::future::ready(*kind == SCStreamOutputType::Audio))
.take(10)
.collect()
.await;Sourcepub fn add_output_type(&mut self, output_type: SCStreamOutputType) -> bool
pub fn add_output_type(&mut self, output_type: SCStreamOutputType) -> bool
Also deliver samples of an additional output type.
By default an AsyncSCStream carries the single output type passed to
new. Call this to capture more than one type from one
stream — for example add SCStreamOutputType::Audio to a stream
created for SCStreamOutputType::Screen to capture audio and video
together. Samples from every registered type share the same lossy
buffer; use next_typed /
try_next_typed to distinguish them.
Returns true if the output type was registered. Registration can fail
if the stream configuration does not enable that type (e.g. audio
capture was not configured).
Sourcepub fn try_next(&self) -> Option<CMSampleBuffer>
pub fn try_next(&self) -> Option<CMSampleBuffer>
Try to get a sample without waiting
Sourcepub fn try_next_typed(&self) -> Option<(CMSampleBuffer, SCStreamOutputType)>
pub fn try_next_typed(&self) -> Option<(CMSampleBuffer, SCStreamOutputType)>
Try to get a sample together with its output type, without waiting.
Sourcepub fn is_closed(&self) -> bool
pub fn is_closed(&self) -> bool
Check if the stream has been closed
Returns true once the stream has stopped — either because this
AsyncSCStream was dropped or because ScreenCaptureKit stopped it
with an error (see take_error).
Sourcepub fn take_error(&self) -> Option<SCError>
pub fn take_error(&self) -> Option<SCError>
Take the error that stopped the stream, if any.
When ScreenCaptureKit stops the stream with an error (e.g. the
captured display is disconnected or screen-recording permission is
revoked), the sample iterator is closed — next resolves
to None after any buffered frames drain — and the SCError is stored
here. Call this once the iteration loop ends to distinguish an error stop
from a normal end of stream:
while let Some(_frame) = stream.next().await {
// process frames …
}
if let Some(err) = stream.take_error() {
eprintln!("capture stopped with error: {err}");
}The stored error is cleared once taken.
Sourcepub fn buffered_count(&self) -> usize
pub fn buffered_count(&self) -> usize
Get the number of buffered samples
Sourcepub fn clear_buffer(&self)
pub fn clear_buffer(&self)
Clear all buffered samples
Sourcepub fn start_capture(&self) -> StreamControlFuture ⓘ
pub fn start_capture(&self) -> StreamControlFuture ⓘ
Start capture asynchronously.
Resolves when ScreenCaptureKit confirms the stream has started.
Unlike SCStream::start_capture,
awaiting this does not block the executor thread — the task is parked
via its Waker and resumed from the Swift completion callback.
The capture is initiated eagerly when this method is called; .await
observes the completion (or error).
§Errors
The awaited result is Err(SCError::CaptureStartFailed) if the stream
fails to start.
Sourcepub fn stop_capture(&self) -> StreamControlFuture ⓘ
pub fn stop_capture(&self) -> StreamControlFuture ⓘ
Stop capture asynchronously.
Resolves when ScreenCaptureKit confirms the stream has stopped. Awaiting
this does not block the executor thread.
§Errors
The awaited result is Err(SCError::CaptureStopFailed) if the stream
fails to stop.
Sourcepub fn update_configuration(
&self,
config: &SCStreamConfiguration,
) -> StreamControlFuture ⓘ
pub fn update_configuration( &self, config: &SCStreamConfiguration, ) -> StreamControlFuture ⓘ
Update stream configuration asynchronously.
Resolves when the reconfiguration completes. Awaiting this does not block the executor thread.
§Errors
The awaited result is Err(SCError::StreamError) if the update fails.
Sourcepub fn update_content_filter(
&self,
filter: &SCContentFilter,
) -> StreamControlFuture ⓘ
pub fn update_content_filter( &self, filter: &SCContentFilter, ) -> StreamControlFuture ⓘ
Update content filter asynchronously.
Resolves when the filter swap completes. Awaiting this does not block the executor thread.
§Errors
The awaited result is Err(SCError::StreamError) if the update fails.