While now we have MediaItem recommended to be used, I was wondering how can we set Http Headers for different Media Items.
Before I created the below helper class which was helping, but now createMediaSource is deprecated.
`object ExoHelper {
fun createMediaSource(
context: Context,
streams: List
): List
streams.map { createMediaSource(context, it) }
fun createMediaSource(
context: Context,
stream: ExoStream
): MediaSource {
val dataSource = buildDataSourceFactory(context, stream.headers)
val factory = buildMediaSourceFactory(dataSource, stream.uri)
return factory.createMediaSource(stream.uri)
}
fun createMergingMediaSource(
context: Context,
streams: List<ExoStream>,
subtitles: List<ExoSubtitle>
): MergingMediaSource {
val sources = arrayListOf<MediaSource>()
streams.forEach {
sources.add(createMediaSource(context, it))
}
subtitles.forEach {
val dataSource = buildDataSourceFactory(context, it.headers)
val subtitleFormat =
Format.createTextSampleFormat(
it.id, // An identifier for the track. May be null.
it.mime, // The mime type. Must be set correctly.
C.SELECTION_FLAG_DEFAULT, // Selection flags for the track.
it.language // The subtitle language. May be null.
)
sources.add(
SingleSampleMediaSource.Factory(dataSource)
.createMediaSource(it.uri, subtitleFormat, C.TIME_UNSET)
)
}
return MergingMediaSource(*sources.toTypedArray())
}
fun retrieveStreamType(uri: Uri): Int =
Util.inferContentType(uri)
private fun buildDataSourceFactory(
context: Context,
headers: Map<String, String>?,
): () -> DefaultDataSource {
return {
val defaultHttpDataSource = DefaultHttpDataSource(USER_AGENT)
headers?.let {
for (h in it)
defaultHttpDataSource.setRequestProperty(h.key, h.value)
}
DefaultDataSource(context, defaultHttpDataSource)
}
}
private fun buildMediaSourceFactory(
factory: DataSource.Factory,
uri: Uri
): MediaSourceFactory {
return when (val type = retrieveStreamType(uri)) {
C.TYPE_SS -> SsMediaSource.Factory(factory)
C.TYPE_DASH -> DashMediaSource.Factory(factory)
C.TYPE_HLS -> HlsMediaSource.Factory(factory)
C.TYPE_OTHER -> ProgressiveMediaSource.Factory(factory)
else -> throw IllegalStateException("Unsupported type: $type");
}
}
}
`
A media item is converted internally to a MediaSource by a MediaSourceFactory. You can inject a customized DefaultMediaSourceFactory that uses your DataSourceFactory.
MediaSourceFactory mediaSourceFactory =
new DefaultMediaSourceFactory(buildDataSourceFactory());
player =
new SimpleExoPlayer.Builder(/* context= */ this)
.setMediaSourceFactory(mediaSourceFactory)
.build();
A media item is converted internally to a
MediaSourceby aMediaSourceFactory. You can inject a customizedDefaultMediaSourceFactorythat uses yourDataSourceFactory.MediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(buildDataSourceFactory()); player = new SimpleExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(mediaSourceFactory) .build();
But this sets headers globally for all MediaItem s.
On my case, each MediaItem has its own headers
There is still an API that supports adding MediaSources directly. It is not deprecated but defined on the ExoPlayer interface (as opposed to the Player interface).
If you want to use the media item based API, you can create your own implementation of the MediaSourceFactory and pass it to the SimpleExoPlayer.Builder. You would roughly move your code from above in that implementation and then return the MediaSource when MediaSource createMediaSource(MediaItem mediaItem) is called:
public static class CustomMediaSourceFactory implements MediaSourceFactory {
// implement the interface methods here (probably most can be no-ops if you wish).
@Override
public MediaSource createMediaSource(MediaItem mediaItem) {
// your code can decide what to do according to the properties of the mediaItem passed in.
return yourMediaSource;
}
}
player =
new SimpleExoPlayer.Builder(/* context= */ this)
.setMediaSourceFactory(customMediaSourceFactory)
.build();
There is still an API that supports adding
MediaSources directly. It is not deprecated but defined on theExoPlayerinterface (as opposed to thePlayerinterface).If you want to use the media item based API, you can create your own implementation of the
MediaSourceFactoryand pass it to theSimpleExoPlayer.Builder. You would roughly move your code from above in that implementation and then return theMediaSourcewhenMediaSource createMediaSource(MediaItem mediaItem)is called:public static class CustomMediaSourceFactory implements MediaSourceFactory { // implement the interface methods here (probably most can be no-ops if you wish). @Override public MediaSource createMediaSource(MediaItem mediaItem) { // your code can decide what to do according to the properties of the mediaItem passed in. return yourMediaSource; } } player = new SimpleExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(customMediaSourceFactory) .build();
@marcbaechinger let me thank you for your support as always. This solution is more clean and flexible for my case.