Skip to content
Docs/SSAI/VOD Mode

VOD SSAI Mode

Video-on-demand ad insertion with full seeking support, pre-stitched manifests, and deterministic quartile tracking for accurate measurement.

VOD vs Live SSAI

VOD SSAI differs fundamentally from live: all ad decisions are made at session creation time, manifests are stable throughout playback, and seeking requires special handling for accurate measurement.

AspectVODLive
Ad DecisioningAt session creation (all breaks)Just-in-time (per break)
Manifest StabilityImmutable after creationSliding window updates
SeekingFull support requiredDVR window only
Quartile TrackingDeterministic (seek-aware)Sequential
Session TTLFixed (content duration + buffer)Refresh-on-access

VOD Marker Processing

VOD content often originates from harvested live streams that contain preserved ad markers. The VODMarkerProcessor detects and normalizes these markers from multiple sources:

HLS Marker Types

  • #EXT-X-DATERANGE - Apple interstitials
  • #EXT-X-CUE-OUT / #EXT-X-CUE-IN - Standard cues
  • #EXT-OATCLS-SCTE35 - SCTE-35 in comments
  • #EXT-X-CHAPTER - Chapter markers

DASH Marker Types

  • EventStream with SCTE-35
  • EventStream with DVB-DASH cues
  • • Period boundaries
  • • Custom event schemes

Processing Pipeline

  1. Parse Origin Manifest: Extract all ad markers with timeline positions
  2. Normalize Markers: Convert all marker types to unified AdBreakCue format
  3. Resolve Pairs: Match start/end markers, calculate break durations
  4. Validate Positions: Ensure markers don't overlap, are within duration
  5. Generate Breaks: Create ad break list with positions and durations

Manifest Stability

VOD manifests are generated once at session creation and remain immutable throughout playback. This is critical for seeking and progress tracking.

HLS VOD Requirements

  • #EXT-X-ENDLIST tag required at end
  • #EXT-X-PLAYLIST-TYPE:VOD present
  • All segments listed from start to end
  • Complete DATERANGE markers for all ad breaks

DASH VOD Requirements

  • type="static" MPD
  • mediaPresentationDuration set
  • All periods present from start
  • Complete EventStream for ad metadata

Common Issue: Missing ENDLIST

VOD playlists without #EXT-X-ENDLIST cause players to poll for updates indefinitely. The SSAI system enforces ENDLIST presence for VOD mode and validates during manifest generation.

Seeking Behavior

VOD SSAI must handle seeking correctly. Users may seek forward through ads, seek backward to rewatch content, or jump directly to specific points.

Seek Into Ad Break

When seeking into an ad break, playback starts from the ad at that position. Impression is credited when at least 2 seconds of the ad are viewed.

Seek Past Ad Break

When seeking past an ad break (skipping), the ads are not credited. Optional: force ad viewing on first pass (requires SDK integration).

Seek Backward

Seeking backward does not re-fire already-credited impressions. Quartiles are deduplicated per slot per session.

Timeline Mapping

Original Timeline (with ad markers):
[Content: 0-30m] [Break @ 30m, 30s] [Content: 30m-60m] [Break @ 60m, 30s] [Content: 60m-90m]

Stitched Timeline (player sees):
[Content: 0-30m] [Ad Pod 1: 30s] [Content: 30m30s-60m30s] [Ad Pod 2: 30s] [Content: 61m-91m]

Total Duration: 90m content + 1m ads = 91m

Seek to 45m → Player position 45m30s (after first ad break)
Seek to 70m → Player position 71m (after both ad breaks)

Deterministic Quartile Tracking

The VODReconciler handles quartile tracking for seekable content with configurable behavior for missed quartiles.

Quartile Positions

Calculated by calculateQuartilePositions(duration):

EventPositionProgress
Impression0s (start)0%
firstQuartileduration × 0.2525%
midpointduration × 0.5050%
thirdQuartileduration × 0.7575%
completeduration (end)100%

Reconciler Config

From VODReconcilerConfig:

  • timingToleranceSeconds: 0.5s
  • fireOnSeek: true (fire missed on seek)
  • maxAgeMinutes: 120 (2 hour retention)

Seek Handling

When fireOnSeek is enabled:

  • • Seek forward: missed quartiles are fired
  • • Seek backward: no duplicate fires
  • • Dedupe via state.fired Set

Session Reconciliation

At session end, the VODReconciler performs final reconciliation to identify discrepancies between expected and actual tracking events.

Reconciliation Output

{
  "podId": "pod-abc123",
  "slots": [
    {
      "slotId": "slot-1",
      "creativeId": "cr-xyz",
      "expected": ["impression", "firstQuartile", "midpoint", "thirdQuartile", "complete"],
      "received": ["impression", "firstQuartile", "midpoint"],
      "missing": ["thirdQuartile", "complete"],
      "duplicates": [],
      "status": "partial"
    }
  ],
  "discrepancies": [
    {
      "type": "missing_quartile",
      "slotId": "slot-1",
      "event": "thirdQuartile",
      "reason": "user_seeked_past"
    }
  ]
}

Pre-roll Handling

VOD pre-rolls have special considerations since they appear before any content:

Immediate Insertion

Pre-roll ads are at position 0 in the stitched manifest

Seek Protection

Optional: SDK can prevent seeking until pre-roll complete

Deep Link Support

Deep links to specific content positions still show pre-roll first

VOD Session Lifecycle

Session TTL Calculation

// VOD session TTL = content duration + buffer
const sessionTTL = contentDuration + (24 * 60 * 60); // +24 hours buffer

// Example: 90 minute movie
// TTL = 90 minutes + 24 hours = 25.5 hours

// Session expires after TTL regardless of activity
// (Unlike live which refreshes on manifest access)

Related Documentation