I'm trying to use Jetpack Compose with ExoPlayer
Problems
- When orientation is changed, ExoPlayer restarts video, but previous playback still exists and plays in the background. For example, if I rotate the phone twice, there are three sound tracks playing, with the latest video in the foreground
-> This was fixed by usingexoPlayer.duration
is alwaysTIME_UNSET
(Long.MIN_VALUE + 1) even whenPlaybackState
changes toPlayer.STATE_READY
exoPlayerState.exoPlayer.duration
.
Versions
Jetpack Compose version: 1.0.4 (latest as of now) ExoPlayer version: 2.15.1 (latest as of now)
Code
Here's my Player Composable and its implementation
class ExoPlayerState(context: Context) {
val exoPlayer = SimpleExoPlayer.Builder(context).build()
val duration by mutableStateOf(exoPlayer.duration)
val bufferedPosition by mutableStateOf(exoPlayer.bufferedPosition)
var position by mutableStateOf(exoPlayer.currentPosition)
}
@Composable
fun Player(
modifier: Modifier = Modifier,
sourceUrl: String
) {
val context = LocalContext.current
val exoPlayerState by remember(context) { mutableStateOf(ExoPlayerState(context)) } // <---- Problem 1?
LaunchedEffect(sourceUrl) {
exoPlayerState.exoPlayer.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_READY -> {
Log.d("Player", "STATE_READY- duration: ${exoPlayerState.duration}") // <----- Problem 2
}
Player.STATE_ENDED -> {}
Player.STATE_BUFFERING, Player.STATE_IDLE -> {}
}
}
})
val mediaSource = generateMediaSource(context, sourceUrl)
exoPlayerState.exoPlayer.setMediaSource(mediaSource)
exoPlayerState.exoPlayer.prepare()
}
AndroidView(factory = {
PlayerView(it).apply {
player = exoPlayerState.exoPlayer
useController = false
(player as SimpleExoPlayer).playWhenReady = true
}
})
// my custom player controller composable
PlayerOverlay(
exoPlayerState = exoPlayerState,
onValueChangeFinished = {
exoPlayerState.exoPlayer.seekTo(exoPlayerState.position)
},
modifier = modifier
)
}
private fun generateMediaSource(context: Context, videoUrl: String): MediaSource {
val mediaItem = MediaItem.Builder()
.setUri(Uri.parse(videoUrl))
.setDrmSessionForClearPeriods(true)
.build()
return DefaultMediaSourceFactory(buildDataSourceFactory(context)).createMediaSource(mediaItem)
}
private fun buildDataSourceFactory(context: Context): DataSource.Factory {
return DefaultDataSourceFactory(
context,
getDefaultHttpDataSourceFactory(context)
)
}
private fun getDefaultHttpDataSourceFactory(context: Context): HttpDataSource.Factory {
return DefaultHttpDataSource.Factory()
.setUserAgent(Util.getUserAgent(context, context.packageName))
}
My Guess
My guess is that I'm now properly saving states of exoplayer and updating it?
I thought creating ExoPlayerState
and remembering it in the composable would properly handle this, but apparently that was not enough.
btw, my custom seekbar logic works fine. It properly seeks the video to the saved position.
onValueChangeFinished = {
exoPlayerState.exoPlayer.seekTo(exoPlayerState.position)
}