Skip to main content

Module filecc

Module filecc 

Source
Expand description

SRT File Transfer Congestion Control (FileCC) — window-based congestion control, draft-sharabayko-srt-01 §5.2 (curated at specs/rules/srt-congestion.md).

This is the file/bulk-transfer mode sibling of crate::livecc::LiveCC (§5.1, the live/streaming mode’s pacing-only model). Unlike LiveCC, which only paces PKT_SND_PERIOD from a configured MAX_BW, FileCC is “a hybrid Additive Increase Multiplicative Decrease (AIMD) algorithm” (specs/rules/srt-congestion.md L3294-3295) that also grows/shrinks a congestion window (CWND_SIZE), in two strictly-sequential phases: Slow Start (§5.2.1.1), then Congestion Avoidance (§5.2.1.2) — see Phase. PKT_SND_PERIOD and CWND_SIZE are computed independently of and do not touch LiveCC’s state — the two controllers are additive alternatives, not layered.

§Usage

use srt_runtime::filecc::{FileCc, Phase};
use core::time::Duration;

let mut cc = FileCc::new(0);
assert_eq!(cc.phase(), Phase::SlowStart);

// On each full ACK, feed the receiver-reported rate/RTT samples:
cc.on_ack(Duration::from_millis(10), 16, 5_000, 1_000, Duration::from_millis(50));

// A NAK (loss report) ends slow start on the first loss (rule 4/9):
cc.on_loss(8, 16, 0.5);
assert_eq!(cc.phase(), Phase::CongestionAvoidance);

§Sans-IO contract

Like every other engine in this crate, FileCc never reads a wall clock: FileCc::on_ack takes an explicit now: Duration; FileCc::on_loss and FileCc::on_timeout are driven purely by caller-reported events. FileCc::tick is provided for forward compatibility (mirrors crate::livecc::LiveCC::tick) but this section defines no periodic, time-only state transition beyond the three events already covered.

§Shared state, not redefined here

Per specs/rules/srt-congestion.md’s header note, the following are cross-referenced, not redefined by this module:

  • RC_INTERVAL / SYN = 10 ms — reused from crate::arq::FULL_ACK_PERIOD (specs/rules/srt-arq.md rule 11), which is the same 10 ms value (L3421-3425).
  • The initial RTT estimate (100 ms) — reused from crate::arq::rtt::INITIAL_RTT (specs/rules/srt-arq.md rule 31) before the first ACK sample is fed.

§Two implementation-defined gaps, flagged (not fabricated)

specs/rules/srt-congestion.md explicitly flags two points the draft text leaves unspecified. Both are resolved here with a documented choice:

  1. EWMA weight for RECEIVING_RATE/EST_LINK_CAPACITY smoothing (gap, L3678-3681 / doc’s “Gaps” section). Chosen: this engine does not smooth them itselfFileCc::on_ack takes receiving_rate_pps and est_link_capacity_pps as already-current values and stores them verbatim, exactly the same treatment this section already gives RTT (cross-ref rule/L3409-3411: “receiver-reported and sender-smoothed”, with the smoothing defined elsewhere, not here). This keeps the engine’s formulas directly hand-computable from fed inputs (no hidden internal averaging to reverse-engineer), and leaves the actual smoothing decision to the caller/sender loop — consistent with how RTT is already layered in this crate (crate::arq::rtt::RttEstimator is a separate, explicit component, not folded into the ARQ sender).
  2. Packet-pairs probing mechanics (gap, L3686-3689). Out of scope: this module consumes RECEIVING_RATE/EST_LINK_CAPACITY as inputs: it does not implement the receiver-side inter-arrival-time measurement or packet-pairs probing described in §5.2.1.3 (rules 30-37) that would produce those inputs on a real receiver. That measurement pipeline is receiver-side and is not part of this sender-side congestion-control state machine.

A third, smaller implementation choice not flagged as a spec gap: the DecRandom distribution (uniform in [1, AvgNAKNum], clamped to 1) is given (rule 23), but no source of randomness is specified. This module uses a small internal, deterministic xorshift PRNG instead of pulling in a rand dependency for a no_std crate — true entropy is not required for correctness here, DecRandom only staggers repeat-decrease timing across congestion periods (rule 24). DecRandom is rounded to the nearest whole number (FileCc::next_dec_random, internal): Step 4’s gate (NAKCount == DecCount * DecRandom) compares two integer counters, so a fractional draw makes that equality unsatisfiable after the first check.

§A quirk of the draft’s own Step 4 pseudocode (verified, not a bug)

NAKCount/DecCount reset to 1 at the start of a congestion period and are ONLY incremented again inside Step 4’s own conditional (rule 28) — never unconditionally per NAK. So once the immediate post-reset check (1 == 1*DecRandom) fails — i.e. whenever the drawn DecRandom != 1 — neither counter ever moves again for the rest of the period, and Step 4 goes silent until the next congestion period redraws DecRandom. This is a property of the draft text as transcribed (specs/rules/srt-congestion.md, confirmed against the actual reference implementation, libsrt congctl.cpp, which uses a different formulation — NAKCount % DecRandom == 0 with both counters incrementing on every same-period NAK regardless of outcome — that does not share this one-shot property). Adopting libsrt’s formulation would be a spec-posture departure this crate is not designated for; this implementation stays literal to the curated draft text. See filecc::tests::repeat_decrease_is_a_one_shot_per_period_once_dec_random_exceeds_one.

Structs§

FileCc
SRT File Transfer Congestion Control — sender-side window + pacing state (draft-sharabayko-srt-01 §5.2). See the module doc for the full formula mapping, the sans-IO contract, and the two flagged spec gaps.

Enums§

Phase
FileCC algorithm phase (specs/rules/srt-congestion.md rules 4-5). Slow Start (§5.2.1.1) runs exactly once at the start of a connection; it transitions to Congestion Avoidance (§5.2.1.2) on the first loss, CWND_SIZE exceeding its maximum, or a timeout — and never transitions back (L3323-3326).