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,
) -> Result<Self, SCError>
pub fn new( filter: &SCContentFilter, config: &SCStreamConfiguration, buffer_capacity: usize, output_type: SCStreamOutputType, ) -> Result<Self, SCError>
Create a new async stream
§Arguments
filter- Content filter specifying what to captureconfig- Stream configurationbuffer_capacity- Max frames to buffer (oldest dropped when full);0is treated as1, since the queue must hold the sample it is about to hand to the consumeroutput_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,
) -> Result<(), SCError>
pub fn add_output_type( &mut self, output_type: SCStreamOutputType, ) -> Result<(), SCError>
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.
§Errors
Returns the SCStream::add_output_handler
error when registration fails, for example if the stream configuration
does not enable that type (e.g. audio capture was not configured); on
failure the already-registered types keep flowing and the queue stays
open.
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 sample queue has been closed — because
stop_capture succeeded, because this
AsyncSCStream’s handlers were dropped, or because ScreenCaptureKit
stopped the stream with an error (see take_error).
Buffered frames still drain through next after this
turns true.
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). If starting fails, the sample queue
is closed so next resolves to None rather than pending
forever on a stream that never ran.
§Restarting is not supported
ScreenCaptureKit cannot restart a stopped SCStream. Once
stop_capture has succeeded, this resolves to
Err without touching the native stream; create a new AsyncSCStream
to capture again.
§Errors
The awaited result is Err(SCError::CaptureStartFailed) if the stream
fails to start or has already been stopped.
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.
A clean stop is never reported to the delegate, so awaiting a successful
stop also closes the sample queue: next resolves to
None once the already-buffered frames drain, and
take_error stays None. The stream cannot be
restarted afterwards — see start_capture.
§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.