Use your phone as a webcam over Wi-Fi — Linux, PipeWire virtual camera, no kernel module, Secure Boot stays on. AGPL-3.0.
  • Rust 64.4%
  • Python 13.6%
  • HTML 11.2%
  • C 7.1%
  • Shell 3.7%
Find a file
Sponge 67155481c6 packaging: itch.io listing copy + shipping checklist
Draft itch page copy (tagline + description, pay-what-you-want/suggested $3)
and a step-by-step shipping checklist (itch + Stripe, GitHub Sponsors, optional
GitHub push-mirror, README link fill). Code + copy done; the rest is account
setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7yrHRq9FjgypppnjtDdpj
2026-07-15 13:17:52 -07:00
docs Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
packaging packaging: itch.io listing copy + shipping checklist 2026-07-15 13:17:52 -07:00
rust AnyCam installer: name the desktop entry AnyCam.desktop to match app_id 2026-07-14 23:17:39 -07:00
static AnyCam Phase C: camera capability cross-talk + custom title bar 2026-07-14 19:34:22 -07:00
.gitignore Add Rust rewrite: self-contained server binary + desktop GUI 2026-07-14 19:00:20 -07:00
anycam.py Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
CLA.md Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
COMMERCIAL-LICENSING.md Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
LICENSE Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
pwcam.c Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00
README.md Initial commit: AGPL-3.0 licensing + repo hygiene 2026-07-13 17:44:35 -07:00

AnyCam

Stream your phone's camera (any camera-enabled device with a browser) to your Linux computer over the local network, and publish it as a PipeWire camera — no kernel module, no v4l2loopback, no module signing, no MOK, no reboot. Pure Python 3 stdlib + openssl for the web part; the virtual camera uses PipeWire + GStreamer (preinstalled on most modern desktops).

The virtual camera works in libwebrtc apps (browsers) now

AnyCam's camera node is produced by pwcam — a small raw-libpipewire source that negotiates SPA formats/buffers the way a real camera does, which is what libwebrtc requires. Verified in Firefox AND Vesktop (real pixels measured via /camtest). The old gst-pipewiresink node (which libwebrtc could never consume — full diagnosis in docs/VIRTUAL-CAMERA.md) remains only as a build-less fallback. Verified in both Firefox and Vesktop on 2026-07-05. Rewrite spec + results: docs/DEEP-REWRITE.md. Still 100% userspace: no v4l2loopback, no MOK, no kernel module, Secure Boot stays on.

Run

python3 anycam.py

Keep it running permanently

