# udptl
UDPTL (UDP Transport Layer) codec and socket for T.38 fax over IP.
Implements the UDPTL packet format defined in ITU-T T.38 with both redundancy and FEC error recovery modes. Wire-compatible with SpanDSP.
## Usage
```toml
[dependencies]
udptl = "0.1"
```
### Async (tokio, default)
```rust
use udptl::AsyncUdptlSocket;
let socket = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
let udptl = AsyncUdptlSocket::new(socket);
udptl.connect("192.168.1.100:9000".parse()?);
// Send an IFP packet (redundancy added automatically)
udptl.send_ifp(&ifp_data).await?;
// Receive
let packet = udptl.recv_packet().await?;
println!("{}", packet); // UDPTL seq=0 primary=128B redundant=3
```
### Sync
```toml
[dependencies]
udptl = { version = "0.1", default-features = false }
```
```rust
use udptl::UdptlSocket;
let socket = std::net::UdpSocket::bind("0.0.0.0:0")?;
let udptl = UdptlSocket::new(socket);
udptl.connect("192.168.1.100:9000".parse()?);
udptl.send_ifp(&ifp_data)?;
let packet = udptl.recv_packet()?;
```
### Codec only
Encode and decode packets without a socket:
```rust
use udptl::{UdptlPacket, ErrorRecovery};
// Decode
let packet = UdptlPacket::decode(&wire_bytes)?;
// Encode
let packet = UdptlPacket::with_redundancy(0, ifp_data, redundant_packets);
let bytes = packet.encode();
// FEC mode
let packet = UdptlPacket::with_fec(0, ifp_data, 3, fec_entries);
let bytes = packet.encode();
```
## Configuration
```rust
use udptl::{UdptlSocket, UdptlConfig, ErrorCorrectionScheme};
let config = UdptlConfig {
error_correction: ErrorCorrectionScheme::Redundancy,
max_redundancy: 3,
fec_span: 3,
fec_entries: 3,
max_packet_size: 512,
};
let udptl = UdptlSocket::with_config(socket, config);
```
## License
MIT
## AI DISCLAIMER
Bro this is slop city deluxe. I heavily supervised the model but it's 2026, get real. **No warranties express or implied** etc etc.