Overview
The stream processing module provides three classes for handling RTSP streams:- StreamProcessor - Processes single RTSP streams with person detection and saving
- MultiStreamManager - Orchestrates multiple concurrent streams
- DisplayManager - Manages grid display window for multiple streams
StreamProcessor
Processes a single RTSP stream, runs person detection, handles saving (image snapshots or video clips), and optionally feeds frames to a DisplayManager.Constructor
__init__()
PersonDetector
required
PersonDetector instance to use for person detection. Can be shared across multiple StreamProcessor instances.
str
default:"person"
Base directory where captured images or video clips are saved.
stream_processor.py:26-28
Methods
process_rtsp_stream()
str
required
RTSP stream URL to connect to.Example:
"rtsp://192.168.1.100:554/stream"int
default:"15"
Analyze every Nth frame. At 30fps,
frame_skip=15 means ~2 detections per second.Additional throttling: Detection also limited to once every 0.5 seconds (line 303).bool
default:"True"
Show a live annotated window resized to 1280×720. Press ‘q’ to quit.
Optional[str]
default:"None"
Save mode for captures:
"image"- Save JPEG snapshots when person enters frame"video"- Record MP4 clips from entry until exitNone- Don’t save anything
- Connects to RTSP stream with automatic reconnection (up to 5 attempts)
- Processes frames continuously
- Runs person detection every
frame_skipframes (and max once per 0.5s) - Tracks person entry/exit with 3-frame exit confirmation threshold
- Saves snapshots or video clips based on
save_mode - Displays annotated frames if
display=True - Handles Ctrl+C gracefully
- User presses ‘q’ (when display enabled)
- User presses Ctrl+C
- Max reconnection attempts exceeded
- Stream ends naturally
stream_processor.py:230-410
process_single_stream()
Union[int, str]
required
Unique identifier for this stream (used in console output and directory naming).
str
required
RTSP stream URL to connect to.
int
default:"15"
Analyze every Nth frame.
Optional[str]
default:"None"
"image", "video", or None.Optional[DisplayManager]
default:"None"
If provided, updates the shared grid display buffer with annotated frames.
- Nearly identical to
process_rtsp_stream(), but designed for threading - Saves files to
{output_dir}/stream_{stream_id}/subdirectories - Updates
display_managerinstead of creating its own window - Prefixes all console output with
[Stream {stream_id}] - Stops when
display_manager.is_runningbecomesFalse
stream_processor.py:34-224
Private Methods
_save_annotated_snapshot()
stream_processor.py:416-434
Person Entry/Exit Detection
StreamProcessor uses a state machine to track person presence: Entry Detection:- Person detected in frame where
person_present = False - Triggers snapshot save (image mode) or starts video recording (video mode)
- Increments
person_entry_count
- Uses 3-frame confirmation threshold (
NO_PERSON_EXIT_THRESHOLD = 3) - Requires 3 consecutive detection frames with no person
- Prevents false exits from brief detection failures
- Stops video recording when person exits
person_present(bool): Current presence stateno_person_streak(int): Consecutive frames without detectionperson_entry_count(int): Total number of entries detected
stream_processor.py:123-181 and stream_processor.py:311-355
Automatic Reconnection
Both stream processing methods implement automatic reconnection:- Waits 2 seconds between reconnection attempts
- Allows up to 5 consecutive failures
- Resets failure counter on successful frame read
stream_processor.py:89-106 and stream_processor.py:281-296
Video Recording
Whensave_mode="video", StreamProcessor records MP4 clips:
Codec: mp4v (MPEG-4)
FPS: Automatically detected from stream via cv2.CAP_PROP_FPS (defaults to 25.0 if invalid)
Recording lifecycle:
- Person enters frame → Create
cv2.VideoWriter - Every frame → Write to video file
- Person exits frame → Release writer and finalize file
stream_processor.py:79-82, 148-159, 175-179, 329-339, 351-354
MultiStreamManager
Orchestrates concurrent processing of multiple RTSP streams. Each stream runs in its own daemon thread via StreamProcessor.Constructor
__init__()
PersonDetector
required
PersonDetector instance shared across all streams.
str
default:"person"
Base directory for all stream outputs. Each stream creates a subdirectory:
{output_dir}/stream_{id}/multi_stream_manager.py:31-34
Methods
process_multiple_streams()
Union[List[str], Dict[Union[int, str], str]]
required
RTSP stream URLs. Can be:List format (auto-numbered 1, 2, 3, …):Dict format (custom IDs):
int
default:"15"
Analyze every Nth frame (applied to all streams).
Optional[str]
default:"None"
"image", "video", or None (applied to all streams).bool
default:"False"
Show a live grid window with all streams. Press ‘q’ to quit.
- Creates output directory structure
- Optionally starts DisplayManager with grid window
- Spawns one daemon thread per stream
- Waits for all threads to complete or display window to close
- Handles Ctrl+C gracefully
- Cleans up display manager on exit
multi_stream_manager.py:36-103
Complete Usage Example:
DisplayManager
Manages a single resizable grid window that composites frames from multiple streams in real time.Constructor
__init__()
int
default:"640"
Width of each stream cell in the grid (pixels).
int
default:"360"
Height of each stream cell in the grid (pixels).
- Automatically calculates grid dimensions based on number of streams
- Uses square-ish layout:
cols = ceil(sqrt(n)),rows = ceil(n / cols) - Total window size:
(cell_width * cols, cell_height * rows)
- 1 stream: 1×1 grid (640×360)
- 2 streams: 2×1 grid (1280×360)
- 3 streams: 2×2 grid (1280×720)
- 4 streams: 2×2 grid (1280×720)
- 9 streams: 3×3 grid (1920×1080)
display_manager.py:22-30
Properties
is_running
display_manager.py:36-38
Methods
start()
List[Union[int, str]]
required
List of stream identifiers that will be displayed. Order determines grid position (left-to-right, top-to-bottom).Example:
- Initializes frame buffer for each stream (all
Noneinitially) - Sets
is_running = True - Spawns daemon thread running
_loop() - Returns immediately (non-blocking)
display_manager.py:40-48
stop()
- Sets
is_running = False - Waits for display thread with 2-second timeout
- Destroys all OpenCV windows
- Clears frame buffer
display_manager.py:50-57
update_frame()
Union[int, str]
required
Stream identifier (must match one from
start()).cv2.typing.MatLike
required
OpenCV image matrix to display. Will be resized to
(cell_width, cell_height) automatically.self._lock).
Usage Pattern:
display_manager.py:59-62
Internal Methods
_build_grid()
- Locks frame buffer during composition
- Resizes each frame to cell dimensions
- Shows “Connecting…” placeholder for streams without frames yet
- Arranges frames in grid layout
- Returns single composited image
display_manager.py:68-109
_loop()
- Creates window:
"Person Detection - All Streams" - Updates display every 30ms (~33 fps)
- Stops when
is_running = Falseor user presses ‘q’ - Destroys window on exit
display_manager.py:111-124
Complete Usage Example
Integration Example
Complete example showing how all three classes work together:Implementation References
- StreamProcessor:
stream_processor.py(435 lines) - MultiStreamManager:
multi_stream_manager.py(104 lines) - DisplayManager:
display_manager.py(125 lines)