Skip to content

feat(qoe): add 7 new aggregate KPIs#216

Open
skatti97 wants to merge 5 commits into
release/22-june-2026from
qoe-v1.1
Open

feat(qoe): add 7 new aggregate KPIs#216
skatti97 wants to merge 5 commits into
release/22-june-2026from
qoe-v1.1

Conversation

@skatti97

@skatti97 skatti97 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Adds 7 new KPIs to the QOE_AGGREGATE event and bumps qoeAggregateVersion to 1.1.0 for cross-platform parity with video-agent-android.

KPI Type What it measures
avgDownloadRate Long (bps) Mean network download throughput across distinct samples
minDownloadRate Long (bps) Lowest download throughput observed (omitted when no sample)
maxDownloadRate Long (bps) Highest download throughput observed (omitted when no sample)
totalSwitchUps Long Count of CONTENT_RENDITION_CHANGE events where quality went up
totalSwitchDowns Long Count of CONTENT_RENDITION_CHANGE events where quality went down
totalPauseTime Long (ms) Total time the user spent paused this session
totalRenditions Long Distinct video renditions (W×H pairs) observed this session

How it's wired

All 7 KPIs live entirely on NRQoEAggregator — no new tracker-side state. The aggregator observes the assembled attribute dict that already flows through processAction:, mirroring the existing pattern for totalRebufferingTime.

Three design choices worth knowing:

  1. avgDownloadRate skips consecutive duplicate samples. AVPlayer's accessLog.events.lastObject returns the same entry between real network events, so naïve every-event observation would bias the mean toward whichever rate was sticky during the longest idle window. The aggregator only accumulates on change — true mean over distinct download events.

  2. totalPauseTime consumes timeSincePaused from the existing timeSince table instead of running its own clock. Mid-pause harvest still reports correctly via an open-segment snapshot in generateAggregateAttributes — written to a local var, never back into self.totalPauseTime, so the matching RESUME doesn't double-count.

  3. totalRenditions keys on width × height, not bitrate. Pixel-area keying is robust to streams that label distinct resolution variants with the same bitrate. Initial rendition is seeded at CONTENT_START because NRTrackerAVPlayer.checkRenditionChange (lines 351-354) silently stamps the first observation without firing CONTENT_RENDITION_CHANGE.

Auto-wired:

  • Dirty check iterates NRVAAllKPIKeys(), so adding entries there picks up new KPIs for duplicate suppression with zero comparator changes.
  • buildQoeEvent does addEntriesFromDictionary, so any new KPI reaches the wire automatically.
  • [aggregator reset] is already called on sendEnd, so per-view reset works for free.

Test plan

  • Unit: 61/61 passing (was 31; +30 new)
  • Spec-required cases for totalPauseTime: closed-only, mid-pause snapshot, no-double-count after resume
  • Distinct-count / dedup / seed-from-start / invalid-rejected for totalRenditions
  • Consecutive-duplicate skip + non-consecutive accept for avgDownloadRate
  • Reset coverage — every new accumulator clears on [aggregator reset]
  • New keys present in NRVA_DEBUG_LOG at harvest time
  • Manual: launch Examples/iOS/SimplePlayerWithAds, pause + resume + change quality, confirm all 7 KPIs surface in the QOE_AGGREGATE event
  • NRQL parity check against Android sample app for the same scenario

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant