fix(camera): share one ffmpeg connection for /api/camera/stream #91

Closed
Pavulon87 wants to merge 1 commits from Pavulon87/KX-Bridge-Release:fix/camera-fix into nightly
Contributor

Description

This PR adds support for multiple concurrent MJPEG stream consumers by introducing a shared MJPEG streaming pipeline.

Previously, every request to /api/camera/stream spawned a separate ffmpeg process and opened a new connection to the printer. Since the printer only supports a very limited number of concurrent camera clients, this could lead to HTTP 429 errors and intermittent "stream unavailable" issues when multiple clients (dashboard, Moonraker-compatible clients, plugins, etc.) accessed the camera simultaneously.

The new implementation introduces a dedicated ffmpeg process that generates a shared MJPEG stream, which is then distributed to all connected /api/camera/stream subscribers. This approach provides:

  • A single printer connection per output type instead of one per HTTP client.
  • Support for multiple simultaneous MJPEG consumers.
  • Continued support for multiple simultaneous H.264 consumers.
  • Reduced ffmpeg process creation and improved resource usage.
  • More reliable camera streaming with fewer connection conflicts.

This implementation was developed with assistance from Claude AI and has been tested locally for several days. It appears to work well in my testing, but I'd appreciate additional review and testing since there may still be edge cases that I haven't encountered.

Closes #90

Type

  • Bug fix
  • Feature
  • Documentation
  • Refactoring

Tested with

  • OrcaSlicer Version: 2.5.0-dev-kx1
  • Printer: Kobra X
  • Moonraker/Klipper Version:

Checklist

  • Tests added/updated
  • CHANGELOG.md updated
  • No debug code included
## Description This PR adds support for multiple concurrent MJPEG stream consumers by introducing a shared MJPEG streaming pipeline. Previously, every request to `/api/camera/stream` spawned a separate ffmpeg process and opened a new connection to the printer. Since the printer only supports a very limited number of concurrent camera clients, this could lead to HTTP 429 errors and intermittent "stream unavailable" issues when multiple clients (dashboard, Moonraker-compatible clients, plugins, etc.) accessed the camera simultaneously. The new implementation introduces a dedicated ffmpeg process that generates a shared MJPEG stream, which is then distributed to all connected `/api/camera/stream` subscribers. This approach provides: - A single printer connection per output type instead of one per HTTP client. - Support for multiple simultaneous MJPEG consumers. - Continued support for multiple simultaneous H.264 consumers. - Reduced ffmpeg process creation and improved resource usage. - More reliable camera streaming with fewer connection conflicts. This implementation was developed with assistance from Claude AI and has been tested locally for several days. It appears to work well in my testing, but I'd appreciate additional review and testing since there may still be edge cases that I haven't encountered. ## Related Issue Closes #90 ## Type - [ ] Bug fix - [X] Feature - [ ] Documentation - [ ] Refactoring ## Tested with - OrcaSlicer Version: 2.5.0-dev-kx1 - Printer: Kobra X - Moonraker/Klipper Version: ## Checklist - [ ] Tests added/updated - [ ] CHANGELOG.md updated - [X] No debug code included
Pavulon87 added 1 commit 2026-07-16 13:03:46 +00:00
fix(camera): share one ffmpeg connection for /api/camera/stream
Some checks failed
PR Check / lint-and-test (pull_request) Has been cancelled
ba59b47c27
Owner

Thanks for this, @Pavulon87 — the shared-fanout approach for /api/camera/stream is exactly right and matches the existing handle_camera_h264 pattern well. Couldn't merge the PR branch directly though: the diff contains unresolved git conflict markers (<<<<<<< HEAD / ======= / >>>>>>> c185c76) inside handle_camera_stream, most likely from a local rebase against nightly that never got resolved before pushing — merging as-is would have left literal conflict markers in the Python source.

I pulled the fix in manually instead: new mjpeg_subscribers fanout channel in CameraCache (same shape as the h264 channel), plus the two related fixes you found (the ensure_running() race where two callers could both see "no process yet" and spawn duplicate ffmpeg processes, and reset() not waking a task stuck in its backoff sleep). Verified locally that two concurrent /api/camera/stream clients now share a single ffmpeg process instead of one each.

Closing this in favor of the manually-integrated version, but the credit for the fix and the race-condition finds is yours. Thanks for the thorough writeup and testing notes!

Thanks for this, @Pavulon87 — the shared-fanout approach for /api/camera/stream is exactly right and matches the existing handle_camera_h264 pattern well. Couldn't merge the PR branch directly though: the diff contains unresolved git conflict markers (`<<<<<<< HEAD` / `=======` / `>>>>>>> c185c76`) inside handle_camera_stream, most likely from a local rebase against nightly that never got resolved before pushing — merging as-is would have left literal conflict markers in the Python source. I pulled the fix in manually instead: new `mjpeg_subscribers` fanout channel in `CameraCache` (same shape as the h264 channel), plus the two related fixes you found (the `ensure_running()` race where two callers could both see "no process yet" and spawn duplicate ffmpeg processes, and `reset()` not waking a task stuck in its backoff sleep). Verified locally that two concurrent `/api/camera/stream` clients now share a single ffmpeg process instead of one each. Closing this in favor of the manually-integrated version, but the credit for the fix and the race-condition finds is yours. Thanks for the thorough writeup and testing notes!
Author
Contributor

Sorry about that. I made some changes to the fork without first branching it, and I must have missed it during the cherry-pick because there were changes to the original.
Thanks for the good work.

I also made two more branches with ams matching. It automatically matches a slot with an identical/similar color, but more testing with color prints is needed.
Zrzut ekranu 2026-07-17 205959.png

Sorry about that. I made some changes to the fork without first branching it, and I must have missed it during the cherry-pick because there were changes to the original. Thanks for the good work. I also made two more branches with ams matching. It automatically matches a slot with an identical/similar color, but more testing with color prints is needed. <img width="337" alt="Zrzut ekranu 2026-07-17 205959.png" src="attachments/9cb3e14a-4ba5-4386-a1df-e44671ac3f3b">
Pavulon87 closed this pull request 2026-07-17 19:04:14 +00:00
Some checks failed
PR Check / lint-and-test (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: viewit/KX-Bridge-Release#91
No description provided.