# MOS, Jitter & Packet Loss

> Read the three voice-quality metrics that FirstByte Voice computes in the media plane and writes into every CDR, and the thresholds that matter.

Every call that FirstByte Voice carries leaves three quality numbers: an
estimated **MOS** (Mean Opinion Score), the **interarrival jitter** in
milliseconds, and the **packet-loss percentage**. The RTP media plane computes
them while the call runs. An external probe does not sample them. At teardown,
the system folds them into the call's CDR, keyed by `call_sid`. This page
tells you what each number means and how the system computes it. It also
tells you the line between a healthy call and a call your customer noticed.

## What you get, at a glance

  - **MOS** — Estimated listening quality from 1.0 to 4.5 (ITU-T G.107 E-model). Higher
    is better. A clean G.711 leg tops out near **4.4**.
  - **Jitter** — RFC 3550 interarrival jitter in **ms**. It shows how unevenly packets
    arrived. Lower is better.
  - **Loss** — The percentage of expected RTP packets that never arrived. Lower is
    better.

All three come **from the same per-packet accounting** on the caller's media
leg. Loss comes from sequence-number continuity. Jitter comes from arrival
timing. Thus the metrics move together and explain each other. A MOS drop
almost always traces back to a spike in one of the other two.

## MOS — the E-model estimate

The MOS reported here is **not** a subjective survey score. It is a *computed*
estimate from the ITU-T **G.107 E-model**. The model derives a
transmission-rating factor **R**, then maps R to an estimated MOS (MOS_CQE).
FirstByte Voice computes it in the media plane, specialized for the G.711
codec on phone legs.

The model builds R when it subtracts impairments from a clean baseline:

```
R = R0 − Id − Ie_eff
```

- **R0 ≈ 93.2**. The baseline for a wired VoIP path (signal/noise, no
  advantage factor).
- **Ie_eff**. The *equipment/effective impairment*. It folds the codec plus
  the effect of packet loss. G.711 with packet-loss concealment is loss-robust
  (a G.113 packet-loss robustness factor of 25.1). Thus small loss costs
  little R. Loss past a few percent costs much more.
- **Id**. The *delay/jitter impairment*. The engine does not measure one-way
  mouth-to-ear delay directly. It approximates delay as a fixed path component
  plus the jitter-buffer depth (roughly the measured jitter). Past the classic
  **177.3 ms** delay knee, Id climbs steeply.

The standard E-model cubic then maps R to MOS. The result is **clamped to
[1.0, 4.5]**. The value 4.5 is the E-model's practical ceiling. G.711 already
spends some R on codec and fixed delay. Thus the realistic clean-call maximum
that you will see is about **4.4**, not 4.5.

> **NOTE:**
> MOS here is an **estimate from loss and jitter**. It is not a measured
>   opinion score and not a per-packet PESQ/POLQA analysis. Treat it as a fast,
>   always-on health signal that ranks calls and flags regressions. Do not treat
>   it as a lab-grade audio-quality verdict.

### How to read the MOS number

<table>
  <thead>
    <tr><th>MOS</th><th>Perceived quality</th><th>What it means for you</th></tr>
  </thead>
  <tbody>
    <tr><td><b>4.3 – 4.4</b></td><td>Excellent</td><td>Effectively toll-quality. The network is not your problem.</td></tr>
    <tr><td><b>4.0 – 4.3</b></td><td>Good</td><td>Normal, healthy calls. Most traffic should sit here.</td></tr>
    <tr><td><b>3.6 – 4.0</b></td><td>Fair</td><td>Users notice occasional artifacts. Watch the trend.</td></tr>
    <tr><td><b>3.1 – 3.6</b></td><td>Poor</td><td>Audible degradation. Investigate the trunk / access network.</td></tr>
    <tr><td><b>&lt; 3.1</b></td><td>Bad</td><td>Complaints are likely. Escalate. The cause is usually heavy loss or a saturated path.</td></tr>
  </tbody>
</table>

The values below are example points from the engine's own E-model for a G.711
leg. Use them to calibrate what a given loss/jitter pair does to MOS:

<table>
  <thead>
    <tr><th>Loss</th><th>Jitter</th><th>≈ MOS</th></tr>
  </thead>
  <tbody>
    <tr><td>0%</td><td>5 ms</td><td>4.4</td></tr>
    <tr><td>1%</td><td>20 ms</td><td>4.3</td></tr>
    <tr><td>3%</td><td>40 ms</td><td>4.1</td></tr>
    <tr><td>5%</td><td>60 ms</td><td>3.8</td></tr>
    <tr><td>10%</td><td>100 ms</td><td>3.3</td></tr>
  </tbody>
</table>

## Jitter — RFC 3550 interarrival jitter

Jitter is the smoothed variation in packet arrival timing. The engine computes
it exactly per **RFC 3550 §6.4.1**. For each packet, the engine:

1. Takes the *transit time*: the arrival clock minus the packet's RTP
   timestamp, both in 8 kHz G.711 ticks.
2. Takes the absolute change in transit from the previous packet.
3. Folds that change into a running estimate with the RFC's **1/16 gain**:

```
J += (|D(i-1, i)| − J) / 16
```

This gives a stable exponential average. The average reacts to sustained
variation but ignores single outliers. The engine accumulates it in timestamp
ticks and **reports it in milliseconds** (ticks ÷ 8 at the 8 kHz clock).

Jitter matters because the receive-side buffer must hold packets long enough
to absorb it before playout. High jitter forces a deeper buffer, which adds
mouth-to-ear delay. The E-model then charges that delay against R. If the
buffer cannot keep up, late packets are dropped and become **loss**.

<table>
  <thead>
    <tr><th>Jitter</th><th>Read</th></tr>
  </thead>
  <tbody>
    <tr><td><b>&lt; 20 ms</b></td><td>Healthy. The jitter buffer absorbs it invisibly.</td></tr>
    <tr><td><b>20 – 50 ms</b></td><td>Acceptable, but it adds buffering delay. Watch it on latency-sensitive AI turns.</td></tr>
    <tr><td><b>&gt; 50 ms</b></td><td>The buffer is under pressure. Expect late-packet loss and a MOS dip.</td></tr>
  </tbody>
</table>

## Packet loss — sequence continuity

The engine measures loss from RTP **sequence-number continuity**, not from a
timer. It anchors on the first packet's sequence number. It tracks the highest
sequence seen (with the 16-bit wraparound handled). At call close it computes:

```
loss% = 100 × (expected − received) / expected
```

Here `expected` is the span of sequence numbers between the first and last
packet. A packet that arrives too late for playout (past the reorder buffer's
depth) counts as lost as well. From the listener's point of view, that packet
never arrived. The engine drops duplicates. Duplicates do not inflate the
received count.

<table>
  <thead>
    <tr><th>Loss</th><th>Read</th></tr>
  </thead>
  <tbody>
    <tr><td><b>&lt; 1%</b></td><td>Good. G.711 packet-loss concealment hides it.</td></tr>
    <tr><td><b>1 – 3%</b></td><td>Noticeable clipping on speech onsets. Concealment works hard.</td></tr>
    <tr><td><b>3 – 5%</b></td><td>Clearly audible dropouts. MOS falls below "good."</td></tr>
    <tr><td><b>&gt; 5%</b></td><td>Poor. The cause is usually a congested or lossy access network / trunk.</td></tr>
  </tbody>
</table>

## Where the numbers live

The engine computes the three metrics cheaply per packet on the caller's media
leg. When the call clears, it folds them into `loss%`/`jitter_ms`/`MOS`. It
writes them to the call's **per-call media quality record** (keyed by
`call_sid`) and into the **CDR** next to the disconnect cause and duration.
Thus you can sort, filter, and alert on quality per call, per trunk, or per
tenant. You use the same reporting surface that shows call outcomes. You do
not need a separate capture step or a packet trace for the common case.

> **NOTE:**
> The **RTP media plane** computes the quality fields. The fields describe the
>   leg that the engine terminates (the PSTN/SIP caller leg). For browser and
>   app legs, the media rides Opus over our QUIC transport. There, the browser's
>   own jitter buffer and encoder govern arrival timing. The E-model MOS here is
>   the G.711 telephony-leg estimate.

## Troubleshooting a low MOS

Start from the MOS. Then use jitter and loss to find the cause.

  1. **Read jitter and loss together**
One or both always explain a low MOS. High **loss** with low jitter points
    at a lossy link or a policed/rate-limited path. High **jitter** with loss
    that appears only under load points at congestion or an undersized receive
    buffer.
  2. **Check whether it is one call or a population**
A single bad call usually comes from the far end's access network. A
    *fleet-wide* shift across a trunk or region is on your side: a saturated
    uplink, a flapping path, or a misconfigured trunk. Group the CDR quality
    fields by trunk and by tenant before you touch anything.
  3. **Check the codec and path**
Check that the leg negotiated the codec that you expect (see
    [Codecs](/modalities/voice/transport-telephony/codecs)). A transcoded or
    mismatched path adds impairment, and the E-model will show it.
  4. **Pull a trace only if you need packet-level detail**
When the aggregate numbers are not enough, capture the SIP/RTP exchange
    for the specific `call_sid`. See
    [SIP & RTP debugging](/modalities/voice/observability/sip-rtp-debugging).

### Symptom → cause → fix

<table>
  <thead>
    <tr><th>Symptom</th><th>Likely cause</th><th>Fix</th></tr>
  </thead>
  <tbody>
    <tr>
      <td>MOS ~3.x, loss &gt; 3%, jitter low</td>
      <td>Lossy or policed access network / trunk</td>
      <td>Check the trunk uplink and provider path. Loss is the dominant R impairment.</td>
    </tr>
    <tr>
      <td>MOS dips only at busy hour</td>
      <td>Congestion. Jitter climbs, then late-packet loss follows.</td>
      <td>Provision headroom on the path. Check that QoS/DSCP marking is honored end to end.</td>
    </tr>
    <tr>
      <td>Jitter high, loss near zero, MOS still fair</td>
      <td>A deep buffer absorbs the bursty arrival</td>
      <td>This is acceptable for voice, but the added delay hurts AI turn latency. See below.</td>
    </tr>
    <tr>
      <td>Great MOS but callers report echo/robotic audio</td>
      <td>Not a network metric. This is an echo/AEC or codec issue.</td>
      <td>MOS does not catch it. Check the leg's codec and see the one-way/quality troubleshooting page.</td>
    </tr>
    <tr>
      <td>MOS 1.0 on many short calls</td>
      <td>Too few packets to measure, or one-way media</td>
      <td>Check that two-way RTP flowed at all. See the one-way-audio troubleshooting page.</td>
    </tr>
  </tbody>
</table>

> **WARNING:**
> MOS is a **listening-quality** estimate from loss and jitter. It does not
>   see echo, gain problems, or a wrong-codec artifact. There is no acoustic
>   echo cancellation in the media path (the runtime relies on the browser's AEC
>   and VAD gating). A perfect MOS with a bad-sounding call means the problem is
>   not the network. Start with
>   [One-way & degraded audio](/modalities/voice/troubleshooting/one-way-audio).

## Related

  - **[Observability overview](/modalities/voice/observability/overview)** — Everything a call leaves behind: events, CDRs, recordings, traces.
  - **[Latency breakdown](/modalities/voice/observability/latency-breakdown)** — Where jitter-buffer depth shows up as turn latency on AI calls.
  - **[SIP & RTP debugging](/modalities/voice/observability/sip-rtp-debugging)** — Drop to packet-level captures when the aggregate numbers are not enough.
  - **[High latency](/modalities/voice/troubleshooting/high-latency)** — Diagnose slow turns, which includes the buffering added to absorb jitter.
