Rust Library Usage
Use openipc-rs as Rust crates when you want to build your own receiver,
diagnostic tool, desktop app, recorder, streamer, or hardware validation
utility.
Dependency Shape
After the crates are published, downstream applications can depend on them from crates.io:
[dependencies]
openipc-core = "0.1"
openipc-rtl88xx = "0.1"
During development before a crates.io release, use a Git dependency or local path dependency on this repository:
[dependencies]
openipc-core = { git = "https://github.com/neelsani/openipc-rs", package = "openipc-core" }
The hardware and WASM crates use the published WebUSB-capable nusb-webusb
package while importing it as nusb:
nusb = { package = "nusb-webusb", version = "0.2.3" }
Parse A Realtek RX Transfer
use openipc_core::{parse_rx_aggregate, RxPacketType};
fn inspect_transfer(transfer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
for packet in parse_rx_aggregate(transfer)? {
if packet.attrib.pkt_rpt_type != RxPacketType::NormalRx {
continue;
}
if packet.attrib.crc_err || packet.attrib.icv_err {
continue;
}
println!(
"802.11 frame={} bytes seq={} rssi0={} snr0={}",
packet.data.len(),
packet.attrib.seq_num,
packet.attrib.rssi[0],
packet.attrib.snr[0],
);
}
Ok(())
}
Reconstruct Video Frames
use openipc_core::{
parse_rx_aggregate, ChannelId, FrameLayout, PipelineEvent, ReceiverPipeline,
RxPacketType, WfbKeypair,
};
fn build_pipeline(keypair_bytes: &[u8]) -> Result<ReceiverPipeline, Box<dyn std::error::Error>> {
let keypair = WfbKeypair::from_bytes(keypair_bytes)?;
let pipeline = ReceiverPipeline::with_keypair(
ChannelId::default_video(),
FrameLayout::WithFcs,
keypair,
0,
)?;
Ok(pipeline)
}
fn push_transfer(
pipeline: &mut ReceiverPipeline,
transfer: &[u8],
) -> Result<Vec<Vec<u8>>, Box<dyn std::error::Error>> {
let mut frames = Vec::new();
for packet in parse_rx_aggregate(transfer)? {
if packet.attrib.pkt_rpt_type != RxPacketType::NormalRx {
continue;
}
if packet.attrib.crc_err || packet.attrib.icv_err {
continue;
}
for event in pipeline.push_80211_frame(packet.data)? {
if let PipelineEvent::VideoFrame(frame) = event {
frames.push(frame.data);
}
}
}
Ok(frames)
}
The returned frame bytes are encoded Annex-B H.264/H.265. Your application can write them to a file, feed a native decoder, or forward RTP/Annex-B to another player.
Open The Native Realtek Driver
use std::time::Duration;
use openipc_core::{
ChannelId, FrameLayout, PipelineEvent, ReceiverPipeline, RxPacketType, WfbKeypair,
};
use openipc_rtl88xx::{
ChannelWidth, DriverOptions, RadioConfig, RealtekDevice, DEFAULT_RX_TRANSFER_SIZE,
};
fn receive_once(keypair_bytes: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let keypair = WfbKeypair::from_bytes(keypair_bytes)?;
let mut pipeline = ReceiverPipeline::with_keypair(
ChannelId::default_video(),
FrameLayout::WithFcs,
keypair,
0,
)?;
let device = RealtekDevice::open_first(DriverOptions::default())?;
let report = device.initialize_monitor(RadioConfig {
channel: 36,
channel_offset: 0,
channel_width: ChannelWidth::Mhz20,
})?;
eprintln!("initialized {:?}", report);
let mut bulk_in = device.bulk_in_endpoint()?;
let buffer = bulk_in.allocate(DEFAULT_RX_TRANSFER_SIZE);
let completion = bulk_in.transfer_blocking(buffer, Duration::from_millis(1000));
completion.status?;
for packet in device.parse_rx_transfer(&completion.buffer[..completion.actual_len])? {
if packet.attrib.pkt_rpt_type != RxPacketType::NormalRx {
continue;
}
for event in pipeline.push_80211_frame(packet.data)? {
if let PipelineEvent::VideoFrame(frame) = event {
println!("video frame: {} bytes", frame.data.len());
}
}
}
Ok(())
}
For a full receive loop with adaptive link, use openipc-rs recv as the
reference implementation and then extract the pieces you need.