Skip to main content

Streamcatcher

PyPI version Python versions License: MIT

A cross-platform Python CLI that connects to an RTSP or RTMP video stream and views it in a small desktop window — including 360° panoramic cameras, which it reprojects into a flat pan/tilt/zoom "look-around" viewport. Pure Python, powered by OpenCV.

Status: early development, built in small, tested vertical slices. Live video playback, the 360° equirectangular look-around viewport, snapshots, recording, auto-reconnect, and an HTTP control API all work today. Video only in the live window — OpenCV does not decode audio (the ffmpeg record mode keeps it).

Features

  • View RTSP/RTMP streams in a native window (OpenCV — video only, no audio).
  • 360° support: reproject an equirectangular panorama into a flat look-around viewport.
  • Look around live: W/A/S/D to pan and tilt, +/- to zoom, q to quit.
  • Snapshots: press p in the viewer to save the current view, or grab one frame headlessly with --snapshot (current directory) or --snapshot out.jpg.
  • Recording: capture a stream to a file while you watch with --record — an opencv mode (video only, no extra dependency) or a lossless, audio-keeping ffmpeg mode. Cap the length with --duration SECONDS.
  • Auto-reconnect: when a live stream drops, reconnect with exponential backoff (retries forever by default; --no-reconnect to exit on the first drop).
  • Offline-first: the package and the entire test suite run with no network, no credentials, and no live stream. OpenCV is lazy-imported only on the live path, and an offline stub backend (-b stub) needs nothing at all.
  • Secrets-safe: stream URLs (which may embed user:pass@host) are wrapped in SecretStr and scrubbed from logs by a redacting filter.

Install

Requires Python 3.12+.

pip install streamcatcher

Or install the latest unreleased code straight from source:

pip install git+https://github.com/ThugipanSivanesan/Streamcatcher

The live viewer pulls in opencv-python; no separate system media player (VLC, ffmpeg app, …) is required.

Headless / server install

On a server, container, or CI box you don't need the desktop window — and the default GUI build's system libraries (libGL.so.1 on Linux) may be missing. Swap in the headless OpenCV build so serve and --snapshot run without them:

pip install streamcatcher
pip uninstall opencv-python
pip install opencv-python-headless

streamcatcher serve and streamcatcher play --snapshot work fully headless. Only the live play window needs the default opencv-python build; on a headless build it exits with a clear message instead of a cryptic OpenCV error. (The two wheels both provide cv2 and can't coexist, so it's a swap, not an extra.)

Usage

# View a plain 2D stream in a window
streamcatcher play rtsp://camera.local:554/stream1

# View a 360° equirectangular camera with a look-around viewport
streamcatcher play rtsp://123.456.7.890/live/live -p equirect

# Capture a single frame in the current directory and exit — no window (respects -p)
streamcatcher play rtsp://cam/live --snapshot

# Or choose the exact output path
streamcatcher play rtsp://cam/live --snapshot shot.jpg

# Record the stream to a file while you watch it (bare --record → timestamped .mp4)
streamcatcher play rtsp://cam/live --record capture.mp4

# Record losslessly with audio (needs the ffmpeg binary), capped at 60 seconds
streamcatcher play rtsp://cam/live --record capture.mp4 --record-mode ffmpeg --duration 60

In the viewer window: W/A/S/D (or drag the mouse) aim · +/- zoom · p snapshot · q quit. The p hotkey writes a timestamped streamcatcher-snapshot-YYYYMMDD-HHMMSS.jpg in the current directory.

Flag Values Env var
--backend / -b opencv (live window, default), stub (offline no-op) STREAMCATCHER_BACKEND
--projection / -p flat (default), equirect STREAMCATCHER_PROJECTION
--snapshot optional PATH — save one frame and exit; defaults to a timestamped JPEG in the current directory —
--record optional PATH — record while playing; defaults to a timestamped .mp4 in the current directory. Mutually exclusive with --snapshot —
--record-mode opencv (default, video only), ffmpeg (lossless + audio) STREAMCATCHER_RECORD_MODE
--duration SECONDS — stop recording and playback after the first frame; requires --record STREAMCATCHER_RECORD_DURATION
--reconnect / --no-reconnect auto-reconnect on drop (default on) STREAMCATCHER_RECONNECT_ENABLED

