Skip to content

p-doom/crowd-cast

Repository files navigation

p(doom)

crowd-cast: Crowd-Sourcing Months-Long Trajectories of Human Computer Work

Infrastructure for capturing paired screencast and keyboard/mouse input data.

Quick Start

Download for macOS Download for Windows

Download the installer for your platform and follow the instructions in the setup wizard.

  • macOS: open CrowdCast.dmg and grant permissions by following the setup wizard.
  • Windows: run crowd-cast-setup.exe. The agent runs from a single executable; OBS is fetched automatically on first launch and no special permissions are required. If SmartScreen shows "Windows protected your PC", click More info, then Run anyway.

Features

  • Single binary: No external OBS installation required (libobs is embedded)
  • Privacy-aware capture: Only records when selected applications are in the foreground
  • Full control: Start or stop recording at any time, and delete the last 10 minutes of recording
  • Automatic updates: Sparkle framework keeps the app up to date in the background
  • Idle detection: Automatically pauses recording when you step away, resumes on return
  • Hardware acceleration: Uses native encoding (VideoToolbox on macOS)
  • Efficient uploads: Streaming uploads via pre-signed S3 URLs with retry/backoff
  • Easy setup: Wizard handles permissions and application selection

How It Works

crowd-cast is a single-binary agent that embeds libobs for screen capture and recording, eliminating the need to install OBS Studio separately.

Key components:

  • Embedded libobs - Screen/window capture with hardware encoding (via libobs-rs)
  • Sync Engine - Coordinates recording with input capture, filters by frontmost app
  • Input Capture - Cross-platform keyboard/mouse capture (rdev/evdev)
  • System Tray - Control recording from the menu bar
┌─────────────────────────────────────────────────────────────────┐
│                     crowd-cast Agent (Rust)                     │
│  ┌──────────┐  ┌────────────────┐  ┌─────────────────────────┐  │
│  │ Tray UI  │  │ Embedded libobs│  │      Sync Engine        │  │
│  │          │  │ (libobs-rs)    │  │  - Frontmost app detect │  │
│  └──────────┘  │                │  │  - Input filtering      │  │
│                │  ┌───────────┐ │  │  - Event buffering      │  │
│                │  │mac-capture│ │  └───────────┬─────────────┘  │
│                │  │obs-x264   │ │              │                │
│                │  │obs-ffmpeg │ │        ┌─────┴─────┐          │
│                │  └───────────┘ │        │ rdev/evdev│          │
│                └────────────────┘        └───────────┘          │
│                        │                       │                │
│                   Video Output            Input Events          │
│                        │                       │                │
│                        └───────────┬───────────┘                │
│                                    │                            │
│                              ┌─────┴─────┐                      │
│                              │ Uploader  │                      │
│                              └─────┬─────┘                      │
└────────────────────────────────────┼────────────────────────────┘
                                     │
                                     ▼
                              ┌─────────────┐
                              │ Lambda + S3 │
                              └─────────────┘

Features

  • Single binary: No external OBS installation required (libobs is embedded)
  • Privacy-aware capture: Only records when selected applications are in the foreground
  • Automatic updates: signed background updates on macOS, Windows, and Linux
  • Idle detection: Automatically pauses recording when you step away, resumes on return
  • Hardware acceleration: Uses native encoding (VideoToolbox on macOS)
  • Efficient uploads: Streaming uploads via pre-signed S3 URLs with retry/backoff
  • Easy setup: Wizard handles permissions and application selection

Quick Start

For users

Download the installer for your platform from the latest release:

  • macOS: CrowdCast.dmg
  • Windows: crowd-cast-setup.exe
  • Linux: curl -fsSL https://github.com/p-doom/crowd-cast/releases/latest/download/install-linux.sh | bash

Linux support is limited to GNOME on Wayland and sway. GNOME supports per-app capture; sway currently supports full-screen capture.

Building from source

# Clone the repository
git clone https://github.com/p-doom/crowd-cast.git
cd crowd-cast

# Build (endpoint required at build time)
CROWD_CAST_API_GATEWAY_URL="https://your-api-gateway.execute-api.region.amazonaws.com/prod/presign" \
  cargo build --release

# Run the setup wizard
./target/release/crowd-cast-agent --setup

Build speed: cargo build --release is tuned for fast incremental rebuilds (~seconds, not minutes) — LTO is off because it costs minutes and buys nothing for this libobs-backed app. For an even quicker loop use cargo run (debug).

On macOS, build.rs automatically installs OBS binaries via cargo-obs-build during cargo build. Set CROWD_CAST_SKIP_OBS_INSTALL=1 to skip this behavior.

