IMA SDK ช่วยให้ผสานรวมโฆษณามัลติมีเดียเข้ากับเว็บไซต์และแอปได้อย่างง่ายดาย IMA SDK สามารถ ขอโฆษณาจากเซิร์ฟเวอร์โฆษณา ที่รองรับ VAST และจัดการการเล่นโฆษณาในแอปของคุณ เมื่อใช้ IMA DAI SDK แอปจะส่ง คำขอสตรีมสำหรับโฆษณาและเนื้อหาวิดีโอ ไม่ว่าจะเป็นเนื้อหา VOD หรือเนื้อหาแบบสด จากนั้น SDK จะแสดงสตรีมวิดีโอที่รวมกัน เพื่อให้คุณไม่ต้องจัดการการสลับระหว่างโฆษณาและวิดีโอเนื้อหาภายในแอป
เลือกโซลูชัน DAI ที่คุณสนใจ
DAI แบบบริการเต็มรูปแบบ
คู่มือนี้ครอบคลุมการผสานรวม IMA DAI SDK เข้ากับแอปวิดีโอเพลเยอร์ หากต้องการดูหรือทำตามตัวอย่างการผสานรวมที่เสร็จสมบูรณ์แล้ว ให้ดาวน์โหลด BasicExample จาก GitHub
ภาพรวม IMA DAI
การติดตั้งใช้งาน IMA DAI เกี่ยวข้องกับคอมโพเนนต์ SDK หลัก 3 อย่าง ดังที่คู่มือนี้ แสดงให้เห็น
IMAAdDisplayContainer: ออบเจ็กต์คอนเทนเนอร์ที่อยู่เหนือองค์ประกอบการเล่นวิดีโอและมีองค์ประกอบ UI ของโฆษณาIMAAdsLoader: ออบเจ็กต์ที่ขอสตรีมและจัดการเหตุการณ์ที่ทริกเกอร์โดยออบเจ็กต์การตอบกลับคำขอสตรีม คุณควรสร้างอินสแตนซ์ของ AdsLoader เพียงรายการเดียว ซึ่งสามารถนำกลับมาใช้ใหม่ได้ตลอดอายุการใช้งานของแอปพลิเคชันIMAStreamRequest– either aIMAVODStreamRequestor aIMALiveStreamRequest: ออบเจ็กต์ที่กำหนดคำขอสตรีม คำขอสตรีมอาจเป็นคำขอสำหรับวิดีโอออนดีมานด์หรือไลฟ์สดก็ได้ คำขอไลฟ์สดจะระบุคีย์ชิ้นงาน ส่วนคำขอ VOD จะระบุรหัส CMS และรหัสวิดีโอ คำขอทั้ง 2 ประเภทอาจมีคีย์ API ที่จำเป็นต่อการเข้าถึงสตรีมที่ระบุ และรหัสเครือข่าย Ad Manager ของ Google สำหรับ IMA SDK เพื่อจัดการตัวระบุโฆษณาตามที่ระบุในการตั้งค่า Google Ad ManagerIMAStreamManager: ออบเจ็กต์ที่จัดการสตรีมการแทรกโฆษณาแบบไดนามิกและการโต้ตอบกับแบ็กเอนด์ DAI Stream Manager ยังจัดการ Pings การติดตามและส่งต่อเหตุการณ์สตรีมและโฆษณาไปยัง ผู้เผยแพร่โฆษณาด้วย
ข้อกำหนดเบื้องต้น
ก่อนเริ่มต้น คุณต้องมีสิ่งต่อไปนี้
- Xcode 13 ขึ้นไป
- วิธีติดตั้ง IMA SDK
- Swift Package Manager (แนะนำ)
- สำเนาที่ดาวน์โหลดของ IMA SDK สำหรับ tvOS
สร้างโปรเจ็กต์ Xcode ใหม่
สร้างโปรเจ็กต์ tvOS ใหม่โดยใช้ Objective-C ใน Xcode ใช้ BasicExample เป็น ชื่อโปรเจ็กต์
เพิ่ม IMA DAI SDK ลงในโปรเจ็กต์ Xcode
หากต้องการติดตั้ง IMA SDK ให้เลือกวิธีที่ต้องการ
แนะนำ: ติดตั้ง SDK โดยใช้ Swift Package Manager
SDK โฆษณาสื่ออินเทอร์แอกทีฟรองรับ Swift Package Manager ตั้งแต่เวอร์ชัน 4.8.2 เป็นต้นไป ทำตาม ขั้นตอนเหล่านี้เพื่อนำเข้าแพ็กเกจ Swift
ใน Xcode ให้ติดตั้งแพ็กเกจ Swift ของ GoogleInteractiveMediaAds โดยไปที่ ไฟล์ > เพิ่มแพ็กเกจ
ในพรอมต์ที่ปรากฏขึ้น ให้ค้นหาที่เก็บ GitHub ของ GoogleInteractiveMediaAds Swift Package
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvosเลือกเวอร์ชันของแพ็กเกจ Swift ของ GoogleInteractiveMediaAds ที่ต้องการใช้ สำหรับโปรเจ็กต์ใหม่ เราขอแนะนำให้ใช้อัปเดตเป็นเวอร์ชันหลักถัดไป
เมื่อดำเนินการเสร็จแล้ว Xcode จะแก้ไขการขึ้นต่อกันของแพ็กเกจและดาวน์โหลดไว้ในเบื้องหลัง ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีเพิ่มการอ้างอิงแพ็กเกจได้ที่บทความของ Apple
การดาวน์โหลดและติดตั้ง SDK ด้วยตนเอง
หากไม่ต้องการใช้ Swift Package Manager ให้ดาวน์โหลดและเพิ่ม IMA SDK ลงในโปรเจ็กต์ด้วยตนเอง
นำเข้า IMA SDK
เพิ่มเฟรมเวิร์ก IMA โดยใช้คำสั่งนำเข้า
Objective-C
#import "ViewController.h"
#import <AVKit/AVKit.h>
@import GoogleInteractiveMediaAds;
Swift
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];
}
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() จะสร้าง 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 ควบคุม การเล่นวิดีโอและติดตามเหตุการณ์การเล่นโดยใช้ AVPlayeradDisplayContainer: จัดการมุมมองที่ใช้ในการแสดงผลองค์ประกอบ UI ของโฆษณาและ จัดการโฟกัส UI ในช่วงพักโฆษณาstreamManager: จัดการการเล่นสตรีมโฆษณาและเนื้อหารวมกัน และ ส่งเหตุการณ์วงจรโฆษณาโดยใช้ผู้มอบสิทธิ์playerViewController: เพลเยอร์ tvOS ที่ใช้ในการนำเสนอวิดีโอ สตรีมที่จัดการโดย IMA SDKadBreakActive: แฟล็กบูลีนที่ระบุว่ามีการเล่นช่วงพักโฆษณาหรือไม่ ใช้เพื่อ ป้องกันการกรอโฆษณาและจัดการโฟกัส UI
ใช้ 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;
}
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
}
ส่งคำขอสตรีม
สร้างค่าคงที่ 2-3 รายการเพื่อเก็บข้อมูลสตรีม แล้วใช้ฟังก์ชันคำขอสตรีมเพื่อส่งคำขอ
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)
}
}
จัดการเหตุการณ์สตรีม
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];
}
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()
}
จัดการเหตุการณ์การบันทึกและข้อผิดพลาด
ผู้รับมอบสิทธิ์ของ Stream Manager สามารถจัดการเหตุการณ์ต่างๆ ได้ แต่สำหรับการติดตั้งใช้งานขั้นพื้นฐาน การใช้งานที่สำคัญที่สุดคือการบันทึกเหตุการณ์ เพื่อป้องกันการดำเนินการค้นหาขณะที่โฆษณากำลังเล่น และเพื่อจัดการข้อผิดพลาด
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")")
}
เท่านี้ก็เรียบร้อย ตอนนี้คุณกำลังขอและแสดงโฆษณาด้วย IMA DAI SDK หากต้องการดูข้อมูลเกี่ยวกับฟีเจอร์ SDK ขั้นสูงเพิ่มเติม โปรดดูคำแนะนำอื่นๆ หรือตัวอย่างใน GitHub