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
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]".
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
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.0
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.0.tar.gz | 26.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aiocamera-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.3 kB
Release files / aiocamera-0.1.0.tar.gz
| Download URL | aiocamera-0.1.0.tar.gz |
|---|---|
| Size | 26.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
02ba920d7251bbd13f897158b9c88171b1aad1c0cc91278088b8ef63701616e6
|
|
BLAKE2b-256 checksum How to use checksums |
e6a6ab576403d02f5ca59644c5873256e9e6119e263cbfb991513d1d9e38efd1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / aiocamera-0.1.0-py3-none-any.whl
| Download URL | aiocamera-0.1.0-py3-none-any.whl |
|---|---|
| Size | 29.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
743348af343e676140188a9e3b85dec29735dacae487ae46abe69b3e16079cec
|
|
BLAKE2b-256 checksum How to use checksums |
1b228de4b17987fb6c303c237873b7583c261a79189827de47d4f184e49718ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|