- CSAI — Out-of-band: The app determines when to insert ads using external schedules, not manifest markers. The app fetches VAST/VMAP and plays the creative.
- CSAI — In-band boundary markers: The manifest signals where an ad break belongs, but not what to play. The client detects the marker and fetches VAST to resolve the ad.
- SGAI — Server-guided: The manifest contains a playable ad URL. The server already decided what to play; the player fetches and plays it directly.
Ad insertion techniques
CSAI — Out-of-band (Standard)
In standard CSAI, the client app relies on external information, not manifest tags, to determine when to insert ads. The player receives an unmodified content manifest and handles ad logic separately. Ad timing comes from outside the manifest. Sources include:- VMAP: The player fetches an external XML file that schedules ad breaks at specific timestamps (for example, at 0s, 300s).
- Hardcoded logic: The application triggers ad requests at fixed intervals based on the player’s current time position (for example, every 20 seconds).
- Sidecar files: The app downloads a separate configuration file (JSON/XML) from a content management system (CMS) that lists ad break times for a specific piece of content.
- Ad SDKs: An SDK (such as Google Interactive Media Ads) manages timing internally via ad rules, removing the need for the developer to manage specific timestamps or manifest tags.
CSAI — In-band boundary markers (Advanced)
In advanced CSAI, the manifest contains markers that signal “ad break here” — but no ad URL. The client must decide what to play. Common boundary markers include:
When the player encounters a boundary marker (for example,
#EXT-X-CUE-OUT), it interprets this as an ad avail opportunity and initiates a VAST or VMAP request to an ad server for the indicated duration. The ad content is fetched and buffered in parallel while the main content continues playing. The main content pauses only when the ad presentation time is reached. By this time, the ad is expected to be ready to play for a seamless experience.
Flow: Parse manifest → detect boundary → client fetches VAST → resolves → plays creative.
In an in-band implementation using the Headless approach, the UI can send an ad list to the service layer via sendMessage({type: 'adList', ads}) to provide the same data that manifest parsing would produce. The service stores the ad list on the PlayerSession and processes ad markers during getCurrentPlaybackPosition(). When an ad’s pre-buffer window is reached (5 seconds before startTime), the service sends an {type: 'adMarkers', ad} message back to the UI, which then triggers ad insertion.
SGAI — Server-guided ad insertion
In server-guided ad insertion (SGAI), the manifest contains markers that say “play this URL” — the ad URL is already there. The server already decided what to play; the player discovers the ad during manifest parsing, fetches the creative from the embedded URL, and plays it. SGAI markers include:
Unlike CSAI, no VAST parsing or ad SDK is required. The player parses the manifest to discover ad URLs, fetches the ad media, pauses content, plays the ad stream, then resumes content. The player also enforces
X-RESTRICT (skip/jump restrictions) and CUE (pre/post-roll behavior) attributes when present.
Flow: Parse manifest → extract ad URL → fetch and play (no VAST, no ad SDK).
Key differences from CSAI:
- The app does not provide ad tag URLs — the manifest is the ad schedule.
- No VAST/VMAP parsing is needed — URLs point directly to playable
.m3u8or.mpdmedia. - The server pre-decides which ad to serve. Real-time client-side personalization is not available.
- The manifest can be cached at the CDN (not personalized per-viewer like SSAI).
Surface modes
All three ad insertion techniques use a dual-player architecture: a main player for content and an ad player for advertisements. The players can share one video surface or each have their own.Single surface (shared)
Both the main player and the ad player share the sameKeplerVideoSurfaceView. Before the ad player can render to the surface, the main player must release it:
- JS approach: Call
clearSurfaceHandle(surfaceHandle)on the main player, then callsetSurfaceHandle(surfaceHandle)on the ad player. - Headless approach: Call
clearVideoView(sessionId)on the main player session, then callsetVideoView(surfaceHandle, sessionId)on the ad player session.
Dual surface
Each player has its own dedicatedKeplerVideoSurfaceView. The ad surface overlays the main content surface using zIndex positioning. The main player doesn’t need to release its surface when the ad plays, and doesn’t need to set it again when the ad ends.
Surface view layout (JSX)
The main content surface useszIndex: 1 and the ad surface uses zIndex: 2 so the ad renders on top:
Handling ads with different aspect ratios (dual surface)
When the ad and main content share the same aspect ratio (for example, both 16:9), the ad surface overlays the main content directly. No additional handling is needed. When the ad has a different aspect ratio than the main content, the inactive surface may be visible behind the active one due to the size mismatch. To prevent this, use offscreen rendering to move the inactive surface off-screen:const [adSurfaceVisible, setAdSurfaceVisible] = useState(false) and toggle it to true when the ad starts and false when the ad ends.
Playback sync strategies
All three ad insertion techniques support two strategies for coordinating the main and ad players during transitions. The surface steps differ depending on whether you use a shared surface or dual surfaces.Pre-buffered ad (seamless)
The ad player is pre-initialized and buffered before the transition occurs. This provides a gapless user experience with no visual interruptions.- Main content plays.
- Ad marker detected. Pre-initialize ad player with
autoPlay=false. - Ad player buffers.
canplayevent fires (JS) or service sends READY status (Headless). - UI receives ready signal. Ad is ready to play, waits for scheduled playback time.
- Position listener detects ad start time reached. Pause main content.
- Main content paused. Switch to the ad and play it:
- Shared surface: Clear the main surface, set the same surface on the ad player, and play the ad.
- Dual surface: Set the ad surface on the ad player and play the ad. Don’t clear the main surface.
- Ad ends. Clear the ad surface, clean up the ad player, and resume main content:
- Shared surface: Set the main surface on the main player again before you resume.
- Dual surface: Resume main content. The main surface was never cleared, so you don’t need to set it again.
- No visual interruption
- Pre-buffers ad content before playback
- Best for premium viewing experiences
autoPlaysetting:false(manual control)
Instant autoplay ad (with gap)
The ad player is initialized on-demand when the ad break occurs. Because the ad loads at playback time withautoPlay=true, there may be a brief transition gap during ad player initialization.
- Main content plays.
- Ad marker detected. Pause main content.
- Main content paused. Initialize the ad player with
autoPlay=true:- Shared surface: Clear the main surface first.
- Dual surface: Don’t clear the main surface.
- Ad player ready. Set the surface on the ad player (the shared surface, or the ad surface in dual surface mode). Playback starts automatically.
- Ad ends. Clear the ad surface, clean up the ad player, and resume main content:
- Shared surface: Set the main surface on the main player again before you resume.
- Dual surface: Resume main content. You don’t need to set the main surface again.
- Brief black or pause screen possible during transition
- Loads ad on-demand
- Simpler implementation
autoPlaysetting:true(automatic playback)
Ad duration enforcement
If the ad’s defined duration is less than the actual media duration, you can force-end the ad at the correct time using a position or time update listener:Back-to-back advertisements
When your ad break contains multiple consecutive ads, you have three approaches depending on device capabilities:- Single ad player (resource-constrained devices): Create only a single ad player at a time to play a single advertisement. This may not provide seamless or gapless transitions and is only recommended for devices with limited memory or decoders.
- Multiple ad players (high-performance devices): Use two ad player instances for seamless back-to-back ad playback. Provides gapless transitions between consecutive ads. Requires sufficient device resources.
- Main content player recreation (alternative approach): Store the current playback position of primary content. Recreate the main player during the last ad playback. This allows creating two separate ad players on resource-constrained devices.
Challenges
Consider the following challenges when implementing client-side or server-guided ad insertion:- Buffering and latency: When ads are loaded on-demand (instant ads), pausing content to load ads can lead to buffering or latency. To resolve this, pre-buffered ads buffer the ad content before the actual presentation time and pause the main content only after sufficient buffering.
- Device compatibility: Inconsistent performance across different devices due to varying hardware capabilities (memory, decoders, processing power).
- Single surface rendering: The ad must play from the first frame after rendering the last frame of main content. There shouldn’t be missing frames or any overlap of frames during the surface switch. Dual surface mode avoids this by using separate surfaces.
- Surface clearing black flash: In shared surface mode, calling
clearSurfaceHandle(JS) orclearVideoView(Headless) clears the display buffer, causing a visible black frame between the main content’s last frame and the ad’s first frame. Use dual surface mode to avoid this.
Choose your implementation approach
Vega provides two implementation approaches for CSAI and SGAI. Both support shared and dual surface modes. Choose the approach that matches your app architecture.JS approach
The JS approach uses direct React Native component integration.VideoPlayer instances are created and managed within the UI component layer. The application has direct access to W3C Media APIs and controls playback through direct method calls.
- Your app uses a single-threaded React Native architecture
- You want direct access to W3C Media API
- You prefer simpler component-level player management
@amazon-devices/react-native-w3cmedia
To implement this approach, see Implement client-side ad insertion using the JS approach.
Headless approach
The Headless approach uses a client-server architecture.IPlayerClient instances in the UI layer communicate with VideoPlayer instances managed in a separate service layer via KeplerPlayerClientTurboModule using JavaScript Interface (JSI) for high-performance inter-process communication (IPC).
- Your app uses a multi-threaded architecture with separate UI and service layers
- You need the performance benefits of JSI-based IPC
- You want session-based player management with
PlayerSession
Related content
- Implement client-side ad insertion using the JS approach
- Implement client-side ad insertion using the Headless approach
- Shared and dual surface implementation:

