راه‌اندازی کیت توسعه نرم‌افزار IMA برای DAI

پلاتفرم را انتخاب کنید: HTML5 Android iOS tvOS Cast Roku

کیت‌های توسعه نرم‌افزار IMA ادغام آگهی‌های چندرسانه‌ای را در وب‌سایت‌ها و برنامه‌هایتان آسان می‌کند. کیت‌های توسعه نرم‌افزار IMA می‌توانند از هر سرور آگهی سازگار با VAST درخواست آگهی کنند و بازپخش آگهی را در برنامه‌هایتان مدیریت کنند. با کیت‌های توسعه نرم‌افزار IMA DAI، برنامه‌ها درخواست جاری‌سازی برای آگهی و ویدیو محتوا—چه VOD و چه محتوای زنده—ایجاد می‌کنند. سپس کیت توسعه نرم‌افزار یک جاری‌سازی ویدیویی ترکیبی را برمی‌گرداند تا مجبور نباشید جابه‌جایی بین آگهی و محتوای ویدیویی را در برنامه‌تان مدیریت کنید.

راهکار «درج آگهی پویا» موردنظرتان را انتخاب کنید

بازرسی پویای ARP با خدمات کامل

این راهنما ادغام IMA DAI SDK در یک برنامه پخش‌کننده ویدیو را پوشش می‌دهد. اگر می‌خواهید یک نمونه ادغام کامل‌شده را مشاهده یا دنبال کنید، BasicExample را از GitHub بارگیری کنید.

نمای کلی IMA DAI

همان‌طور که این راهنما نشان می‌دهد، پیاده‌سازی IMA DAI شامل سه مؤلفه اصلی کیت توسعه نرم‌افزار است:

  • IMAAdDisplayContainer: شیء نگهدارنده‌ای که روی عنصر بازپخش ویدیو قرار می‌گیرد و عناصر میانای کاربر آگهی را در خود جای می‌دهد.
  • ‫ IMAAdsLoader: شیئی که جاری‌سازی‌ها را درخواست می‌کند و رویدادهای را که توسط اشیای پاسخ درخواست جاری‌سازی راه‌اندازی می‌شوند مدیریت می‌کند. باید فقط یک بارگذارنده آگهی را نمونه‌سازی کنید که بتواند درطول عمر برنامه دوباره استفاده شود.
  • IMAStreamRequest – یا IMAVODStreamRequest یا IMALiveStreamRequest: شیئی که درخواست جاری‌سازی را تعریف می‌کند. درخواست‌های جاری‌سازی می‌تواند برای ویدیو به‌درخواست یا جاری‌سازی زنده باشد. درخواست‌های جاری‌سازی زنده کلید دارایی را مشخص می‌کنند، درحالی‌که درخواست‌های VOD شناسه CMS و شناسه ویدیو را مشخص می‌کنند. هر دو نوع درخواست می‌توانند به‌صورت اختیاری شامل کلید API موردنیاز برای دسترسی به جاری‌سازی‌های مشخص‌شده و کد شبکه Google Ad Manager برای IMA SDK باشند تا شناسه‌های آگهی را همان‌طور که در تنظیمات Google Ad Manager مشخص شده است مدیریت کند.
  • ‫ IMAStreamManager: شیئی که جریان‌های درج آگهی پویا و تعاملات با زیرینه DAI را مدیریت می‌کند. مدیر جاری‌سازی همچنین پینگ‌های ردیابی را مدیریت می‌کند و رویدادهای جاری‌سازی و آگهی را به ناشر بازارسال می‌کند.

پیش‌نیازها

قبل‌از شروع، به موارد زیر نیاز دارید:

ایجاد پروژه Xcode جدید

در Xcode، پروژه tvOS جدیدی بااستفاده از Objective-C ایجاد کنید. از BasicExample به‌عنوان نام پروژه استفاده کنید.

افزودن IMA DAI SDK به پروژه Xcode

برای نصب کیت توسعه نرم‌افزار آگهی‌های رسانه‌ای تعاملی، روش ترجیحی را انتخاب کنید.

توصیه‌شده: «کیت توسعه نرم‌افزار» را بااستفاده از «مدیر بسته Swift» نصب کنید

