Migrating from AVPlayer to the Bitmovin Player
In this guide, we will show you how to migrate from AVPlayer
to the Bitmovin Player in just two simple steps.
Step 1: Add and configure your Bitmovin Player
You have several options for integrating the Player SDK into your project.
We recommend you use the latest version of our Bitmovin Player. You can find all versions here.
Using CocoaPods
Add the following lines to the Podfile of your project and replace the Version Number
with the latest version of the SDK.
+ pod 'BitmovinPlayer', 'Version Number'
Using Swift Package Manager
To integrate using Xcode 14.1 or newer, open your project file and specify it in Project > Package Dependencies using the following URL:
https://github.com/bitmovin/player-ios.git
Note
You can also add the Player SDK to your project via different ways. Learn how to do that in our Getting Started Guide.
Configure the player license key
Add your player license key to the <dict>
in the Info.plist
:
+ <key>BitmovinPlayerLicenseKey</key>
+ <string>PLAYER_LICENSE_KEY</string>
Alternatively, you can also set the license key programmatically on the PlayerConfig
via PlayerConfig.key
.
Allowlist your Bundle Identifiers
And finally, register the bundle identifier of your application in your Bitmovin Dashboard under Player -> Licenses and Analytics -> Licenses. This security mechanism protects your license from being used elsewhere.
Step 2: Replace AVPlayer references in your project
Replace AVPlayer API
The APIs of AVPlayer
and Bitmovin Player are similar in functionality. As a result, the migration is mostly about changing method names and signatures.
Here is an example of creating a new Bitmovin Player:
- import AVFoundation
+ import BitmovinPlayer
- let player = AVPlayer()
+ let analyticsConfig = AnalyticsConfig(licenseKey: "<ANALYTICS_LICENSE_KEY>")
+ let player = PlayerFactory.createPlayer(analytics: .enabled(analyticsConfig: analyticsConfig))
- #import <AVFoundation/AVFoundation.h>
+ #import <BitmovinPlayerCore/BitmovinPlayerCore.h>
+ #import <BitmovinPlayerAnalytics/BitmovinPlayerAnalytics.h>
+ #import <CoreCollector/CoreCollector-Swift.h>
- AVPlayer *player = [AVPlayer new];
+ BMAAnalyticsConfig *analyticsConfig = [[BMAAnalyticsConfig alloc] initWithLicenseKey:@"<ANALYTICS_LICENSE_KEY>"];
+ id<BMPPlayer> player = [BMPPlayerFactory createPlayerWithAnalyticsConfig:analyticsConfig];
Next, replace occurrences of AVPlayerItem
by SourceConfig
and load your sources. Here is an example of loading an HLS stream:
guard let url = URL(string: "https://cdn.bitmovin.com/content/art-of-motion/m3u8s/playlist.m3u8") else {
return
}
- let playerItem = AVPlayerItem(url: url)
- player.replaceCurrentItem(with: playerItem)
+ let sourceConfig = SourceConfig(url: url, type: .hls)
+ player.load(sourceConfig: sourceConfig)
player.play()
NSURL *streamUrl = [NSURL URLWithString:@"https://cdn.bitmovin.com/content/art-of-motion/m3u8s/playlist.m3u8"];
- AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
- [player replaceCurrentItemWithPlayerItem:playerItem];
+ BMPSourceConfig *sourceConfig = [[BMPSourceConfig alloc] initWithUrl:url type:BMPSourceTypeHls];
+ [player loadSourceConfig: sourceConfig];
[player play];
User Interface
SwiftUI
To migrate the UI used from SwiftUI, replace occurrences of VideoPlayer
with VideoPlayerView
.
- import AVKit
+ import BitmovinPlayer
import SwiftUI
- VideoPlayer(player: player)
+ VideoPlayerView(player: player)
Note
For full snippet for embedding
VideoPlayerView
into a SwiftUI view, see here.
UIKit
To migrate the UI used from UIKit, replace occurrences of AVPlayerViewController
with PlayerView
.
An example:
- import AVKit
+ import BitmovinPlayer
- let playerViewController = AVPlayerViewController()
- playerViewController.player = player
+ let playerView = PlayerView(player: player, frame: .zero)
- #import <AVKit/AVKit.h>
+ #import <BitmovinPlayer/BitmovinPlayer.h>
- AVPlayerViewController *viewController = [AVPlayerViewController new];
- viewController.player = player;
+ BMPPlayerView *playerView = [[BMPPlayerView alloc] initWithPlayer:player frame:CGRectZero];
Note
For full snippet for embedding
PlayerView
into aUIViewController
, see here.
Learn how to customize your cross-platform Bitmovin UI here.
Note
To support you with the further migration, we've created mapping tables between the
AVPlayer
and Bitmovin Player API, which you can refer to - especially for more advanced use cases.
And that's it!
You're ready to play a video in your application using the Bitmovin Player 😀
Summary
In this guide, we demonstrated how easy it is to migrate from AVPlayer
to the Bitmovin Player including Analytics in just two simple steps: adding & configuring the Bitmovin Player and mapping the AVPlayer
API to the Bitmovin Player API.
Next, you can
- check out our Getting Started Guide.
- download fully working examples and explore more features in our GitHub repository.
- choose additional platforms to deploy on in our Getting Started Hub and try our no-code wizards.
- browse our Bitmovin Player API reference.
- try our Analytics product to get real-time insights into your new iOS Player.
- see if some of the questions you might have are answered in our Community and ask your own!
Appendix: Mapping Tables
For more advanced use cases, we've created the following mapping tables to support your migration.
In comparison, the Bitmovin Player API allows more expressive and terse syntax by using event based communication whereas AVPlayer
makes heavy use of Key-Value Observing and Notifications.
AVPlayer ➡️ Bitmovin Player mapping
AVPlayer | Bitmovin Player |
---|---|
AVPlayerItem(url:) | SourceConfig(url:) |
AVPlayer.replaceCurrentItem(with:) | Player.load(sourceConfig:) |
AVPlayer.replaceCurrentItem(with: nil) | Player.unload() |
AVPlayer.currentItem | Player.source |
AVPlayer.currentItem.tracks | Player.availableAudio and Player.availableSubtitles |
AVPlayer.currentItem.duration | Player.duration |
AVPlayer.currentItem.error | PlayerListener.onSourceError and SourceListener.onSourceError |
AVPlayer.currentItem.preferredForwardBufferDuration | Player.buffer.setTargetLevel() |
AVPlayer.currentItem.preferredPeakBitRate | PlayerConfig.adaptationConfig.maxSelectableBitrate |
AVPlayer.status | PlayerListener.onReady |
AVPlayer.error | PlayerListener.onPlayerError |
AVPlayer.timeControlStatus when .playing | PlayerListener.onPlaying |
AVPlayer.timeControlStatus when .paused | PlayerListener.onPaused |
AVPlayer.addPeriodicTimeObserver | PlayerListener.onTimeChanged |
AVQueuePlayer(items:) | Player.load(playlistConfig:) |
AVQueuePlayer.items() | Player.playlist.sources |
AVQueuePlayer.insert(_:, after:) | Player.playlist.add(source: at:) |
AVQueuePlayer.remove(_:) | Player.playlist.remove(atIndex:) |
AVQueuePlayer.removeAllItems() | Player.unload() |
Additional Notes
Seeking
AVPlayer.seek(to time: CMTime)
(seeking in the current source) is equivalent to:
player.seek(time:)
for non-live streamsplayer.timeShift
for live streams
AVQueuePlayer.advanceToNextItem()
(seeking in any source of the playlist) is equivalent to:
player.playlist.seek(source:, time:)
for non-live streams- Live streams are currently not supported in Playlists.
Events
The Bitmovin Player provides an extensive Event Framework. Listening to a Player
event is as simple as:
class EventListener: NSObject, PlayerListener {
func onReady(_ event: ReadyEvent, player: Player) {
// player is ready for immediate playback
}
}
let listener = EventListener()
player.add(listener: listener)
@interface EventListener : NSObject<PlayerListener>
@end
@implementation EventListener
- (void)onReady:(ReadyEvent *)event player:(Player *)player {
// player is ready for immediate playback
}
@end
EventListener *listener = [[EventListener alloc] init];
[player addListener:listener];
Compared to Key-Value Observing based solution with AVPlayer:
class Observer: NSObject {
override public func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
switch keyPath {
case #keyPath(AVPlayer.status):
handlePlayerStatusChanged(change: change)
default:
break
}
}
private func handlePlayerStatusChanged(change: [NSKeyValueChangeKey: Any]?) {
guard let newValue = change?[.newKey] as? Int,
let newStatus = AVPlayer.Status(rawValue: newValue) else {
return
}
switch newStatus {
case .readyToPlay:
// player is ready for immediate playback
default:
break
}
}
}
let observer = Observer()
player.addObserver(observer, forKeyPath: #keyPath(AVPlayer.status), options: [.new], context: nil)
@interface Observer : NSObject
@end
@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"status"]) {
[self handlePlayerStatusChanged:change];
}
}
- (void)handlePlayerStatusChanged:(NSDictionary<NSKeyValueChangeKey,id> *)change {
NSNumber *newValue = change[NSKeyValueChangeNewKey];
AVPlayerStatus newStatus = (AVPlayerStatus)[newValue integerValue];
switch (newStatus) {
case AVPlayerStatusReadyToPlay:
// player is ready for immediate playback
break;
default:
break;
}
}
@end
Observer *observer = [Observer new];
[player addObserver:observer forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
For more advanced use cases, take a look at our API documentation for the PlayerListener
here.
Updated about 2 months ago