Implementing DAI Plugin for iOS

This topic covers the use of the IMA DAI plugin and how it can be implemented through custom coding.

Overview

The Brightcove SDK offers a plugin to interact with the Google IMA-DAI library for multimedia ads playback in your application, and abstracts the setup and basic interaction with the Google IMA-DAI library so you can focus on requesting video streams.

Cocoapods Implementation

You can use CocoaPods to add the DAI Plugin for Brightcove Player SDK to your project. You can find the latest Brightcove-Player-DAI podspec here. The pod incorporates the correct version of IMA automatically.

CocoaPod podfile snippet:

    source 'https://github.com/CocoaPods/Specs'
    source 'https://github.com/brightcove/BrightcoveSpecs.git'

    platform :ios, '14.0'
    use_frameworks!

    target 'MyDAIPlayer' do
        pod 'Brightcove-Player-DAI'
    end

Manual Implementation

To add the DAI Plugin for Brightcove Player SDK to your project manually follow these steps:

  1. Download the Brightcove Player SDK framework.
  2. Download the DAI Plugin for Brightcove Player SDK framework.
  3. Download the Google IMA framework.
  4. Add the dymnamic framework.
    • On the "General" tab of your application target, add the BrightcovePlayerSDK.framework or BrightcovePlayerSDK.xcframework.
    • From the Brightcove Player SDK download to the list of Frameworks, Libraries, and Embedded Content. The universal Framework and XCFramework are found in the ios/dynamic directory of the download. The Embed setting must be "Embed & Sign".
  5. Add the DAI framework.
    • On the "General" tab of your application target, add the BrightcoveDAI.framework or BrightcoveDAI.xcframework.
    • From the DAI Plugin from Brightcove Player SDK download to the list of Frameworks, Libraries, and Embedded Content. The Embed setting must be "Embed & Sign".
  6. Add the Google Media Ads framework.
    • On the "General" tab of your application target, add the GoogleInteractiveMediaAds.xcframework.
    • From the Google IMA download to the list of Frameworks, Libraries, and Embedded Content. The Embed setting for the XCFrameworks must be "Embed & Sign".
  7. On the "Build Settings" tab of your application target, ensure that the "Framework Search Paths" include the paths to the frameworks.
  8. On the "Build Settings" tab of your application target Ensure that -ObjC has been added to the "Other Linker Flags" build setting.
  9. For (Universal Framework only).
    • On the "Build Phases" tab, add a "Run Script" phase with the command bash ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/BrightcoveDAI.framework/strip-frameworks.sh.
    • Check "Run script only when installing". This will remove unneeded architectures from the build, which is important for App Store submission.
  10. For (Apple Silicon only).
    • On the "Build Settings" tab of your application target, ensure that arm64 has been added to your "Excluded Architectures" build setting for Any iOS Simulator SDK.

Swift Package Manager Implementation

Add the DAI Plugin for Brightcove Player SDK to your project with Swift Package Manager following these steps:

  1. add the Core XCFramework.
    1. Select the "Package Dependencies" tab for your Project
    2. Click the "+" button
    3. In the "Search or Enter Package URL" field enter:

      https://github.com/brightcove/brightcove-player-sdk-ios.git
    4. When the UI updates click the "Add Package" button
    5. After Xcode processess the repo you are prompted to "Choose Package Products"
    6. ensure that your app target is selected and click the "Add Package" button.
  2. Add the DAI package to Swift Package Manager using:

    https://github.com/brightcove/brightcove-player-sdk-ios-dai.git
  3. Download the Google IMA framework.
  4. Add the Google Media Ads framework.
    • On the "General" tab of your application target, add the GoogleInteractiveMediaAds.xcframework.
    • From the Google IMA download to the list of Frameworks, Libraries, and Embedded Content. The Embed setting for the XCFrameworks must be "Embed & Sign".
  5. On the "Build Settings" tab of your application target, ensure that the "Framework Search Paths" include the paths to the frameworks.

Quick Start

Create ads rendering settings:

                IMASettings *imaSettings = [IMASettings new];
                imaSettings.ppid = kViewControllerIMAPublisherID;
                imaSettings.language = kViewControllerIMALanguage;

                IMAAdsRenderingSettings *adsRenderingSettings = [IMAAdsRenderingSettings new];
                adsRenderingSettings.linkOpenerDelegate = self;
                adsRenderingSettings.linkOpenerPresentingController = self;

                UIView *videoContainerView = <UIView of video container>;
                

BCOVDAIAdsRequestPolicy provides methods to specify DAI Streams for VOD and Live. Select the appropriate method to select your ads policy.

        BCOVDAIAdsRequestPolicy *adsRequestPolicy = [BCOVDAIAdsRequestPolicy videoPropertiesAdsRequestPolicy];

        BCOVPlayerSDKManager *manager = [BCOVPlayerSDKManager sharedManager];
        

BrightcoveDAI adds some category methods to BCOVPlaybackManager. The first of these is - createDAIPlaybackControllerWithSettings:adsRenderingSettings:adsRequestPolicy:adContainer:viewController:companionSlots:viewStrategy:. Use this method to create your playback controller.

                id<BCOVPlaybackController> controller = 
                [manager createDAIPlaybackControllerWithSettings:imaSettings
                                            adsRenderingSettings:adsRenderingSettings
                                                adsRequestPolicy:adsRequestPolicy
                                                     adContainer:playerView.contentOverlayView
                                                  viewController:self
                                                  companionSlots:nil
                                                    viewStrategy:nil];
                controller.delegate = self;

                [videoContainerView addSubview:playerView];

                NSString *policyKey = <your-policy-key>;
                NSString *accountId = <your-account-id>;
                NSString *videoID = <your-video-id>;
                BCOVPlaybackService *playbackService = [[BCOVPlaybackService alloc] initWithAccountId:accountID
                                                                                            policyKey:policyKey];
                NSDictionary *configuration = @{
                kBCOVPlaybackServiceConfigurationKeyVideoID:videoID
                };
                [playbackService findVideoWithConfiguration:configuration
                                            queryParameters:nil
                                                 completion:^(BCOVVideo *video,
                                                              NSDictionary *jsonResponse,
                                                              NSError *error) {
                [controller setVideos:@[ video ]];
                [controller play];
                }];
                

The complete code is:

                    IMASettings *imaSettings = [IMASettings new];
                    imaSettings.ppid = kViewControllerIMAPublisherID;
                    imaSettings.language = kViewControllerIMALanguage;

                    IMAAdsRenderingSettings *adsRenderingSettings = [IMAAdsRenderingSettings new];
                    adsRenderingSettings.linkOpenerDelegate = self;
                    adsRenderingSettings.linkOpenerPresentingController = self;

                    UIView *videoContainerView = <UIView of video container>;

                    BCOVDAIAdsRequestPolicy *adsRequestPolicy = [BCOVDAIAdsRequestPolicy videoPropertiesAdsRequestPolicy];

                    BCOVPlayerSDKManager *manager = [BCOVPlayerSDKManager sharedManager];
                    id<BCOVPlaybackController> controller =
                    [manager createDAIPlaybackControllerWithSettings:imaSettings
                                                adsRenderingSettings:adsRenderingSettings
                                                    adsRequestPolicy:adsRequestPolicy
                                                         adContainer:playerView.contentOverlayView
                                                      viewController:self
                                                      companionSlots:nil
                                                        viewStrategy:nil];
                    controller.delegate = self;

                    [videoContainerView addSubview:playerView];

                    NSString *policyKey = <your-policy-key>;
                    NSString *accountId = <your-account-id>;
                    NSString *videoID = <your-video-id>;
                    BCOVPlaybackService *playbackService = [[BCOVPlaybackService alloc] initWithAccountId:accountID
                                                                                                policyKey:policyKey];
                    NSDictionary *configuration = @{
                    kBCOVPlaybackServiceConfigurationKeyVideoID:videoID
                    };
                    [playbackService findVideoWithConfiguration:configuration
                                                queryParameters:nil
                                                    completion:^(BCOVVideo *video,
                                                                 NSDictionary *jsonResponse,
                                                                 NSError *error) {
                              [controller setVideos:@[ video ]];
                              [controller play];
                    }];
                    

Play and Pause

The Brightcove DAI Plugin implements custom play and pause. Use the play method on the BCOVPlaybackController or the -[BCOVSessionProviderExtension dai_play] or -[BCOVSessionProviderExtension dai_pause] (BCOVSessionProviderExtension), and not the AVPlayer.

Using the Built-In PlayerUI

  1. In your UIViewController, create a BCOVPUIPlayerView property called the player view, to contain the playback controls, the video content view, and a special view where DAI can display its ads

                    // PlayerUI's player view
                    @property (nonatomic) BCOVPUIPlayerView *playerView;
                    
  2. Create your player view

    Supply a nil playback controller. This player view contains both the video content view and the view that displays playback controls and ad controls. Set up the player view to match the video container from your layout ( videoView ) when it resizes

                    // Create and configure Control View.
                    BCOVPUIBasicControlView *controlView = [BCOVPUIBasicControlView basicControlViewWithVODLayout];
    
                    // Create the player view with a nil playback controller.
                    self.playerView = [[BCOVPUIPlayerView alloc] initWithPlaybackController:nil options:nil controlsView:controlView];
    
                    // Add BCOVPUIPlayerView to your video view.
                    [self.videoView addSubview:self.playerView];
                    
  3. Set up the layout for the player view

    Do this with Auto Layout or the older Springs & Struts method.

    1. Springs & Struts

                          self.playerView.frame = self.videoView.bounds;
                          self.playerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
                              

    2. Auto Layout

                          self.playerView.translatesAutoresizingMaskIntoConstraints = NO;
                          [NSLayoutConstraint activateConstraints:@[
                                                                    [self.playerView.topAnchor constraintEqualToAnchor:self.videoView.topAnchor],
                                                                    [self.playerView.rightAnchor constraintEqualToAnchor:self.videoView.rightAnchor],
                                                                    [self.playerView.leftAnchor constraintEqualToAnchor:self.videoView.leftAnchor],
                                                                    [self.playerView.bottomAnchor constraintEqualToAnchor:self.videoView.bottomAnchor],
                                                                    ]];
                              
  4. Create the Content Overlay View

    The contentOverlayView is a special view used for overlaying views on the main video content.

                    // Create the playback controller.
                    id<BCOVPlaybackController> controller =
                                        [manager createDAIPlaybackControllerWithSettings:imaSettings
                                                                    adsRenderingSettings:adsRenderingSettings
                                                                        adsRequestPolicy:adsRequestPolicy
                                                                             adContainer:playerView.contentOverlayView
                                                                          viewController:self
                                                                          companionSlots:nil
                                                                            viewStrategy:nil];
    
                    controller.delegate = self;
    
                    // Assign new playback controller to the player view.
                    // This associates the playerController's session with the PlayerUI.
                    // You can keep this player view around and assign new
                    // playback controllers to it as they are created.
                    self.playerView.playbackController = self.playbackController;
                        

Now, when playing video with ads, you will see the PlayerUI controls while playing video content, plus ad markers on the timeline scrubber.

The playerUI is highly customizable. For more info and sample code, please se Custom Layouts section in the README.md file of the Brightcove Native Player SDK repository

Seek without Ads

Use -[BCOVPlaybackController seekWithoutAds:(CMTime)seekToTime completionHandler:(void (^)(BOOL finished))completion] to resume playback at a specific time without forcing the user to watch ads scheduled before seekToTime.

In preparation for seekWithoutAds:completionHandler: , disable autoPlay when setting up the BCOVPlaybackController.

        - (void)playbackController:(NSObject<BCOVPlaybackController>*)controller
                    playbackSession:(NSObject*)session
                         didReceiveLifecycleEvent:(BCOVPlaybackSessionLifecycleEvent *)lifecycleEvent
        {
            if ([kBCOVPlaybackSessionLifecycleEventReady isEqualToString:lifecycleEvent.eventType])
            {
                // self.resumePlayback is a hypothetical instance variable used here for illustration.
                if (self.resumePlayback)
                {
                    __weak typeof(controller) weakController = controller;

                    // seek without playing ads which are scheduled before the seek time, i.e. resume playback.
                    [controller seekWithoutAds:CMTimeMake(seekWithoutAdsValue, seekWithoutAdsTimescale)
                                completionHandler:^(BOOL finished) {

                        if (!finished)
                        {
                            NSLog (@"seekWithoutAds failed to finish");
                        }

                typeof(controller) strongController = weakController;
                if (strongController)
                {
                    // fade out the shutter to reveal the player view.
                    strongController.shutterFadeTime = 0.25;
                    strongController.shutter = NO;

                    // turn off seek without ads - especially important if this player is being used with a playlist
                    self.resumePlayback = NO;
                }
             }];
            }
          }
        }
            

Enable the shutter to hide the player view:

    NSObject<BCOVPlaybackController> *playbackController;

    playbackController = [sdkManager createFWPlaybackControllerWithAdContextPolicy:nil
                                                                      viewStrategy:nil];
    playbackController.delegate = self;

    if (self.resumePlayback)
    {
        // set the shutter fade time to zero to hide the player view immediately.
        playbackController.shutterFadeTime = 0.0;
        playbackController.shutter = YES;

        // disable autoPlay when resuming playback.
        playbackController.autoPlay = NO;
    }
        

Customizing Plugin Behavior

You can combine BCOVDAI with another plugin for the Brightcove Player SDK for iOS to create a custom view strategy and supply a custom ads request policy.

Ads Request Policy for VOD and Live

BCOVDAIAdsRequestPolicy has two factory methods to generate ads request policy; one for VOD and one for Live.

Method for VOD

The method +videoPropertiesAdsRequestPolicy : returns an ads request policy which looks for the kBCOVDAIVideoPropertiesKeySourceId and kBCOVDAIVideoPropertiesKeyVideoId in each BCOVVideo's properties to determine the DAI Stream.

Method for Live

The method +videoPropertiesAssetKeyAdsRequestPolicy : returns an ads request policy that checks each BCOVVideo's properties for the key kBCOVDAIVideoPropertiesKeyAssetKey to determine the DAI Live Stream.

Add properties to a video

You can add properties to a video by using the update: method on the BCOVVideo object. The following example adds the properties needed for VOD:

        // Objective-C
        - (BCOVVideo *)updateVideo:(BCOVVideo *)video
        {
            return [video update:^(id<BCOVMutableVideo> mutableVideo)
            {
                NSDictionary *adProperties = @{
                        kBCOVDAIVideoPropertiesKeySourceId: kViewControllerGoogleDAISourceId,
                        kBCOVDAIVideoPropertiesKeyVideoId: kViewControllerGoogleDAIVideoId
                };

                NSMutableDictionary *propertiesToUpdate = mutableVideo.properties.mutableCopy;
                    propertiesToUpdate addEntriesFromDictionary:adProperties];
                mutableVideo.properties = propertiesToUpdate;
            }];
        }
            
        // Swift
        func updateVideo(_ video: BCOVVideo) -> BCOVVideo {
            return update { (mutableVideo: BCOVMutableVideo?) in
                guard let mutableVideo = mutableVideo else {
                    return
                }

                if var updatedProperties = mutableVideo.properties {
                    updatedProperties[kBCOVDAIVideoPropertiesKeySourceId] = GoogleDAIConfig.SourceID
                    updatedProperties[kBCOVDAIVideoPropertiesKeyVideoId] = GoogleDAIConfig.VideoID

                    mutableVideo.properties = updatedProperties
                }
            }
        }
            

View Strategy

With a custom view strategy, the ad container view and ad companion slots can be tied with the video content view. This is an example of custom view strategy

            BCOVPlaybackControllerViewStrategy customViewStrategy =
            ^UIView* (UIView *view, id<BCOVPlaybackController< playbackController){

                    BCOVPlaybackControllerViewStrategy defaultControlsViewStrategy = [playbackManager defaultControlsViewStrategy];
                    UIView *contentAndDefaultControlsView = defaultControlsViewStrategy(view, playbackController);
                    
                    [ addSubview:contentAndDefaultControlsView];
                        
                    return ;
            };
                

Composing Sesion Providers

If you are using more than one plugin to the Brightcove Player SDK for iOS that needs to create a customized playback controller, you must compose a chain of session providers and pass the final session provider to the -[BCOVPlayerSDKManager createPlaybackControllerWithSessionProvider:viewStrategy:] method.

When composing session providers, the session preloading can be enabled from BCOVBasicSessionProvider.

Registering Ad Overlays

If you are placing any views over ads while they are playing, it is necceessary to register those views with the IMA SDK. Learn more.

To get the current IMAAdDisplayContainer object neccessary to register your overlays from the playbackController:playbackSession:didEnterAdSequence: delegate method of your BCOVPlaybackController instance. Follow the next example:

        - (void)playbackController:(id)controller playbackSession:(id)session
        didEnterAdSequence:(BCOVAdSequence *)adSequence
        {
            NSDictionary *props = session.video.properties;
            IMAAdDisplayContainer *adDisplayContainer = props[kBCOVDAIVideoPropertiesKeyAdDisplayContainer];
            [adDisplayContainer registerFriendlyObstruction:self.adOverlayView];
        }
            

To unregister the obstructions when the ad sequence is finished, the playbackController:playbackSession:didExitAdSequence: delegate method of your BCOVPlaybackController instance. Follow the next example:

            - (void)playbackController:(id)controller playbackSession:(id)session
            didExitAdSequence:(BCOVAdSequence *)adSequence
                
            {
                NSDictionary *props = session.video.properties;
                IMAAdDisplayContainer *adDisplayContainer = props[kBCOVIMAVideoPropertiesKeyAdDisplayContainer];
                [adDisplayContainer unregisterAllFriendlyObstructions];
            }
                

AirPlay

To use this functionality in your apps set enableBackgroundPlayback to YES on IMASettings along with enabling AirPlay on your BCOVPlaybackController. Learn more.

AVPlayerViewController Support

Displaying Ad UI

To display your own Ad UI during ad playback you can use the playbackController:playbackSession:didReceiveLifecycleEvent: delegate method. Here is an example:

        #pragma mark BCOVPlaybackControllerDelegate
            - (void)playbackController:(id)controller
            playbackSession:(id)session
            didReceiveLifecycleEvent:(BCOVPlaybackSessionLifecycleEvent *)lifecycleEvent
            {
                ...
                if ([lifecycleEvent.eventType isEqualToString:kBCOVDAILifecycleEventAdsManagerDidReceiveAdEvent])
                {
                    IMAAdEvent *adEvent = lifecycleEvent.properties[@"adEvent"];
                }
                switch (adEvent.type)
                {
                    case kIMAAdEvent_STARTED:
                        [self displayAdUI];
                        break;
                    case kIMAAdEvent_COMPLETE:
                        [self hideAdUI];
                        break;
                    default:
                        break;
            } 
        }
            

Limitations

  • The use of Brightcove VOD Streams are limited.
  • DRM is not supported.