API reference¶
Auto-generated from the source docstrings. The headline entry point is
StreamSession; see
Python API for a task-oriented walkthrough.
Control core — player.session¶
streamcatcher.player.session
¶
Headless stream control core, shared by the GUI player and the HTTP API.
:class:StreamSession owns the OpenCV capture and the optional 360 viewport and
exposes programmatic controls — open, read a frame, render the current
viewport, look around (pan/tilt/zoom), inspect state, close — with no window
and no keyboard loop. The GUI player (:class:~streamcatcher.player.opencv_player.OpenCvPlayer)
and, later, the FastAPI server both drive the same session, so the controls live
in exactly one place.
cv2 is imported lazily inside :meth:StreamSession.open, so importing this
module never requires OpenCV; tests inject a fake cv2.
StreamOpenError
¶
Bases: RuntimeError
Raised when OpenCV cannot open the stream URL.
SnapshotError
¶
Bases: RuntimeError
Raised when a snapshot frame can't be captured or written to disk.
ViewState
dataclass
¶
A snapshot of the viewport orientation (None fields when not 360).
StreamSession
¶
Own a live capture and an optional 360 viewport, with no window.
Source code in src/streamcatcher/player/session.py
has_viewport
property
¶
Whether this session reprojects a look-around viewport (equirect).
open
¶
Open the stream capture with low-latency options. Raises on failure.
Source code in src/streamcatcher/player/session.py
close
¶
Release the capture. Safe to call more than once.
reconnect
¶
Tear down and re-open the capture, keeping the viewport orientation.
Returns True on success, or False if the stream still can't be
opened (so the caller can back off and retry). The viewport _view
(yaw/pitch/hfov) is left untouched, so a look-around orientation
survives a reconnect; cached remap tables are dropped in case the
stream returns at a different resolution.
Source code in src/streamcatcher/player/session.py
is_open
¶
read_frame
¶
Read the next raw frame; returns the frame, or None when it ends.
render
¶
Return the viewport for frame — reprojected in 360, else unchanged.
Source code in src/streamcatcher/player/session.py
grab_view
¶
Read and render the next viewport frame; None when the stream ends.
look
¶
Apply pan/tilt/zoom degree deltas to the viewport (a no-op when flat).
zoom is a horizontal-FOV delta: negative narrows the view (zooms in).
Source code in src/streamcatcher/player/session.py
state
¶
Current projection and (in 360) the viewport orientation.
Source code in src/streamcatcher/player/session.py
snapshot
¶
Grab the current viewport and save it to path as an image file.
The saved image is the reprojected look-around viewport in 360 modes
(what the viewer sees), or the raw frame when flat. Reads until a frame
arrives — a just-opened decoder can return empty first — then writes it,
raising :class:SnapshotError if no frame comes or the file can't be
written.
Source code in src/streamcatcher/player/session.py
write_snapshot
¶
Write an already-rendered frame to path as an image file.
Creates the parent directory if needed. Raises :class:SnapshotError
when OpenCV can't encode/write the file (e.g. an unknown extension or an
unwritable location).
Source code in src/streamcatcher/player/session.py
Configuration — config¶
streamcatcher.config
¶
Typed, secret-safe configuration for Streamcatcher.
The stream URL is treated as a secret because RTSP/RTMP URLs routinely embed
credentials (rtsp://user:pass@host). It is stored as a SecretStr so it
never prints in reprs/tracebacks, and :meth:Settings.secret_values exposes the
plaintext only to seed the log-redaction filter (see logging_setup).
Backend
¶
Bases: StrEnum
Playback backend that the player factory dispatches on.
Projection
¶
Bases: StrEnum
How the player interprets each frame's geometry.
Settings
¶
Bases: BaseSettings
Runtime settings, populated from STREAMCATCHER_* environment vars.
secret_values
¶
Plaintext credentials from the stream URL, to seed log redaction.
Returns the embedded password and username (if any) — the sensitive
parts of an rtsp://user:pass@host URL — rather than the whole URL,
so the non-secret host can still be logged.
Source code in src/streamcatcher/config.py
strip_url_credentials
¶
Return url with any user:pass@ userinfo removed (host/port kept).
Source code in src/streamcatcher/config.py
Reprojection — player.reprojection¶
streamcatcher.player.reprojection
¶
Reprojection of 360 equirectangular streams to a flat, look-around viewport.
A 360 camera streams an equirectangular frame — a 2:1 panorama of the whole sphere. Viewed raw it looks warped. This module aims a virtual pinhole camera — an ordinary flat window — at the source, so the viewer sees an undistorted slice and can pan/tilt/zoom around it.
The lookup tables are built with NumPy only (no OpenCV), so the math is pure,
deterministic, and unit-testable without a decoder or a display. The player
feeds the returned map_x/map_y to cv2.remap to warp each frame.
:class:EquirectView is the virtual camera: it samples a full 360×180
equirectangular panorama for whatever slice the current yaw/pitch/hfov select.
Coordinate convention (right-handed camera space): +X right, +Y up,
+Z forward. yaw pans left/right about +Y; pitch tilts up/down
(positive looks up); roll rotates about +Z (used only as a fixed mounting
offset). hfov is the horizontal field of view — smaller is more zoomed in.
The *_offset_deg values are fixed mounting rotations added on top of the
interactive yaw/pitch, so a camera mounted rotated still reads level.
EquirectView
¶
EquirectView(
out_width=1280,
out_height=720,
hfov_deg=100.0,
yaw_deg=0.0,
pitch_deg=0.0,
yaw_offset_deg=0.0,
pitch_offset_deg=0.0,
roll_offset_deg=0.0,
)
Bases: _SphericalView
A virtual pinhole camera aimed into a full 360×180 equirectangular panorama.
Longitude spans the full 360 across the frame width and latitude the full
180 across its height, so every camera ray lands somewhere on the frame.
Source code in src/streamcatcher/player/reprojection.py
Reconnect — player.reconnect¶
streamcatcher.player.reconnect
¶
Reconnect policy and backoff schedule — pure, no OpenCV.
When a live stream drops, the GUI player retries the connection with
exponential backoff. The policy (how fast to back off, and whether to retry at
all) and the delay schedule live here, separate from any window or cv2 so
they stay deterministic and unit-testable. The retry loop that consumes this
schedule lives in :class:~streamcatcher.player.opencv_player.OpenCvPlayer.
ReconnectPolicy
dataclass
¶
How to retry a dropped stream.
enabled=False restores the old behaviour: exit on the first drop.
Otherwise the player retries forever, waiting base_delay seconds and
multiplying by factor after each failed attempt, capped at max_delay.
backoff_delays
¶
Yield reconnect wait times forever: base, base·factor, … capped at max.
Infinite by design — with the default policy this is
1, 2, 4, 8, 16, 30, 30, … — so the player retries until the stream
returns or the user quits. The caller is responsible for stopping.
Source code in src/streamcatcher/player/reconnect.py
Player protocol & backends¶
streamcatcher.player.base
¶
streamcatcher.player.opencv_player
¶
Live stream GUI player — a thin OpenCV window over :class:StreamSession.
OpenCV's highgui creates and pumps its own native window via imshow /
waitKey from a plain Python process — including on macOS — so, unlike
libVLC, we own the window without an embedded native drawable or an external
app. The tradeoff: cv2.VideoCapture decodes video frames only, so this
backend has no audio.
All stream and viewport logic lives in :class:StreamSession; this class only
adds the window, the waitKey loop, and the keyboard bindings, so the same
controls are reused by the (headless) HTTP API. cv2 is imported lazily so
importing this module never requires OpenCV; tests inject a fake cv2.
SnapshotError
¶
Bases: RuntimeError
Raised when a snapshot frame can't be captured or written to disk.
StreamOpenError
¶
Bases: RuntimeError
Raised when OpenCV cannot open the stream URL.
OpenCvPlayer
¶
Play a live RTMP/RTSP stream in an OpenCV window (video only).
With projection=Projection.EQUIRECT the stream is treated as a 360
equirectangular panorama and reprojected to a flat viewport the user can
look around with W/A/S/D (tilt/pan) and +/- (zoom).
Projection.FLAT (the default) shows frames unchanged.
Source code in src/streamcatcher/player/opencv_player.py
play
¶
Open the stream and show frames until the window closes or 'q' is hit.
Source code in src/streamcatcher/player/opencv_player.py
stop
¶
Release the capture and destroy the window.
Source code in src/streamcatcher/player/opencv_player.py
snapshot
¶
Capture a single frame from the stream and save it to path (no window).
Opens the stream if it isn't already, grabs one rendered frame — applying
the configured projection, so the still matches what the window
shows — writes it, then leaves the capture as it found it. Raises
:class:StreamOpenError if the stream won't open or
:class:SnapshotError if no frame arrives or the file can't be written.
Source code in src/streamcatcher/player/opencv_player.py
streamcatcher.player.stub_player
¶
Offline stub player — the default backend.
Implements the :class:~streamcatcher.player.base.Player interface without any
window, decoder, or network, so the app and the whole test suite run fully
offline. It records what it was asked to do rather than doing it.
StubPlayer
¶
Player factory — player.factory¶
streamcatcher.player.factory
¶
Player factory — selects a backend from settings.
Defaults to the offline stub. The live OpenCV backend (:class:OpenCvPlayer)
lazy-imports cv2 inside its own play method, so importing this factory
never requires OpenCV to be installed — only playing a stream does.
get_player
¶
Build the player for settings.backend using its stream URL.
Source code in src/streamcatcher/player/factory.py
HTTP API models — api.models¶
streamcatcher.api.models
¶
Request and response models for the HTTP control API.
These use only Pydantic (already a core dependency), so this module imports with no web stack. The stream URL is accepted on input but never returned in any response — responses expose only the opaque session id and the viewport state.