Skip to content

Commit

Permalink
631 metrics collector (#638)
Browse files Browse the repository at this point in the history
Co-authored-by: Gaëtan Muller <[email protected]>
Co-authored-by: Gaëtan Muller <[email protected]>
  • Loading branch information
3 people committed Jul 29, 2024
1 parent b0bc5b2 commit 62b0ec7
Show file tree
Hide file tree
Showing 26 changed files with 842 additions and 619 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener
import ch.srgssr.pillarbox.analytics.commandersact.CommandersAct
import ch.srgssr.pillarbox.analytics.commandersact.MediaEventType
import ch.srgssr.pillarbox.analytics.commandersact.TCMediaEvent
import ch.srgssr.pillarbox.core.business.tracker.TotalPlaytimeCounter
import ch.srgssr.pillarbox.player.analytics.TotalPlaytimeCounter
import ch.srgssr.pillarbox.player.extension.hasAccessibilityRoles
import ch.srgssr.pillarbox.player.extension.isForced
import ch.srgssr.pillarbox.player.tracks.audioTracks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ class CommandersActStreamingTest {
return mockk<ExoPlayer> {
val player = this

every { player.playWhenReady } returns true
every { player.isPlaying } returns isPlaying
every { player.currentPosition } returns currentPosition
every { player.isCurrentMediaItemLive } returns isCurrentMediaItemLive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import ch.srgssr.pillarbox.core.business.utils.LocalMediaCompositionWithFallback
import ch.srgssr.pillarbox.player.test.utils.TestPillarboxRunHelper
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerRepository
import io.mockk.Called
import io.mockk.clearAllMocks
import io.mockk.confirmVerified
import io.mockk.mockk
import io.mockk.slot
Expand Down Expand Up @@ -97,10 +98,9 @@ class CommandersActTrackerIntegrationTest {
@AfterTest
@OptIn(ExperimentalCoroutinesApi::class)
fun tearDown() {
clearAllMocks()
player.release()

shadowOf(Looper.getMainLooper()).idle()

Dispatchers.resetMain()
}

Expand Down Expand Up @@ -591,16 +591,14 @@ class CommandersActTrackerIntegrationTest {
}

@Test
@OptIn(ExperimentalCoroutinesApi::class)
fun `player pause, seeking and pause`() = runTest(testDispatcher) {
fun `player pause, seeking and pause`() {
player.setMediaItem(SRGMediaItemBuilder(URN_NOT_LIVE_VIDEO).build())
player.prepare()
player.playWhenReady = false

TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY)

clock.advanceTime(2.seconds.inWholeMilliseconds)
advanceTimeBy(2.seconds)

TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled(player)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.LoadControl
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
import ch.srgssr.pillarbox.player.analytics.MetricsCollector
import ch.srgssr.pillarbox.player.analytics.PillarboxAnalyticsCollector
import ch.srgssr.pillarbox.player.analytics.PlaybackSessionManager
import ch.srgssr.pillarbox.player.analytics.StallTracker
import ch.srgssr.pillarbox.player.analytics.metrics.MetricsCollector
import ch.srgssr.pillarbox.player.analytics.metrics.PlaybackMetrics
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
Expand All @@ -32,7 +32,6 @@ import ch.srgssr.pillarbox.player.extension.setSeekIncrements
import ch.srgssr.pillarbox.player.qos.DummyQoSHandler
import ch.srgssr.pillarbox.player.qos.PillarboxEventsDispatcher
import ch.srgssr.pillarbox.player.qos.QoSCoordinator
import ch.srgssr.pillarbox.player.qos.StartupTimesTracker
import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory
import ch.srgssr.pillarbox.player.tracker.AnalyticsMediaItemTracker
import ch.srgssr.pillarbox.player.tracker.CurrentMediaItemPillarboxDataTracker
Expand All @@ -46,27 +45,27 @@ import kotlin.coroutines.CoroutineContext
/**
* Pillarbox player
*
* @param context
* @param coroutineContext
* @param exoPlayer
* @param mediaItemTrackerProvider
* @param analyticsCollector
*
* @constructor
* @param context The context.
* @param coroutineContext The [CoroutineContext].
* @param exoPlayer The underlying player.
* @param mediaItemTrackerProvider The [MediaItemTrackerProvider].
* @param analyticsCollector The [PillarboxAnalyticsCollector].
* @param metricsCollector The [MetricsCollector].
*/
class PillarboxExoPlayer internal constructor(
context: Context,
coroutineContext: CoroutineContext,
private val exoPlayer: ExoPlayer,
mediaItemTrackerProvider: MediaItemTrackerProvider,
analyticsCollector: PillarboxAnalyticsCollector,
private val metricsCollector: MetricsCollector = MetricsCollector(),
) : PillarboxPlayer, ExoPlayer by exoPlayer {
private val listeners = ListenerSet<PillarboxPlayer.Listener>(applicationLooper, clock) { listener, flags ->
listener.onEvents(this, Player.Events(flags))
}
private val itemPillarboxDataTracker = CurrentMediaItemPillarboxDataTracker(this)
private val analyticsTracker = AnalyticsMediaItemTracker(this, mediaItemTrackerProvider)
private val sessionManager = PlaybackSessionManager()
internal val sessionManager = PlaybackSessionManager()
private val window = Window()
override var smoothSeekingEnabled: Boolean = false
set(value) {
Expand Down Expand Up @@ -123,12 +122,13 @@ class PillarboxExoPlayer internal constructor(
)

init {
sessionManager.setPlayer(this)
metricsCollector.setPlayer(this)
QoSCoordinator(
context = context,
player = this,
eventsDispatcher = PillarboxEventsDispatcher(sessionManager),
startupTimesTracker = StartupTimesTracker(),
metricsCollector = MetricsCollector(this),
metricsCollector = metricsCollector,
messageHandler = DummyQoSHandler,
sessionManager = sessionManager,
coroutineContext = coroutineContext,
Expand All @@ -141,7 +141,6 @@ class PillarboxExoPlayer internal constructor(
if (BuildConfig.DEBUG) {
addAnalyticsListener(PillarboxEventLogger())
}
addAnalyticsListener(StallTracker())
}

constructor(
Expand Down Expand Up @@ -170,6 +169,7 @@ class PillarboxExoPlayer internal constructor(
clock: Clock,
coroutineContext: CoroutineContext,
analyticsCollector: PillarboxAnalyticsCollector = PillarboxAnalyticsCollector(clock),
metricsCollector: MetricsCollector = MetricsCollector()
) : this(
context,
coroutineContext,
Expand Down Expand Up @@ -197,9 +197,31 @@ class PillarboxExoPlayer internal constructor(
.setDeviceVolumeControlEnabled(true) // allow player to control device volume
.build(),
mediaItemTrackerProvider = mediaItemTrackerProvider,
analyticsCollector = analyticsCollector
analyticsCollector = analyticsCollector,
metricsCollector = metricsCollector,
)

/**
* Get current metrics
* @return `null` if there is no current metrics.
*/
fun getCurrentMetrics(): PlaybackMetrics? {
return metricsCollector.getCurrentMetrics()
}

/**
* Get metrics for item [index]
*
* @param index The index in the timeline.
* @return `null` if there are no metrics.
*/
fun getMetricsFor(index: Int): PlaybackMetrics? {
if (currentTimeline.isEmpty) return null
currentTimeline.getWindow(index, window)
val periodUid = currentTimeline.getUidOfPeriod(window.firstPeriodIndex)
return sessionManager.getSessionFromPeriodUid(periodUid)?.let { metricsCollector.getMetricsForSession(it) }
}

override fun addListener(listener: Player.Listener) {
exoPlayer.addListener(listener)
if (listener is PillarboxPlayer.Listener) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,14 @@ class PlaybackSessionManager {
}

/**
* Register player
* Set the player
*
* @param player
*/
fun registerPlayer(player: ExoPlayer) {
fun setPlayer(player: ExoPlayer) {
player.addAnalyticsListener(analyticsListener)
}

/**
* Unregister player
*
* @param player
*/
fun unregisterPlayer(player: ExoPlayer) {
player.removeAnalyticsListener(analyticsListener)
}

/**
* Add listener
*
Expand Down Expand Up @@ -148,18 +139,24 @@ class PlaybackSessionManager {
/**
* Get session from event time
*
* @param eventTime
* @return
* @param eventTime The [AnalyticsListener.EventTime].
*/
fun getSessionFromEventTime(eventTime: AnalyticsListener.EventTime): Session? {
if (eventTime.timeline.isEmpty) {
return null
}

eventTime.timeline.getWindow(eventTime.windowIndex, window)

val periodUid = eventTime.timeline.getUidOfPeriod(window.firstPeriodIndex)
return getSessionFromPeriodUid(periodUid)
}

/**
* Get session from a period uid
*
* @param periodUid The period uid.
*/
fun getSessionFromPeriodUid(periodUid: Any): Session? {
return sessions[periodUid]
}

Expand Down Expand Up @@ -281,17 +278,16 @@ class PlaybackSessionManager {
override fun onPlayerReleased(eventTime: AnalyticsListener.EventTime) {
DebugLogger.debug(TAG, "onPlayerReleased")
finishAllSessions()
listeners.clear()
}

private fun getOrCreateSession(eventTime: AnalyticsListener.EventTime): Session? {
if (eventTime.timeline.isEmpty) {
return null
}

eventTime.timeline.getWindow(eventTime.windowIndex, window)

val periodUid = eventTime.timeline.getUidOfPeriod(window.firstPeriodIndex)
var session = sessions[periodUid]
var session = getSessionFromPeriodUid(periodUid)
if (session == null) {
val newSession = Session(periodUid, window.mediaItem)
sessions[periodUid] = newSession
Expand Down
Loading

0 comments on commit 62b0ec7

Please sign in to comment.