A desktop application built with Kivy/KivyMD for controlling networked PTZ cameras over the VISCA over IP protocol while displaying the live RTSP video stream. Supports gesture-based camera control (pan/tilt/zoom) and saving position presets with frame thumbnails.
- Camera list with add, edit and delete operations
- Live RTSP video preview rendered in real time (60 FPS render loop)
- Gesture-driven PTZ control:
- Single-finger drag — pan/tilt the camera in the gesture direction
- Multi-touch — optical zoom in/out depending on direction
- Position presets:
- Save the current camera position together with a frame snapshot
- Quick recall (camera returns to the saved position)
- Long-press to delete a preset
- Local persistence of camera configurations and presets in SQLite
| Component | Purpose |
|---|---|
| Kivy / KivyMD | UI framework, Material Design widgets |
| OpenCV | RTSP capture, frame conversion |
| SQLAlchemy + SQLite | ORM and local store for cameras and presets |
| NumPy | Frame manipulation as pixel arrays |
| socket (UDP) | Transport for VISCA commands to the camera |
- Python 3.8+
- A PTZ camera with VISCA over IP and RTSP streaming support
- Network connectivity to the camera
Platform note.
requirements.txtpins Windows-only dependencies (pypiwin32,pywin32,kivy-deps.glew/gstreamer/sdl2). On macOS/Linux these packages are not needed — Kivy installs directly from PyPI.
git clone <repo-url>
cd ptzcam_kivy
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txtgit clone <repo-url>
cd ptzcam_kivy
python3 -m venv .venv
source .venv/bin/activate
pip install kivy kivymd opencv-python numpy SQLAlchemy SQLAlchemy-Utils python-dotenv Pillowpython app.pyThe SQLite database ~/Documents/bzbcam.db is created automatically on the first run.
On the main screen tap the «+» button and fill in the connection parameters:
| Field | Description | Example |
|---|---|---|
| Title | Human-readable camera name | Conference Room |
| IP Address | Camera's IP address on the network | 192.168.20.183 |
| VISCA Port | UDP port for VISCA commands | 1259 |
| RTSP Port | TCP port of the RTSP stream | 554 |
| User Name | Login for RTSP access | admin |
| Password | Password for RTSP access | •••••• |
The RTSP URL is built using the template: rtsp://<user>:<pass>@<ip>:<port>/live/av1
Pick a camera from the list — the player opens with the live video.
- Pan/Tilt: touch the video area and drag in the desired direction. The camera moves while the finger is held; releasing stops it.
- Zoom: use a multi-touch gesture (upward — zoom in, downward — zoom out).
- Camera settings: ⚙ icon in the top-right corner.
- Save preset: tap «🔖+» at the bottom of the player screen, enter a name and save.
- Recall preset: short tap on the preset tile.
- Delete preset: long-press the tile and confirm in the dialog.
┌────────────────────────────────────────────────────────────┐
│ app.py (entry) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────▼────────────┐
│ Bzbcam (MDApp) │
│ bzbcam.py + .kv │
│ ─ ScreenManager │
│ ─ Touch dispatcher │
│ ─ 60Hz render loop │
└───┬─────────┬─────────┬─┘
│ │ │
┌───────────▼──┐ ┌────▼─────┐ ┌─▼─────────────┐
│ ViscaClient │ │VideoStream│ │ SQLAlchemy │
│ (UDP / VISCA│ │(cv2 thread│ │ models.py │
│ protocols) │ │ + Lock) │ │ SQLite │
└──────┬───────┘ └─────┬─────┘ └───────────────┘
│ │
▼ ▼
┌─────────────────────────┐
│ PTZ Camera │
│ VISCA :1259 / RTSP │
└─────────────────────────┘
| File | Responsibility |
|---|---|
app.py |
Entry point — instantiates and runs Bzbcam |
bzbcam.py |
Main application class, screen navigation, gesture handling |
bzbcam.kv |
KivyMD UI layout (screens, toolbars, lists) |
models.py |
SQLAlchemy models: Camera, Preset, AppSettings |
video_straem.py |
Threaded wrapper around cv2.VideoCapture for RTSP |
visca/visca.py |
ViscaClient — UDP client for VISCA commands |
visca/protocols.py |
Hex templates of VISCA commands (zoom, pan, memory recall, …) |
consts.py |
Screen indices in the ScreenManager |
camera_editor.py |
Screen class for adding/editing a camera |
camera_player.py |
Screen class of the video player |
preset_viewer.py |
Preset tile with long-press-to-delete behavior |
utls.py |
Helper functions (currently just rtsp_builder) |
While the finger moves across the video area, Bzbcam.on_touch_move computes the gesture angle through __calc_degrees (atan2-style quadrant logic) and dispatches the matching VISCA command:
- 45°–135° →
pan_up - 135°–225° →
pan_left - 225°–315° →
pan_down - otherwise →
pan_right
Multi-touch gestures are interpreted as zoom: upper half-plane → zoom_tele, lower → zoom_wide. Both motion types are continuous: the corresponding *_stop commands are sent in on_touch_up.
- Database:
~/Documents/bzbcam.db(SQLite, path hardcoded inmodels.py) - Preset thumbnails: PNG files
{preset_id}.pngin the current working directory - Thumbnail backup: pickled
numpy.ndarrayin thePreset.imgBLOB column — used to regenerate the PNG when the file is missing
ptzcam_kivy/
├── app.py # Entry point
├── bzbcam.py # MDApp + controller
├── bzbcam.kv # UI layout
├── camera_editor.py # Screen class
├── camera_player.py # Screen class
├── preset_viewer.py # Preset tile
├── consts.py # Screen indices
├── models.py # SQLAlchemy ORM
├── utls.py # Utilities
├── video_straem.py # RTSP threading wrapper
├── tab.py # (currently unused)
├── visca/
│ ├── __init__.py
│ ├── visca.py # VISCA client
│ └── protocols.py # Hex command templates
├── requirements.txt
└── README.md
- The RTSP path is hardcoded as
/live/av1— for cameras with a different path segment, editutls.rtsp_builder. - The database location
~/Documents/bzbcam.dbis not configurable. - Preset thumbnails live in the CWD — running the app from different directories causes PNG regeneration.
- No connection-state indicator for the camera / RTSP stream.
- No automated tests.
The application sends VISCA commands as UDP datagrams wrapped in a payload header (payload_type 01 00, length, sequence number, body). Command templates are stored in visca/protocols.py as hex strings with letter placeholders (p, q, …) substituted via .replace() at call time.
Supported operations:
camera_on/camera_off- Pan/Tilt:
pan_up,pan_down,pan_left,pan_right, diagonals,pan_stop,pan_home - Zoom:
zoom_tele,zoom_wide,zoom_stop - Focus:
focus_near,focus_far - Memory:
memory_set,memory_recall(used for presets) - Preset speed:
set_preset_speed,get_preset_speed