feat(webcam): Implement threaded streaming for high FPS#1
Conversation
This commit completely overhauls the webcam functionality to provide a much smoother, higher frame-rate live stream. Previously, each request for `cam.jpg` would open the camera device, capture a single frame, and then close the device. This process was extremely slow, resulting in a frame rate of less than 1 FPS and causing unnecessary wear on the flash storage. The new implementation introduces a background thread for continuous capture: - A dedicated pthread is spawned on the first request to the webcam page. - This thread keeps the camera device open and continuously captures frames into `/tmp/cam.jpg` (a RAM disk), significantly reducing latency and eliminating flash writes. - The thread automatically terminates and releases the camera if no new web requests are received within a 2-second timeout. - The frontend refresh interval has been reduced from 2000ms to 125ms, enabling a frame rate of ~8 FPS. To support this new model, the webcam C API was refactored: - `v_capture_image` is replaced by `v_open_camera`, `v_capture_frame_to_file`, and `v_close_camera` for more granular control over the device state. Additionally, a minor fix was made to the config parser to include the file path in the "cannot read" error message.
WalkthroughRefactors webcam capture from a synchronous single-call to a lifecycle API (open/capture/close) and adds a background thread that periodically captures frames to Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant HTTP as HTTP Handler
participant Thread as Capture Thread
participant Camera as Webcam (v_open/v_capture/v_close)
participant FS as Filesystem (/tmp)
rect rgb(230,245,255)
Note over HTTP,Thread: Request-driven background capture lifecycle
end
Client->>HTTP: GET /mnt/.../webcam/cam.jpg
activate HTTP
HTTP->>HTTP: lock mutex
HTTP->>HTTP: check thread running & update timestamp
alt thread not running
HTTP->>Thread: pthread_create(webcam_capture_thread)
end
HTTP->>HTTP: unlock mutex
HTTP->>FS: return `/tmp/cam.jpg` (or fallback image)
deactivate HTTP
activate Thread
Thread->>Camera: v_open_camera()
Camera-->>Thread: opened (v_is_open = true)
loop capture loop (until inactivity)
Thread->>Camera: v_capture_frame_to_file("/tmp/cam.tmp")
Camera->>FS: write `/tmp/cam.tmp`
Thread->>FS: rename `/tmp/cam.tmp` -> `/tmp/cam.jpg`
Thread->>Thread: check last_request_time (exit if >2s)
end
Thread->>Camera: v_close_camera()
Camera-->>Thread: closed (v_is_open = false)
deactivate Thread
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Potential focus areas:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/config-parser.c(1 hunks)src/httpd.h(1 hunks)src/request.c(3 hunks)src/webcam.c(3 hunks)webserver/opt/webfs/webcam/index.html(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/httpd.h (1)
src/webcam.c (3)
v_open_camera(735-793)v_capture_frame_to_file(795-804)v_close_camera(806-818)
src/request.c (1)
src/webcam.c (3)
v_open_camera(735-793)v_capture_frame_to_file(795-804)v_close_camera(806-818)
🔇 Additional comments (12)
src/config-parser.c (1)
24-24: LGTM! Enhanced error reporting.Including the file path in the error message improves diagnostics when configuration file access fails.
webserver/opt/webfs/webcam/index.html (2)
17-17: LGTM! Increased refresh rate for smoother streaming.The 125ms interval provides approximately 8 FPS, aligning with the PR objectives to improve the live stream experience.
45-58: LGTM! Responsive image styling.The changes make the webcam feed responsive with
width: 100%, improving display across different screen sizes.src/request.c (3)
17-17: LGTM! Threading support added.pthread.h is correctly included to support the new threaded webcam capture implementation.
1213-1216: LGTM! Well-structured thread state management.The global state variables are appropriately guarded by the mutex to coordinate the webcam thread lifecycle and timeout tracking.
1339-1363: LGTM! Well-designed on-demand thread lifecycle.The request handler properly:
- Uses mutex protection for shared state access
- Updates the last request timestamp to keep the thread alive
- Starts the thread only when needed
- Provides a placeholder image while the background thread initializes
The atomic rename from
/tmp/cam.tmpto/tmp/cam.jpg(line 1245) ensures clients never read partially-written images.src/httpd.h (1)
187-189: LGTM! Clean lifecycle-based camera API.The new three-function API (open/capture/close) provides better control over camera lifecycle compared to the previous single-function approach, enabling the persistent camera connection needed for high-FPS streaming.
src/webcam.c (5)
52-52: LGTM! State tracking for camera lifecycle.The
v_is_openflag properly tracks camera state to prevent double-open/close operations.
301-302: LGTM! Reduced timeout improves responsiveness.Changing from 2-second to 0.5-second timeout reduces the delay when capturing frames, contributing to the improved frame rate. The 0.5s timeout is still generous enough to handle transient delays.
735-793: LGTM! Robust camera initialization.The
v_open_cameraimplementation:
- Prevents double-open via the
v_is_openguard- Intelligently tries the last working device first, falling back to scanning /dev/video0-3
- Tests both format modes (0 and 1)
- Properly cleans up resources on failure via
mem_manager_end()- Tracks successful configuration for future opens
This design minimizes initialization time for subsequent opens while maintaining robustness.
795-804: LGTM! Simple and correct frame capture.The function properly validates that the camera is open before attempting capture and provides good debug logging.
806-818: LGTM! Proper cleanup sequence.The
v_close_camerafunction follows the correct teardown order: stop capturing → uninitialize device → close device → release memory → clear state.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/request.c (1)
1232-1260: Race condition: camera access while thread is exiting.The thread sets
webcam_thread_running = 0at line 1236, then releases the mutex and breaks from the loop, but doesn't close the camera until line 1258. During this window, the request handler could see the flag as 0, create a new thread, which would then try to access the camera while the old thread is still closing it.The camera functions in
webcam.cuse thev_is_openflag without mutex protection, so concurrent access from two threads could lead to race conditions, resulting in camera errors or undefined behavior.Restructure the cleanup to close the camera before marking the thread as not running:
while (1) { + int should_exit = 0; pthread_mutex_lock(&webcam_mutex); time_t now = time(NULL); if (now - last_webcam_request_time > 2) { if (debug) fprintf(stderr, "+++ webcam thread: timeout, exiting\n"); - webcam_thread_running = 0; - pthread_mutex_unlock(&webcam_mutex); - break; + should_exit = 1; } pthread_mutex_unlock(&webcam_mutex); + if (should_exit) { + break; + } + if (debug) fprintf(stderr, "+++ webcam thread: capturing frame\n"); // capture one frame to the file "/tmp/cam.jpg" int result = v_capture_frame_to_file("/tmp/cam.tmp"); if (result == 0) { if (debug) fprintf(stderr, "+++ webcam thread: capture successful\n"); rename("/tmp/cam.tmp", "/tmp/cam.jpg"); } else { if (debug) fprintf(stderr, "--- webcam thread: capture failed, result: %d\n", result); // errors, use the default image custom_copy_file("/mnt/UDISK/webfs/webcam/default.jpg", "/tmp/cam.jpg", "wb", NULL); } usleep(75000); // 75ms } + // Close camera before marking thread as not running if (debug) fprintf(stderr, "+++ webcam thread: closing camera\n"); v_close_camera(); - if (debug) fprintf(stderr, "+++ webcam thread: exited\n"); + if (debug) fprintf(stderr, "+++ webcam thread: camera closed\n"); + + pthread_mutex_lock(&webcam_mutex); + webcam_thread_running = 0; + pthread_mutex_unlock(&webcam_mutex); + + if (debug) fprintf(stderr, "+++ webcam thread: exited\n"); return NULL; }
🧹 Nitpick comments (1)
src/request.c (1)
1244-1252: Add error checking for rename operation.The
rename()call at line 1247 doesn't check for errors. While the impact is minor (the old image would continue to be served), adding error checking would improve observability and help diagnose issues.int result = v_capture_frame_to_file("/tmp/cam.tmp"); if (result == 0) { if (debug) fprintf(stderr, "+++ webcam thread: capture successful\n"); - rename("/tmp/cam.tmp", "/tmp/cam.jpg"); + if (rename("/tmp/cam.tmp", "/tmp/cam.jpg") != 0) { + if (debug) fprintf(stderr, "--- webcam thread: rename failed: %s\n", strerror(errno)); + // Fall back to copying the file + custom_copy_file("/tmp/cam.tmp", "/tmp/cam.jpg", "wb", NULL); + unlink("/tmp/cam.tmp"); + } } else {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/request.c(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/request.c (1)
src/webcam.c (3)
v_open_camera(735-793)v_capture_frame_to_file(795-804)v_close_camera(806-818)
🔇 Additional comments (4)
src/request.c (4)
17-17: LGTM!Adding pthread.h is necessary for the threading functionality introduced in this PR.
1213-1216: LGTM!The thread state variables are well-structured with appropriate types and initialization. The static mutex initializer is correct for file-scope variables.
1219-1219: Good fix for the thread resource leak!The addition of
pthread_detach(pthread_self())at the beginning of the thread function correctly addresses the resource leak issue flagged in the previous review. This ensures thread resources are automatically released when the thread terminates.
1340-1365: Request handler logic is well-structured.The mutex-protected updates to
last_webcam_request_timeandwebcam_thread_runningare correct. The placeholder image creation (lines 1358-1360) prevents 404 errors on first request, and redirecting to/tmp/cam.jpgachieves the goal of serving from RAM instead of flash.
|
Upstream PR: AGG2017#2 |
This commit completely overhauls the webcam functionality to provide a much smoother, higher frame-rate live stream.
Previously, each request for
cam.jpgwould open the camera device, capture a single frame, and then close the device. This process was extremely slow, resulting in a frame rate of less than 1 FPS and causing unnecessary wear on the flash storage.The new implementation introduces a background thread for continuous capture:
/tmp/cam.jpg(a RAM disk), significantly reducing latency and eliminating flash writes.To support this new model, the webcam C API was refactored:
v_capture_imageis replaced byv_open_camera,v_capture_frame_to_file, andv_close_camerafor more granular control over the device state.Additionally, a minor fix was made to the config parser to include the file path in the "cannot read" error message.
Summary by CodeRabbit
Bug Fixes
New Features