|
| 1 | +# Valeria - Linux setup |
| 2 | + |
| 3 | +This guide is required for **`pymobiledevice3 valeria`** (iOS H.264 |
| 4 | +screen capture) on Linux. The distro `usbmuxd` package may not include |
| 5 | +the `DEVICE_MODE` feature needed to enable QuickTime capture mode, so |
| 6 | +we build upstream `usbmuxd` master from source. |
| 7 | + |
| 8 | +## Why this is needed |
| 9 | + |
| 10 | +The screen-capture path uses Apple's QuickTime alt-config protocol over |
| 11 | +USB. On macOS the OS-level `usbmuxd` handles the lockdown handshake on |
| 12 | +the device's USBMux interface (the one that's normally used for sync, |
| 13 | +backups, etc.) - and the iDevice will not begin streaming H.264 until |
| 14 | +that handshake has happened. |
| 15 | + |
| 16 | +Distribution `usbmuxd` packages don't yet support driving a device into |
| 17 | +QuickTime alt-config - the upstream feature for this (env var |
| 18 | +`USBMUXD_DEFAULT_DEVICE_MODE`) was added to upstream master in 2022 but |
| 19 | +predates the latest tagged release, so it hasn't shipped in any distro |
| 20 | +package. We need to build upstream `usbmuxd` from source and run it |
| 21 | +with that env var set. |
| 22 | + |
| 23 | +## What this changes about your machine |
| 24 | + |
| 25 | +- The distro `usbmuxd` is replaced with our fork under `/usr/local/` |
| 26 | + (the `dynamic-config-switch` branch on top of upstream master). |
| 27 | +- Any iDevice you plug in starts in QuickTime mode (USB configuration |
| 28 | + 5). Each `pmd3 valeria` or `pmd3 screen-mirror` invocation toggles |
| 29 | + it between config 5 (capture active) and config 4 (idle, USBMux only) |
| 30 | + for the duration of that capture, then back. Mirrors macOS's |
| 31 | + `iOSScreenCaptureAssistant` behaviour. |
| 32 | +- Standard things - file transfer, lockdown queries, app installation, |
| 33 | + iDevice-as-network-modem tethering - keep working. They go through |
| 34 | + the same `/var/run/usbmuxd` socket and the same iface 1 USBMux |
| 35 | + endpoints, which are present in both configurations. |
| 36 | + |
| 37 | +If you need to revert later, see [Reverting](#reverting) below. |
| 38 | + |
| 39 | +## Build and install |
| 40 | + |
| 41 | +Tested on Ubuntu 22.04. The build chain is **libplist → libimobiledevice-glue |
| 42 | +→ usbmuxd**. Ubuntu's `libplist-dev` is too old (2.2.0; we need 2.3+), |
| 43 | +which is why the chain starts there. |
| 44 | + |
| 45 | +```bash |
| 46 | +# 1. Build dependencies |
| 47 | +sudo apt-get update && sudo apt-get install -y \ |
| 48 | + autoconf automake libtool pkg-config build-essential git \ |
| 49 | + libusb-1.0-0-dev |
| 50 | + |
| 51 | +# 2. Remove distro usbmuxd (it'll conflict with upstream master) |
| 52 | +sudo systemctl stop usbmuxd 2>/dev/null || true |
| 53 | +sudo apt-get remove -y usbmuxd |
| 54 | + |
| 55 | +# 3. Build the libimobiledevice stack from upstream |
| 56 | +cd /tmp |
| 57 | +for proj in libplist libimobiledevice-glue; do |
| 58 | + git clone --depth 1 https://github.com/libimobiledevice/$proj |
| 59 | + (cd $proj && ./autogen.sh && ./configure && make -j && sudo make install) |
| 60 | +done |
| 61 | +sudo ldconfig |
| 62 | + |
| 63 | +# 3a. usbmuxd from our fork's dynamic-config-switch branch (adds the |
| 64 | +# SetActiveConfiguration IPC that pmd3's per-capture config switch |
| 65 | +# uses; pending upstream PR). |
| 66 | +git clone -b dynamic-config-switch \ |
| 67 | + https://github.com/renegadelink/usbmuxd |
| 68 | +(cd usbmuxd && ./autogen.sh && ./configure --without-preflight \ |
| 69 | + && make -j && sudo make install) |
| 70 | + |
| 71 | +# 4. Configure systemd to start usbmuxd with QuickTime mode enabled |
| 72 | +sudo mkdir -p /etc/systemd/system/usbmuxd.service.d |
| 73 | +sudo tee /etc/systemd/system/usbmuxd.service.d/override.conf >/dev/null <<'EOF' |
| 74 | +[Service] |
| 75 | +Environment=USBMUXD_DEFAULT_DEVICE_MODE=2 |
| 76 | +EOF |
| 77 | +sudo systemctl daemon-reload |
| 78 | +sudo systemctl enable --now usbmuxd |
| 79 | +``` |
| 80 | + |
| 81 | +## Verify |
| 82 | + |
| 83 | +```bash |
| 84 | +# Should report a recent build SHA, not v1.0.x: |
| 85 | +/usr/local/sbin/usbmuxd --version |
| 86 | + |
| 87 | +# Should list "USBMUXD_DEFAULT_DEVICE_MODE": |
| 88 | +strings /usr/local/sbin/usbmuxd | grep DEVICE_MODE |
| 89 | + |
| 90 | +# Plug an iDevice, then confirm the daemon picked it up |
| 91 | +# and switched to QuickTime mode (configuration value 5): |
| 92 | +pymobiledevice3 usbmux list |
| 93 | +cat /sys/bus/usb/devices/*/bConfigurationValue \ |
| 94 | + /sys/bus/usb/devices/*/idVendor 2>/dev/null \ |
| 95 | + | paste - - | awk '$2 == "05ac" {print "active config:", $1}' |
| 96 | +``` |
| 97 | + |
| 98 | +If the active config is `5`, you're set. |
| 99 | + |
| 100 | +## Run |
| 101 | + |
| 102 | +```bash |
| 103 | +# Capture raw H.264 to a file |
| 104 | +pymobiledevice3 valeria -o capture.h264 --duration 10 |
| 105 | +``` |
| 106 | + |
| 107 | +The iDevice screen must be **on** (not asleep) for frames to actually |
| 108 | +flow. The protocol completes its handshake either way, but a sleeping |
| 109 | +display produces no encoder output. |
| 110 | + |
| 111 | +## Troubleshooting |
| 112 | + |
| 113 | +**`Valeria: capture started (0x0)` and an empty output file** - the |
| 114 | +handshake reached the protocol thread but the device never sent a video |
| 115 | +clock setup. Most often the iDevice screen is asleep. Tap the iDevice |
| 116 | +to wake it and re-run. |
| 117 | + |
| 118 | +**`failed to start capture: [Errno 16] Resource busy`** - another |
| 119 | +process (often a stale `pmd3 valeria` from a prior run, or a separate |
| 120 | +`usbmuxd` instance) is still holding the USBMux interface. Check |
| 121 | +`ps aux | grep usbmuxd` - there should be exactly one instance. |
| 122 | + |
| 123 | +**Multiple captures in a row** - each `pymobiledevice3 valeria` |
| 124 | +invocation asks `usbmuxd` to switch the iDevice into the QT-enabled USB |
| 125 | +configuration for the duration of the capture, and back to a QT-free |
| 126 | +configuration when done (mirroring macOS's `iOSScreenCaptureAssistant` |
| 127 | +pattern). This means each invocation is self-contained - no daemon, no |
| 128 | +kept-alive USB iface claim - and back-to-back captures across long |
| 129 | +idles work cleanly. |
| 130 | + |
| 131 | +**Other `pymobiledevice3` commands stop working after install** - |
| 132 | +double-check that the distro `usbmuxd` was removed cleanly. `which usbmuxd` |
| 133 | +should return `/usr/local/sbin/usbmuxd`. `systemctl status usbmuxd` |
| 134 | +should show the upstream binary. |
| 135 | + |
| 136 | +## Reverting |
| 137 | + |
| 138 | +To go back to the distro setup (no QuickTime alt-config): |
| 139 | + |
| 140 | +```bash |
| 141 | +sudo systemctl stop usbmuxd |
| 142 | +sudo rm /etc/systemd/system/usbmuxd.service.d/override.conf |
| 143 | +sudo apt-get install -y --reinstall usbmuxd |
| 144 | +sudo systemctl daemon-reload |
| 145 | +sudo systemctl restart usbmuxd |
| 146 | +``` |
| 147 | + |
| 148 | +The upstream `libplist` / `libimobiledevice-glue` libraries you built |
| 149 | +into `/usr/local/` are harmless to leave installed. Remove them too |
| 150 | +with `(cd /tmp/<lib> && sudo make uninstall)` if you want a perfectly |
| 151 | +clean revert. |
| 152 | + |
| 153 | +## Background - what `USBMUXD_DEFAULT_DEVICE_MODE=2` does |
| 154 | + |
| 155 | +Upstream `usbmuxd` since commit `6d0183dd` (2022-12-22) supports the |
| 156 | +env var `USBMUXD_DEFAULT_DEVICE_MODE`. The values map to USB |
| 157 | +configuration numbers Apple defines on iDevices: |
| 158 | + |
| 159 | +| Mode | USB config | Description | |
| 160 | +|------|------------|-------------| |
| 161 | +| 1 | 4 | Default - usbmuxd only (sync, lockdown, tethering) | |
| 162 | +| 2 | 5 | QuickTime alt-config - adds the AV streaming interface | |
| 163 | +| 3 | 6 | Other internal mode | |
| 164 | +| 4 | 7 | Other internal mode | |
| 165 | +| 5 | 8 | Other internal mode | |
| 166 | + |
| 167 | +Setting it to `2` tells `usbmuxd` to put the iDevice into a USB layout |
| 168 | +that exposes BOTH a QT-capable configuration (5) and a USBMux-only |
| 169 | +configuration (4). The `SetActiveConfiguration` IPC on our fork's |
| 170 | +`dynamic-config-switch` branch then lets `pymobiledevice3 valeria` |
| 171 | +toggle between them per-capture - matching macOS's |
| 172 | +`iOSScreenCaptureAssistant`, which also keeps the iDevice in the |
| 173 | +non-QT config when idle and switches into the QT config only while a |
| 174 | +capture is active. |
| 175 | + |
| 176 | +If you don't need that idle-when-not-capturing behaviour (e.g. a |
| 177 | +multi-device farm where you'd rather avoid the per-capture lockdown |
| 178 | +disconnect), skip our fork and use stock upstream `usbmuxd` with the |
| 179 | +same `USBMUXD_DEFAULT_DEVICE_MODE=2`. The iDevice stays in config 5 |
| 180 | +permanently; the USBMux iface is always present so lockdown ops keep |
| 181 | +working unaffected. |
0 commit comments