On Linux there is no automatic OBS install, so the linker has to be told where libobs lives — otherwise the build fails at link time with rust-lld: error: unable to find library -lobs. Set LIBOBS_PATH to a directory that contains libobs.so (the unversioned linker symlink), built for the OBS ABI in CROWD_CAST_OBS_ABI (default 32.0.2):

# Linux build
LIBOBS_PATH=/path/to/obs/usr/lib \
CROWD_CAST_API_GATEWAY_URL="https://your-api-gateway.execute-api.region.amazonaws.com/prod/presign" \
  cargo build --release

cargo check does not link, so it will not catch a missing or wrong LIBOBS_PATH — only cargo build/run will.

Platform-Specific Setup

macOS

  1. Grant Accessibility permission to the agent (System Settings → Privacy & Security → Accessibility)

macOS Distribution

First-time setup on a release machine:

scripts/setup-macos-signing.sh \
  --p12 /path/to/developer-id.p12

For a full release (build, sign, notarize, publish to GitHub Releases + upload appcast to S3):

scripts/build-and-publish-macos.sh \
  --github-repo p-doom/crowd-cast \
  --s3-bucket crowd-cast-bucket \
  --identity "Developer ID Application: Your Name (TEAMID)" \
  --notarize \
  --version 1.0.0 \
  --build-number 1055 \
  --sparkle-public-ed-key "YOUR_PUBLIC_KEY" \
  --sparkle-private-ed-key-file /path/to/private-key.txt

Auto-updates are delivered via Sparkle using an appcast hosted on S3.

Linux

Linux installs into user space under ~/.local and downloads the matching libobs bundle during installation. The production installer is published with each release:

curl -fsSL https://github.com/p-doom/crowd-cast/releases/latest/download/install-linux.sh | bash

Supported Linux sessions are GNOME on Wayland and sway. Other desktop sessions are blocked during setup so the agent does not run in an unvalidated capture mode. Linux auto-updates use a signed appcast hosted on S3 and install silently when recording and uploads are idle.

Windows

Download crowd-cast-setup.exe from the latest release and run the setup wizard. Windows auto-updates are delivered through the signed Windows appcast.

Configuration

Most settings are managed through the setup wizard and the tray menu. The configuration file is at:

  • macOS: ~/Library/Application Support/dev.crowd-cast.agent/config.toml
  • Linux: ~/.config/agent/config.toml
  • Windows: %APPDATA%\agent\config.toml

Key settings:

[capture]
target_apps = ["org.mozilla.firefox", "com.apple.Terminal"]
capture_all = false
idle_timeout_secs = 120          # Pause after 2 min of inactivity
single_active_app_capture = true # One app captured at a time (multi-scene)

[recording]
autostart_on_launch = true
notify_on_start_stop = true
segment_duration_secs = 300      # 5-minute recording segments

[upload]
delete_after_upload = true

Upload endpoint is set at build time via CROWD_CAST_API_GATEWAY_URL.

Data Format

Input logs are stored in MessagePack format. Each file contains an array of [timestamp_us, [event_type, event_data]] tuples:

[0,         ["ContextChanged", ["com.apple.Terminal"]]]
[1234000,   ["KeyPress",       [0, "KeyA"]]]
[1334000,   ["KeyRelease",     [0, "KeyA"]]]
[1500000,   ["MouseMove",      [12.5, -3.2]]]
[2000000,   ["MousePress",     ["Left", 540.0, 320.0]]]
[2100000,   ["MouseRelease",   ["Left", 540.0, 320.0]]]
[2500000,   ["MouseScroll",    [0.0, -3.0, 540.0, 320.0]]]
[3999000,   ["ContextChanged", ["UNCAPTURED"]]]

Event types:

  • ContextChanged: app switch (bundle ID or UNCAPTURED for untracked apps)
  • KeyPress / KeyRelease: [key_code, key_name]
  • MouseMove: [delta_x, delta_y]
  • MousePress / MouseRelease: [button, x, y]
  • MouseScroll: [delta_x, delta_y, x, y]

Timestamps are microseconds relative to the segment start. Video and input files share the same session/segment IDs for alignment.

Development

This section is for contributors who want to modify crowd-cast.

First Run (Recommended)

crowd-cast-agent --setup

This runs the interactive setup wizard that guides you through configuration.

Normal Usage

crowd-cast-agent

The agent will:

  1. Bootstrap OBS libraries (downloads if needed)
  2. Initialize embedded libobs for capture
  3. Show in your system tray
  4. Capture input when selected apps are in foreground

Command Line Options

