From 2e2aa68985090a57bde29a9f07b1334ae1c7a2c3 Mon Sep 17 00:00:00 2001 From: Cloud IX Team Date: Tue, 14 Jul 2026 20:40:38 -0700 Subject: [PATCH] Fix failed evaluation cases in ima-dai-sdk reference guides. PiperOrigin-RevId: 948065609 --- README.md | 26 +- skills/ads/ima-dai-sdk/SKILL.md | 89 ++++++ .../references/android-ima-extension.md | 152 ++++++++++ skills/ads/ima-dai-sdk/references/dash.md | 35 +++ .../ima-dai-sdk/references/full-service.md | 261 ++++++++++++++++++ skills/ads/ima-dai-sdk/references/hls.md | 31 +++ skills/ads/ima-dai-sdk/references/ios-tvos.md | 158 +++++++++++ .../ads/ima-dai-sdk/references/pod-serving.md | 169 ++++++++++++ skills/ads/ima-dai-sdk/references/roku.md | 144 ++++++++++ skills/ads/ima-dai-sdk/references/web.md | 220 +++++++++++++++ .../SKILL.md | 16 +- .../references/ima-sdk-android-guide.md | 3 +- .../references/ima-sdk-ios-guide.md | 2 +- .../references/ima-sdk-tvos-guide.md | 15 +- .../references/ima-sdk-web-guide.md | 0 .../references/ima-sdk-web-iframe-mode.md | 0 .../references/ima-sdk-web-mobile-safari.md | 0 17 files changed, 1292 insertions(+), 29 deletions(-) create mode 100644 skills/ads/ima-dai-sdk/SKILL.md create mode 100644 skills/ads/ima-dai-sdk/references/android-ima-extension.md create mode 100644 skills/ads/ima-dai-sdk/references/dash.md create mode 100644 skills/ads/ima-dai-sdk/references/full-service.md create mode 100644 skills/ads/ima-dai-sdk/references/hls.md create mode 100644 skills/ads/ima-dai-sdk/references/ios-tvos.md create mode 100644 skills/ads/ima-dai-sdk/references/pod-serving.md create mode 100644 skills/ads/ima-dai-sdk/references/roku.md create mode 100644 skills/ads/ima-dai-sdk/references/web.md rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/SKILL.md (84%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-android-guide.md (98%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-ios-guide.md (99%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-tvos-guide.md (92%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-web-guide.md (100%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-web-iframe-mode.md (100%) rename skills/ads/{ima-sdk-basics => ima-sdk-client-side}/references/ima-sdk-web-mobile-safari.md (100%) diff --git a/README.md b/README.md index 0fff60dae8..c66f205994 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ repo to install. ## Available Skills + - **Getting started with Google Cloud** - [**Authenticating to Google Cloud**](./skills/cloud/google-cloud-recipe-auth) - [**Google Cloud Recipe: Foundation Builder**](./skills/cloud/google-cloud-recipe-foundation-builder) @@ -89,18 +90,19 @@ repo to install. - **Web and app hosting** - [**Cloud Run Basics**](./skills/cloud/cloud-run-basics) - [**Firebase Basics**](./skills/cloud/firebase-basics) -- **Advertising** - - [**AI Migration Agent Instructions for the Google Mobile Ads SDK**](./skills/ads/google-mobile-ads/google-mobile-ads-android-migrate-to-next-gen) - - [**Data Manager API Audience Ingestion**](./skills/ads/data-manager-api/data-manager-api-audience-ingestion) - - [**Data Manager API Event Ingestion**](./skills/ads/data-manager-api/data-manager-api-event-ingestion) - - [**Data Manager API Setup**](./skills/ads/data-manager-api/data-manager-api-setup) - - [**Google Ads API MCP Server Installation**](./skills/ads/google-ads-api/google-ads-api-mcp-setup) - - [**Google Ads API Quickstart**](./skills/ads/google-ads-api/google-ads-api-quickstart) - - [**Google Mobile Ads SDK - Banner Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-banner) - - [**Google Mobile Ads SDK - Install**](./skills/ads/google-mobile-ads/google-mobile-ads-get-started) - - [**Google Mobile Ads SDK - Interstitial Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-interstitial) - - [**Google Mobile Ads SDK - Rewarded Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-rewarded) - - [**IMA SDK basics**](./skills/ads/interactive-media-ads/ima-sdk-basics) +- **Advertising** + - [**AI Migration Agent Instructions for the Google Mobile Ads SDK**](./skills/ads/google-mobile-ads/google-mobile-ads-android-migrate-to-next-gen) + - [**Data Manager API Audience Ingestion**](./skills/ads/data-manager-api/data-manager-api-audience-ingestion) + - [**Data Manager API Event Ingestion**](./skills/ads/data-manager-api/data-manager-api-event-ingestion) + - [**Data Manager API Setup**](./skills/ads/data-manager-api/data-manager-api-setup) + - [**Google Ads API MCP Server Installation**](./skills/ads/google-ads-api/google-ads-api-mcp-setup) + - [**Google Ads API Quickstart**](./skills/ads/google-ads-api/google-ads-api-quickstart) + - [**Google Mobile Ads SDK - Banner Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-banner) + - [**Google Mobile Ads SDK - Install**](./skills/ads/google-mobile-ads/google-mobile-ads-get-started) + - [**Google Mobile Ads SDK - Interstitial Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-interstitial) + - [**Google Mobile Ads SDK - Rewarded Ads**](./skills/ads/google-mobile-ads/google-mobile-ads-rewarded) + - [**IMA SDK client-side**](./skills/ads/ima-sdk-client-side) + - [**IMA SDK DAI Basics**](./skills/ads/ima-dai-sdk) - **Others** - [**Getting Started with Google Analytics Admin API**](./skills/analytics/google-analytics-admin-api-basics) - [**Getting Started with Google Analytics Data API**](./skills/analytics/google-analytics-data-api-basics) diff --git a/skills/ads/ima-dai-sdk/SKILL.md b/skills/ads/ima-dai-sdk/SKILL.md new file mode 100644 index 0000000000..bc8dd71831 --- /dev/null +++ b/skills/ads/ima-dai-sdk/SKILL.md @@ -0,0 +1,89 @@ +--- +name: ima-dai-sdk +description: >- + Integrates the Google Interactive Media Ads (IMA) DAI SDK into websites, web apps, + mobile apps, or TV apps for Google Dynamic Ad Insertion (DAI) with HLS or DASH streams. + + Use when: + - Integrating the IMA DAI SDK for Google Dynamic Ad Insertion (DAI), Server-Side Ad Insertion (SSAI), Full Service DAI, or DAI Pod Serving redirect. + - Requesting HLS (.m3u8) or MPEG-DASH (.mpd) DAI streams on Web, Android, iOS/tvOS, or Roku platforms. + + Don't use for: + - Client-side ad insertion (CSAI) with VAST and VMAP requests (use the `ima-sdk-client-side` skill instead). + +license: Apache-2.0 +metadata: + author: Google LLC + version: "1.0.0" +--- + +# IMA SDK DAI Basics + +The Google IMA DAI SDK lets you load a single stream containing both content and +ads into websites, apps, TVs and other digital platforms. Use an IMA SDK to +request an HLS or MPEG DASH stream from a video stitcher or stream origin. + +## Mandatory Prerequisites + +Correct integration requires identifying the streaming protocol, DAI type, and target platform up front. Review the relevant reference guides below to ensure proper setup: + +1. **Identify the Streaming Protocol:** + + * **HLS (HTTP Live Streaming):** Stream URLs end in `.m3u8`. Read the [HLS Guide](references/hls.md). + * **MPEG DASH:** Stream URLs end in `.mpd`. Read the [DASH Guide](references/dash.md). + +2. **Identify the DAI Type:** + + * **Full Service DAI:** Request a Google DAI stream URL using an `assetKey` (Live) or `cmsId`/`videoId` (VOD). Read the [Full Service Guide](references/full-service.md). + * **Pod Serving DAI:** Request a Google DAI `streamId` using an `assetKey`/`customAssetKey` (linear stream, livestream) or `cmsId`/`videoId` (VOD). Construct a playback stream URL with the `streamId`. Read the [Pod Serving Guide](references/pod-serving.md). + +3. **Identify the Target Platform:** + + * **Web/NodeJs/ReactJs:** Native JavaScript/TypeScript environments (browsers, Smart TVs). Read the [Web Guide](references/web.md). + * **Android/AndroidTV:** Native Java/Kotlin environments. Read the [Android Guide](references/android-ima-extension.md). + * **iOS/tvOS:** Native Swift/Objective-C environments. Read the [iOS/tvOS Guide](references/ios-tvos.md). + * **Roku:** Native BrightScript/SceneGraph environments. Read the [Roku Guide](references/roku.md). + +-------------------------------------------------------------------------------- + +## Quick Start (General Workflow) + +For all apps, the integration follows this general logical flow: + +1. Import the SDK: Set up prerequisites and dependencies. +2. Initialization: Early setup, Warmup, Settings Configuration, and Ad UI Setup. +3. Ad Stream request: Create the livestream or VOD stream request. +4. Ad Stream Load Success/Failure: Handle the stream load event to obtain the `StreamManager` or handle early fatal errors. +5. Stream Playback Events: Listen to timed metadata events for forwarding ID3/emsg tags to the IMA DAI SDK, and handle non-fatal LOG events or fatal stream errors. +6. Cleanup: Properly destroy the `StreamManager` to release resources and prevent memory leaks. + +### Basic Web Live Stream Integration Example + +```typescript +import { LiveStreamRequest, StreamManager, StreamEvent } from '@google/ima-dai-sdk'; + +const videoElement = document.getElementById('video-player') as HTMLVideoElement; +const adUiElement = document.getElementById('ad-ui') as HTMLElement; + +// 1. Initialize StreamManager +const streamManager = new StreamManager(videoElement, adUiElement); + +// 2. Listen for stream loaded event +streamManager.addEventListener( + StreamEvent.Type.LOADED, + (event: StreamEvent) => { + const streamUrl = event.getStreamData().url; + videoElement.src = streamUrl; + videoElement.play(); + } +); + +// 3. Request live stream +const streamRequest = new LiveStreamRequest(); +streamRequest.assetKey = 'YOUR_ASSET_KEY'; +streamRequest.networkCode = 'YOUR_NETWORK_CODE'; +streamManager.requestStream(streamRequest); +``` + +For detailed, platform-specific implementation details, refer to the guides in the **Mandatory Prerequisites** section. + diff --git a/skills/ads/ima-dai-sdk/references/android-ima-extension.md b/skills/ads/ima-dai-sdk/references/android-ima-extension.md new file mode 100644 index 0000000000..9f51a228eb --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/android-ima-extension.md @@ -0,0 +1,152 @@ +# Google IMA SDK DAI - Media3 IMA Extension Integration Guide + +This guide covers integrating the Google IMA SDK for Android DAI using the +**Media3 IMA Extension** (`androidx.media3:media3-exoplayer-ima`) and +`ImaServerSideAdInsertionMediaSource`. + +Using the extension is the recommended approach for Media3 ExoPlayer +integrations as it handles stream wrapping, ad UI overlays, and ad event +synchronization automatically without requiring a custom `VideoStreamPlayer` +implementation. + +-------------------------------------------------------------------------------- + +## 1. Dependencies and Configuration + +Add the Media3 IMA extension dependency to your app's `build.gradle.kts` file. + +Use latest version of the IMA SDK, which is published at +https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/download.md.txt. +Use the latest version of the ExoPlayer and IMA extension at +https://developer.android.com/jetpack/androidx/releases/media3.md.txt. + +```kotlin +android { + defaultConfig { + minSdk = 23 + } +} + +dependencies { + implementation("androidx.media3:media3-ui:$latestVersion") + implementation("androidx.media3:media3-exoplayer:$latestVersion") + + // Recommend using latest version of media3-exoplayer-ima (version 1.10.1 or higher) + implementation("androidx.media3:media3-exoplayer-ima:1.10.1") +} +``` + +-------------------------------------------------------------------------------- + +### 2. Initialization & Best Practices + +Configure the SDK early to minimize latency, and reuse the SDK settings: + +* **Early Warmup & Settings Reuse:** Call `ImaSdkFactory.initialize()` in your `Application.onCreate()` to preload SDK resources. +* **Reuse `ImaSdkSettings`:** You MUST reuse the exact same `ImaSdkSettings` instance in both the `ImaSdkFactory.initialize()` call and the `ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setImaSdkSettings()` call. Recreating settings causes cache misses and ad loading latency spikes. +* **Do NOT use `ImaAdsLoader`:** For DAI / SSAI streams, do NOT use the client-side `ImaAdsLoader` class. Use `ImaServerSideAdInsertionMediaSource.AdsLoader` instead. + +```kotlin +// 1. Application class for early warmup +class MyApplication : Application() { + companion object { + lateinit var sdkFactory: ImaSdkFactory + lateinit var imaSettings: ImaSdkSettings + } + + override fun onCreate() { + super.onCreate() + sdkFactory = ImaSdkFactory.getInstance() + + // Configure settings early + imaSettings = sdkFactory.createImaSdkSettings().apply { + language = "en" + isDebugMode = true + } + + sdkFactory.initialize(this, imaSettings) + } +} +... + +private fun createAdsLoader(): ImaServerSideAdInsertionMediaSource.AdsLoader { + // Reuse the exact same imaSettings from MyApplication + return ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView) + .setImaSdkSettings(MyApplication.imaSettings) + .build() +} +``` + +### 3. Build DAI Request URIs + +Create an `ImaServerSideAdInsertionUriBuilder` to build an SSAI stream URL. + +```kotlin +import android.net.Uri +import androidx.media3.common.C +import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionUriBuilder + +// Example 1: HLS Live Stream +val hlsLiveUri: Uri = ImaServerSideAdInsertionUriBuilder() + .setAssetKey("YOUR_LIVE_ASSET_KEY") + .setFormat(C.CONTENT_TYPE_HLS) + .build() + +// Example 2: HLS VOD Stream +val hlsVodUri: Uri = ImaServerSideAdInsertionUriBuilder() + .setContentSourceId("YOUR_CMS_ID") + .setVideoId("YOUR_VIDEO_ID") + .setFormat(C.CONTENT_TYPE_HLS) + .build() +``` + +### 4. Create an ads-aware `MediaSourceFactory` + +When using the ExoPlayer IMA extension, you must use the +`ImaServerSideAdInsertionMediaSource.AdsLoader` class instead of the +`ImaAdsLoader` class. + +When creating the `ImaServerSideAdInsertionMediaSource.AdsLoader`, you must +provide the `PlayerView` object for rendering ad UI elements. + +You must create a `DefaultMediaSourceFactory` object for your content and call +the `DefaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory()` +method with the `ImaServerSideAdInsertionMediaSource.AdsLoader` object. + +```kotlin +// Construct the AdsLoader instance +val adsLoader = ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, playerView) + .build() + +// Instantiate the factory and bind the AdsLoader for SSAI handling +val mediaSourceFactory = DefaultMediaSourceFactory(context) + .setServerSideAdInsertionMediaSourceFactory(adsLoader) +``` + +### 5. Set Up ExoPlayer + +Create the ExoPlayer instance with your ad-aware media source factory and +provide the ExoPlayer instance to the `AdsLoader` for monitoring ad events. + +```kotlin +// Construct the player using the configured factory +val player = ExoPlayer.Builder(context) + .setMediaSourceFactory(mediaSourceFactory) + .build() + +// Bind the player to the AdsLoader immediately +adsLoader.setPlayer(player) +``` + +### 6. Load the media item + +### 6. Load Media and Manage the Lifecycle + +Pass your constructed SSAI URI directly into the player as a `MediaItem`. + +When destroying the player activity or handling background events, you must +execute a strict teardown sequence to preserve the user's session. Call +`release()` on the `AdsLoader` before releasing the ExoPlayer instance. Capture +the returned `AdsLoader.State` object and store it securely (e.g., in a +`ViewModel`). Provide this saved state back to the `AdsLoader.Builder` during +reinitialization to ensure ad playback resumes exactly where it left off. diff --git a/skills/ads/ima-dai-sdk/references/dash.md b/skills/ads/ima-dai-sdk/references/dash.md new file mode 100644 index 0000000000..0e90db5ffe --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/dash.md @@ -0,0 +1,35 @@ +# Google IMA SDK DAI - DASH Integration Guide + +This guide covers DASH stream request configuration with IMA DAI SDK. + +When requesting a DASH stream, you set the stream format as follows. + +* **Web (TypeScript):** + + ```typescript + const streamRequest = new google.ima.dai.api.LiveStreamRequest(); + streamRequest.format = 'dash'; // the format property is also available for + // VODStreamRequest, + // PodStreamRequest, PodVodStreamRequest. + ``` + +* **Android (Kotlin):** + + ```kotlin + val request = sdkFactory.createLiveStreamRequest("YOUR_ASSET_KEY", "YOUR_NETWORK_CODE", null) + request.format = StreamFormat.DASH + + // Always pass the configured request object to adsLoader.requestStream(request) + adsLoader.requestStream(request) + ``` + + Or if using Media3 SSAI Uri Builder: + + ```kotlin + val ssaiUri = ImaServerSideAdInsertionUriBuilder() + .setAssetKey(assetKey) + .setFormat(androidx.media3.common.C.CONTENT_TYPE_DASH) + // ... + .build() + ``` + diff --git a/skills/ads/ima-dai-sdk/references/full-service.md b/skills/ads/ima-dai-sdk/references/full-service.md new file mode 100644 index 0000000000..665f2a0e1c --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/full-service.md @@ -0,0 +1,261 @@ +# Google IMA SDK DAI - Full Service Integration Guide + +In a Full Service integration, Google Ad Manager handles the entire server-side +ad insertion (SSAI) workflow, including selecting ads, transcoding them, and +stitching them directly into your video manifest. The SDK requests the stream +directly from Google DAI servers and returns a fully stitched stream URL (or +handles playback internally). + +-------------------------------------------------------------------------------- + +## 1. Web Integration + +### A. Requesting a Stream + +You can request either a Live stream (requires `assetKey`) or a VOD stream +(requires `contentSourceId`/CMS ID and `videoId`). + +```typescript +const videoElement = document.getElementById('video') as HTMLVideoElement; +const adUiElement = document.getElementById('adUi') as HTMLElement; +let streamManager: google.ima.dai.api.StreamManager; + +function initializeDAI(): void { + streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement); + setupStreamManagerListeners(); +} + +function requestLiveStream(assetKey: string, networkCode: string, apiKey?: string): void { + const streamRequest = new google.ima.dai.api.LiveStreamRequest(); + streamRequest.assetKey = assetKey; + streamRequest.networkCode = networkCode; + + if (apiKey) { + streamRequest.apiKey = apiKey; + } + streamManager.requestStream(streamRequest); +} + +function requestVODStream(cmsId: string, videoId: string, networkCode: string, apiKey?: string): void { + const streamRequest = new google.ima.dai.api.VODStreamRequest(); + streamRequest.contentSourceId = cmsId; + streamRequest.videoId = videoId; + streamRequest.networkCode = networkCode; + if (apiKey) { + streamRequest.apiKey = apiKey; + } + streamManager.requestStream(streamRequest); +} +``` + +### B. Handling Stream Events + +Listen for the `LOADED` event to get the stitched stream URL, and `ERROR` to +fallback to a backup stream. + +```typescript +function setupStreamManagerListeners(): void { + streamManager.addEventListener( + google.ima.dai.api.StreamEvent.Type.LOADED, + onStreamLoaded, + false + ); + streamManager.addEventListener( + google.ima.dai.api.StreamEvent.Type.ERROR, + onStreamError, + false + ); +} + +function onStreamLoaded(e: google.ima.dai.api.StreamEvent): void { + const streamData = e.getStreamData(); + if (streamData) { + const streamUrl = streamData.url; + // Explicitly assign the returned stream URL to the video element's src property: + videoElement.src = streamUrl; + videoElement.play(); + } +} + +function onStreamError(e: google.ima.dai.api.StreamEvent): void { + const streamData = e.getStreamData(); + if (streamData) { + console.error('DAI Error:', streamData.errorMessage); + } + playBackupStream(); // Fallback to unmonetized content +} +``` + +-------------------------------------------------------------------------------- + +## 2. Android Integration + +You can integrate using the Media3 ExoPlayer SSAI extension (recommended) or +using custom player APIs. + +### A. Media3 ExoPlayer SSAI Extension (Recommended) + +The `media3-exoplayer-ima` extension handles player setup and metadata +extraction automatically. + +```kotlin +private fun initializePlayer() { + // 1. Create the SSAI AdsLoader + adsLoader = ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView).build() + + // 2. Set up MediaSource Factory with SSAI support + val mediaSourceFactory = DefaultMediaSourceFactory(this) + val adsMediaSourceFactory = + ImaServerSideAdInsertionMediaSource.Factory(adsLoader, mediaSourceFactory) + mediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory) + + // 3. Create ExoPlayer + player = ExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build() + playerView.player = player + adsLoader.setPlayer(player) + + // 4. Build SSAI URI (Live example) + val ssaiUri = ImaServerSideAdInsertionUriBuilder() + .setAssetKey("YOUR_ASSET_KEY") + .setFormat(androidx.media3.common.C.CONTENT_TYPE_HLS) + .build() + + val ssaiMediaItem = MediaItem.fromUri(ssaiUri) + player.setMediaItem(ssaiMediaItem) + player.prepare() + player.play() +} +``` + +### B. Custom Player Integration + +If not using the extension, you must implement `VideoStreamPlayer` and request +the stream manually. + +```kotlin +val sdkFactory = ImaSdkFactory.getInstance() +val displayContainer = + ImaSdkFactory.createStreamDisplayContainer(adUiContainer, videoStreamPlayer) +val adsLoader = sdkFactory.createAdsLoader(context, settings, displayContainer) + +adsLoader.addAdsLoadedListener { event -> + val streamManager = event.streamManager + streamManager.init() // SDK automatically calls VideoStreamPlayer.loadUrl() +} + +val request = + sdkFactory.createVodStreamRequest("CMS_ID", "VIDEO_ID", null, "NETWORK_CODE") +adsLoader.requestStream(request) +``` + +The SDK will call your `VideoStreamPlayer.loadUrl(url, subtitles)` where you +must load the URL into your player. + +-------------------------------------------------------------------------------- + +## 3. iOS/tvOS Integration (AVPlayer) + +### A. Requesting the Stream + +Use `IMALiveStreamRequest` or `IMAVODStreamRequest` and wrap `AVPlayer` with +`IMAAVPlayerVideoDisplay`. + +```swift +override func viewDidLoad() { + super.viewDidLoad() + setupPlayer() + adsLoader = IMAAdsLoader(settings: nil) + adsLoader?.delegate = self + adDisplayContainer = IMAAdDisplayContainer(adContainer: videoView, viewController: self, companionSlots: nil) + imaVideoDisplay = IMAAVPlayerVideoDisplay(avPlayer: videoPlayer) // Handles HLS ID3 automatically +} + +func requestStream() { + if isLive { + let request = IMALiveStreamRequest(assetKey: "YOUR_ASSET_KEY", networkCode: "YOUR_NETWORK_CODE", adDisplayContainer: adDisplayContainer!, videoDisplay: imaVideoDisplay, userContext: nil) + adsLoader?.requestStream(with: request) + } else { + let request = IMAVODStreamRequest(contentSourceID: "YOUR_CMS_ID", videoID: "YOUR_VIDEO_ID", networkCode: "YOUR_NETWORK_CODE", adDisplayContainer: adDisplayContainer!, videoDisplay: imaVideoDisplay, userContext: nil) + adsLoader?.requestStream(with: request) + } +} +``` + +### B. Handling Stream Load + +In `IMAAdsLoaderDelegate`, initialize the `StreamManager`. For Full Service, the +SDK automatically loads the stream into the `AVPlayer` and starts playback. + +```swift +func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) { + streamManager = adsLoadedData.streamManager + streamManager?.delegate = self + streamManager?.initialize(with: nil) +} +``` + +-------------------------------------------------------------------------------- + +## 4. Passing Custom Parameters (Targeting) + +You can pass custom parameters (such as targeting key-values) to the ad request +by setting ad tag parameters on the stream request. + +### A. Web (TypeScript) + +Set `adTagParameters` on the `StreamRequest` object. + +```typescript +function requestLiveStreamWithTargeting(assetKey: string, networkCode: string): void { + const streamRequest = new google.ima.dai.api.LiveStreamRequest(); + streamRequest.assetKey = assetKey; + streamRequest.networkCode = networkCode; + + // Pass custom parameters + streamRequest.adTagParameters = { + 'genre': 'action', + 'age': '25' + }; + + streamManager.requestStream(streamRequest); +} +``` + +### B. Android (Kotlin) + +#### Using Media3 ExoPlayer SSAI Extension + +Use `setAdTagParameters` on `ImaServerSideAdInsertionUriBuilder`. + +```kotlin +val adTagParameters = mapOf("genre" to "action", "age" to "25") +val ssaiUri = ImaServerSideAdInsertionUriBuilder() + .setAssetKey("YOUR_ASSET_KEY") + .setFormat(androidx.media3.common.C.CONTENT_TYPE_HLS) + .setAdTagParameters(adTagParameters) + .build() +``` + +### C. iOS/tvOS (Swift) + +Set `adTagParameters` on the `IMAStreamRequest` object. + +```swift +func requestStreamWithTargeting() { + let request = IMALiveStreamRequest( + assetKey: "YOUR_ASSET_KEY", + networkCode: "YOUR_NETWORK_CODE", + adDisplayContainer: adDisplayContainer!, + videoDisplay: imaVideoDisplay, + userContext: nil + ) + + // Pass custom parameters + request.adTagParameters = [ + "genre": "action", + "age": "25" + ] + + adsLoader?.requestStream(with: request) +} +``` diff --git a/skills/ads/ima-dai-sdk/references/hls.md b/skills/ads/ima-dai-sdk/references/hls.md new file mode 100644 index 0000000000..d44dd4a439 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/hls.md @@ -0,0 +1,31 @@ +# Google IMA SDK DAI - HLS Integration Guide + +This guide covers HLS stream request configuration with IMA DAI SDK. + +When requesting an HLS stream, you set the stream format as follows. + +* **Web (TypeScript):** + + ```typescript + const streamRequest = new google.ima.dai.api.LiveStreamRequest(); + streamRequest.format = 'hls'; // the format property is also available for + // VODStreamRequest, + // PodStreamRequest, PodVodStreamRequest. + ``` + +* **Android (Kotlin):** + + ```kotlin + val request = sdkFactory.createLiveStreamRequest(...) + request.format = StreamFormat.HLS + ``` + + Or if using Media3 SSAI Uri Builder: + + ```kotlin + val ssaiUri = ImaServerSideAdInsertionUriBuilder() + .setAssetKey(assetKey) + .setFormat(androidx.media3.common.C.CONTENT_TYPE_HLS) + // ... + .build() + ``` diff --git a/skills/ads/ima-dai-sdk/references/ios-tvos.md b/skills/ads/ima-dai-sdk/references/ios-tvos.md new file mode 100644 index 0000000000..c25b8fd4dd --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/ios-tvos.md @@ -0,0 +1,158 @@ +# Google IMA SDK DAI - iOS & tvOS Integration Guide + +This guide covers the general integration steps for the Google IMA SDK for iOS +and tvOS DAI. The two SDKs share the same core API and lifecycle flow. + +For protocol-specific details, see [HLS Guide](hls.md). For DAI workflows, see +[Full Service Guide](full-service.md) and [Pod Serving Guide](pod-serving.md). + +-------------------------------------------------------------------------------- + +## 1. Installation + +By default, use Swift Package Manager to add the the main branch of +https://github.com/googleads/swift-package-manager-google-interactive-media-ads-ios +or +https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos + +If the app must use CocoaPods, install the `GoogleAds-IMA-iOS-SDK` or +`GoogleAds-IMA-tvOS-SDK` pod. + +## 2. Implement IMA protocols + +* **`IMAAdsLoaderDelegate`**: Required for your app to receive events when ads + successfully load or if they fail to load. +* **`IMAStreamManagerDelegate`**: Required for your app to handle events + during ad playback (e.g., when an ad starts, pauses, completes, or when the + user clicks an ad). + +```swift +class PlayerViewController: IMAAdsLoaderDelegate, IMAStreamManagerDelegate + +``` + +-------------------------------------------------------------------------------- + +## 3. Initialize AdsLoader Early + +You must create the `AdsLoader` object early, such as when your view did load. +Set the `IMASettings` to match your app UI localization needs. Reuse the +`AdsLoader` and `IMASettings` objects. + +```swift +import AVFoundation +import GoogleInteractiveMediaAds + +class PlayerViewController: ..., IMAAdsLoaderDelegate, IMAStreamManagerDelegate { + private var adsLoader: IMAAdsLoader? + // ... + + override func viewDidLoad() { + super.viewDidLoad() + ... + + let settings = IMASettings() + settings.language = LANGUAGE_CODE // Use 2 letter language code to match the ad UI with your app UI's language. + + // 2. Initialize AdsLoader WITH the configured settings + adsLoader = IMAAdsLoader(settings: settings) + } +} +``` + +## 4. Create Ad Display Container + +You must create the `IMAAdDisplayContainer` object for the SDK to render the ad +UI during ad playback. Pass your `videoView` so the SDK knows where to overlay +the ad UI on top of the video player. + +```swift +adDisplayContainer = IMAAdDisplayContainer(adContainer: videoView) +``` + +## 5. Create `IMAAVPlayerVideoDisplay` object + +You must create the `IMAAVPlayerVideoDisplay` object and provide your `AVPlayer` +object. You must provide the `AVPlayer` object that is intended to play the ads +if you have multiple instances of the `AVPlayer` class in your app. The SDK +needs to listen for the timed metadata (ID3 tags) embedded in the HLS manifest +from the ad player to trigger ad events. + +```swift +imaVideoDisplay = IMAAVPlayerVideoDisplay(avPlayer: adVideoPlayer) +``` + +## 6. Make DAI Stream Request + +Refer to the [full-service DAI guide](full-service.md) and +[pod-serving DAI guide](pod-serving.md) to determine the exact DAI stream +request. + +-------------------------------------------------------------------------------- + +## 7. tvOS-Specific Requirements + +Integrating on tvOS requires handling Apple TV remote focus and gestures. + +### A. Safe Area Layout + +Align your ad UI container with the safe area layout guide to prevent TV +overscan clipping. + +```swift +func setupAdContainer(in viewController: UIViewController, overlaying videoView: UIView) -> UIView { + let adContainerView = UIView() + adContainerView.translatesAutoresizingMaskIntoConstraints = false + viewController.view.addSubview(adContainerView) + + // Align with safe area + let safeArea = viewController.view.safeAreaLayoutGuide + NSLayoutConstraint.activate([ + adContainerView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), + adContainerView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor), + adContainerView.topAnchor.constraint(equalTo: safeArea.topAnchor), + adContainerView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor) + ]) + return adContainerView +} +``` + +### B. Focus Management (Siri Remote) + +The SDK automatically manages the focus of the "Skip" button. You must override +`preferredFocusEnvironments` to prefer the ad container when an ad is active: + +```swift +var adIsPlaying = false // Update this based on IMAAdEvents + +override var preferredFocusEnvironments: [UIFocusEnvironment] { + if adIsPlaying, let adContainer = adContainerView { + return [adContainer] + } + return super.preferredFocusEnvironments +} +``` + +### C. Remote Control Gestures + +Disable custom remote gestures (like swipe or click-to-pause) during ad playback +to prevent users from skipping ads. + +```swift +func streamManager(_ streamManager: IMAStreamManager, didReceive event: IMAAdEvent) { + switch event.type { + case .AD_BREAK_STARTED: + adIsPlaying = true + disableAppGestures() // Disable custom gestures + setNeedsFocusUpdate() + updateFocusIfNeeded() // Ensure focus updates immediately when ad break starts + case .AD_BREAK_ENDED: + adIsPlaying = false + enableAppGestures() // Re-enable custom gestures + setNeedsFocusUpdate() + updateFocusIfNeeded() + default: + break + } +} +``` diff --git a/skills/ads/ima-dai-sdk/references/pod-serving.md b/skills/ads/ima-dai-sdk/references/pod-serving.md new file mode 100644 index 0000000000..0733b0b6d3 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/pod-serving.md @@ -0,0 +1,169 @@ +# Google IMA SDK DAI - Pod Serving Integration Guide + +In Pod Serving, you use Google DAI with a third-party manifest manipulator or video technology partner (VTP). Google DAI builds the ad pods, but your manifest manipulator stitches the ad pods with content to give you a final, single ad-stitched stream. + +The SDK registers a session, receives a `streamId`, and you use this ID to construct the stream URL that your player will play. + +-------------------------------------------------------------------------------- + +## Key Differences: Live vs. VOD Metadata Loading + +* **Live Linear Pod Serving:** Relies on in-manifest timed metadata (ID3 or DASH emsg tags embedded in the live stream) to report ad events. +* **VOD Pod Serving:** Requires calling `streamManager.loadStreamMetadata()` (Web) or `streamManager.loadThirdPartyStream()` (Android/iOS) because VOD streams do NOT contain in-manifest timed metadata tags. Calling `loadStreamMetadata()` immediately after initiating playback or loading the VTP URL fetches out-of-band ad break cue points from Google DAI servers. + +-------------------------------------------------------------------------------- + +## 1. Web Integration + +### A. Requesting the Stream + +When requesting a Pod Serving stream on Web, create either: +* `PodStreamRequest` (for Live): Set `networkCode` and `customAssetKey`. +* `PodVodStreamRequest` (for VOD): Set `networkCode` and `cmsId`/`videoId` (or `contentSourceId`/`videoId`). + +```typescript +let streamManager: google.ima.dai.api.StreamManager; + +// 1. Live Pod Stream Request +function requestLivePodStream(networkCode: string, customAssetKey: string): void { + const streamRequest = new google.ima.dai.api.PodStreamRequest(); + streamRequest.networkCode = networkCode; + streamRequest.customAssetKey = customAssetKey; + streamManager.requestStream(streamRequest); +} + +// 2. VOD Pod Stream Request +function requestVODPodStream(networkCode: string, cmsId: string, videoId: string): void { + const streamRequest = new google.ima.dai.api.PodVodStreamRequest(); + streamRequest.networkCode = networkCode; + streamRequest.contentSourceId = cmsId; + streamRequest.videoId = videoId; + streamManager.requestStream(streamRequest); +} +``` + +### B. Handling `STREAM_INITIALIZED` & Out-of-Band Metadata Loading + +1. Listen for the `STREAM_INITIALIZED` event to obtain `event.getStreamData().streamId`. +2. Send the `streamId` to your third-party manifest stitcher (VTP) to assemble the playback URL. +3. For VOD, call `streamManager.loadStreamMetadata()` immediately after retrieving the VTP manifest URL to fetch ad cue points out-of-band. + +```typescript +streamManager.addEventListener( + google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED, + onStreamInitialized, + false +); + +function onStreamInitialized(e: google.ima.dai.api.StreamEvent): void { + const streamData = e.getStreamData(); + if (!streamData) return; + + const streamId = streamData.streamId; + + // 1. Send streamId to your VTP / manifest manipulator + getVtpStreamUrl({ streamId: streamId }).then((vtpManifestUrl: string) => { + // 2. For VOD: Call loadStreamMetadata() immediately to fetch ad cue points out-of-band + if (isVod) { + streamManager.loadStreamMetadata(); + } + + // 3. Pass manifest URL to video player + videoElement.src = vtpManifestUrl; + videoElement.play(); + }); +} +``` + +### C. Segment Redirects Troubleshooting + +If ad segments return black screens or fail during Pod Serving: +* **Automatic 302 Redirects:** Verify that your video player or browser automatically follows HTTP 302 redirects issued by segment redirect URLs. +* **Authentication & Credentials:** Set `withCredentials = true` on your player's HTTP request config if redirects require session cookies or authentication headers. +* **Network Diagnostics:** Inspect browser Network Inspector logs for 302 redirect status responses, CORS preflight failures (`OPTIONS`), or missing Access-Control-Allow-Origin headers on ad CDN servers. + +-------------------------------------------------------------------------------- + +## 2. Android Integration + +*Note: Media3 SSAI extension does not support Pod Serving out-of-the-box. You must use the Custom Player integration method.* + +### A. Requesting the Stream + +```kotlin +val request: StreamRequest = if (isLive) { + sdkFactory.createPodStreamRequest(NETWORK_CODE, CUSTOM_ASSET_KEY, API_KEY) +} else { + sdkFactory.createPodVodStreamRequest(NETWORK_CODE) +} + +request.format = StreamFormat.HLS // or StreamFormat.DASH +adsLoader.requestStream(request) +``` + +### B. Handling `STREAM_INITIALIZED` & Playback + +Listen for `STREAM_INITIALIZED` (or `onAdsManagerLoaded`) to obtain `streamId`, send it to your third-party manifest stitcher, and load the stream URL. + +```kotlin +override fun onAdsManagerLoaded(event: AdsManagerLoadedEvent) { + val streamManager = event.streamManager + streamManager.init() + + // 1. Extract streamId from StreamManager upon initialization + val streamId = streamManager.streamId + + if (isLive) { + // 2a. Send streamId to VTP and play live stream + val liveStreamUrl = getVtpLiveStreamURL(streamId) + videoPlayer.setStreamUrl(liveStreamUrl) + videoPlayer.play() + } else { + // 2b. Send streamId to VTP and load VOD stream via loadThirdPartyStream() + val vodStreamUrl = getVtpVodStreamURL(streamId) + // loadThirdPartyStream() automatically retrieves out-of-band ad metadata and triggers player load + streamManager.loadThirdPartyStream(vodStreamUrl, emptyList()) + } +} +``` + +-------------------------------------------------------------------------------- + +## 3. iOS/tvOS Integration + +### A. Requesting the Stream + +Use `IMAPodStreamRequest` or `IMAPodVODStreamRequest`. + +```swift +func requestPodStream() { + if isLive { + let request = IMAPodStreamRequest(networkCode: "YOUR_NETWORK_CODE", customAssetKey: "YOUR_CUSTOM_ASSET_KEY", adDisplayContainer: adDisplayContainer!, videoDisplay: imaVideoDisplay, pictureInPictureProxy: nil, userContext: nil) + adsLoader?.requestStream(with: request) + } else { + let request = IMAPodVODStreamRequest(networkCode: "YOUR_NETWORK_CODE", adDisplayContainer: adDisplayContainer!, videoDisplay: imaVideoDisplay, pictureInPictureProxy: nil, userContext: nil) + adsLoader?.requestStream(with: request) + } +} +``` + +### B. Handling Load & Playback + +```swift +func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) { + streamManager = adsLoadedData.streamManager + streamManager?.delegate = self + streamManager?.initialize(with: nil) + + guard let streamID = streamManager?.streamId else { return } + let urlString = getStreamUrl(streamId: streamID) + guard let streamUrl = URL(string: urlString) else { return } + + if isLive { + imaVideoDisplay.loadStream(streamUrl, withSubtitles: []) + imaVideoDisplay.play() + } else { + streamManager?.loadThirdPartyStream(streamUrl, streamSubtitles: []) + } +} +``` diff --git a/skills/ads/ima-dai-sdk/references/roku.md b/skills/ads/ima-dai-sdk/references/roku.md new file mode 100644 index 0000000000..b8cdffcced --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/roku.md @@ -0,0 +1,144 @@ +# Google IMA SDK DAI - Roku Integration Guide + +This guide covers the integration steps for the Google IMA SDK for Roku DAI, which is designed for Roku SceneGraph (RSG) applications using BrightScript. + +-------------------------------------------------------------------------------- + +## 1. SDK Download & CLI Setup + +Download and install the Roku IMA SDK package into your Roku channel project structure using terminal CLI commands: + +```bash +# 1. Download the latest SDK package zip +curl -L -O https://pubads.g.doubleclick.net/html5/shared/js/sdks/roku/v3/roku_ima_sdk.zip + +# 2. Extract the package +unzip roku_ima_sdk.zip -d roku_ima_sdk + +# 3. Copy SDK components directly into your Roku channel project package structure +cp roku_ima_sdk/components/ImaSdk.brs components/ +cp roku_ima_sdk/components/ImaSdkTask.xml components/ +``` + +Include `ImaSdk.brs` and `ImaSdkTask.xml` inside your Roku project's `components/` directory so SceneGraph can load the SDK task node. + +-------------------------------------------------------------------------------- + +## 2. Component Initialization + +The Roku IMA SDK runs as a background `Task` node to avoid blocking the main render thread. Create the `ImaSdkTask` SceneGraph node in BrightScript: + +```brightscript +sub init() + m.video = m.top.findNode("myVideo") + + ' Create the ImaSdkTask SceneGraph node using createObject("roSGNode", "ImaSdkTask") + m.imaTask = createObject("roSGNode", "ImaSdkTask") + m.imaTask.observeField("sdkLoaded", "onSdkLoaded") + m.imaTask.observeField("errors", "onSdkErrors") + m.imaTask.observeField("adEvents", "onAdEvent") + + ' Start the task + m.imaTask.control = "RUN" +end sub +``` + +-------------------------------------------------------------------------------- + +## 3. Requesting a Stream + +Pass the stream request parameters to the SDK Task. + +### A. Live Stream Request + +```brightscript +sub requestLiveStream() + request = { + assetKey: "YOUR_ASSET_KEY" + apiKey: "YOUR_API_KEY" ' Optional + networkCode: "YOUR_NETWORK_CODE" + type: "live" + } + m.imaTask.requestStream = request +end sub +``` + +### B. VOD Stream Request + +```brightscript +sub requestVodStream() + request = { + contentSourceId: "YOUR_CMS_ID" + videoId: "YOUR_VIDEO_ID" + apiKey: "YOUR_API_KEY" ' Optional + networkCode: "YOUR_NETWORK_CODE" + type: "vod" + } + m.imaTask.requestStream = request +end sub +``` + +-------------------------------------------------------------------------------- + +## 4. Handling Stream Load & Playback + +When the stream is loaded, the SDK Task will return the stream URL via `streamInfo`. You must pass this URL to your `Video` node. + +```brightscript +sub onSdkLoaded(event as Object) + streamInfo = event.getData() + if streamInfo <> invalid and streamInfo.url <> invalid + ' Set the stream URL to the Video node + videoContent = createObject("roSGNode", "ContentNode") + videoContent.url = streamInfo.url + videoContent.streamFormat = "hls" + + m.video.content = videoContent + m.video.control = "play" + + ' Important: Pass the Video node to the SDK so it can monitor playback progress + m.imaTask.videoNode = m.video + end if +end sub +``` + +-------------------------------------------------------------------------------- + +## 5. Ad Event & Trickplay Control + +Roku OS does **NOT** automatically block seek or fast-forward inputs during ad breaks. You must explicitly control trickplay state when ad breaks begin and end. + +```brightscript +sub onAdEvent(event as Object) + adEvent = event.getData() + if adEvent <> invalid + if adEvent.type = "AD_BREAK_STARTED" + ' Disable seek/rewind/fast-forward trickplay on the Video node + m.video.enableTrickPlay = false + else if adEvent.type = "AD_BREAK_ENDED" + ' Re-enable trickplay controls + m.video.enableTrickPlay = true + end if + end if +end sub + +' Intercept remote key press events if your channel implements custom seeking UI +function onKeyEvent(key as String, press as Boolean) as Boolean + handled = false + if m.adIsPlaying + if key = "fastforward" or key = "rewind" or key = "left" or key = "right" + ' Block custom seeking during ad breaks + handled = true + end if + end if + return handled +end function +``` + +-------------------------------------------------------------------------------- + +## 6. Roku-Specific Considerations + +* **Trickplay Control:** Roku does NOT automatically enforce ad lockouts. You must explicitly set `enableTrickPlay = false` during `AD_BREAK_STARTED` and intercept remote key presses in `onKeyEvent` if custom seek controls are present. +* **Keep-Alive:** The `ImaSdkTask` node must remain running for the duration of the stream to send ad tracking pings. Do not destroy it until video playback ends. + diff --git a/skills/ads/ima-dai-sdk/references/web.md b/skills/ads/ima-dai-sdk/references/web.md new file mode 100644 index 0000000000..5c56125e47 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/web.md @@ -0,0 +1,220 @@ +# Google IMA SDK DAI - Web Integration Guide + +This guide covers the general lifecycle and integration steps for the Google IMA +DAI SDK on the Web (HTML5/JavaScript). + +For protocol-specific details, see [HLS Guide](hls.md) and +[DASH Guide](dash.md). For DAI workflows, see +[Full Service Guide](full-service.md) and [Pod Serving Guide](pod-serving.md). + +-------------------------------------------------------------------------------- + +## 1. SDK Setup + +Include the IMA DAI SDK script in your HTML: + +```html + +``` + +-------------------------------------------------------------------------------- + +## 2. Initialization + +Instantiate the `StreamManager` early. It requires the content video element and +the Ad UI container element (which overlays the video). + +```javascript +const videoElement = document.getElementById('video'); +const adUiElement = document.getElementById('adUi'); +let streamManager; + +function initializeDAI() { + // The Ad UI element must be positioned directly over the video element + streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement); + setupStreamManagerListeners(); +} +``` + +-------------------------------------------------------------------------------- + +## 3. Event Handling + +You must listen for lifecycle events to manage playback and UI. + +```javascript +function setupStreamManagerListeners() { + // Stream loaded (Full Service) + streamManager.addEventListener(google.ima.dai.api.StreamEvent.Type.LOADED, onStreamLoaded, false); + + // Stream initialized (Pod Serving) + streamManager.addEventListener(google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED, onStreamInitialized, false); + + // Error handling + streamManager.addEventListener(google.ima.dai.api.StreamEvent.Type.ERROR, onStreamError, false); + + // Ad Break events + streamManager.addEventListener( + [ + google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED, + google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED + ], + onAdBreakEvent, + false + ); +} +``` + +-------------------------------------------------------------------------------- + +## 4. Ad Break UI Management + +During ad breaks, you **must** disable custom player controls (seeking, pausing, +etc.) to prevent users from bypassing ads, and display the Ad UI overlay. + +```javascript +function onAdBreakEvent(e) { + switch (e.type) { + case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED: + videoElement.controls = false; // Hide native controls + adUiElement.style.display = 'block'; // Show Ad UI overlay + // Disable custom UI controls here + break; + case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED: + videoElement.controls = true; // Restore controls + adUiElement.style.display = 'none'; // Hide Ad UI overlay + // Enable custom UI controls here + break; + } +} +``` + +-------------------------------------------------------------------------------- + +## 5. Cleanup + +Destroy the `StreamManager` when the player is disposed to release resources. + +````javascript +function destroyDAI() { + if (streamManager) { + streamManager.destroy(); + streamManager = null; + } +} + +-------------------------------------------------------------------------------- + +## 6. HLS Integration (Timed Metadata) + +When playing HLS streams, you must extract timed metadata (ID3 tags) and pass it to the SDK. + +### A. hls.js (Cross-Browser) + +HLS.js extracts ID3 tags from TS and fMP4 media segments during fragment parsing and emits them via event listeners (`Hls.Events.FRAG_PARSING_METADATA`). You must pass the exact presentation timestamp (`sample.pts`) along with `sample.data` so the SDK matches ad cues accurately with video playback timeline. + +```javascript +import Hls from 'hls.js'; + +function setupHlsPlayer(streamUrl) { + const hls = new Hls(); + hls.loadSource(streamUrl); + hls.attachMedia(videoElement); + + hls.on(Hls.Events.FRAG_PARSING_METADATA, (event, data) => { + if (streamManager && data) { + data.samples.forEach((sample) => { + // Parameters: Type ('ID3'), Raw Data (Uint8Array), PTS (Presentation Timestamp) + // Pass the exact sample.pts so the SDK matches cues accurately + streamManager.processMetadata('ID3', sample.data, sample.pts); + }); + } + }); +} +``` + +If dash.js or ShakaPlayer is used, read and follow the official guide for extracting and processing timed metadata at https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/timed-metadata. + +-------------------------------------------------------------------------------- + +## 7. Custom Targeting Parameters + +Custom targeting parameters allow Google Ad Manager to serve targeted ad campaigns based on audience segments, content categories, or user metadata. + +Set key-value string pairs on `streamRequest.adTagParameters`: + +```typescript +const streamRequest = new google.ima.dai.api.LiveStreamRequest(); +streamRequest.assetKey = 'YOUR_ASSET_KEY'; +streamRequest.networkCode = 'YOUR_NETWORK_CODE'; + +// Configure custom targeting parameters for Google Ad Manager +streamRequest.adTagParameters = { + 'category': 'sports', + 'user_type': 'premium' +}; + +streamManager.requestStream(streamRequest); +``` + +-------------------------------------------------------------------------------- + +## 8. Iframe Integration + +When hosting the video player and the IMA SDK inside an ` +``` + +If using the `sandbox` attribute, you must include the following directives: + +* `allow-scripts`: Required to run the IMA SDK. +* `allow-same-origin`: Required for the SDK to function correctly and access necessary APIs. +* `allow-popups`: Required to allow ad click-throughs to open in new tabs/windows. + +### Cross-Origin Iframe Workaround + +If you detect that the IMA SDK is loaded inside an iframe from a domain different from the domain of the main page containing the video player, you must provide the main page's URL to the SDK by setting the `adsRequest.pageUrl` property. + +Example code: + +```typescript +const adsRequest: google.ima.AdsRequest = new google.ima.AdsRequest(); +adsRequest.adTagUrl = 'YOUR_AD_TAG_URL'; + +// Manually set the page URL to the parent page's URL +adsRequest.pageUrl = 'https:///'; +``` + +-------------------------------------------------------------------------------- + +## 9. Mobile Safari Considerations + +Mobile Safari (particularly on iOS/iPhone) has specific behaviors and restrictions that affect video playback and ad overlays. + +You must do the following steps: + +* Check and make sure that the `playsinline` attribute is added for the HTML `