capture_helper.preview module

capture_helper.preview

Live-preview helpers for the scene configurator GUI. Two flavours, both built on the existing iterators so no new capture path is introduced:

Why ffmpeg for JPEG (and not Pillow / OpenCV)

ffmpeg is already a hard runtime requirement of capture-helper. Encoding the raw BGR frame through a tiny ffmpeg -f rawvideo -f mjpeg - call keeps the dependency surface flat — no Pillow, no OpenCV — at the cost of one subprocess per encode, which is negligible for preview-rate frames.

Author

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

capture_helper.preview.frame_to_jpeg(frame, *, quality=5)[source]

Encode a single BGR frame to a JPEG byte string via ffmpeg.

Parameters:
  • frame (numpy.ndarray) – A (H, W, 3) uint8 array in OpenCV BGR channel order — exactly what capture_helper.iter_camera_frames() yields.

  • quality (int, default 5) – ffmpeg -q:v value (2 best … 31 worst). Lower = larger file, better image. 5 is a good preview trade-off.

Returns:

The JPEG-encoded image.

Return type:

bytes

Raises:

Examples

>>> import numpy as np
>>> jpg = frame_to_jpeg(np.zeros((16, 16, 3), dtype=np.uint8))
>>> jpg[:2] == b"\xff\xd8"   # JPEG SOI marker
True
capture_helper.preview.iter_camera_jpeg(source, *, output_width=480, output_height=270, fps=10.0, quality=5, max_frames=None)[source]

Yield a stream of JPEG-encoded frames from a live camera.

Each yielded item is a complete JPEG suitable for concatenation into an multipart/x-mixed-replace MJPEG HTTP response (see capture_helper.api()).

Parameters:
  • source (Source) – A "camera" device.

  • output_width (int) – Preview size per frame.

  • output_height (int) – Preview size per frame.

  • fps (float, default 10.0) – Capture-side frame rate — kept low so the preview is cheap.

  • quality (int, default 5) – JPEG quality per frame.

  • max_frames (int, optional) – Stop after this many frames. None = until the consumer disconnects.

Yields:

bytes – Successive JPEG images.

Raises:

ValueError – If source is not a camera.

Return type:

Iterator[bytes]

Examples

>>> cam = ...
>>> for jpg in iter_camera_jpeg(cam, max_frames=3):
...     handle(jpg)
async capture_helper.preview.mic_level(source, *, target_sample_rate=16000, frame_ms=20, window_ms=200)[source]

Capture a short slice of live audio and reduce it to a level reading.

Drives capture_helper.iter_mic_audio() for roughly window_ms of audio, then returns RMS / peak levels — the GUI polls this to animate a VU meter without holding a long-lived stream open.

Parameters:
  • source (Source) – A "microphone" device.

  • target_sample_rate (int, default 16000) – Sample rate for the capture (matches the iterator default).

  • frame_ms (int, default 20) – Per-frame duration fed to the iterator.

  • window_ms (int, default 200) – Approximate total capture window in milliseconds.

Returns:

{"rms": …, "rms_dbfs": …, "peak": …, "peak_dbfs": …}.

Return type:

dict[str, float]

Raises:

ValueError – If source is not a microphone.

Examples

>>> import asyncio
>>> mic = ...
>>> asyncio.run(mic_level(mic))
capture_helper.preview.rms_dbfs(pcm)[source]

Compute linear RMS and its dBFS equivalent for a PCM block.

Parameters:

pcm (numpy.ndarray) – Float32 samples in [-1, 1] (any shape; flattened internally).

Returns:

(rms_linear, rms_dbfs). rms_dbfs is clamped at -120.0 for digital silence to avoid -inf.

Return type:

tuple[float, float]

Examples

>>> import numpy as np
>>> lin, db = rms_dbfs(np.zeros(100, dtype=np.float32))
>>> lin == 0.0 and db == -120.0
True
capture_helper.preview.snapshot_jpeg(source, *, output_width=480, output_height=270, quality=5)[source]

Grab one live frame from a camera and return it as JPEG bytes.

Parameters:
  • source (Source) – A "camera" device from capture_helper.pick_source() / list_sources().

  • output_width (int) – Preview size (aspect-preserving fit-and-pad, per the camera iterator).

  • output_height (int) – Preview size (aspect-preserving fit-and-pad, per the camera iterator).

  • quality (int, default 5) – JPEG quality passed to frame_to_jpeg().

Returns:

A single JPEG image.

Return type:

bytes

Raises:
  • ValueError – If source is not a camera.

  • RuntimeError – If no frame could be captured (permission denied / device busy).

Examples

>>> cam = ...
>>> jpg = snapshot_jpeg(cam)