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
AMP1 | AMP2 |
---|---|
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: Bool | N/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: String | N/A |
static var domain: String | N/A |
var allowBackgroundPlayback: Bool | playerConfig.playbackConfig.isBackgroundPlaybackEnabled |
var nowPlayingInfoCenterEnabled: Bool | playerConfig.nowPlayingConfig.isNowPlayingInfoEnabled |
var playbackStateOnAppDidBecomeActive: PlaybackState | N/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: Bool | N/A |
var url: String? | sourceConfig.url |
var isAdPlaying: Bool | player.isAd |
var isExternalPlayerPlaying: Bool | N/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: Bool | N/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: Bool | playerConfig.playbackConfig.isAutoplayEnabled |
var isDVREnabled: Bool | playerConfig.playbackConfig.isTimeShiftEnabled |
var streamType: StreamType | sourceConfig.type |
var playbackState: PlaybackState | No direct migration. Alternatively, use PlayerListener.onPlaying , PlayerListener.onPaused , PlayerListener.onStallStarted , PlayerListener.onPlaybackFinished . |
var timedMetadata: [AVMetadataItem] | Use the event PlayerListener.onMetadata |
var overrideDefaultFullscreenBehaviour: Bool | Implement playerView.fullscreenHandler |
var bufferingState: BufferingState | No direct migration. Alternatively use PlayerListener.onStallStarted and PlayerListener.onStallEnded . |
var streamDownloader: StreamDownloader? | No direct migration. Alternatively, use playerConfig.networkConfig.preprocessHttpRequest |
var isStalled: Bool | No direct migration. Alternatively, use PlayerListener.onStallStarted and PlayerListener.onStallEnded . |
var playerView: AmpPlayerView? | No direct migration. Alternatively, use PlayerView or follow the Botmovin Player UI guide to add a customized web interface. |
var isAutoRecoveryEnabled: Bool | N/A |
var currentSystemVolume: Float | player.volume |
var streamAutoRecoveryInterval: Double | N/A |
var isLiveStream: Bool | player.isLive |
var posterImage: PosterImage? | sourceConfig.posterSource |
var fps: Float | player.currentVideoFrameRate |
var streamDuration: Double | player.duration |
var dvrStartTime: Double | player.maxTimeShift |
var dvrLiveTime: Double | N/A. Live time is setting player.timeshift to 0. |
var currentStreamTime: Double | player.currentTime |
var currentUTCTime: Double? | player.currentTime |
var playerSpeed: Float | player.playbackSpeed |
var loadingIndicatorEnabled: Bool | N/A. AMP1 customizable UI is longer supported. |
var audioTracks: [String] | Player.availableAudio |
var closeCaptions: [String] | Player.availableSubtitles |
var airplayEnabled: Bool | player.allowsAirPlay |
var chromecastEnabled: Bool | playerConfig.remoteControlConfig.isCastEnabled |
var logsEnabled: Bool | DebugConfig.logging.logger = ConsoleLogger() // Or nil to disable |
var logColoringEnabled: Bool | N/A |
var fowardBufferDuration: TimeInterval | player.buffer.setTargetLevel(TimeInterval) |
var peakBitRate: Double | playerConfig.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: Bool | N/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: Bool | playerView.isFullscreen |
var playerFrame: CGRect? | playerView.frame |
var fullscreenFrame: CGRect? | playerView.frame |
var isBackgroundSnapshotEnabled: Bool | N/A |
var isPreplaybackCoverEnabled: Bool | N/A |
var isPictureInPictureEnabled: Bool | playerViewConfig.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: Bool | playerView.isPictureInPictureAvailable |
var isSharePlayEnabled: Bool | No direct migration. Follow the Watch content together with SharePlay to implement this feature. |
var sharePlayActivityTitle: String | No 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() -> Bool | No 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) -> Int | player.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
AMP1 | AMP2 |
---|---|
willHandleUrl | onSourceLoad |
willRequestStream | N/A |
onAVPlayerCreation | N/A |
onExternalPlaybackChange | No direct migration. Alternatively, use onAirPlayChanged , onCastStarted , onCastStopped . |
onAmpError | onPlayerError , onSourceError |
onPlaybackStateChanged | No direct migration. Alternatively, use onPlaying , onPaused , onPlaybackFinished , onPlayerError , or onSourceError . |
onBufferingStateChanged | onStallStarted , onStallEnded |
onBitrateChanged | onVideoPlaybackQualityChanged |
onAudioTrackChanged | onAudioChanged |
onPositionChange | onTimeChanged |
onTimeChange | onTimeChanged |
onCuepointReached | onCueEnter |
onPlaybackEnded | onPlaybackFinished |
onReplay | N/A |
onPlaybackStalled | onStallStarted |
onHandledResignActive | N/A |
onHandledDidBecomeActive | N/A |
onHandleAVPlayerAccess | N/A |
onStreamInformationChanged | N/A. AMP1-specific stream information that isn’t a feature, just an information facilitator. You can gather this information through other means. |
willStop | onSourceUnload , onPlayerInactive |
onEnableLogs | N/A |
onDisableLogs | N/A |
onTimedMetadata | onMetadata , onMetadataParsed |
onSeekBegan | onSeek , onTimeShift |
onSeekEnd | onSeeked , onTimeShifted |
onSeek | onSeek , onTimeShift |
onGoLive | onTimeShifted |
onJumpToTime | N/A. Not required and deprecated. |
willCancelPlayback | No direct migration. Alternatively, use onSourceUnload . |
streamReadyToPlay | onReady |
onPlaybackInformationChanged | N/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 |
onAdPeriodStarted | N/A |
onAdPeriodEnded | N/A |
onAdError | onAdError |
onAdBreakStarted | onAdBreakStarted |
onAdBreakEnded | onAdBreakFinished |
onAdRequested | onAdManifestLoaded |
onAdRequestCompleted | onAdScheduled |
onAdStarted | onAdStarted |
onAdEnded | onAdFinished |
onAdResumed | N/A |
onAdPaused | N/A |
onAdSkipped | onAdSkipped |
onAdProceed | No direct migration. Alternatively, use onAdQuartile . |
onMuteStateChange | onMuted , onUnmuted |
onExternalPlaybackStart (iOS) | onCastStarted , onCastPlaying |
onExternalPlaybackEnd (iOS) | onCastStopped |
onReadyToSendMetadata (iOS) | onCastStart , onCastWaitingForDevice |
onUrlDidSet | N/A. Not required and deprecated. |
onExternalAdStarted | onAdStarted |
onExternalAdEnded | onAdFinished |
onVolumeChanged | N/A |
onPlayerItemChanged | onSourceLoaded |
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
AMP1 | AMP2 |
---|---|
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
AMP1 | ANP2 |
---|---|
title | sourceConfig.title = "title" |
isCompleted | offlineContentManager.offlineState |
isPaused | offlineContentManager.offlineState |
isCancelled | offlineContentManager.offlineState |
originUrl | offlineContentManager.sourceConfig.url |
assetLocation | N/A |
asset | offlineContentManager.createOfflineSourceConfig |
progress | OfflineContentManagerListener.onContentDownloadProgressChanged |
deleteVideo() | offlineContentManager.deleteOfflineData() |
isProtected | offlineContentManager.offlineDrmLicenseInformation |
loadFairplayKeys() | offlineContentManager.syncOfflineDrmLicenseInformation |
updatePersistableContentKey(fairplayConfiguration: FairplayConfiguration) | offlineContentManager.renewOfflineLicense() |
AmpDownloaderDelegate mapping
AMP1 | AMP2 |
---|---|
AmpDownloaderDelegate.downloadStarted | No direct migration. Use OfflineContentManagerListener.onContentDownloadProgressChanged instead. |
AmpDownloaderDelegate.downloadPaused | OfflineContentManagerListener.onContentDownloadSuspended |
AmpDownloaderDelegate.downloadResumed | OfflineContentManagerListener.onContentDownloadResumed |
AmpDownloaderDelegate.downloadCompleted | OfflineContentManagerListener.onContentDownloadFinished |
AmpDownloaderDelegate.downloadCancelled | OfflineContentManagerListener.onContentDownloadCanceled |
AmpDownloaderDelegate.downloadDeleted | N/A |
AmpDownloaderDelegate.downloadProgressChanged | OfflineContentManagerListener.onContentDownloadProgressChanged |
AmpDownloaderDelegate.downloadStateChanged | Not required, served the same purpose of downloadCompleted |
AmpDownloaderDelegate.downloadFailed | OfflineContentManagerListener.onOfflineError |
AmpDownloaderDelegate.updatePersistableContentKeyCompleted | OfflineContentManagerListener.onOfflineContentLicenseRenewed |
Updated about 3 hours ago