Product Updates

Gapless Playback and Video Playlists with Bitmovin’s V3 Mobile Player SDKs

David Steinacher
Christian Stoenescu
. 7 min read
- Bitmovin

During the summer, we released V3 of our Player SDKs for Android and iOS, marking a significant milestone in our mission to deliver the best tools to achieve any simple or complex video workflow packaged into a best-in-class developer experience.

In this blog post, we will dive deeper into the more technical aspects of what V3 includes, going in-depth into the major new features like the Playlist API and other improvements made to each SDK.

How V3 came to be

V2 of our Mobile SDKs was designed around the playback of a single source loaded into the Player (e.g., a DASH, HLS, Smooth or Progressive stream). As all of the Player functionality was tied to a single source, it was impossible to load or prepare another source simultaneously. For the Player to playback a different source, the active source needed to be unloaded first, and only then could the new source begin loading.

- Bitmovin

V2 allows only single-source workflows.

The problem with this approach is that there is always some downtime involved when switching sources, during which the viewer will see a black screen until enough data from the new source has buffered. This downtime is a limiting factor when trying to provide an excellent user experience for viewers.

- Bitmovin

Downtime when transitioning from one source to another.

To address this issue, we developed and released V3 – a collection of major improvements to our core APIs as well as the addition of new ones.

Gapless playback

The first improvement I will go into is our Gapless Playback functionality. V3 represents a shift in our architecture whereby the playback Sources and the Player become more decoupled. This decoupling allows sources to have a life cycle and emit events. In addition, each source can be interacted with even when it is not actively playing, which enables the Player to manage multiple sources concurrently – playing the active source while preparing the other sources for playback.

With this upgrade, it is now possible to seamlessly playback multiple sources without a single frame of downtime because data from the following source can buffer while the previous one is still playing.

- Bitmovin

Achieving gapless playback in V3 by loading multiple sources into a player.

Managing multiple sources

With the promotion of Source to a top-level entity comes a collection of powerful APIs that were previously only available via the Player, acting on a single source. Now you can get duration data, query available audio and video qualities, set a specific subtitle track, and much more with each source without it actively playing back.

To enable working with multiple sources in the Player and help facilitate gapless playback, we introduced the Playlist API. It provides the means to dynamically add and remove sources, as well as a way to change the source that is playing.

Together, the new source and playlist APIs offer the power and flexibility to achieve a wide variety of new video experiences.

- Bitmovin

New Source and Playlist APIs are at the heart of V3.

What’s new for the Android SDK?

- Bitmovin

Embracing Kotlin

We’re continuously striving to offer a better developer experience for our users, so it’s only natural that we fully embrace Kotlin, which has become the standard for Android developers worldwide. Our team is a big fan of Kotlin and has been using it internally for a while now, which is why we were adamant about making the full power of Kotlin available for users of our Android SDK. For V3, we developed our APIs looking through a Kotlin lens and will continue to do so for future releases.

The result is a safe, modern, and ergonomic API surface for developers to utilize. In practice, this means that we use nullability information, default parameter values, sealed class hierarchies, extensions, reified functions, function types, and more awesome Kotlin features that improve the developer experience.

By embracing Kotlin, we implemented our new EventEmitter, which improves how to manage event subscriptions, which leads us to our next section.

Improving the way of working with events

The latest mobile SDK comes with a complete rework on handling events. We got rid of over 70 lines of boilerplate code, helping make managing event subscriptions easier and usage more intuitive.

In V2, each event had a separate *Listener class associated with it which had to be used to subscribe to that event. This meant that you first had to find the event you are interested in, then find the associated *Listener class and create an instance of that class to pass it into the EventHandler (the Player in this case).

V3 simplified this workflow. There are no more separate *Listener classes and the event itself is all you need. If you want to subscribe to an event, you just need to pass that event and an action to the new EventEmitter (the Player or Source, in this case). For Kotlin users, the action argument is just a standard Kotlin lambda. In contrast, for Java users, it’s a simple implementation of a new EventListener interface, written as a Java lambda.

player.on(PlayerEvent.Ready::class) { println("Ready") }

// alternatively
player.on<PlayerEvent.Ready> { println(“Ready”) }

All events are now part of a sealed class hierarchy, making it easy to discover and autocomplete all available events in a particular situation. For Kotlin users, we additionally offer reified extension functions that make it even easier to manage event subscriptions by inferring the type of event that an action is associated with.

val onPlayerReady: (PlayerEvent.Ready) -> Unit = { println(“Ready”) }
 
player.on(onPlayerReady)
player.off(onPlayerReady)

There’s also a new API available that allows you to subscribe to just the next occurrence of an event. Subscribing an action will automatically unsubscribe the action after the first occurrence without requiring manual cleanup.

player.next<PlayerEvent.Ready> { println(“Ready”) }

Package restructuring & open-source API

- Bitmovin
Source code of the api package is delivered with the SDK.

Over the years, our public API became loosely spread around different packages, which hurts discoverability. To improve on this, we grouped APIs that belong together into common packages and put all of these common packages into a single api root package, making it easier to discover API objects and how they interact with each other. At the same time, we converted most of them into abstract types or data classes, which should make it easier to mock the SDK in your tests.

With all of the public APIs in one package, we went a step further and now deliver the source code of this package with the SDK. As a result, it is now easy to click through the API within your development environment and see all of our public API code and documentation exactly how we wrote it. Additionally, making the public API open source means that developers have to consult the external documentation on our website less frequently.

New documentation in Kotlin

We’ve completely reworked our documentation to provide a much-improved learning experience on our website, including a landing page with general information, search functionality, and a cleaner design (with dark mode!). All of it is available in Kotlin and can be viewed here

- Bitmovin

New search functionality

- Bitmovin

Better discoverability using the new documentation

What’s new for the iOS SDK?

XCFramework

Starting with V3,  we ship the SDK as an XCFramework. Apple introduced this new bundle format with Xcode 11 to support devices and simulators simultaneously. By shipping it as an XCFramework, we were able to add support for Apple’s first-party package manager, the Swift Package Manager.

Swift Package Manager support

We distribute the latest version of our SDK through Apple’s proprietary swift package manager (SPM). It is the easiest way to integrate our framework into any iOS/tvOS project, and we are excited to offer this convenience to our users.

Improving the way of creating a custom view

Creating a custom native UI (UIView) on V2 was pretty tedious. It was necessary to subclass a specific type and use this as a base for the view. We simplified this in V3, and there is no longer the need to subclass any of our types to create a custom view. We achieved this by introducing a public API on the Player where an AVPlayerLayer or an AVPlayerViewController can be registered.

Any custom UIView subclass with an AVPlayerLayer (or an AVPlayerViewController) can now be used and registered with the player:

// Create a subclass of UIView
class CustomView: UIView {
    init(player: Player, frame: CGRect) {
        super.init(frame: frame)

        // register the AVPlayerLayer of this view to the Player
        player.register(playerLayer)
    }

    var playerLayer: AVPlayerLayer {
        layer as! AVPlayerLayer
    }

    override class var layerClass: AnyClass {
        AVPlayerLayer.self
    }
}

Register the view layer with the Player by calling player.register(playerLayer:). Add the view to your view hierarchy and it will be complete.

Revamping the API for our offline Playback feature

V3 brings the iOS SDK closer to the Android SDK when it comes to using the offline playback feature. We deprecated the old API of the OfflineManager and introduced an OfflineContentManager that can you can use to manage the download process and the downloaded data for each SourceConfig independently.

In addition to this newly designed API, we introduced the same listener-based event communication you are already familiar with from the Player.

View our detailed tutorial about how to use our offline playback feature.

Wrapping it up

Our native Player SDKs for Android and iOS represent an evolution of our core APIs packed with new and exciting features. If you are currently using V2 of our SDKs or want to explore how it is to improve your current deployment, check out our Android or iOS Migration Guides. Additionally, if you want to test out our mobile and any other SDKs, you can sign up for a free 30-day trial, where you will have complete access to our getting started guides, such as these for Android and iOS.

David Steinacher

David Steinacher

Software Engineer | Player iOS

David is a iOS Software Engineer at Bitmovin and one of the primary drivers of improving the SDK. Outside of his professional work at Bitmovin, he is passionate about developing Apps and Libraries for the Apple ecosystems.


Related Posts

- Bitmovin
Product Updates

Bitmovin’s Stream Lab – The Latest Approach to Stream Testing, Support and Playback

- Bitmovin
Product Updates

Streaming Video on the Apple Vision Pro: Supporting visionOS with the Bitmovin Player

Join the conversation