Konfigurowanie pakietu IMA SDK na potrzeby DAI

Wybierz platformę: HTML5 Android iOS tvOS Cast Roku

Pakiety IMA SDK ułatwiają integrację reklam multimedialnych z witrynami i aplikacjami. Pakiety IMA SDK mogą wysyłać żądania reklam do dowolnego serwera reklam zgodnego z VAST i zarządzać odtwarzaniem reklam w aplikacjach. Za pomocą pakietów IMA DAI SDK aplikacje wysyłają żądanie strumienia reklamy i treści wideo – VOD lub treści na żywo. Pakiet SDK zwraca wtedy połączony strumień wideo, dzięki czemu nie musisz zarządzać przełączaniem między reklamą a treściami wideo w aplikacji.

Wybierz interesujące Cię rozwiązanie DAI

Dynamiczne wstawianie reklam z pełną obsługą

W tym przewodniku pokazujemy, jak zintegrować pakiet IMA DAI SDK z prostą aplikacją odtwarzacza wideo. Jeśli chcesz wyświetlić gotową przykładową integrację lub z niej skorzystać, pobierz BasicExample z GitHub.

Omówienie IMA DAI

Wdrażanie IMA DAI obejmuje 3 główne komponenty pakietu SDK, jak pokazano w tym przewodniku:

  • IMAAdDisplayContainer: Obiekt kontenera, który znajduje się nad elementem odtwarzania wideo i zawiera elementy interfejsu reklamy.
  • IMAAdsLoader: Obiekt, który wysyła żądania strumieni i obsługuje zdarzenia wywoływane przez obiekty odpowiedzi na żądania strumieni. Należy utworzyć tylko jeden moduł wczytywania reklam, który można ponownie wykorzystywać przez cały okres działania aplikacji.
  • IMAStreamRequest – obiekt IMAVODStreamRequest lub IMALiveStreamRequest: obiekt, który definiuje żądanie strumienia. Żądania strumieni mogą dotyczyć transmisji wideo na żądanie lub transmisji na żywo. Żądania transmisji na żywo określają klucz pliku, a żądania VOD – identyfikator CMS i identyfikator wideo. Oba typy żądań mogą opcjonalnie zawierać klucz API potrzebny do uzyskania dostępu do określonych strumieni oraz kod sieci Google Ad Managera, aby pakiet IMA SDK mógł obsługiwać identyfikatory reklam zgodnie z ustawieniami Google Ad Managera.
  • IMAStreamManager: obiekt, który obsługuje strumienie dynamicznego wstawiania reklam i interakcje z backendem DAI. Menedżer strumienia obsługuje też pingi śledzące i przesyła zdarzenia strumienia i reklamy do wydawcy.

Wymagania wstępne

Zanim zaczniesz, musisz mieć:

Tworzenie nowego projektu Xcode

W Xcode utwórz nowy projekt tvOS w języku Objective-C. Użyj nazwy projektu BasicExample.

Dodawanie pakietu IMA DAI SDK do projektu w Xcode

Aby zainstalować pakiet IMA DAI SDK, użyj jednej z tych 3 metod.

Instalowanie pakietu SDK za pomocą menedżera pakietów Swift

Pakiet SDK do wyświetlania interaktywnych reklam medialnych obsługuje Swift Package Manager od wersji 4.8.2. Aby zaimportować pakiet Swift, wykonaj te czynności.

  1. W Xcode zainstaluj pakiet Swift GoogleInteractiveMediaAds, klikając File > Add Packages (Plik > Dodaj pakiety).

  2. W wyświetlonym oknie wyszukaj repozytorium GitHub GoogleInteractiveMediaAds Swift Package:

    https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos
    
  3. Wybierz wersję pakietu Swift GoogleInteractiveMediaAds, której chcesz używać. W przypadku nowych projektów zalecamy używanie opcji Do następnej wersji głównej.

Gdy skończysz, Xcode przetworzy zależności pakietu i pobierze je w tle. Więcej informacji o dodawaniu zależności pakietów znajdziesz w artykule Apple.

Instalowanie pakietu SDK za pomocą CocoaPods

CocoaPods to menedżer zależności dla projektów Xcode. Jest to zalecana metoda instalowania pakietu IMA DAI SDK. Więcej informacji o instalowaniu i używaniu CocoaPods znajdziesz w dokumentacji CocoaPods. Po zainstalowaniu CocoaPods wykonaj te instrukcje, aby zainstalować pakiet IMA DAI SDK:

  1. W tym samym katalogu, w którym znajduje się plik BasicExample.xcodeproj, utwórz plik tekstowy o nazwie Podfile i dodaj tę konfigurację:

    source 'https://github.com/CocoaPods/Specs.git'
    platform :tvos, '15'
    target "BasicExample" do
      pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.16.0'
    end
    
  2. W katalogu zawierającym plik Podfile uruchom:

    pod install --repo-update
  3. Sprawdź, czy instalacja się powiodła. W tym celu otwórz plik BasicExample.xcworkspace i upewnij się, że zawiera on 2 projekty: BasicExamplePods (zależności zainstalowane przez CocoaPods).

Ręczne pobieranie i instalowanie pakietu SDK

Jeśli nie chcesz używać narzędzi Swift Package Manager ani CocoaPods, możesz pobrać pakiet IMA DAI SDK i ręcznie dodać go do projektu.

Importowanie pakietu IMA SDK

Dodaj platformę IMA za pomocą instrukcji importowania:

Objective-C

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

@import GoogleInteractiveMediaAds;

Swift

import AVFoundation
import GoogleInteractiveMediaAds
import UIKit

Tworzenie odtwarzacza wideo i integrowanie pakietu IMA SDK

W tym przykładzie pokazujemy, jak zainicjować pakiet 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
  }

viewDidLoad() setupAdsLoader() tworzy IMAAdsLoader, setupPlayer() tworzy AVPlayerViewController, a setupAdContainer() przygotowuje UIView do wyświetlania reklam. Gdy widok stanie się widoczny, wywołuje funkcję viewDidAppear()requestStream(), aby poprosić o strumień DAI.

Aby zdefiniować parametry żądania transmisji, w tym przykładzie użyto stałych, takich jak asset key w przypadku transmisji na żywo lub content source IDvideo ID w przypadku transmisji VOD. W przykładzie do zarządzania pakietem IMA SDK używane są też te komponenty:

  • adsLoader: obsługuje żądania strumieniowe do Google Ad Managera. Zalecamy używanie pojedynczej instancji przez cały cykl życia aplikacji.
  • videoDisplay: implementacja IMAVideoDisplay, która umożliwia IMA kontrolowanie odtwarzania filmów i śledzenie zdarzeń odtwarzania za pomocą AVPlayer.
  • adDisplayContainer: zarządza widokiem używanym do renderowania elementów interfejsu reklamy i obsługiwania fokusu interfejsu podczas przerw na reklamy.
  • streamManager: zarządza odtwarzaniem połączonego strumienia reklamy i treści oraz wysyła zdarzenia cyklu życia reklamy za pomocą swojego delegata.
  • playerViewController: odtwarzacz tvOS używany do wyświetlania strumienia wideo zarządzanego przez pakiet IMA SDK.
  • adBreakActive: flaga logiczna wskazująca, czy odtwarzana jest przerwa na reklamę. Służy do zapobiegania przewijaniu reklam i zarządzania fokusem interfejsu.

Wdrażanie interfejsu IMAAdsLoader

Następnie utwórz instancję IMAAdsLoader i dołącz widok kontenera reklamy do hierarchii widoków.

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
}

Tworzenie żądania strumienia

Utwórz kilka stałych do przechowywania informacji o strumieniu, a następnie zaimplementuj funkcję żądania strumienia, aby wysłać żądanie.

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)
}

Obsługa zdarzeń strumienia

Zdarzenia IMAAdsLoaderIMAStreamManager są używane do obsługi inicjowania, błędów i zmian stanu strumienia. Te zdarzenia są wywoływane za pomocą protokołów IMAAdsLoaderDelegateIMAStreamManagerDelegate. Nasłuchuj zdarzenia wczytania reklam i inicjuj strumień. Jeśli nie uda się wczytać reklamy, odtwórz strumień zapasowy.

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()
}

Obsługa zdarzeń logowania i błędów

Delegat menedżera strumienia może obsługiwać kilka zdarzeń, ale w przypadku podstawowych implementacji najważniejsze zastosowania to rejestrowanie zdarzeń, zapobieganie działaniom związanym z przewijaniem podczas odtwarzania reklam i obsługa błędów.

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")")
}

To wszystko. Reklamy są teraz wyświetlane i wysyłane za pomocą pakietu IMA DAI SDK. Aby dowiedzieć się więcej o bardziej zaawansowanych funkcjach pakietu SDK, zapoznaj się z innymi przewodnikami lub przykładami na GitHubie.