18 KiB
🎬 Video Streaming Module
The USDA Vision Camera System now includes a modular video streaming system that provides YouTube-like video playback capabilities for your React web application.
🌟 Features
- Progressive Streaming - True chunked streaming for web browsers (no download required)
- HTTP Range Request Support - Enables seeking and progressive download with 206 Partial Content
- Native MP4 Support - Direct streaming of MP4 files optimized for web playback
- Memory Efficient - 8KB chunked delivery, no large file loading into memory
- Browser Compatible - Works with HTML5
<video>tag in all modern browsers - Intelligent Caching - Optimized streaming performance with byte-range caching
- Thumbnail Generation - Extract preview images from videos
- Modular Architecture - Clean separation of concerns
- No Authentication Required - Open access for internal network use
- CORS Enabled - Ready for web browser integration
🏗️ Architecture
The video module follows clean architecture principles:
usda_vision_system/video/
├── domain/ # Business logic (pure Python)
├── infrastructure/ # External dependencies (OpenCV, FFmpeg)
├── application/ # Use cases and orchestration
├── presentation/ # HTTP controllers and API routes
└── integration.py # Dependency injection and composition
🚀 API Endpoints
List Videos
GET /videos/
Query Parameters:
camera_name(optional): Filter by camera namestart_date(optional): Filter videos created after this date (ISO format: 2025-08-04T14:30:22)end_date(optional): Filter videos created before this date (ISO format: 2025-08-04T14:30:22)limit(optional): Maximum results (default: 50, max: 1000)include_metadata(optional): Include video metadata (default: false)
Example Request:
curl "http://localhost:8000/videos/?camera_name=camera1&include_metadata=true&limit=10"
Response:
{
"videos": [
{
"file_id": "camera1_auto_blower_separator_20250804_143022.mp4",
"camera_name": "camera1",
"filename": "camera1_auto_blower_separator_20250804_143022.mp4",
"file_size_bytes": 31457280,
"format": "mp4",
"status": "completed",
"created_at": "2025-08-04T14:30:22",
"start_time": "2025-08-04T14:30:22",
"end_time": "2025-08-04T14:32:22",
"machine_trigger": "blower_separator",
"is_streamable": true,
"needs_conversion": false,
"metadata": {
"duration_seconds": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"codec": "mp4v",
"bitrate": 5000000,
"aspect_ratio": 1.777
}
}
],
"total_count": 1
}
Stream Video
GET /videos/{file_id}/stream
Headers:
Range: bytes=0-1023(optional): Request specific byte range for seeking
Example Requests:
# Stream entire video (progressive streaming)
curl http://localhost:8000/videos/camera1_auto_blower_separator_20250805_123329.mp4/stream
# Stream specific byte range (for seeking)
curl -H "Range: bytes=0-1023" \
http://localhost:8000/videos/camera1_auto_blower_separator_20250805_123329.mp4/stream
Response Headers:
Accept-Ranges: bytesContent-Length: {size}Content-Range: bytes {start}-{end}/{total}(for range requests)Cache-Control: public, max-age=3600Content-Type: video/mp4
Streaming Implementation:
- ✅ Progressive Streaming: Uses FastAPI
StreamingResponsewith 8KB chunks - ✅ HTTP Range Requests: Returns 206 Partial Content for seeking
- ✅ Memory Efficient: No large file loading, streams directly from disk
- ✅ Browser Compatible: Works with HTML5
<video>tag playback - ✅ Chunked Delivery: Optimal 8KB chunk size for smooth playback
- ✅ CORS Enabled: Ready for web browser integration
Response Status Codes:
200 OK: Full video streaming (progressive chunks)206 Partial Content: Range request successful404 Not Found: Video not found or not streamable416 Range Not Satisfiable: Invalid range request
Get Video Info
GET /videos/{file_id}
Example Request:
curl http://localhost:8000/videos/camera1_recording_20250804_143022.avi
Response includes complete metadata:
{
"file_id": "camera1_recording_20250804_143022.avi",
"camera_name": "camera1",
"filename": "camera1_recording_20250804_143022.avi",
"file_size_bytes": 52428800,
"format": "avi",
"status": "completed",
"created_at": "2025-08-04T14:30:22",
"start_time": "2025-08-04T14:30:22",
"end_time": "2025-08-04T14:32:22",
"machine_trigger": "vibratory_conveyor",
"is_streamable": true,
"needs_conversion": true,
"metadata": {
"duration_seconds": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"codec": "XVID",
"bitrate": 5000000,
"aspect_ratio": 1.777
}
}
Get Thumbnail
GET /videos/{file_id}/thumbnail?timestamp=5.0&width=320&height=240
Query Parameters:
timestamp(optional): Time position in seconds to extract thumbnail from (default: 1.0)width(optional): Thumbnail width in pixels (default: 320)height(optional): Thumbnail height in pixels (default: 240)
Example Request:
curl "http://localhost:8000/videos/camera1_recording_20250804_143022.avi/thumbnail?timestamp=5.0&width=320&height=240" \
--output thumbnail.jpg
Response: JPEG image data with caching headers
Content-Type: image/jpegCache-Control: public, max-age=3600
Streaming Info
GET /videos/{file_id}/info
Example Request:
curl http://localhost:8000/videos/camera1_recording_20250804_143022.avi/info
Response: Technical streaming details
{
"file_id": "camera1_recording_20250804_143022.avi",
"file_size_bytes": 52428800,
"content_type": "video/x-msvideo",
"supports_range_requests": true,
"chunk_size_bytes": 262144
}
Video Validation
POST /videos/{file_id}/validate
Example Request:
curl -X POST http://localhost:8000/videos/camera1_recording_20250804_143022.avi/validate
Response: Validation status
{
"file_id": "camera1_recording_20250804_143022.avi",
"is_valid": true
}
Cache Management
POST /videos/{file_id}/cache/invalidate
Example Request:
curl -X POST http://localhost:8000/videos/camera1_recording_20250804_143022.avi/cache/invalidate
Response: Cache invalidation status
{
"file_id": "camera1_recording_20250804_143022.avi",
"cache_invalidated": true
}
Admin: Cache Cleanup
POST /admin/videos/cache/cleanup?max_size_mb=100
Example Request:
curl -X POST "http://localhost:8000/admin/videos/cache/cleanup?max_size_mb=100"
Response: Cache cleanup results
{
"cache_cleaned": true,
"entries_removed": 15,
"max_size_mb": 100
}
🌐 React Integration
Basic Video Player
function VideoPlayer({ fileId }) {
return (
<video
controls
width="100%"
preload="metadata"
style={{ maxWidth: '800px' }}
>
<source
src={`${API_BASE_URL}/videos/${fileId}/stream`}
type="video/mp4"
/>
Your browser does not support video playback.
</video>
);
}
Advanced Player with Thumbnail
function VideoPlayerWithThumbnail({ fileId }) {
const [thumbnail, setThumbnail] = useState(null);
useEffect(() => {
fetch(`${API_BASE_URL}/videos/${fileId}/thumbnail`)
.then(response => response.blob())
.then(blob => setThumbnail(URL.createObjectURL(blob)));
}, [fileId]);
return (
<video controls width="100%" poster={thumbnail}>
<source
src={`${API_BASE_URL}/videos/${fileId}/stream`}
type="video/mp4"
/>
</video>
);
}
Video List Component
function VideoList({ cameraName }) {
const [videos, setVideos] = useState([]);
useEffect(() => {
const params = new URLSearchParams();
if (cameraName) params.append('camera_name', cameraName);
params.append('include_metadata', 'true');
fetch(`${API_BASE_URL}/videos/?${params}`)
.then(response => response.json())
.then(data => setVideos(data.videos));
}, [cameraName]);
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{videos.map(video => (
<VideoCard key={video.file_id} video={video} />
))}
</div>
);
}
🔧 Configuration
The video module is automatically initialized when the API server starts. Configuration options:
# In your API server initialization
video_module = create_video_module(
config=config,
storage_manager=storage_manager,
enable_caching=True, # Enable streaming cache
enable_conversion=True # Enable format conversion
)
Configuration Parameters
enable_caching: Enable/disable intelligent byte-range caching (default: True)cache_size_mb: Maximum cache size in MB (default: 100)cache_max_age_minutes: Cache entry expiration time (default: 30)enable_conversion: Enable/disable automatic AVI to MP4 conversion (default: True)conversion_quality: Video conversion quality: "low", "medium", "high" (default: "medium")
System Requirements
- OpenCV: Required for thumbnail generation and metadata extraction
- FFmpeg: Optional, for video format conversion (graceful fallback if not available)
- Storage: Sufficient disk space for video files and cache
- Memory: Recommended 2GB+ RAM for caching and video processing
🔐 Authentication & Security
Current Security Model
⚠️ IMPORTANT: No authentication is currently implemented.
- Open Access: All video streaming endpoints are publicly accessible
- CORS Policy: Currently set to allow all origins (
allow_origins=["*"]) - Network Security: Designed for internal network use only
- No API Keys: No authentication tokens or API keys required
- No Rate Limiting: No request rate limiting currently implemented
Security Considerations for Production
For Internal Network Deployment
# Current configuration is suitable for:
# - Internal corporate networks
# - Isolated network segments
# - Development and testing environments
For External Access (Recommendations)
If you need to expose the video streaming API externally, consider implementing:
-
Authentication Layer
# Example: Add JWT authentication from fastapi import Depends, HTTPException from fastapi.security import HTTPBearer security = HTTPBearer() async def verify_token(token: str = Depends(security)): # Implement token verification logic pass -
CORS Configuration
# Restrict CORS to specific domains app.add_middleware( CORSMiddleware, allow_origins=["https://yourdomain.com"], allow_credentials=True, allow_methods=["GET", "POST"], allow_headers=["*"] ) -
Rate Limiting
# Example: Add rate limiting from slowapi import Limiter limiter = Limiter(key_func=get_remote_address) @app.get("/videos/") @limiter.limit("10/minute") async def list_videos(): pass -
Network Security
- Use HTTPS/TLS for encrypted communication
- Implement firewall rules to restrict access
- Consider VPN access for remote users
- Use reverse proxy (nginx) for additional security
Access Control Summary
┌─────────────────────────────────────────────────────────────┐
│ Current Access Model │
├─────────────────────────────────────────────────────────────┤
│ Authentication: ❌ None │
│ Authorization: ❌ None │
│ CORS: ✅ Enabled (all origins) │
│ Rate Limiting: ❌ None │
│ HTTPS: ⚠️ Depends on deployment │
│ Network Security: ⚠️ Firewall/VPN recommended │
└─────────────────────────────────────────────────────────────┘
📊 Performance
- Caching: Intelligent byte-range caching reduces disk I/O
- Adaptive Chunking: Optimal chunk sizes based on file size
- Range Requests: Only download needed portions
- Format Conversion: Automatic conversion to web-compatible formats
🛠️ Service Management
Restart Service
sudo systemctl restart usda-vision-camera
Check Status
# Check video module status
curl http://localhost:8000/system/video-module
# Check available videos
curl http://localhost:8000/videos/
Logs
sudo journalctl -u usda-vision-camera -f
🧪 Testing
Run the video module tests:
cd /home/alireza/USDA-vision-cameras
PYTHONPATH=/home/alireza/USDA-vision-cameras python tests/test_video_module.py
🔍 Troubleshooting
Video Not Playing
- Check if file exists:
GET /videos/{file_id}curl http://localhost:8000/videos/camera1_recording_20250804_143022.avi - Verify streaming info:
GET /videos/{file_id}/infocurl http://localhost:8000/videos/camera1_recording_20250804_143022.avi/info - Test direct stream:
GET /videos/{file_id}/streamcurl -I http://localhost:8000/videos/camera1_recording_20250804_143022.avi/stream - Validate video file:
POST /videos/{file_id}/validatecurl -X POST http://localhost:8000/videos/camera1_recording_20250804_143022.avi/validate
Performance Issues
- Check cache status: Clean up cache if needed
curl -X POST "http://localhost:8000/admin/videos/cache/cleanup?max_size_mb=100" - Monitor system resources: Check CPU, memory, and disk usage
- Adjust cache size: Modify configuration parameters
- Invalidate specific cache: For updated files
curl -X POST http://localhost:8000/videos/{file_id}/cache/invalidate
Format Issues
- AVI files: Automatically converted to MP4 for web compatibility
- Conversion requires FFmpeg: Optional dependency with graceful fallback
- Supported formats: AVI (with conversion), MP4 (native), WebM (native)
Common HTTP Status Codes
- 200: Success - Video streamed successfully
- 206: Partial Content - Range request successful
- 404: Not Found - Video file doesn't exist or isn't streamable
- 416: Range Not Satisfiable - Invalid range request
- 500: Internal Server Error - Failed to read video data or generate thumbnail
Browser Compatibility
- Chrome/Chromium: Full support for MP4 and range requests
- Firefox: Full support for MP4 and range requests
- Safari: Full support for MP4 and range requests
- Edge: Full support for MP4 and range requests
- Mobile browsers: Generally good support for MP4 streaming
Error Scenarios and Solutions
Video File Issues
# Problem: Video not found (404)
curl http://localhost:8000/videos/nonexistent_video.mp4
# Response: {"detail": "Video nonexistent_video.mp4 not found"}
# Solution: Verify file_id exists using list endpoint
# Problem: Video not streamable
curl http://localhost:8000/videos/corrupted_video.avi/stream
# Response: {"detail": "Video corrupted_video.avi not found or not streamable"}
# Solution: Use validation endpoint to check file integrity
Range Request Issues
# Problem: Invalid range request (416)
curl -H "Range: bytes=999999999-" http://localhost:8000/videos/small_video.mp4/stream
# Response: {"detail": "Invalid range request: Range exceeds file size"}
# Solution: Check file size first using /info endpoint
# Problem: Malformed range header
curl -H "Range: invalid-range" http://localhost:8000/videos/video.mp4/stream
# Response: {"detail": "Invalid range request: Malformed range header"}
# Solution: Use proper range format: "bytes=start-end"
Thumbnail Generation Issues
# Problem: Thumbnail generation failed (404)
curl http://localhost:8000/videos/audio_only.mp4/thumbnail
# Response: {"detail": "Could not generate thumbnail for audio_only.mp4"}
# Solution: Verify video has visual content and is not audio-only
# Problem: Invalid timestamp
curl "http://localhost:8000/videos/short_video.mp4/thumbnail?timestamp=999"
# Response: Returns thumbnail from last available frame
# Solution: Check video duration first using metadata
System Resource Issues
# Problem: Cache full or system overloaded (500)
curl http://localhost:8000/videos/large_video.mp4/stream
# Response: {"detail": "Failed to read video data"}
# Solution: Clean cache or wait for system resources
curl -X POST "http://localhost:8000/admin/videos/cache/cleanup?max_size_mb=50"
Debugging Workflow
# Step 1: Check system health
curl http://localhost:8000/health
# Step 2: Verify video exists and get info
curl http://localhost:8000/videos/your_video_id
# Step 3: Check streaming capabilities
curl http://localhost:8000/videos/your_video_id/info
# Step 4: Validate video file
curl -X POST http://localhost:8000/videos/your_video_id/validate
# Step 5: Test basic streaming
curl -I http://localhost:8000/videos/your_video_id/stream
# Step 6: Test range request
curl -I -H "Range: bytes=0-1023" http://localhost:8000/videos/your_video_id/stream
Performance Monitoring
# Monitor cache usage
curl -X POST "http://localhost:8000/admin/videos/cache/cleanup?max_size_mb=100"
# Check system resources
curl http://localhost:8000/system/status
# Monitor video module status
curl http://localhost:8000/videos/ | jq '.total_count'
🎯 Next Steps
- Restart the usda-vision-camera service to enable video streaming
- Test the endpoints using curl or your browser
- Integrate with your React app using the provided examples
- Monitor performance and adjust caching as needed
The video streaming system is now ready for production use! 🚀