diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6c9d761fd..bc470307a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ androidx-core = "1.15.0" androidx-datastore = "1.1.1" androidx-fragment = "1.8.5" androidx-lifecycle = "2.8.7" -androidx-media3 = "1.4.1" +androidx-media3 = "1.5.0-rc01" androidx-navigation = "2.8.3" androidx-paging = "3.3.2" androidx-test-core = "1.6.1" @@ -21,7 +21,7 @@ comscore = "6.11.1" dependency-analysis-gradle-plugin = "2.4.2" detekt = "1.23.7" dokka = "2.0.0-Beta" -guava = "33.0.0-android" +guava = "33.3.1-android" json = "20240303" junit = "4.13.2" kotlin = "2.0.21" diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/commandersact/CommandersActStreaming.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/commandersact/CommandersActStreaming.kt index 19f6bbbd0..1543a9ff6 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/commandersact/CommandersActStreaming.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/commandersact/CommandersActStreaming.kt @@ -202,8 +202,7 @@ internal class CommandersActStreaming( ?: C.LANGUAGE_UNDETERMINED event.audioTrackLanguage = audioTrackLanguage - - event.audioTrackHasAudioDescription = currentAudioTrack?.format?.hasAccessibilityRoles() ?: false + event.audioTrackHasAudioDescription = currentAudioTrack?.format?.hasAccessibilityRoles() == true } override fun onIsPlayingChanged(eventTime: EventTime, isPlaying: Boolean) { diff --git a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/Playlist.kt b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/Playlist.kt index a48d6d844..1f33d0ce0 100644 --- a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/Playlist.kt +++ b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/Playlist.kt @@ -498,9 +498,9 @@ data class Playlist(val title: String, val items: List, val descriptio urn = "urn:srf:video:unknown", description = "Content that does not exist" ), - DemoItem.URN( + DemoItem.URL( title = "Custom MediaSource", - urn = "https://custom-media.ch/fondue", + uri = "https://custom-media.ch/fondue", description = "Using a custom CustomMediaSource" ), BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeAtStartAndEnd, diff --git a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/StoryViewModel.kt b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/StoryViewModel.kt index fcf224389..6d846f994 100644 --- a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/StoryViewModel.kt +++ b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/layouts/StoryViewModel.kt @@ -14,7 +14,7 @@ import androidx.media3.common.MediaItem import androidx.media3.common.Player import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status -import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_TO_POSITION_MS +import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_FOR_DURATION_MS import androidx.media3.exoplayer.source.preload.TargetPreloadStatusControl import ch.srgssr.pillarbox.core.business.PillarboxExoPlayer import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader @@ -36,6 +36,14 @@ class StoryViewModel(application: Application) : AndroidViewModel(application) { private val mediaSourceFactory = PillarboxMediaSourceFactory(application).apply { addAssetLoader(SRGAssetLoader(application)) } + private val loadControl = PillarboxLoadControl( + bufferDurations = PillarboxLoadControl.BufferDurations( + minBufferDuration = 5.seconds, + maxBufferDuration = 20.seconds, + bufferForPlayback = 500.milliseconds, + bufferForPlaybackAfterRebuffer = 1.seconds, + ), + ) private val preloadManager = PillarboxPreloadManager( context = application, targetPreloadStatusControl = StoryPreloadStatusControl(), @@ -44,17 +52,8 @@ class StoryViewModel(application: Application) : AndroidViewModel(application) { parameters = parameters.buildUpon() .setForceLowestBitrate(true) .build() - } - ) - - private val loadControl = PillarboxLoadControl( - bufferDurations = PillarboxLoadControl.BufferDurations( - minBufferDuration = 5.seconds, - maxBufferDuration = 20.seconds, - bufferForPlayback = 500.milliseconds, - bufferForPlaybackAfterRebuffer = 1_000.milliseconds, - ), - allocator = preloadManager.allocator, + }, + loadControl = loadControl, ) private var currentPage = C.INDEX_UNSET @@ -160,8 +159,8 @@ class StoryViewModel(application: Application) : AndroidViewModel(application) { val offset = abs(rankingData - currentPage) return when (offset) { - 1 -> Status(STAGE_LOADED_TO_POSITION_MS, 1.seconds.inWholeMicroseconds) - 2, 3, 4 -> Status(STAGE_LOADED_TO_POSITION_MS, 1.milliseconds.inWholeMicroseconds) + 1 -> Status(STAGE_LOADED_FOR_DURATION_MS, 1.seconds.inWholeMilliseconds) + 2, 3, 4 -> Status(STAGE_LOADED_FOR_DURATION_MS, 1.milliseconds.inWholeMilliseconds) else -> null } } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt index 94bf07516..b96873022 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt @@ -305,8 +305,6 @@ class PillarboxExoPlayer internal constructor( } handleBlockedTimeRange(timeRange) } - - else -> Unit } } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt index d74d1c4ee..e58cfec1b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt @@ -6,9 +6,9 @@ package ch.srgssr.pillarbox.player import androidx.media3.common.C import androidx.media3.common.Timeline +import androidx.media3.common.util.NullableType import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.LoadControl -import androidx.media3.exoplayer.Renderer import androidx.media3.exoplayer.analytics.PlayerId import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.TrackGroupArray @@ -46,6 +46,14 @@ class PillarboxLoadControl( defaultLoadControl.onPrepared(playerId) } + override fun onTracksSelected( + parameters: LoadControl.Parameters, + trackGroups: TrackGroupArray, + trackSelections: Array, + ) { + defaultLoadControl.onTracksSelected(parameters, trackGroups, trackSelections) + } + override fun onStopped(playerId: PlayerId) { defaultLoadControl.onStopped(playerId) } @@ -70,15 +78,12 @@ class PillarboxLoadControl( return defaultLoadControl.shouldContinueLoading(parameters) } - override fun onTracksSelected( - playerId: PlayerId, + override fun shouldContinuePreloading( timeline: Timeline, mediaPeriodId: MediaSource.MediaPeriodId, - renderers: Array, - trackGroups: TrackGroupArray, - trackSelections: Array - ) { - defaultLoadControl.onTracksSelected(playerId, timeline, mediaPeriodId, renderers, trackGroups, trackSelections) + bufferedDurationUs: Long, + ): Boolean { + return defaultLoadControl.shouldContinuePreloading(timeline, mediaPeriodId, bufferedDurationUs) } override fun shouldStartPlayback(parameters: LoadControl.Parameters): Boolean { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt index 3a9ebca57..fae7a0b9f 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt @@ -10,17 +10,15 @@ import android.os.Looper import android.os.Process import androidx.media3.common.C import androidx.media3.common.MediaItem -import androidx.media3.exoplayer.DefaultRendererCapabilitiesList -import androidx.media3.exoplayer.RendererCapabilitiesList +import androidx.media3.exoplayer.LoadControl +import androidx.media3.exoplayer.RenderersFactory import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.preload.DefaultPreloadManager import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status -import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_TO_POSITION_MS +import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_FOR_DURATION_MS import androidx.media3.exoplayer.source.preload.TargetPreloadStatusControl import androidx.media3.exoplayer.trackselection.TrackSelector -import androidx.media3.exoplayer.upstream.Allocator import androidx.media3.exoplayer.upstream.BandwidthMeter -import androidx.media3.exoplayer.upstream.DefaultAllocator import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory import kotlin.math.abs import kotlin.time.Duration.Companion.milliseconds @@ -34,8 +32,8 @@ import kotlin.time.Duration.Companion.seconds * @param mediaSourceFactory The [PillarboxMediaSourceFactory] to create each [MediaSource]. * @param trackSelector The [TrackSelector] for this preload manager. * @param bandwidthMeter The [BandwidthMeter] for this preload manager. - * @param rendererCapabilitiesListFactory The [RendererCapabilitiesList.Factory] for this preload manager. - * @property allocator The [Allocator] for this preload manager. Have to be the same as the one used by the Player. + * @param renderersFactory The [RenderersFactory] for this preload manager. + * @param loadControl The [LoadControl] for this preload manager. * @param playbackThread The [Thread] on which the players run. Its lifecycle is handled internally by [PillarboxPreloadManager]. * * @see DefaultPreloadManager @@ -46,10 +44,8 @@ class PillarboxPreloadManager( mediaSourceFactory: PillarboxMediaSourceFactory = PillarboxMediaSourceFactory(context), trackSelector: TrackSelector = PillarboxTrackSelector(context), bandwidthMeter: BandwidthMeter = PillarboxBandwidthMeter(context), - rendererCapabilitiesListFactory: RendererCapabilitiesList.Factory = DefaultRendererCapabilitiesList.Factory( - PillarboxRenderersFactory(context) - ), - val allocator: DefaultAllocator = DefaultAllocator(false, C.DEFAULT_BUFFER_SEGMENT_SIZE), + renderersFactory: RenderersFactory = PillarboxRenderersFactory(context), + loadControl: LoadControl = PillarboxLoadControl(), private val playbackThread: HandlerThread = HandlerThread("PillarboxPreloadManager:Playback", Process.THREAD_PRIORITY_AUDIO), ) { private val preloadManager: DefaultPreloadManager @@ -82,15 +78,14 @@ class PillarboxPreloadManager( playbackThread.start() playbackLooper = playbackThread.looper trackSelector.init({}, bandwidthMeter) - preloadManager = DefaultPreloadManager( - targetPreloadStatusControl ?: DefaultTargetPreloadStatusControl(), - mediaSourceFactory, - trackSelector, - bandwidthMeter, - rendererCapabilitiesListFactory, - allocator, - playbackLooper, - ) + preloadManager = DefaultPreloadManager.Builder(context, targetPreloadStatusControl ?: DefaultTargetPreloadStatusControl()) + .setMediaSourceFactory(mediaSourceFactory) + .setTrackSelectorFactory { trackSelector } + .setBandwidthMeter(bandwidthMeter) + .setRenderersFactory(renderersFactory) + .setLoadControl(loadControl) + .setPreloadLooper(playbackLooper) + .build() } /** @@ -187,8 +182,8 @@ class PillarboxPreloadManager( val offset = abs(rankingData - currentPlayingIndex) return when (offset) { - 1 -> Status(STAGE_LOADED_TO_POSITION_MS, 1.seconds.inWholeMicroseconds) - 2, 3 -> Status(STAGE_LOADED_TO_POSITION_MS, 500.milliseconds.inWholeMicroseconds) + 1 -> Status(STAGE_LOADED_FOR_DURATION_MS, 1.seconds.inWholeMilliseconds) + 2, 3 -> Status(STAGE_LOADED_FOR_DURATION_MS, 500.milliseconds.inWholeMilliseconds) else -> null } } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/session/PillarboxMediaController.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/session/PillarboxMediaController.kt index 7f939ba2f..de411e0e3 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/session/PillarboxMediaController.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/session/PillarboxMediaController.kt @@ -201,7 +201,7 @@ open class PillarboxMediaController internal constructor() : PillarboxPlayer { */ @get:UnstableApi val customLayout: ImmutableList - get() = mediaController.getCustomLayout() + get() = mediaController.customLayout /** * Session extras @@ -519,31 +519,10 @@ open class PillarboxMediaController internal constructor() : PillarboxPlayer { mediaController.seekForward() } - @UnstableApi - @Deprecated("Use #hasPreviousMediaItem() instead.", ReplaceWith("hasPreviousMediaItem()")) - override fun hasPrevious(): Boolean { - @Suppress("DEPRECATION") - return mediaController.hasPrevious() - } - - @UnstableApi - @Deprecated("Use #hasPreviousMediaItem() instead.", ReplaceWith("hasPreviousMediaItem()")) - override fun hasPreviousWindow(): Boolean { - @Suppress("DEPRECATION") - return mediaController.hasPreviousWindow() - } - override fun hasPreviousMediaItem(): Boolean { return mediaController.hasPreviousMediaItem() } - @UnstableApi - @Deprecated("Use #seekToPreviousMediaItem() instead.", ReplaceWith("seekToPreviousMediaItem()")) - override fun previous() { - @Suppress("DEPRECATION") - mediaController.previous() - } - @UnstableApi @Deprecated("Use #seekToPreviousMediaItem() instead.", ReplaceWith("seekToPreviousMediaItem()")) override fun seekToPreviousWindow() {