SDK IMA упрощают интеграцию мультимедийной рекламы в ваши веб-сайты и приложения. SDK IMA могут запрашивать рекламу с любого рекламного сервера , совместимого с VAST , и управлять воспроизведением рекламы в ваших приложениях. С SDK IMA DAI приложения отправляют запрос на потоковое видео, включающее рекламу и контент — либо видео по запросу, либо прямой эфир. Затем SDK возвращает объединенный видеопоток, так что вам не нужно управлять переключением между рекламой и контентом внутри вашего приложения.
Выберите интересующее вас решение DAI.
Полный спектр услуг DAI
This guide demonstrates how to integrate the IMA DAI SDK into a simple video player app. If you would like to view or follow along with a completed sample integration, download the BasicExample from GitHub.
Обзор IMA DAI
Реализация IMA DAI включает в себя три основных компонента SDK, как показано в этом руководстве:
-
IMAAdDisplayContainer: Объект-контейнер, который располагается поверх элемента воспроизведения видео и содержит элементы пользовательского интерфейса рекламы. -
IMAAdsLoader: Объект, который запрашивает потоки и обрабатывает события, запускаемые объектами запроса и ответа потока. Следует создавать только один экземпляр загрузчика рекламы, который можно использовать повторно на протяжении всего жизненного цикла приложения. -
IMAStreamRequest— это либоIMAVODStreamRequest, либоIMALiveStreamRequest: объект, определяющий запрос потока. Запросы потока могут быть как для видео по запросу, так и для прямых трансляций. Запросы прямых трансляций указывают ключ ресурса, а запросы видео по запросу — идентификатор CMS и идентификатор видео. Оба типа запросов могут дополнительно включать ключ API, необходимый для доступа к указанным потокам, и сетевой код Google Ad Manager для обработки идентификаторов рекламы в SDK IMA, как указано в настройках Google Ad Manager. -
IMAStreamManager: Объект, обрабатывающий потоки динамической вставки рекламы и взаимодействие с бэкэндом DAI. Менеджер потоков также обрабатывает запросы отслеживания и пересылает события потока и рекламы издателю.
Предварительные требования
Прежде чем начать, вам потребуется следующее:
- Xcode 13 или более поздняя версия
- Swift Package Manager , CocoaPods или загруженная копия IMA DAI SDK для tvOS
Создайте новый проект Xcode.
В Xcode создайте новый проект tvOS, используя Objective-C. В качестве имени проекта укажите BasicExample .
Добавьте SDK IMA DAI в проект Xcode.
Для установки SDK IMA DAI используйте один из этих трех способов.
Установите SDK с помощью Swift Package Manager.
Начиная с версии 4.8.2, SDK для интерактивной рекламы поддерживает Swift Package Manager . Выполните следующие шаги для импорта пакета Swift.
В Xcode установите пакет GoogleInteractiveMediaAds Swift, перейдя в меню Файл > Добавить пакеты .
В появившемся окне найдите репозиторий GoogleInteractiveMediaAds Swift Package на GitHub:
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvosВыберите версию пакета GoogleInteractiveMediaAds Swift, которую вы хотите использовать. Для новых проектов мы рекомендуем использовать версию Up to Next Major Version .
После завершения Xcode разрешит зависимости ваших пакетов и загрузит их в фоновом режиме. Более подробную информацию о добавлении зависимостей пакетов см. в статье Apple .
Установите SDK с помощью CocoaPods.
CocoaPods — это менеджер зависимостей для проектов Xcode и рекомендуемый способ установки SDK IMA DAI. Для получения дополнительной информации об установке или использовании CocoaPods см. документацию CocoaPods . После установки CocoaPods используйте следующие инструкции для установки SDK IMA DAI:
В той же директории, где находится файл BasicExample.xcodeproj , создайте текстовый файл с именем Podfile и добавьте в него следующую конфигурацию:
source 'https://github.com/CocoaPods/Specs.git' platform :tvos, '15' target "BasicExample" do pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.16.0' endВ директории, содержащей файл Podfile, выполните следующую команду:
pod install --repo-updateУбедитесь в успешности установки, открыв файл BasicExample.xcworkspace и проверив, что он содержит два проекта: BasicExample и Pods (зависимости, устанавливаемые CocoaPods).
Загрузка и установка SDK вручную
Если вы не хотите использовать Swift Package Manager или CocoaPods, вы можете загрузить SDK IMA DAI и добавить его в свой проект вручную.
Импортировать IMA SDK
Добавьте фреймворк IMA, используя оператор импорта:
Objective-C
#import "ViewController.h"
#import <AVKit/AVKit.h>
@import GoogleInteractiveMediaAds;
Быстрый
import AVFoundation
import GoogleInteractiveMediaAds
import UIKit
Создайте видеоплеер и интегрируйте IMA SDK.
В следующем примере выполняется инициализация 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];
}
Быстрый
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() создает объект IMAAdsLoader , setupPlayer() создает объект AVPlayerViewController , а setupAdContainer() подготавливает UIView для показа рекламы. Когда представление становится видимым, viewDidAppear() вызывает requestStream() для запроса потока DAI.
Для определения параметров запроса потока в этом примере используются константы, такие как asset key для прямых трансляций или content source ID и video ID для потоков VOD. В примере также используются следующие компоненты для управления IMA SDK:
-
adsLoader: Обрабатывает запросы потока к Google Ad Manager. Мы рекомендуем использовать один экземпляр на протяжении всего жизненного цикла приложения. -
videoDisplay: РеализацияIMAVideoDisplay, позволяющая IMA управлять воспроизведением видео и отслеживать события воспроизведения с помощью AVPlayer. -
adDisplayContainer: Управляет представлением, используемым для отрисовки элементов пользовательского интерфейса рекламы и обработки фокуса пользовательского интерфейса во время рекламных пауз. -
streamManager: Управляет воспроизведением объединенного потока рекламы и контента и отправляет события жизненного цикла рекламы, используя свой делегат. -
playerViewController: Плеер tvOS, используемый для отображения видеопотока, управляемого SDK IMA. -
adBreakActive: Логический флаг, указывающий, воспроизводится ли рекламная пауза; используется для предотвращения перемотки рекламы и управления фокусом пользовательского интерфейса.
Реализуйте IMAAdsLoader.
Далее создайте экземпляр IMAAdsLoader и прикрепите представление контейнера рекламы к иерархии представлений.
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;
}
Быстрый
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
}
Отправить запрос на трансляцию
Создайте несколько констант для хранения информации о потоке, а затем реализуйте функцию запроса потока для выполнения запроса.
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];
}
}
Быстрый
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)
}
Обработка событий потока
Компоненты IMAAdsLoader и IMAStreamManager генерируют события, используемые для обработки инициализации, ошибок и изменений состояния потока. Эти события генерируются по протоколам IMAAdsLoaderDelegate и IMAStreamManagerDelegate . Они отслеживают событие загрузки рекламы и инициализируют поток. Если загрузка рекламы не удалась, вместо неё воспроизводится резервный поток.
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];
}
Быстрый
@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()
}
Обработка событий логирования и ошибок.
Делегат менеджера потока может обрабатывать несколько событий, но для базовых реализаций наиболее важными являются ведение журнала событий, предотвращение перемотки во время воспроизведения рекламы и обработка ошибок.
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];
}
Быстрый
// 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")")
}
Вот и всё! Теперь вы запрашиваете и отображаете рекламу с помощью SDK IMA DAI. Чтобы узнать о более продвинутых функциях SDK, ознакомьтесь с другими руководствами или примерами на GitHub .