AnyCam ships as a systemd user service (user-level so it keeps access to your session's PipeWire):

# ~/.config/systemd/user/anycam.service  (see repo history for the unit)
systemctl --user enable --now anycam.service    # start now + at every login
systemctl --user status anycam.service          # check on it
journalctl --user -u anycam.service -f          # live logs
loginctl enable-linger $USER                    # optional: survive logout

Restart=always respawns it after any crash (and keeps retrying at boot until the drive holding the app directory is mounted).

Then:

  1. On your phone (same Wi-Fi): open https://<LAN-IP>:8443/ — the exact address is printed in the terminal. Tap Advanced → Proceed on the one-time certificate warning, press Start Camera, allow camera access.
  2. On the computer: open http://localhost:8080/view — live view, no warnings (localhost is a secure context).
  3. Test which apps can use the camera: open http://localhost:8080/camtest in the app you care about. It measures the actual pixels received and shows a brightness reading — green = real image, red/0 = black/no frames.

The virtual camera — what works

While running, AnyCam publishes a PipeWire camera node "AnyCam Phone Camera" (media.class=Video/Source, media.role=Camera) via the bundled pwcam helper (raw libpipewire-0.3, auto-compiled on first run). 100% userspace: no /dev/video*, no kernel module, Secure Boot stays on.

Consumer Works? Notes
Firefox (browser) verified: real pixels via /camtest (I420 negotiated)
OBS (PipeWire Video Capture source) nothing to configure
GNOME Camera / Snapshot portal-native
GStreamer pipewiresrc, portal consumers 30fps, delivers cleanly
MJPEG consumers (VLC/ffplay, see below) separate HTTP path, always works
Vesktop (Electron/Discord, Chromium libwebrtc) verified: placeholder + camtest pass
Chrome / other Chromium 🟡 same capturer; may need chrome://flags PipeWire camera + a one-time portal permission click

When no phone is streaming the node shows a "camera not connected" frame; a phone stream takes over live (letterboxed to whatever the app negotiated). Disable the node with --no-vcam.

Building pwcam happens automatically at startup when gcc, pkg-config and the dev headers are present:

sudo apt install libpipewire-0.3-dev libswscale-dev libturbojpeg0-dev

Without them AnyCam falls back to the old gst-pipewiresink node, which works for OBS/portal apps but delivers zero frames to libwebrtc (browsers, Discord) — that incompatibility is diagnosed exhaustively in docs/VIRTUAL-CAMERA.md.

Feeding apps that read MJPEG or /dev/video

The stream is also plain MJPEG on localhost — this path always works, independent of PipeWire:

Consumer How
VLC vlc http://localhost:8080/stream.mjpg
ffplay ffplay http://localhost:8080/stream.mjpg
OBS Media/Browser Source with the URL above
latest still GET http://localhost:8080/snapshot.jpg

(Browsers and Discord don't need /dev/video* anymore — the pwcam PipeWire node covers them. The historical v4l2loopback notes remain in docs/VIRTUAL-CAMERA.md §"The v4l2loopback path" for reference only.)

Why is the phone side HTTPS?

Mobile browsers disable getUserMedia on non-localhost http://. AnyCam generates a self-signed cert on first run (cert/); you tap through the warning once per phone. The desktop side stays plain HTTP on localhost (already secure).

The cert is generated to Apple's TLS rules (subjectAltName with the LAN IP, ≤825-day validity, serverAuth EKU) — without those, iPhones refuse the connection outright ("server unexpectedly dropped the connection") instead of offering the tap-through warning. If your machine's LAN IP changes, AnyCam notices the SAN mismatch at startup and regenerates the cert automatically (the phone then shows one fresh certificate warning — tap through again).

Options

python3 anycam.py --port 8443 --local-port 8080 [--no-vcam]

Endpoints

Path (localhost:8080) Purpose
/view Desktop live viewer (WebSocket)
/camtest Diagnostic: measures pixels an app actually receives
/stream.mjpg MJPEG multipart stream
/snapshot.jpg Latest single frame
wss://…:8443/ws?role=pub Phone publishes here (HTTPS)

Files

  • anycam.py — the whole server (stdlib only).
  • pwcam.c — the raw-libpipewire virtual camera source (auto-compiled to pwcam on first run; delete the binary to force a rebuild).
  • static/index.html (phone capture), view.html (desktop viewer), camtest.html (per-app camera diagnostic; ?report=1 POSTs results to /camtest/report for headless verification).
  • cert/ — auto-generated self-signed cert (delete to regenerate).
  • placeholder.jpg — generated "camera not connected" frame (delete to regen).
  • docs/ — architecture, the virtual-camera diagnosis, and the rewrite spec.

Notes

  • Trusted-LAN tool: anyone on the LAN who accepts the cert can view or publish.
  • One phone publishes at a time (second publisher takes over); any number of viewers.
  • Phone page has live controls: resolution (480p / 720p / 1080p), frame rate (20 / 30 / 60 fps — applied to the camera itself, not just the send loop), JPEG quality, camera pick + front/back flip, live preview with the actual capture size in the status line, landscape/rotation re-negotiation, a hardened screen wake-lock (re-acquired if the OS revokes it), and a Dim screen button (75% black veil against OLED burn-in — preview stays faintly visible; tap anywhere to undim).
  • The camera node advertises I420/NV12/YUY2 at 1280×720, 1920×1080 and 640×480, each at 30 and 60 fps — apps negotiate what they want (verified up to 1080p60). Frames are letterboxed to whatever the consumer picked.

License & commercial use

AnyCam is free software under the GNU AGPL-3.0 (see LICENSE).

  • Using AnyCam is always free — including commercially. Stream, record for YouTube/Twitch, take video calls, work on camera: no fee, no license, no attribution. The AGPL places no conditions on use.
  • Forks and redistribution must stay AGPL-3.0 and keep the copyright notices.
  • Want to embed AnyCam's code in a closed-source or proprietary product? That needs a commercial license — see COMMERCIAL-LICENSING.md.

Contributions are welcome under the Contributor License Agreement.

Support this project

AnyCam is pay-what-you-want. If it saved you the price of a webcam, consider chipping in:

  • Pay-what-you-want download:
  • Sponsor / tip: