⚠ MOCK · Lighthouse Briefings · thresholds here are illustrative placeholders until labelled REAL.
← Lighthouse Briefings · analysis · 2026-06-19
analysis kVoipSummaryStat · V4

VoIP V4 Bad-Call Definition · part1 Criteria

v4 bad-call judgment on kVoipSummaryStat: hybrid engine + deliberate-action exclusion + two-layer dedup, inheriting v2's A/V/C+R skeleton. All thresholds pending calibration.
🔴 All thresholds are TBD. Every numeric threshold below (ratio %, duration ms/s, SDK-score cutoffs, counts) is a placeholder pending calibration; the (prov. X) in parentheses is a hypothesis only and must not be used as a production rule. Calibrate against real calls (regress vs v2) first.

00 What & why

Defines whether a call has a quality problem, from the V4 SDK kVoipSummaryStat end-of-call instrumentation (one doc per leg). For post-hoc analysis, the quality dashboard, alerts, and v2-vs-v4 comparison. Inherits v2's A/V/C + R numbering, swapping only the underlying fields and judgment engine, and adds v4-native capabilities (A06 / V07 / C05–C07 / S class / R33–R35).

Three levers: ① the SDK's own quality verdicts (aqa/vqs/nqs/oqs, 1–5) cross-check / backstop; ② event streams glc/nan turn "share / single-event duration" from estimate into exact; ③ the stl state timeline separates real faults from deliberate user actions (v2's biggest blind spot).

00 Judgment engine: hybrid

Event streams glc/nan/lma are the (primary) tunable, attributable triggers; SDK scores cross-check and backstop. Confidence tiers:

TierCondition
High (confirmed)≥1 event trigger and matching SDK score ≤ 🔴待定(prov. 2)
Mid (likely)event trigger only, or SDK score ≤ 🔴待定(prov. 2) only
Low (review)borderline SDK score = 🔴待定(prov. 3) only

Dashboard default = High + Mid; alerts use High only.

00·5 Pre-filter: exclude deliberate user actions new in v4

v2 mis-flagged deliberate mute / hold / camera-off as "no audio / black screen". v4 uses stl to strip deliberate actions before judging — the single biggest accuracy gain over v2.

ActionSignalEffect
Mute micstl.ty=12 (AudioMute) v=1subtract from A03 silence
Camera offstl.ty=11 (VideoMute) v=1subtract from V03 black-screen
Call on holdstl.ty=9 (SessionOnHold) v≠0subtract from A01/C01 share

00·6 Two layers · symptom dedup · dual-condition share

Fixing double-counting: one network event often lights up "loss-share + interruption + freeze" at once, so we split two layers:

LayerMembersFlags a bad call?
Symptom layer (user-perceived)A·V·C01/C02/C04·S✅ flags + deduped
Network-condition layer (upstream cause)🔹C03/C05/C06/C07 · R01–R12❌ attribution only

Bad call = OR over the symptom layer; roll up two legs by rideither leg bad → call bad.

Primary-symptom dedup: when several fire, take one primary by priority (rest are secondary, not re-counted in the problem distribution). Priority (high→low): ① C01/C02 interruption → ② A03/V03 silence·black → ③ A02/V05 single long freeze → ④ A01/V01 freeze share → ⑤ V02/V04 blur·V07 low-fps·A06 degradation → ⑥ A04/V06 slow first frame·C04 short call.

Dual-condition share (fixes long-call dilution): every share trigger becomes share > X% OR cumulative duration > N s, either one fires (X, N both 🔴TBD). Plus A02/V05/C02 single-event backstops.

01 Audio problems (A)

denominator = vod voipDuration (s)

#ProblemTrigger (thresholds 🔴TBD)SDK checkFields
A01High audio-freeze shareΣ glc.pem[ty=7]/(vod×1000) > 🔴待定(prov.5%) OR cum > 🔴待定(prov.N s)aqa ≤ 🔴待定(prov.2)glc=glitch array (ty=7 audio freeze, pem=dur ms); vod=in-call dur (s); aqa=audioQualityScoreAvg (1–5)
A02Single freeze too longmax(glc.pem[ty=7]) > 🔴待定(prov.10000ms)glc.pem=single freeze ms; aft=audioFreezeThreshold ms
A03aOur side silent (this leg)after removing this leg's mute: arv=0glc.ty=1aps ≤ 🔴待定(prov.2)arv=mic capture vol (0=silent); glc.ty=1=MicCaptureFailed; aps=audioPlayScore
A03bPeer silent (needs join)apv=0 and join rid peer not muted; downgrade if peer leg missingaps ≤ 🔴待定(prov.2)apv=playback vol (0=peer silent); rid=roomId
A04Slow first audio framefaf > 🔴待定(prov.3000ms)faf=firstAudioFrameDelay ms
A05Low audio quality (backstop)aqa ≤ 🔴待定(prov.2) OR aqm = 🔴待定(prov.1)aqm=audioQualityScoreMin
A06Degraded but no freezeapo < 🔴待定(prov.80%) and alp+adp+asp+atp highaqa ≤ 🔴待定(prov.2)apo=original-packet %; alp/adp/asp/atp=late/dup/shrink/stretch %

02 Video problems (V)

denominator = vpd videoPlayDuration (s); only when vpd>0

#ProblemTrigger (thresholds 🔴TBD)SDK checkFields
V01High video-freeze sharevfp > 🔴待定(prov.5%) (SDK-computed) ∥ glc method OR cum > 🔴待定(prov.N s)vqs ≤ 🔴待定(prov.2)vfp=videoFreezePercent (=vfd/vpd); vpd=render dur (s); vqs=videoQualityScore
V02Blur / low quality shareresolution stl.ty=10 v<🔴待定(prov.360) share > 🔴待定(prov.5%) ∥ veq > 🔴待定(prov.35, per-codec)vqs ≤ 🔴待定(prov.2)stl.ty=10=resolution (v=px height); veq=encoder QP (lower=better); vec=encoder
V03Black screenafter removing camera-off: vbs share/dur over 🔴待定glc.ty=2vbs=blackscreen total ms; glc.ty=2=CameraCaptureFailed
V04Single blur too longsingle low-res span > 🔴待定(prov.10000ms)stl.ty=10=resolution event
V05Single video freeze too longmax(glc.pem[ty=6]) > 🔴待定(prov.10000ms)glc.pem(ty=6)=single video freeze ms
V06Slow first video framefvf > 🔴待定(prov.5000ms)fvf=firstVideoFrameDelay ms
V07Low fps / jankvpf < 🔴待定(prov.12fps) ∥ vjp highvqs ≤ 🔴待定(prov.2)vpf=render fps; vjp=jank %

03 Connection problems (C)

denominator = vod (s) · 🔹 = network-condition layer (attribution only, does not flag a bad call)

#ProblemTrigger (thresholds 🔴TBD)SDK checkFields
C01High interruption shareΣ glc.pem[ty=5]/(vod×1000) > 🔴待定(prov.5%) OR cum > 🔴待定(prov.N s)nqs ≤ 🔴待定(prov.2)glc(ty=5 Connecting media interrupt); nqs=networkQualityScore
C02Single interruption too longmax(glc.pem[ty=5]) > 🔴待定(prov.10000ms)glc.pem(ty=5)=single interrupt ms
C04Short call abnormal dropiac=1 AND vod < 🔴待定(prov.10s) AND aer≠0iac=isAccepted; aer=abnormalEndReason (≠0=abnormal)
🔹C03High-latency shareΣ nan.pem[ty=3]/(vod×1000) > 🔴待定(prov.5%)tra > 🔴待定(prov.300)nan=anomalies (ty=3 RTT spike); tra=transportRTTAvg ms
🔹C05Frequent reconnectrcc > 🔴待定(prov.3)rcc=ICE reallocate count
🔹C06High packet-loss shareΣ nan.pem[ty=1 or 2]/(vod×1000) > 🔴待定(prov.5%)sla/rla > 🔴待定(prov.5)nan(ty=1 up / 2 down loss); sla/rla=up/down loss avg %
🔹C07BWE crashnan.ty=6 (BWECrash)🔴待定(prov.1) ∥ ekb < 🔴待定(prov.500)nan.ty=6=BW dropped >50%; ekb=est. kbps

04 Setup failures (S) counted separately from quality

Only calls with intent where media never established; normal no-answer / reject / cancel (rsn=3/4/8) are funnel, not bad calls. ⚠️ Depends on the full rsn/aer enum (FootPrintSummaryReason), not yet available.

#ProblemTriggerFields
S01Media setup failediac=1 but ict null → media never came upict=media-setup time ms (null=fail)
S02Ring / push failedinvited but irg=0itr timeoutirg=isRinging; itr=invite→ring ms
S03Signaling errorrsc error code ∥ pud emptyrsc=responseCode; pud=peerUid (empty=never paired)
S04Slow connectict > 🔴待定(prov.3000ms) (cause = R31)ict=media-setup time ms

05 Root causes (R)

nan.ty maps straight to loss/RTT/jitter events; lma inline-splits local / peer / relay; stl supplies network/device/behaviour context. R uses "did it appear" for attribution; only symptom triggers use share thresholds, avoiding double-counting.

Network

#CauseTrigger (🔴TBD)Fields
R01Uplink lossnan.ty=1sla > 🔴待定(prov.5)nan(ty=1 send-loss spike); sla=send loss avg %
R02Downlink lossnan.ty=2rla > 🔴待定(prov.5)rla=recv loss avg %
R03Relay-link lossrla high but lma.mdl/lma.pul low → relay segmentlma=last-mile (mdl=my dl loss, pul=peer ul loss)
R04/05Up/down latencylma.mur/lma.mdr high ∥ nan.ty=3lma.mur/mdr=my last-mile up/down RTT ms
R06Relay/cross-region latencytra > 🔴待定(prov.300) but lma segments lowtra=transportRTTAvg ms
R07High jitternan.ty=4rja > 🔴待定(prov.100)rja=recv jitter avg ms
R08/09Net switch / frequentstl.ty=0 changes ≥1 / >3stl.ty=0=NetworkType (0 none/5 WiFi…)
R10VPNstl.ty=1 v=1stl.ty=1=VPN event
R11Insufficient bandwidthekb < 🔴待定(prov.500) ∥ okb < 🔴待定(prov.300) ∥ nan.ty=6ekb=est. kbps; okb=actual uplink kbps
R12Weak networknqs ≤ 🔴待定(prov.2)nqs=networkQualityScore (1–5)

Device

#CauseTrigger (🔴TBD)Fields
R13Mic not workingarv=0glc.ty=1mpm=0arv=mic vol; mpm=mic permission
R14Camera not workingglc.ty=2cpm=0cpm=camera permission
R15High CPUvet/vdt > 🔴待定(prov.30)vet/vdt=per-frame enc/dec time ms
R16Low memorylme=1 OR amm < 🔴待定(prov.200)lme=low-mem warn; amm=avail RAM MB
R17Thermaltts ≥ 🔴待定(prov.2) ∥ stl.ty=4 v≥🔴待定(prov.2)tts=thermalStatus (0..4); stl.ty=4=Thermal
R18/19Low battery / saverbtl < 🔴待定(prov.10) / bsm=1btl=battery %; bsm=power saver
R20Low-end deviceccr ≤ 🔴待定(prov.4) AND tmm < 🔴待定(prov.3000)ccr=cores; tmm=total RAM MB
R21Bluetooth routeble=1stl.ty=5 flappingble=BT audio; stl.ty=5=AudioRoute

Codec · Peer · Server · v4-new

#CauseTrigger (🔴TBD)Fields
R22/23Slow enc/decvet/vdt > 🔴待定(prov.30)vet/vdt=per-frame enc/dec ms
R24Encoder downgradeveq up ∥ stl.ty=10.r setveq=QP; stl.ty=10.r=QualityLimitation
R25Poor FEC/PLCapo low while alp/adp highapo=original-packet %
R26Peer uplink losslma.pul > 🔴待定(prov.5) (inline) ∥ join peer slalma.pul=peer ul loss % (no join)
R27Peer device weakjoin rid peer vet highrid=roomId
R28Peer net downtbr=0 AND tbs>0lma.pdl very hightbr/tbs=bytes recv/sent; lma.pdl=peer dl loss %
R29Peer app backgroundjoin peer stl.ty=7 v=1stl.ty=7=AppState (v=1 bg)
R30Bad server picktip region ≠ both AND tra > 🔴待定(prov.200)tip=relay IP; tra=transport RTT
R31Slow ICEict > 🔴待定(prov.3000)ict=media-setup ms
R32Server timeout dropaer≠0 AND rsn = timeout codeaer=abnormalEnd; rsn=reason enum
R33Deliberate action (non-fault)stl.ty=11/12 Mute ∥ ty=9 Holdused to exclude false flags (see 00·5)
R34BWE crashnan.ty=6 (BWECrash)explains sudden freeze / downscale
R35Local app backgroundstl.ty=7 v=1 overlaps glc.ty=1/2explains background capture pre-emption

06 Conventions

TODO: calibrate all thresholds (regress vs v2) · rsn/aer enum table (hard blocker for S class) · join S class to signaling/push tables · per-codec QP calibration.