Skip to content

feat(studio): a Linux virtual camera for each published scene - #82

Merged
kixelated merged 3 commits into
mainfrom
quest/m0/vcam/linux
Nov 7, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m0/vcam/linux

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Finishes quest/m0/vcam/linux.md.

Problem

The Linux virtual camera (moq-dev/studio#9) shows only the program. Since moq-dev/studio#71 a published scene is a second picture at its own size (a vertical cut, say), and a video-call app could not pick it.

Approach

  • vcam::Camera takes the scene it shows (None for the program). Its PipeWire node is moq-studio-<scene>, described "MoQ Studio: "; the program's stays moq-studio / "MoQ Studio". Its capture id comes from the node, so the Devices panel filter follows.
  • State holds the cameras in one map keyed by Option<String> (program under None). Engine::set_camera(scene, on) refuses a scene that is not published.
  • The program clock adds a scene camera's wanted() (an app reads it) to the scene's Compositor::set_outputs draw flag, so a scene nobody else reads is drawn only while an app reads its camera. After the render it offers the scene's Compositor::output texture, or None when not drawn. The reopen check uses the scene's canvas from the collection, not the drawn texture, so a resize while nobody reads still reopens the node before an app links at the old size.
  • A scene camera whose scene is no longer published under its name (unpublished, renamed, removed) is taken out on the next tick and closed after the state lock is released.
  • v4l2loopback: a process-wide set of held device paths, so two cameras never write the same device. The kernel lets a second writer set the same format, which would interleave two pictures.
  • The Output panel shows each published scene's toggle and status under the scene's own section.

Impact

  • Output panel: each published scene gets a Virtual camera toggle of its own, which PipeWire apps list as "MoQ Studio: " (pipewire:moq-studio-<scene>). Not saved in the scene file, like the program's.
  • Unpublishing or renaming a scene turns its camera off.
  • The v4l2loopback fallback gives each camera its own device. The README shows how to load more than one.
  • The Devices panel leaves out every camera the studio has on.
  • Public API: none outside moq-studio. No wire or scene-file change.

Alternatives

  • Following a rename by reopening under the new name. An app loses the camera either way, because the node name changes. Dropping it matches how the scene's broadcast, also named after the scene, ends on a rename.
  • Reopening on the drawn output's canvas, as the program's camera does. An unread scene is not drawn, so a resize while nobody reads would only reopen once an app had already linked at the old size.
  • Two fields (program camera, scene map). One map keeps a single path for status, toggling, and the Devices filter.

Choices made without asking

  • The scene camera's node name embeds the scene name verbatim (moq-studio-<scene>). It is unique because scene names are unique, and moq-video resolves pipewire:<name> by exact match.
  • A rename drops the camera instead of following it (see Alternatives).

Validation

On nixos (RTX 3070 Ti):

  • just ci: pass (278 tests).
  • cargo clippy -p moq-studio --features pipewire --all-targets -D warnings: pass.
  • engine::tests::the_virtual_camera_reaches_a_pipewire_app (extended), in a private PipeWire session (sh/pipewire-session.sh), on the GPU and on lavapipe as just render-sw runs it: pass. It now covers the program camera and the tall scene camera at once, each showing its own color at its own size. It also checks the scene camera reopening when the scene is resized, turning off once the scene is unpublished, and refusing to turn on for an unpublished scene.
  • vcam::v4l2::tests::a_v4l2_app_reads_the_program (extended so a second camera never shares the device): not run, because nixos has no v4l2loopback module. The nightly's v4l2loopback job runs it.

Follow-ups

None.

Review

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

Quest-Claim: 96f5881cb29fb521
@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

Each published scene gets a Virtual camera toggle of its own in the Output
panel, beside the program's. Its PipeWire node is named moq-studio-<scene>
("MoQ Studio: <scene>"), or it takes a v4l2loopback device no other camera
holds. The camera asks for its scene to be drawn while an app reads it,
reopens when the scene's size changes, and turns off once the scene is no
longer published under its name. The Devices panel leaves out every camera
the studio publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Linux virtual camera for each published scene feat(studio): a Linux virtual camera for each published scene Nov 7, 2026
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 08:20
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 20 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6e626a5b-8b8e-4d9f-8350-7e4c168c759d
Commits

Reviewing files that changed from the base of the PR and between 526f4d0 and c797ae6.

Files selected for processing: 8

  • README.md
  • quest/m0/vcam/README.md
  • quest/m0/vcam/linux.md
  • rs/moq-studio/src/engine.rs
  • rs/moq-studio/src/ui.rs
  • rs/moq-studio/src/vcam.rs
  • rs/moq-studio/src/vcam/pipewire.rs
  • rs/moq-studio/src/vcam/v4l2.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Share

@kixelated kixelated left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: bb2d80d
Base: 167cdca

No actionable findings.

Direction: treating each scene camera as another compositor consumer preserves on-demand drawing and the scene's own canvas. Checking canvas changes independently of an available texture lets an unread camera reopen at the new size. Removed scene cameras are extracted under the state lock and closed outside it; per-camera PipeWire IDs feed the Devices exclusion list. The process-wide v4l2 device reservation is held across selection/configuration, preventing simultaneous studio cameras from selecting the same path.

The expanded PipeWire test checks simultaneous distinct pictures and sizes, resize while unread, unpublish cleanup and refusal of unpublished scenes. The v4l2 test checks non-sharing and subsequent release. README and quest closure match the feature.

Verification: GitHub-only static review of all eight changed files, the surrounding engine draw/lifecycle and UI paths, and camera worker/readback/backend ownership. No builds, tests, quest validation, PipeWire session, v4l2loopback module, GPU runs or performance measurements executed. The reported validation was not independently reproduced, and the author explicitly did not run the extended v4l2 test locally. COMMENT only, not approval.

(Written by review (OpenAI))

@kixelated
kixelated enabled auto-merge (squash) November 7, 2026 08:41
@kixelated

Copy link
Copy Markdown
Contributor Author

Pick (/quest-complete, maintainer away under /afk, recommended option): ✅ /quest-merge on head c797ae6, a conflict-free merge of main into the reviewed head bb2d80d. Codex found nothing, and the PR leaves no decisions open.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 107f736 into main Nov 7, 2026
3 checks passed
@kixelated
kixelated deleted the quest/m0/vcam/linux branch November 7, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant