SDK IMA giúp bạn dễ dàng tích hợp quảng cáo đa phương tiện vào trang web và ứng dụng của mình. SDK IMA có thể yêu cầu quảng cáo từ bất kỳ máy chủ quảng cáo tuân thủ VAST nào và quản lý việc phát quảng cáo trong ứng dụng của bạn. Với SDK DAI của IMA, các ứng dụng sẽ đưa ra yêu cầu về luồng cho quảng cáo và video nội dung – có thể là nội dung VOD hoặc nội dung trực tiếp. Sau đó, SDK sẽ trả về một luồng video kết hợp, nhờ đó bạn không phải quản lý việc chuyển đổi giữa quảng cáo và video nội dung trong ứng dụng của mình.
Chọn giải pháp DAI mà bạn quan tâm
DAI trọn gói
Hướng dẫn này minh hoạ cách tích hợp SDK DAI của IMA vào một ứng dụng trình phát video đơn giản. Nếu muốn xem hoặc làm theo một mẫu tích hợp hoàn chỉnh, hãy tải BasicExample xuống từ GitHub.
Tổng quan về DAI của IMA
Việc triển khai DAI của IMA bao gồm 3 thành phần SDK chính như minh hoạ trong hướng dẫn này:
IMAAdDisplayContainer: Một đối tượng vùng chứa nằm phía trên phần tử phát video và chứa các phần tử giao diện người dùng quảng cáo.IMAAdsLoader: Một đối tượng yêu cầu luồng và xử lý các sự kiện do đối tượng phản hồi yêu cầu luồng kích hoạt. Bạn chỉ nên khởi tạo một trình tải quảng cáo và có thể dùng lại trình tải này trong suốt thời gian hoạt động của ứng dụng.IMAStreamRequest–IMAVODStreamRequesthoặcIMALiveStreamRequest: Một đối tượng xác định yêu cầu phát trực tuyến. Yêu cầu phát trực tuyến có thể là yêu cầu phát video theo yêu cầu hoặc phát trực tiếp. Yêu cầu phát trực tiếp chỉ định một khoá thành phần, trong khi yêu cầu VOD chỉ định một mã CMS và mã video. Cả hai loại yêu cầu đều có thể bao gồm một khoá API (không bắt buộc) cần thiết để truy cập vào các luồng truyền phát được chỉ định và mã mạng Google Ad Manager để SDK IMA xử lý mã nhận dạng quảng cáo như được chỉ định trong phần cài đặt Google Ad Manager.IMAStreamManager: Một đối tượng xử lý các luồng chèn quảng cáo động và các lượt tương tác với phần phụ trợ DAI. Trình quản lý luồng phát cũng xử lý các ping theo dõi và chuyển tiếp các sự kiện luồng phát và quảng cáo đến nhà xuất bản.
Điều kiện tiên quyết
Trước khi bắt đầu, bạn cần có những thông tin sau:
- Xcode 13 trở lên
- Trình quản lý gói Swift, CocoaPods hoặc bản sao đã tải xuống của SDK IMA DAI cho tvOS
Tạo một dự án Xcode mới
Trong Xcode, hãy tạo một dự án tvOS mới bằng Objective-C. Sử dụng BasicExample làm tên dự án.
Thêm IMA DAI SDK vào dự án Xcode
Hãy sử dụng một trong 3 phương thức sau để cài đặt IMA DAI SDK.
Cài đặt SDK bằng Trình quản lý gói Swift
SDK quảng cáo trên phương tiện truyền thông tương tác hỗ trợ Trình quản lý gói Swift kể từ phiên bản 4.8.2. Hãy làm theo các bước sau để nhập gói Swift.
Trong Xcode, hãy cài đặt Gói Swift GoogleInteractiveMediaAds bằng cách chuyển đến File (Tệp) > Add Packages (Thêm gói).
Trong thông báo nhắc xuất hiện, hãy tìm kiếm kho lưu trữ GitHub cho Gói Swift GoogleInteractiveMediaAds:
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvosChọn phiên bản của Gói Swift GoogleInteractiveMediaAds mà bạn muốn sử dụng. Đối với các dự án mới, bạn nên sử dụng Phiên bản lớn tiếp theo.
Khi bạn hoàn tất, Xcode sẽ phân giải các phần phụ thuộc của gói và tải các phần phụ thuộc đó xuống ở chế độ nền. Để biết thêm thông tin chi tiết về cách thêm các phần phụ thuộc của gói, hãy xem bài viết của Apple.
Cài đặt SDK bằng CocoaPods
CocoaPods là một trình quản lý phần phụ thuộc cho các dự án Xcode và là phương thức được đề xuất để cài đặt IMA DAI SDK. Để biết thêm thông tin về cách cài đặt hoặc sử dụng CocoaPods, hãy xem tài liệu về CocoaPods. Sau khi cài đặt CocoaPods, hãy làm theo hướng dẫn sau để cài đặt IMA DAI SDK:
Trong cùng thư mục với tệp BasicExample.xcodeproj, hãy tạo một tệp văn bản có tên là Podfile rồi thêm cấu hình sau:
source 'https://github.com/CocoaPods/Specs.git' platform :tvos, '15' target "BasicExample" do pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.16.0' endTừ thư mục chứa Podfile, hãy chạy:
pod install --repo-updateXác minh rằng quá trình cài đặt đã thành công bằng cách mở tệp BasicExample.xcworkspace và xác nhận rằng tệp này chứa 2 dự án: BasicExample và Pods (các phần phụ thuộc do CocoaPods cài đặt).
Tải xuống và cài đặt SDK theo cách thủ công
Nếu không muốn sử dụng Trình quản lý gói Swift hoặc CocoaPods, bạn có thể tải SDK IMA DAI xuống và thêm SDK này vào dự án theo cách thủ công.
Nhập IMA SDK
Thêm khung IMA bằng câu lệnh nhập:
Objective-C
#import "ViewController.h"
#import <AVKit/AVKit.h>
@import GoogleInteractiveMediaAds;
Swift
import AVFoundation
import GoogleInteractiveMediaAds
import UIKit
Tạo trình phát video và tích hợp SDK IMA
Ví dụ sau đây khởi chạy 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
}
Trong viewDidLoad(), setupAdsLoader() tạo IMAAdsLoader, setupPlayer() tạo AVPlayerViewController và setupAdContainer() chuẩn bị UIView để hiển thị quảng cáo. Khi khung hiển thị xuất hiện, viewDidAppear() sẽ gọi requestStream() để yêu cầu luồng DAI.
Để xác định các thông số yêu cầu phát trực tiếp, ví dụ này sử dụng các hằng số, chẳng hạn như asset key cho sự kiện phát trực tiếp hoặc content source ID và video ID cho sự kiện phát trực tiếp theo yêu cầu. Ví dụ này cũng sử dụng các thành phần sau để quản lý IMA SDK:
adsLoader: Xử lý các yêu cầu về luồng phát đến Google Ad Manager. Bạn nên sử dụng một phiên bản duy nhất cho vòng đời của ứng dụng.videoDisplay: Một chế độ triển khaiIMAVideoDisplaycho phép IMA kiểm soát việc phát video và theo dõi các sự kiện phát bằng AVPlayer.adDisplayContainer: Quản lý khung hiển thị dùng để kết xuất các phần tử giao diện người dùng của quảng cáo và xử lý tiêu điểm giao diện người dùng trong các khoảng thời gian quảng cáo.streamManager: Quản lý việc phát luồng quảng cáo và nội dung kết hợp, đồng thời gửi các sự kiện trong vòng đời của quảng cáo bằng cách sử dụng uỷ quyền của mình.playerViewController: Trình phát tvOS dùng để trình bày luồng video do IMA SDK quản lý.adBreakActive: Một cờ boolean cho biết liệu một khoảng quảng cáo có đang phát hay không, được dùng để ngăn việc tìm kiếm qua quảng cáo và quản lý tiêu điểm trên giao diện người dùng.
Triển khai IMAAdsLoader
Tiếp theo, hãy khởi tạo IMAAdsLoader và đính kèm khung hiển thị vùng chứa quảng cáo vào hệ phân cấp khung hiển thị.
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
}
Đưa ra yêu cầu phát trực tuyến
Tạo một vài hằng số để lưu giữ thông tin về luồng phát, sau đó triển khai hàm yêu cầu luồng phát để đưa ra yêu cầu.
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)
}
Xử lý sự kiện luồng
IMAAdsLoader và IMAStreamManager kích hoạt các sự kiện dùng để xử lý quá trình khởi chạy, lỗi và các thay đổi về trạng thái luồng. Các sự kiện này được kích hoạt qua giao thức IMAAdsLoaderDelegate và IMAStreamManagerDelegate. Theo dõi sự kiện quảng cáo đã tải và khởi chạy luồng. Nếu quảng cáo không tải được, hãy phát một luồng dự phòng.
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()
}
Xử lý sự kiện ghi nhật ký và lỗi
Có một số sự kiện mà uỷ quyền của trình quản lý luồng phát có thể xử lý, nhưng đối với các hoạt động triển khai cơ bản, những mục đích quan trọng nhất là thực hiện ghi nhật ký sự kiện, ngăn chặn các thao tác tìm kiếm trong khi quảng cáo đang phát và xử lý lỗi.
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")")
}
Vậy là xong! Giờ đây, bạn đang yêu cầu và hiển thị quảng cáo bằng SDK IMA DAI. Để tìm hiểu về các tính năng nâng cao khác của SDK, hãy xem các hướng dẫn khác hoặc các mẫu trên GitHub.