Video Cloud SSAI with the Native Player SDKs

Product(s)
Video Cloud
SSAI
Role(s)
Device SDK Developer
Topic(s)
Advertising
SSAI
SDK
Android
iOS
tvOS

In this topic, you will learn how to play Server-Side ads with the Brightcove Native Player SDKs, from videos ingested for Dynamic Delivery.

Overview

Server-Side Ad Insertion (SSAI) allows you to embed ads into your videos so that they can't be blocked by ad blockers in the browser. Dynamic Delivery is the next generation ingest and delivery system which reduces your storage footprint and dynamically packages media. Learn more about using Dynamic Delivery for your videos.

SSAI works with both DRM and non-DRM content.

Server-side ad
Server-side ad

To play server-side ads with your video content stored in Video Cloud, follow these steps:

  1. Create an ad configuration
  2. Build your app:

  3. Play a video with ads

Create an ad configuration

The ad configuration defines various aspects of SSAI playback, including a URL to your Video Multiple Ad Playlist (VMAP) file, beacons, and other configurations. For now, you must contact your Brightcove account manager to create your ad configuration.

Android implementation

Follow these steps within your app to get your ad configuration and play your video:

  1. Include the Once UX plugin in your app, as shown in the Basic Once UX Player sample. This will be the starting point for your app. Notice that the plugin uses a companion ad container.
  2. In Android Studio, open your MainActivity.java file.
  3. Comment out or delete the following code:
    private String onceUxAdDataUrl = "http://once.unicornmedia.com/now/ads/vmap/od/auto/c501c3ee-7f1c-4020-aa6d-0b1ef0bbd4a9/202ef8bb-0d9d-4f6f-bd18-f45aa3010fe6/8a146f45-9fac-462e-a111-de60ec96198b/content.once";
    ...
    plugin.processVideo(onceUxAdDataUrl);
  4. Define constants for your account id, policy key, video id, and your ad configuration id.
    private String accountId = "your account id";
    private String policyKey = "your policy key";
    private String videoId = "your video id";
    private String adConfigId = "your ad configuration id";
  5. In the onCreate method, set the query parameter value to your ad configuration id.
    Map<String, String> parameters = new HashMap<>();
    parameters.put("ad_config_id", adConfigId);
    
  6. Create an instance of the catalog service, which provides asynchronous methods for retrieving data from the Playback API.
    Catalog catalog = new Catalog(brightcoveVideoView.getEventEmitter(), accountId, policyKey);
    
  7. Call the catalog service to retrieve your video along with your ad configuration from the Playback API. Start video playback with your specified ads.
    catalog.findVideoByID(videoId, null, parameters, new VideoListener() {
      @Override
      public void onVideo(Video video) {
        try {
          // The OnceUX plugin will look for a VMAP URL in the video object properties.
          // If valid, it will play the video.
          plugin.processVideo(video);
        } catch (NoSourceFoundException e) {
          // If NoSourceFoundException is thrown it means a suitable VMAP URL was not found.
          // You can try to play it on a regular view,
          brightcoveVideoView.add(video);
        }
      }
    });

Android MainActivity file

Your MainActivity.java file should look similar to this:

package com.brightcove.player.samples.onceux.basic;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import com.brightcove.onceux.OnceUxComponent;
import com.brightcove.onceux.event.OnceUxEventType;
import com.brightcove.player.controller.NoSourceFoundException;
import com.brightcove.player.edge.Catalog;
import com.brightcove.player.edge.VideoListener;
import com.brightcove.player.event.Event;
import com.brightcove.player.event.EventEmitter;
import com.brightcove.player.event.EventListener;
import com.brightcove.player.model.Video;
import com.brightcove.player.view.BrightcoveExoPlayerVideoView;
import com.brightcove.player.view.BrightcovePlayer;

import java.util.HashMap;
import java.util.Map;

/**
 * This app illustrates how to use the Once UX plugin to ensure that:
 *
 * - player controls are hidden during ad playback,
 *
 * - tracking beacons are fired from the client side,
 *
 * - videos are clickable during ad playback and visit the appropriate website,
 *
 * - the companion banner is shown on page switched appropriately as new ads are played
 */
