Migrate iOS SDK

Use this guide to migrate your Player iOS SDK from AMP1 to AMP2.

The guide lists specific player features and functions, their AMP1 implementation, and how you should implement them in AMP2 for successful migration.

Additional reference:

1. Instantiate the player

AMP1 implementation (docs):

AmpPlayer.setApiKey(PLAYER_API_KEY)
ampPlayer = AmpPlayer(parentView: self.view)

AMP2 implementation (docs):

let playerConfig = PlayerConfig()
playerConfig.key = PLAYER_LICENSE_KEY
// Player controller instance
let player = PlayerFactory.createPlayer(
	playerConfig: playerConfig,
	analytics: .enabled(analyticsConfig: analyticsConfig)
)

// Player view instance
let playerView = PlayerView(player: player, frame: .zero)

// Add the playerView to a parent view
playerView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
playerView.frame = view.bounds
view.addSubview(playerView)
view.bringSubviewToFront(playerView)

2. Load an asset

AMP1 implementation (docs):

ampPlayer.play(url: VIDEO_URL)
// OR
if let assetUrl = URL(VIDEO_URL) {
    ampPlayer.play(asset: AVPlayerAsset(assetUrl))
}

AMP2 implementation (doc):

guard let streamUrl = URL(string: VIDEO_URL) else {
	return
}
 
let sourceConfig = SourceConfig(url: streamUrl, type: .hls)
 
let source = SourceFactory.createSource(from: sourceConfig)
 
player.load(source: source)

3. Set up DRM content

AMP1 implementation(docs) :

let configuration = FairplayConfiguration(
    provider: .azure,
    serverURL: DRM_SERVER_URL,
    certificateUrl: DRM_CERTIFICATE_URL)

ampPlayer.play(url: YOUR_DRM_URL, configuration: configuration)

AMP2 implementation (docs) :

let fpsConfig = FairplayConfig(license: LICENSE_URL, certificateURL: CERTIFICATE_URL)

sourceConfig = SourceConfig(url: fairplayStreamUrl, type: .hls)

sourceConfig.drmConfig = fpsConfig

4. Set up iOS configurations

Set up iOS configurations following the mapping table below.

iOS configurations mapping table

AMP1AMP2
AmpPlayer(parentView: view)// For player management PlayerFactory.createPlayer(playerConfig: playerConfig, analytics: .enabled(analyticsConfig: analyticsConfig)) //For rendering the video VideoPlayerView(player: player) // Or PlayerView(player: player, frame: .zero)
var player: AVPlayer?N/A
var enableAdUI: BoolN/A
var streamInfo: StreamInfo?N/A. You can obtain some values through other channels like sourceConfig.url .These values are very specific and merely informational (don't have any effect on playback).
var version: StringN/A
static var domain: StringN/A
var allowBackgroundPlayback: BoolplayerConfig.playbackConfig.isBackgroundPlaybackEnabled
var nowPlayingInfoCenterEnabled: BoolplayerConfig.nowPlayingConfig.isNowPlayingInfoEnabled
var playbackStateOnAppDidBecomeActive: PlaybackStateN/A. AMP1 customizable UI is no longer supported.
var title: String?sourceConfig.title
var subtitle: String?N/A. AMP1 customizable UI is no longer supported.
var observable: Observable<PlayerEventObserver>?N/A. AMP1-specific, for holding listeners. Not intended for customer use.
var isStreamAutoRecoveryEnabled: BoolN/A
var url: String?sourceConfig.url
var isAdPlaying: Boolplayer.isAd
var isExternalPlayerPlaying: BoolN/A. AMP1-specific, used to communicate between packages. Not intended for customer use.
var timeWithoutAds: Double?N/A. AMP1-specific, in path to deprecation.
var hasPostroll: BoolN/A. AMP1-specific, used to communicate between packages. Not intended for customer use.
var media: Media?It is passed to the player as SourceConfig instead.
var autoplay: BoolplayerConfig.playbackConfig.isAutoplayEnabled
var isDVREnabled: BoolplayerConfig.playbackConfig.isTimeShiftEnabled
var streamType: StreamTypesourceConfig.type
var playbackState: PlaybackStateNo direct migration. Alternatively, use PlayerListener.onPlaying, PlayerListener.onPaused, PlayerListener.onStallStarted, PlayerListener.onPlaybackFinished.
var timedMetadata: [AVMetadataItem]Use the event PlayerListener.onMetadata
var overrideDefaultFullscreenBehaviour: BoolImplement playerView.fullscreenHandler
var bufferingState: BufferingStateNo direct migration. Alternatively use PlayerListener.onStallStarted and PlayerListener.onStallEnded.
var streamDownloader: StreamDownloader?No direct migration. Alternatively, use playerConfig.networkConfig.preprocessHttpRequest
var isStalled: BoolNo direct migration. Alternatively, use PlayerListener.onStallStarted and PlayerListener.onStallEnded.
var playerView: AmpPlayerView?No direct migration. Alternatively, use PlayerViewor follow the Botmovin Player UI guide to add a customized web interface.
var isAutoRecoveryEnabled: BoolN/A
var currentSystemVolume: Floatplayer.volume
var streamAutoRecoveryInterval: DoubleN/A
var isLiveStream: Boolplayer.isLive
var posterImage: PosterImage?sourceConfig.posterSource
var fps: Floatplayer.currentVideoFrameRate
var streamDuration: Doubleplayer.duration
var dvrStartTime: Doubleplayer.maxTimeShift
var dvrLiveTime: DoubleN/A. Live time is setting player.timeshift to 0.
var currentStreamTime: Doubleplayer.currentTime
var currentUTCTime: Double?player.currentTime
var playerSpeed: Floatplayer.playbackSpeed
var loadingIndicatorEnabled: BoolN/A. AMP1 customizable UI is longer supported.
var audioTracks: [String]Player.availableAudio
var closeCaptions: [String]Player.availableSubtitles
var airplayEnabled: Boolplayer.allowsAirPlay
var chromecastEnabled: BoolplayerConfig.remoteControlConfig.isCastEnabled
var logsEnabled: BoolDebugConfig.logging.logger = ConsoleLogger() // Or nil to disable
var logColoringEnabled: BoolN/A
var fowardBufferDuration: TimeIntervalplayer.buffer.setTargetLevel(TimeInterval)
var peakBitRate: DoubleplayerConfig.adaptationConfig.maxSelectableBitrate
var loadedTimeRanges: [NSValue]?N/A
var certificateUrl: String?Not required. Follow the DRM setup process.
var serverUrl: String?Not required. Follow the DRM setup process .
var customHeaders: [String:String]?Not required. Follow the DRM setup process .
var fairplayConfiguration: FairplayConfiguration?Not required. Follow the DRM setup process .
var keepMute: BoolN/A.
var inlineCustomView: AmpUIView?N/A. AMP1 customizable UI is no longer supported.
var fullCustomScreenView: AmpUIView?N/A. AMP1 customizable UI is no longer supported.
var parentViewController: UIViewController?N/A
var isFullscreen: BoolplayerView.isFullscreen
var playerFrame: CGRect?playerView.frame
var fullscreenFrame: CGRect?playerView.frame
var isBackgroundSnapshotEnabled: BoolN/A
var isPreplaybackCoverEnabled: BoolN/A
var isPictureInPictureEnabled: BoolplayerViewConfig.PictureInPictureConfig.isEnabled
var pictureDelegate: AVPictureInPictureControllerDelegate?No direct migration. Follow the Picture-in-Picture without using the Bitmovin Web UI to implement this feature.
var pictureController: AVPictureInPictureController?No direct migration. Follow the Picture-in-Picture without using the Bitmovin Web UI to implement this feature.
var pictureInPictureDelegate: AVPictureInPictureControllerDelegate?No direct migration. Follow the Picture-in-Picture without using the Bitmovin Web UI to implement this feature.
var isPictureInPictureSupported: BoolplayerView.isPictureInPictureAvailable
var isSharePlayEnabled: BoolNo direct migration. Follow the Watch content together with SharePlay to implement this feature.
var sharePlayActivityTitle: StringNo direct migration. Follow the Watch content together with SharePlay to implement this feature.
func setLicense(\_ license: String)playerConfig.key
static func setApiKey(\_ apiKey: String)playerConfig.key
func startSharePlay(title: String, url streamUrl: String? = nil)No direct migration. Follow the Watch content together with SharePlay to implement this feature.
func leaveSharePlay()No direct migration. Follow the Watch content together with SharePlay to implement this feature.
func endSharePlay()No direct migration. Follow the Watch content together with SharePlay to implement this feature.
func completeSharePlayDRMInformation(url: String, configuration: FairplayConfiguration)No direct migration. Follow the Watch content together with SharePlay to implement this feature.
func setEventManager(\_ url: String, states: [State_AMP], verificationTimeSeconds: Int)N/A. Internal use only. No need to migrate.
func setPoster(\_ url: String, type: PosterImageType = .poster, mode: UIView.ContentMode? = nil, parentView: UIView? = nil)playerView.setPosterImage(url: URL, keepPersistent: Bool)
func setPoster(\_ image: UIImage, type: PosterImageType = .poster, mode: UIView.ContentMode? = nil, parentView: UIView? = nil)sourceConfig.posterSource
func showPosterImage(type: PosterImageType = .poster)No direct migration. Use sourceConfig.isPosterPersistent to persist the poster since the start of the playback.
func isPlaybackComplete() -> BoolNo direct migration. Alternatively, use PlayerListener.onPlaybackFinished.
func playerItemFailedToPlayToEndTime(\_ notification: Notification)N/A. Internal use only. No need to migrate.
func newErrorLogEntry(\_ notification: Notification)N/A. Internal use only. No need to migrate.
func updatePosition()N/A. Internal use only. No need to migrate.
func changePosition()N/A. Internal use only. No need to migrate.
func setMediaInfo(\_ data: [String: Any])No direct migration. For image, use sourceConfig.posterSource. For title, use sourceConfig.title.
func play()player.play()
func pause()player.pause()
func replay()No direct migration. Use player.seek(time:) instead.
func goLive()player.timeShift
func seekTo(\_ seconds: TimeInterval)player.seek(time:) // VoD // or player.timeShift // Live streams
func seekTo(\_ date: Date)player.timeShift
func stop()player.destroy()
func destroy()player.destroy()
func setMediaCharacteristic(\_ mediaCharacteristic: String, type: MediaCharacteristic)player.setSubtitle(trackIdentifier: String?) // Close captions // or player.setAudio(trackIdentifier: String)
func registerObserver(\_ observer: PlayerEventObserver) -> Intplayer.add(listener: PlayerListener)
func removeObserver(\_ id: Int)player.add(listener: PlayerListener)
func mute()player.mute()
func unmute()player.unmute()
func setMaxQualityLevel(level: QualityLevel)N/A
func manageFullScreen()playerView.enterFullscreen() and playerView.exitFullscreen()
func toggleCloseCaptions(\_ sender: AnyObject)No direct migration. Use player.setSubtitle(trackIdentifier: String?) instead.
func manageCloseCaptions(\_ sender: AnyObject)N/A
func managePictureInPicture()playerView.enterPictureInPicture() and playerView.exitPictureInPicture()
func tap()N/A
func setSpinnerVisibility(shown: Bool)N/A
func setCloseCaption(url: String, forLanguage: String)sourceConfig.add(subtitleTrack: SubtitleTrack)
func removeCloseCaptions()N/A
func setThumbnail(url: String, path: String?)sourceConfig.thumbnailTrack
func setupFullscreen(handler: FullscreenHandler?, autoFullscreen: Bool)N/A

iOS delegate mapping table

AMP1AMP2
willHandleUrlonSourceLoad
willRequestStreamN/A
onAVPlayerCreationN/A
onExternalPlaybackChangeNo direct migration. Alternatively, use onAirPlayChanged, onCastStarted, onCastStopped.
onAmpErroronPlayerError, onSourceError
onPlaybackStateChangedNo direct migration. Alternatively, use onPlaying, onPaused, onPlaybackFinished, onPlayerError, or onSourceError.
onBufferingStateChangedonStallStarted, onStallEnded
onBitrateChangedonVideoPlaybackQualityChanged
onAudioTrackChangedonAudioChanged
onPositionChangeonTimeChanged
onTimeChangeonTimeChanged
onCuepointReachedonCueEnter
onPlaybackEndedonPlaybackFinished
onReplayN/A
onPlaybackStalledonStallStarted
onHandledResignActiveN/A
onHandledDidBecomeActiveN/A
onHandleAVPlayerAccessN/A
onStreamInformationChangedN/A. AMP1-specific stream information that isn’t a feature, just an information facilitator. You can gather this information through other means.
willStoponSourceUnload, onPlayerInactive
onEnableLogsN/A
onDisableLogsN/A
onTimedMetadataonMetadata, onMetadataParsed
onSeekBeganonSeek, onTimeShift
onSeekEndonSeeked, onTimeShifted
onSeekonSeek, onTimeShift
onGoLiveonTimeShifted
onJumpToTimeN/A. Not required and deprecated.
willCancelPlaybackNo direct migration. Alternatively, use onSourceUnload.
streamReadyToPlayonReady
onPlaybackInformationChangedN/A. Not required and deprecated.
onTap (iOS)N/A
onBarShown (iOS)N/A
onHiddenBar (iOS)N/A
onDoubleTap (iOS)N/A
onEnterFullscreen (iOS)N/A
onExitFullscreen (iOS)N/A
onToggleClosedCaption (iOS)No direct migration. Alternatively, use onSubtitleChanged.
willEnterFullscreen (iOS)N/A
willExitFullscreen (iOS)N/A
onRotate (iOS)N/A
onAdPeriodStartedN/A
onAdPeriodEndedN/A
onAdErroronAdError
onAdBreakStartedonAdBreakStarted
onAdBreakEndedonAdBreakFinished
onAdRequestedonAdManifestLoaded
onAdRequestCompletedonAdScheduled
onAdStartedonAdStarted
onAdEndedonAdFinished
onAdResumedN/A
onAdPausedN/A
onAdSkippedonAdSkipped
onAdProceedNo direct migration. Alternatively, use onAdQuartile.
onMuteStateChangeonMuted, onUnmuted
onExternalPlaybackStart (iOS)onCastStarted, onCastPlaying
onExternalPlaybackEnd (iOS)onCastStopped
onReadyToSendMetadata (iOS)onCastStart, onCastWaitingForDevice
onUrlDidSetN/A. Not required and deprecated.
onExternalAdStartedonAdStarted
onExternalAdEndedonAdFinished
onVolumeChangedN/A
onPlayerItemChangedonSourceLoaded

5. Set up IMA ads

AMP1 implementation (docs):

// Framework imports
import AmpIMA
import GoogleInteractiveMediaAds
// Create an instance of the IMA manager
ima = AmpIMAManager(ampPlayer: player, imaHostView: player.playerView!, videoView: player.playerView!)
// Request the ads through the ad tag
ima.requestImaAds(adTagUrl: AD_TAG)

AMP2 implementation (docs):

You don't have to import the Google IMA framework, but add the necessary dependencies to the project.

// Create an ad source
let adSource = AdSource(tag: AD_TAG, ofType: .ima)
// Set up the ad at a position
let preRollAd = AdItem(adSources: [adSource], atPosition: "pre")
// Add the AdItems to the AdvertisingConfig
let adConfig = AdvertisingConfig(schedule: [preRollAd, midRollAd, postRollAd])
// Create a new PlayerConfig containing the advertising config.
let playerConfig = PlayerConfig()
playerConfig.advertisingConfig = adConfig

6. Set up offline playback

AMP1 implementation (docs):

// Framework import wherever you need to call the downloader
import AmpDownloader
// Restore on-going downloads from an app force close
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    AmpDownloadManager.restoreDownloads()
    return true
}
// Start a new download whenever needed, fairplay config only if required
let download = AmpDownloadManager.startNewDownload(title: "TITLE", url: STREAM_URL, fairplayConfiguration: FairplayConfiguration)

AMP2 implementation (docs):

// Initialize the offline manager
OfflineManager.initializeOfflineManager()

// Create the object for dowload
let sourceConfig = SourceConfig(url: URL(string: "STREAM_URL")!)
sourceConfig.title = "TITLE"
sourceConfig.sourceDescription = "DESCRIPTION"

// Add DRM content license
let fpsConfig = FairplayConfig(license: licenseUrl, certificateURL: certificateUrl)  
sourceConfig.drmConfig = fpsConfig
// Get the offline content manager
let offlineManager = OfflineManager.sharedInstance()  
do {  
    let offlineContentManager = try offlineManager.offlineContentManager(for: sourceConfig)  
} catch {  
    // Error 
}

// Start the download
offlineContentManager.download()

AmpDownloader and OfflineManager methods mapping

The tables lists AmpDownloader methods and their Offline Manager counterparts in AMP2.

AmpDownloadManager mapping

AMP1AMP2
AmpDownloadManager.getDownloadList()N/A
AmpDownloadManager.getDownload(playlist:String)N/A
AmpDownloadManager.startNewDownload(title:String, url:String, fairplayConfiguration: FairplayConfiguration? = nil, assetArtworkData:Data? = nil)offlineContentManager?.download()
AmpDownloadManager.resumeDownload(for asset:AmpDownload)offlineContentManager?.resumeDownload()
AmpDownloadManager.pauseDownload(for asset:AmpDownload)offlineContentManager?.pauseDownload()
AmpDownloadManager.deleteDownload(for asset:AmpDownload)offlineContentManager?.deleteOfflineData()
AmpDownloadManager.cancelDownload(for asset:AmpDownload)offlineContentManager?.cancelDownload()
AmpDownloadManager.restoreDownloads()OfflineManager?.initializeOfflineManager()

AmpDownload mapping

AMP1ANP2
titlesourceConfig.title = "title"
isCompletedofflineContentManager.offlineState
isPausedofflineContentManager.offlineState
isCancelledofflineContentManager.offlineState
originUrlofflineContentManager.sourceConfig.url
assetLocationN/A
assetofflineContentManager.createOfflineSourceConfig
progressOfflineContentManagerListener.onContentDownloadProgressChanged
deleteVideo()offlineContentManager.deleteOfflineData()
isProtectedofflineContentManager.offlineDrmLicenseInformation
loadFairplayKeys()offlineContentManager.syncOfflineDrmLicenseInformation
updatePersistableContentKey(fairplayConfiguration: FairplayConfiguration)offlineContentManager.renewOfflineLicense()

AmpDownloaderDelegate mapping

AMP1AMP2
AmpDownloaderDelegate.downloadStartedNo direct migration. Use OfflineContentManagerListener.onContentDownloadProgressChanged instead.
AmpDownloaderDelegate.downloadPausedOfflineContentManagerListener.onContentDownloadSuspended
AmpDownloaderDelegate.downloadResumedOfflineContentManagerListener.onContentDownloadResumed
AmpDownloaderDelegate.downloadCompletedOfflineContentManagerListener.onContentDownloadFinished
AmpDownloaderDelegate.downloadCancelledOfflineContentManagerListener.onContentDownloadCanceled
AmpDownloaderDelegate.downloadDeletedN/A
AmpDownloaderDelegate.downloadProgressChangedOfflineContentManagerListener.onContentDownloadProgressChanged
AmpDownloaderDelegate.downloadStateChangedNot required, served the same purpose of downloadCompleted
AmpDownloaderDelegate.downloadFailedOfflineContentManagerListener.onOfflineError
AmpDownloaderDelegate.updatePersistableContentKeyCompletedOfflineContentManagerListener.onOfflineContentLicenseRenewed