Questa guida illustra l'integrazione dell'SDK IMA DAI in un'app video player. Se vuoi visualizzare o seguire un'integrazione di esempio completata, scarica BasicExample da GitHub.
Panoramica di IMA DAI
L'implementazione di IMA DAI prevede tre componenti principali dell'SDK, come illustrato in questa guida:
IMAAdDisplayContainer: un oggetto contenitore che si trova sopra l'elemento di riproduzione video e ospita gli elementi UI dell'annuncio.IMAAdsLoader: un oggetto che richiede stream e gestisce gli eventi attivati dagli oggetti di risposta alla richiesta di stream. Devi creare un'istanza di un solo caricatore di annunci, che può essere riutilizzato per tutta la durata dell'applicazione.IMAStreamRequest: unIMAVODStreamRequesto unIMALiveStreamRequest: un oggetto che definisce una richiesta di stream. Le richieste di stream possono riguardare video on demand o live streaming. Le richieste di live streaming specificano una chiave asset, mentre le richieste VOD specificano un ID CMS e un ID video. Entrambi i tipi di richiesta possono includere facoltativamente una chiave API necessaria per accedere agli stream specificati e un codice di rete Google Ad Manager per consentire all'SDK IMA di gestire gli identificatori degli annunci come specificato nelle impostazioni di Google Ad Manager.IMAStreamManager: un oggetto che gestisce gli stream di inserimento di annunci dinamici e le interazioni con il backend DAI. Il gestore degli stream gestisce anche i ping di monitoraggio e inoltra gli eventi di stream e annunci al publisher.
Prerequisiti
Prima di iniziare, devi disporre di quanto segue:
- Xcode 13 o versioni successive
- Metodo per l'installazione dell'SDK IMA:
- Swift Package Manager (opzione preferita)
- Una copia scaricata dell'SDK IMA per tvOS
Crea un nuovo progetto Xcode
In Xcode, crea un nuovo progetto tvOS utilizzando Objective-C. Utilizza BasicExample come nome del progetto.
Aggiungi l'SDK IMA DAI al progetto Xcode
Per installare l'SDK IMA, scegli un metodo preferito.
(Consigliato) Installa l'SDK utilizzando Swift Package Manager
L'SDK Interactive Media Ads supporta Swift Package Manager a partire dalla versione 4.8.2. Segui questi passaggi per importare il pacchetto Swift.
In Xcode, installa il pacchetto Swift GoogleInteractiveMediaAds andando su File > Aggiungi pacchetti.
Nel prompt visualizzato, cerca il repository GitHub del pacchetto Swift GoogleInteractiveMediaAds:
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvosSeleziona la versione del pacchetto Swift GoogleInteractiveMediaAds che vuoi utilizzare. Per i nuovi progetti, ti consigliamo di utilizzare l'opzione Up to Next Major Version.
Al termine, Xcode risolve le dipendenze del pacchetto e le scarica in background. Per maggiori dettagli su come aggiungere dipendenze dei pacchetti, consulta l'articolo di Apple.
Download e installazione manuale dell'SDK
Se non vuoi utilizzare Swift Package Manager, scarica e aggiungi manualmente l'SDK IMA al tuo progetto.
Importa l'SDK IMA
Aggiungi il framework IMA utilizzando un'istruzione di importazione:
Objective-C
#import "ViewController.h"
#import <AVKit/AVKit.h>
@import GoogleInteractiveMediaAds;
Swift
import AVFoundation
import GoogleInteractiveMediaAds
import UIKit
Crea un video player e integra l'SDK IMA
L'esempio seguente inizializza l'SDK IMA:
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
}
In viewDidLoad(), setupAdsLoader() crea IMAAdsLoader,
setupPlayer() crea AVPlayerViewController e setupAdContainer()
prepara UIView per la visualizzazione degli annunci. Quando la visualizzazione diventa visibile,
viewDidAppear() chiama requestStream() per richiedere lo stream DAI.
Per definire i parametri della richiesta di stream, questo esempio utilizza costanti, come
asset key per i live streaming o content source ID e video ID per gli stream
VOD. L'esempio utilizza anche i seguenti componenti per gestire l'SDK IMA:
adsLoader: gestisce le richieste di stream a Google Ad Manager. Ti consigliamo di utilizzare un'unica istanza per il ciclo di vita dell'app.videoDisplay: un'implementazione diIMAVideoDisplayche consente a IMA di controllare la riproduzione dei video e monitorare gli eventi di riproduzione utilizzando AVPlayer.adDisplayContainer: gestisce la visualizzazione utilizzata per il rendering degli elementi UI degli annunci e la gestione dello stato attivo della UI durante le interruzioni pubblicitarie.streamManager: gestisce la riproduzione dello stream combinato di annunci e contenuti e invia gli eventi del ciclo di vita degli annunci utilizzando il relativo delegato.playerViewController: il player tvOS utilizzato per presentare lo stream video gestito dall'SDK IMA.adBreakActive: un flag booleano che indica se è in corso la riproduzione di un'interruzione pubblicitaria, utilizzato per impedire la ricerca negli annunci e per gestire lo stato attivo della UI.
Implementa IMAAdsLoader
Poi, crea un'istanza di IMAAdsLoader e collega la visualizzazione del contenitore dell'annuncio alla gerarchia di oggetti View.
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
}
Invia una richiesta di stream
Crea alcune costanti per contenere le informazioni sullo stream, quindi implementa la funzione di richiesta dello stream per effettuare la richiesta.
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.
switch (self.streamType) {
case StreamTypeLive: {
IMALiveStreamRequest *request =
[[IMALiveStreamRequest alloc] initWithAssetKey:kAssetKey
networkCode:kNetworkCode
adDisplayContainer:self.adDisplayContainer
videoDisplay:self.videoDisplay
userContext:nil];
NSLog(@"IMA: Requesting Live Stream with Asset Key: %@.", kAssetKey);
request.useHLSInterstitials = YES;
[self.adsLoader requestStreamWithRequest:request];
break;
}
case StreamTypeVOD: {
IMAVODStreamRequest *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);
[self.adsLoader requestStreamWithRequest:request];
break;
}
}
}
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
switch self.currentStreamType {
case .live:
// Create a live stream request.
let 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)")
request.useHLSInterstitials = true
adsLoader.requestStream(with: request)
case .vod:
// Create a VOD stream request.
let 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)
}
}
Gestisci gli eventi di streaming
IMAAdsLoader e IMAStreamManager attivano eventi che gestiscono l'inizializzazione, gli errori e le modifiche allo stato dello stream. Questi eventi vengono attivati tramite i protocolli IMAAdsLoaderDelegate e IMAStreamManagerDelegate. Ascolta un evento di caricamento degli annunci e inizializza lo stream. Se un annuncio non viene caricato, riproduci uno stream di backup.
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()
}
Gestisci gli eventi di logging e di errore
Il delegato del gestore degli stream può gestire diversi eventi, ma per le implementazioni di base gli utilizzi più importanti sono l'esecuzione del log eventi, la prevenzione delle azioni di ricerca durante la riproduzione degli annunci e la gestione degli errori.
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")")
}
È tutto. Ora richiedi e visualizzi gli annunci con l'SDK IMA DAI. Per scoprire funzionalità più avanzate dell'SDK, consulta le altre guide o gli esempi su GitHub.