public class MainActivity extends BrightcovePlayer {

    // Private class constants

    private final String TAG = this.getClass().getSimpleName();

    private String accountId = "your account Id";
    private String policyKey = "your policy key";
    private String videoId = "your video Id";
    private String adConfigId = "your ad configuration Id";

    // Private instance variables

    private OnceUxComponent plugin;
    public OnceUxComponent getOnceUxPlugin() {
        return plugin;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // When extending the BrightcovePlayer, we must assign brightcoveVideoView before
        // entering the superclass.  This allows for some stock video player lifecycle
        // management.
        setContentView(R.layout.onceux_activity_main);
        brightcoveVideoView = (BrightcoveExoPlayerVideoView) findViewById(R.id.brightcove_video_view);
        brightcoveVideoView.getAnalytics().setAccount("1752604059001");
        super.onCreate(savedInstanceState);

        // Setup the event handlers for the OnceUX plugin, set the companion ad container,
        // register the VMAP data URL inside the plugin and start the video.  The plugin will
        // detect that the video has been started and pause it until the ad data is ready or an
        // error condition is detected.  On either event the plugin will continue playing the
        // video.
        registerEventHandlers();
        plugin = new OnceUxComponent(this, brightcoveVideoView);
        View view = findViewById(R.id.ad_frame);
        if (view != null && view instanceof ViewGroup) {
            plugin.addCompanionContainer((ViewGroup) view);
        }

        Map<String, String> parameters = new HashMap<>();
        parameters.put("ad_config_id", adConfigId);

        Catalog catalog = new Catalog(brightcoveVideoView.getEventEmitter(), accountId, policyKey);

        catalog.findVideoByID(videoId, null, parameters, new VideoListener() {
            @Override
            public void onVideo(Video video) {
                try {
                    // The OnceUX plugin will look for a VMAP URL in the video object properties.
                    // If valid, it will play the video.
                    plugin.processVideo(video);
                } catch (NoSourceFoundException e) {
                    // If NoSourceFoundException is thrown it means a suitable VMAP URL was not found.
                    // You can try to play it on a regular view,
                    brightcoveVideoView.add(video);
                }
            }
        });
   }

    // Private instance methods

    /**
     * Procedural abstraction used to setup event handlers for the OnceUX plugin.
     */
    private void registerEventHandlers() {
        // Handle the case where the ad data URL has not been supplied to the plugin.
        EventEmitter eventEmitter = brightcoveVideoView.getEventEmitter();
        eventEmitter.on(OnceUxEventType.NO_AD_DATA_URL, new EventListener() {
            @Override
            public void processEvent(Event event) {
                // Log the event and display a warning message (later)
                Log.e(TAG, event.getType());
                // TODO: throw up a stock Android warning widget.
            }
        });
    }
}
 

iOS implementation

Follow these steps within your app to get your ad configuration and play your video:

  1. Include the Once UX plugin in your app, as shown in the Basic Once UX Player sample. This will be the starting point for your app. Notice that the storyboard contains the definitions for the videoContainerView and the companionSlotContainerView.
  2. In Xcode, open your ViewController.m file.
  3. Comment out or delete the following code:
    static NSString *kViewControllerVideoURLString = @"http://onceux.unicornmedia.com/now/ads/vmap/od/auto/c6589dd5-8f31-4ae3-8a5f-a54ca3d7c973/632f6399-9e87-4ce2-a7c0-39209be2b5d0/bee45a63-71ea-4a20-800f-b67091867eeb/content.once";
    ...
    // Create video
    BCOVVideo *video = [BCOVVideo videoWithURL:[NSURL URLWithString:kViewControllerVideoURLString]];
    [self.controller setVideos:@[video]];
    
  4. Define constants for your account id, policy key, video id, and your ad configuration id.
    static NSString *accountId = @"your account id";
    static NSString *policyKey = @"your policy key";
    static NSString *videoId = @"your video id";
    static NSString *adConfigId = @"your ad configuration id";
    
  5. Define the BCOVPlaybackService class, which provides asynchronous methods for retrieving data from the Playback API.
    @interface ViewController () <BCOVPlaybackControllerDelegate>
    @property (nonatomic, strong) id<BCOVPlaybackController> controller;
    @property (nonatomic, strong) BCOVPlaybackService *playbackService;
    @property (nonatomic) BCOVPUIPlayerView *playerView;
    @property (weak, nonatomic) IBOutlet UIView *videoContainerView;
    @property (weak, nonatomic) IBOutlet UIView *companionSlotContainerView;
    
    @end
  6. In the viewDidLoad method, set the query parameter value to your ad configuration id.
    NSDictionary *queryParameters = @{
      @"ad_config_id" : adConfigId
    };
    
  7. Create an instance of the BCOVPlaybackService.
    self.playbackService = [[BCOVPlaybackService alloc]
      initWithAccountId:accountId
      policyKey:policyKey];
  8. Call the playbackService to retrieve your video along with your ad configuration from the Playback API. Start video playback with your specified ads.
    [self.playbackService findVideoWithVideoID:videoId parameters:queryParameters completion:^(BCOVVideo *video, NSDictionary *jsonResponse, NSError *error) {
      {
        if (error == nil)
        {
          // play it.
          [self.controller setVideos: @[ video ]];
        }
        else
        {
          NSLog (@"Error! Playback Service says: %@",
                 error.localizedDescription);
        }
      }
    }];

iOS ViewController using Objective-C

Your ViewController.m file should look similar to this:

//
//  ViewController.m
//  SSAI with Video Id
//
//  Copyright © 2018 Brightcove. All rights reserved.
//

#import "ViewController.h"

@import BrightcovePlayerSDK;
@import BrightcoveOUX;

NSString *accountId = @"your account Id";
NSString *policyKey = @"your policy key";
NSString *videoId = @"your video Id";
NSString *adConfigId = @"your ad configuration Id";

@interface ViewController () <BCOVPlaybackControllerDelegate>
@property (nonatomic, strong) id<BCOVPlaybackController> playbackController;
@property (nonatomic, strong) BCOVPlaybackService *playbackService;
@property (nonatomic) BCOVPUIPlayerView *playerView;
@property (weak, nonatomic) IBOutlet UIView *videoContainerView;
@property (weak, nonatomic) IBOutlet UIView *companionSlotContainerView;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  BCOVPlayerSDKManager *manager = [BCOVPlayerSDKManager sharedManager];

  // Create a companion slot.
  BCOVOUXCompanionSlot *companionSlot = [[BCOVOUXCompanionSlot alloc] initWithView:self.companionSlotContainerView width:500 height:61];

  // In order to display an ad progress banner on the top of the view, we create this display container.  This object is also responsible for populating the companion slots.
  BCOVOUXAdComponentDisplayContainer *adComponentDisplayContainer = [[BCOVOUXAdComponentDisplayContainer alloc]   initWithCompanionSlots:@[companionSlot]];

  self.playbackController = [manager createOUXPlaybackControllerWithViewStrategy:nil];

  // In order for the ad display container to receive ad information, we add it as a session consumer.
  [self.playbackController addSessionConsumer:adComponentDisplayContainer];

  self.playbackController.delegate = self;
  self.playbackController.autoPlay = YES;

  BCOVPUIBasicControlView *controlView = [BCOVPUIBasicControlView basicControlViewWithVODLayout];
  // Set playback controller later.
  self.playerView = [[BCOVPUIPlayerView alloc] initWithPlaybackController:nil options:nil controlsView:controlView];

  [self.videoContainerView addSubview:self.playerView];
  self.playerView.translatesAutoresizingMaskIntoConstraints = NO;
  [NSLayoutConstraint activateConstraints:@[
      [self.playerView.topAnchor constraintEqualToAnchor:self.videoContainerView.topAnchor],
      [self.playerView.rightAnchor constraintEqualToAnchor:self.videoContainerView.rightAnchor],
      [self.playerView.leftAnchor constraintEqualToAnchor:self.videoContainerView.leftAnchor],
      [self.playerView.bottomAnchor constraintEqualToAnchor:self.videoContainerView.bottomAnchor],
  ]];

  self.playerView.playbackController = self.playbackController;

  NSDictionary *queryParameters = @{
                                @"ad_config_id" : adConfigId
                                };

  self.playbackService = [[BCOVPlaybackService alloc]
                      initWithAccountId:accountId
                      policyKey:policyKey];

  [self.playbackService findVideoWithVideoID:videoId parameters:queryParameters completion:^(BCOVVideo *video, NSDictionary *jsonResponse, NSError *error) {
    {
        if (error == nil)
        {
            // play it.
            [self.playbackController setVideos: @[ video ]];
        }
        else
        {
            NSLog (@"Error! Playback Service says: %@",
                   error.localizedDescription);
        }
    }
  }];
}

#pragma mark BCOVPlaybackControllerBasicDelegate

- (void)playbackController:(id<BCOVPlaybackController>)controller didAdvanceToPlaybackSession:(id<BCOVPlaybackSession>)session
{
  NSLog(@"ViewController Debug - Advanced to new session.");
}

#pragma mark BCOVPlaybackControllerAdsDelegate

- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didEnterAdSequence:(BCOVAdSequence *)adSequence
{
  NSLog(@"ViewController Debug - Entering ad sequence");
}

- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didExitAdSequence:(BCOVAdSequence *)adSequence
{
  NSLog(@"ViewController Debug - Exiting ad sequence");
}

- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didEnterAd:(BCOVAd *)ad
{
  NSLog(@"ViewController Debug - Entering ad");
}

- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didExitAd:(BCOVAd *)ad
{
  NSLog(@"ViewController Debug - Exiting ad");
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}
@end

iOS ViewController using Swift

Your ViewController.swift file should look similar to this:

//
//  ViewController.swift
//  SSAI with Video Id
//
//  Copyright © 2018 Brightcove. All rights reserved.
//

import UIKit
import BrightcovePlayerSDK
import BrightcoveOUX

// ** Customize these values with your own account information **
struct Constants {
  static let kAccountId = "your account id"
  static let kPolicyKey = "your policy key"
  static let kVideoId = "your video id"
  static let kAdConfigId = "your ad configuration id"
}

class ViewController: UIViewController {
  @IBOutlet weak var videoContainerView: UIView!
  @IBOutlet weak var companionSlotContainerView: UIView!

  let playbackService = BCOVPlaybackService(accountId: Constants.kAccountId, policyKey: Constants.kPolicyKey)
  var controller: BCOVPlaybackController?
  var playerView: BCOVPUIPlayerView?

    override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.

      // Create a companion slot.
      let companionSlot = BCOVOUXCompanionSlot(view: companionSlotContainerView, width: 500, height: 61)

      // In order to display an ad progress banner on the top of the view, we create this display container.  This object is also responsible for populating the companion slots.
      let adComponentDisplayContainer = BCOVOUXAdComponentDisplayContainer(companionSlots: [companionSlot])

      controller = BCOVPlayerSDKManager.shared().createOUXPlaybackController(viewStrategy: nil)

      // In order for the ad display container to receive ad information, we add it as a session consumer.
      controller?.add(adComponentDisplayContainer)
      controller?.delegate = self
      controller?.isAutoPlay = true

      let controlView = BCOVPUIBasicControlView.withVODLayout()

      // Set playback controller later.
      playerView = BCOVPUIPlayerView(playbackController: nil, options: nil, controlsView: controlView)
      if let playerView = playerView {
        playerView.translatesAutoresizingMaskIntoConstraints = false
        videoContainerView.addSubview(playerView)
        NSLayoutConstraint.activate([
          playerView.topAnchor.constraint(equalTo: videoContainerView.topAnchor),
          playerView.rightAnchor.constraint(equalTo: videoContainerView.rightAnchor),
          playerView.leftAnchor.constraint(equalTo: videoContainerView.leftAnchor),
          playerView.bottomAnchor.constraint(equalTo: videoContainerView.bottomAnchor)
        ])
        playerView.playbackController = controller
      }

      requestContentFromPlaybackService()
    }

    func requestContentFromPlaybackService() {
      let queryParameters = ["ad_config_id": Constants.kAdConfigId]

      playbackService?.findVideo(withVideoID: Constants.kVideoId, parameters: queryParameters) { (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in

        if let v = video {
          self.controller?.setVideos([v] as NSArray)
        } else {
          print("ViewController Debug - Error retrieving video: \(error?.localizedDescription ?? "unknown error")")
        }
      }
    }

    // MARK: - BCOVPlaybackControllerDelegate
    extension ViewController: BCOVPlaybackControllerDelegate {

        func playbackController(_ controller: BCOVPlaybackController!, didAdvanceTo session: BCOVPlaybackSession!) {
            print("ViewController Debug - Advanced to new session.")
        }
    }

    // MARK: - BCOVPlaybackControllerAdsDelegate
    extension ViewController: BCOVPlaybackControllerAdsDelegate {

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter adSequence: BCOVAdSequence!) {
        print("ViewController Debug - Entering ad sequence")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAdSequence adSequence: BCOVAdSequence!) {
        print("ViewController Debug - Exiting ad sequence")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter ad: BCOVAd!) {
        print("ViewController Debug - Entering ad")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAd ad: BCOVAd!) {
        print("ViewController Debug - Exiting ad")
    }
}
 

tvOS implementation

Follow these steps within your app to get your ad configuration and play your video:

  1. Start with the Basic Once UX Player for tvOS sample written in Swift.
  2. Either delete or comment out the code in the VideoConfig.swift file.
  3. In Xcode, open your ViewController.m file.
  4. Define constants for your account id, policy key, video id, and your ad configuration id.
    struct VideoConfig {
      static let kAccountId = "your account id"
      static let kPolicyKey = "your policy key";
      static let kVideoId = "your video id"
      static let kAdConfigId = "your ad configuration id"
    }
    
  5. Create an instance of the BCOVPlaybackService.
    let playbackService = BCOVPlaybackService(accountId: VideoConfig.kAccountId, policyKey: VideoConfig.kPolicyKey)
  6. In the requestContentFromPlaybackService() method, set the query parameter value to your ad configuration id.
    let queryParameters = ["ad_config_id": VideoConfig.kAdConfigId]
    
  7. Call the playbackService to retrieve your video along with your ad configuration from the Playback API. Start video playback with your specified ads.
    playbackService?.findVideo(withVideoID: VideoConfig.kVideoId, parameters: queryParameters) { (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in
    
      if let v = video {
        //  since "isAutoPlay" is true, setVideos will begin playing the content
        self.playbackController?.setVideos([v] as NSArray)
      } else {
        print("ViewController Debug - Error retrieving video: \(error?.localizedDescription ?? "unknown error")")
      }
    }

tvOS ViewController using Swift

Your ViewController.swift file should look similar to this:

//
//  ViewController.swift
//  SSAI POC = tvOS + Swift + SSAI + VOD
//
//  Copyright © 2018 Brightcove. All rights reserved.
//

import UIKit
import BrightcovePlayerSDK
import BrightcoveOUX

// ** Customize these values with your own account information **
struct VideoConfig {
  static let kAccountId = "your account id"
  static let kPolicyKey = "your policy key";
  static let kVideoId = "your video id"
  static let kAdConfigId = "your ad configuration id"
}

class ViewController: UIViewController {
    @IBOutlet weak var videoContainerView: UIView!

    let playbackService = BCOVPlaybackService(accountId: VideoConfig.kAccountId, policyKey: VideoConfig.kPolicyKey)

    private lazy var playerView: BCOVTVPlayerView? = {
        let options = BCOVTVPlayerViewOptions()
        options.presentingViewController = self

        guard let _playerView = BCOVTVPlayerView(options: options) else {
            return nil
        }

        _playerView.translatesAutoresizingMaskIntoConstraints = false
        self.videoContainerView.addSubview(_playerView)
        NSLayoutConstraint.activate([
            _playerView.topAnchor.constraint(equalTo: self.videoContainerView.topAnchor),
            _playerView.rightAnchor.constraint(equalTo: self.videoContainerView.rightAnchor),
            _playerView.leftAnchor.constraint(equalTo: self.videoContainerView.leftAnchor),
            _playerView.bottomAnchor.constraint(equalTo: self.videoContainerView.bottomAnchor)
            ])

        return _playerView
    }()

    private lazy var playbackController: BCOVPlaybackController? = {
        guard let _playbackController = BCOVPlayerSDKManager.shared()?.createOUXPlaybackController(viewStrategy: nil) else {
            return nil
        }

        _playbackController.delegate = self
        _playbackController.isAutoAdvance = true
        _playbackController.isAutoPlay = true

        return _playbackController
    }()

    private weak var currentSession: BCOVPlaybackSession?

    private var currentTime: TimeInterval?
    private var duration: TimeInterval?
    private var playingAdSequence = false
    private var topDrawerView: UIView?

    override var preferredFocusEnvironments: [UIFocusEnvironment] {
        return [playerView?.controlsView ?? self]
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // Link the playback controller to the Player View
        playerView?.playbackController = playbackController

        requestContentFromPlaybackService()
    }

    func requestContentFromPlaybackService() {
        let queryParameters = ["ad_config_id": VideoConfig.kAdConfigId]

        playbackService?.findVideo(withVideoID: VideoConfig.kVideoId, parameters: queryParameters) { (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in

            if let v = video {
                //  since "isAutoPlay" is true, setVideos will begin playing the content
                self.playbackController?.setVideos([v] as NSArray)
            } else {
                print("ViewController Debug - Error retrieving video: \(error?.localizedDescription ?? "unknown error")")
            }
        }
    }
}
// MARK: - BCOVPlaybackControllerDelegate
extension ViewController: BCOVPlaybackControllerDelegate {
    func playbackController(_ controller: BCOVPlaybackController!, didAdvanceTo session: BCOVPlaybackSession!) {
        currentSession = session
        print("ViewController Debug - Advanced to new session.")
    }
}

// MARK: - BCOVPlaybackControllerAdsDelegate
extension ViewController: BCOVPlaybackControllerAdsDelegate {
    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter adSequence: BCOVAdSequence!) {
        print("ViewController Debug - Entering ad sequence")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAdSequence adSequence: BCOVAdSequence!) {
        print("ViewController Debug - Exiting ad sequence")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter ad: BCOVAd!) {
        print("ViewController Debug - Entering ad")
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAd ad: BCOVAd!) {
        print("ViewController Debug - Exiting ad")
    }

    func playbackController(_ controller: BCOVPlaybackController!, didCompletePlaylist playlist: NSFastEnumeration!) {
        print("ViewController Debug - Playlist complete; replaying video")
        playbackController?.setVideos(playlist)
    }

    func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent!) {
        if let eventType = lifecycleEvent.eventType {
            print("ViewController Debug - lifecycle event type: \(eventType)")
        }
    }

}

tvOS ViewController using Swift + FairPlay

Your ViewController.swift file should look similar to this:

//
//  ViewController.swift
//  AppleTV = tvOS + Swift + SSAI + VOD + FairPlay
//
//  Copyright © 2018 Brightcove. All rights reserved.
//

// This sample app shows how to set up and use the TV Player UI on tvOS.

import UIKit
import BrightcovePlayerSDK
import BrightcoveOUX

struct VideoConfig {
  static let kAccountId = "your account id"
  static let kPolicyKey = "your policy key";
  static let kVideoId = "your video id"
  static let kAdConfigId = "your ad configuration id"
}

class ViewController: UIViewController
{
  @IBOutlet var videoContainerView: UIView!

  var fairPlayAuthProxy: BCOVFPSBrightcoveAuthProxy?
  var playbackController:BCOVPlaybackController?

  let playbackService = BCOVPlaybackService(accountId: kViewControllerAccountID, policyKey: kViewControllerPlaybackServicePolicyKey)

  private lazy var playerView: BCOVTVPlayerView? = {
    let options = BCOVTVPlayerViewOptions()
    options.presentingViewController = self

    guard let _playerView = BCOVTVPlayerView(options: options) else {
      return nil
    }

    _playerView.translatesAutoresizingMaskIntoConstraints = false
    self.videoContainerView.addSubview(_playerView)
    NSLayoutConstraint.activate([
      _playerView.topAnchor.constraint(equalTo: self.videoContainerView.topAnchor),
      _playerView.rightAnchor.constraint(equalTo: self.videoContainerView.rightAnchor),
      _playerView.leftAnchor.constraint(equalTo: self.videoContainerView.leftAnchor),
      _playerView.bottomAnchor.constraint(equalTo: self.videoContainerView.bottomAnchor)
    ])

    return _playerView
  }()

  private weak var currentSession: BCOVPlaybackSession?

  private var currentTime: TimeInterval?
  private var duration: TimeInterval?
  private var playingAdSequence = false
  private var topDrawerView: UIView?

  override var preferredFocusEnvironments: [UIFocusEnvironment] {
    return [playerView?.controlsView ?? self]
  }

  override func viewDidLoad()
  {
    super.viewDidLoad()

    let sdkManager = BCOVPlayerSDKManager.sharedManager()
    self.fairPlayAuthProxy = BCOVFPSBrightcoveAuthProxy(publisherId: nil,
                                                        applicationId: nil)

    // Create chain of session providers
    let basicSessionProvider = sdkManager?.createBasicSessionProvider(with:nil)

    let fairplaySessionProvider = sdkManager?.createFairPlaySessionProvider(withApplicationCertificate:nil,
                                            authorizationProxy:self.fairPlayAuthProxy!,
                                            upstreamSessionProvider:basicSessionProvider)

    let ouxSessionProvider = sdkManager?.createOUXSessionProvider(withUpstreamSessionProvider: fairplaySessionProvider)

    // Create the playback controller

    let playbackController = sdkManager?.createPlaybackController(with:ouxSessionProvider, viewStrategy:nil)

    playbackController?.isAutoAdvance = false
    playbackController?.isAutoPlay = true
    playbackController?.delegate = self

    if playbackController?.view != nil {
        playbackController?.view.frame = self.videoContainerView.bounds
        playbackController?.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        self.videoContainerView.addSubview((playbackController!.view)!)
    }

    self.playbackController = playbackController

    // Link the playback controller to the Player View
    playerView?.playbackController = playbackController

    requestContentFromPlaybackService()
  }

  func requestContentFromPlaybackService() {
    let queryParameters = ["ad_config_id": VideoConfig.kAdConfigId]

    playbackService?.findVideo(withVideoID: VideoConfig.kVideoId, parameters: queryParameters) { (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in

      if let v = video {
        //  since "isAutoPlay" is true, setVideos will begin playing the content
        self.playbackController?.setVideos([v] as NSArray)
      } else {
        print("ViewController Debug - Error retrieving video: \(error?.localizedDescription ?? "unknown error")")
      }
    }
  }

}
// MARK: - BCOVPlaybackControllerDelegate
extension ViewController: BCOVPlaybackControllerDelegate {

  func playbackController(_ controller: BCOVPlaybackController!, didAdvanceTo session: BCOVPlaybackSession!) {
    currentSession = session
    print("ViewController Debug - Advanced to new session.")
  }
}

// MARK: - BCOVPlaybackControllerAdsDelegate
extension ViewController: BCOVPlaybackControllerAdsDelegate {

  func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter adSequence: BCOVAdSequence!) {
    print("ViewController Debug - Entering ad sequence")
  }

  func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAdSequence adSequence: BCOVAdSequence!) {
    print("ViewController Debug - Exiting ad sequence")
  }

  func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didEnter ad: BCOVAd!) {
    print("ViewController Debug - Entering ad")
  }

  func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didExitAd ad: BCOVAd!) {
    print("ViewController Debug - Exiting ad")
  }

  func playbackController(_ controller: BCOVPlaybackController!, didCompletePlaylist playlist: NSFastEnumeration!) {
    print("ViewController Debug - Playlist complete; replaying video")
    playbackController?.setVideos(playlist)
  }

  func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent!) {
    if let eventType = lifecycleEvent.eventType {
      print("ViewController Debug - lifecycle event type: \(eventType)")
    }
  }
}
 

Playing a video with ads

Any video that you retrieve from Video Cloud that has been ingested with Dynamic Delivery, will include the ads specified in the VMAP file in your ad configuration.