Provides PillarboxPlayer, an AndroidX Media3 Player implementation for media playback on Android.
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>")
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()
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")
}
}
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.
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.
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.
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 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 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()
- 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.
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: