From 5a04a7a75a53ffd0939792a2fb961a6485881189 Mon Sep 17 00:00:00 2001 From: Cloud IX Team Date: Mon, 13 Jul 2026 14:39:35 -0700 Subject: [PATCH] Add ima-dai-sdk skill directory and files. PiperOrigin-RevId: 947251048 --- README.md | 26 +- skills/ads/ima-dai-sdk/SKILL.md | 89 ++++++ .../references/android-ima-extension.md | 162 +++++++++++ skills/ads/ima-dai-sdk/references/dash.md | 31 +++ .../ima-dai-sdk/references/full-service.md | 259 ++++++++++++++++++ skills/ads/ima-dai-sdk/references/hls.md | 31 +++ skills/ads/ima-dai-sdk/references/ios-tvos.md | 156 +++++++++++ .../ads/ima-dai-sdk/references/pod-serving.md | 159 +++++++++++ skills/ads/ima-dai-sdk/references/roku.md | 131 +++++++++ skills/ads/ima-dai-sdk/references/web.md | 233 ++++++++++++++++ .../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, 1284 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..9ffcb41c09 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/android-ima-extension.md @@ -0,0 +1,162 @@ +# 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") + + // CRITICAL: Add the Media3 IMA Extension + implementation("androidx.media3:media3-exoplayer-ima:$latestVersion") +} +``` + +-------------------------------------------------------------------------------- + +### 2. Initialization + +Configure the SDK early to minimize latency, and reuse the SDK settings. + +* **Early Warmup & Settings Reuse (Best Practice):** Call + `ImaSdkFactory.initialize()` in your `Application.onCreate()` to preload SDK + resources. +* Reuse the same `ImaSdkSettings` instance when creating the + `ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setImaSdkSettings` to + avoid cache misses that cause latency spikes. + +```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 = YOUR_LANGUAGE_CODE // Use 2 lowercase letters to identify + // the language of your app UI. + isDebugMode = true + } + + sdkFactory.initialize(this, imaSettings) + } +} +... + +private ImaServerSideAdInsertionMediaSource.AdsLoader createAdsLoader() { + val adsLoaderBuilder = new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView); + + return adsLoaderBuilder + .setImaSdkSettings(getImaSdkSettings()) + .build(); +} +``` + +### 3. Build DAI Request UIRs + +Create an `ImaServerSideAdInsertionUriBuilder` to build an SSAI livestream URL. + +Make sure to use an `assetKey`, which is a string of alphanumeric characters as +a URL-safe base64 encoded value. For example, here are the assetKeys of DAI +sample streams: `c-rArva4ShKVIAkNfy6HUQ`, +`XYrjlG09QTa8pxAo5Fzjww`,`PSzZMzAkSXCmlJOWDmRj8Q`. + +```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_DASH) + .build() + +// Example 2: HLS VOD Stream +val hlsVodUri: Uri = ImaServerSideAdInsertionUriBuilder() + .setContentSourceId("YOUR_CMS_ID") + .setVideoId("YOUR_VIDEO_ID") + .setFormat(C.CONTENT_TYPE_DASH) + .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..66fd5fc24e --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/dash.md @@ -0,0 +1,31 @@ +# 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(...) + request.format = StreamFormat.DASH + ``` + + 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..321456190a --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/full-service.md @@ -0,0 +1,259 @@ +# 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; + playStream(streamUrl); // Load into your player (hls.js, dash.js, etc.) + } +} + +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..b37395d2f0 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/ios-tvos.md @@ -0,0 +1,156 @@ +# 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() + case .AD_BREAK_ENDED: + adIsPlaying = false + enableAppGestures() // Re-enable custom gestures + setNeedsFocusUpdate() + 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..3995383815 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/pod-serving.md @@ -0,0 +1,159 @@ +# 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. + +-------------------------------------------------------------------------------- + +## 1. Web Integration + +### A. Requesting the Stream + +Use `PodStreamRequest` (Livestream) or `PodVodStreamRequest` (VOD). + +```typescript +let streamManager: google.ima.dai.api.StreamManager; + +function requestStream(): void { + const streamRequest = new google.ima.dai.api.PodStreamRequest(); + streamRequest.networkCode = 'YOUR_NETWORK_CODE'; + streamRequest.customAssetKey = 'YOUR_CUSTOM_ASSET_KEY'; + streamManager.requestStream(streamRequest); + + // For VOD stream, use the code lines below + // const streamRequest = new google.ima.dai.api.PodVodStreamRequest(); + // streamRequest.networkCode = 'YOUR_NETWORK_CODE'; + // streamManager.requestStream(streamRequest); + } +``` + +### B. Handling `STREAM_INITIALIZED` & Playback + +Listen for `STREAM_INITIALIZED` to get the `streamId`, build the URL, and play. + +```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; + const url = YOUR_STREAM_URL.replace('[[STREAMID]]', streamId); + playStream(url); +} +``` + +For VOD, you must pass the `streamId` to your manifest manipulator, call +`loadStreamMetadata()` to fetch the ad cue points, and retrieve or construct the +stream URL to the video player. + +```typescript +function onStreamInitializedVOD(e: google.ima.dai.api.StreamEvent): void { + const streamData = e.getStreamData(); + if (!streamData) return; + + const streamId = streamData.streamId; + + // 1. Send the DAI stream ID to your video stitcher to retrieve a stream URL + getStreamUrl({ streamId: streamId }).then((streamUrl: string) => { + // 2. Load the ad metadata from Google + streamManager.loadStreamMetadata(); + + // 3. Play the stream + playStream(streamUrl); + }); +} +``` + +-------------------------------------------------------------------------------- + +## 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 = + sdkFactory.createPodStreamRequest(NETWORK_CODE, CUSTOM_ASSET_KEY, API_KEY) + // or VOD: sdkFactory.createPodVodStreamRequest(NETWORK_CODE) + +request.format = StreamFormat.HLS // or DASH +adsLoader.requestStream(request) +``` + +### B. Handling Load & Playback + +Extract the `streamId`, construct the URL, and load it. For Live, play directly. +For VOD, load via `StreamManager`. + +```kotlin +override fun onAdsManagerLoaded(event: AdsManagerLoadedEvent) { + val streamManager = event.streamManager + streamManager.init() + val streamId = streamManager.streamId + + if (isLive) { + val liveStreamUrl = getLiveStreamURL(streamId) + videoPlayer.setStreamUrl(liveStreamUrl) + videoPlayer.play() // SDK does NOT call VideoStreamPlayer.loadUrl for live pod serving + } else { + val vodStreamUrl = getVODStreamURL(streamId) + // Note: Android SDK does not have a separate loadStreamMetadata() method. + // loadThirdPartyStream() automatically retrieves ad metadata and loads the stream. + streamManager.loadThirdPartyStream(vodStreamUrl, emptyList()) // Triggers VideoStreamPlayer.loadUrl() + } +} +``` + +-------------------------------------------------------------------------------- + +## 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..8cef9c207e --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/roku.md @@ -0,0 +1,131 @@ +# 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 Setup + +Download Roku IMA SDK zip package using the following commands: + +```bash +curl -L -O https://pubads.g.doubleclick.net/html5/shared/js/sdks/roku/v3/roku_ima_sdk.zip +``` + +Install the IMA SDK to your app. + +-------------------------------------------------------------------------------- + +## 2. Initialization + +The Roku IMA SDK runs as a background `Task` node to prevent blocking the main +render thread. + +```brightscript +sub init() + m.video = m.top.findNode("myVideo") + + ' Create the IMA SDK Task node + 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 Handling + +Monitor `adEvents` to manage player controls (like disabling trickplay/seeking +during ads). + +```brightscript +sub onAdEvent(event as Object) + adEvent = event.getData() + if adEvent <> invalid + if adEvent.type = "AD_BREAK_STARTED" + ' Disable seek/rewind/fast-forward + m.video.enableTrickPlay = false + else if adEvent.type = "AD_BREAK_ENDED" + ' Re-enable trickplay + m.video.enableTrickPlay = true + end if + end if +end sub +``` + +-------------------------------------------------------------------------------- + +## 6. Roku-Specific Considerations + +* **Trickplay Control:** You must explicitly disable `enableTrickPlay` on the + `Video` node during ad breaks, as Roku does not automatically block user + seek inputs. +* **Keep-Alive:** The `ImaSdkTask` must remain running for the duration of the + stream to handle tracking pings. Do not destroy it until the video is + closed. 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..0421b46ac1 --- /dev/null +++ b/skills/ads/ima-dai-sdk/references/web.md @@ -0,0 +1,233 @@ +# 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) +Listen for `Hls.Events.FRAG_PARSING_METADATA` and pass the raw ID3 data to `StreamManager.processMetadata()`. + +```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) + streamManager.processMetadata('ID3', sample.data, sample.pts); + }); + } + }); +} +```` + +If dash.js, 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 + +### B. Native Safari (iOS/macOS) + +Safari handles HLS natively. Listen for `cuechange` events on the metadata +`TextTrack` and pass the parsed metadata to `StreamManager.onTimedMetadata()`. + +```javascript +function setupNativePlayer(streamUrl) { + videoElement.src = streamUrl; + videoElement.textTracks.addEventListener('addtrack', (event) => { + const track = event.track; + if (track.kind === 'metadata') { + track.mode = 'hidden'; + track.addEventListener('cuechange', () => { + for (const cue of track.activeCues) { + const metadata = {}; + metadata[cue.value.key] = cue.value.data; + streamManager.onTimedMetadata(metadata); + } + }); + } + }); +} +``` + +-------------------------------------------------------------------------------- + +## 7. 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 Work-around + +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:///'; +``` + +-------------------------------------------------------------------------------- + +## 8. 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 + `