Menyiapkan IMA SDK untuk DAI

Pilih platform: HTML5 Android iOS tvOS Cast Roku

IMA SDK mempermudah integrasi iklan multimedia ke dalam situs dan aplikasi Anda. IMA SDK dapat meminta iklan dari server iklan yang kompatibel dengan VAST dan mengelola pemutaran iklan di aplikasi Anda. Dengan IMA DAI SDK, aplikasi membuat permintaan streaming untuk video iklan dan konten—baik konten VOD maupun live. Kemudian, SDK akan menampilkan streaming video gabungan, sehingga Anda tidak perlu mengelola peralihan antara video iklan dan konten dalam aplikasi Anda.

Pilih solusi DAI yang Anda minati

DAI layanan lengkap

Panduan ini menunjukkan cara mengintegrasikan IMA DAI SDK ke dalam aplikasi pemutar video sederhana. Jika Anda ingin melihat atau mengikuti contoh integrasi yang telah selesai, download BasicExample dari GitHub.

Ringkasan IMA DAI

Penerapan DAI IMA melibatkan tiga komponen SDK utama seperti yang ditunjukkan dalam panduan ini:

  • IMAAdDisplayContainer: Objek penampung yang berada di atas elemen pemutaran video dan menampung elemen UI iklan.
  • IMAAdsLoader: Objek yang meminta streaming dan menangani peristiwa yang dipicu oleh objek respons permintaan streaming. Anda hanya boleh membuat instance satu pemuat iklan, yang dapat digunakan kembali selama masa aktif aplikasi.
  • IMAStreamRequest IMAVODStreamRequest atau IMALiveStreamRequest: Objek yang menentukan permintaan streaming. Permintaan streaming dapat berupa video-on-demand atau live stream. Permintaan live stream menentukan kunci aset, sedangkan permintaan VOD menentukan ID CMS dan ID video. Kedua jenis permintaan dapat secara opsional menyertakan kunci API yang diperlukan untuk mengakses streaming tertentu, dan kode jaringan Google Ad Manager agar IMA SDK menangani ID iklan seperti yang ditentukan dalam setelan Google Ad Manager.
  • IMAStreamManager: Objek yang menangani streaming penyisipan iklan dinamis dan interaksi dengan backend DAI. Pengelola streaming juga menangani pelacakan ping dan meneruskan peristiwa streaming dan iklan ke penayang.

Prasyarat

Sebelum memulai, Anda memerlukan hal berikut:

Buat project Xcode baru

Di Xcode, buat project tvOS baru menggunakan Objective-C. Gunakan BasicExample sebagai nama project.

Tambahkan IMA DAI SDK ke project Xcode

Gunakan salah satu dari tiga metode ini untuk menginstal IMA DAI SDK.

Menginstal SDK menggunakan Swift Package Manager

Interactive Media Ads SDK mendukung Swift Package Manager mulai dari versi 4.8.2. Ikuti langkah-langkah berikut untuk mengimpor paket Swift.

  1. Di Xcode, instal GoogleInteractiveMediaAds Swift Package dengan membuka File > Add Packages.

  2. Pada perintah yang muncul, telusuri repositori GitHub GoogleInteractiveMediaAds Swift Package:

    https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos
    
  3. Pilih versi GoogleInteractiveMediaAds Swift Package yang ingin Anda gunakan. Untuk project baru, sebaiknya gunakan Up to Next Major Version.

Setelah selesai, Xcode akan menyelesaikan dependensi paket Anda dan mendownloadnya di latar belakang. Untuk mengetahui detail selengkapnya tentang cara menambahkan dependensi paket, lihat artikel Apple.

Menginstal SDK menggunakan CocoaPods

CocoaPods adalah pengelola dependensi untuk project Xcode dan merupakan metode yang direkomendasikan untuk menginstal IMA DAI SDK. Untuk mengetahui informasi selengkapnya tentang cara menginstal atau menggunakan CocoaPods, lihat dokumentasi CocoaPods. Setelah menginstal CocoaPods, gunakan petunjuk berikut untuk menginstal IMA DAI SDK:

  1. Di direktori yang sama dengan file BasicExample.xcodeproj, buat file teks bernama Podfile, lalu tambahkan konfigurasi berikut:

    source 'https://github.com/CocoaPods/Specs.git'
    platform :tvos, '15'
    target "BasicExample" do
      pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.16.0'
    end
    
  2. Dari direktori yang berisi Podfile, jalankan:

    pod install --repo-update
  3. Verifikasi bahwa penginstalan berhasil dengan membuka file BasicExample.xcworkspace dan mengonfirmasi bahwa file tersebut berisi dua project: BasicExample dan Pods (dependensi yang diinstal oleh CocoaPods).

Mendownload dan menginstal SDK secara manual

Jika tidak ingin menggunakan Swift Package Manager atau CocoaPods, Anda dapat mendownload IMA DAI SDK dan menambahkannya secara manual ke project Anda.

Mengimpor IMA SDK

Tambahkan framework IMA menggunakan pernyataan impor:

Objective-C

#import "ViewController.h"
#import <AVKit/AVKit.h>

@import GoogleInteractiveMediaAds;

Swift

import AVFoundation
import GoogleInteractiveMediaAds
import UIKit

Buat pemutar video dan integrasikan IMA SDK

Contoh berikut melakukan inisialisasi IMA SDK:

Objective-C

// Live stream asset key, VOD content source and video IDs, and backup content URL.
static NSString *const kAssetKey = @"c-rArva4ShKVIAkNfy6HUQ";
static NSString *const kContentSourceID = @"2548831";
static NSString *const kVideoID = @"tears-of-steel";
static NSString *const kNetworkCode = @"21775744923";
static NSString *const kBackupStreamURLString =
    @"http://googleimadev-vh.akamaihd.net/i/big_buck_bunny/bbb-,480p,720p,1080p,.mov.csmil/"
    @"master.m3u8";
static const StreamType kDefaultStreamType = StreamTypeLive;

@interface ViewController () <IMAAdsLoaderDelegate,
                              IMAStreamManagerDelegate,
                              AVPlayerViewControllerDelegate>
@property(nonatomic) IMAAdsLoader *adsLoader;
@property(nonatomic) IMAAdDisplayContainer *adDisplayContainer;
@property(nonatomic) UIView *adContainerView;
@property(nonatomic) id<IMAVideoDisplay> videoDisplay;
@property(nonatomic) IMAStreamManager *streamManager;
@property(nonatomic) AVPlayerViewController *playerViewController;
@property(nonatomic, getter=isAdBreakActive) BOOL adBreakActive;
@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.view.backgroundColor = [UIColor blackColor];
  self.streamType = kDefaultStreamType;
  [self setupAdsLoader];
  [self setupPlayer];
  [self setupAdContainer];
}

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  [self requestStream];
}

- (void)setupPlayer {
  // Create a stream video player.
  AVPlayer *player = [[AVPlayer alloc] init];
  self.playerViewController = [[AVPlayerViewController alloc] init];
  self.playerViewController.player = player;

  // Attach video player to view hierarchy.
  [self addChildViewController:self.playerViewController];
  [self.view addSubview:self.playerViewController.view];
  self.playerViewController.view.frame = self.view.bounds;
  [self.playerViewController didMoveToParentViewController:self];
}

Swift

class ViewController:
  UIViewController,
  IMAAdsLoaderDelegate,
  IMAStreamManagerDelegate,
  AVPlayerViewControllerDelegate
{
  // Live stream asset key, VOD content source and video IDs, Google Ad Manager network code, and
  // backup content URL.
  static let assetKey = "c-rArva4ShKVIAkNfy6HUQ"
  static let contentSourceID = "2548831"
  static let videoID = "tears-of-steel"
  static let networkCode = "21775744923"
  static let backupStreamURLString =
    "http://googleimadev-vh.akamaihd.net/i/big_buck_bunny/bbb-,480p,720p,1080p,.mov.csmil/master.m3u8"

  var adsLoader: IMAAdsLoader?
  var videoDisplay: IMAAVPlayerVideoDisplay!
  var adDisplayContainer: IMAAdDisplayContainer?
  var adContainerView: UIView?
  private var streamManager: IMAStreamManager?
  private var contentPlayhead: IMAAVPlayerContentPlayhead?
  private var playerViewController: AVPlayerViewController!
  private var userSeekTime = 0.0
  private var adBreakActive = false

  private enum StreamType {
    case live
    /// Video on demand.
    case vod
  }

  /// Set the stream type here.
  private let currentStreamType: StreamType = .live

  deinit {
    NotificationCenter.default.removeObserver(self)
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = UIColor.black

    setupAdsLoader()
    setupPlayer()
    setupAdContainer()
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    requestStream()
  }

  func setupPlayer() {
    let player = AVPlayer()
    let playerViewController = AVPlayerViewController()
    playerViewController.delegate = self
    playerViewController.player = player

    // Set up our content playhead and contentComplete callback.
    contentPlayhead = IMAAVPlayerContentPlayhead(avPlayer: player)
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(ViewController.contentDidFinishPlaying(_:)),
      name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
      object: player.currentItem)

    self.addChild(playerViewController)
    playerViewController.view.frame = self.view.bounds
    self.view.insertSubview(playerViewController.view, at: 0)
    playerViewController.didMove(toParent: self)
    self.playerViewController = playerViewController
  }

Di viewDidLoad(), setupAdsLoader() membuat IMAAdsLoader, setupPlayer() membuat AVPlayerViewController, dan setupAdContainer() menyiapkan UIView untuk penayangan iklan. Saat tampilan menjadi terlihat, viewDidAppear() memanggil requestStream() untuk meminta streaming DAI.

Untuk menentukan parameter permintaan streaming, contoh ini menggunakan konstanta, seperti asset key untuk Live stream, atau content source ID dan video ID untuk streaming VOD. Contoh ini juga menggunakan komponen berikut untuk mengelola IMA SDK:

  • adsLoader: Menangani permintaan streaming ke Google Ad Manager. Sebaiknya gunakan satu instance untuk siklus proses aplikasi.
  • videoDisplay: Implementasi IMAVideoDisplay yang memungkinkan IMA mengontrol pemutaran video dan melacak peristiwa pemutaran menggunakan AVPlayer.
  • adDisplayContainer: Mengelola tampilan yang digunakan untuk merender elemen UI iklan dan menangani fokus UI selama jeda iklan.
  • streamManager: Mengelola pemutaran gabungan iklan dan streaming konten serta mengirim peristiwa siklus proses iklan menggunakan delegasinya.
  • playerViewController: Pemutar tvOS yang digunakan untuk menampilkan streaming video yang dikelola oleh IMA SDK.
  • adBreakActive: Flag boolean yang menunjukkan apakah jeda iklan sedang diputar, digunakan untuk mencegah pencarian iklan dan mengelola fokus UI.

Mengimplementasikan IMAAdsLoader

Selanjutnya, buat instance IMAAdsLoader dan lampirkan tampilan penampung iklan ke hierarki tampilan.

Objective-C

- (void)setupAdsLoader {
  self.adsLoader = [[IMAAdsLoader alloc] init];
  self.adsLoader.delegate = self;
}

- (void)setupAdContainer {
  // Attach the ad container to the view hierarchy on top of the player.
  self.adContainerView = [[UIView alloc] init];
  [self.view addSubview:self.adContainerView];
  self.adContainerView.frame = self.view.bounds;
  // Keep hidden initially, until an ad break.
  self.adContainerView.hidden = YES;
}

Swift

func setupAdsLoader() {
  let adsLoader = IMAAdsLoader(settings: nil)
  adsLoader.delegate = self
  self.adsLoader = adsLoader
}

func setupAdContainer() {
  // Attach the ad container to the view hierarchy on top of the player.
  let adContainerView = UIView()
  self.view.addSubview(adContainerView)
  adContainerView.frame = self.view.bounds
  // Keep hidden initially, until an ad break.
  adContainerView.isHidden = true
  self.adContainerView = adContainerView
}

Membuat permintaan streaming

Buat beberapa konstanta untuk menyimpan informasi streaming, lalu terapkan fungsi permintaan streaming untuk membuat permintaan.

Objective-C

- (void)requestStream {
  self.videoDisplay =
      [[IMAAVPlayerVideoDisplay alloc] initWithAVPlayer:self.playerViewController.player];
  self.adDisplayContainer = [[IMAAdDisplayContainer alloc] initWithAdContainer:self.adContainerView
                                                                viewController:self];

  // Use the streamType property to determine which request to create.
  IMAStreamRequest *request;

  switch (self.streamType) {
    case StreamTypeLive: {
      request = [[IMALiveStreamRequest alloc] initWithAssetKey:kAssetKey
                                                   networkCode:kNetworkCode
                                            adDisplayContainer:self.adDisplayContainer
                                                  videoDisplay:self.videoDisplay
                                                   userContext:nil];
      NSLog(@"IMA: Requesting Live Stream with Asset Key: %@.", kAssetKey);
      break;
    }
    case StreamTypeVOD: {
      request = [[IMAVODStreamRequest alloc] initWithContentSourceID:kContentSourceID
                                                             videoID:kVideoID
                                                         networkCode:kNetworkCode
                                                  adDisplayContainer:self.adDisplayContainer
                                                        videoDisplay:self.videoDisplay
                                                         userContext:nil];
      NSLog(@"IMA: Requesting VOD Stream with Video ID: %@.", kVideoID);
      break;
    }
  }

  if (request) {
    [self.adsLoader requestStreamWithRequest:request];
  } else {
    // Fallback or error handling if no request object was created
    NSLog(@"IMA Error: Could not create stream request for unknown type.");
    [self playBackupStream];
  }
}

Swift

func requestStream() {
  guard let playerViewController = self.playerViewController else { return }
  guard let adContainerView = self.adContainerView else { return }
  guard let adsLoader = self.adsLoader else { return }

  self.videoDisplay = IMAAVPlayerVideoDisplay(avPlayer: playerViewController.player!)
  let adDisplayContainer = IMAAdDisplayContainer(
    adContainer: adContainerView, viewController: self)
  self.adDisplayContainer = adDisplayContainer

  // Variable to hold the specific stream request object.
  let request: IMAStreamRequest

  switch self.currentStreamType {
  case .live:
    // Create a live stream request.
    request = IMALiveStreamRequest(
      assetKey: ViewController.assetKey,
      networkCode: ViewController.networkCode,
      adDisplayContainer: adDisplayContainer,
      videoDisplay: self.videoDisplay,
      pictureInPictureProxy: nil,
      userContext: nil)
    print("IMA: Requesting Live Stream with asset key \(ViewController.assetKey)")

  case .vod:
    // Create a VOD stream request.
    request = IMAVODStreamRequest(
      contentSourceID: ViewController.contentSourceID,
      videoID: ViewController.videoID,
      networkCode: ViewController.networkCode,
      adDisplayContainer: adDisplayContainer,
      videoDisplay: self.videoDisplay,
      pictureInPictureProxy: nil,
      userContext: nil)
    print(
      "IMA: Requesting VOD Stream with content source ID \(ViewController.contentSourceID) and "
        + "video ID \(ViewController.videoID)")
  }

  adsLoader.requestStream(with: request)
}

Menangani peristiwa streaming

Peristiwa IMAAdsLoader dan IMAStreamManager yang diaktifkan digunakan untuk menangani inisialisasi, error, dan perubahan status streaming. Peristiwa ini diaktifkan melalui protokol IMAAdsLoaderDelegate dan IMAStreamManagerDelegate. Memproses peristiwa iklan dimuat dan melakukan inisialisasi streaming. Jika iklan gagal dimuat, putar streaming cadangan sebagai gantinya.

Objective-C

- (void)playBackupStream {
  NSURL *backupStreamURL = [NSURL URLWithString:kBackupStreamURLString];
  [self.videoDisplay loadStream:backupStreamURL withSubtitles:@[]];
  [self.videoDisplay play];
  [self startMediaSession];
}

- (void)startMediaSession {
  [[AVAudioSession sharedInstance] setActive:YES error:nil];
  [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
}

#pragma mark - IMAAdsLoaderDelegate

- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {
  // Initialize and listen to stream manager's events.
  self.streamManager = adsLoadedData.streamManager;
  self.streamManager.delegate = self;
  [self.streamManager initializeWithAdsRenderingSettings:nil];
  NSLog(@"Stream created with: %@.", self.streamManager.streamId);
}

- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {
  // Fall back to playing the backup stream.
  NSLog(@"Error loading ads: %@", adErrorData.adError.message);
  [self playBackupStream];
}

Swift

@objc func contentDidFinishPlaying(_ notification: Notification) {
  guard let adsLoader = self.adsLoader else { return }
  adsLoader.contentComplete()
}

func startMediaSession() {
  try? AVAudioSession.sharedInstance().setActive(true, options: [])
  try? AVAudioSession.sharedInstance().setCategory(.playback)
}

// MARK: - IMAAdsLoaderDelegate

func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) {
  let streamManager = adsLoadedData.streamManager!
  streamManager.delegate = self
  streamManager.initialize(with: nil)
  self.streamManager = streamManager
}

func adsLoader(_ loader: IMAAdsLoader, failedWith adErrorData: IMAAdLoadingErrorData) {
  print("Error loading ads: \(adErrorData.adError.message)")
  let streamUrl = URL(string: ViewController.backupStreamURLString)
  self.videoDisplay.loadStream(streamUrl!, withSubtitles: [])
  self.videoDisplay.play()
  playerViewController.player?.play()
}

Menangani peristiwa logging dan error

Ada beberapa peristiwa yang dapat ditangani oleh delegasi pengelola streaming, tetapi untuk penerapan dasar, penggunaan yang paling penting adalah untuk melakukan logging peristiwa, untuk mencegah tindakan penelusuran saat iklan diputar, dan untuk menangani error.

Objective-C

#pragma mark - IMAStreamManagerDelegate

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

      NSLog(@"%@", extendedAdPodInfo);
      break;
    }
    case kIMAAdEvent_AD_BREAK_STARTED: {
      self.adContainerView.hidden = NO;
      // Trigger an update to send focus to the ad display container.
      self.adBreakActive = YES;
      [self setNeedsFocusUpdate];
      break;
    }
    case kIMAAdEvent_AD_BREAK_ENDED: {
      self.adContainerView.hidden = YES;
      // Trigger an update to send focus to the content player.
      self.adBreakActive = NO;
      [self setNeedsFocusUpdate];
      break;
    }
    case kIMAAdEvent_ICON_FALLBACK_IMAGE_CLOSED: {
      // Resume playback after the user has closed the dialog.
      [self.videoDisplay play];
      break;
    }
    default:
      break;
  }
}

- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdError:(IMAAdError *)error {
  // Fall back to playing the backup stream.
  NSLog(@"StreamManager error: %@", error.message);
  [self playBackupStream];
}

Swift

// MARK: - IMAStreamManagerDelegate
func streamManager(_ streamManager: IMAStreamManager, didReceive event: IMAAdEvent) {
  print("StreamManager event \(event.typeString).")
  switch event.type {
  case IMAAdEventType.STREAM_STARTED:
    self.startMediaSession()
  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:
    if let adContainerView = self.adContainerView {
      adContainerView.isHidden = false
    }
    // Trigger an update to send focus to the ad display container.
    adBreakActive = true
    setNeedsFocusUpdate()
    break
  case IMAAdEventType.AD_BREAK_ENDED:
    if let adContainerView = self.adContainerView {
      adContainerView.isHidden = true
    }
    // Trigger an update to send focus to the content player.
    adBreakActive = false
    setNeedsFocusUpdate()
    break
  case IMAAdEventType.ICON_FALLBACK_IMAGE_CLOSED:
    // Resume playback after the user has closed the dialog.
    self.videoDisplay.play()
    break
  default:
    break
  }
}

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

Selesai. Sekarang Anda meminta dan menampilkan iklan dengan IMA DAI SDK. Untuk mempelajari fitur SDK yang lebih canggih, lihat panduan lainnya atau contoh di GitHub.