crowd-cast-agent [OPTIONS]

OPTIONS:
    -h, --help    Print help message
    -s, --setup   Run the setup wizard (re-select apps, etc.)

ENVIRONMENT:
    RUST_LOG      Set log level (e.g., debug, info, warn)
    CROWD_CAST_LOG_PATH
                  Override log directory (default: ~/Library/Logs/crowd-cast on macOS)
    CROWD_CAST_API_GATEWAY_URL
                  Lambda endpoint for pre-signed S3 URLs (set at build time)

Building from Source

Prerequisites

macOS (Apple Silicon):

brew install simde       # Required for ARM builds
brew install create-dmg  # Required for release DMG packaging

Build Steps

# 1. Clone with submodules
git clone --recursive https://github.com/p-doom/crowd-cast.git
cd crowd-cast

# 2. Build the agent (macOS auto-installs OBS binaries in build.rs)
cargo build

# 3. Run tests
cargo test

libobs-rs Integration

The agent uses libobs-rs to embed OBS functionality. Key crates:

  • libobs - Raw FFI bindings to libobs
  • libobs-wrapper - Safe Rust wrapper
  • libobs-bootstrapper - Downloads OBS binaries at runtime (macOS)
  • cargo-obs-build - Downloads OBS binaries at build time

The fork at libobs-rs/ includes macOS support from PR #53.

Adding New Capture Sources

To add support for new capture types, implement them in src/capture/sources.rs:

pub fn new_window_capture(ctx: &ObsContext, window_name: &str) -> Result<ObsSourceRef> {
    // Use libobs-wrapper to create window capture source
}

Releasing

macOS

First-time setup on a release machine:

scripts/setup-macos-signing.sh \
  --p12 /path/to/developer-id.p12

For a full release (build, sign, notarize, publish to GitHub Releases + upload appcast to S3):

scripts/build-and-publish-macos.sh \
  --github-repo p-doom/crowd-cast \
  --s3-bucket crowd-cast-bucket \
  --identity "Developer ID Application: Your Name (TEAMID)" \
  --notarize \
  --version 1.0.0 \
  --build-number 1055 \
  --sparkle-public-ed-key "YOUR_PUBLIC_KEY" \
  --sparkle-private-ed-key-file /path/to/private-key.txt

Auto-updates are delivered via Sparkle using an appcast hosted on S3.

Windows

Build the per-user installer (no admin / UAC required) with Inno Setup:

# One-time: install the Inno Setup compiler
winget install JRSoftware.InnoSetup

# Build the release binary + installer
$env:CROWD_CAST_API_GATEWAY_URL = "https://.../prod/presign"
pwsh scripts/build-windows-installer.ps1
# -> dist/crowd-cast-setup-<version>.exe

The installer (installer/windows/crowd-cast.iss) installs the agent and its obs.dll loader under %LOCALAPPDATA%\Programs\crowd-cast, creates a Start Menu shortcut tagged with the app's AppUserModelID (so toast notifications are branded "crowd-cast"), and registers an uninstaller that stops the agent and removes the autostart entry and install directory. Autostart itself is managed in-app via the setup wizard's "start at login" option.

On first launch the agent downloads the rest of the OBS runtime (codecs, plugins) into the install folder and relaunches itself automatically, a one-time step that needs network access.

Linux

Support coming soon...

Backend Setup

The agent expects a Lambda endpoint that returns pre-signed S3 URLs. Example Lambda handler:

import boto3
import json

s3 = boto3.client('s3')
BUCKET = 'your-bucket'

def handler(event, context):
    body = json.loads(event['body'])
    file_name = body['fileName']
    version = body['version']
    user_id = body['userId']
    
    key = f"uploads/{version}/{user_id}/{file_name}"
    
    content_type = (
        "application/msgpack" if file_name.endswith(".msgpack") else "video/mp4"
    )

    upload_url = s3.generate_presigned_url(
        'put_object',
        Params={'Bucket': BUCKET, 'Key': key, 'ContentType': content_type},
        ExpiresIn=3600
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'uploadUrl': upload_url,
            'key': key,
            'contentType': content_type,
        })
    }

Utilities

Overlay keylogs on top of a screen capture:

python scripts/overlay_keylogs.py --video capture.mp4 --input input.msgpack --output capture_with_keys.mp4

To just generate subtitles (ASS):

python scripts/overlay_keylogs.py --input input.msgpack --ass-out keylogs.ass

Contributing

Contributions welcome! Please open an issue first to discuss proposed changes.

License

MIT License, see LICENSE.md

About

Crowd-sourcing months-long trajectories of human computer work.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors