The playlist API is based on MediaItem
instances, which can be conveniently built
using MediaItem.Builder
. Inside the player, a MediaItem
is converted into
a playable MediaSource
by a MediaSource.Factory
. Without
custom configuration,
this conversion is carried out by a DefaultMediaSourceFactory
, which is
capable of building complex media sources corresponding to the properties of the
media item. Some of the properties that can be set on media items are outlined
below.
Simple media items
A media item consisting only of the stream URI can be built with the fromUri
convenience method:
Kotlin
val mediaItem = MediaItem.fromUri(videoUri)
Java
MediaItem mediaItem = MediaItem.fromUri(videoUri);
For all other cases, a MediaItem.Builder
can be used. In the following example, a
media item is built with an ID and some attached metadata:
Kotlin
val mediaItem = MediaItem.Builder().setMediaId(mediaId).setTag(myAppData).setUri(videoUri).build()
Java
MediaItem mediaItem = new MediaItem.Builder().setMediaId(mediaId).setTag(myAppData).setUri(videoUri).build();
Attaching metadata can be useful for updating your app's UI when playlist transitions occur.
Images
Playback of images requires a duration in the media item to specify for how long the image should be shown during playback. See the Images guide page for more information on Motion Photos and Image Loading Libraries (for example, Glide).
Kotlin
val mediaItem = MediaItem.Builder().setUri(imageUri).setImageDurationMs(3000).build()
Java
MediaItem mediaItem = new MediaItem.Builder().setUri(imageUri).setImageDurationMs(3_000).build();
Non-standard file extensions for adaptive media
ExoPlayer provides adaptive media sources for DASH, HLS, and SmoothStreaming. If the URI of such an adaptive media item ends with a standard file extension, the corresponding media source is automatically created. If the URI has a non-standard extension or no extension at all, then the MIME type can be set explicitly to indicate the type of the media item:
Kotlin
val mediaItem = MediaItem.Builder().setUri(hlsUri).setMimeType(MimeTypes.APPLICATION_M3U8).build()
Java
MediaItem mediaItem = new MediaItem.Builder().setUri(hlsUri).setMimeType(MimeTypes.APPLICATION_M3U8).build();
For progressive media streams a MIME type is not required.
Protected content
For protected content, the media item's DRM properties should be set. The UUID is required, all the other properties are optional.
An example config for playing an item protected with Widevine DRM where the license URI is not available directly in the media (e.g. in a DASH playlist) and multiple sessions are required (e.g. due to key rotation):
Kotlin
val mediaItem = MediaItem.Builder() .setUri(videoUri) .setDrmConfiguration( MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) .setLicenseUri(licenseUri) .setMultiSession(true) .setLicenseRequestHeaders(httpRequestHeaders) .build() ) .build()
Java
MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setDrmConfiguration( new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) .setLicenseUri(licenseUri) .setMultiSession(true) .setLicenseRequestHeaders(httpRequestHeaders) .build()) .build();
Inside the
player, DefaultMediaSourceFactory
will pass these properties to a
DrmSessionManagerProvider
to obtain a DrmSessionManager
, which is then
injected into the created MediaSource
. DRM behaviour can be
further customized
to your needs.
Sideloading subtitle tracks
To sideload subtitle tracks, MediaItem.Subtitle
instances can be added when
building a media item:
Kotlin
val subtitle = SubtitleConfiguration.Builder(subtitleUri) .setMimeType(mimeType) // The correct MIME type (required). .setLanguage(language) // The subtitle language (optional). .setSelectionFlags(selectionFlags) // Selection flags for the track (optional). .build() val mediaItem = MediaItem.Builder().setUri(videoUri).setSubtitleConfigurations(listOf(subtitle)).build()
Java
MediaItem.SubtitleConfiguration subtitle = new MediaItem.SubtitleConfiguration.Builder(subtitleUri) .setMimeType(mimeType) // The correct MIME type (required). .setLanguage(language) // The subtitle language (optional). .setSelectionFlags(selectionFlags) // Selection flags for the track (optional). .build(); MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setSubtitleConfigurations(ImmutableList.of(subtitle)) .build();
Internally, DefaultMediaSourceFactory
will use a MergingMediaSource
to
combine the content media source with a SingleSampleMediaSource
for each
subtitle track. DefaultMediaSourceFactory
does not support sideloading
subtitles for multi-period DASH.
Clipping a media stream
To clip the content referred to by a media item, set custom start and end positions:
Kotlin
val mediaItem = MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration( MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(startPositionMs) .setEndPositionMs(endPositionMs) .build() ) .build()
Java
MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration( new ClippingConfiguration.Builder() .setStartPositionMs(startPositionMs) .setEndPositionMs(endPositionMs) .build()) .build();
Internally, DefaultMediaSourceFactory
will use a ClippingMediaSource
to wrap
the content media source. There are additional clipping properties. See the
MediaItem.Builder
Javadoc for more details.
Ad insertion
To insert ads, a media item's ad tag URI property should be set:
Kotlin
val mediaItem = MediaItem.Builder() .setUri(videoUri) .setAdsConfiguration(MediaItem.AdsConfiguration.Builder(adTagUri).build())
Java
MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setAdsConfiguration(new MediaItem.AdsConfiguration.Builder(adTagUri).build()) .build();
Internally, DefaultMediaSourceFactory
will wrap the content media source in an
AdsMediaSource
to insert ads as defined by the ad tag. For this to work, the
the player also needs to have its DefaultMediaSourceFactory
configured accordingly.