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

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

این راهنما نحوه ادغام 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 Package Manager

کیت توسعه نرم‌افزار آگهی‌های رسانه‌ای تعاملی از نسخه ۴.۸.۲ از مدیر بسته 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 را که می‌خواهید استفاده کنید انتخاب کنید. برای پروژه‌های جدید، توصیه می‌کنیم از Up to Next Major Version استفاده کنید.

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

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

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

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

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

آبجکتیو-سی

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

@import GoogleInteractiveMediaAds;

Swift

import AVFoundation
import GoogleInteractiveMediaAds
import UIKit

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

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

آبجکتیو-سی

// 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() تماس می‌گیرد تا جاری‌سازی «درج آگهی پویا» را درخواست کند.

برای تعریف پارامترهای درخواست جاری‌سازی، این مثال از ثابت‌هایی مانند asset key برای جاری‌سازی‌های زنده، یا content source ID و video ID برای جاری‌سازی‌های VOD استفاده می‌کند. این نمونه همچنین از مؤلفه‌های زیر برای مدیریت کیت توسعه نرم‌افزار آگهی‌های رسانه‌ای تعاملی استفاده می‌کند:

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