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
1 changed files with 25 additions and 36 deletions

View File

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