Skip to content

Commit

Permalink
Improve PillarboxPreloadManager
Browse files Browse the repository at this point in the history
  • Loading branch information
MGaetan89 committed Oct 1, 2024
1 parent 3f3355c commit 2c1c34d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,36 @@
package ch.srgssr.pillarbox.demo.ui.showcases.layouts

import android.app.Application
import android.os.HandlerThread
import android.os.Process
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.upstream.DefaultAllocator
import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader
import ch.srgssr.pillarbox.demo.shared.data.Playlist
import ch.srgssr.pillarbox.demo.shared.source.BlockedTimeRangeAssetLoader
import ch.srgssr.pillarbox.player.PillarboxExoPlayer
import ch.srgssr.pillarbox.player.PillarboxLoadControl
import ch.srgssr.pillarbox.player.PillarboxPreloadManager
import ch.srgssr.pillarbox.player.PlayerPool
import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

/**
* [ViewModel] that manages multiple [Player]s that can be used in a story-like layout.
*/
class StoryViewModel(application: Application) : AndroidViewModel(application) {
private val playbackThread = HandlerThread("MediaSourceEdge:Playback", Process.THREAD_PRIORITY_AUDIO).apply { start() }
private val preloadLooper = playbackThread.looper
private val loadControl = PillarboxLoadControl(
bufferDurations = PillarboxLoadControl.BufferDurations(
minBufferDuration = 5.seconds,
maxBufferDuration = 20.seconds,
bufferForPlayback = 500.milliseconds,
),
allocator = DefaultAllocator(false, C.DEFAULT_BUFFER_SEGMENT_SIZE),
)
private val preloadManager = PillarboxPreloadManager(
context = application,
mediaSourceFactory = PillarboxMediaSourceFactory(application).apply {
addAssetLoader(SRGAssetLoader(application))
},
playerPool = PlayerPool(
playersCount = 3,
playerFactory = {
PillarboxExoPlayer(
context = application,
mediaSourceFactory = PillarboxMediaSourceFactory(application).apply {
addAssetLoader(SRGAssetLoader(application))
addAssetLoader(BlockedTimeRangeAssetLoader(application))
},
loadControl = loadControl,
playbackLooper = preloadLooper,
).apply {
repeatMode = Player.REPEAT_MODE_ONE
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
prepare()
}
},
),
playerFactory = { playbackLooper ->
PillarboxExoPlayer(
context = application,
playbackLooper = playbackLooper,
).apply {
repeatMode = Player.REPEAT_MODE_ONE
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
prepare()
}
},
)

/**
Expand Down Expand Up @@ -104,6 +78,5 @@ class StoryViewModel(application: Application) : AndroidViewModel(application) {
override fun onCleared() {
super.onCleared()
preloadManager.release()
playbackThread.quit()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
package ch.srgssr.pillarbox.player

import android.content.Context
import android.os.HandlerThread
import android.os.Looper
import android.os.Process
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.DefaultRendererCapabilitiesList
Expand All @@ -18,6 +20,7 @@ import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STA
import androidx.media3.exoplayer.source.preload.TargetPreloadStatusControl
import androidx.media3.exoplayer.trackselection.TrackSelector
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
Expand All @@ -37,45 +40,59 @@ import kotlin.time.Duration.Companion.seconds
* @param bandwidthMeter
* @param rendererCapabilitiesListFactory
* @param loadControl
* @param preloadLooper
* @param playerPool
* @param playbackThread
* @param playersCount
* @param playerFactory
*
* @see DefaultPreloadManager
*/
class PillarboxPreloadManager(
context: Context,
targetPreloadStatusControl: TargetPreloadStatusControl<Int>? = null,
private val mediaSourceFactory: MediaSource.Factory = PillarboxMediaSourceFactory(context),
mediaSourceFactory: MediaSource.Factory = PillarboxMediaSourceFactory(context),
trackSelector: TrackSelector = PillarboxTrackSelector(context).apply {
init({}, PillarboxBandwidthMeter(context))
},
bandwidthMeter: BandwidthMeter = PillarboxBandwidthMeter(context),
rendererCapabilitiesListFactory: RendererCapabilitiesList.Factory = DefaultRendererCapabilitiesList.Factory(
PillarboxRenderersFactory(context)
),
private val loadControl: LoadControl = PillarboxLoadControl(),
preloadLooper: Looper = context.mainLooper,
private val playerPool: PlayerPool = PlayerPool(
playersCount = 3,
playerFactory = {
PillarboxExoPlayer(
context = context,
mediaSourceFactory = mediaSourceFactory,
loadControl = loadControl,
)
},
private val loadControl: LoadControl = PillarboxLoadControl(
bufferDurations = PillarboxLoadControl.BufferDurations(
minBufferDuration = 5.seconds,
maxBufferDuration = 20.seconds,
bufferForPlayback = 500.milliseconds,
),
allocator = DefaultAllocator(false, C.DEFAULT_BUFFER_SEGMENT_SIZE),
),
private val playbackThread: HandlerThread = HandlerThread("PillarboxPreloadManager:Playback", Process.THREAD_PRIORITY_AUDIO),
playersCount: Int = 3,
playerFactory: (playbackLooper: Looper) -> PillarboxExoPlayer = { playbackLooper ->
PillarboxExoPlayer(
context = context,
loadControl = loadControl,
playbackLooper = playbackLooper,
)
},
) {
private val preloadManager = DefaultPreloadManager(
targetPreloadStatusControl ?: DefaultTargetPreloadStatusControl(),
mediaSourceFactory,
trackSelector,
bandwidthMeter,
rendererCapabilitiesListFactory,
loadControl.allocator,
preloadLooper,
private val playerPool = PlayerPool(
playersCount = playersCount,
playerFactory = { playerFactory(playbackThread.looper) },
)

// We use a lazy creation so the playbackThread can be started first
private val preloadManager by lazy {
DefaultPreloadManager(
targetPreloadStatusControl ?: DefaultTargetPreloadStatusControl(),
mediaSourceFactory,
trackSelector,
bandwidthMeter,
rendererCapabilitiesListFactory,
loadControl.allocator,
playbackThread.looper,
)
}

/**
* The index of the currently playing media item.
*
Expand All @@ -95,6 +112,10 @@ class PillarboxPreloadManager(
val sourceCount: Int
get() = preloadManager.sourceCount

init {
playbackThread.start()
}

/**
* Add a [MediaItem] with its [rankingData] to the preload manager.
*
Expand Down Expand Up @@ -146,6 +167,7 @@ class PillarboxPreloadManager(
fun release() {
playerPool.release()
preloadManager.release()
playbackThread.quit()
}

/**
Expand Down Expand Up @@ -200,7 +222,7 @@ class PillarboxPreloadManager(

/**
* Default implementation of [TargetPreloadStatusControl] that will preload the first second of the `n ± 1` item, and the first half-second of
* the `n ± 2` item, where `n` is the index of the current item.
* the `n ± 2,3` item, where `n` is the index of the current item.
*/
@Suppress("MagicNumber")
inner class DefaultTargetPreloadStatusControl : TargetPreloadStatusControl<Int> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ class PillarboxPreloadManagerTest {
createdPlayersCount = 0
preloadManager = PillarboxPreloadManager(
context = context,
playerPool = PlayerPool(
playersCount = PLAYERS_COUNT,
playerFactory = {
createdPlayersCount++
playerFactory = { playbackLooper ->
createdPlayersCount++

PillarboxExoPlayer(context)
}
)
PillarboxExoPlayer(
context = context,
playbackLooper = playbackLooper,
)
},
)
}

Expand Down Expand Up @@ -104,8 +104,6 @@ class PillarboxPreloadManagerTest {
}

private companion object {
private const val PLAYERS_COUNT = 3

private val VOD1 = MediaItem.fromUri("urn:rts:video:13444390")
private val VOD2 = MediaItem.fromUri("urn:rts:video:13444333")
private val VOD3 = MediaItem.fromUri("urn:rts:video:13444466")
Expand Down

0 comments on commit 2c1c34d

Please sign in to comment.