### Install SSAI Plugin with CocoaPods (iOS) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Example of a CocoaPods Podfile to install the Brightcove SSAI plugin for iOS. This requires specifying the BrightcoveSpecs repository and the 'Brightcove-Player-SSAI' pod. ```bash source 'https://github.com/brightcove/BrightcoveSpecs.git' use_frameworks! platform :ios, '12.0' target 'MyVideoPlayer' do pod 'Brightcove-Player-SSAI' end ``` -------------------------------- ### Initialize Player with Shutter Enabled (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Demonstrates the initial setup of the Brightcove playback controller with the shutter feature enabled. This is done to temporarily cover the video view during seeking operations for a smoother user experience. ```swift playbackController = sdkManager.createSSAIPlaybackController() // activate the shutter before loading video. playbackController.shutterFadeTime = 0.0 playbackController.shutter= true // load the video ``` -------------------------------- ### Install SSAI Plugin with CocoaPods XCFramework (iOS) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Example of a CocoaPods Podfile to install the Brightcove SSAI plugin for iOS using the XCFramework subspec. This ensures compatibility with newer Xcode versions and build processes. ```bash source 'https://github.com/brightcove/BrightcoveSpecs.git' use_frameworks! platform :ios, '12.0' target 'MyVideoPlayer' do pod 'Brightcove-Player-SSAI/XCFramework' end ``` -------------------------------- ### Install Open Measurement SDK Dependencies (Bash) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md These commands show how to add the Brightcove Player SSAI and Open Measurement SDKs to your project using CocoaPods. It supports both universal frameworks and XCFrameworks. Ensure you have CocoaPods installed. ```bash pod 'Brightcove-Player-SSAI' pod 'Brightcove-Player-OpenMeasurement' ``` ```bash pod 'Brightcove-Player-SSAI/XCFramework' pod 'Brightcove-Player-OpenMeasurement' ``` -------------------------------- ### Install Brightcove SSAI Plugin using CocoaPods Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Instructions for installing the Brightcove SSAI plugin and the Open Measurement component using CocoaPods. It specifies adding the BrightcoveSpecs repository and the respective pods to the Podfile. ```bash # Podfile source 'https://github.com/brightcove/BrightcoveSpecs.git' use_frameworks! platform :ios, '12.0' target 'MyVideoPlayer' do pod 'Brightcove-Player-SSAI' # For Open Measurement support: pod 'Brightcove-Player-OpenMeasurement' end ``` ```bash # For XCFramework installation: source 'https://github.com/brightcove/BrightcoveSpecs.git' use_frameworks! platform :ios, '12.0' target 'MyVideoPlayer' do pod 'Brightcove-Player-SSAI/XCFramework' pod 'Brightcove-Player-OpenMeasurement' end ``` -------------------------------- ### Create BCOVVideo from Custom VMAP URL (Swift) Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Demonstrates creating a BCOVVideo object from a custom VMAP URL using `BCOVVideo.video(withURL:)`. This is useful for direct VMAP URL integrations or custom ad server setups. It requires the BrightcovePlayerSDK and BrightcoveSSAI frameworks. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class CustomVMAPURLPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self playbackController?.isAutoPlay = true } // Load from direct VMAP URL func loadVideoFromVMAPURL() { if let url = URL(string: "https://your-ad-server.com/vmap/content123.xml") { let video = BCOVVideo.video(withURL: url) playbackController?.setVideos([video]) } } // Load from Unicorn Once-style URL func loadUnicornOnceVideo(contentId: String, adConfigId: String) { let vmapURLString = "https://once.unicornmedia.com/now/vmap/(adConfigId)/(contentId)/content.vmap" if let url = URL(string: vmapURLString) { let video = BCOVVideo.video(withURL: url) playbackController?.setVideos([video]) } } // Load with query parameters for ad targeting func loadVideoWithAdTargeting(baseURL: String, targeting: [String: String]) { var urlComponents = URLComponents(string: baseURL) urlComponents?.queryItems = targeting.map { URLQueryItem(name: $0.key, value: $0.value) } if let url = urlComponents?.url { let video = BCOVVideo.video(withURL: url) playbackController?.setVideos([video]) } } } ``` -------------------------------- ### Handle Ad Lifecycle Events with BCOVPlaybackControllerAdsDelegate in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Implement the BCOVPlaybackControllerAdsDelegate to manage ad sequences and individual ad events. This example demonstrates hiding and showing UI elements during ad playback, identifying ad positions (preroll, midroll, postroll), and accessing ad properties like skippability and VAST extensions. It requires the BrightcovePlayerSDK and BrightcoveSSAI frameworks. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class AdLifecycleViewController: UIViewController, BCOVPlaybackControllerDelegate, BCOVPlaybackControllerAdsDelegate { var playbackController: BCOVPlaybackController? @IBOutlet weak var controlsContainerView: UIView! @IBOutlet weak var adBadgeLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Ensure super.viewDidLoad() is called let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self } // MARK: - BCOVPlaybackControllerAdsDelegate func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didEnter adSequence: BCOVAdSequence) { print("Entering ad sequence with \(adSequence.ads.count) ads") // Hide custom controls during ads controlsContainerView.isHidden = true adBadgeLabel.isHidden = false // Determine ad position switch adSequence.properties[kBCOVAdSequencePropertyKeyPosition] as? String { case "preroll": print("Pre-roll ads starting") case "midroll": print("Mid-roll ads starting") case "postroll": print("Post-roll ads starting") default: break } } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didExitAdSequence adSequence: BCOVAdSequence) { print("Exiting ad sequence") // Show custom controls after ads controlsContainerView.isHidden = false adBadgeLabel.isHidden = true } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, ad: BCOVAd, didEnter ad: BCOVAd) { print("Now playing ad: \(ad.title ?? "Unknown")") print("Ad duration: \(CMTimeGetSeconds(ad.duration)) seconds") // Check for skippable ads if let skippable = ad.properties[BCOVAd.PropertyKeySkippable] as? Bool, skippable { let skipTime = ad.properties[BCOVAd.PropertyKeySkipTime] as? Double ?? 5.0 print("Ad is skippable after \(skipTime) seconds") } // Access VAST extensions if available if let extensions = ad.properties[kBCOVSSAIVASTAdExtensionsKey] as? [[String: Any]] { print("Ad has \(extensions.count) VAST extensions") } if let creativeExtensions = ad.properties[kBCOVSSAIVASTAdCreativeExtensionsKey] as? [[String: Any]] { print("Ad has \(creativeExtensions.count) creative extensions") } } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, ad: BCOVAd, didExitAd ad: BCOVAd) { print("Finished playing ad: \(ad.title ?? "Unknown")") } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, ad: BCOVAd, didProgressTo progress: TimeInterval) { // Update ad progress UI let duration = CMTimeGetSeconds(ad.duration) let remaining = duration - progress adBadgeLabel.text = String(format: "Ad: %.0fs", remaining) } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didPauseAd ad: BCOVAd) { print("Ad paused") } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didResumeAd ad: BCOVAd) { print("Ad resumed") } } ``` -------------------------------- ### Initialize and Play Video with Brightcove SSAI (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md This snippet demonstrates the core steps to initialize the Brightcove SSAI playback controller, add an ad component, and play a video. It requires the Brightcove Player SDK and the SSAI plugin. Inputs include policy key, account ID, video ID, and an ad configuration ID. The output is a playing video within the player view. ```swift let displayContainer = BCOVSSAIAdComponentDisplayContainer(companionSlots: []) let sdkManager = BCOVPlayerSDKManager.sharedManager() let playbackController = sdkManager.createSSAIPlaybackController() playbackController.add(displayContainer) videoView.addSubview(playerView) let policyKey = "" let accountID = "" let videoID = "" let playbackService = BCOVPlaybackService(withAccountId: accountID, policyKey: policyKey) let configuration = [ BCOVPlaybackService.ConfigurationKeyAssetID: videoID ] let queryParameters = [ BCOVPlaybackService.ParamaterKeyAdConfigId: "" ] playbackService.findVideo(withConfiguration: configuration, queryParameters: queryParameters) { (video: BCOVVideo?, jsonResponse: Any?, error: Error?) in if let video { playbackController.setVideos([video]) playbackController.play() } } ``` -------------------------------- ### Initialize SSAI Playback Controller in iOS Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Demonstrates how to create and configure an SSAI-enabled playback controller using `BCOVPlayerSDKManager.createSSAIPlaybackController()`. It sets up the playback controller, delegates, auto-play, and a `BCOVPUIPlayerView` with Picture-in-Picture support. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class VideoPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? var playerView: BCOVPUIPlayerView? override func viewDidLoad() { super.viewDidLoad() // Create SSAI playback controller let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self playbackController?.isAutoPlay = true // Setup player view let options = BCOVPUIPlayerViewOptions() options.showPictureInPictureButton = true guard let playerView = BCOVPUIPlayerView(playbackController: playbackController, options: options, controlsView: BCOVPUIBasicControlView.withVODLayout()) else { return } self.playerView = playerView playerView.frame = view.bounds playerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(playerView) } } ``` -------------------------------- ### Initialize SSAI Session Provider with Open Measurement (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md This Swift code demonstrates how to create an SSAI session provider that integrates with Open Measurement. It requires the `omidPartner` string, which identifies your integration with IAB Tech Lab. If the OpenMeasurement SDK is not present, this parameter is ignored. ```swift sdkManager.createSSAISessionProvider(withUpstreamSessionProvider:omidPartner:) ``` -------------------------------- ### Create BCOVVideo from VMAP XML Data (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Demonstrates how to manually create a BCOVVideo object using VMAP XML data. This is useful when you have VMAP data and need to integrate it into the Brightcove player. It includes optional properties for DRM configurations like FairPlay. ```swift let properties = [ "key_systems": [ "com.apple.fps.1_0": [ "key_request_url": "", "certificate_url": "" ] ] ] let source = BCOVSource(vmapxmlData: vmapData, properties: properties) let video = BCOVVideo(withSource: source, cuePoints: nil, properties: nil) playbackController.setVideos([video]) ``` -------------------------------- ### Preventing Pre-roll Ad Repetition with AVPlayerViewControllerDelegate Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md This Swift code illustrates how to prevent a pre-roll ad from playing again by checking its start time and a flag indicating if the pre-roll has already played. It uses `willPresentInterstitialTimeRange:` to seek past the pre-roll if it has already played and `didPresentInterstitialTimeRange:` to set the flag. ```swift func playerViewController(_ playerViewController: AVPlayerViewController, willPresent interstitial: AVInterstitialTimeRange) { if CMTimeCompare(interstitial.timeRange.start, .zero) == 0 && didPlayPreroll { playerViewController.player?.seek(to: interstitial.timeRange.duration) } } func playerViewController(_ playerViewController: AVPlayerViewController, didPresent interstitial: AVInterstitialTimeRange) { if CMTimeCompare(interstitial.timeRange.start, .zero) == 0 { didPlayPreroll = true } } ``` -------------------------------- ### Displaying Custom Ad UI with AVPlayerViewControllerDelegate Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md This Swift code demonstrates how to display and hide a custom ad user interface by leveraging the `AVPlayerViewControllerDelegate` methods `willPresentInterstitialTimeRange:` and `didPresentInterstitialTimeRange:`. It involves adding and removing views from the `contentOverlayView` of the `AVPlayerViewController`. ```swift func displayAdUI(withAdDuration duration: TimeInterval) { // UI Setup // Adding a subview to the AVPlayerViewController's contentOverlayView } func hideAdUI() { // Hide the view/s that were added in displayAdUI: } // MARK: AVPlayerViewControllerDelegate func playerViewController(_ playerViewController: AVPlayerViewController, willPresent interstitial: AVInterstitialTimeRange) { displayAdUI(withAdDuration: CMTimeGetSeconds(interstitial.timeRange.duration)) } func playerViewController(_ playerViewController: AVPlayerViewController, didPresent interstitial: AVInterstitialTimeRange) { hideAdUI() } ``` -------------------------------- ### Manually Create BCOVVideo with Custom VMAP URL (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md This Swift code snippet shows how to manually create a `BCOVVideo` object using a custom VMAP source URL, bypassing the `BCOVPlaybackService`. This is useful when you have a direct URL to your ad manifest. The video is then loaded into the playback controller. ```swift if let url = URL(string: "https://sdks.support.brightcove.com/assets/ads/ssai/sample-vmap.xml") { let video = BCOVVideo.video(withURL: url) playbackController.setVideos([video]) } ``` -------------------------------- ### Create BCOVSource from VMAP XML Data with Brightcove iOS SDK Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Illustrates how to create a `BCOVSource` directly from VMAP XML data using `initWithVMAPXMLData(_:properties:)`. This method is useful for integrating with custom ad servers or programmatically setting FairPlay DRM properties. It requires importing BrightcovePlayerSDK and BrightcoveSSAI. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class VMAPDataPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self } // Load video from VMAP XML data func loadVideoFromVMAPData(_ vmapData: Data) { // Create source without DRM let source = BCOVSource(vmapxmlData: vmapData, properties: [:]) let video = BCOVVideo(withSource: source, cuePoints: nil, properties: nil) playbackController?.setVideos([video]) } // Load FairPlay DRM video from VMAP XML data func loadDRMVideoFromVMAPData(_ vmapData: Data, keyRequestURL: String, certificateURL: String) { // Configure FairPlay DRM properties let properties: [String: Any] = [ "key_systems": [ "com.apple.fps.1_0": [ "key_request_url": keyRequestURL, "certificate_url": certificateURL ] ] ] let source = BCOVSource(vmapxmlData: vmapData, properties: properties) let video = BCOVVideo(withSource: source, cuePoints: nil, properties: nil) playbackController?.setVideos([video]) } // Fetch VMAP from custom server and create video func fetchAndLoadVMAP(from url: URL) { URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let vmapData = data, error == nil else { print("Failed to fetch VMAP: \(error?.localizedDescription ?? "Unknown error")") return } DispatchQueue.main.async { self?.loadVideoFromVMAPData(vmapData) } }.resume() } } ``` -------------------------------- ### Load Video with SSAI Configuration (Swift) Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Loads a single video from Brightcove Video Cloud with a specified ad configuration ID for SSAI. This function uses the BCOVPlaybackService to find the video and then sets it on the BCOVPlaybackController. Ensure the BrightcovePlayerSDK and BrightcoveSSAI frameworks are imported. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class VideoCloudSSAIPlayerViewController: UIViewController, BCOVPlaybackControllerDelegate { var playbackController: BCOVPlaybackController? var playbackService: BCOVPlaybackService? let accountId = "your-account-id" let policyKey = "your-policy-key" let adConfigId = "your-ad-config-id" override func viewDidLoad() { super.viewDidLoad() // Setup SSAI display container let displayContainer = BCOVSSAIAdComponentDisplayContainer(companionSlots: []) // Setup playback controller let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self playbackController?.isAutoPlay = true playbackController?.add(displayContainer) // Setup playback service playbackService = BCOVPlaybackService( withAccountId: accountId, policyKey: policyKey ) } // Load single video with SSAI func loadVideo(videoId: String) { let configuration: [String: Any] = [ BCOVPlaybackService.ConfigurationKeyAssetID: videoId ] let queryParameters: [String: Any] = [ BCOVPlaybackService.ParamaterKeyAdConfigId: adConfigId ] playbackService?.findVideo( withConfiguration: configuration, queryParameters: queryParameters ) { [weak self] (video, jsonResponse, error) in if let error = error { print("Error loading video: \(error.localizedDescription)") return } if let video = video { self?.playbackController?.setVideos([video]) } } } // Load playlist with SSAI func loadPlaylist(playlistId: String) { let configuration: [String: Any] = [ BCOVPlaybackService.ConfigurationKeyAssetID: playlistId ] let queryParameters: [String: Any] = [ BCOVPlaybackService.ParamaterKeyAdConfigId: adConfigId ] playbackService?.findPlaylist( withConfiguration: configuration, queryParameters: queryParameters ) { [weak self] (playlist, jsonResponse, error) in if let error = error { print("Error loading playlist: \(error.localizedDescription)") return } if let videos = playlist?.videos { self?.playbackController?.setVideos(videos) } } } // Load video by reference ID func loadVideoByReference(referenceId: String) { let configuration: [String: Any] = [ BCOVPlaybackService.ConfigurationKeyAssetReferenceID: referenceId ] let queryParameters: [String: Any] = [ BCOVPlaybackService.ParamaterKeyAdConfigId: adConfigId ] playbackService?.findVideo( withConfiguration: configuration, queryParameters: queryParameters ) { [weak self] (video, jsonResponse, error) in if let video = video { self?.playbackController?.setVideos([video]) } } } } ``` -------------------------------- ### Integrate SSAI with AVPlayerViewController in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt This Swift code demonstrates integrating Brightcove SSAI with AVPlayerViewController. It sets up the playback controller and player view controller, and implements delegate methods to manage ad playback, enforce linear playback during ads, and handle user seeking behavior to prevent skipping ads. Dependencies include AVKit, BrightcovePlayerSDK, BrightcoveSSAI, and CoreMedia. ```swift import AVKit import BrightcovePlayerSDK import BrightcoveSSAI import CoreMedia class AVPlayerViewControllerSSAIViewController: UIViewController, BCOVPlaybackControllerDelegate, AVPlayerViewControllerDelegate { var playbackController: BCOVPlaybackController? var playerViewController: AVPlayerViewController? var seekToTime: CMTime = .invalid var didPlayPreroll = false override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self // Disable automatic interstitial generation if using custom playback rate // playbackController?.generateInterstitialTimeRanges = false setupAVPlayerViewController() } func setupAVPlayerViewController() { playerViewController = AVPlayerViewController() playerViewController?.delegate = self if let playerVC = playerViewController { addChild(playerVC) playerVC.view.frame = view.bounds playerVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(playerVC.view) playerVC.didMove(toParent: self) } } // MARK: - BCOVPlaybackControllerDelegate func playbackController(_ controller: BCOVPlaybackController, didAdvanceTo session: BCOVPlaybackSession) { // Connect AVPlayerViewController to the session's player playerViewController?.player = session.player } func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didExitAdSequence adSequence: BCOVAdSequence) { // Resume to target time after ad sequence if CMTimeCompare(seekToTime, .invalid) != 0 { playerViewController?.player?.seek(to: seekToTime) seekToTime = .invalid } } // MARK: - AVPlayerViewControllerDelegate func playerViewController(_ playerViewController: AVPlayerViewController, willPresent interstitial: AVInterstitialTimeRange) { // Enforce linear playback during ads playerViewController.requiresLinearPlayback = true // Skip pre-roll if already played if CMTimeCompare(interstitial.timeRange.start, .zero) == 0 && didPlayPreroll { playerViewController.player?.seek(to: interstitial.timeRange.duration) } // Display custom ad UI on contentOverlayView displayAdUI(duration: CMTimeGetSeconds(interstitial.timeRange.duration)) } func playerViewController(_ playerViewController: AVPlayerViewController, didPresent interstitial: AVInterstitialTimeRange) { // Re-enable seeking after ad playerViewController.requiresLinearPlayback = false // Track pre-roll completion if CMTimeCompare(interstitial.timeRange.start, .zero) == 0 { didPlayPreroll = true } hideAdUI() } func playerViewController(_ playerViewController: AVPlayerViewController, timeToSeekAfterUserNavigatedFrom oldTime: CMTime, to targetTime: CMTime) -> CMTime { // Prevent seeking over ads guard let interstitialTimeRanges = playerViewController.player?.currentItem?.interstitialTimeRanges else { return targetTime } // Find ads between old time and target time var adsToPlay = [AVInterstitialTimeRange]() for interstitial in interstitialTimeRanges { let adStart = interstitial.timeRange.start if CMTimeCompare(targetTime, adStart) == 1 && CMTimeCompare(adStart, oldTime) == 1 { adsToPlay.append(interstitial) } } // If seeking over ads, seek to last ad and save target for later if let lastAd = adsToPlay.last { seekToTime = targetTime return lastAd.timeRange.start } return targetTime } // MARK: - Ad UI func displayAdUI(duration: TimeInterval) { let adLabel = UILabel() adLabel.text = String(format: "Ad - %.0fs", duration) adLabel.textColor = .white adLabel.backgroundColor = UIColor.black.withAlphaComponent(0.7) adLabel.tag = 999 adLabel.sizeToFit() adLabel.frame.origin = CGPoint(x: 20, y: 20) playerViewController?.contentOverlayView?.addSubview(adLabel) } func hideAdUI() { playerViewController?.contentOverlayView?.viewWithTag(999)?.removeFromSuperview() } } ``` -------------------------------- ### Create SSAI Playback Controller with Custom View Strategy (Swift) Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Creates an SSAI playback controller with a custom view strategy for advanced view customization. Pass nil for the default view strategy or provide a custom block for specialized rendering requirements. This method is part of the BCOVPlayerSDKManager. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class CustomVideoPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() // Create with custom view strategy let customViewStrategy: BCOVPlaybackControllerViewStrategy = { (view, playbackController) in // Custom view configuration let containerView = UIView(frame: view?.bounds ?? .zero) containerView.backgroundColor = .black if let playerView = view { playerView.frame = containerView.bounds playerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] containerView.addSubview(playerView) } return containerView } playbackController = sdkManager.createSSAIPlaybackControllerWithViewStrategy(customViewStrategy) playbackController?.delegate = self playbackController?.isAutoPlay = true if let controllerView = playbackController?.view { controllerView.frame = view.bounds controllerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(controllerView) } } } ``` -------------------------------- ### Create SSAI Session Provider Chained with FairPlay (Swift) Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Creates an SSAI session provider that can be chained with other session providers like FairPlay. This allows combining SSAI with DRM protection for secure ad-supported content. FairPlay must be the upstream session provider. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class DRMVideoPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() // Create FairPlay session provider for DRM let fairPlayAuthProxy = BCOVFPSBrightcoveAuthProxy( withPublisherId: nil, applicationId: nil ) let fairPlaySessionProvider = sdkManager.createFairPlaySessionProvider( withAuthorizationProxy: fairPlayAuthProxy, upstreamSessionProvider: nil ) // Chain SSAI with FairPlay - FairPlay must be upstream let ssaiSessionProvider = sdkManager.createSSAISessionProviderWithUpstreamSessionProvider( fairPlaySessionProvider ) // Create playback controller with chained providers playbackController = sdkManager.createPlaybackController( withSessionProvider: ssaiSessionProvider, viewStrategy: nil ) playbackController?.delegate = self playbackController?.isAutoPlay = true if let controllerView = playbackController?.view { controllerView.frame = view.bounds view.addSubview(controllerView) } } } ``` -------------------------------- ### Create SSAI Session Provider with Open Measurement (Swift) Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Creates an SSAI session provider with IAB Open Measurement support for ad viewability tracking. The omidPartner string identifies your integration as assigned by IAB Tech Lab. This feature is not available for Mac Catalyst. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class OpenMeasurementVideoPlayerViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() #if !targetEnvironment(macCatalyst) // Create SSAI session provider with Open Measurement // Use your IAB Tech Lab assigned partner name let ssaiSessionProvider = sdkManager.createSSAISessionProviderWithUpstreamSessionProvider( nil, omidPartner: "yourcompanyname" // Assigned by IAB Tech Lab ) playbackController = sdkManager.createPlaybackController( withSessionProvider: ssaiSessionProvider, viewStrategy: nil ) #else // Mac Catalyst fallback without Open Measurement playbackController = sdkManager.createSSAIPlaybackController() #endif playbackController?.delegate = self playbackController?.isAutoPlay = true if let controllerView = playbackController?.view { controllerView.frame = view.bounds view.addSubview(controllerView) } } } ``` -------------------------------- ### Handle SSAI and Playback Errors in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt This Swift code demonstrates how to handle various playback and SSAI-specific errors using the BCOVPlaybackControllerDelegate. It differentiates between general playback errors, SSAI timeline load errors, and missing VMAP sources, offering specific actions for each. Dependencies include BrightcovePlayerSDK and BrightcoveSSAI. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class ErrorHandlingViewController: UIViewController, BCOVPlaybackControllerDelegate { var playbackController: BCOVPlaybackController? @IBOutlet weak var errorLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self } // MARK: - BCOVPlaybackControllerDelegate func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent) { // Handle SSAI-specific errors if lifecycleEvent.eventType == kBCOVSSAILifecycleErrorEvent { if let error = lifecycleEvent.properties[kBCOVSSAILifecycleEventPropertiesKeyError] as? NSError { handleSSAIError(error) } } // Handle general playback errors if lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventError { if let error = lifecycleEvent.properties[kBCOVPlaybackSessionLifecycleEventPropertiesKeyError] as? NSError { handlePlaybackError(error) } } // Handle playback failure if lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventFail { if let error = lifecycleEvent.properties[kBCOVPlaybackSessionLifecycleEventPropertiesKeyError] as? NSError { handlePlaybackFailure(error) } } } func handleSSAIError(_ error: NSError) { guard error.domain == kBCOVSSAIErrorDomain else { return } switch error.code { case kBCOVSSAIErrorCodeTimelineLoadError: print("SSAI Error: Failed to load ad timeline") errorLabel.text = "Ad timeline unavailable" // Continue playback without ads or show error UI case kBCOVSSAIErrorCodeVMAPMissingError: print("SSAI Error: VMAP source not found") errorLabel.text = "Ad configuration missing" // Check video configuration or fallback to non-SSAI playback default: print("SSAI Error: (error.localizedDescription)") errorLabel.text = error.localizedDescription } } func handlePlaybackError(_ error: NSError) { print("Playback error: (error.domain) - (error.code)") print("Description: (error.localizedDescription)") if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? Error { print("Underlying error: (underlyingError)") } // Log to analytics logErrorToAnalytics(error: error) } func handlePlaybackFailure(_ error: NSError) { print("Playback failed: (error.localizedDescription)") // Show user-friendly error DispatchQueue.main.async { [weak self] in self?.errorLabel.text = "Unable to play video. Please try again." self?.errorLabel.isHidden = false } // Attempt recovery or offer retry showRetryOption() } func logErrorToAnalytics(error: NSError) { // Send error to your analytics service let errorInfo: [String: Any] = [ "domain": error.domain, "code": error.code, "description": error.localizedDescription, "timestamp": Date() ] print("Analytics: (errorInfo)") } func showRetryOption() { let alert = UIAlertController( title: "Playback Error", message: "Unable to load video. Would you like to try again?", preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "Retry", style: .default) { [weak self] _ in // Retry loading the video self?.retryLastVideo() }) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) present(alert, animated: true) } func retryLastVideo() { // Implement retry logic } } ``` -------------------------------- ### Implement Seek Without Ads (Resume Playback) in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt This Swift code demonstrates how to implement the 'seek without ads' functionality. It utilizes the `adsDisabled` property and shutter for smooth transitions. The `loadVideoWithResumePosition` function stores the resume time, and the `playbackController` delegate methods handle disabling ads, seeking, re-enabling ads, and resuming playback. ```swift import BrightcovePlayerSDK import BrightcoveSSAI import CoreMedia class ResumePlaybackViewController: UIViewController, BCOVPlaybackControllerDelegate { var playbackController: BCOVPlaybackController? var resumeTime: CMTime = .invalid override func viewDidLoad() { super.viewDidLoad() let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self // Disable autoplay for controlled resume playbackController?.isAutoPlay = false // Enable shutter for clean seek transition playbackController?.shutterFadeTime = 0.0 playbackController?.shutter = true } func loadVideoWithResumePosition(videoId: String, resumeSeconds: Double) { // Store resume position resumeTime = CMTime(seconds: resumeSeconds, preferredTimescale: 600) // Load video (will trigger lifecycle ready event) loadVideo(videoId: videoId) } func loadVideo(videoId: String) { let playbackService = BCOVPlaybackService( withAccountId: "your-account-id", policyKey: "your-policy-key" ) let configuration: [String: Any] = [ BCOVPlaybackService.ConfigurationKeyAssetID: videoId ] let queryParameters: [String: Any] = [ BCOVPlaybackService.ParamaterKeyAdConfigId: "your-ad-config-id" ] playbackService.findVideo( withConfiguration: configuration, queryParameters: queryParameters ) { [weak self] (video, _, error) in if let video = video { self?.playbackController?.setVideos([video]) } } } // MARK: - BCOVPlaybackControllerDelegate func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent) { if lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventReady { // Check if we need to resume if CMTimeCompare(resumeTime, .invalid) != 0 { // Disable ads during seek playbackController?.adsDisabled = true // Seek to resume position session.providerExtension.ssai_seek( to: resumeTime ) { [weak self] finished in guard let self = self else { return } // Re-enable ads after seek self.playbackController?.adsDisabled = false // Clear resume time self.resumeTime = .invalid // Fade out shutter and start playback self.playbackController?.shutterFadeTime = 0.25 self.playbackController?.shutter = false if finished { self.playbackController?.play() } } } else { // No resume - normal playback with shutter fade playbackController?.shutterFadeTime = 0.25 playbackController?.shutter = false playbackController?.play() } } } // Save playback position for later resume func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didProgressTo progress: TimeInterval) { // Save position periodically (e.g., to UserDefaults or server) let videoId = session.video.properties[BCOVVideo.PropertyKeyId] as? String ?? "" UserDefaults.standard.set(progress, forKey: "resume_(videoId)") } } ``` -------------------------------- ### Manage Ad UI with BCOVSSAIAdComponentDisplayContainer in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt Manages ad UI display including countdown banners, learn more buttons, and skip buttons for skippable ads. Companion ad slots can also be configured. This component must be added as a session consumer to the playback controller to function. It requires the BrightcovePlayerSDK and BrightcoveSSAI frameworks. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class AdDisplayViewController: UIViewController, BCOVSSAIAdComponentDelegate { var playbackController: BCOVPlaybackController? var displayContainer: BCOVSSAIAdComponentDisplayContainer? @IBOutlet weak var companionAdView: UIView! override func viewDidLoad() { super.viewDidLoad() // Create companion slot for 300x250 ad let companionSlot = BCOVSSAICompanionSlot( view: companionAdView, width: 300, height: 250 ) // Create display container with companion slots displayContainer = BCOVSSAIAdComponentDisplayContainer( companionSlots: [companionSlot] ) displayContainer?.delegate = self // Setup playback controller let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() playbackController?.delegate = self // Add display container as session consumer if let container = displayContainer { playbackController?.add(container) } } // MARK: - BCOVSSAIAdComponentDelegate func willOpenInAppBrowser(with ad: BCOVAd) { print("Opening in-app browser for ad: (ad.title ?? "Unknown")") // Pause other app activities if needed } func didOpenInAppBrowser(with ad: BCOVAd) { print("In-app browser opened") } func willCloseInAppBrowser(with ad: BCOVAd) { print("Closing in-app browser") } func didCloseInAppBrowser(with ad: BCOVAd) { print("In-app browser closed") // Resume app activities } func willOpenExternalBrowser(with ad: BCOVAd) { print("Opening system browser for ad clickthrough") } } ``` -------------------------------- ### Subscribe to VMAP Response Notifications in Swift Source: https://context7.com/brightcove/brightcove-player-sdk-ios-ssai/llms.txt This Swift code snippet demonstrates how to subscribe to VMAP response notifications using `NotificationCenter`. It shows how to add an observer for the `bcovssaivmapResponseReceived` notification and how to access the raw VMAP data from the notification's user info. This is useful for custom parsing, analytics, or debugging. ```swift import BrightcovePlayerSDK import BrightcoveSSAI class VMAPNotificationViewController: UIViewController { var playbackController: BCOVPlaybackController? override func viewDidLoad() { super.viewDidLoad() // Subscribe to VMAP response notifications NotificationCenter.default.addObserver( self, selector: #selector(vmapResponseReceived(_:)), name: NSNotification.Name.bcovssaivmapResponseReceived, object: nil // nil to receive from all playback controllers ) // Or subscribe for specific playback controller only: // NotificationCenter.default.addObserver( // self, // selector: #selector(vmapResponseReceived(_:)), // name: NSNotification.Name.bcovssaivmapResponseReceived, // object: playbackController // ) let sdkManager = BCOVPlayerSDKManager.sharedManager() playbackController = sdkManager.createSSAIPlaybackController() } deinit { NotificationCenter.default.removeObserver(self) } @objc func vmapResponseReceived(_ notification: Notification) { // Identify which playback controller sent the notification guard let controller = notification.object as? BCOVPlaybackController else { return } print("VMAP received for controller: (controller)") // Access raw VMAP data if let vmapData = notification.userInfo?[kBCOVSSAIVMAPResponseReceivedNotificationDataUserInfoKey] as? Data { print("VMAP data size: (vmapData.count) bytes") // Convert to string for debugging if let vmapString = String(data: vmapData, encoding: .utf8) { print("VMAP XML:\n\(vmapString)") } // Parse VMAP for custom analytics parseVMAPForAnalytics(vmapData) } } func parseVMAPForAnalytics(_ data: Data) { // Custom VMAP parsing logic // Example: Count ad breaks, extract ad IDs, etc. } } ``` -------------------------------- ### Add SSAI Plugin with Swift Package Manager (iOS) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Instructions for adding the Brightcove SSAI plugin to an iOS project using Swift Package Manager. This involves first adding the Core XCFramework and then the SSAI package itself. ```swift https://github.com/brightcove/brightcove-player-sdk-ios-ssai.git ``` -------------------------------- ### Import Brightcove SSAI Plugin (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Swift code snippet demonstrating how to import the Brightcove SSAI plugin into your iOS project. This import statement is necessary to use the plugin's functionalities. ```swift import BrightcoveSSAI ``` -------------------------------- ### Seek to Time While Disabling Ads (Swift) Source: https://github.com/brightcove/brightcove-player-sdk-ios-ssai/blob/master/README.md Illustrates how to seek to a specific time in the video content while temporarily disabling ads. This is achieved by setting the `adsDisabled` property to true before seeking and then re-enabling it in the completion handler. It also shows how to manage the player's shutter for a cleaner transition. ```swift func playbackController(_ controller: BCOVPlaybackController, playbackSession session: BCOVPlaybackSession, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent) { if lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventReady { // disable ads. playbackController.adsDisabled = true // seek somewhere into the video content. session.providerExtension.ssai_seek(to: resumeTime) { [weak self] finished in // re-enable ads. self?.playbackController.adsDisabled = false // open the shutter self?.playbackController.shutterFadeTime = 0.25 self?.playbackController.shutter = false } } } ```