feat(streaming): Add live streaming functionality for USDA Vision Camera system
- Introduced non-blocking live preview streaming that operates independently from recording. - Implemented REST API endpoints for starting and stopping streams, and retrieving live streams. - Developed a web interface (`camera_preview.html`) for users to control and view camera streams. - Created TypeScript definitions for API integration in React projects. - Added comprehensive testing script (`test_streaming.py`) to validate API endpoints and concurrent operations. - Updated database migration to fix visibility of experiment repetitions for all authenticated users.
This commit is contained in:
300
streaming/streaming-api.http
Normal file
300
streaming/streaming-api.http
Normal file
@@ -0,0 +1,300 @@
|
||||
### USDA Vision Camera Streaming API
|
||||
### Base URL: http://localhost:8000
|
||||
###
|
||||
### This file contains streaming-specific API endpoints for live camera preview
|
||||
### Use with VS Code REST Client extension or similar tools.
|
||||
|
||||
@baseUrl = http://localhost:8000
|
||||
|
||||
### =============================================================================
|
||||
### STREAMING ENDPOINTS (NEW FUNCTIONALITY)
|
||||
### =============================================================================
|
||||
|
||||
### Start camera streaming for live preview
|
||||
### This creates a separate camera connection that doesn't interfere with recording
|
||||
POST {{baseUrl}}/cameras/camera1/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### Expected Response:
|
||||
# {
|
||||
# "success": true,
|
||||
# "message": "Started streaming for camera camera1"
|
||||
# }
|
||||
|
||||
###
|
||||
|
||||
### Stop camera streaming
|
||||
POST {{baseUrl}}/cameras/camera1/stop-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### Expected Response:
|
||||
# {
|
||||
# "success": true,
|
||||
# "message": "Stopped streaming for camera camera1"
|
||||
# }
|
||||
|
||||
###
|
||||
|
||||
### Get live MJPEG stream (open in browser or use as img src)
|
||||
### This endpoint returns a continuous MJPEG stream
|
||||
### Content-Type: multipart/x-mixed-replace; boundary=frame
|
||||
GET {{baseUrl}}/cameras/camera1/stream
|
||||
|
||||
### Usage in HTML:
|
||||
# <img src="http://localhost:8000/cameras/camera1/stream" alt="Live Stream" />
|
||||
|
||||
### Usage in React:
|
||||
# <img src={`${apiBaseUrl}/cameras/${cameraName}/stream?t=${Date.now()}`} />
|
||||
|
||||
###
|
||||
|
||||
### Start streaming for camera2
|
||||
POST {{baseUrl}}/cameras/camera2/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
### Get live stream for camera2
|
||||
GET {{baseUrl}}/cameras/camera2/stream
|
||||
|
||||
###
|
||||
|
||||
### Stop streaming for camera2
|
||||
POST {{baseUrl}}/cameras/camera2/stop-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### =============================================================================
|
||||
### CONCURRENT OPERATIONS TESTING
|
||||
### =============================================================================
|
||||
|
||||
### Test Scenario: Streaming + Recording Simultaneously
|
||||
### This demonstrates that streaming doesn't block recording
|
||||
|
||||
### Step 1: Start streaming first
|
||||
POST {{baseUrl}}/cameras/camera1/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
### Step 2: Start recording (while streaming continues)
|
||||
POST {{baseUrl}}/cameras/camera1/start-recording
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"filename": "concurrent_test.avi"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
### Step 3: Check both are running
|
||||
GET {{baseUrl}}/cameras/camera1
|
||||
|
||||
### Expected Response shows both recording and streaming active:
|
||||
# {
|
||||
# "camera1": {
|
||||
# "name": "camera1",
|
||||
# "status": "connected",
|
||||
# "is_recording": true,
|
||||
# "current_recording_file": "concurrent_test.avi",
|
||||
# "recording_start_time": "2025-01-28T10:30:00.000Z"
|
||||
# }
|
||||
# }
|
||||
|
||||
###
|
||||
|
||||
### Step 4: Stop recording (streaming continues)
|
||||
POST {{baseUrl}}/cameras/camera1/stop-recording
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
### Step 5: Verify streaming still works
|
||||
GET {{baseUrl}}/cameras/camera1/stream
|
||||
|
||||
###
|
||||
|
||||
### Step 6: Stop streaming
|
||||
POST {{baseUrl}}/cameras/camera1/stop-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### =============================================================================
|
||||
### MULTIPLE CAMERA STREAMING
|
||||
### =============================================================================
|
||||
|
||||
### Start streaming on multiple cameras simultaneously
|
||||
POST {{baseUrl}}/cameras/camera1/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
POST {{baseUrl}}/cameras/camera2/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
### Check status of all cameras
|
||||
GET {{baseUrl}}/cameras
|
||||
|
||||
###
|
||||
|
||||
### Access multiple streams (open in separate browser tabs)
|
||||
GET {{baseUrl}}/cameras/camera1/stream
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/cameras/camera2/stream
|
||||
|
||||
###
|
||||
|
||||
### Stop all streaming
|
||||
POST {{baseUrl}}/cameras/camera1/stop-stream
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
POST {{baseUrl}}/cameras/camera2/stop-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### =============================================================================
|
||||
### ERROR TESTING
|
||||
### =============================================================================
|
||||
|
||||
### Test with invalid camera name
|
||||
POST {{baseUrl}}/cameras/invalid_camera/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### Expected Response:
|
||||
# {
|
||||
# "detail": "Camera streamer not found: invalid_camera"
|
||||
# }
|
||||
|
||||
###
|
||||
|
||||
### Test stream endpoint without starting stream first
|
||||
GET {{baseUrl}}/cameras/camera1/stream
|
||||
|
||||
### Expected: May return error or empty stream depending on camera state
|
||||
|
||||
###
|
||||
|
||||
### Test starting stream when camera is in error state
|
||||
POST {{baseUrl}}/cameras/camera1/start-stream
|
||||
Content-Type: application/json
|
||||
|
||||
### If camera has issues, expected response:
|
||||
# {
|
||||
# "success": false,
|
||||
# "message": "Failed to start streaming for camera camera1"
|
||||
# }
|
||||
|
||||
### =============================================================================
|
||||
### INTEGRATION EXAMPLES FOR AI ASSISTANTS
|
||||
### =============================================================================
|
||||
|
||||
### React Component Integration:
|
||||
# const CameraStream = ({ cameraName }) => {
|
||||
# const [isStreaming, setIsStreaming] = useState(false);
|
||||
#
|
||||
# const startStream = async () => {
|
||||
# const response = await fetch(`${baseUrl}/cameras/${cameraName}/start-stream`, {
|
||||
# method: 'POST'
|
||||
# });
|
||||
# if (response.ok) {
|
||||
# setIsStreaming(true);
|
||||
# }
|
||||
# };
|
||||
#
|
||||
# return (
|
||||
# <div>
|
||||
# <button onClick={startStream}>Start Stream</button>
|
||||
# {isStreaming && (
|
||||
# <img src={`${baseUrl}/cameras/${cameraName}/stream?t=${Date.now()}`} />
|
||||
# )}
|
||||
# </div>
|
||||
# );
|
||||
# };
|
||||
|
||||
### JavaScript Fetch Example:
|
||||
# const streamAPI = {
|
||||
# async startStream(cameraName) {
|
||||
# const response = await fetch(`${baseUrl}/cameras/${cameraName}/start-stream`, {
|
||||
# method: 'POST',
|
||||
# headers: { 'Content-Type': 'application/json' }
|
||||
# });
|
||||
# return response.json();
|
||||
# },
|
||||
#
|
||||
# async stopStream(cameraName) {
|
||||
# const response = await fetch(`${baseUrl}/cameras/${cameraName}/stop-stream`, {
|
||||
# method: 'POST',
|
||||
# headers: { 'Content-Type': 'application/json' }
|
||||
# });
|
||||
# return response.json();
|
||||
# },
|
||||
#
|
||||
# getStreamUrl(cameraName) {
|
||||
# return `${baseUrl}/cameras/${cameraName}/stream?t=${Date.now()}`;
|
||||
# }
|
||||
# };
|
||||
|
||||
### Vue.js Integration:
|
||||
# <template>
|
||||
# <div>
|
||||
# <button @click="startStream">Start Stream</button>
|
||||
# <img v-if="isStreaming" :src="streamUrl" />
|
||||
# </div>
|
||||
# </template>
|
||||
#
|
||||
# <script>
|
||||
# export default {
|
||||
# data() {
|
||||
# return {
|
||||
# isStreaming: false,
|
||||
# cameraName: 'camera1'
|
||||
# };
|
||||
# },
|
||||
# computed: {
|
||||
# streamUrl() {
|
||||
# return `${this.baseUrl}/cameras/${this.cameraName}/stream?t=${Date.now()}`;
|
||||
# }
|
||||
# },
|
||||
# methods: {
|
||||
# async startStream() {
|
||||
# const response = await fetch(`${this.baseUrl}/cameras/${this.cameraName}/start-stream`, {
|
||||
# method: 'POST'
|
||||
# });
|
||||
# if (response.ok) {
|
||||
# this.isStreaming = true;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# };
|
||||
# </script>
|
||||
|
||||
### =============================================================================
|
||||
### TROUBLESHOOTING
|
||||
### =============================================================================
|
||||
|
||||
### If streams don't start:
|
||||
# 1. Check camera status: GET /cameras
|
||||
# 2. Verify system health: GET /health
|
||||
# 3. Test camera connection: POST /cameras/{name}/test-connection
|
||||
# 4. Check if camera is already recording (shouldn't matter, but good to know)
|
||||
|
||||
### If stream image doesn't load:
|
||||
# 1. Verify stream was started: POST /cameras/{name}/start-stream
|
||||
# 2. Check browser console for CORS errors
|
||||
# 3. Try accessing stream URL directly in browser
|
||||
# 4. Add timestamp to prevent caching: ?t=${Date.now()}
|
||||
|
||||
### If concurrent operations fail:
|
||||
# 1. This should work - streaming and recording use separate connections
|
||||
# 2. Check system logs for resource conflicts
|
||||
# 3. Verify sufficient system resources (CPU/Memory)
|
||||
# 4. Test with one camera first, then multiple
|
||||
|
||||
### Performance Notes:
|
||||
# - Streaming uses ~10 FPS by default (configurable)
|
||||
# - JPEG quality set to 70% (configurable)
|
||||
# - Each stream uses additional CPU/memory
|
||||
# - Multiple concurrent streams may impact performance
|
||||
Reference in New Issue
Block a user