Files
camoufox/docs/MEDIA-DEVICES.md
T
Jake WriterandClaude Opus 5.5 d3d9d73c2d docs: one AGENTS.md for every agent, a roadmap, and docs that match the code
- AGENTS.md holds the engineering rules for any coding agent, plus the
  repo map, build, patch and test commands that CLAUDE.md used to carry.
  CLAUDE.md now only imports it, so there is one set of rules.
  ci/tribal-rules.yml is the record of settled decisions it points to.
- ROADMAP.md lists planned work, each item linked to its issue.
- README:
  - fpgen and the coherence check replace BrowserForge;
  - the patch workflow uses the make targets instead of the removed
    developer UI;
  - letter-spacing noise is described as off by default, as it is.
- docs/:
  - beta-testing-ff146.md removed;
  - patch-upgrading-guide rewritten around the make targets;
  - per-context-patches without the canvas patch that no longer exists,
    and with measured preset counts;
  - playwright-maintenance without the JSM wrapper that does not exist;
  - smaller fixes in MEDIA-DEVICES, input-dispatch and FONTS.
- ci/README: every job, and the real shard, skiplist and entry-point lists.
- pythonlib, tester and patch-dependency READMEs corrected against the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:22:28 -06:00

2.8 KiB

Media devices

How Camoufox presents navigator.mediaDevices (media-device-spoofing.patch, pythonlib/camoufox/media-devices.json, draw_media_devices()).

What a page can see

Stock Firefox exposes, before any getUserMedia grant, at most one device per input kind with empty label/deviceId/groupId and no audiooutput. After a grant (or while capturing) it lists every device with the OS's own names, per-origin hashed ids, and shared groupIds for devices of the same hardware. The captured track's label and getSettings().deviceId/groupId are those of one listed device.

Camoufox reproduces exactly that for a spoofed machine:

  • The fake media engine (MediaEngineFake::EnumerateDevices) emits the identity's microphones, cameras and speakers, with labels, raw ids and groups from the config, and MediaManager uses it for mics/cams and for the outputs whenever mediaDevices:enabled is set. MediaDevices.cpp is stock, so the pre-/post-grant rules, feature policy, permission prompts, the capture indicator and the camera/microphone groupId correlation all run unchanged.
  • getUserMedia() therefore succeeds iff the identity has the requested device kind (a claimed camera captures the fake engine's test pattern; a camera-less identity gets NotFoundError, like a real machine without one).
  • The patch treats the identity's devices (LocalMediaDevice::IsIdentityDevice()) as real hardware, so they get the permission prompt, count as capturing and expose their labels after a grant. media.navigator.permission.fake stays off, as in stock Firefox, because a page can detect it.

Config keys

key meaning
mediaDevices:enabled use the identity's devices instead of the host's
mediaDevices:micros / webcams / speakers counts
mediaDevices:microphoneLabels / webcamLabels / speakerLabels labels, aligned with the counts
mediaDevices:microphoneGroups / webcamGroups / speakerGroups raw group ids (same string = same groupId)

The Python package draws all of them from media-devices.json, seeded by the identity, unless the caller set any mediaDevices: key. Label styles follow the OS: Windows WASAPI friendly names (Microphone Array (Realtek(R) Audio)), macOS CoreAudio names (MacBook Pro Microphone, FaceTime HD Camera), Linux PulseAudio/PipeWire descriptions plus v4l2 card names with the USB id, and a Monitor of … source per output as PulseAudio exposes.

Guard

tests/patches/media-devices-coherence.py checks the pre-grant shape, that capture succeeds exactly for listed kinds, that captured tracks match listed devices, that labels are never the fake engine's, and that a camera-only identity does not crash the content process (it did, before build12).