pyezcams
v0.1.3 · PyPI
Minimal (stdlib-only) toolkit to run a node that captures USB webcams and
streams them over RTSP/WebRTC via MediaMTX.
A single command (pyezcams) starts MediaMTX and one ffmpeg per camera and
keeps them alive.
No Python dependencies. Relies on three system binaries: ffmpeg, v4l2-ctl
and mediamtx. Linux only (v4l2 capture).
Stateless: the library stores nothing and assumes no paths. All configuration is the match file, passed explicitly and required.
Prerequisites
| Binary | Install |
|---|---|
ffmpeg |
sudo apt install ffmpeg |
v4l2-ctl |
sudo apt install v4l-utils |
mediamtx |
binary from releases onto the PATH |
check_prerequisites() verifies them at startup and, if any is missing, says what
to do.
Install
pip install pyezcams
Usage
pyezcams --config cameras.txt # defaults: 720p30
pyezcams --config cameras.txt --resolution 1920x1080 --fps 25
--config is required (no default path). The command checks prerequisites,
detects the encoder, starts MediaMTX and launches one ffmpeg per camera,
supervising them (relaunches any that die, clean shutdown on SIGINT/SIGTERM).
Defaults
720p30 surveillance standard; everything works with no flags. Only
--config is required; --resolution and --fps are optional overrides.
| Parameter | Default | Applies to |
|---|---|---|
| resolution | 1280x720 (max) |
capture (both cases) |
| framerate | 30 (max) |
capture (both cases) |
| bitrate | 4M |
re-encode (MJPG) only |
| GOP | 60 (2s @30fps) |
re-encode (MJPG) only |
| RTSP output | rtsp://localhost:8554/<alias> |
both cases |
--resolution and --fps are a ceiling, not a fixed value. Each camera is
probed with v4l2-ctl and captures at the largest mode it actually offers at
or below them; a camera that only does MJPG 640x480@30 gets exactly that, and
the downgrade is logged as a warning. Asking for a size the camera does not
have would make V4L2 silently fall back to YUYV at its smallest size — that is
the bug this avoids.
Per case:
- H264 (copy) — captures at the negotiated mode and copies the native
stream (
-c:v copy, ~0 CPU). Bitrate does not apply (nothing is re-encoded). - MJPG (re-encode) — captures at the negotiated mode and re-encodes with the detected encoder at the given bitrate.
Bitrate and RTSP base are module constants in command.py; build_command
also takes video_size, framerate and bitrate keyword args for per-camera
overrides.
Match file
One camera per line, path = alias (blank lines and # comments ignored):
/dev/v4l/by-path/...-video-index0 = laser20w
/dev/v4l/by-path/...-video-index0 = cnc_a
Get the paths with ls -l /dev/v4l/by-path/.
API
from pyezcams import (
parse_match, check_prerequisites, detect_encoder,
detect_mode, detect_format, build_command, run,
)
parse_match(path) -> dict— read the match file into{alias: usb_path}.check_prerequisites() -> None— verify the three binaries;RuntimeErrorif any is missing.detect_encoder() -> str | None— first H.264 encoder that passes a real 1-frame test (h264_nvenc > h264_qsv > h264_vaapi > h264_v4l2m2m > libx264). Hang-proof: each test is capped byENCODER_TEST_TIMEOUT(10s). A hardware encoder that hangs (broken driver/firmware) is discarded on timeout and the detection falls through to the next candidate instead of blocking node startup; softwarelibx264always works as the final fallback.detect_mode(usb_path, max_width, max_height, max_fps) -> Mode | None— the camera's bestMode(fmt, width, height, fps)at or below the ceiling: preferred format first (H264 > MJPG), then the largest frame size that fits, then the highest fps not abovemax_fps(or the lowest available if the camera only offers more).Noneif it has no usable format. OnlySize: Discretemodes are parsed; a stepwise-only camera yieldsNone.detect_format(usb_path) -> str | None—"H264"(copy) or"MJPG"(re-encode), orNone. Kept for back-compat; preferdetect_mode, since a format alone does not tell you at which sizes the camera offers it.build_command(usb_path, alias, fmt, encoder, video_size=..., framerate=..., bitrate=...) -> list[str]— build (not run) the ffmpeg argv; the last three default to the 720p30/4M standard.ValueErroriffmtisNone. Pass a size/fps the camera really has (i.e. fromdetect_mode), not a wish.run(config, video_size=..., framerate=...) -> None— orchestrate the node: prerequisites -> MediaMTX -> one ffmpeg per camera -> supervision -> clean shutdown.video_size/framerateare a ceiling.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyezcams-0.1.3.tar.gz.
File metadata
- Download URL: pyezcams-0.1.3.tar.gz
- Upload date:
- Size: 12.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9aa1777dc45ed5708df9b59df958c3d4243536ef6aa68e8a94d42081a2f701da
|
|
| MD5 |
7850ac985a85a605da4a1ac1f39897a0
|
|
| BLAKE2b-256 |
1d9e780dc5e1efaefd4c02c41ccb7b9cde4d131d59f25c7e79a0a868b6c7e834
|
File details
Details for the file pyezcams-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pyezcams-0.1.3-py3-none-any.whl
- Upload date:
- Size: 14.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12a3050b66fdbb0f90de18c8f984b1e6b41bbbae6ebe39e3b43b78eae36ab4e9
|
|
| MD5 |
419507e125a96cf18520b8f09f334011
|
|
| BLAKE2b-256 |
2fb0fa338b303a0d6b0bf236571016eb069cc27d98764f055e2b328156468219
|