I would like to get HLS streaming working using Media3 with a background playback service. I see references online on how to use MediaController setMediaItem
in the activity/composable, however, not sure how to use media source which also uses custom HTTP header. Here is my composable function.
@Composable
fun PlayerTest(
token: String,
url: String,
) {
val context = LocalContext.current
val dataSourceFactory = DataSource.Factory {
val dataSource = DefaultHttpDataSource.Factory().createDataSource()
dataSource.setRequestProperty("Authorization", token)
dataSource
}
val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(url))
val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
DisposableEffect(
Box {
AndroidView(
factory = {
PlayerView(context).apply {
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
useController = false
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
},
update = { playerView ->
controllerFuture.addListener({
val controller = controllerFuture.get()
controller.setMediaSource(hlsMediaSource) // <--- NOT FOUND
playerView.setPlayer(controller)
}, MoreExecutors.directExecutor())
}
)
AudioPlayer(...)
},
) {
onDispose {
MediaController.releaseFuture(controllerFuture)
}
}
}
Also, I don't see options in MediaItem.Builder() that takes media source. I am using Media3 version 1.3.0
. Any suggestions?
The method setMediaSource
is available in ExoPlayer implementation which is created in the service. Or should I let service build the URL and pass setMediaSource instead of doing it in the composable?
It doesn't need to think how media are operated on UI layer because MediaSessionService
can cope with it.
UI layer just connects MediaSessionService
through SessionToken
which builds MediaController
, and you can set it to PlayerView
.
@Composable
fun SampleVideoScreen(viewModel: SampleVideoViewModel) {
val context = LocalContext.current
DisposableEffect(viewModel) {
viewModel.prepare(context)
onDispose { viewModel.dispose() }
}
val controller by viewModel.mediaController.observeAsState()
AndroidView(
factory = {
PlayerView(context)
},
update = { playerView ->
playerView.player = controller
}
)
}
class SampleVideoViewModel: ViewModel() {
private var sessionToken: SessionToken? = null
val mediaController = MutableLiveData<MediaController>()
fun prepare(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
val playIntent = Intent(context, PlaybackService::class.java)
.setAction("playSample")
.putExtra("url", "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8")
context.startService(playIntent)
val component = ComponentName(context, PlaybackService::class.java)
sessionToken = SessionToken(context, component).also { session ->
MediaController.Builder(context, session)
.buildAsync().get().also { controller ->
mediaController.postValue(controller)
}
}
}
}
fun dispose() {
mediaController.value?.clearMediaItems()
mediaController.postValue(null)
}
}