pillarbox-player
Provides PillarboxPlayer, an AndroidX Media3 Player implementation for media playback on Android.
Integration
To use this module, add the following dependency to your module's build.gradle/build.gradle.kts file:
implementation("ch.srgssr.pillarbox:pillarbox-player:<pillarbox_version>")Getting started
Create the player
val player = PillarboxExoPlayer(context, Default)
// Make the player ready to play content
player.prepare()
// Will start playback when a MediaItem is ready to play
player.play() Playback monitoring
By default, PillarboxExoPlayer does not record any monitoring data. You can configure this behaviour when creating the player:
val player = PillarboxExoPlayer(context, Default) {
    // Disable monitoring recording (default behavior)
    disableMonitoring()
    // Output each monitoring event to Logcat
    monitoring(Logcat)
    // Send each monitoring event to a remote server
    monitoring(Remote) {
        config(endpointUrl = "https://example.com/monitoring")
    }
}Create a MediaItem
val mediaUri = "https://example.com/media.mp4"
val mediaItem = MediaItem.fromUri(mediaUri)
player.setMediaItem(mediaItem)More information about MediaItem creation can be found in the MediaItem documentation.
Display a Player
PillarboxPlayer can be used with the Views provided by AndroidX Media3 without any modifications.
To quickly get started, add the following to your module's build.gradle/build.gradle.kts file:
implementation("androidx.media3:media3-ui:<androidx_media3_version>")Then link your player to a PlayerView:
@Override
fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    val player = PillarboxExoPlayer(context, Default)
    val playerView: PlayerView = findViewById(R.id.player_view)
    // A player can only be attached to one View!
    playerView.player = player
}For more detailed information, you can check AndroidX Media3 UI.
Tip: for integration with Compose, you can use pillarbox-ui.
Release a Player
When the player is not needed anymore, you have to release it. This will free resources allocated by the player.
player.release()Warning: the player can't be used anymore after that.
Custom AssetLoader
AssetLoader is used to load content that doesn't directly have a playable URL, for example, a resource id or a URI. Its responsibility is to provide a MediaSource that:
Is playable by the player;
Contains tracking data;
Provides optional media metadata.
class CustomAssetLoader(context: Context) : AssetLoader(DefaultMediaSourceFactory(context)) {
    override fun canLoadAsset(mediaItem: MediaItem): Boolean {
        return mediaItem.localConfigruation?.uri?.scheme == "custom"
    }
    override suspend fun loadAsset(mediaItem: MediaItem): Asset {
        val data = service.fetchData(mediaItem.localConfigruation!!.uri)
        val trackerData = MutableMediaItemTrackerData()
        trackerData[KEY] = FactoryData(CustomMediaItemTracker.Factory(), CustomTrackerData("CustomData"))
        val mediaMetadata = MediaMetadata.Builder()
            .setTitle(data.title)
            .setArtworkUri(data.imageUri)
            .setChapters(data.chapters)
            .setCredits(data.credits)
            .build()
        val mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(data.url))
        return Asset(
            mediaSource = mediaSource,
            trackersData = trackerData.toMediaItemTrackerData(),
            mediaMetadata = mediaMetadata,
            blockedTimeRanges = emptyList(),
        )
    }
}Now pass your CustomAssetLoader to your player, so it can understand and play your custom data:
val player = PillarboxExoPlayer(context, Default) {
    +CustomAssetLoader(context)
}
player.prepare()
player.setMediaItem(MediaItem.fromUri("custom://video:1234"))
player.play()Chapters
Chapters represent the temporal segmentation of the playing media.
A Chapter can be created like that:
val chapter = Chapter(
    id = "1",
    start = 0L,
    end = 12_000L,
    mediaMetadata = MediaMetadata.Builder().setTitle("Chapter 1").build(),
)PillarboxPlayer provides methods to observe and access chapters:
val player = PillarboxExoPlayer(context, Default)
player.addListener(object : Listener {
    override fun onChapterChanged(chapter: Chapter?) {
        if (chapter == null) {
            // Hide chapter information
        } else {
            // Display chapter information
        }
    }
})
val chapters = player.getCurrentChapters()
val currentChapter = player.getChapterAtPosition()
val chapterAtPosition = player.getChapterAtPosition(10_000L)Chapters can be added to a MediaItem via its metadata:
val mediaMetadata = MediaMetadata.Builder()
    .setChapters(listOf(chapter))
    .build()
val mediaItem = MediaItem.Builder()
    .setMediaMetadata(mediaMetadata)
    .build()Credits
Credits represent a point in the player timeline where opening credits or closing credits should be displayed.
A Credit can be created like that:
val openingCredits = Credit.Opening(start = 5_000L, end = 10_000L)
val closingCredits = Credit.Closing(start = 20_000L, end = 30_000L)PillarboxPlayer provides methods to observe and access credits:
val player = PillarboxExoPlayer(context, Default)
player.addListener(object : Listener {
    override fun onCreditChanged(credit: Credit?) {
        when (credit) {
            is Credit.Opening -> Unit // Show "Skip intro" button
            is Credit.Closing -> Unit // Show "Skip credits" button
            else -> Unot // Hide button
        }
    }
})
val credits = player.getCurrentCredits()
val currentCredit = player.getCreditAtPosition()
val creditAtPosition = player.getCreditAtPosition(5_000L)Chapters can be added to a MediaItem via its metadata:
val mediaMetadata = MediaMetadata.Builder()
    .setCredits(listOf(openingCredits, closingCredits))
    .build()
val mediaItem = MediaItem.Builder()
    .setMediaMetadata(mediaMetadata)
    .build()Known issues
Playing DRM content on two instances of PillarboxPlayer is not supported on all devices.
Known affected devices: Samsung Galaxy A13, Huawei Nova 5i Pro, Huawei P40 Lite.
Related issue: androidx/media#1877.
Further reading
As PillarboxExoPlayer extends from ExoPlayer, all documentation related to ExoPlayer is also valid for Pillarbox. Here are some useful links to get more information about ExoPlayer:
You can check the following pages for a deeper understanding of Pillarbox concepts: