設定 DAI 專用的 IMA SDK

使用 IMA SDK,即可輕鬆將多媒體廣告整合至網站和應用程式。IMA SDK 可向任何 符合 VAST 規定的廣告伺服器請求廣告,並在應用程式中管理廣告播放。應用程式可透過 IMA DAI SDK,對廣告和內容影片 (隨選影片或直播內容) 提出串流請求。SDK 接著會傳回合併的影片串流,因此您不必在應用程式中管理廣告和內容影片之間的切換。

選取您感興趣的 DAI 解決方案

全方位服務 DAI

本指南說明如何將 IMA DAI SDK 整合至簡易影片播放器應用程式。如要查看或跟著操作已完成的整合範例,請從 GitHub 下載 BasicExample

IMA DAI 總覽

如本指南所示,導入 IMA DAI 需使用四個主要 SDK 元件:

  • IMAAdDisplayContainer: 位於影片播放元素頂端的容器物件,內含廣告 UI 元素。
  • IMAAdsLoader: 這個物件會要求串流,並處理串流要求回應物件觸發的事件。 您應只例項化一個廣告載入器,並在整個應用程式生命週期內重複使用。
  • IMAStreamRequest IMAVODStreamRequest IMALiveStreamRequest。定義串流要求的物件。串流要求可以是隨選影片或直播串流。直播串流請求會指定素材資源金鑰,而 VOD 請求則會指定 CMS ID 和影片 ID。這兩種要求類型都可以選擇性地加入存取指定串流所需的 API 金鑰,以及 Google Ad Manager 聯播網代碼,供 IMA SDK 處理廣告 ID,如 Google Ad Manager 設定中所指定。
  • IMAStreamManager: 這個物件會處理動態廣告插播串流,以及與 DAI 後端的互動。串流管理工具也會處理追蹤 Ping,並將串流和廣告事件轉送給發布商。

必要條件

開始之前,請先備妥下列項目:

您也需要用來向 IMA SDK 請求串流的參數。 如需要求參數範例,請參閱「範例串流」。

直播參數
素材資源金鑰 素材資源金鑰 在 Google Ad Manager 中識別直播活動
範例:c-rArva4ShKVIAkNfy6HUQ
VOD 串流參數
內容來源 ID Google Ad Manager 的內容來源 ID。
範例:2548831
影片 ID Google Ad Manager 中的影片 ID。
範例:tears-of-steel
通用參數 (VOD 和直播)
聯播網代碼 您的 Google Ad Manager 聯播網代碼。
範例:21775744923

建立新的 Xcode 專案

在 Xcode 中,使用 Objective-C 建立名為「BasicExample」的新 iOS 專案。

將 IMA DAI SDK 新增至 Xcode 專案

請使用下列三種方法之一安裝 IMA DAI SDK。

使用 CocoaPods 安裝 SDK (建議)

CocoaPods 是 Xcode 專案的依附元件管理工具,建議您使用這個工具安裝 IMA DAI SDK。如要進一步瞭解如何安裝或使用 CocoaPods,請參閱 CocoaPods 說明文件。安裝 CocoaPods 後,請按照下列操作說明安裝 IMA DAI SDK:

  1. BasicExample.xcodeproj 檔案所在的目錄中,建立名為 Podfile 的文字檔案,並新增下列設定:

    source 'https://github.com/CocoaPods/Specs.git'
    
    platform :ios, '14'
    
    target 'BasicExample' do
      pod 'GoogleAds-IMA-iOS-SDK', '~> 3.26.1'
    end
    

  2. 在包含 Podfile 的目錄中執行下列指令:

    pod install --repo-update

使用 Swift Package Manager 安裝 SDK

互動式媒體廣告 SDK 從 3.18.4 版開始支援 Swift Package Manager。請按照下列步驟匯入 Swift 套件。

  1. 在 Xcode 中,依序前往「File」>「Add Packages」,安裝 IMA DAI SDK Swift Package。

  2. 在顯示的提示中,搜尋 IMA DAI SDK Swift Package GitHub 存放區:

    https://github.com/googleads/swift-package-manager-google-interactive-media-ads-ios
    
  3. 選取要使用的 IMA DAI SDK Swift 套件版本。對於新專案,建議使用「Up to Next Major Version」

完成後,Xcode 會解析套件依附元件,並在背景下載。如要進一步瞭解如何新增套件依附元件,請參閱 Apple 的文章

手動下載並安裝 SDK

如果不想使用 Swift Package Manager 或 CocoaPods,可以下載 IMA DAI SDK,然後手動新增至專案。

建立簡單的影片播放器

在主要檢視控制器中導入影片播放器,並使用封裝在 UI 檢視區塊中的 AV 播放器。IMA SDK 會使用 UI 檢視畫面顯示廣告 UI 元素。

Objective-C

#import "ViewController.h"

#import <AVKit/AVKit.h>

/// Content URL.
static NSString *const kBackupContentUrl =
    @"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";

@interface ViewController ()
/// Play button.
@property(nonatomic, weak) IBOutlet UIButton *playButton;

@property(nonatomic, weak) IBOutlet UIView *videoView;
/// Video player.
@property(nonatomic, strong) AVPlayer *videoPlayer;
@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.view.backgroundColor = [UIColor blackColor];

  // Load AVPlayer with the path to your content.
  NSURL *contentURL = [NSURL URLWithString:kBackupContentUrl];
  self.videoPlayer = [AVPlayer playerWithURL:contentURL];

  // Create a player layer for the player.
  AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer];

  // Size, position, and display the AVPlayer.
  playerLayer.frame = self.videoView.layer.bounds;
  [self.videoView.layer addSublayer:playerLayer];
}

- (IBAction)onPlayButtonTouch:(id)sender {
  [self.videoPlayer play];
  self.playButton.hidden = YES;
}

@end

Swift

// Copyright 2024 Google LLC. All rights reserved.
//
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the License for the specific language governing
// permissions and limitations under the License.

import AVFoundation
import UIKit

class ViewController: UIViewController {

  /// Content URL.
  static let backupStreamURLString =
    "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"

  /// Play button.
  @IBOutlet private weak var playButton: UIButton!

  @IBOutlet private weak var videoView: UIView!
  /// Video player.
  private var videoPlayer: AVPlayer?

  override func viewDidLoad() {
    super.viewDidLoad()

    playButton.layer.zPosition = CGFloat(MAXFLOAT)

    // Load AVPlayer with path to our content.
    // note: this unwrap is safe because the URL is a constant string.
    let contentURL = URL(string: ViewController.backupStreamURLString)!
    videoPlayer = AVPlayer(url: contentURL)

    // Create a player layer for the player.
    let playerLayer = AVPlayerLayer(player: videoPlayer)

    // Size, position, and display the AVPlayer.
    playerLayer.frame = videoView.layer.bounds
    videoView.layer.addSublayer(playerLayer)
  }

  @IBAction func onPlayButtonTouch(_ sender: Any) {
    videoPlayer?.play()
    playButton.isHidden = true
  }
}

初始化廣告載入器

將 IMA SDK 匯入檢視畫面控制器,並採用 IMAAdsLoaderDelegateIMAStreamManagerDelegate 通訊協定,處理廣告載入器和串流管理員事件。

新增這些私有屬性,儲存重要的 IMA SDK 元件:

在檢視畫面載入後,初始化廣告載入器、廣告顯示容器和影片顯示。

Objective-C

@import GoogleInteractiveMediaAds;

// ...

@interface ViewController () <IMAAdsLoaderDelegate, IMAStreamManagerDelegate>
/// The entry point for the IMA DAI SDK to make DAI stream requests.
@property(nonatomic, strong) IMAAdsLoader *adsLoader;
/// The container where the SDK renders each ad's user interface elements and companion slots.
@property(nonatomic, strong) IMAAdDisplayContainer *adDisplayContainer;
/// The reference of your video player for the IMA DAI SDK to monitor playback and handle timed
/// metadata.
@property(nonatomic, strong) IMAAVPlayerVideoDisplay *imaVideoDisplay;
/// References the stream manager from the IMA DAI SDK after successful stream loading.
@property(nonatomic, strong) IMAStreamManager *streamManager;

// ...

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // ...

  self.adsLoader = [[IMAAdsLoader alloc] initWithSettings:nil];
  self.adsLoader.delegate = self;

  // Create an ad display container for rendering each ad's user interface elements and companion
  // slots.
  self.adDisplayContainer =
      [[IMAAdDisplayContainer alloc] initWithAdContainer:self.videoView
                                          viewController:self
                                          companionSlots:nil];

  // Create an IMAAVPlayerVideoDisplay to give the SDK access to your video player.
  self.imaVideoDisplay = [[IMAAVPlayerVideoDisplay alloc] initWithAVPlayer:self.videoPlayer];
}

Swift

import GoogleInteractiveMediaAds
// ...

class ViewController: UIViewController, IMAAdsLoaderDelegate, IMAStreamManagerDelegate {
  // ...

  /// The entry point for the IMA DAI SDK to make DAI stream requests.
  private var adsLoader: IMAAdsLoader?
  /// The container where the SDK renders each ad's user interface elements and companion slots.
  private var adDisplayContainer: IMAAdDisplayContainer?
  /// The reference of your video player for the IMA DAI SDK to monitor playback and handle timed
  /// metadata.
  private var imaVideoDisplay: IMAAVPlayerVideoDisplay!
  /// References the stream manager from the IMA DAI SDK after successfully loading the DAI stream.
  private var streamManager: IMAStreamManager?

  // ...

  override func viewDidLoad() {
    super.viewDidLoad()

    // ...

    adsLoader = IMAAdsLoader(settings: nil)
    adsLoader?.delegate = self

    // Create an ad display container for rendering ad UI elements and the companion ad.
    adDisplayContainer = IMAAdDisplayContainer(
      adContainer: videoView,
      viewController: self,
      companionSlots: nil)

    // Create an IMAAVPlayerVideoDisplay to give the SDK access to your video player.
    imaVideoDisplay = IMAAVPlayerVideoDisplay(avPlayer: videoPlayer)
  }

提出串流要求

使用者按下播放按鈕時,請發出新的串流要求。 使用 IMALiveStreamRequest 類別進行直播。如為 VOD 串流,請使用 IMAVODStreamRequest 類別。

串流請求需要串流參數,以及廣告顯示容器和影片顯示的參照。

Objective-C

- (IBAction)onPlayButtonTouch:(id)sender {
  [self requestStream];
  self.playButton.hidden = YES;
}

- (void)requestStream {
  // Create a stream request. Use one of "Live stream request" or "VOD request", depending on your
  // type of stream.
  IMAStreamRequest *request;
  if (kStreamType == StreamTypeLive) {
    // Live stream request. Replace the asset key with your value.
    request = [[IMALiveStreamRequest alloc] initWithAssetKey:kLiveStreamAssetKey
                                                 networkCode:kNetworkCode
                                          adDisplayContainer:self.adDisplayContainer
                                                videoDisplay:self.imaVideoDisplay
                                                 userContext:nil];
  } else {
    // VOD request. Replace the content source ID and video ID with your values.
    request = [[IMAVODStreamRequest alloc] initWithContentSourceID:kVODContentSourceID
                                                           videoID:kVODVideoID
                                                       networkCode:kNetworkCode
                                                adDisplayContainer:self.adDisplayContainer
                                                      videoDisplay:self.imaVideoDisplay
                                                       userContext:nil];
  }
  [self.adsLoader requestStreamWithRequest:request];
}

Swift

@IBAction func onPlayButtonTouch(_ sender: Any) {
  requestStream()
  playButton.isHidden = true
}

func requestStream() {
  // Create a stream request. Use one of "Livestream request" or "VOD request".
  if ViewController.requestType == StreamType.live {
    // Livestream request.
    let request = IMALiveStreamRequest(
      assetKey: ViewController.assetKey,
      networkCode: ViewController.networkCode,
      adDisplayContainer: adDisplayContainer!,
      videoDisplay: imaVideoDisplay,
      userContext: nil)
    adsLoader?.requestStream(with: request)
  } else {
    // VOD stream request.
    let request = IMAVODStreamRequest(
      contentSourceID: ViewController.contentSourceID,
      videoID: ViewController.videoID,
      networkCode: ViewController.networkCode,
      adDisplayContainer: adDisplayContainer!,
      videoDisplay: imaVideoDisplay,
      userContext: nil)
    adsLoader?.requestStream(with: request)
  }
}

監聽串流載入事件

如果串流要求初始化成功或失敗,IMAAdsLoader 類別會呼叫 IMAAdsLoaderDelegate 方法。

adsLoadedWithData 委派方法中,設定 IMAStreamManagerDelegate。 初始化串流管理工具。初始化時,串流管理工具會開始播放。

failedWithErrorData 委派方法中,記錄錯誤。你也可以播放備份串流。請參閱 DAI 最佳做法

Objective-C

- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {
  NSLog(@"Stream created with: %@.", adsLoadedData.streamManager.streamId);
  self.streamManager = adsLoadedData.streamManager;
  self.streamManager.delegate = self;
  [self.streamManager initializeWithAdsRenderingSettings:nil];
}

- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {
  // Log the error and play the content.
  NSLog(@"AdsLoader error, code:%ld, message: %@", adErrorData.adError.code,
        adErrorData.adError.message);
  [self.videoPlayer play];
}

Swift

func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) {
  print("DAI stream loaded. Stream session ID: \(adsLoadedData.streamManager!.streamId!)")
  streamManager = adsLoadedData.streamManager!
  streamManager!.delegate = self
  streamManager!.initialize(with: nil)
}

func adsLoader(_ loader: IMAAdsLoader, failedWith adErrorData: IMAAdLoadingErrorData) {
  print("Error loading DAI stream. Error message: \(adErrorData.adError.message!)")
  // Play the backup stream.
  videoPlayer.play()
}

監聽廣告事件

IMAStreamManager 會呼叫 IMAStreamManagerDelegate 方法,將串流事件和錯誤傳遞至應用程式。

以這個範例來說,請將主要廣告事件記錄到控制台:

Objective-C

- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdEvent:(IMAAdEvent *)event {
  NSLog(@"Ad event (%@).", event.typeString);
  switch (event.type) {
    case kIMAAdEvent_STARTED: {
      // Log extended data.
      NSString *extendedAdPodInfo = [[NSString alloc]
          initWithFormat:@"Showing ad %ld/%ld, bumper: %@, title: %@, description: %@, contentType:"
                         @"%@, pod index: %ld, time offset: %lf, max duration: %lf.",
                         (long)event.ad.adPodInfo.adPosition, (long)event.ad.adPodInfo.totalAds,
                         event.ad.adPodInfo.isBumper ? @"YES" : @"NO", event.ad.adTitle,
                         event.ad.adDescription, event.ad.contentType,
                         (long)event.ad.adPodInfo.podIndex, event.ad.adPodInfo.timeOffset,
                         event.ad.adPodInfo.maxDuration];

      NSLog(@"%@", extendedAdPodInfo);
      break;
    }
    case kIMAAdEvent_AD_BREAK_STARTED: {
      NSLog(@"Ad break started");
      break;
    }
    case kIMAAdEvent_AD_BREAK_ENDED: {
      NSLog(@"Ad break ended");
      break;
    }
    case kIMAAdEvent_AD_PERIOD_STARTED: {
      NSLog(@"Ad period started");
      break;
    }
    case kIMAAdEvent_AD_PERIOD_ENDED: {
      NSLog(@"Ad period ended");
      break;
    }
    default:
      break;
  }
}

- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdError:(IMAAdError *)error {
  NSLog(@"StreamManager error with type: %ld\ncode: %ld\nmessage: %@", error.type, error.code,
        error.message);
  [self.videoPlayer play];
}

Swift

func streamManager(_ streamManager: IMAStreamManager, didReceive event: IMAAdEvent) {
  print("Ad event \(event.typeString).")
  switch event.type {
  case IMAAdEventType.STARTED:
    // Log extended data.
    if let ad = event.ad {
      let extendedAdPodInfo = String(
        format: "Showing ad %zd/%zd, bumper: %@, title: %@, "
          + "description: %@, contentType:%@, pod index: %zd, "
          + "time offset: %lf, max duration: %lf.",
        ad.adPodInfo.adPosition,
        ad.adPodInfo.totalAds,
        ad.adPodInfo.isBumper ? "YES" : "NO",
        ad.adTitle,
        ad.adDescription,
        ad.contentType,
        ad.adPodInfo.podIndex,
        ad.adPodInfo.timeOffset,
        ad.adPodInfo.maxDuration)

      print("\(extendedAdPodInfo)")
    }
    break
  case IMAAdEventType.AD_BREAK_STARTED:
    print("Ad break started.")
    break
  case IMAAdEventType.AD_BREAK_ENDED:
    print("Ad break ended.")
    break
  case IMAAdEventType.AD_PERIOD_STARTED:
    print("Ad period started.")
    break
  case IMAAdEventType.AD_PERIOD_ENDED:
    print("Ad period ended.")
    break
  default:
    break
  }
}

func streamManager(_ streamManager: IMAStreamManager, didReceive error: IMAAdError) {
  print("StreamManager error with type: \(error.type)")
  print("code: \(error.code)")
  print("message: \(error.message ?? "Unknown Error")")
}

執行應用程式,如果成功,您就能使用 IMA SDK 請求及播放 Google DAI 串流。如要瞭解更多進階 SDK 功能,請參閱左側邊欄列出的其他指南,或 GitHub 上的範例