play opens a real OpenCV window by default; pass -b stub (or set STREAMCATCHER_BACKEND=stub) for the offline no-op backend used in development and tests.

How it works

  • Playback: OpenCV (cv2.VideoCapture + highgui) opens its own native window from a plain Python CLI. RTSP is forced over TCP to reduce dropped packets.
  • 360° reprojection: pure-NumPy equirectangular→pinhole remap tables are fed to cv2.remap. The math is deterministic and unit-tested with no GPU, window, or live stream needed.
  • Headless control core: StreamSession drives open / read / render / look-around / close with no window attached — the same core the HTTP control API sits on.
  • CLI: Typer.

HTTP control API

Install the [api] extra and run the server so another program — or an AI agent — can open sessions, drive the look-around, and pull frames:

pip install 'streamcatcher[api]'
streamcatcher serve                                   # binds 127.0.0.1:8000
streamcatcher serve --host 0.0.0.0 --port 9000 --token changeme

Interactive OpenAPI docs are served at /docs. Key endpoints:

Endpoint Purpose
POST /session Open a session for a stream URL; returns an id (the URL is never echoed back).
GET /session/{id}/frame Current look-around viewport as a JPEG — how an agent "sees" now.
GET /session/{id}/panorama Raw, full-frame JPEG (before reprojection).
GET /session/{id}/stream.mjpg MJPEG stream of the viewport.
POST /session/{id}/look Pan/tilt/zoom by {pan, tilt, zoom} degree deltas.
POST /session/{id}/look/{pan_left…zoom_out} Discrete look steps.
GET /session/{id}/state Current projection and orientation.
DELETE /session/{id} Close the session.

The server binds 127.0.0.1 by default. Set --token (or STREAMCATCHER_API_TOKEN) to require an Authorization: Bearer <token> header on every request. Stream URLs and their credentials are never returned in any response.

Documentation

Full docs — CLI usage, the 360°/camera guide, the Python and HTTP APIs, architecture, and an API reference generated from the source — are built with mkdocs-material under docs/. Build them locally with uv run mkdocs serve (see Contributing). See the changelog for what has shipped so far.

Contributing

See CONTRIBUTING.md for the full development workflow. The short version, using uv:

uv sync --extra api           # create the environment (incl. the API extra)
uv run pytest                 # tests (offline, no credentials)
uv run ruff check .           # lint
uv run ruff format --check .  # format check
uv run mkdocs serve           # preview the docs (needs: uv sync --group docs)
pre-commit install            # enable local hooks

Security

  • CI runs gitleaks (secret scanning) and osv-scanner (dependency vulnerabilities) on every pull request.
  • Pre-commit hooks scan for secrets and private keys before anything is committed.
  • Stream URLs and their embedded credentials are never written to logs.

License

MIT

Metadata

Release files for streamcatcher 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for streamcatcher 0.3.0
File Size Uploaded
streamcatcher-0.3.0.tar.gz 152.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for streamcatcher 0.3.0
File Interpreter ABI Platform
streamcatcher-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 199.9 kB

Release files / streamcatcher-0.3.0.tar.gz

Download URL streamcatcher-0.3.0.tar.gz
Size 152.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f11d71a7783f03902a58d43f0c4d6ad8ff63ec2be27655bed63578534837ea50
BLAKE2b-256 checksum
How to use checksums
6c22023d483d2130deee9183e6813d796da19c6dc00b41edd9f8593b6ac8344f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Jul 28, 2026.

Transparency log

Release files / streamcatcher-0.3.0-py3-none-any.whl

Download URL streamcatcher-0.3.0-py3-none-any.whl
Size 47.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5fa7c873e28b8545f84a6ca5d945d309423902de1cdc546f969d60097d3af0dc
BLAKE2b-256 checksum
How to use checksums
faad82256564afcf12759d3857e8a2f002405eae7e54fabb7f06b6ff2cbe6d22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Jul 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page