Voice Broadcast - use internal playback timer instead of relying on the media player position

This commit is contained in:
Florian Renaud 2023-01-25 18:07:16 +01:00
parent af67705778
commit a06104534b

View file

@ -206,7 +206,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
}
}
State.Buffering -> {
val savedPosition = currentVoiceBroadcast?.voiceBroadcastId?.let { playbackTracker.getPlaybackTime(it) }
val savedPosition = currentVoiceBroadcast?.let { playbackTracker.getPlaybackTime(it.voiceBroadcastId) }
when {
// resume playback from the next sequence item
playlist.currentSequence != null -> playlist.getNextItem()?.let { startPlayback(it.startTime) }
@ -226,18 +226,21 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
private fun startPlayback(position: Int) {
stopPlayer()
val playlistItem = playlist.findByPosition(position)
val content = playlistItem?.audioEvent?.content ?: run { Timber.w("## Voice Broadcast | No content to play at position $position"); return }
val playlistItem = playlist.findByPosition(position) ?: run { Timber.w("## Voice Broadcast | No content to play at position $position"); return }
val sequence = playlistItem.sequence ?: run { Timber.w("## Voice Broadcast | Playlist item has no sequence"); return }
val sequencePosition = position - playlistItem.startTime
prepareCurrentPlayerJob = sessionScope.launch {
try {
val mp = prepareMediaPlayer(content)
val mp = prepareMediaPlayer(playlistItem.audioEvent.content)
playlist.currentSequence = sequence - 1 // will be incremented in onNextMediaPlayerStarted
mp.start()
if (sequencePosition > 0) {
mp.seekTo(sequencePosition)
}
currentVoiceBroadcast?.let { playbackTicker.startPlaybackTicker(it.voiceBroadcastId) }
onNextMediaPlayerStarted(mp)
} catch (failure: VoiceBroadcastFailure.ListeningError) {
playingState = State.Error(failure)
@ -259,7 +262,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
playingState = State.Playing
currentMediaPlayer?.start()
} else {
val savedPosition = currentVoiceBroadcast?.voiceBroadcastId?.let { playbackTracker.getPlaybackTime(it) } ?: 0
val savedPosition = currentVoiceBroadcast?.let { playbackTracker.getPlaybackTime(it.voiceBroadcastId) } ?: 0
startPlayback(savedPosition)
}
}
@ -270,6 +273,8 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
playbackTracker.updatePausedAtPlaybackTime(voiceBroadcast.voiceBroadcastId, positionMillis, positionMillis.toFloat() / duration)
}
playingState == State.Playing || playingState == State.Buffering -> {
stopPlayer()
playbackTracker.updatePlayingAtPlaybackTime(voiceBroadcast.voiceBroadcastId, positionMillis, positionMillis.toFloat() / duration)
startPlayback(positionMillis)
}
playingState == State.Idle || playingState == State.Paused -> {
@ -355,6 +360,8 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
private fun stopPlayer() {
tryOrNull { currentMediaPlayer?.stop() }
playbackTicker.stopPlaybackTicker()
currentMediaPlayer?.release()
currentMediaPlayer = null
@ -376,7 +383,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
State.Paused,
State.Buffering,
is State.Error,
State.Idle -> playbackTicker.stopPlaybackTicker(voiceBroadcastId)
State.Idle -> playbackTicker.stopPlaybackTicker()
}
// Notify playback tracker about error
@ -416,22 +423,6 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
prepareNextMediaPlayer()
}
private fun getCurrentPlaybackPosition(): Int? {
val voiceBroadcastId = currentVoiceBroadcast?.voiceBroadcastId ?: return null
val computedPosition = tryOrNull { currentMediaPlayer?.currentPosition }?.let { playlist.currentItem?.startTime?.plus(it) }
val savedPosition = playbackTracker.getPlaybackTime(voiceBroadcastId)
return computedPosition ?: savedPosition
}
private fun getCurrentPlaybackPercentage(): Float? {
val playlistPosition = playlist.currentItem?.startTime
val computedPosition = tryOrNull { currentMediaPlayer?.currentPosition }?.let { playlistPosition?.plus(it) } ?: playlistPosition
val duration = playlist.duration
val computedPercentage = if (computedPosition != null && duration > 0) computedPosition.toFloat() / duration else null
val savedPercentage = currentVoiceBroadcast?.voiceBroadcastId?.let { playbackTracker.getPercentage(it) }
return computedPercentage ?: savedPercentage
}
private inner class MediaPlayerListener :
MediaPlayer.OnInfoListener,
MediaPlayer.OnCompletionListener,
@ -492,39 +483,37 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
initialTime = playbackTracker.getPlaybackTime(id)?.toLong() ?: 0L,
intervalInMs = 50L
).apply {
tickListener = CountUpTimer.TickListener { onPlaybackTick(id) }
tickListener = CountUpTimer.TickListener { onPlaybackTick(id, it.toInt()) }
resume()
}
onPlaybackTick(id)
}
fun stopPlaybackTicker(id: String) {
fun stopPlaybackTicker() {
playbackTicker?.stop()
playbackTicker?.tickListener = null
playbackTicker = null
onPlaybackTick(id)
}
private fun onPlaybackTick(id: String) {
val playbackTime = getCurrentPlaybackPosition()
val percentage = getCurrentPlaybackPercentage()
private fun onPlaybackTick(id: String, position: Int) {
val percentage = tryOrNull { position.toFloat() / playlist.duration }
when (playingState) {
State.Playing -> {
if (playbackTime != null && percentage != null) {
playbackTracker.updatePlayingAtPlaybackTime(id, playbackTime, percentage)
if (percentage != null) {
playbackTracker.updatePlayingAtPlaybackTime(id, position, percentage)
}
}
State.Paused,
State.Buffering -> {
if (playbackTime != null && percentage != null) {
playbackTracker.updatePausedAtPlaybackTime(id, playbackTime, percentage)
if (percentage != null) {
playbackTracker.updatePausedAtPlaybackTime(id, position, percentage)
}
}
State.Idle -> {
// restart the playback time if player completed with less than 250 ms remaining time
if (playbackTime == null || percentage == null || (playlist.duration - playbackTime) < 250) {
// restart the playback time if player completed with less than 1s remaining time
if (percentage == null || (playlist.duration - position) < 1000) {
playbackTracker.stopPlayback(id)
} else {
playbackTracker.updatePausedAtPlaybackTime(id, playbackTime, percentage)
playbackTracker.updatePausedAtPlaybackTime(id, position, percentage)
}
}
is State.Error -> Unit