Skip to main content

AsyncSCStream

Struct AsyncSCStream 

Source
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

Source

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 capture
  • config - Stream configuration
  • buffer_capacity - Max frames to buffer (oldest dropped when full)
  • output_type - Type of output (Screen, Audio, Microphone)
Source

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.

Source

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.

Source

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.

Source

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;
Source

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).

Source

pub fn try_next(&self) -> Option<CMSampleBuffer>

Try to get a sample without waiting

Source

pub fn try_next_typed(&self) -> Option<(CMSampleBuffer, SCStreamOutputType)>

Try to get a sample together with its output type, without waiting.

Source

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).

Source

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.

Source

pub fn buffered_count(&self) -> usize

Get the number of buffered samples

Source

pub fn clear_buffer(&self)

Clear all buffered samples

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn inner(&self) -> &SCStream

Get a reference to the underlying stream

Trait Implementations§

Source§

impl Debug for AsyncSCStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.