From e83acb8da52c3f0de39671cc50f20750aa9ad2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaquim=20St=C3=A4hli?= Date: Fri, 1 Nov 2024 16:50:20 +0100 Subject: [PATCH] Add simple api to setup the thumbnail seeking --- .../layouts/thumbnail/ThumbnailViewModel.kt | 58 +------------------ .../pillarbox/player/extension/Tracks.kt | 10 ++++ .../ui/SimpleProgressTrackerState.kt | 2 +- .../ui/SmoothProgressTrackerState.kt | 28 ++++++--- 4 files changed, 32 insertions(+), 66 deletions(-) diff --git a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/thumbnail/ThumbnailViewModel.kt b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/thumbnail/ThumbnailViewModel.kt index 9b22b1e58..2e5466133 100644 --- a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/thumbnail/ThumbnailViewModel.kt +++ b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/thumbnail/ThumbnailViewModel.kt @@ -10,17 +10,13 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import androidx.media3.common.C import androidx.media3.exoplayer.image.ImageOutput import ch.srgssr.pillarbox.core.business.PillarboxExoPlayer import ch.srgssr.pillarbox.core.business.SRGMediaItem import ch.srgssr.pillarbox.demo.shared.data.DemoItem import ch.srgssr.pillarbox.player.PillarboxExoPlayer import ch.srgssr.pillarbox.ui.ProgressTrackerState -import ch.srgssr.pillarbox.ui.SimpleProgressTrackerState -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.StateFlow -import kotlin.time.Duration +import ch.srgssr.pillarbox.ui.SmoothProgressTrackerState /** * A ViewModel to demonstrate how to work with Image track. @@ -42,7 +38,7 @@ class ThumbnailViewModel(application: Application) : AndroidViewModel(applicatio /** * Progress tracker state */ - val progressTrackerState: ProgressTrackerState = ThumbnailProgressTracker(player, viewModelScope, this) + val progressTrackerState: ProgressTrackerState = SmoothProgressTrackerState(player, viewModelScope, this) init { player.prepare() @@ -63,54 +59,4 @@ class ThumbnailViewModel(application: Application) : AndroidViewModel(applicatio override fun onDisabled() { _thumbnail.value = null } - - /** - * Thumbnail progress tracker - * FIXME create a real progress tracker in pillarbox-ui - */ - internal class ThumbnailProgressTracker( - private val player: PillarboxExoPlayer, - coroutineScope: CoroutineScope, - private val imageOutput: ImageOutput, - ) : ProgressTrackerState { - private var storedSeekParameters = player.seekParameters - private var storedPlayWhenReady = player.playWhenReady - private var storedSmoothSeeking = player.smoothSeekingEnabled - private var storedTrackSelectionParameters = player.trackSelectionParameters - private val simpleProgressTrackerState = SimpleProgressTrackerState(player, coroutineScope) - private var startChanging = false - override val progress: StateFlow = simpleProgressTrackerState.progress - - override fun onChanged(progress: Duration) { - simpleProgressTrackerState.onChanged(progress) - if (!startChanging) { - startChanging = true - storedPlayWhenReady = player.playWhenReady - storedSmoothSeeking = player.smoothSeekingEnabled - storedSeekParameters = player.seekParameters - storedTrackSelectionParameters = player.trackSelectionParameters - player.smoothSeekingEnabled = true - player.playWhenReady = false - player.trackSelectionParameters = player.trackSelectionParameters.buildUpon() - .setPreferredVideoRoleFlags(C.ROLE_FLAG_TRICK_PLAY) - .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true) - .setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, true) - .setTrackTypeDisabled(C.TRACK_TYPE_METADATA, true) - .setPrioritizeImageOverVideoEnabled(true) - .build() - player.setImageOutput(imageOutput) - } - player.seekTo(progress.inWholeMilliseconds) - } - - override fun onFinished() { - startChanging = false - simpleProgressTrackerState.onFinished() - player.trackSelectionParameters = storedTrackSelectionParameters - player.smoothSeekingEnabled = storedSmoothSeeking - player.setSeekParameters(storedSeekParameters) - player.playWhenReady = storedPlayWhenReady - player.setImageOutput(null) - } - } } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt index 6d1daad38..1d5c1a059 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt @@ -4,6 +4,7 @@ */ package ch.srgssr.pillarbox.player.extension +import androidx.media3.common.C import androidx.media3.common.Tracks import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange import ch.srgssr.pillarbox.player.source.PillarboxMediaSource @@ -27,3 +28,12 @@ fun Tracks.getBlockedTimeRangeOrNull(): List? { it.type == PillarboxMediaSource.TRACK_TYPE_PILLARBOX_BLOCKED }?.getTrackFormat(0)?.customData as? List } + +/** + * Contains image track + * + * @return true if there is a track of type [C.TRACK_TYPE_IMAGE], false otherwise + */ +fun Tracks.containsImageTrack(): Boolean { + return containsType(C.TRACK_TYPE_IMAGE) +} diff --git a/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SimpleProgressTrackerState.kt b/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SimpleProgressTrackerState.kt index d91fd2637..9767e126c 100644 --- a/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SimpleProgressTrackerState.kt +++ b/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SimpleProgressTrackerState.kt @@ -21,7 +21,7 @@ import kotlin.time.Duration.Companion.milliseconds * [Player] progress tracker that only updated the player's actual progress when [onFinished] is called. * * @param player The [Player] whose current position must be tracked. - * @param coroutineScope + * @param coroutineScope The [CoroutineScope] to state in the current progress. */ class SimpleProgressTrackerState( private val player: Player, diff --git a/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SmoothProgressTrackerState.kt b/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SmoothProgressTrackerState.kt index c0bcbf467..d135d3817 100644 --- a/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SmoothProgressTrackerState.kt +++ b/pillarbox-ui/src/main/java/ch/srgssr/pillarbox/ui/SmoothProgressTrackerState.kt @@ -7,7 +7,9 @@ package ch.srgssr.pillarbox.ui import androidx.media3.common.C import androidx.media3.common.Player import androidx.media3.exoplayer.SeekParameters +import androidx.media3.exoplayer.image.ImageOutput import ch.srgssr.pillarbox.player.PillarboxExoPlayer +import ch.srgssr.pillarbox.player.extension.containsImageTrack import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow import kotlin.time.Duration @@ -16,11 +18,13 @@ import kotlin.time.Duration * [Player] progress tracker that updates the player's actual progress everytime that [onChanged] is called. * * @param player The [Player] whose current position must be tracked. - * @param coroutineScope + * @param coroutineScope The [CoroutineScope] to state in the current progress. + * @param imageOutput The [ImageOutput] to render the image track. */ class SmoothProgressTrackerState( private val player: PillarboxExoPlayer, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, + private val imageOutput: ImageOutput = ImageOutput.NO_OP, ) : ProgressTrackerState { private var storedSeekParameters = player.seekParameters private var storedPlayWhenReady = player.playWhenReady @@ -41,13 +45,18 @@ class SmoothProgressTrackerState( player.setSeekParameters(SeekParameters.CLOSEST_SYNC) player.smoothSeekingEnabled = true player.playWhenReady = false - player.trackSelectionParameters = player.trackSelectionParameters.buildUpon() - .setPreferredVideoRoleFlags(C.ROLE_FLAG_TRICK_PLAY) - .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true) - .setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, true) - .setTrackTypeDisabled(C.TRACK_TYPE_METADATA, true) - .setTrackTypeDisabled(C.TRACK_TYPE_IMAGE, true) - .build() + player.trackSelectionParameters = player.trackSelectionParameters.buildUpon().apply { + setPreferredVideoRoleFlags(C.ROLE_FLAG_TRICK_PLAY) + setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true) + setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, true) + setTrackTypeDisabled(C.TRACK_TYPE_METADATA, true) + if (player.currentTracks.containsImageTrack() && imageOutput != ImageOutput.NO_OP) { + setPrioritizeImageOverVideoEnabled(true) + } else { + setTrackTypeDisabled(C.TRACK_TYPE_IMAGE, true) + } + }.build() + player.setImageOutput(imageOutput) } player.seekTo(progress.inWholeMilliseconds) } @@ -59,5 +68,6 @@ class SmoothProgressTrackerState( player.smoothSeekingEnabled = storedSmoothSeeking player.setSeekParameters(storedSeekParameters) player.playWhenReady = storedPlayWhenReady + player.setImageOutput(null) } }