Skip to main content

Client Side Ad Insertion

The FLAdvertisingGoogleIMA library integrates with GoogleIMA to offer Client Side Ad Insertion capability for both VOD and Live.

GoogleIMA

Supported GoogleIMA SDK version

  • iOS 3.22.1
  • tvOS 4.12.0

Create Ad Request

Create a GoogleIMAAdRequest ad request with ad tag url. The following snippet shows ad request creation with sample ad url end point.​

let adTagUrl =
"https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&" +
"iu=/124319096/external/ad_rule_samples&ciu_szs=300x250" +
"&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vast&" +
"cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&" +
"vid=short_onecue&correlator=&unviewed_position_start=1";
let adRequest = FLAdvertisingGoogleIMAFactory.adRequest(adTagUrl: adTagUrl);

Create Ad Player

Create a ad Player using main content Player and GoogleIMAAdRequest and UIViewController. Please note, the created ad player cannot be used standalone, as it just manages GoogleIMA Ad playback alone. The main content playback is not managed by this ad player.​ The viewcontroler should be the parent of playback view.

// player is the main content player
let adPlayer = FLAdvertisingGoogleIMAFactory.adPlayer(
contentPlayer: player,
request: adRequest,
playerViewController: viewController,
shouldSupportPIP: true
);
note

Pre-fetch license is required for pip support. Otherwise will endup seeing DRM failures.

Create Ad Composed Player

AdComposablePlayer manages both main content player and ad player, providing a seamless main content playback with GoogleIMA ads served as per GoogleIMA Ad response.

let adComposedPlayer = FLAdvertisingGoogleIMAFactory.adComposablePlayer(
contentPlayer: player,
adPlayer: adPlayer,
);

This player could be used like conventional player from FLPlayer library for playback. ​

note

GoogleIMA sdk will provide the skipAd button if the ad is skippable.

Listening to Ad Events

The client can set delegate on ad player and listen to ad events to track the state of ad playback and implement custom UIs. The AdPlayer invokes the following ad delegate methods:

/// Called on ad playback failure
func adPlayer(ad: Ad?, didFailWith error: Error)

/// Called when cue points are available
func adPlayerDidProvide(cuepoints: [Int])

/// Called on ad break start
func adPlayerDidStart(adBreak: AdBreak)

/// Called on ad start
func adPlayerDidStart(ad: Ad)

/// Called on ad playback progress change
func adPlayer(ad: Ad?, didChange progress: TimeInterval)

/// Called on ad playback buffer state change
func adPlayer(ad: Ad?, didChangeBuffer isBuffering: Bool)

/// Called on occurence of ad track event
func adPlayer(ad: Ad?, didTrack event: AdTrackingEvent)

/// Called on ad end
func adPlayerDidEnd(ad: Ad)

/// Called on ad break end
func adPlayerDidEnd(adBreak: AdBreak)

Ad Metadata

AdBreak

Represents an ad break object.

PropertyTypeDescription
positionAdPositionThe position of the ad break, can be on of preroll, midroll or postroll
startTimeTimeIntervalThe start position in seconds
durationTimeIntervalThe duration of the ad break, in seconds
totalAdsIntTotal number of adverts in the ad break
note

The totalAds read from AdPlayer may not be accurate when it comes from adPlayerDidStart event. This may change once ad playback starts. GoogleIMA advises to read totalAds on or after FIRST_QUARTILE ad tracking event, to get a reliable data.

Represents an advert object.

PropertyTypeDescription
adIdStringThe identifier of the advert
sequenceIntThe sequence of the advert
durationTimeIntervalThe duration of the advert in seconds
isSkippableBoolIndicates if the ad is skippable
adBreakAdBreakThe ad break which the advert belongs to
titleStringThe title of the ad
vastPropertiesVASTPropertiesVAST properties of advert
VASTProperties

Represents a VAST properties object.

PropertyTypeDescription
skipOffsetTimeIntervalThe number of seconds before the skip button is presented
isAutoPlayBoolWhether the player will auto-play content
adPositionAdPositionThe position of the ad break, can be on of preroll, midroll or postroll
adSystemstringThe source ad server of the advert
AdTitlestringThe common name of the advert
adServingIdstringAn identifier used to compare impression-level data across systems
creativeIdstringCreativeId of the advert
categorystringCategory of the advert content
descriptionstringThe longer description of the advert
advertiserstringAdvertiser name
pricingstringPricing element of the advert
surveystringAn URI to any resource file having to do with collecting survey data
expiresstringNumber of seconds in which the ad is valid for execution

Events tracked as part of AdTrackingEvent

EventDescription
adBreakStartFired when an ad break in a stream starts.
adStartFired when an ad starts playing.
adFirstQuartileFired when the ad playhead crosses first quartile.
adMidpointFired when the ad playhead crosses midpoint.
adThirdQuartileFired when the ad playhead crosses third quartile.
adPauseFired when an ad is paused.
adResumeFired when an ad is resumed.
adMuteFired when an ad is muted.
adUnmuteFired when an ad is unmuted.
adEndFired when an ad completes playing.
adBreakEndFired when an ad break in a stream ends.
adClickedFired when an ad is clicked.
adPlaybackViewTappedFired when a non-clickthrough portion of a video ad is clicked.
adIconTappedFired when ad icon is tapped.
adIconFallbackImageClosedFired on closing the icon fallback image dialog. This event only fires for Connected TV devices.
adSkippedFired when an ad was skipped.
adCompletedAllFired when all the valid ads in the ads response finished playing, or when the response doesn't return any valid ads.
adCuePointChangedFired when VOD stream cuepoints have changed.

Picture in Picture

​In order to support Picture in Picture, GoogleIMA SDK uses the main content player to play ad assets. In the process of playing ads, the main content asset and ad assets are switched with the player. Whenever a fairplay protected asset is loaded with player on ad break end, a on demand request for license key is made by the player. So it is suggested to prefetch the license key to avoid player requesting on-demand license whever a ad playback ends and main content is resumed playback.

CCPA compliance

The CCPA consent string must be sent as us_privacy query param with ad request url. Please check Google IMA SDK Documentation² to read further on for CCPA compliance. ​ The below code shows an ad request url with the IAB parameter "1YNN".

let adTagUrl =
'https://pubads.g.doubleclick.net/gampad/ads' +
'iu=/124319096/external/single_ad_samples&output=vast&us_privacy=1YNN';

Best Practices for Using AVPlayerViewController in tvOS

Overview

When integrating AVPlayerViewController in a tvOS application, it is recommended to use it as a child view controller within your playback view controller. This ensures that ads, ad UI, and focus management function correctly.

Implementation Example

Below is an example of how to properly integrate AVPlayerViewController within a custom playback view controller.

import UIKit
import AVKit
import FLPlayer
import FLPlayerInterface

class QPAVPlayerViewController: UIViewController {
// AVPlayerViewController instance for handling playback
private var playerViewController: AVPlayerViewController = AVPlayerViewController()

// Quickplay Player instance (set after the ViewController is created)
var qpPlayer: Player? {
didSet {
if let qpPlayer = qpPlayer, let avPlayer = qpPlayer.rawPlayer as? AVPlayer {
embedAVPlayerViewController()
playerViewController.player = avPlayer
addPlaybackView()
}
}
}

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
// Ensure a smooth playback transition
}

// Embeds AVPlayerViewController within the current ViewController
private func embedAVPlayerViewController() {
addChild(playerViewController)
playerViewController.view.frame = view.bounds
view.insertSubview(playerViewController.view, at: 0)
playerViewController.didMove(toParent: self)
}

// Attaches the player’s playback view to AVPlayerViewController
private func addPlaybackView() {
guard let playbackView = qpPlayer?.playbackView else { return }
playbackView.frame = playerViewController.view.bounds
playerViewController.view.insertSubview(playbackView, at: 0)
}
}

Key Considerations

  • Encapsulation: AVPlayerViewController should always be contained within your playback view controller to properly handle media playback and interactions.
  • Focus & UI Handling: Using AVPlayerViewController ensures seamless integration of ads, player controls, and focus management on tvOS.
  • Full-Screen Playback: The example assumes that playback occurs in full screen, which is the default behavior on tvOS.
  • Efficient View Management: The playerViewController instance is properly added and removed using addChild(_:) and didMove(toParent:) to ensure memory management best practices.

By following these best practices, you can ensure a robust and scalable media playback experience on tvOS.