Events Playback
Pre-requisite: The user has to be signed in to perform the following operations.
Events video urls are in m3u8 format which can be streamed over via exoplayer and HlsMediaSource.
Generating an Event Playback Url.
The video URL in the event object may or may not contain an Authorization query param. If the Url contains the query params you are to replace it.
- Start by fetching the Event Tokens
- For the event that you need to play replace the
Autorizationin the video url with the token after matching the bucket names - The Newly constructed url can be played using
HlsMediaSourceand exoplayer. - You will also need to use
DefaultHttpDataSourceto pass authentication to exoplayer.
For Example: If the video url in the event is https://www.example.com/video.m3u8 and the event entity has the bucket name 180-days then we need to look the tokens and look for the bucket named 180-days this object will give us the authentication token so the newly formed url would be https://www.example.cpm/video.m3u8?Authorization=123123123123123
Using Exoplayer and Jetpack Compose
@Composable
private fun Player(event: Event, token: String) {
val exoPlayer = remember {
ExoPlayer
.Builder(context)
.setRenderersFactory(
DefaultRenderersFactory(context).setEnableDecoderFallback(true)
)
.build()
}
LaunchedEffect(event.videoUrl) {
val uri = Uri.parse(event.videoUrl.orEmpty())
val uriBuilder = uri.buildUpon().clearQuery()
for (paramKey in uri.getQueryParameterNames()) {
val value: String =
if (paramKey == "Authorization") token else uri.getQueryParameter(paramKey)
uriBuilder.appendQueryParameter(paramKey, value)
}
exoPlayer.apply {
val dataSourceFactory =
DefaultHttpDataSource.Factory()
.setDefaultRequestProperties(hashMapOf("Authorization" to token))
val mediaSource =
HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(uriBuilder))
setMediaSource(mediaSource)
addListener(object: Player.Listener {
override fun onPlaybackStateChanged(
playerState: Int
) {
// Handle this
}
override fun onTimelineChanged(
timeline: Timeline,
reason: Int
) {
super.onTimelineChanged(timeline, reason)
// Handle This
}
override fun onPlayerError(
error: PlaybackException
) {
// Handle This
}
})
prepare()
seekTo(state.currentPosition)
playWhenReady = true
}
}
DisposableEffect(Unit) {
onDispose {
exoPlayer.release()
}
}
Box(
modifier = Modifier.align(Alignment.Center),
) {
val context = LocalContext.current
AndroidView(
modifier = Modifier,
factory = {
PlayerView(context).apply {
hideController()
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
useController = false
player = exoPlayer
keepScreenOn = true
}
},
)
}
}