Skip to content

Commit be67017

Browse files
StaehliJMGaetan89
andauthored
580 implements custom listeners like media3 (#589)
Co-authored-by: Gaëtan Muller <[email protected]> Co-authored-by: Gaëtan Muller <[email protected]>
1 parent 794bb54 commit be67017

File tree

8 files changed

+311
-48
lines changed

8 files changed

+311
-48
lines changed

pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/SRGEventLoggerTracker.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ package ch.srgssr.pillarbox.core.business.tracker
66

77
import android.util.Log
88
import androidx.media3.exoplayer.ExoPlayer
9-
import androidx.media3.exoplayer.util.EventLogger
109
import ch.srgssr.pillarbox.player.tracker.MediaItemTracker
10+
import ch.srgssr.pillarbox.player.utils.PillarboxEventLogger
1111
import kotlin.time.Duration.Companion.milliseconds
1212

1313
/**
1414
* Enable/Disable EventLogger when item is currently active.
1515
*/
1616
class SRGEventLoggerTracker : MediaItemTracker {
17-
private val eventLogger = EventLogger(TAG)
17+
private val eventLogger = PillarboxEventLogger(TAG)
1818

1919
override fun start(player: ExoPlayer, initialData: Any?) {
2020
Log.w(TAG, "---- Start")

pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ import androidx.media3.common.Player
1313
import androidx.media3.common.Timeline.Window
1414
import androidx.media3.common.TrackSelectionParameters
1515
import androidx.media3.common.util.Clock
16+
import androidx.media3.common.util.ListenerSet
1617
import androidx.media3.exoplayer.DefaultRenderersFactory
1718
import androidx.media3.exoplayer.ExoPlayer
1819
import androidx.media3.exoplayer.LoadControl
1920
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
2021
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
21-
import androidx.media3.exoplayer.util.EventLogger
22+
import ch.srgssr.pillarbox.player.analytics.PillarboxAnalyticsCollector
2223
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
2324
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
2425
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
@@ -31,20 +32,25 @@ import ch.srgssr.pillarbox.player.tracker.CurrentMediaItemPillarboxDataTracker
3132
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerProvider
3233
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerRepository
3334
import ch.srgssr.pillarbox.player.tracker.TimeRangeTracker
35+
import ch.srgssr.pillarbox.player.utils.PillarboxEventLogger
3436

3537
/**
3638
* Pillarbox player
3739
*
3840
* @param exoPlayer
3941
* @param mediaItemTrackerProvider
42+
* @param analyticsCollector
4043
*
4144
* @constructor
4245
*/
4346
class PillarboxExoPlayer internal constructor(
4447
private val exoPlayer: ExoPlayer,
45-
mediaItemTrackerProvider: MediaItemTrackerProvider
48+
mediaItemTrackerProvider: MediaItemTrackerProvider,
49+
analyticsCollector: PillarboxAnalyticsCollector,
4650
) : PillarboxPlayer, ExoPlayer by exoPlayer {
47-
private val listeners = HashSet<PillarboxPlayer.Listener>()
51+
private val listeners = ListenerSet<PillarboxPlayer.Listener>(applicationLooper, clock) { listener, flags ->
52+
listener.onEvents(this, Player.Events(flags))
53+
}
4854
private val itemPillarboxDataTracker = CurrentMediaItemPillarboxDataTracker(this)
4955
private val analyticsTracker = AnalyticsMediaItemTracker(this, mediaItemTrackerProvider)
5056
private val window = Window()
@@ -56,9 +62,8 @@ class PillarboxExoPlayer internal constructor(
5662
seekEnd()
5763
}
5864
clearSeeking()
59-
val listeners = HashSet(listeners)
60-
listeners.forEach {
61-
it.onSmoothSeekingEnabledChanged(value)
65+
listeners.sendEvent(PillarboxPlayer.EVENT_SMOOTH_SEEKING_ENABLED_CHANGED) { listener ->
66+
listener.onSmoothSeekingEnabledChanged(value)
6267
}
6368
}
6469
}
@@ -72,9 +77,8 @@ class PillarboxExoPlayer internal constructor(
7277
set(value) {
7378
if (analyticsTracker.enabled != value) {
7479
analyticsTracker.enabled = value
75-
val listeners = HashSet(listeners)
76-
listeners.forEach {
77-
it.onTrackingEnabledChanged(value)
80+
listeners.sendEvent(PillarboxPlayer.EVENT_TRACKING_ENABLED_CHANGED) { listener ->
81+
listener.onTrackingEnabledChanged(value)
7882
}
7983
}
8084
}
@@ -84,26 +88,33 @@ class PillarboxExoPlayer internal constructor(
8488
this,
8589
object : TimeRangeTracker.Callback {
8690
override fun onBlockedTimeRange(blockedTimeRange: BlockedTimeRange) {
87-
notifyBlockedTimeRangeReached(blockedTimeRange)
91+
listeners.sendEvent(PillarboxPlayer.EVENT_BLOCKED_TIME_RANGE_REACHED) { listener ->
92+
listener.onBlockedTimeRangeReached(blockedTimeRange)
93+
}
8894
handleBlockedTimeRange(blockedTimeRange)
8995
}
9096

9197
override fun onChapterChanged(chapter: Chapter?) {
92-
notifyChapterChanged(chapter)
98+
listeners.sendEvent(PillarboxPlayer.EVENT_CHAPTER_CHANGED) { listener ->
99+
listener.onChapterChanged(chapter)
100+
}
93101
}
94102

95103
override fun onCreditChanged(credit: Credit?) {
96-
notifyCreditChanged(credit)
104+
listeners.sendEvent(PillarboxPlayer.EVENT_CREDIT_CHANGED) { listener ->
105+
listener.onCreditChanged(credit)
106+
}
97107
}
98108
}
99109
)
100110

101111
init {
112+
addListener(analyticsCollector)
102113
exoPlayer.addListener(ComponentListener())
103114
itemPillarboxDataTracker.addCallback(timeRangeTracker)
104115
itemPillarboxDataTracker.addCallback(analyticsTracker)
105116
if (BuildConfig.DEBUG) {
106-
addAnalyticsListener(EventLogger())
117+
addAnalyticsListener(PillarboxEventLogger())
107118
}
108119
}
109120

@@ -130,6 +141,7 @@ class PillarboxExoPlayer internal constructor(
130141
mediaItemTrackerProvider: MediaItemTrackerProvider = MediaItemTrackerRepository(),
131142
seekIncrement: SeekIncrement = SeekIncrement(),
132143
clock: Clock,
144+
analyticsCollector: PillarboxAnalyticsCollector = PillarboxAnalyticsCollector(clock),
133145
) : this(
134146
ExoPlayer.Builder(context)
135147
.setClock(clock)
@@ -151,9 +163,11 @@ class PillarboxExoPlayer internal constructor(
151163
.build()
152164
)
153165
)
166+
.setAnalyticsCollector(analyticsCollector)
154167
.setDeviceVolumeControlEnabled(true) // allow player to control device volume
155168
.build(),
156-
mediaItemTrackerProvider = mediaItemTrackerProvider
169+
mediaItemTrackerProvider = mediaItemTrackerProvider,
170+
analyticsCollector = analyticsCollector
157171
)
158172

159173
override fun addListener(listener: Player.Listener) {
@@ -170,24 +184,6 @@ class PillarboxExoPlayer internal constructor(
170184
}
171185
}
172186

173-
private fun notifyChapterChanged(chapter: Chapter?) {
174-
HashSet(listeners).forEach {
175-
it.onChapterChanged(chapter)
176-
}
177-
}
178-
179-
private fun notifyBlockedTimeRangeReached(blockedTimeRange: BlockedTimeRange) {
180-
HashSet(listeners).forEach {
181-
it.onBlockedTimeRangeReached(blockedTimeRange)
182-
}
183-
}
184-
185-
private fun notifyCreditChanged(timeRange: Credit?) {
186-
HashSet(listeners).forEach {
187-
it.onCreditChanged(timeRange)
188-
}
189-
}
190-
191187
override fun setMediaItem(mediaItem: MediaItem) {
192188
exoPlayer.setMediaItem(mediaItem.clearTag())
193189
}
@@ -330,6 +326,7 @@ class PillarboxExoPlayer internal constructor(
330326
if (playbackState != Player.STATE_IDLE) {
331327
stop()
332328
}
329+
listeners.release()
333330
exoPlayer.release()
334331
}
335332

pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface PillarboxPlayer : Player {
1919
* Listener
2020
*/
2121
interface Listener : Player.Listener {
22+
2223
/**
2324
* On smooth seeking enabled changed
2425
*
@@ -76,4 +77,32 @@ interface PillarboxPlayer : Player {
7677
* Enable or disable MediaItem tracking
7778
*/
7879
var trackingEnabled: Boolean
80+
81+
companion object {
82+
83+
/**
84+
* Event Blocked Time Range Reached.
85+
*/
86+
const val EVENT_BLOCKED_TIME_RANGE_REACHED = 100
87+
88+
/**
89+
* The current [Chapter] has changed.
90+
*/
91+
const val EVENT_CHAPTER_CHANGED = 101
92+
93+
/**
94+
* The current [Credit] Changed.
95+
*/
96+
const val EVENT_CREDIT_CHANGED = 102
97+
98+
/**
99+
* [trackingEnabled] has changed.
100+
*/
101+
const val EVENT_TRACKING_ENABLED_CHANGED = 103
102+
103+
/**
104+
* [smoothSeekingEnabled] has changed.
105+
*/
106+
const val EVENT_SMOOTH_SEEKING_ENABLED_CHANGED = 104
107+
}
79108
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) SRG SSR. All rights reserved.
3+
* License information is available from the LICENSE file.
4+
*/
5+
package ch.srgssr.pillarbox.player.analytics
6+
7+
import androidx.media3.common.util.Clock
8+
import androidx.media3.common.util.ListenerSet.Event
9+
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime
10+
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector
11+
import ch.srgssr.pillarbox.player.PillarboxPlayer
12+
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
13+
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
14+
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
15+
16+
/**
17+
* Pillarbox analytics collector
18+
*
19+
* @constructor
20+
*
21+
* @param clock The [Clock] used to generate timestamps.
22+
*/
23+
class PillarboxAnalyticsCollector(
24+
clock: Clock = Clock.DEFAULT
25+
) : DefaultAnalyticsCollector(clock), PillarboxPlayer.Listener {
26+
27+
override fun onSmoothSeekingEnabledChanged(smoothSeekingEnabled: Boolean) {
28+
val eventTime = generateCurrentPlayerMediaPeriodEventTime()
29+
30+
sendEventPillarbox(
31+
eventTime, PillarboxAnalyticsListener.EVENT_SMOOTH_SEEKING_ENABLED_CHANGED
32+
) { listener -> listener.onSmoothSeekingEnabledChanged(eventTime, smoothSeekingEnabled) }
33+
}
34+
35+
override fun onTrackingEnabledChanged(trackingEnabled: Boolean) {
36+
val eventTime = generateCurrentPlayerMediaPeriodEventTime()
37+
38+
sendEventPillarbox(
39+
eventTime, PillarboxAnalyticsListener.EVENT_TRACKING_ENABLED_CHANGED
40+
) { listener -> listener.onTrackingEnabledChanged(eventTime, trackingEnabled) }
41+
}
42+
43+
override fun onChapterChanged(chapter: Chapter?) {
44+
val eventTime = generateCurrentPlayerMediaPeriodEventTime()
45+
46+
sendEventPillarbox(
47+
eventTime, PillarboxAnalyticsListener.EVENT_CHAPTER_CHANGED
48+
) { listener -> listener.onChapterChanged(eventTime, chapter) }
49+
}
50+
51+
override fun onCreditChanged(credit: Credit?) {
52+
val eventTime = generateCurrentPlayerMediaPeriodEventTime()
53+
54+
sendEventPillarbox(
55+
eventTime, PillarboxAnalyticsListener.EVENT_CREDIT_CHANGED
56+
) { listener -> listener.onCreditChanged(eventTime, credit) }
57+
}
58+
59+
override fun onBlockedTimeRangeReached(blockedTimeRange: BlockedTimeRange) {
60+
val eventTime = generateCurrentPlayerMediaPeriodEventTime()
61+
62+
sendEventPillarbox(
63+
eventTime, PillarboxAnalyticsListener.EVENT_BLOCKED_TIME_RANGE_REACHED
64+
) { listener -> listener.onBlockedTimeRangeReached(eventTime, blockedTimeRange) }
65+
}
66+
67+
private fun sendEventPillarbox(eventTime: EventTime, eventFlag: Int, event: Event<PillarboxAnalyticsListener>) {
68+
sendEvent(
69+
eventTime, eventFlag
70+
) { listener -> if (listener is PillarboxAnalyticsListener) event.invoke(listener) }
71+
}
72+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) SRG SSR. All rights reserved.
3+
* License information is available from the LICENSE file.
4+
*/
5+
package ch.srgssr.pillarbox.player.analytics
6+
7+
import androidx.media3.exoplayer.analytics.AnalyticsListener
8+
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime
9+
import ch.srgssr.pillarbox.player.PillarboxPlayer
10+
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
11+
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
12+
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
13+
14+
/**
15+
* Pillarbox analytics listener
16+
*
17+
* @see [AnalyticsListener]
18+
*/
19+
interface PillarboxAnalyticsListener : AnalyticsListener {
20+
/**
21+
* On smooth seeking enabled changed
22+
*
23+
* @param eventTime The [EventTime].
24+
* @param smoothSeekingEnabled The new value of [PillarboxPlayer.smoothSeekingEnabled]
25+
*/
26+
fun onSmoothSeekingEnabledChanged(eventTime: EventTime, smoothSeekingEnabled: Boolean) {}
27+
28+
/**
29+
* On tracking enabled changed
30+
*
31+
* @param eventTime The [EventTime].
32+
* @param trackingEnabled The new value of [PillarboxPlayer.trackingEnabled]
33+
*/
34+
fun onTrackingEnabledChanged(eventTime: EventTime, trackingEnabled: Boolean) {}
35+
36+
/**
37+
* `onChapterChanged` is called when either:
38+
* - The player position changes while playing automatically.
39+
* - The use seeks to a new position.
40+
* - The playlist changes.
41+
*
42+
* @param eventTime The [EventTime].
43+
* @param chapter `null` when the current position is not in a chapter.
44+
*/
45+
fun onChapterChanged(eventTime: EventTime, chapter: Chapter?) {}
46+
47+
/**
48+
* On blocked time range reached
49+
*
50+
* @param eventTime The [EventTime].
51+
* @param blockedTimeRange The [BlockedTimeRange] reached by the player.
52+
*/
53+
fun onBlockedTimeRangeReached(eventTime: EventTime, blockedTimeRange: BlockedTimeRange) {}
54+
55+
/**
56+
* `onCreditChanged` is called when either:
57+
* - The player position changes while playing automatically.
58+
* - The use seeks to a new position.
59+
* - The playlist changes.
60+
*
61+
* @param eventTime The [EventTime]
62+
* @param credit `null` when the current position is not in a Credit.
63+
*/
64+
fun onCreditChanged(eventTime: EventTime, credit: Credit?) {}
65+
66+
companion object {
67+
/**
68+
* @see [PillarboxPlayer.EVENT_BLOCKED_TIME_RANGE_REACHED]
69+
*/
70+
const val EVENT_BLOCKED_TIME_RANGE_REACHED = PillarboxPlayer.EVENT_BLOCKED_TIME_RANGE_REACHED
71+
72+
/**
73+
* @see [PillarboxPlayer.EVENT_CREDIT_CHANGED]
74+
*/
75+
const val EVENT_CREDIT_CHANGED = PillarboxPlayer.EVENT_CREDIT_CHANGED
76+
77+
/**
78+
* @see [PillarboxPlayer.EVENT_CHAPTER_CHANGED]
79+
*/
80+
const val EVENT_CHAPTER_CHANGED = PillarboxPlayer.EVENT_CHAPTER_CHANGED
81+
82+
/**
83+
* @see [PillarboxPlayer.EVENT_TRACKING_ENABLED_CHANGED]
84+
*/
85+
const val EVENT_TRACKING_ENABLED_CHANGED = PillarboxPlayer.EVENT_TRACKING_ENABLED_CHANGED
86+
87+
/**
88+
* @see [PillarboxPlayer.EVENT_SMOOTH_SEEKING_ENABLED_CHANGED]
89+
*/
90+
const val EVENT_SMOOTH_SEEKING_ENABLED_CHANGED = PillarboxPlayer.EVENT_SMOOTH_SEEKING_ENABLED_CHANGED
91+
}
92+
}

0 commit comments

Comments
 (0)