Skip to content

feat: QOE #192

Open
avinash-newrelic wants to merge 22 commits intomasterfrom
feat/qoe
Open

feat: QOE #192
avinash-newrelic wants to merge 22 commits intomasterfrom
feat/qoe

Conversation

@avinash-newrelic
Copy link
Collaborator

@avinash-newrelic avinash-newrelic commented Mar 11, 2026

Summary

Introduces a QOE_AGGREGATE action under VideoAction that periodically reports Quality of Experience KPIs during video playback. QoE events are generated at harvest time and injected directly into the harvest batch, independent of the regular event buffer.

KPIs Tracked

Attribute Type Description
startupTime Long (ms) Time from CONTENT_REQUEST to CONTENT_START, minus pre-roll ad time (wall-clock totalPreRollAdTime), clamped to >= 0
peakBitrate Long (bps) Highest observed contentBitrate during the session
averageBitrate Long (bps) Time-weighted average of contentBitrate during active playback only; non-play time (pause, buffer, seek) is excluded
totalPlaytime Long (ms) Total content playtime excluding pause, buffer, and seek; computed in real-time at harvest
totalRebufferingTime Long (ms) Total duration of all CONTENT_BUFFER_END events except the first one in the session (initial load), regardless of bufferType
rebufferingRatio Double (%) (totalRebufferingTime / totalPlaytime) * 100, recomputed at harvest with real-time totalPlaytime
hadStartupError Bool true if a CONTENT_ERROR occurred before CONTENT_START
hadPlaybackError Bool true if a CONTENT_ERROR occurred after CONTENT_START

How It Works

  • At CONTENT_REQUEST, the tracker registers a QoE provider on the harvest manager — ensuring QoE is sent even if CONTENT_START never happens (e.g., startup error)
  • QoE is decoupled from the VideoAction event buffer — collected independently on each harvest cycle based on a configurable multiplier, even when the batch is empty
  • A snapshot-based dirty check ensures QoE is only sent when KPI values have actually changed
  • totalPlaytime is computed in real-time at harvest using a read-only peek, avoiding stale values between events
  • At CONTENT_END, the final QoE event is built eagerly while tracker state is still valid and enqueued for the next harvest, avoiding race conditions
  • QoE context attributes use a strict whitelist from the last content event
  • KPI attribute keys are unprefixed and centralized in a single list used by the aggregator, harvest manager, and dirty check
  • Startup time: Used wall-clock ad duration (totalPreRollAdTime) instead of ad playtime for accurate startup KPI
  • Average bitrate: Paused bitrate timer during non-play states (pause, buffer, seek) so only active playback is counted

Fixes

  • viewId: Moved viewIdIndex increment from sendStart to sendRequest so post-roll ads share the same viewId as their content

Configuration

Option Type Default Description
qoeAggregateEnabled Bool false Master switch for QoE aggregate events
qoeAggregateIntervalMultiplier Integer 1 QoE evaluated every N harvest cycles

avinash-newrelic and others added 22 commits March 11, 2026 14:00
Add QOE_AGGREGATE action name and kpi.* attribute constants to
NRVideoDefs.h. Add qoeAggregateEnabled, qoeAggregateIntervalMultiplier,
and isQoeAggregateEnabled to NRVAVideoConfiguration for controlling
QoE event frequency relative to harvest cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce NRQoEAggregator to compute Quality of Experience metrics:
- Time-weighted average bitrate and peak bitrate tracking
- Rebuffering ratio from connection-type buffer events
- Startup time (timeSinceRequested minus pre-roll ad duration)
- Startup and playback failure flags

Uses a static dispatch table for action routing and @synchronized
for thread safety. Reads fully-assembled attributes from the tracker
pipeline (no parallel state or direct player API calls).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add qoeEventProvider callback and enqueueFinalQoeEvent to
NRVAHarvestManager. QoE events are generated at harvest time
(not heartbeat time) and injected directly into the batch array,
bypassing the crash-safe buffer.

Key design:
- Provider block called only when batch contains VideoAction events
- Multiplier formula (count-1)%N controls QoE frequency
- enqueueFinalQoeEvent accepts pre-built events from sendEnd to
  avoid race conditions with tracker thread cleanup
- pendingFinalQoe takes priority and auto-clears the provider

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NRVideoTracker changes:
- Initialize QoE aggregator when feature is enabled
- Snapshot content event attributes in preSendAction for QoE reuse
- Register qoeEventProvider block at sendStart (content path)
- Build final QoE eagerly at sendEnd and enqueue via
  enqueueFinalQoeEvent to avoid race with aggregator reset
- buildQoeEvent composes attributes from lastContentEventAttributes
  (filtering timeSince*/bufferType) overlaid with KPI metrics

NRVAVideo facade:
- Add setQoeEventProvider and enqueueFinalQoeEvent pass-throughs
- Add isQoeAggregateEnabled for tracker feature check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace dead CDN URLs for Sintel and Airshow with working streams:
- Sintel -> Apple bipbop HEVC test stream (~30 min adaptive VOD)
- Airshow -> Akamai live HLS test stream

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ll others

The previous implementation only counted "connection"-type buffers. Android and
JS SDKs use a simpler approach: skip the first CONTENT_BUFFER_END (initial
buffer) and accumulate all subsequent ones regardless of bufferType. This aligns
rebuffering metrics across all three platforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…upError/hadPlaybackError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ering

Use explicit bufferType check rather than skipping the first CONTENT_BUFFER_END
by position. This is more correct — it skips the actual initial buffer type
regardless of event ordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Align with Android and JS SDKs which emit unprefixed attribute names
(e.g. startupTime, not kpi.startupTime). QOE_PREFIX set to empty string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… the same viewId as their content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blocklist approach with strict whitelist of 19 context attributes
carried over from the last content event. Removes 17 attributes not
produced by the iOS video core (asn, device, page, etc.).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d centralized KPI keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tegration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…by session flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…on attributes to QoE whitelist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
47 tests covering NRQoEAggregator KPI computation (startup time,
bitrate tracking, rebuffering skip-first logic, error flags, reset)
and NRVAHarvestManager QoE integration (multiplier gate, dirty check,
pending final QoE priority).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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