Everything needed to make the Google Pixelbook Go (Atlas, 2019) camera work on a fresh Arch Linux install: the built-in IMX208 sensor, correct colors, and a virtual camera that WeChat and Tencent Meeting (wemeet) accept.
The work is split into three independent layers. Each has its own folder with details and its own README:
| Layer | Folder | What it fixes |
|---|---|---|
| 1. Sensor enablement | imx208-ipu3-camera-fix/ |
Camera does not exist at all |
| 2. Image quality | imx208-camera-color-fix/ |
Picture almost black / wrong colors |
| 3. Virtual camera | (this repo, root) | WeChat / wemeet can't use the IPU3 camera |
Plus the hard-won app-compatibility knowledge in §6.
You don't have to follow the steps manually. Point your AI coding agent
(Kimi Code, Claude Code, Cursor, Aider…) at this repo — it contains everything
needed, including CLAUDE.md with machine-readable internals. Paste this:
Clone https://github.com/ComradeSanta/pixelbookgo-archlinux-camera and set up
the camera on this Pixelbook Go running Arch Linux. Follow README.md: build
and install the patched libcamera (part 1), install the color fix (part 2),
then set up the v4l2loopback virtual camera for WeChat/wemeet (part 3,
including the driver patch and the wemeet shim). Add the libcamera packages to
IgnorePkg in /etc/pacman.conf. Verify each layer works before moving on
(verify-camera.sh, cam -l, a test frame from the virtual camera).
The agent will need sudo access for package installs, DKMS and modprobe.
The Pixelbook Go has a Sony IMX208 sensor behind an Intel IPU3 ISP. Out of the box on Arch:
- libcamera refuses to initialize the sensor — no IMX208 sensor-helper or
properties entry exists upstream, so
cam -lshows nothing and no app sees a camera. (The kernelimx208/ipu3_*modules themselves load fine.) - With the sensor registered, the picture is nearly black with wrong
colors — the IPU3 IPA falls back to an empty
uncalibrated.yaml, and libcamera's AGC never touches the IMX208'sdigital_gainregister. - Most video-call apps (WeChat, wemeet) can't use the IPU3 camera anyway:
they only understand plain UVC-style
/dev/video*devices, so a v4l2loopback virtual camera fed by the real sensor is required — and wemeet additionally trips over quirks in v4l2loopback ≥ 0.13 that other apps tolerate (see §5).
Patches libcamera 0.7.1 with the IMX208 sensor helper + properties (from Peter Lishov's upstream patchwork submission, build-fixed).
cd imx208-ipu3-camera-fix/arch/libcamera
makepkg -si # builds and installs the patched libcamera 0.7.1
../../imx208-ipu3-camera-fix/verify-camera.sh # or: ../verify-camera.sh from inside arch/libcameraverify-camera.sh should print all [ OK ] and exit 0.
Important: tell pacman not to clobber the patched libcamera on upgrades.
Add to /etc/pacman.conf:
IgnorePkg = libcamera libcamera-ipa libcamera-tools libcamera-debug libcamera-docs gst-plugin-libcamera python-libcamera
Details and Fedora/other-distro instructions:
imx208-ipu3-camera-fix/README.md.
The imx208.yaml IPA tuning file plus a script that pushes the sensor's
digital_gain to 8× (libcamera's AGC doesn't control it).
cd imx208-camera-color-fix
sudo ./install.shThis installs the tuning file, a user systemd service (imx208-dgain.service)
and a suspend/resume hook. Verify:
cam -c1 -I 2>&1 | grep "Using tuning file" # .../ipa/ipu3/imx208.yamlDetails (Chinese): imx208-camera-color-fix/README.md.
Apps like WeChat and wemeet read plain /dev/video* devices, so we run a
gstreamer feed from the real sensor into a v4l2loopback device.
sudo pacman -S v4l2loopback-dkms gstreamer gst-plugins-good gst-plugin-libcamerav4l2loopback 0.15 allows only one capture-side owner per device, which
breaks real-world usage (an app that probes the camera on one fd and captures
on another gets EBUSY; two apps can't share the camera). This repo ships
v4l2loopback-shared-capture.patch which
relaxes the token gates so multiple capture consumers can read the same feed,
and lets consumers map the whole buffer pool:
cd /usr/src/v4l2loopback-0.15.4
sudo patch -p1 < /path/to/this/repo/v4l2loopback-shared-capture.patch
sudo dkms build v4l2loopback/0.15.4 -k "$(uname -r)" --force
sudo dkms install v4l2loopback/0.15.4 -k "$(uname -r)" --forceDKMS keeps the patched source in /usr/src, so the patch survives kernel
updates automatically. It is only lost if the v4l2loopback-dkms package
itself is reinstalled/upgraded — then re-apply the two commands above.
echo v4l2loopback | sudo tee /etc/modules-load.d/v4l2loopback.conf
echo 'options v4l2loopback devices=1 card_label="Virtual Camera" exclusive_caps=0 max_buffers=8' \
| sudo tee /etc/modprobe.d/v4l2loopback.conf
sudo modprobe v4l2loopback && sudo chmod 666 /dev/video0(The pixel_formats=NV12,YUYV option seen in older guides no longer exists in
0.15.x — passing it is harmless but ignored.)
The feed pipes the real sensor (1280×720 NV12) through videoconvert to
YUYV 1280×720@30 — the one format wemeet's TRTC engine reliably accepts.
cp v4l2loopback-gst.sh ~/.local/bin/ && chmod +x ~/.local/bin/v4l2loopback-gst.sh
cp imx208-digital-gain-fix.sh ~/.local/bin/ && chmod +x ~/.local/bin/imx208-digital-gain-fix.sh
cp systemd/v4l2loopback-camera.service ~/.config/systemd/user/
systemctl --user daemon-reload
# Deliberately NOT enabled: a running feed keeps the sensor (and its LED) on.
# Start/stop it with vcam-toggle.sh / the "Toggle Virtual Camera" menu entry.PipeWire's libcamera plugin (libspa-libcamera) segfaults on the IMX208,
crash-loops wireplumber, and takes gnome-shell down with it (looks like a
shutdown — you land back at the GDM login screen). Disable it — the physical
camera is used via the gstreamer feed instead:
mkdir -p ~/.config/wireplumber/wireplumber.conf.d
cp wireplumber/51-disable-libcamera.conf ~/.config/wireplumber/wireplumber.conf.d/
systemctl --user restart wireplumberwemeet's TRTC engine sends per-frame DQBUF/QBUF with
v4l2_buffer.memory = 0; v4l2loopback rejects anything but
V4L2_MEMORY_MMAP with EINVAL, and the camera stays black while other apps
work. wemeet-v4l2fix.c is an LD_PRELOAD shim that
rewrites the field:
gcc -O2 -shared -fPIC -o wemeet-v4l2fix.so wemeet-v4l2fix.c -ldlwemeet.sh preloads it automatically. The stock 腾讯会议 menu entry is
overridden at user level (~/.local/share/applications/wemeetapp.desktop)
with Exec=env LD_PRELOAD=...wemeet-v4l2fix.so /opt/wemeet/wemeetapp.sh %u
so the normal icon gets the fix — no separate shortcut.
./install-desktop.sh # menu entries: Toggle / Enable / Disable Virtual CameraDaily flow:
- 开关虚拟摄像头 (Toggle Virtual Camera) → feed starts, LED lights (also applies the digital-gain fix and cycles wireplumber)
- Open WeChat / 腾讯会议 / any camera app
- Toggle again to turn it off — LED goes out
vcam-on.sh / vcam-off.sh (module load/unload + diagnostics) remain as the
full-control variants. wemeet.sh / wechat.sh are optional wrappers that
warn you (GUI notification) if you forgot to toggle the feed on.
IMX208 ──CIO2──> IPU3 IMGU ──NV12──> libcamera (patched, tuned imx208.yaml + dgain)
│
gstreamer: libcamerasrc → videoconvert → YUY2 1280×720@30
▼
v4l2loopback (patched) /dev/videoN
"Virtual Camera"
┌───────┼──────────────┬─────────────┐
WeChat wemeet guvcview Chrome, mpv…
(v4l2) (+LD_PRELOAD (v4l2)
wemeet-v4l2fix)
Three pieces had to be fixed at different levels; all three are in this repo:
- driver (
v4l2loopback-shared-capture.patch) — multiple apps can consume one loopback; consumers can map the whole buffer pool - userspace shim (
wemeet-v4l2fix.so) — wemeet'smemory=0bug - feed (
v4l2loopback-gst.sh) — YUYV 1280×720@30, the format wemeet wants
| App | Works? | Notes |
|---|---|---|
| ✅ | reads /dev/videoN directly; nothing special needed |
|
| Tencent Meeting (wemeet) | ✅ | only via the LD_PRELOAD shim (built into wemeet.sh and the stock icon override) |
| guvcview | ✅ | direct V4L2; install guvcview. Takes photos/videos |
| Chrome/Chromium | ✅ | uses V4L2 directly |
| mpv/ffplay | ✅ | mpv av://v4l2:/dev/videoN for a quick preview |
| OBS Studio | ✅ | add a Video Capture Device (V4L2) source |
| GNOME Camera (Snapshot 50) | ❌ | broken upstream twice: (1) panics without the GStreamer pipewiredeviceprovider, which pipewire ≥ 1.6 no longer ships; (2) its camerabin negotiates formats the locked loopback can't do. Don't use it for now |
| PipeWire-native apps | PipeWire's libcamera plugin is disabled (it segfaults on the IMX208 — see §3.4). PipeWire sees the virtual camera via V4L2 instead |
Physical camera exclusivity: libcamera allows exactly one client of the
IMX208 at a time. When the feed runs, the sensor is busy — that's the point of
the loopback: unlimited apps share the virtual device. For a libcamera-native
app (qcam, cam, gst libcamerasrc), toggle the feed off first.
One virtual camera, many apps: with the driver patch in §3.1, several apps may read the virtual camera simultaneously. Without it, only one app at a time could open it.
| File | Purpose |
|---|---|
vcam-toggle.sh / vcam-toggle.desktop |
daily on/off for the feed (LED follows) |
vcam-on.sh / vcam-off.sh (+.desktop) |
full module load/unload + diagnostics |
vcam-feed.sh |
fallback feed daemon (the systemd service is canonical) |
v4l2loopback-gst.sh |
the actual gstreamer pipeline (→ ~/.local/bin/) |
systemd/v4l2loopback-camera.service |
user service that runs the feed |
imx208-digital-gain-fix.sh |
sensor brightness fix (→ ~/.local/bin/) |
wireplumber/51-disable-libcamera.conf |
stop wireplumber's libcamera SEGV loop |
v4l2loopback-shared-capture.patch |
driver patch: shared capture consumers |
wemeet.sh, wechat.sh |
app wrappers (camera check + shim for wemeet) |
wemeet-v4l2fix.c / .so |
LD_PRELOAD shim fixing wemeet's black camera |
install-desktop.sh |
install the GNOME menu entries |
| Symptom | Likely cause / fix |
|---|---|
cam -l shows no camera |
patched libcamera not active (§1); check IgnorePkg after upgrades |
| Picture dark/washed out | color fix not applied (§2); check tuning file + digital_gain |
| wemeet camera black | launched without the shim — use the stock icon or ./wemeet.sh; verify with WEMEET_V4L2FIX_LOG=/tmp/fix.log |
| App says camera busy | pre-patch v4l2loopback (§3.1) — re-apply the driver patch + dkms |
| Session "shuts down" to login screen | wireplumber's libcamera plugin crash — apply §3.4 |
| guvcview fails to start stream | pre-§3.1 driver; also try -b (disable libv4l2) |
| Camera LED always on | feed running — toggle off (vcam-toggle.sh); the service is intentionally not auto-enabled |
| Reboot hangs with camera on | stop the feed first (the toggle also restarts wireplumber, which is the component that hangs) |
MIT (project scripts). Upstream patches keep their original licenses (libcamera patch: Peter Lishov; original Fedora guide: Mason Zhang).