openipc-core
Shared protocol code for openipc-rs.
This crate contains the parts of the OpenIPC receive path that do not need to know whether bytes came from native USB, WebUSB, a capture file, or a test fixture. It is the right dependency when you want to parse or reconstruct OpenIPC video without taking a dependency on a specific USB frontend.
What It Does
- Parse Realtek rtl88xx USB RX aggregates and 24-byte RX descriptors.
- Select Jaguar1 or Jaguar3 RX descriptor layouts explicitly when processing transfers from a hardware driver.
- Filter OpenIPC/WFB 802.11 frames by channel id and radio port.
- Handle WFB session packets, optional session TLVs, data decryption, and FEC recovery.
- Expose recovered non-video WFB payload bytes from telemetry, tunnel/data, audio, or custom radio ports without parsing those application protocols.
- Parse RTP and depacketize H.264/H.265 into Annex-B access units.
- Expose RTP payload bytes for app-owned sinks such as UDP forwarding or Opus audio decoding.
- Build adaptive-link feedback payloads and WFB uplink packets.
- Build radiotap + 802.11 WFB transmit frames. Hardware crates add their own USB/driver descriptors before transmission.
- Select the first valid copy of each WFB packet when several independent receive adapters feed one protocol pipeline.
Basic Receive Shape
use ;
const VIDEO_ROUTE: PayloadRouteId = new;
push_rx_transfer assumes the Jaguar1 descriptor layout for compatibility.
When bytes come from openipc-rtl88xx, call push_rx_transfer_with_kind with
device.rx_descriptor_kind() so RTL8812CU/EU and RTL8822CU/EU transfers use the
Jaguar3 layout.
The returned frame data is still encoded video. Feed it to WebCodecs, a native decoder, a file writer, or an RTP/Annex-B bridge depending on your application. Long-running receivers should treat per-frame WFB errors as dropped packets and continue scanning the current USB aggregate.
Multiple-Adapter Diversity
Use one ReceiverRuntime for every adapter tuned to the same channel. Put a
DiversityCombiner before it and forward only the first valid copy:
use ;
The combiner identifies encrypted data by channel, WFB session generation, and data nonce. Session packets use their crypto-box nonce. It forwards the first valid copy immediately and never waits for a stronger RSSI sample. Unique fragments from every radio then share the normal WFB decryptor and FEC assembler, so fragments split across adapters can recover one block. Filter corrupt copies before the combiner so a later valid copy is still accepted.
DiversityStats reports first-copy wins and duplicates per source. Adapter
opening, concurrent USB polling, tuning, and failure handling stay at the
application or hardware-crate boundary.
Payload Routes And RTP
PayloadPipeline is the lower-level channel-recovery state machine in
openipc-core. It emits recovered bytes after channel filtering, WFB session
handling, decryption, and FEC. It does not parse RTP, MAVLink, MSP, CRSF, IP,
vendor data, or any other application protocol.
Most apps should start with ReceiverRuntime. It wraps PayloadRouteManager
and the RTP depacketizer used for the configured video route. The route manager
keeps one runtime per (channel_id, key_slot) and attaches one or more route
IDs to that runtime. This avoids decrypting and FEC-recovering the same channel
twice when you want multiple outputs, such as local video display plus RTP
forwarding.
For OpenIPC video, configure one video route. Recovered payloads on that route
are treated as RTP and are turned into Annex-B H.264/H.265 frames. If you also
want RTP forwarding, ask ReceiverRuntime to tap the same route as raw payload
bytes:
use ;
const VIDEO_ROUTE: PayloadRouteId = new;
Applications that already have recovered RTP, such as a native UDP listener, can enter after WFB without maintaining a separate depacketizer:
use ;
with_direct_video_route bypasses only 802.11, WFB decryption, and FEC. Route
fanout, optional RTP reordering, codec configuration tracking, and H.264/H.265
depacketization are unchanged. The older with_mock_video_route and
push_mock_payload names remain aliases for development code.
Add another route for a non-video WFB channel when you want recovered telemetry, IP/VPN, or custom bytes:
use ;
const TELEMETRY_ROUTE: PayloadRouteId = new;
RadioPort::TelemetryRx is OpenIPC's observed telemetry downlink port. That
stream may contain MAVLink, MSP/OSD, or another router-specific payload. Use
RadioPort::TunnelRx, RadioPort::AudioRx, or RadioPort::Custom(n) for other
payload channels. openipc-core does not parse MAVLink or any other telemetry
protocol. Applications can parse, display, record, or inspect those bytes later.
Audio follows the same rule, with one extra helper. OpenIPC-documented audio is
usually Opus RTP payload type 98 mixed into the main video RTP route. Use
ReceiverBatchOptions::rtp_payload_taps to copy only that payload type while
the video depacketizer keeps consuming the same route. Custom wfb-ng profiles
can also carry audio on a separate route such as RadioPort::AudioRx.
openipc-core still does not own audio playback; it only provides recovered RTP
packet bytes and metadata.
RtpDepacketizer is also conservative about fragmented video. If a fragmented
H.264/H.265 RTP access unit has a sequence gap, the partial frame is dropped and
the depacketizer waits for the next clean fragment start.
Crate Boundaries
Logging
The crate emits diagnostics through the standard log
facade and never installs a logger. Applications choose the subscriber. Session
changes are info, FEC recovery and rejected RTP are debug, failures are
warn, and per-packet WFB/RTP details are trace.
openipc-core intentionally has no USB device ownership. Pair it with:
openipc-rtl88xxfor native or WebUSB Realtek adapter IO.openipc-webor@openipc-rs/webfor browser/WASM applications.apps/openipc-clifor CLI-style native receive-loop examples.
Status
The protocol pipeline has unit tests for parser, crypto, FEC, RTP, and uplink helpers. Live radio behavior still depends on the USB driver and adapter validation in higher-level crates.
An optional PixelPilot/wfb-ng reference test can compare Rust FEC output against
PixelPilot's vendored zfex.c implementation:
OPENIPC_PIXELPILOT_REF=/path/to/PixelPilot \
The test builds a temporary C harness, generates PixelPilot parity/recovery
vectors, and compares them with FecCode plus a WFB-shaped
PlainAssembler recovery case. It is ignored by default so normal CI does not
depend on a PixelPilot checkout or a C compiler.