Configurer le SDK IMA pour l'insertion dynamique d'annonces

Les SDK IMA permettent d'intégrer facilement des annonces multimédias à vos sites Web et applications. Les SDK IMA peuvent demander des annonces à n'importe quel ad server conforme à la norme VAST et gérer la lecture des annonces dans vos applications. Avec les SDK IMA DAI, les applications envoient une demande de flux pour les annonces et le contenu vidéo (VOD ou contenu en direct). Le SDK renvoie ensuite un flux vidéo combiné, ce qui vous évite d'avoir à gérer le basculement entre les vidéos d'annonces et de contenu dans votre application.

Sélectionnez la solution DAI qui vous intéresse.

Insertion dynamique de séries d'annonces

Ce guide explique comment intégrer le SDK IMA DAI dans une application de lecteur vidéo simple. Si vous souhaitez afficher ou suivre un exemple d'intégration complet, téléchargez PodServingExample depuis GitHub.

Présentation d'IMA DAI

L'implémentation d'IMA DAI implique quatre composants SDK principaux, comme indiqué dans ce guide :

  • IMAAdDisplayContainer : objet conteneur qui se trouve au-dessus de l'élément de lecture vidéo et qui héberge les éléments d'UI de l'annonce.
  • IMAAdsLoader : objet qui demande des flux et gère les événements déclenchés par les objets de réponse aux demandes de flux. Vous ne devez instancier qu'un seul AdsLoader, qui peut être réutilisé tout au long de la durée de vie de l'application.
  • IMAStreamRequest : IMAPodVODStreamRequest ou IMAPodStreamRequest.
  • IMAStreamManager : objet qui gère les flux d'insertion dynamique d'annonces et les interactions avec le backend DAI. Le gestionnaire de flux gère également les pings de suivi et transmet les événements de flux et d'annonces à l'éditeur.

De plus, pour lire des flux de diffusion de pods, vous devez implémenter un gestionnaire VTP personnalisé. Ce gestionnaire VTP personnalisé envoie l'ID de flux à votre partenaire technique vidéo (VTP) ainsi que toute autre information dont il a besoin pour renvoyer un fichier manifeste de flux contenant à la fois du contenu et des annonces insérées. Votre VTP vous fournira des instructions sur la façon d'implémenter votre gestionnaire VTP personnalisé.

Prérequis

Avant de commencer, vous avez besoin des éléments suivants :

Vous avez également besoin des paramètres utilisés pour demander votre flux à partir du SDK IMA.

Paramètres de diffusion en direct
Code du réseau Code de réseau de votre compte Ad Manager 360.
Clé d'élément personnalisée Clé d'élément personnalisé qui identifie votre événement de diffusion de pod dans Ad Manager 360. Il peut être créé par votre outil de manipulation du fichier manifeste ou par un partenaire d'insertion de séries d'annonces tiers.
Paramètres des flux VOD
Code du réseau Code de réseau de votre compte Ad Manager 360.

Créer un projet Xcode

Dans Xcode, créez un projet iOS en Objective-C nommé "PodServingExample".

Ajouter le SDK IMA DAI au projet Xcode

Utilisez l'une de ces trois méthodes pour installer le SDK IMA DAI.

Installer le SDK à l'aide de CocoaPods (méthode recommandée)

CocoaPods est un gestionnaire de dépendances pour les projets Xcode. Il s'agit de la méthode recommandée pour installer le SDK IMA DAI. Pour en savoir plus sur l'installation ou l'utilisation de CocoaPods, consultez la documentation CocoaPods. Une fois CocoaPods installé, suivez les instructions ci-dessous pour installer le SDK IMA DAI :

  1. Dans le même répertoire que votre fichier PodServingExample.xcodeproj, créez un fichier texte nommé Podfile et ajoutez la configuration suivante :

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

  2. Depuis le répertoire contenant le fichier Podfile, exécutez la commande suivante :

    pod install --repo-update

Installer le SDK à l'aide de Swift Package Manager

Le SDK Interactive Media Ads est compatible avec Swift Package Manager à partir de la version 3.18.4. Suivez les étapes ci-dessous pour importer le package Swift.

  1. Dans Xcode, installez le package Swift du SDK IMA DAI en accédant à File > Add Packages (Fichier > Ajouter des packages).

  2. Dans l'invite qui s'affiche, recherchez le dépôt GitHub du package Swift du SDK IMA DAI :

    https://github.com/googleads/swift-package-manager-google-interactive-media-ads-ios
    
  3. Sélectionnez la version du package Swift du SDK IMA DAI que vous souhaitez utiliser. Pour les nouveaux projets, nous vous recommandons d'utiliser Up to Next Major Version.

Une fois que vous avez terminé, Xcode résout les dépendances de votre package et les télécharge en arrière-plan. Pour savoir comment ajouter des dépendances de package, consultez l'article d'Apple.

Télécharger et installer le SDK manuellement

Si vous ne souhaitez pas utiliser Swift Package Manager ni CocoaPods, vous pouvez télécharger le SDK IMA DAI et l'ajouter manuellement à votre projet.

Créer un lecteur vidéo simple

Implémentez un lecteur vidéo dans votre contrôleur de vue principal, en utilisant un lecteur AV enveloppé dans une vue d'interface utilisateur. Le SDK IMA utilise la vue de l'UI pour afficher les éléments de l'UI des annonces.

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

Initialiser le chargeur d'annonces

Importez le SDK IMA dans votre contrôleur de vue et adoptez les protocoles IMAAdsLoaderDelegate et IMAStreamManagerDelegate pour gérer les événements du chargeur d'annonces et du gestionnaire de flux.

Ajoutez ces propriétés privées pour stocker les composants clés du SDK IMA :

  • IMAAdsLoader : gère les demandes de flux pendant toute la durée de vie de votre application.
  • IMAAdDisplayContainer : gère l'insertion et la gestion des éléments d'interface utilisateur des annonces.
  • IMAAVPlayerVideoDisplay : assure la communication entre le SDK IMA et votre lecteur multimédia, et gère les métadonnées temporelles.
  • IMAStreamManager : gère la lecture du flux et déclenche les événements liés aux annonces.

Initialisez le chargeur d'annonces, le conteneur d'affichage des annonces et l'affichage vidéo après le chargement de la vue.

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 each ad's user interface elements and companion
    // slots.
    adDisplayContainer = IMAAdDisplayContainer(
      adContainer: videoView,
      viewController: self,
      companionSlots: nil)

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

Envoyer une requête de flux

Lorsqu'un utilisateur appuie sur le bouton de lecture, envoyez une nouvelle demande de flux. Utilisez la classe IMAPodStreamRequest pour les diffusions en direct. Pour les flux VOD, utilisez la classe IMAPodVODStreamRequest.

La demande de flux nécessite vos paramètres de flux, ainsi qu'une référence à votre conteneur d'affichage d'annonces et à votre affichage vidéo.

Objective-C

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

- (void)requestStream {
  // Create a stream request.
  IMAStreamRequest *request;
  if (kStreamType == StreamTypeLive) {
    // Live stream request. Replace the network code and custom asset key with your values.
    request = [[IMAPodStreamRequest alloc] initWithNetworkCode:kNetworkCode
                                                customAssetKey:kCustomAssetKey
                                            adDisplayContainer:self.adDisplayContainer
                                                  videoDisplay:self.imaVideoDisplay
                                         pictureInPictureProxy:nil
                                                   userContext:nil];
  } else {
    // VOD request. Replace the network code with your value.
    request = [[IMAPodVODStreamRequest alloc] initWithNetworkCode:kNetworkCode
                                               adDisplayContainer:self.adDisplayContainer
                                                     videoDisplay:self.imaVideoDisplay
                                            pictureInPictureProxy:nil
                                                      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 = IMAPodStreamRequest(
      networkCode: ViewController.networkCode,
      customAssetKey: ViewController.customAssetKey,
      adDisplayContainer: adDisplayContainer!,
      videoDisplay: self.imaVideoDisplay,
      pictureInPictureProxy: nil,
      userContext: nil)
    adsLoader?.requestStream(with: request)
  } else {
    // VOD stream request.
    let request = IMAPodVODStreamRequest(
      networkCode: ViewController.networkCode,
      adDisplayContainer: adDisplayContainer!,
      videoDisplay: self.imaVideoDisplay,
      pictureInPictureProxy: nil,
      userContext: nil)
    adsLoader?.requestStream(with: request)
  }
}

Écouter les événements de chargement du flux

La classe IMAAdsLoader appelle les méthodes IMAAdsLoaderDelegate en cas de réussite ou d'échec de l'initialisation de la requête de flux.

Dans la méthode déléguée adsLoadedWithData, définissez votre IMAStreamManagerDelegate. Transmettez l'ID du flux à votre gestionnaire VTP personnalisé et récupérez l'URL du fichier manifeste du flux. Pour les diffusions en direct, chargez l'URL du fichier manifeste dans votre lecteur vidéo et lancez la lecture. Pour les flux VOD, transmettez l'URL du fichier manifeste à la méthode loadThirdPartyStream du gestionnaire de flux. Cette méthode demande des données d'événements d'annonces à Ad Manager 360, puis charge l'URL du fichier manifeste et lance la lecture.

Dans la méthode déléguée failedWithErrorData, enregistrez l'erreur. Vous pouvez également lire le flux de sauvegarde. Consultez les bonnes pratiques concernant l'insertion dynamique d'annonces.

Objective-C

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

  // Build the Pod serving Stream URL.
  NSString *streamID = adsLoadedData.streamManager.streamId;
  // Your custom VTP handler takes the stream ID and returns the stream manifest URL.
  NSString *urlString = gCustomVTPHandler(streamID);
  NSURL *streamUrl = [NSURL URLWithString:urlString];
  if (kStreamType == StreamTypeLive) {
    // Load live streams directly into the AVPlayer.
    [self.imaVideoDisplay loadStream:streamUrl withSubtitles:@[]];
    [self.imaVideoDisplay play];
  } else {
    // Load VOD streams using the `loadThirdPartyStream` method in IMA SDK's stream manager.
    // The stream manager loads the stream, requests metadata, and starts playback.
    [self.streamManager loadThirdPartyStream:streamUrl streamSubtitles:@[]];
  }
}

- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {
  // Log the error and play the backup 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

  // Initialize the stream manager to handle ad click and user interactions with ad UI elements.
  streamManager!.initialize(with: nil)

  // Build the Pod serving Stream URL.
  let streamID = streamManager!.streamId
  // Your custom VTP handler takes the stream ID and returns the stream manifest URL.
  let urlString = ViewController.customVTPParser(streamID!)
  let streamUrl = URL(string: urlString)
  if ViewController.requestType == StreamType.live {
    // Live streams can be loaded directly into the AVPlayer.
    imaVideoDisplay.loadStream(streamUrl!, withSubtitles: [])
    imaVideoDisplay.play()
  } else {
    // VOD streams are loaded using the IMA SDK's stream manager.
    // The stream manager loads the stream, requests metadata, and starts playback.
    streamManager!.loadThirdPartyStream(streamUrl!, streamSubtitles: [])
  }
}

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

Implémenter votre gestionnaire VTP personnalisé

Le gestionnaire VTP personnalisé envoie l'ID de flux du spectateur à votre partenaire technique vidéo (PTV), ainsi que toute autre information dont votre PTV a besoin pour renvoyer un fichier manifeste de flux contenant à la fois du contenu et des annonces insérées. Votre fournisseur de services de paiement vous fournira des instructions spécifiques sur la façon d'implémenter votre gestionnaire VTP personnalisé.

Par exemple, un VTP peut inclure une URL de modèle de fichier manifeste contenant la macro [[STREAMID]]. Dans cet exemple, le gestionnaire insère l'ID du flux à la place de la macro et renvoie l'URL du fichier manifeste résultante.

Objective-C

/// Custom VTP Handler.
///
/// Returns the stream manifest URL from the video technical partner or manifest manipulator.
static NSString *(^gCustomVTPHandler)(NSString *) = ^(NSString *streamID) {
  // Insert synchronous code here to retrieve a stream manifest URL from your video tech partner
  // or manifest manipulation server.
  // This example uses a hardcoded URL template, containing a placeholder for the stream
  // ID and replaces the placeholder with the stream ID.
  NSString *manifestUrl = @"YOUR_MANIFEST_URL_TEMPLATE";
  return [manifestUrl stringByReplacingOccurrencesOfString:@"[[STREAMID]]"
                                                withString:streamID];
};

Swift

/// Custom VTP Handler.
///
/// Returns the stream manifest URL from the video technical partner or manifest manipulator.
static let customVTPParser = { (streamID: String) -> (String) in
  // Insert synchronous code here to retrieve a stream manifest URL from your video tech partner
  // or manifest manipulation server.
  // This example uses a hardcoded URL template, containing a placeholder for the stream
  // ID and replaces the placeholder with the stream ID.
  let manifestURL = "YOUR_MANIFEST_URL_TEMPLATE"
  return manifestURL.replacingOccurrences(of: "[[STREAMID]]", with: streamID)
}

Écouter les événements publicitaires

IMAStreamManager appelle les méthodes IMAStreamManagerDelegate pour transmettre les événements et les erreurs de flux à votre application.

Dans cet exemple, enregistrez les événements d'annonce principaux dans la console :

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

Nettoyer les composants IMA DAI

Pour arrêter la lecture du flux, arrêter tout suivi des annonces et libérer tous les éléments de flux chargés, appelez IMAStreamManager.destroy().

Exécutez votre application. Si l'opération réussit, vous pouvez demander et lire des flux Google DAI avec le SDK IMA. Pour en savoir plus sur les fonctionnalités avancées du SDK, consultez les autres guides listés dans la barre latérale de gauche ou les exemples sur GitHub.