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:

Packages

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard