capture_helper.sources module

capture_helper.sources

Cross-platform capture-device enumeration via ffmpeg’s -list_devices mode. We shell out and parse stderr because there is no clean cross-platform Python API for this — the per-OS alternatives (pyobjc-AVFoundation on macOS, v4l2-python on Linux, pywin32 / dshow on Windows) are heavier, harder to install, and less consistent than just asking ffmpeg.

Limitations

  • ffmpeg -list_devices is stderr-based and the line format drifts between ffmpeg versions and platforms. We parse defensively and return [] rather than raising when the format surprises us.

  • Returns devices with the names ffmpeg reports, including any trailing platform-specific bracketed metadata.

  • Screen / window sources are NOT exposed in v0.0.1 — they need a different ffmpeg input format per OS (avfoundation screen index on macOS, x11grab / wayland on Linux, gdigrab on Windows) and the abstraction is best designed once we have a real iter function consuming them. v0.2.0 target.

Usage Example

>>> from capture_helper.sources import list_sources, pick_source, ffmpeg_input_args
>>> for s in list_sources():
...     print(s["kind"], s["index"], s["name"], s["driver"])
>>> cam = pick_source("camera", name_substring="FaceTime")
>>> ffmpeg_input_args(cam)
['-f', 'avfoundation', '-i', '0:none']

Author

Warith Harchaoui, Ph.D. — https://linkedin.com/in/warith-harchaoui/

class capture_helper.sources.Source[source]

Bases: TypedDict

One capture device, normalised across platforms.

Keys

kind"camera" | "microphone"

Audio / video distinction.

namestr

Human-readable name as reported by the OS driver. Use this when passing to a future iter_camera_frames / iter_mic_audio — the per-OS ffmpeg input string is built internally.

indexint

Numeric device index in ffmpeg’s listing (0-based). Some OS backends only accept the index; others accept name OR index.

platformstr

"darwin" / "linux" / "windows" — useful for callers that branch on the underlying driver (avfoundation / v4l2 / dshow / pulse).

driverstr

ffmpeg input format flag — "avfoundation" / "v4l2" / "dshow" / "pulse" / "alsa".

driver: str
index: int
kind: Literal['camera', 'microphone']
name: str
platform: str
capture_helper.sources.ffmpeg_input_args(source)[source]

Build the ffmpeg -f <driver> -i <spec> argument pair for source.

Encapsulates the per-OS quirks so capture_helper.camera and capture_helper.mic don’t have to know about them:

  • avfoundation (macOS) addresses devices as "<video_idx>:<audio_idx>" where "none" opts out of the other track.

  • v4l2 (Linux) uses the device path (/dev/videoN) reported by list_sources().

  • pulse (Linux) and alsa (Linux) take the source/device name.

  • dshow (Windows) prefixes the device name with video= or audio=.

Parameters:

source (Source) – A device dict returned by list_sources() / pick_source().

Returns:

Two elements: ["-f", "<driver>"] followed by ["-i", "<spec>"] — ready to splice into an ffmpeg command line.

Return type:

list[str]

Raises:

ValueError – If the source’s driver is unknown to this helper.

capture_helper.sources.list_sources(kind=None)[source]

Enumerate available capture devices on the current OS.

Parameters:

kind ("camera" | "microphone" | None) – Filter to one kind. None (default) returns both.

Returns:

Possibly empty if ffmpeg is missing or the OS driver returns nothing parseable. Never raises.

Return type:

list[Source]

Examples

>>> from capture_helper import list_sources
>>> for s in list_sources("microphone"):
...     print(f"[{s['index']}] {s['name']} (driver={s['driver']})")
capture_helper.sources.pick_source(kind, *, name_substring=None, index=None)[source]

Pick a single capture device matching the given constraints.

Runs list_sources() for kind and applies each non-None filter as a hard predicate. Returns the first remaining candidate (OS-listing order — usually “built-in first, peripherals after”). Raises ValueError if nothing matches.

Parameters:
  • kind ("camera" | "microphone") – Which kind of device to pick.

  • name_substring (str, optional) – Case-insensitive substring filter against Source["name"]. Useful to disambiguate when several devices of the same kind are present ("BlackHole", "USB", "FaceTime", …).

  • index (int, optional) – Exact match against Source["index"]. When the OS reports stable indices (avfoundation, dshow), this picks a specific device unambiguously.

Returns:

The chosen device.

Return type:

Source

Raises:

ValueError – If no device matches (either the catalog is empty, or every candidate is filtered out by the constraints).

Examples

>>> from capture_helper import pick_source
>>> cam = pick_source("camera")                  # first available camera
>>> mic = pick_source("microphone", name_substring="BlackHole")
>>> usb_cam = pick_source("camera", index=1)