«کیت توسعه نرم‌افزار آگهی‌های رسانه‌ای تعاملی» از نسخه ۴.۸.۲ از مدیر بسته Swift پشتیبانی می‌کند. برای وارد کردن بسته Swift، این مراحل را دنبال کنید.

  1. در Xcode، با رفتن به فایل > افزودن بسته‌ها، بسته GoogleInteractiveMediaAds Swift را نصب کنید.

  2. در پیام‌واره‌ای که ظاهر می‌شود، مخزن GoogleInteractiveMediaAds Swift Package GitHub را جستجو کنید:

    https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos
    
  3. نسخه GoogleInteractiveMediaAds Swift Package را که می‌خواهید استفاده کنید انتخاب کنید. برای پروژه‌های جدید، توصیه می‌کنیم از تا نسخه اصلی بعدی استفاده کنید.

وقتی کارتان تمام شد، Xcode وابستگی‌های بسته را حل می‌کند و آن‌ها را در پس‌زمینه بارگیری می‌کند. برای جزئیات بیشتر درباره نحوه افزودن وابستگی‌های بسته، مقاله Apple را ببینید.

بارگیری و نصب دستی کیت توسعه نرم‌افزار

اگر نمی‌خواهید از «مدیر بسته Swift» استفاده کنید، «کیت توسعه نرم‌افزار IMA» را بارگیری کنید و به‌صورت دستی به پروژه‌تان اضافه کنید.

وارد کردن کیت توسعه نرم‌افزار آگهی‌های رسانه‌ای تعاملی

چارچوب IMA را بااستفاده از عبارت وارد کردن اضافه کنید:

آبجکتیو-سی

#import "ViewController.h"
#import <AVKit/AVKit.h>

@import GoogleInteractiveMediaAds;

Swift

import AVFoundation
import GoogleInteractiveMediaAds
import UIKit

ایجاد یک پخش‌کننده ویدیو و ادغام کیت توسعه نرم‌افزار IMA

مثال زیر کیت توسعه نرم‌افزار IMA را مقداردهی اولیه می‌کند:

آبجکتیو-سی

// 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» استفاده می‌کند:

  • adsLoader: درخواست‌های جاری‌سازی را به Google Ad Manager ارسال می‌کند. توصیه می‌کنیم برای چرخه حیات برنامه از یک نمونه واحد استفاده کنید.
  • videoDisplay: پیاده‌سازی IMAVideoDisplay که به IMA اجازه می‌دهد بازپخش ویدیو را کنترل کند و رویدادهای بازپخش را بااستفاده از AVPlayer ردیابی کند.
  • adDisplayContainer: نمای استفاده‌شده برای پردازش عناصر میانای کاربر آگهی و مدیریت تمرکز میانای کاربر درطول وقفه‌های آگهی را مدیریت می‌کند.
  • streamManager: بازپخش جاری‌سازی ترکیبی آگهی و محتوا را مدیریت می‌کند و رویدادهای چرخه عمر آگهی را بااستفاده از نماینده خود ارسال می‌کند.
  • playerViewController: پخش‌کننده tvOS که برای ارائه جاری‌سازی ویدیو مدیریت‌شده توسط IMA SDK استفاده می‌شود.
  • adBreakActive: پرچم بولی که نشان می‌دهد آیا وقفه آگهی درحال پخش است یا نه، و برای جلوگیری از جستجو در آگهی‌ها و مدیریت تمرکز واسط کاربر استفاده می‌شود.

پیاده‌سازی IMAAdsLoader

سپس، IMAAdsLoader را نمونه‌سازی کنید و نمای محفظه آگهی را به سلسله‌مراتب نما متصل کنید.

آبجکتیو-سی

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

درخواست جاری‌سازی کنید

چند ثابت برای نگهداری اطلاعات جاری‌سازی ایجاد کنید و سپس تابع درخواست جاری‌سازی را برای انجام درخواست پیاده‌سازی کنید.

آبجکتیو-سی

- (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 فعال می‌شوند. به رویداد آگهی بارشده گوش دهید و جاری‌سازی را مقداردهی اولیه کنید. اگر آگهی بار نشد، به‌جای آن یک جاری‌سازی پشتیبان پخش کنید.

آبجکتیو-سی

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

مدیریت رویدادهای خطا و گزارش

نماینده مدیر جاری‌سازی می‌تواند چندین رویداد را مدیریت کند، اما برای پیاده‌سازی‌های پایه، مهم‌ترین کاربردها عبارتند از ثبت رویداد، جلوگیری از کنش‌های جستجو درحین پخش آگهی، و مدیریت خطاها.

آبجکتیو-سی

#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» آگهی درخواست و نمایش می‌دهید. برای آشنایی با ویژگی‌های پیشرفته‌تر کیت توسعه نرم‌افزار، به راهنماهای دیگر یا نمونه‌های موجود در GitHub مراجعه کنید.