A Flask-based web application for monitoring multiple RTSP camera streams in real-time with a modern Bulma CSS interface.
- Flexible Camera Support - Monitor any number of RTSP camera streams configured via JSON
- Motion Detection - Real-time motion detection with visual overlays and alerts
- Quality Toggle - Switch between main (ch0/HD) and sub (ch1/SD) streams on the fly
- Auto-Reconnect - Automatic reconnection when camera feeds drop
- TV Static Animation - Animated static display when cameras are offline
- mDNS Support - Works with .local hostnames (e.g.,
sonoff-cam-2.local) - Password Protection - Supports RTSP authentication
- Responsive Design - Bulma CSS framework with mobile support
- Local Storage - Remembers your quality and motion detection preferences
- Offline Operation - All assets served locally, no internet connection required
- Python 3.7 or higher
- pip (Python package manager)
- RTSP camera streams
-
Clone or download this repository
-
Install Python dependencies
pip install -r requirements.txtThe required packages are:
- Flask - Web framework
- opencv-python - RTSP stream handling and MJPEG conversion
- numpy - Image processing
- python-dotenv - Environment variable management
- Configure your cameras
Edit cameras.json to add your camera configurations:
[
{
"id": 1,
"name": "Front Door",
"url": "rtsp://thingino:thingino@sonoff-cam-1.local:554/ch0",
"enabled": true
},
{
"id": 2,
"name": "Backyard",
"url": "rtsp://thingino:thingino@sonoff-cam-2.local:554/ch0",
"enabled": true
},
{
"id": 3,
"name": "Garage",
"url": "rtsp://thingino:thingino@sonoff-cam-3.local:554/ch0",
"enabled": true
}
]Optional: Copy and edit .env for stream settings:
cp .env.example .envEdit .env for optional stream configuration:
# Stream Settings (optional)
JPEG_QUALITY=80
RETRY_INTERVAL=5
STREAM_TIMEOUT=10
FLASK_PORT=8080RTSP URL Format:
rtsp://username:password@hostname:port/path
For Thingino cameras:
- Main stream (high quality):
/ch0 - Sub stream (lower quality):
/ch1 - Default port:
554
- Start the Flask application
python app.py- Open your web browser
Navigate to:
http://localhost:8080
(If you need to use a different port, change FLASK_PORT in your .env file)
- Toggle Stream Quality
Click the HD or SD buttons on each camera to switch between:
- HD (Main) - High quality stream (ch0) - Higher bandwidth
- SD (Sub) - Lower quality stream (ch1) - Lower bandwidth, recommended for multiple cameras
Your quality preferences are saved automatically.
- Enable Motion Detection
Click the ποΈ (eye) button on any camera to enable motion detection:
- Green bounding boxes appear around detected motion
- Motion badge pulses when movement is detected
- "MOTION DETECTED" text overlays the video feed
- Your motion detection preferences are saved per camera
Motion detection uses OpenCV's background subtraction algorithm and runs server-side with minimal CPU overhead.
The application supports real-time motion detection using OpenCV's MOG2 (Mixture of Gaussians) background subtraction algorithm.
- Visual Overlays - Green bounding boxes highlight areas with detected motion
- Real-time Alerts - Pulsing badge indicator when motion is active
- Per-Camera Control - Enable/disable independently for each camera
- Configurable Sensitivity - Adjust detection thresholds via environment variables
- Persistent Preferences - Motion settings saved to browser localStorage
- Low Overhead - Adds only ~5-10% CPU usage per stream
- Click the ποΈ button on any camera card to enable motion detection
- The button turns yellow to indicate motion detection is active
- Move in front of the camera to trigger detection
- Green boxes will highlight detected motion areas
- The motion badge will pulse when movement is detected
- Click the ποΈ button again to disable motion detection
Motion detection can be fine-tuned via environment variables in .env:
# Motion Detection Settings
MOTION_DETECTION_ENABLED=False # Global default (users can enable per-camera)
MOTION_SENSITIVITY=medium # Options: low, medium, high
MOTION_MIN_AREA=500 # Minimum motion area in pixelsSensitivity Levels:
- low - Less sensitive, fewer false positives (e.g., trees, shadows)
- medium - Balanced detection (recommended)
- high - More sensitive, may trigger on small movements
Min Area: Filters out tiny movements. Increase for larger objects only, decrease for detecting small motions.
- Motion detection adds approximately 5-10% CPU overhead per camera
- The background subtraction algorithm is highly optimized
- Enable motion detection on 2-3 cameras simultaneously on typical hardware
- Disable motion detection when not needed to conserve resources
- The MOG2 algorithm adapts to lighting changes automatically
Motion detection is implemented using OpenCV's MOG2 (Mixture of Gaussians) background subtraction algorithm. Here's the processing pipeline:
- MOG2 maintains a statistical model of the background scene
- Uses the last 500 frames (~17 seconds at 30fps) to build the model
- Adapts automatically to gradual lighting changes (clouds, sunset)
- Each pixel is modeled as a mixture of Gaussian distributions
When motion detection is enabled, each video frame goes through these steps:
- Background Subtraction - Compare current frame to background model
- Shadow Removal - Detect and remove shadows (reduces false positives)
- Binary Threshold - Create clean foreground/background mask
- Noise Reduction - Morphological operations (closing/opening) to clean up small artifacts
- Contour Detection - Find connected regions of motion
- Area Filtering - Ignore contours smaller than minimum area (default: 500 pixels)
- Bounding Boxes - Draw green rectangles around significant motion regions
- Overlay Text - Add "MOTION DETECTED" indicator when motion is present
- Motion detection runs on the Flask server, not in the browser
- Processes frames in the existing RTSP β MJPEG conversion pipeline
- All connected clients see the same motion indicators
- No additional bandwidth required (same MJPEG stream)
- JavaScript polls
/api/motion/<camera_id>/<quality>every 500ms - Updates motion badge indicator based on detection status
- Badge pulses (yellow) when motion is actively detected
- Preferences saved to browser localStorage for persistence
- MOG2 needs 5-10 seconds after enabling to build a stable background model
- During this "learning phase", expect some false positives
- Once stabilized, detection becomes very accurate
- Model continuously updates to adapt to scene changes
- Uses threading locks to handle multiple concurrent viewers
- Safe for Flask's multithreaded environment
- Each camera/quality combination has one shared StreamHandler instance
| Parameter | Default | Description |
|---|---|---|
history |
500 frames | How many frames to use for background model |
varThreshold |
16 (medium) | Pixel variance threshold (lower = more sensitive) |
detectShadows |
True | Identify and ignore moving shadows |
min_contour_area |
500 pixels | Minimum size to consider as motion |
motion_timeout |
2.0 seconds | How long to keep "motion detected" flag active |
We chose MOG2 over simpler approaches (frame differencing) because:
- Adaptive - Learns and updates background automatically
- Robust - Handles lighting changes, shadows, and camera noise
- Efficient - Highly optimized C++ implementation in OpenCV
- Industry Standard - Proven in commercial surveillance systems
- Low Latency - Processes 640x480 frames in ~10-20ms
For detailed implementation notes, architecture decisions, and future enhancement ideas, see MOTION_DETECTION_NOTES.md.
Each camera entry supports:
| Field | Required | Description |
|---|---|---|
id |
Yes | Unique numeric camera identifier |
name |
Yes | Display name for the camera |
url |
Yes | RTSP camera URL (format: rtsp://username:password@hostname:port/path) |
enabled |
No | Enable/disable camera (default: true) |
Add or remove cameras as needed - the application dynamically loads all enabled cameras.
| Variable | Default | Description |
|---|---|---|
FLASK_PORT |
5000 | Web server port |
FLASK_DEBUG |
False | Enable debug mode |
FLASK_SECRET_KEY |
- | Flask secret key for sessions |
JPEG_QUALITY |
80 | JPEG compression quality (1-100) |
RETRY_INTERVAL |
5 | Seconds between reconnection attempts |
STREAM_TIMEOUT |
10 | Connection timeout in seconds |
MOTION_DETECTION_ENABLED |
False | Global default for motion detection |
MOTION_SENSITIVITY |
medium | Motion sensitivity: low, medium, or high |
MOTION_MIN_AREA |
500 | Minimum motion area in pixels |
- 90-100: Highest quality, largest bandwidth
- 75-85: Good balance (recommended)
- 60-70: Lower quality, reduced bandwidth
- Below 60: Noticeable quality loss
- Check RTSP URL - Verify hostname, port, path, and credentials
- Test with VLC - Try opening the RTSP URL in VLC Media Player
- Network connectivity - Ensure cameras are reachable from your computer
- Firewall - Check firewall rules for port 554 (RTSP)
- macOS: Should work out of the box (Bonjour/mDNS built-in)
- Alternative: Use IP addresses instead:
rtsp://username:password@192.168.1.100:554/ch0
- Use sub streams - Toggle to SD quality (ch1) for lower resolution
- Reduce JPEG quality - Lower
JPEG_QUALITYin.env - Fewer simultaneous viewers - Each browser connection creates a new stream
- Check network stability - WiFi signal strength, bandwidth
- Camera health - Some cameras struggle with multiple connections
- Increase timeout - Set higher
STREAM_TIMEOUTin.env
- Check browser console - Press F12 and look for JavaScript errors
- Canvas support - Ensure browser supports HTML5 Canvas
- Try different browser - Test in Chrome, Firefox, or Safari
- Check button state - Ensure ποΈ button is yellow (active)
- Trigger motion - Move in front of camera to test detection
- Adjust sensitivity - Try
MOTION_SENSITIVITY=highin.env - Check CPU usage - Motion detection requires processing power
- Browser console - Press F12 and check for API errors
- Reduce sensitivity - Set
MOTION_SENSITIVITY=lowin.env - Increase min area - Set
MOTION_MIN_AREA=1000or higher - Check camera placement - Avoid areas with trees, flags, or busy backgrounds
- Lighting conditions - MOG2 adapts but extreme changes may trigger false positives
rtsp-cam-viewer/
βββ app.py # Flask application and routes
βββ config.py # Configuration management
βββ cameras.json # Camera configurations
βββ requirements.txt # Python dependencies
βββ README.md # User documentation (this file)
βββ MOTION_DETECTION_NOTES.md # Technical implementation notes
βββ .env # Environment variables (optional)
βββ .env.example # Example configuration
βββ .gitignore # Git ignore rules
β
βββ static/
β βββ css/
β β βββ bulma.min.css # Bulma CSS framework (local)
β β βββ style.css # Custom styles
β βββ js/
β βββ app.js # Client-side logic, TV static, motion detection
β
βββ templates/
β βββ index.html # Main HTML template
β
βββ utils/
βββ __init__.py
βββ stream_handler.py # RTSP stream processing & motion detection
- Backend: Flask (Python web framework)
- Video Processing: OpenCV (RTSP capture, MJPEG encoding, MOG2 motion detection)
- Frontend: Bulma CSS (local), Vanilla JavaScript
- Streaming: MJPEG over HTTP (multipart/x-mixed-replace)
- Motion Detection: MOG2 background subtraction algorithm
- Icons: Unicode symbols (no external dependencies)
- Protect
cameras.json- Contains camera credentials and URLs - Never commit credentials - Consider using environment variables for passwords
- Use strong passwords - Change default camera passwords
- Network isolation - Consider VLANs or separate networks for cameras
- HTTPS in production - Use reverse proxy (nginx, Apache) with SSL
- Latency: MJPEG has ~1-2 second delay (acceptable for monitoring)
- Bandwidth: Each viewer creates a separate stream
- No audio: Current implementation is video-only
- Browser limit: Most browsers limit ~6 simultaneous connections per domain
- Motion detection initialization: MOG2 needs 5-10 seconds to build background model after enabling
- Motion detection false positives: Trees, shadows, and lighting changes may trigger false alerts (adjustable via sensitivity)
- Motion detection with visual overlays (β Implemented)
- Recording/snapshot capability
- PTZ (Pan-Tilt-Zoom) controls
- Motion detection recording (save clips when motion detected)
- Motion detection notifications (email/SMS alerts)
- User authentication
- HLS streaming option for lower latency
- Multi-user support with stream sharing
This project is open source and available for personal and commercial use.
For issues or questions:
- Check the troubleshooting section above
- Review MOTION_DETECTION_NOTES.md for technical details
- Review Flask and OpenCV documentation
- Test RTSP streams with VLC Media Player first
Built with:
