# Adaptive Jitter Buffer Configuration Guide ## Overview The FNE (Fixed Network Equipment) includes an adaptive jitter buffer system that can automatically reorder out-of-sequence RTP packets from peers experiencing network issues such as: - **Satellite links** with high latency and variable jitter - **Cellular connections** with packet reordering - **Congested network paths** causing sporadic delays - **Multi-path routing** leading to out-of-order delivery The jitter buffer operates with **zero latency for perfect networks** - if packets arrive in order, they pass through immediately without buffering. Only out-of-order packets trigger the adaptive buffering mechanism. ## How It Works ### Zero-Latency Fast Path When packets arrive in perfect sequence order, they are processed immediately with **no additional latency**. The jitter buffer is effectively transparent. ### Adaptive Reordering When an out-of-order packet is detected: 1. The jitter buffer holds the packet temporarily 2. Waits for missing packets to arrive 3. Delivers frames in correct sequence order 4. Times out after a configurable period if gaps persist ### Per-Peer, Per-Stream Isolation - Each peer connection can have independent jitter buffer settings - Within each peer, each call/stream has its own isolated buffer - This prevents one problematic stream from affecting others ## Configuration ### Location Jitter buffer configuration is defined in the FNE configuration file (typically `fne-config.yml`) under the `master` section: ```yaml master: # ... other master configuration ... jitterBuffer: enabled: false defaultMaxSize: 4 defaultMaxWait: 40000 peerOverrides: - peerId: 31003 enabled: true maxSize: 6 maxWait: 80000 ``` ### Parameters #### Global Settings - **enabled** (boolean, default: `false`) - Master enable/disable switch for jitter buffering - When `false`, all peers operate with zero-latency pass-through - When `true`, peers use jitter buffering with default parameters - **defaultMaxSize** (integer, range: 2-8, default: `4`) - Maximum number of frames to buffer per stream - Larger values provide more reordering capability but add latency - **Recommended values:** - `4` - Standard networks (LAN, stable WAN) - `6` - High-jitter networks (cellular, congested paths) - `8` - Extreme conditions (satellite, very poor links) - **defaultMaxWait** (integer, range: 10000-200000 microseconds, default: `40000`) - Maximum time to wait for missing packets - Frames older than this are delivered even with gaps - **Recommended values:** - `40000` (40ms) - Terrestrial networks - `60000` (60ms) - Cellular networks - `80000` (80ms) - Satellite links #### Per-Peer Overrides The `peerOverrides` array allows you to customize jitter buffer behavior for specific peers: ```yaml peerOverrides: # Satellite link - high latency, requires larger buffer - peerId: 31003 enabled: true maxSize: 6 maxWait: 80000 # Cellular peer - variable jitter - peerId: 31004 enabled: true maxSize: 5 maxWait: 60000 # Local fiber peer - disable jitter buffer for minimal latency - peerId: 31005 enabled: false ``` Each override entry supports: - **peerId** (integer) - The peer ID to configure - **enabled** (boolean) - Enable/disable for this specific peer - **maxSize** (integer, range: 2-8) - Buffer size override - **maxWait** (integer, range: 10000-200000) - Timeout override ## Configuration Examples ### Example 1: Disabled (Default) For networks with reliable connectivity: ```yaml master: jitterBuffer: enabled: false defaultMaxSize: 4 defaultMaxWait: 40000 ``` All peers operate with zero-latency pass-through. Best for: - Local area networks - Stable dedicated connections - Networks with minimal packet loss/reordering ### Example 2: Global Enable with Defaults Enable jitter buffering for all peers with conservative settings: ```yaml master: jitterBuffer: enabled: true defaultMaxSize: 4 defaultMaxWait: 40000 ``` Good starting point for: - Mixed network environments - Networks with occasional jitter - General purpose deployments ### Example 3: Selective Peer Configuration Enable only for problematic peers: ```yaml master: jitterBuffer: enabled: false # Disabled by default defaultMaxSize: 4 defaultMaxWait: 40000 peerOverrides: # Enable only for satellite peer - peerId: 31003 enabled: true maxSize: 8 maxWait: 80000 # Enable for cellular peer - peerId: 31004 enabled: true maxSize: 6 maxWait: 60000 ``` Recommended approach for: - Mostly stable networks with a few problem peers - Minimizing overall system latency - Targeted optimization ### Example 4: High-Jitter Network For challenging network environments: ```yaml master: jitterBuffer: enabled: true defaultMaxSize: 6 defaultMaxWait: 60000 peerOverrides: # Satellite link needs even more buffering - peerId: 31003 enabled: true maxSize: 8 maxWait: 100000 ``` Suitable for: - Wide area networks with variable quality - Networks with frequent reordering - Deployments with multiple satellite/cellular links ## Monitoring and Tuning ### Startup Logging When the FNE starts, jitter buffer configuration is displayed in the logs: ``` I: 2025-12-03 22:07:11.374 Jitter Buffer Enabled: yes I: 2025-12-03 22:07:11.374 Jitter Buffer Default Max Size: 6 frames I: 2025-12-03 22:07:11.374 Jitter Buffer Default Max Wait: 50000 microseconds I: 2025-12-03 22:07:11.374 Jitter Buffer Peer Overrides: 3 peer(s) configured ``` ### Per-Peer Configuration Logging When a peer connects and jitter buffering is enabled, you'll see (with verbose logging): ``` I: 2025-12-03 22:10:15.234 PEER 31003 jitter buffer configured (override): enabled=yes, maxSize=8, maxWait=80000 I: 2025-12-03 22:10:16.456 PEER 31004 jitter buffer configured (default): enabled=yes, maxSize=4, maxWait=40000 ``` ### Future Monitoring (To Be Implemented) Planned monitoring capabilities: - Per-peer jitter buffer statistics (reordered frames, dropped frames, timeouts) - InfluxDB metrics for trend analysis - REST API endpoints for runtime statistics - Automatic tuning recommendations based on observed behavior ## Performance Characteristics ### CPU Impact - **Zero-latency path:** Negligible overhead (~1 comparison per packet) - **Buffering path:** Minimal overhead (~map lookup + timestamp check) - **Memory:** ~500 bytes per active stream buffer ### Latency Impact - **In-order packets:** 0ms additional latency - **Out-of-order packets:** Buffered until: - Missing packets arrive, OR - `maxWait` timeout expires - **Typical latency:** 10-40ms for reordered packets on terrestrial networks ### Effectiveness Based on the adaptive jitter buffer design: - **100% pass-through** for perfect networks (zero latency) - **~95-99% recovery** of out-of-order packets within timeout window - **Automatic timeout delivery** prevents indefinite stalling ## Troubleshooting ### Symptom: Audio/Data Gaps Despite Jitter Buffer **Possible Causes:** 1. `maxWait` timeout too short for network conditions 2. `maxSize` buffer too small for reordering depth 3. Actual packet loss (not just reordering) **Solutions:** - Increase `maxWait` by 20-40ms increments - Increase `maxSize` by 1-2 frames - Verify network packet loss with diagnostics ### Symptom: Excessive Latency **Possible Causes:** 1. Jitter buffer enabled on stable connections 2. `maxWait` set too high 3. `maxSize` set too large **Solutions:** - Disable jitter buffer for known-good peers using overrides - Reduce `maxWait` in 10-20ms decrements - Reduce `maxSize` to minimum (2-4 frames) ### Symptom: No Improvement **Possible Causes:** 1. Jitter buffer not actually enabled for the problematic peer 2. Issues beyond reordering (e.g., corruption, auth failures) 3. Problems at application layer, not transport layer **Solutions:** - Verify peer override configuration is correct - Check FNE logs for peer-specific configuration messages - Enable verbose and debug logging to trace packet flow ## Technical Details ### RTP Sequence Number Handling The jitter buffer correctly handles RTP sequence number wraparound (RFC 3550): - Sequence numbers are 16-bit unsigned integers (0-65535) - After 65535, sequence resets to 0 - Buffer correctly calculates sequence differences across wraparound - No special configuration needed ### Thread Safety - Each peer's jitter buffer map is protected by a mutex - Per-stream buffers operate independently - Safe for concurrent access from multiple worker threads ### Memory Management - Buffered frames use RAII for automatic cleanup - Timed-out frames are automatically freed - Stream buffers cleaned up when streams end - No memory leaks under normal operation ## Best Practices 1. **Start Disabled**: Begin with jitter buffering disabled and enable only as needed 2. **Target Specific Peers**: Use per-peer overrides rather than global enable when possible 3. **Conservative Tuning**: Start with default parameters and adjust incrementally 4. **Monitor Performance**: Watch for signs of latency or audio quality issues 5. **Document Changes**: Keep records of which peers need special configuration 6. **Test Thoroughly**: Validate changes don't introduce unintended latency ## Reference ### Related Documentation - `ADAPTIVE_JITTER_BUFFER.md` - Technical implementation details - `AdaptiveJitterBuffer.example.cpp` - Code examples - `fne-config.example.yml` - Full configuration example ### Configuration Schema ```yaml jitterBuffer: enabled: # false defaultMaxSize: <2-8> # 4 defaultMaxWait: <10000-200000> # 40000 peerOverrides: - peerId: # Required enabled: # Optional, defaults to global enabled maxSize: <2-8> # Optional, defaults to defaultMaxSize maxWait: <10000-200000> # Optional, defaults to defaultMaxWait ``` ## Version History - **December 2025** - Initial implementation - Zero-latency fast path - Per-peer, per-stream adaptive buffering - Configuration parsing and validation - Sequence wraparound support