Releases: GingerFluffyCat/PixelPilot
Release list
v0.5.0 - Rebased onto devourer's rebuild-on-upstream
The devourer submodule now points at v0.5.1 (rebuild-on-upstream): upstream
OpenIPC/devourer's current architecture (async-URB-pool RX -> IRtlDevice::
StartRxLoop, PCIe transport, Jaguar2/3/Kestrel chip support) with every
APFPV capability re-ported and verified building — no regression:
- Full WPA2/CCMP station-mode connect (scan -> arm -> auth -> assoc -> 4-way
-> DHCP -> RTP/LQ streaming), driver-level auto-reconnect, SSH-over-dongle. - The close-range DIG-saturation fix (hardware-validated RSSI-tracked gain).
- A-MPDU RX reorder + the station RX-filter fix.
- AP mode rewired onto upstream's real hardware beacon/ACK primitives
(IRtlDevice::StartBeacon/StopBeacon) instead of the old manual register
pokes + a software beacon-poll loop, per upstream's own bench-validated
docs/ap-mode.md recipe. Also fixes a real bug: AP-mode TX was calling
send_packet synchronously from the RX callback thread (returns BUSY by
design) — now queued to a separate TX-pump thread. Real-hardware
association is still untested this round. - The Android JNI bridge (apfpv_jni.cpp, WfbngLink.cpp) ported onto the new
IRtlDevice/RtlJaguarDevice API, including a rewritten
app/wfbngrtl8812/src/main/cpp/CMakeLists.txt (the old file mixed the
pre-rebuild extern/WiFiDriver/{hal,src} tree with the rebuilt devourer/
{hal,src} tree on one include path, which broke the build with duplicate
type definitions). WFB-ng's protocol layer (TxFrame/RxFrame/
SignalQualityCalculator, no upstream equivalent) still comes from
extern/WiFiDriver, now re-registered as a proper submodule.
Also: the settings menu now hides dongle-only APFPV actions (VRX beacon
cal, dongle AP test, Reconnect/adapter picker, DE-legal-limits, channel/
width ranges) when in phone-Wi-Fi mode, where there's no dongle for them
to act on.
Verified: a full clean assembleDebug (arm64-v8a + x86_64) builds with zero
errors. Real-hardware validation (dongle connect/stream, AP-mode
association) is still outstanding — this release is a clean build/
architecture milestone, not a hardware-tested one.
v0.4.4 - Upstream merge: UDP forwarding, new adapter recognition
Merged 3 commits from OpenIPC/PixelPilot upstream:
- UDP Forwarding: new menu option to forward the received video stream
to a second IP:port. Independent of the APFPV/wfb-ng video path. - RTL8812EU/RTL8822EU USB device recognition: new device IDs in
usb_device_filter.xml so the app requests USB permission for these
dongles. Note: this only affects device recognition -- our devourer/
WiFiDriver fork doesn't have driver support for these (Jaguar3-generation)
chips yet, only the RTL8812AU family (Jaguar1). - Docs fix (gs.key link).
Our own APFPV-specific code (WPA2 station-mode, RxDeframe, ApfpvStation,
LqFeedback, the DVR-duration/GL-fanout/dongle-reconnect fixes from v0.4.3)
is unaffected -- verified by resolving each merge conflict to protect it,
then rebuilding.
Debug APK, built 2026-07-24.
v0.4.3 - DVR duration, GL fan-out refresh-rate, dongle reconnect
- DVR recording duration bug fixed:
mp4_h26x_write_nal's last argument
is the sample's duration, not an absolute timestamp -- was passing the
cumulative-since-start value, which made recordings play progressively
slower the longer they ran. - GL fan-out now matches the real display refresh rate: the display-swap
throttle assumed a fixed 60Hz panel; on a 120Hz panel that read as judder
even though stream and panel were both genuinely ~120fps. - Dongle physical unplug/replug reconnect hardening (WFB-ng and APFPV
dongle paths):openDevice()returning null (a real race right after a
replug) is now handled instead of crashing with an NPE; USB connections are
closed on stop so a reattach'sopenDevice()doesn't race against a
leaked fd; a stale internal device-map entry from the torn-down session no
longer gets silently reused on reattach.
Debug APK, built 2026-07-24. CI: https://github.com/AlaEddine-Zoghlami/PixelPilot/actions/runs/30120151316
v0.4.2
Fixed
- MTK HW HEVC decode — root-caused and fixed the long-standing "MTK HW HEVC freezes" workaround: the H.265 csd-0 buffer (VPS+SPS+PPS) was built in reverse order (PPS,SPS,VPS) due to an
insert(begin())bug, which the software decoder tolerated but the hardware decoder didn't. HW decode is now the default on MTK and sustains 120fps with 0 discards. - Decoder stall-recovery watchdog — a MediaTek video-firmware throughput ceiling (confirmed via kernel-level atrace, transport-independent) could permanently freeze the decoder under sustained high bitrate. Added a watchdog that detects "data arriving, 0 frames decoded" and forces a recovery flush, turning a permanent freeze into a bounded ~2-4s recoverable stutter.
- Stale-frame flush storm — the flush-to-keyframe path could enter a self-sustaining loop on any decoder that fell behind; previously only disabled for the software-decoder fallback, now disabled for MTK regardless of which decoder is active.
- App-resume reconnect (phone-Wi-Fi) —
onPause()wasn't stoppingApfpvWifiManager, so its running-guard never reset andonResume()'s reconnect silently no-op'd. - Decode-feed thread decouple — a slow decode step could stall the RTP-receive/parse thread; feeding the decoder now runs on its own thread with a bounded, drop-oldest queue.
- Mutex-starvation fix (devourer/WiFiDriver) — the decoder feed thread's input-buffer retry loop could win a mutex re-acquire race indefinitely against the stall watchdog's recovery flush; added a yield point so recovery can actually land.
- UAF-prone RX teardown (devourer/WiFiDriver) —
stopAsyncRx()could free USB transfers libusb still owned after a sleep-poll timeout, surfacing later as a destroyed-mutex SIGABRT on replug. Now actively pumps libusb events itself and leaks (rather than frees) a transfer that's still genuinely stuck. - Exception-unsafe pause/resume around RF calibration (devourer/WiFiDriver) — an exception during
arm()/IQK left the async RX pool permanently paused, compounding into the UAF above. Guarded with RAII so resume always runs.
Added
- Diagnostics: feed-queue overflow logging, URB free-list depth/pool-empty events (
rxd-pool), GL fan-out stage timing, and live-tunable BA buffer-size / LQ-feedback-interval / RTP-reorder-timeout knobs (debug.pixelpilot.*props) for A/B testing the dongle's accept-vs-decline BlockAck tradeoff without a rebuild per value.
Full changelog: see CHANGELOG.md.
v0.4.1 — recording FPS fix, GLFanout encode-only, JSch ProGuard fix
PixelPilot v0.4.1
Bumps the Devourer submodule to v0.4.1 (RTP-sequence reorder → fixes dongle
video quality drops) plus three app-side fixes.
1. Recording FPS: no more progressive slowdown
The mp4 muxer stamped every sample with a fixed 90000/fps cadence. When the
actual decode rate (60 / 90 / 120 fps) != the configured fps, each sample's
timestamp was wrong by a constant factor, so a long recording's total duration
drifted from real time — the longer you recorded, the slower playback got.
Now samples are stamped from the real wall-clock encoder PTS
(\eglPresentationTimeANDROID, fed through \AMediaCodecBufferInfo.presentationTimeUs)
for the GL-fanout DVR, and from the NALU \creationTime\ for the legacy DVR path.
Duration tracks wall-clock at any source fps.
2. GLFanout: encode-only render path
The DVR encoder now receives every decoded frame at the full decode rate
while the display surface is throttled (prevents BLAST buffer exhaustion). The
encoder's timestamps stay in sync with the source, avoiding the drift above.
3. ProGuard: JSch ClassNotFoundException fix
Release builds (\minifyEnabled = true) stripped \com.jcraft.jsch.jce.*
because JSch loads its crypto providers reflectively
(\getJceName()\ → \Class.forName). Opening SSH/settings then threw
\ClassNotFoundException: com.jcraft.jsch.jce.Random.
Added -keep class com.jcraft.jsch.**\ and -dontwarn\ for JSch's optional
Android-absent deps (jna / bouncycastle / log4j / slf4j / newsclub / ietf-jgss).
Verification
On-device: 0 ClassNotFoundException on settings/SSH path; connect stable
(state→7); RTP reorder backlog 0; render 89–91 fps; recording duration matches
wall-clock.
Full diff: \c1cfbb0\ (one commit since v0.4.0). Devourer submodule at v0.4.1.
v0.4.0
APFPV reconnect-on-resume fix (release + close the dongle UsbDeviceConnection on pause so resume re-opens cleanly). Decode drop-to-freshest, GL fan-out DVR, colortrans reversal shader. VTX IP route (VpnService) + SSH-over-dongle + phone-wifi manager. Bumps embedded Devourer v0.4.0.
v0.3.8
APFPV: add channel/width RANGE selector matching the air WebUI The AP operates on channel ranges (primary+secondary/center): 40 MHz "36_40" has primary ch36, 80 MHz "36_48" primary ch36. A userspace station can't follow a runtime CSA, so it must arm to the AP's EXACT primary. Picking a bare "40" mismatched (AP 40 MHz range = 36_40, primary 36) -> connect-then-drop. New "APFPV Channel/Width" submenu mirrors aalinkFPV-channel.cgi 1:1 and arms the dongle to the range's primary; legalApfpvChannel(primary,bw) reproduces the AP's exact secondary-offset/center. Legal DE 5.2 GHz UNII-1 (5170-5250) only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.3.7
PixelPilot v0.3.7 — APFPV: remove Taiga/ch52 hardcode; VR single-decode
v0.3.6
PixelPilot v0.3.6 — VR single-decode dual-eye fan-out (fixes eye free…
v0.3.5
PixelPilot v0.3.5 — fix VR eye freezing (GLFanout renderTask gate re-…