aiocamera
Camera as a service — discover, capture, and serve camera frames over ZMQ.
A background daemon automatically detects connected cameras (Intel RealSense, Luxonis OAK, generic USB webcams), continuously captures frames, and serves the latest frame on demand via ZMQ IPC. Includes a CLI, a Python client library, and a browser-based MJPEG debug viewer.
Install
pip install aiocamera # core only (opencv, pyzmq, numpy, click)
pip install "aiocamera[realsense]" # + pyrealsense2 (pyrealsense2-macosx on macOS)
pip install "aiocamera[oak]" # + depthai
pip install "aiocamera[all]" # everything
aiocamera uses OpenCV's contrib build (opencv-contrib-python), as aprilcube does, so both
install together. Do not add opencv-python to the same environment: both wheels provide
cv2. For development, pip install -e ".[all]".
macOS
Intel publishes no pyrealsense2 wheels for macOS. There the realsense extra installs
pyrealsense2-macosx instead, which builds the
same pyrealsense2 module (Apple Silicon, macOS 15+).
librealsense needs root on macOS to open a RealSense. With one connected, aiocamera start asks
for your password through sudo and runs the daemon as root, and only the daemon and the camera
queries behind the setup prompts: your profiles stay yours, and aiocamera status, clients and
web-debug work as you. aiocamera stop signals the root daemon through sudo, so it may ask for
the password again. There is no need to run aiocamera itself with sudo.
CLI
aiocamera start # uses latest matching profile, or prompts
aiocamera start --fresh # ignore saved profiles, prompt and optionally save
aiocamera start --profile my_setup # use a specific saved profile
aiocamera status # show detected cameras and active streams
aiocamera stop # stop the daemon
aiocamera profiles # list all saved configuration profiles
aiocamera web-debug [PORT] # MJPEG viewer at http://localhost:PORT (default 8080)
# Runtime reconfiguration (while service is running)
aiocamera config rs_234322071225 --resolution 1280x720 --fps 30
aiocamera config rs_234322071225 --exposure 500
aiocamera config rs_234322071225 --exposure auto
aiocamera config rs_234322071225 # list supported modes
Python API
Start / Stop
import aiocamera
# Start the daemon
aiocamera.start() # uses latest matching profile, or prompts
aiocamera.start(fresh=True) # ignore profiles, prompt, offer to save
aiocamera.start(profile="my_setup") # use a specific saved profile
aiocamera.start(camera_configs={ # explicit configs, no prompts or profiles
"rs_234322071225": {"resolution": [1280, 720], "fps": 30, "exposure": 500},
})
# Returns PID, or None if already running
aiocamera.stop() # stop the daemon
A camera's config, in a profile or camera_configs, may also choose its streams and
whether it opens at all. A config keyed by a camera type (realsense, oak, webcam)
applies to every camera of that type without one of its own:
aiocamera.start(camera_configs={
"rs_234322071225": {"resolution": [640, 480], "fps": 60, "streams": ["color"]}, # no depth
"webcam": {"enabled": False}, # no webcams
})
A RealSense opens color and depth by default, and aligns depth to color when both run.
aiocamera start --fresh asks for both and saves them in the profile.
Client
from aiocamera import CameraClient
with CameraClient() as client:
cameras = client.list_cameras()
status = client.status()
# Get frame at capture resolution
frame = client.get_frame("rs_234322071225_color") # numpy.ndarray
# Get frame at a specific resolution (resized server-side)
frame = client.get_frame("rs_234322071225_color", resolution=(320, 240))
# Set a default resolution for all subsequent get_frame() calls
client.set_resolution(320, 240)
frame = client.get_frame("rs_234322071225_color") # 320x240
client.set_resolution(0, 0) # clear → back to capture resolution
# Runtime reconfiguration
modes = client.get_supported_modes("rs_234322071225") # valid (w,h,fps) combos
client.set_config("rs_234322071225", resolution=(1280, 720), fps=30) # blocking
client.set_exposure("rs_234322071225", 500) # immediate, no restart
client.set_exposure("rs_234322071225", None) # auto exposure
# Intrinsics
intrinsics = client.get_intrinsics("rs_234322071225")
K = client.get_intrinsics_matrix("rs_234322071225") # 3x3 numpy array
Camera IDs
Camera IDs are based on serial numbers for stability across plug/unplug cycles:
| Type | ID format | Example |
|---|---|---|
| RealSense | rs_{serial} |
rs_234322071225 |
| OAK | oak_{mxid} |
oak_18443010A1B2C3D4 |
| Webcam | webcam{n} |
webcam0 |
Stream names are {camera_id}_{stream} — e.g. rs_234322071225_color, rs_234322071225_depth, oak_18443010A1B2C3D4_CAM_A.
Supported Cameras
| Type | SDK | Streams | Install extra |
|---|---|---|---|
| Intel RealSense | pyrealsense2 | color, depth | [realsense] |
| Luxonis OAK | depthai | per-socket (CAM_A, CAM_B, ...) | [oak] |
| USB Webcam | OpenCV | color | (included) |
Architecture
aiocamera daemon
├── discovery: RealSense → OAK → webcam (avoids double-counting)
├── per-camera capture threads (as fast as possible)
├── frame store (dict: stream_id → latest numpy frame)
├── reconfigure coordination (per-driver threading events)
└── ZMQ REP socket (ipc:///tmp/aiocamera.sock)
aiocamera client (CameraClient)
└── ZMQ REQ socket → get_frame / set_config / set_exposure / list_cameras / status / stop
Files
~/.config/aiocamera/camera_configs.json— saved configuration profiles (matched by camera serial set)/tmp/aiocamera.pid— daemon PID file/tmp/aiocamera.log— daemon log output/tmp/aiocamera.sock— ZMQ IPC socket
aiocamera was called pycaas before its first release. Profiles saved by pycaas
(~/.config/pycaas/camera_configs.json) are used until a profile is saved under
aiocamera. A running pycaas daemon holds the cameras: stop it (pycaas stop)
before aiocamera start.
Metadata
Release files for aiocamera 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aiocamera-0.1.2.tar.gz | 29.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aiocamera-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.7 kB
Release files / aiocamera-0.1.2.tar.gz
| Download URL | aiocamera-0.1.2.tar.gz |
|---|---|
| Size | 29.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
557856abf4f7bea034b766f905f92c89d593fecb1bfe7c9d8f51669bd6e786a2
|
|
BLAKE2b-256 checksum How to use checksums |
6c69435e34b53f44bf734af1b6de0c9eb2425cc2b453be96ce3b2dda6560efaf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / aiocamera-0.1.2-py3-none-any.whl
| Download URL | aiocamera-0.1.2-py3-none-any.whl |
|---|---|
| Size | 32.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
163a7239ce579c6522bcada446fb30a7efb623521425fd2c34ab823e07c9fa9f
|
|
BLAKE2b-256 checksum How to use checksums |
e40f7e833d20daf84cf3bc31c66a6ae78f2e6cb3c1f92a152538bc1b8d7bd2c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log