Supports Mac Catalyst 13.0 and above since SDK release v6.10.3.
The SSAI plugin for Brightcove Player SDK for iOS provides a dynamic library framework for installation.
You can use CocoaPods to add the SSAI Plugin for Brightcove Player SDK to your project. You can find the latest Brightcove-Player-SSAI
podspec here.
source 'https://github.com/brightcove/BrightcoveSpecs.git'
use_frameworks!
platform :ios, '12.0'
target 'MyVideoPlayer' do
pod 'Brightcove-Player-SSAI'
end
XCFramework will be installed appending the /XCFramework
subspec in the pod.
source 'https://github.com/brightcove/BrightcoveSpecs.git'
use_frameworks!
platform :ios, '12.0'
target 'MyVideoPlayer' do
pod 'Brightcove-Player-SSAI/XCFramework'
end
When updating your installation, it's a good idea to refresh the local copy of your BrightcoveSpecs repository so that you have the latest podspecs locally, just like you would update your CococaPods master repository.
Typically if you use pod update
in Terminal this will happen automatically, or alternatively you can update explicitly with pod repo update brightcove
. (Your BrightcoveSpecs repository may have a different name if you explicitly added it to your list of podspecs repos.)
To add the SSAI Plugin for Brightcove Player SDK to your project manually:
- Install the latest version of the Brightcove Player SDK.
- Download the latest zip'ed release of the BrightcoveSSAI plugin from our release page.
- Add
BrightcoveSSAI.framework
orBrightcoveSSAI.xcframework
to your project. - Add
OMSDK_Brightcove.xcframework
to your project. - On the "Build Settings" tab of your application target, ensure that the "Framework Search Paths" include the paths to the frameworks. This should have been done automatically unless the framework is stored under a different root directory than your project.
- On the "General" tab of your application target, add 'BrightcoveSSAI.framework' or 'BrightcoveSSAI.xcframework' to the "Frameworks, Libraries, Embedded Content" section.
- (Open Measurement support) If you need to support OM in your app add 'OMSDK_Brightcove.xcframework' to the "Frameworks, Libraries, Embedded Content" section.
- (Mac Catalyst and Open Measurement only) In the "Filter" for
OMSDK_Brightcove.xcframework
, ensure that only the valueiOS
is selected. - (Universal Framework only) On the "Build Phases" tab, add a "Run Script" phase with the command
bash ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/BrightcoveSSAI.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. - (Apple Silicon with Universal Framework only) On the "Build Settings" tab of your application target:
- Ensure that
arm64
has been added to your "Excluded Architectures" build setting forAny iOS Simulator SDK
.
- Ensure that
To add the SSAI Plugin for Brightcove Player SDK to your project with Swift Package Manager:
- First follow the steps to add the Core XCFramework with Swift Package Mananger.
- Add the SSAI package to Swift Package Manager using
https://github.com/brightcove/brightcove-player-sdk-ios-ssai.git
. - You can then select either
BrightcoveSSAI
orBrightcoveSSAI & Open Measurement
if you need OM support.
The SSAI Plugin for Brightcove Player SDK for iOS can be imported into code a few different ways; @import BrightcoveSSAI;
, #import <BrightcoveSSAI/BrightcoveSSAI.h>
or #import <BrightcoveSSAI/[specific class].h>
.
BrightcoveSSAI is a plugin for Brightcove Player SDK for iOS that provides support for Brightcove Server Side Ad Insertion.
[1] BCOVSSAIAdComponentDisplayContainer *displayContainer = [[BCOVSSAIAdComponentDisplayContainer alloc] initWithCompanionSlots:@[]];
BCOVPlayerSDKManager *manager = [BCOVPlayerSDKManager sharedManager];
[2] id<BCOVPlaybackController> playbackController = [sdkManager createSSAIPlaybackController];
[3] [playbackController addSessionConsumer:displayContainer];
[4] [self.videoContainer addSubview:playbackController.view];
[5] BCOVPlaybackService *playbackService = [[BCOVPlaybackService alloc] initWithAccountId:accoundId
policyKey:policyKey];
NSDictionary *configuration = @{
kBCOVPlaybackServiceConfigurationKeyAssetID:videoID
};
[playbackService findVideoWithConfiguration:configuration
queryParameters:@{ kBCOVPlaybackServiceParamaterKeyAdConfigId: <valid-ad-config-id> }
completion:^(BCOVVideo *video,
NSDictionary *jsonResponse,
NSError *error) {
[6] [playbackController setVideos:@[ video ]];
[playbackController play];
}];
To summarize:
- First create a
BCOVSSAIAdComponentDisplayContainer
. This object will help manage companion slots. Pass in the companion slots that you have, if any. - BrightcoveSSAI adds some category methods to
BCOVPlaybackManager
. The first of these is-createSSAIPlaybackControllerWithViewStrategy:
. Use this method to create your playback controller. You will typically passnil
for the view strategy. - In order for the
BCOVSSAIAdComponentDisplayContainer
to display ad information and populate companion ad views, it must be added as a session consumer. - Add the playback controller's view to the video container in your own view hierarchy.
- Request the video or playlist from the Playback Service. When using a Unicorn Once-style VMAP URL, create the BCOVVideo object directly using
[BCOVVideo videoWithURL:<unicorn-style-url>]
. - Load the video into the playback controller.
Open Measurement (OM) is an IAB Tech Lab initiative aimed to standardize viewability and verification measurement. Using OM, publishers can access multiple SDKs collapsed into a single integration, simplifying maintenance without sacrificing functionality. The SDK provided by IAB Tech Lab can be used in the SSAI Plugin for Brightcove Player for iOS. Since v6.10.3 the OMSDK is not installed along with the SSAI plugin. You can install it in your iOS apps adding the pod in your Podfile:
Universal Framework (Fat Framework) example:
pod 'Brightcove-Player-SSAI'
pod 'Brightcove-Player-OpenMeasurement'
XCFramework example:
pod 'Brightcove-Player-SSAI/XCFramework'
pod 'Brightcove-Player-OpenMeasurement'
To take the advantage of using IAB Open Measurement, the SSAI Plugin for iOS provides a new signature:
[BCOVPlayerSDKManager createSSAISessionProviderWithUpstreamSessionProvider:omidPartner:]
The omidPartner
string identifies the integration. The value can not be empty or nil, if partner is not available, use "unknown". The IAB Tech Lab will assign a unique partner name to you at the time of integration, so this is the value you should use here. However, if the OpenMeasurement SDK is not embedded in your iOS app the omidPartner
string will be ignored and OM will not take effect. The [BCOVPlayerSDKManager createSSAISessionProviderWithUpstreamSessionProvider:omidPartner:]
signature is not available for Mac Catalyst.
Open Measurement can not be used in Mac Catalyst apps due IAB Tech Lab does not provide the necessary slice. If you need to create an app that shares code between iOS and Mac Catalyst, and uses Open Measurement for iOS app follow the steps:
- Remove 'Brightcove-Player-OpenMeasurement' from your Podfile.
- Update your pods running
pod update
. - Download the latest zip'ed release of the
BrightcoveSSAI
plugin from our release page. - Add
OMSDK_Brightcove.xcframework
to your project. - On the "General" tab of your application target, add
OMSDK_Brightcove.xcframework
to the "Frameworks, Libraries, Embedded Content" section. - In the "Filter" for
OMSDK_Brightcove.xcframework
, ensure that only the valueiOS
is selected.
If you have a custom VMAP source URL and do not need to use the BCOVPlaybackService
you can manually create a BCOVVideo
with your URL like this:
BCOVVideo *video = [BCOVVideo videoWithURL:[NSURL URLWithString:@"https://sdks.support.brightcove.com/assets/ads/ssai/sample-vmap.xml"]];
[self.playbackController setVideos:@[video]];
If you have VMAP XML data you can manually create a BCOVVideo
with your data like this:
// If using FairPlay DRM you'll need to construct the key_systems dictionary
// otherwise you can pass `nil` for the properties value.
NSDictionary *properties = @{
@"key_systems": @{
@"com.apple.fps.1_0": @{
@"key_request_url": @"<insert key request URL>",
@"certificate_url": @"<insert certificate URL>"
}
}
};
BCOVSource *source = [[BCOVSource alloc] initWithVMAPXMLData:vmapData properties:properties];
BCOVVideo *video = [[BCOVVideo alloc] initWithSource:source cuePoints:nil properties:nil];
[self.playbackController setVideos:@[video]];
BrightcoveSSAI provides ad playback information via the BCOVPlaybackControllerAdsDelegate
. For example, if you want to hide your controls during an ad, you could implement -[BCOVPlaybackControllerAdsDelegate playbackController:playbackSession:didEnterAdSequence:]
to hide them.
For more information on how to use these delegate methods, please see Brightcove Player SDK for iOS.
The BrightcoveSSAI plugin provides a seeking function that should be used when implementing controls. This seek function is exposed on the session through the providerExtension
property. Here is how it is used:
CMTime contentTimeToSeekTo = <calculation-from-scrub-bar>;
[self.currentSession.providerExtension ssai_seekToTime:contentTimeToSeekTo completionHandler:^(BOOL finished) {
if (finished)
{
[self.playbackController play];
}
}];
The completionHandler
will execute at the completion of a successful seek. It will not execute if a seek was already initiated by a previous call to -[BCOVSessionProviderExtension ssai_seekToTime:completionHandler:]
or if an ad is playing back. To test whether a seek attempt can be made, check the -[BCOVSessionProviderExtension ssai_canSeek]
property. For more information on both of these methods, be sure to read the headerdoc.
The BrightcovePlayerSDK provides the adsDisabled
BOOL property for disabling ads while seeking, allowing an application to resume playback without requiring the end-user to view previously played ads.
In preparation for seeking, disable autoPlay
when setting up the BCOVPlaybackController
.
Ad-disabling logic should be added to the kBCOVPlaybackSessionLifecycleEventReady
handler of the -playbackController:playbackSession:didReceiveLifecycleEvent:
method of your BCOVPlaybackController
delegate.
- (void)playbackController:(id<BCOVPlaybackController>)controller
playbackSession:(id<BCOVPlaybackSession>)session
didReceiveLifecycleEvent:(BCOVPlaybackSessionLifecycleEvent *)lifecycleEvent
{
if ([kBCOVPlaybackSessionLifecycleEventReady isEqualToString:lifecycleEvent.eventType])
{
// disable ads.
_playbackController.adsDisabled = YES;
// seek somewhere into the video content.
[session.providerExtension ssai_seekToTime:resumeTime completionHandler:^(BOOL finished)
{
// re-enable ads.
_playbackController.adsDisabled = NO;
// open the shutter.
_playbackController.shutterFadeTime = 0.25;
_playbackController.shutter = NO;
}];
}
When calling ssai_seekToTime:completionHandler:
to resume playback at a particular time, the first frame of the video might be visible until the seek completes. For a cleaner presentation, temporarily cover the video view during seeking by setting the shutter
property of BCOVPlabackController to YES
before calling -setVideos:
. When seeking is complete, dismiss the shutter by setting the shutter
property to NO
. The shutterFadeTime
property defines the duration of the shutter fade animation.
self.playbackController = [sdkManager createSSAIPlaybackController];
// activate the shutter before loading video.
self.playbackController.shutterFadeTime = 0.0;
self.playbackController.shutter = YES;
// load the video.
Note that for performance reasons, small tolerances are built into video seeking. A seek to a content playhead position which is close to the start of an ad sequence can result in a seek to the start of the ad sequence. If autoPlay is NO in this case, AVPlayer will be paused on the first frame of the ad sequence.
The BrightcovePlayerSDK provides a built-in set of UI controls that can be used with the SSAI plugin for both basic playback and ad controls. To use the controls, create a BCOVPUIPlayerView
, and associate it with your SSAI playback controller.
First, create a playerView property in your class.
@property (nonatomic) BCOVPUIPlayerView *playerView;
Create the BCOVPUIPlayerView
instance and save a reference to the object.
BCOVPUIBasicControlView *controlView = [BCOVPUIBasicControlView basicControlViewWithVODLayout];
self.playerView = [[BCOVPUIPlayerView alloc] initWithPlaybackController:nil options:nil controlsView:controlView];
// Insert the playerView into your own video view.
[self.videoContainer addSubview:self.playerView];
You'll need to set up the layout for the player view, you can do this with Auto Layout or the older Springs & Struts method.
Set its frame to match your container view, then add the player view to the container view in your view hierarchy. Note that videoContainer
is your own view object in your app's layout.
self.playerView.frame = self.videoContainer.bounds;
self.playerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.playerView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.playerView.topAnchor constraintEqualToAnchor:self.videoContainer.topAnchor],
[self.playerView.rightAnchor constraintEqualToAnchor:self.videoContainer.rightAnchor],
[self.playerView.leftAnchor constraintEqualToAnchor:self.videoContainer.leftAnchor],
[self.playerView.bottomAnchor constraintEqualToAnchor:self.videoContainer.bottomAnchor],
]];
Now create the BCOVPlaybackController
, assign it to your player view, and then start playing videos.
// Initialize companion slots
BCOVSSAIAdComponentDisplayContainer *displayContainer = [[BCOVSSAIAdComponentDisplayContainer alloc] initWithCompanionSlots:@[]];
// Create the playback controller
BCOVPlayerSDKManager *manager = [BCOVPlayerSDKManager sharedManager];
id<BCOVPlaybackController> playbackController = [sdkManager createSSAIPlaybackControllerWithViewStrategy:nil];
// Listen for display/companion ad messages
[playbackController addSessionConsumer:displayContainer];
// Tell the player view this is the playback controller we're using
self.playerView.playbackController = playbackController;
// Create and play your video. For Unicorn Once-style VMAP URLs, create the BCOVVideo object directly.
BCOVPlaybackService *playbackService = [[BCOVPlaybackService alloc] initWithAccountId:accoundId
policyKey:policyKey];
NSDictionary *configuration = @{
kBCOVPlaybackServiceConfigurationKeyAssetID:videoID
};
[playbackService findVideoWithConfiguration:configuration
queryParameters:{ kBCOVPlaybackServiceParamaterKeyAdConfigId: <valid-ad-config-id> }
completion:^(BCOVVideo *video,
NSDictionary *jsonResponse,
NSError *error) {
[playbackController setVideos:@[ video ]];
[playbackController play];
}];
See the README in the BrightcovePlayerSDK for more details about how to use and customize the PlayerUI controls.
Should you want access to the VMAP response data you can subscribe to the kBCOVSSAIVMAPResponseReceivedNotification
notification. Once received, the notification's userInfo dictionary will contain the VMAP response as NSData. You can use the kBCOVSSAIVMAPResponseReceivedNotificationDataUserInfoKey
constant to access it from userInfo. Since you may have multiple playback controllers, and thus multiple VMAP responses, you can check the notification's object, id playbackController = notification.object
to verify which video the VMAP data is for. Additionally, when you subscribe to the notification you can set a playback controller as the object so that only VMAP data notifications regarding that playback controller will be received. For example:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vmapResponseReceived:) name:kBCOVSSAIVMAPResponseReceivedNotification object:self.playbackController];
You can also access the VMAP response data through the BCOVOUXSession
object:
// Obj-C
- (void)playbackController:(id<BCOVPlaybackController>)controller didAdvanceToPlaybackSession:(id<BCOVPlaybackSession>)session
{
if ([session respondsToSelector:NSSelectorFromString(@"vmapResponseData")])
{
NSData *vmapResponseData = [(NSObject *)session valueForKeyPath:@"vmapResponseData"];
NSString *xmlString = [[NSString alloc] initWithData:vmapResponseData encoding:NSUTF8StringEncoding];
NSLog(@"VMAP XML: %@", xmlString);
}
}
// Swift
func playbackController(_ controller: BCOVPlaybackController!, didAdvanceTo session: BCOVPlaybackSession!) {
if (session.responds(to: NSSelectorFromString("vmapResponseData")))
{
let vmapResponseData = (session as? NSObject)?.value(forKeyPath: "vmapResponseData") as? Data
var xmlString: String? = nil
if let vmapResponseData = vmapResponseData {
xmlString = String(data: vmapResponseData, encoding: .utf8)
}
print("VMAP XML: \(xmlString ?? "")")
}
}
If you'd like to enforce linear playback durings ads while using AVPlayerViewController all you have to do is set AVPlayerViewController's delegate to one of your classes and add the following:
#pragma mark - AVPlayerViewControllerDelegate
- (void)playerViewController:(AVPlayerViewController *)playerViewController willPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
playerViewController.requiresLinearPlayback = YES;
}
- (void)playerViewController:(AVPlayerViewController *)playerViewController didPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
playerViewController.requiresLinearPlayback = NO;
}
You can also add your own advertising UI using these methods as well by adding views to AVPlayerViewController's contentOverlayView
when willPresentInterstitialTimeRange:
is called and and removing or hiding it when didPresentInterstitialTimeRange:
is called. Here is an example:
#pragma mark - AVPlayerViewControllerDelegate
- (void)playerViewController:(AVPlayerViewController *)playerViewController willPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
...
[self displayAdUI:CMTimeGetSeconds(interstitial.timeRange.duration)];
}
- (void)playerViewController:(AVPlayerViewController *)playerViewController didPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
...
[self hideAdUI];
}
- (void)displayAdUI:(NSTimeInterval)adLength
{
// UI Setup
// Adding a subview to the AVPlayerViewController's contentOverlayView
...
}
- (void)hideAdUI
{
// Hide the view/s that were added in displayAdUI:
}
You may also wish to prevent users from seeking over an ad. Here is an example of how to prevent that:
#pragma mark - AVPlayerViewControllerDelegate
- (CMTime)playerViewController:(AVPlayerViewController *)playerViewController timeToSeekAfterUserNavigatedFromTime:(CMTime)oldTime toTime:(CMTime)targetTime
{
// Check to see if we'll be seeking over any interstitials
// If we are, seek to the beginning of the last interstitial
// and save the targetTime so we can seek after the interstitial
// has finished.
NSMutableArray *timeRanges = @[].mutableCopy;
for (AVInterstitialTimeRange *timeRange in playerViewController.player.currentItem.interstitialTimeRanges)
{
CMTime startTime = timeRange.timeRange.start;
if (CMTimeCompare(targetTime, startTime) == 1 && CMTimeCompare(startTime, oldTime) == 1)
{
[timeRanges addObject:timeRange];
}
}
// If we encounter an ad we should seek to the start time
// of the ad and save the desired seek time to we can seek
// to it after the ad completes
if (timeRanges.count > 0)
{
self.seekToTime = targetTime;
AVInterstitialTimeRange *lastTimeRange = timeRanges.lastObject;
return lastTimeRange.timeRange.start;
}
return targetTime;
}
- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didExitAdSequence:(BCOVAdSequence *)adSequence
{
...
// If we encountered an ad while seeking we can now
// seek to the desired location
if (CMTimeCompare(self.seekToTime, kCMTimeInvalid) != 0)
{
[playerViewController.player seekToTime:self.seekToTime];
self.seekToTime = kCMTimeInvalid;
}
}
If you'd like to prevent a pre-roll ad from playing again here in an example of how to approach that:
- (void)playerViewController:(AVPlayerViewController *)playerViewController willPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
...
if (CMTimeCompare(interstitial.timeRange.start, kCMTimeZero) == 0 && self.didPlayPreroll)
{
[playerViewController.player seekToTime:interstitial.timeRange.duration];
return;
}
}
- (void)playerViewController:(AVPlayerViewController *)playerViewController didPresentInterstitialTimeRange:(AVInterstitialTimeRange *)interstitial
{
...
if (CMTimeCompare(interstitial.timeRange.start, kCMTimeZero) == 0)
{
self.didPlayPreroll = YES;
}
}
The Brightcove SSAI plugin can be used with Brightcove's Picture-in-Picture support.
When entering and exiting an ad sequence the requiresLinearPlayback
property on the current AVPictureInPictureController
will be enabled and disabled accordingly so that users can not skip ad breaks.
If you want to use a custom playback rate along with using AVPlayerViewController you'll need to disable the automatic generation of interstitialTimeRanges
. You can do this by setting the generateInterstitialTimeRanges
property on your BCOVPlaybackController
to NO
.
SSAI supports URL variables which allow you to pass arbitrary data from the content.vmap URL into the SSAI system. For example the URL content.vmap?foo={{url.foo}}&bar={{url.bar}}
would become content.vmap?foo=123&bar=hello
after replacing each {{url.<variableName>}}
variable with a real value.
Here is an example of how you could perform the variable replacement:
// Objective-C
- (void)requestContentFromPlaybackService
{
__weak typeof(self) weakSelf = self;
...
[self.playbackService findVideoWithConfiguration:configuration queryParameters:queryParmaters completion:^(BCOVVideo *video, NSDictionary *jsonResponse, NSError *error) {
if (jsonResponse)
{
NSDictionary *updatedJSON = [weakSelf replaceSSAIVariablesInJSON:jsonResponse];
video = [BCOVPlaybackService videoFromJSONDictionary:updatedJSON];
[weakSelf.playbackController setVideos:@[video]];
}
}];
}
- (NSDictionary *)replaceSSAIVariablesInJSON:(NSDictionary *)jsonResponse
{
NSMutableDictionary *updatedJson = jsonResponse.mutableCopy;
NSArray *sources = jsonResponse[@"sources"];
NSMutableArray *updatedSources = @[].mutableCopy;
for (NSDictionary *source in sources)
{
NSString *vmapURL = source[@"vmap"];
if ([vmapURL containsString:@"{{url."])
{
NSMutableDictionary *updatedSource = source.mutableCopy;
updatedSource[@"vmap"] = [self replaceSSAIVariablesInVMAPURL:vmapURL];
[updatedSources addObject:updatedSource];
}
else
{
[updatedSources addObject:source];
}
}
updatedJson[@"sources"] = updatedSources;
return updatedJson;
}
- (NSString *)replaceSSAIVariablesInVMAPURL:(NSString *)vmapURL
{
NSDictionary *replacementValues = @{
@"foo": @"123",
@"bar": @"hello"
};
for (NSString *key in replacementValues.allKeys)
{
NSString *valueToReplace = [NSString stringWithFormat:@"{{url.%@}}", key];
NSString *replacementValue = [NSString stringWithFormat:@"%@", replacementValues[key]];
vmapURL = [vmapURL stringByReplacingOccurrencesOfString:valueToReplace withString:replacementValue].mutableCopy;
}
return vmapURL;
}
// Swift
func requestContentFromPlaybackService() {
...
playbackService.findVideo(withConfiguration: configuration, queryParameters: queryParameters, completion: { [weak self] (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) in
guard let jsonResponse = jsonResponse else {
return
}
let updatedJSON = self?.replaceSSAIVariablesInJSON(jsonResponse)
let updatedVideo = BCOVPlaybackService.video(fromJSONDictionary: updatedJSON)
self?.playbackController?.setVideos([updatedVideo] as NSFastEnumeration)
})
}
func replaceSSAIVariablesInJSON(_ jsonResponse: [AnyHashable: Any]) -> [AnyHashable: Any] {
guard let sources = jsonResponse["sources"] as? [[AnyHashable: Any]] else {
return jsonResponse
}
var updatedJson = jsonResponse
var updatedSources = [[AnyHashable: Any]]()
for source in sources {
guard let vmapURL = source["vmap"] as? String else {
continue
}
if vmapURL.contains("{{url.") {
var updatedSource = source
updatedSource["vmap"] = replaceSSAIVariablesInVMAPURL(vmapURL)
updatedSources.append(updatedSource)
} else {
updatedSources.append(source)
}
}
updatedJson["sources"] = updatedSources
return updatedJson
}
func replaceSSAIVariablesInVMAPURL(_ vmapURL: String) -> String {
let replacementValues = [
"foo": "123",
"bar": "hello"
]
var updatedURL = vmapURL
for key in replacementValues.keys {
guard let replacementValue = replacementValues[key] else {
continue
}
let valueToReplace = "{{url.\(key)}}"
updatedURL = updatedURL.replacingOccurrences(of: valueToReplace, with: replacementValue)
}
return updatedURL
}
-
Because tvOS does not support Web browsing, Companion Ads, Learn More and all ad clickthroughs are ignored on that platform.
-
You cannot use BrightcoveSSAI with any other Brightcove plugins except for the BrightcoveFairPlay plugin, or the BrightcoveIMA plugin (for pre-roll ads only).
-
If you want to use the BrightcoveSSAI plugin along with the BrightcoveFairPlay plugin then the BrightcoveFairPlay plugin must be the
upstreamSessionProvider
of the SSAI session provider. -
The SSAI plugin does not support playing non-SSAI content. If you need to play non-SSAI content you need to create another playback controller that does not use the SSAI plugin.
If you have questions, need help or want to provide feedback, please use the Support Portal or contact your Account Manager. To receive notification of new SDK software releases, subscribe to the Brightcove Native Player SDKs Google Group.