as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
Ring
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持
感谢您的访问。此页面目前仅提供英语版本。我们正在开发中文版本。谢谢您的理解。

Implement client-side ad insertion using the Headless approach

In this tutorial, you'll implement client-side ad insertion (CSAI) in your Vega app using the Headless approach. This approach uses a client-server architecture where IPlayerClient instances in the UI layer communicate with VideoPlayer instances in a separate service layer via JavaScript Interface (JSI)-based inter-process communication (IPC). You'll set up a dual-player architecture with a main player client for content and an ad player client that takes over a shared video surface during ad breaks.

The examples in this tutorial demonstrate pre-buffered ads for seamless ad transitions. For background on CSAI concepts, techniques, and sync strategies, see Client-side ad insertion in Vega.

In this tutorial, you learn how to:

  • Set up player client references and imports
  • Detect an ad marker and pre-initialize the ad player client
  • Wait for the ad to buffer, then pause main content
  • Switch the video surface and play the ad
  • Return to main content after the ad ends

Prerequisites

  • Vega development environment set up
  • Vega SDK v0.22 or later
  • @amazon-devices/kepler-player-client and @amazon-devices/kepler-player-server packages installed
  • Main content player client already initialized and connected to the service
  • Basic knowledge of React Native and TypeScript

Set up player client references

Import the required packages and create refs for the main player client, ad player client, and session IDs.

Copied to clipboard.


import {
  PlayerClientFactory,
  IPlayerClient,
  IPlayerSessionPositionListener,
} from '@amazon-devices/kepler-player-client';

import {
  IPlayerSessionId,
  IPlayerSessionMediaInfo,
  IPlayerSessionStatus,
  IPlayerSessionState,
} from '@amazon-devices/kepler-player-server';

// Player client references
const playerClient = useRef<IPlayerClient | undefined>(undefined);       // Main content player client (already initialized)
const adPlayerClient = useRef<IPlayerClient | undefined>(undefined);     // Ad player client
const playerSessionId = useRef<IPlayerSessionId | undefined>(undefined); // Main session ID
const adPlayerSessionId = useRef<IPlayerSessionId | undefined>(undefined); // Ad session ID
const sessionIdCounter = useRef(1);

Detect the ad marker and pre-initialize the ad player client

When the position listener detects an upcoming ad break, pre-initialize the ad player client with autoPlay=false and register listeners to know when the ad is buffered and when it ends.

  1. Set up a position listener to detect upcoming ad breaks.

Copied to clipboard.


const positionListener: IPlayerSessionPositionListener = {
  onPositionUpdated: (updatedPosition) => {
    const currentTime = updatedPosition[0]?.position || 0;
    // App-specific logic to determine if an ad break is approaching
    if (adBreakApproaching(currentTime)) {
      initializeAdPlayer(adMediaInfo);
    }
  },
};

  1. Create the ad player client initialization function. Register a status listener to detect the READY and ENDED states. On platforms where IPlayerSessionState.READY is not available, the service sends a playerReady message as a fallback.

Copied to clipboard.


const initializeAdPlayer = async (adMediaInfo: IPlayerSessionMediaInfo) => {
  const factory = new PlayerClientFactory();
  adPlayerClient.current = factory.getOrMakeClient(serviceComponentId);
  adPlayerSessionId.current = { id: sessionIdCounter.current++ };

  // Register status listener to detect ENDED state (and READY if available)
  await adPlayerClient.current?.registerStatusListener({
    onSessionStatusChanged: (updatedStatus: Array<IPlayerSessionStatus>) => {
      const adStatus = updatedStatus.find(
        s => s.sessionId?.id === adPlayerSessionId.current?.id
      );
      if (
        IPlayerSessionState.READY !== undefined &&
        adStatus?.playbackState === IPlayerSessionState.READY
      ) {
        onAdReady();
      } else if (adStatus?.playbackState === IPlayerSessionState.ENDED) {
        onAdEnded();
      }
    }
  }, adPlayerSessionId.current);

  // Fallback: On platforms where IPlayerSessionState.READY is not available,
  // the service sends a 'playerReady' message instead
  if (IPlayerSessionState.READY === undefined) {
    await adPlayerClient.current?.registerMessageListener(
      {
        onMessageReceived: (message: any) => {
          if (message.type === 'playerReady') {
            onAdReady();
          }
        },
      },
      adPlayerSessionId.current,
    );
  }

  // Load ad with autoPlay=false for manual control
  await adPlayerClient.current?.load(
    adMediaInfo,
    { startPosition: 0, autoPlay: false },
    adPlayerSessionId.current,
  );
};

Wait for the ad to buffer, then pause main content

After the ad player signals READY (buffered) and the position listener detects the ad start time is reached, pause the main content.

Copied to clipboard.


let adReady = false;

const onAdReady = () => {
  adReady = true;
  // Wait for position listener to detect ad start time
};

// In position listener, when ad start time is reached and ad is ready:
if (adReady && currentTime >= adStartTime) {
  playerClient.current?.pause(playerSessionId.current);
}

Switch the surface and play the ad

When the main content status changes to PAUSED, clear the main surface and hand it to the ad player.

Copied to clipboard.


await playerClient.current?.registerStatusListener({
  onSessionStatusChanged: (updatedStatus: Array<IPlayerSessionStatus>) => {
    const mainStatus = updatedStatus.find(
      s => s.sessionId?.id === playerSessionId.current?.id
    );
    if (mainStatus?.playbackState === IPlayerSessionState.PAUSED && adPlayerClient.current) {
      clearMainSurfaceHandle();
      setAdSurfaceHandle();
      adPlayerClient.current?.play(adPlayerSessionId.current);
    }
  }
}, playerSessionId.current);

Return to main content

After the ad ends, clear the ad surface, clean up the ad player client, and resume main content.

Copied to clipboard.


const onAdEnded = async () => {
  await clearAdSurfaceHandle();
  cleanupAdPlayer();
  await setMainSurfaceHandle();
  await playerClient.current?.play(playerSessionId.current);
};

const cleanupAdPlayer = () => {
  if (!adPlayerClient.current) return;
  adPlayerClient.current.unloadSync(1000, adPlayerSessionId.current);
  adPlayerClient.current = undefined;
  adReady = false;
};

Clean up resources

When your component unmounts or the player session ends, unload the ad player client and release resources.

Copied to clipboard.


const cleanup = () => {
  if (adPlayerClient.current) {
    adPlayerClient.current.unloadSync(1000, adPlayerSessionId.current);
    adPlayerClient.current = undefined;
  }
  // Main player client cleanup handled by your existing teardown logic
};


Last updated: Mar 13, 2026