1
0
Fork 0
mirror of https://gitlab.com/futo-org/fcast.git synced 2025-06-24 21:25:23 +00:00

Fixed playback updates stopping.

This commit is contained in:
Koen 2024-01-06 10:53:59 +01:00
parent 4bfbf8770c
commit f0fa5c067f
2 changed files with 105 additions and 77 deletions

View file

@ -76,29 +76,6 @@ class NetworkService : Service() {
Log.e(TAG, "Failed to send version ${session.id}")
}
}
var encounteredError = false
while (!_stopped && !encounteredError) {
try {
val updateMessage = generateUpdateMessage()
withContext(Dispatchers.IO) {
try {
session.send(Opcode.PlaybackUpdate, updateMessage)
Log.i(TAG, "Update sent ${session.id}")
} catch (eSend: Throwable) {
Log.e(TAG, "Unhandled error sending update ${session.id}", eSend)
encounteredError = true
return@withContext
}
}
} catch (eTimer: Throwable) {
Log.e(TAG, "Unhandled error on timer thread ${session.id}", eTimer)
} finally {
delay(1000)
}
}
Log.i(TAG, "Send loop closed ${session.id}")
}
}
@ -158,33 +135,12 @@ class NetworkService : Service() {
instance = null
}
fun generateUpdateMessage(): PlaybackUpdateMessage {
val player = PlayerActivity.instance
return if (player != null) {
PlaybackUpdateMessage(
System.currentTimeMillis(),
player.currentPosition / 1000.0,
player.duration / 1000.0,
if (player.isPlaying) 1 else 2,
player.speed.toDouble()
)
} else {
PlaybackUpdateMessage(
System.currentTimeMillis(),
0.0,
0.0,
0,
0.0
)
}
}
private inline fun <reified T> send(opcode: Opcode, message: T) {
val sender: (FCastSession) -> Unit = { session: FCastSession ->
_scope?.launch(Dispatchers.IO) {
try {
session.send(opcode, message)
Log.i(TAG, "Playback error sent ${session.id}")
Log.i(TAG, "Opcode sent (opcode = $opcode) ${session.id}")
} catch (e: Throwable) {
Log.w(TAG, "Failed to send playback error", e)
}
@ -196,15 +152,18 @@ class NetworkService : Service() {
}
fun sendPlaybackError(error: String) {
Log.i(TAG, "sendPlaybackError")
val message = PlaybackErrorMessage(error)
send(Opcode.PlaybackError, message)
}
fun sendPlaybackUpdate(message: PlaybackUpdateMessage) {
Log.i(TAG, "sendPlaybackUpdate")
send(Opcode.PlaybackUpdate, message)
}
fun sendCastVolumeUpdate(value: VolumeUpdateMessage) {
Log.i(TAG, "sendCastVolumeUpdate")
send(Opcode.VolumeUpdate, value)
}

View file

@ -19,6 +19,7 @@ import android.widget.TextView
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.PlaybackParameters
@ -33,9 +34,10 @@ import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.ui.PlayerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import java.io.File
@ -52,13 +54,7 @@ class PlayerActivity : AppCompatActivity() {
private lateinit var _exoPlayer: ExoPlayer
private var _shouldPlaybackRestartOnConnectivity: Boolean = false
private lateinit var _connectivityManager: ConnectivityManager
private lateinit var _scope: CoroutineScope
private var _wasPlaying = false
val currentPosition get() = _exoPlayer.currentPosition
val speed get() = _exoPlayer.playbackParameters.speed
val duration get() = _exoPlayer.duration
val isPlaying get() = _exoPlayer.isPlaying
private var _wasPlaying = false
private val _connectivityEvents = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
@ -66,7 +62,7 @@ class PlayerActivity : AppCompatActivity() {
Log.i(TAG, "_connectivityEvents onAvailable")
try {
_scope.launch(Dispatchers.Main) {
lifecycleScope.launch(Dispatchers.Main) {
Log.i(TAG, "onConnectionAvailable")
val pos = _exoPlayer.currentPosition
@ -84,7 +80,7 @@ class PlayerActivity : AppCompatActivity() {
}
}
private val _playerEventListener = object: Player.Listener {
private val _playerEventListener = object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
Log.i(TAG, "onPlaybackStateChanged playbackState=$playbackState")
@ -100,15 +96,17 @@ class PlayerActivity : AppCompatActivity() {
setStatus(true, null)
}
NetworkService.instance?.generateUpdateMessage()?.let {
_scope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendPlaybackUpdate(it)
} catch (e: Throwable) {
Log.e(TAG, "Unhandled error sending playback update", e)
}
}
}
sendPlaybackUpdate()
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
sendPlaybackUpdate()
}
override fun onPositionDiscontinuity(oldPosition: Player.PositionInfo, newPosition: Player.PositionInfo, reason: Int) {
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
sendPlaybackUpdate()
}
override fun onPlayerError(error: PlaybackException) {
@ -134,8 +132,15 @@ class PlayerActivity : AppCompatActivity() {
val fullMessage = getFullExceptionMessage(error)
setStatus(false, fullMessage)
_scope.launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendPlaybackUpdate(PlaybackUpdateMessage(
System.currentTimeMillis(),
0.0,
0.0,
0,
0.0
))
NetworkService.instance?.sendPlaybackError(fullMessage)
} catch (e: Throwable) {
Log.e(TAG, "Unhandled error sending playback error", e)
@ -145,7 +150,7 @@ class PlayerActivity : AppCompatActivity() {
override fun onVolumeChanged(volume: Float) {
super.onVolumeChanged(volume)
_scope.launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendCastVolumeUpdate(VolumeUpdateMessage(System.currentTimeMillis(), volume.toDouble()))
} catch (e: Throwable) {
@ -156,14 +161,55 @@ class PlayerActivity : AppCompatActivity() {
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {
super.onPlaybackParametersChanged(playbackParameters)
NetworkService.instance?.generateUpdateMessage()?.let {
_scope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendPlaybackUpdate(it)
} catch (e: Throwable) {
Log.e(TAG, "Unhandled error sending playback update", e)
}
}
sendPlaybackUpdate()
}
}
private fun sendPlaybackUpdate() {
val state: Int
if (_exoPlayer.playbackState == ExoPlayer.STATE_READY) {
if (_exoPlayer.playWhenReady) {
state = 1
} else {
state = 2
}
} else if (_exoPlayer.playbackState == ExoPlayer.STATE_BUFFERING) {
if (_exoPlayer.playWhenReady) {
state = 1
} else {
state = 2
}
} else {
state = 0
}
val time: Double
val duration: Double
val speed: Double
if (state != 0) {
duration = (_exoPlayer.duration / 1000.0).coerceAtLeast(1.0)
time = (_exoPlayer.currentPosition / 1000.0).coerceAtLeast(0.0).coerceAtMost(duration)
speed = _exoPlayer.playbackParameters.speed.toDouble().coerceAtLeast(0.01)
} else {
time = 0.0
duration = 0.0
speed = 1.0
}
val playbackUpdate = PlaybackUpdateMessage(
System.currentTimeMillis(),
time,
duration,
state,
speed
)
lifecycleScope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendPlaybackUpdate(playbackUpdate)
} catch (e: Throwable) {
Log.e(TAG, "Unhandled error sending playback update", e)
}
}
}
@ -180,7 +226,6 @@ class PlayerActivity : AppCompatActivity() {
_imageSpinner = findViewById(R.id.image_spinner)
_textMessage = findViewById(R.id.text_message)
_layoutOverlay = findViewById(R.id.layout_overlay)
_scope = CoroutineScope(Dispatchers.Main)
setStatus(true, null)
@ -219,6 +264,17 @@ class PlayerActivity : AppCompatActivity() {
instance = this
NetworkService.activityCount++
lifecycleScope.launch(Dispatchers.Main) {
while (lifecycleScope.isActive) {
try {
sendPlaybackUpdate()
delay(1000)
} catch (e: Throwable) {
Log.e(TAG, "Failed to send playback update.", e)
}
}
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
@ -288,12 +344,25 @@ class PlayerActivity : AppCompatActivity() {
Log.i(TAG, "onDestroy")
instance = null
_scope.cancel()
_connectivityManager.unregisterNetworkCallback(_connectivityEvents)
_exoPlayer.removeListener(_playerEventListener)
_exoPlayer.stop()
_playerControlView.player = null
NetworkService.activityCount--
GlobalScope.launch(Dispatchers.IO) {
try {
NetworkService.instance?.sendPlaybackUpdate(PlaybackUpdateMessage(
System.currentTimeMillis(),
0.0,
0.0,
0,
0.0
))
} catch (e: Throwable) {
Log.e(TAG, "Failed to send playback update.", e)
}
}
}
@OptIn(UnstableApi::class)