Skip to content

doom-fish/screencapturekit-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ScreenCaptureKit-rs

Crates.io Documentation License Build Status

πŸ’Ό Looking for a hosted desktop recording API?
Check out Recall.ai - an API for recording Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.

Safe, idiomatic Rust bindings for macOS ScreenCaptureKit framework.

Capture screen content, windows, and applications with high performance and low overhead on macOS 12.3+.

✨ Features

  • πŸŽ₯ Screen & Window Capture - Capture displays, windows, or specific applications
  • πŸ”Š Audio Capture - Capture system audio and microphone input
  • ⚑ Real-time Processing - High-performance frame callbacks with custom dispatch queues
  • πŸ—οΈ Builder Pattern API - Clean, type-safe configuration with ::builder()
  • πŸ”„ Async Support - Runtime-agnostic async API (works with Tokio, async-std, smol, etc.)
  • 🎨 IOSurface Access - Zero-copy GPU texture access for Metal/OpenGL
  • πŸ›‘οΈ Memory Safe - Proper reference counting and leak-free by design
  • πŸ“¦ Zero Dependencies - No runtime dependencies (only dev dependencies for examples)

πŸ“¦ Installation

Add to your Cargo.toml:

[dependencies]
screencapturekit = "1"

For async support:

[dependencies]
screencapturekit = { version = "1", features = ["async"] }

For latest macOS features:

[dependencies]
screencapturekit = { version = "1", features = ["macos_15_0"] }

πŸš€ Quick Start

Basic Screen Capture

use screencapturekit::prelude::*;

struct Handler;

impl SCStreamOutputTrait for Handler {
    fn did_output_sample_buffer(&self, sample: CMSampleBuffer, _type: SCStreamOutputType) {
        println!("πŸ“Ή Received frame at {} pts", sample.get_presentation_timestamp());
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Get available displays
    let content = SCShareableContent::get()?;
    let display = &content.displays()[0];
    
    // Configure capture
    let filter = SCContentFilter::builder()
        .display(display)
        .exclude_windows(&[])
        .build();
    
    let config = SCStreamConfiguration::new()
        .with_width(1920)
        .with_height(1080)
        .with_pixel_format(PixelFormat::BGRA);
    
    // Start streaming
    let mut stream = SCStream::new(&filter, &config);
    stream.add_output_handler(Handler, SCStreamOutputType::Screen);
    stream.start_capture()?;
    
    // Capture runs in background...
    std::thread::sleep(std::time::Duration::from_secs(5));
    
    stream.stop_capture()?;
    Ok(())
}

Async Capture

use screencapturekit::async_api::{AsyncSCShareableContent, AsyncSCStream};
use screencapturekit::prelude::*;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Get content asynchronously
    let content = AsyncSCShareableContent::get().await?;
    let display = &content.displays()[0];
    
    // Create filter and config
    let filter = SCContentFilter::builder()
        .display(display)
        .exclude_windows(&[])
        .build();
    
    let config = SCStreamConfiguration::new()
        .with_width(1920)
        .with_height(1080);
    
    // Create async stream with frame buffer
    let stream = AsyncSCStream::new(&filter, &config, 30, SCStreamOutputType::Screen);
    stream.start_capture()?;
    
    // Capture frames asynchronously
    for _ in 0..10 {
        if let Some(frame) = stream.next().await {
            println!("πŸ“Ή Got frame!");
        }
    }
    
    stream.stop_capture()?;
    Ok(())
}

Window Capture with Audio

use screencapturekit::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = SCShareableContent::get()?;
    
    // Find a specific window
    let window = content.windows()
        .iter()
        .find(|w| w.title().as_deref() == Some("Safari"))
        .ok_or("Safari window not found")?;
    
    // Capture window with audio
    let filter = SCContentFilter::builder()
        .window(window)
        .build();
    
    let config = SCStreamConfiguration::new()
        .with_width(1920)
        .with_height(1080)
        .with_captures_audio(true)
        .with_sample_rate(48000)
        .with_channel_count(2);
    
    let mut stream = SCStream::new(&filter, &config);
    // Add handlers...
    stream.start_capture()?;
    
    Ok(())
}

Content Picker (macOS 14.0+)

Use the system picker UI to let users choose what to capture:

use screencapturekit::content_sharing_picker::*;
use screencapturekit::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = SCContentSharingPickerConfiguration::new();
    
    // Advanced API: Get filter with metadata (dimensions, scale)
    match SCContentSharingPicker::pick(&config) {
        SCPickerOutcome::Picked(result) => {
            // Get dimensions from the picked content
            let (width, height) = result.pixel_size();
            println!("Selected: {}x{} (scale: {})", width, height, result.scale());
            
            let stream_config = SCStreamConfiguration::new()
                .with_width(width)
                .with_height(height);
            
            // Get filter for streaming
            let filter = result.filter();
            let mut stream = SCStream::new(&filter, &stream_config);
            // ...
        }
        SCPickerOutcome::Cancelled => println!("User cancelled"),
        SCPickerOutcome::Error(e) => eprintln!("Error: {}", e),
    }
    
    Ok(())
}

Async Content Picker (macOS 14.0+)

Use the async version in async contexts to avoid blocking:

use screencapturekit::async_api::AsyncSCContentSharingPicker;
use screencapturekit::content_sharing_picker::*;
use screencapturekit::prelude::*;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = SCContentSharingPickerConfiguration::new();
    
    // Async picker - doesn't block the executor
    match AsyncSCContentSharingPicker::pick(&config).await {
        SCPickerOutcome::Picked(result) => {
            let (width, height) = result.pixel_size();
            println!("Selected: {}x{}", width, height);
            
            let filter = result.filter();
            // Use filter with stream...
        }
        SCPickerOutcome::Cancelled => println!("User cancelled"),
        SCPickerOutcome::Error(e) => eprintln!("Error: {}", e),
    }
    
    Ok(())
}

🎯 Key Concepts

Builder Pattern

Different types use slightly different patterns:

// Content filters use .builder() with .build()
let filter = SCContentFilter::builder()
    .display(&display)
    .exclude_windows(&windows)
    .build();

// Stream configuration uses ::new() with .with_*() chainable methods
let config = SCStreamConfiguration::new()
    .with_width(1920)
    .with_height(1080)
    .with_pixel_format(PixelFormat::BGRA)
    .with_captures_audio(true);

// Options for content retrieval
let content = SCShareableContent::with_options()
    .on_screen_windows_only(true)
    .exclude_desktop_windows(true)
    .get()?;

Custom Dispatch Queues

Control callback threading with custom dispatch queues:

use screencapturekit::dispatch_queue::{DispatchQueue, DispatchQoS};

let queue = DispatchQueue::new("com.myapp.capture", DispatchQoS::UserInteractive);

stream.add_output_handler_with_queue(
    my_handler,
    SCStreamOutputType::Screen,
    Some(&queue)
);

QoS Levels:

  • Background - Maintenance tasks
  • Utility - Long-running tasks
  • Default - Standard priority
  • UserInitiated - User-initiated tasks
  • UserInteractive - UI updates (highest priority)

IOSurface Access

Zero-copy GPU texture access:

impl SCStreamOutputTrait for Handler {
    fn did_output_sample_buffer(&self, sample: CMSampleBuffer, _type: SCStreamOutputType) {
        if let Some(pixel_buffer) = sample.get_image_buffer() {
            if let Some(surface) = pixel_buffer.get_iosurface() {
                let width = surface.get_width();
                let height = surface.get_height();
                let pixel_format = surface.get_pixel_format();
                
                // Use with Metal/OpenGL...
                println!("IOSurface: {}x{} format: {}", width, height, pixel_format);
            }
        }
    }
}

πŸŽ›οΈ Feature Flags

Core Features

Feature Description
async Runtime-agnostic async API (works with any executor)

macOS Version Features

Feature flags enable APIs for specific macOS versions. They are cumulative (enabling macos_15_0 enables all earlier versions).

Feature macOS APIs Enabled
macos_13_0 13.0 Ventura Audio capture, synchronization clock
macos_14_0 14.0 Sonoma Content picker, screenshots, content info
macos_14_2 14.2 Menu bar capture, child windows, presenter overlay
macos_14_4 14.4 Current process shareable content
macos_15_0 15.0 Sequoia Recording output, HDR capture, microphone
macos_15_2 15.2 Screenshot in rect, stream active/inactive delegates
macos_26_0 26.0 Advanced screenshot config, HDR screenshot output

Version-Specific Example

let mut config = SCStreamConfiguration::new()
    .with_width(1920)
    .with_height(1080);

#[cfg(feature = "macos_13_0")]
config.set_should_be_opaque(true);

#[cfg(feature = "macos_14_2")]
{
    config.set_ignores_shadows_single_window(true);
    config.set_includes_child_windows(false);
}

πŸ“š API Overview

Core Types

  • SCShareableContent - Query available displays, windows, and applications
  • SCContentFilter - Define what to capture (display/window/app)
  • SCStreamConfiguration - Configure resolution, format, audio, etc.
  • SCStream - Main capture stream with output handlers
  • CMSampleBuffer - Frame data with timing and metadata

Async API (requires async feature)

  • AsyncSCShareableContent - Async content queries
  • AsyncSCStream - Async stream with frame iteration
  • AsyncSCScreenshotManager - Async screenshot capture (macOS 14.0+)
  • AsyncSCContentSharingPicker - Async content picker UI (macOS 14.0+)

Display & Window Types

  • SCDisplay - Display information (resolution, ID, etc.)
  • SCWindow - Window information (title, bounds, owner, etc.)
  • SCRunningApplication - Application information (name, PID, etc.)

Media Types

  • CMSampleBuffer - Sample buffer with timing and attachments
  • CMTime - High-precision timestamps
  • IOSurface - GPU-backed pixel buffers
  • CGImage - CoreGraphics images

Configuration Types

  • PixelFormat - BGRA, YCbCr420v, YCbCr420f, l10r (10-bit)
  • SCPresenterOverlayAlertSetting - Privacy alert behavior
  • SCCaptureDynamicRange - HDR/SDR modes (macOS 15.0+)
  • SCScreenshotConfiguration - Advanced screenshot config (macOS 26.0+)
  • SCScreenshotDynamicRange - SDR/HDR screenshot output (macOS 26.0+)

πŸƒ Examples

The examples/ directory contains focused API demonstrations:

Quick Start (Numbered by Complexity)

  1. 01_basic_capture.rs - Simplest screen capture
  2. 02_window_capture.rs - Capture specific windows
  3. 03_audio_capture.rs - Audio + video capture
  4. 04_pixel_access.rs - Read pixel data with std::io::Cursor
  5. 05_screenshot.rs - Single screenshot, HDR capture (macOS 14.0+, 26.0+)
  6. 06_iosurface.rs - Zero-copy GPU buffers
  7. 07_list_content.rs - List available content
  8. 08_async.rs - Async/await API with multiple examples
  9. 09_closure_handlers.rs - Closure-based handlers and delegates
  10. 10_recording_output.rs - Direct video file recording (macOS 15.0+)
  11. 11_content_picker.rs - System UI for content selection (macOS 14.0+)

See examples/README.md for detailed descriptions.

Run an example:

# Basic examples
cargo run --example 01_basic_capture
cargo run --example 09_closure_handlers

# Feature-gated examples
cargo run --example 05_screenshot --features macos_14_0
cargo run --example 08_async --features async
cargo run --example 10_recording_output --features macos_15_0
cargo run --example 11_content_picker --features macos_14_0

πŸ§ͺ Testing

Run Tests

# All tests
cargo test

# With features
cargo test --features async
cargo test --all-features

# Specific test
cargo test test_stream_configuration

Linting

cargo clippy --all-features -- -D warnings
cargo fmt --check

πŸ—οΈ Architecture

Module Organization

screencapturekit/
β”œβ”€β”€ cm/                     # Core Media (CMSampleBuffer, CMTime, CVPixelBuffer)
β”œβ”€β”€ cg/                     # Core Graphics (CGRect, CGImage)
β”œβ”€β”€ stream/                 # Stream management
β”‚   β”œβ”€β”€ configuration/      # SCStreamConfiguration
β”‚   β”œβ”€β”€ content_filter/     # SCContentFilter
β”‚   └── sc_stream/          # SCStream
β”œβ”€β”€ shareable_content/      # SCShareableContent, SCDisplay, SCWindow
β”œβ”€β”€ output/                 # Frame buffers and pixel data
β”œβ”€β”€ dispatch_queue/         # Custom dispatch queues
β”œβ”€β”€ error/                  # Error types
β”œβ”€β”€ screenshot_manager/     # SCScreenshotManager (macOS 14.0+)
β”œβ”€β”€ content_sharing_picker/ # SCContentSharingPicker (macOS 14.0+)
β”œβ”€β”€ recording_output/       # SCRecordingOutput (macOS 15.0+)
β”œβ”€β”€ async_api/              # Async wrappers (feature = "async")
β”œβ”€β”€ utils/                  # FFI strings, FourCharCode utilities
└── prelude/                # Convenience re-exports

Memory Management

  • Reference Counting - Proper CFRetain/CFRelease for all CoreFoundation types
  • RAII - Automatic cleanup in Drop implementations
  • Thread Safety - Safe to share across threads (where supported)
  • Leak Free - Comprehensive leak tests ensure no memory leaks

πŸ”§ Platform Requirements

  • macOS 12.3+ (Monterey) - Base ScreenCaptureKit support
  • macOS 13.0+ (Ventura) - Additional features with macos_13_0
  • macOS 14.0+ (Sonoma) - Content picker, advanced config
  • macOS 15.0+ (Sequoia) - Recording output, HDR capture

🀝 Contributing

Contributions welcome! Please:

  1. Follow existing code patterns (builder pattern with ::new() and .with_*() methods)
  2. Add tests for new functionality
  3. Run cargo test and cargo clippy
  4. Update documentation

πŸ‘₯ Contributors

Thanks to everyone who has contributed to this project!

πŸ“„ License

Licensed under either of:

at your option.

About

Rust crate for Apple's ScreenCaptureKit

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Contributors 11