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.

+ source 'https://github.com/bitmovin/cocoapod-specs.git'
+ 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 a UIViewController, 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


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

AVPlayerBitmovin Player
AVPlayerItem(url:)SourceConfig(url:)
AVPlayer.replaceCurrentItem(with:)Player.load(sourceConfig:)
AVPlayer.replaceCurrentItem(with: nil)Player.unload()
AVPlayer.currentItemPlayer.source
AVPlayer.currentItem.tracksPlayer.availableAudio and Player.availableSubtitles
AVPlayer.currentItem.durationPlayer.duration
AVPlayer.currentItem.errorPlayerListener.onSourceError and SourceListener.onSourceError
AVPlayer.currentItem.preferredForwardBufferDurationPlayer.buffer.setTargetLevel()
AVPlayer.currentItem.preferredPeakBitRatePlayerConfig.adaptationConfig.maxSelectableBitrate
AVPlayer.statusPlayerListener.onReady
AVPlayer.errorPlayerListener.onPlayerError
AVPlayer.timeControlStatus when .playingPlayerListener.onPlaying
AVPlayer.timeControlStatus when .pausedPlayerListener.onPaused
AVPlayer.addPeriodicTimeObserverPlayerListener.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:

AVQueuePlayer.advanceToNextItem() (seeking in any source of the playlist) is equivalent to:

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.