Voice Broadcast - use internal playback timer instead of relying on the media player position
This commit is contained in:
parent
af67705778
commit
a06104534b
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue