Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0de4950
Slip PillarboxData into separate TrackGroup
StaehliJ Sep 18, 2024
cb968b2
Don't fill pillarbox tracks if they are "empty"
StaehliJ Sep 18, 2024
d59bfc2
Remove useless comments
StaehliJ Sep 19, 2024
935b49c
Add tests for PillarboxMediaPeriod
StaehliJ Sep 19, 2024
09ea82d
Update doc
StaehliJ Sep 19, 2024
2ffa69b
Remove remaining PillarboxData references
StaehliJ Sep 19, 2024
415e131
Revisit MediaItemTracker
StaehliJ Sep 20, 2024
d39069b
Update tests
StaehliJ Sep 23, 2024
39a06d5
Rename tests
StaehliJ Sep 24, 2024
76b032d
Add methods to PillarboxExoPlayer too and expose methods
StaehliJ Sep 24, 2024
1807f90
Release first exoplayer to avoid side effects with Listeners
StaehliJ Sep 24, 2024
5d2eaa3
Also update with AnalyticsListener
StaehliJ Sep 24, 2024
b73a51e
Fix CommandersAct lifecycle
StaehliJ Sep 24, 2024
80f1d59
Small documentation changes
MGaetan89 Sep 25, 2024
e5636d0
Don't handle seek to another item
StaehliJ Sep 24, 2024
1b6c3d7
Update pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/Pill…
StaehliJ Sep 25, 2024
9361771
Update pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/trac…
StaehliJ Sep 25, 2024
1aa7b00
Remove not necessary attribute
StaehliJ Sep 25, 2024
1c8c6fd
Apply suggestion
StaehliJ Sep 25, 2024
7737a1f
Remove wait for state IDLE after a release
StaehliJ Sep 25, 2024
31fc939
Fix suggestion
StaehliJ Sep 25, 2024
8e89376
Remove useless verify exactly 0
StaehliJ Sep 25, 2024
4e9d447
Fix doc
StaehliJ Sep 26, 2024
ed9dd8f
Fix missing enabledRunningInBackground
StaehliJ Sep 26, 2024
6269006
Fix mock in test
StaehliJ Sep 26, 2024
94261c9
Fix vod tests
StaehliJ Sep 26, 2024
6f33464
Handle stop with CommandersActTracker
StaehliJ Sep 26, 2024
1d2d842
MediaItemTracker are stop when player reach Player.STATE_IDLE.
StaehliJ Sep 26, 2024
7e93883
use assertEquals instead of ==
StaehliJ Sep 26, 2024
4c2522c
Merge branch 'main' into revisit-media-item-tracker
StaehliJ Sep 27, 2024
c850f42
Fix detect
StaehliJ Sep 27, 2024
e2e1e3f
Update pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/trac…
StaehliJ Sep 27, 2024
f4eb5a5
lint
StaehliJ Sep 27, 2024
46c4915
Merge branch 'main' into revisit-media-item-tracker
StaehliJ Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import ch.srgssr.pillarbox.analytics.commandersact.CommandersAct
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActEvent
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActPageView
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActSrg
import ch.srgssr.pillarbox.analytics.commandersact.NoOpCommandersAct
import ch.srgssr.pillarbox.analytics.comscore.ComScore
import ch.srgssr.pillarbox.analytics.comscore.ComScorePageView
import ch.srgssr.pillarbox.analytics.comscore.ComScoreSrg
import ch.srgssr.pillarbox.analytics.comscore.NoOpComScore

/**
* Analytics for SRG SSR
Expand Down Expand Up @@ -44,18 +46,18 @@ object SRGAnalytics {
* SRG CommandersAct analytics, do not use it unless you don't have any other choice!
* Meant to be used internally inside Pillarbox
*/
val commandersAct: CommandersAct?
val commandersAct: CommandersAct
get() {
return instance?.commandersAct
return instance?.commandersAct ?: NoOpCommandersAct
}

/**
* SRG ComScore analytics, do not use it unless you don't have any other choice!
* Meant to be used internally inside Pillarbox
*/
val comScore: ComScore?
val comScore: ComScore
get() {
return instance?.comScore
return instance?.comScore ?: NoOpComScore
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,20 @@ interface CommandersAct {
*/
fun setConsentServices(consentServices: List<String>)
}

internal object NoOpCommandersAct : CommandersAct {

override fun sendPageView(pageView: CommandersActPageView) = Unit

override fun sendEvent(event: CommandersActEvent) = Unit

override fun sendTcMediaEvent(event: TCMediaEvent) = Unit

override fun putPermanentData(labels: Map<String, String>) = Unit

override fun removePermanentData(label: String) = Unit

override fun getPermanentDataLabel(label: String): String? = null

override fun setConsentServices(consentServices: List<String>) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,16 @@ interface ComScore {
*/
fun setUserConsent(userConsent: ComScoreUserConsent)
}

internal object NoOpComScore : ComScore {

override fun sendPageView(pageView: ComScorePageView) = Unit

override fun putPersistentLabels(labels: Map<String, String>) = Unit

override fun removePersistentLabel(label: String) = Unit

override fun getPersistentLabel(label: String): String? = null

override fun setUserConsent(userConsent: ComScoreUserConsent) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ import androidx.media3.exoplayer.LoadControl
import ch.srgssr.pillarbox.core.business.integrationlayer.service.HttpMediaCompositionService
import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService
import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader
import ch.srgssr.pillarbox.core.business.tracker.DefaultMediaItemTrackerRepository
import ch.srgssr.pillarbox.player.PillarboxExoPlayer
import ch.srgssr.pillarbox.player.PillarboxExoPlayer.Companion.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION
import ch.srgssr.pillarbox.player.PillarboxLoadControl
import ch.srgssr.pillarbox.player.SeekIncrement
import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerProvider
import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration
Expand All @@ -37,7 +35,6 @@ object DefaultPillarbox {
* @param context The context.
* @param seekIncrement The seek increment.
* @param maxSeekToPreviousPosition The [Player.getMaxSeekToPreviousPosition] value.
* @param mediaItemTrackerRepository The provider of MediaItemTracker, by default [DefaultMediaItemTrackerRepository].
* @param mediaCompositionService The [MediaCompositionService] to use, by default [HttpMediaCompositionService].
* @param loadControl The load control, by default [PillarboxLoadControl].
* @return [PillarboxExoPlayer] suited for SRG.
Expand All @@ -46,15 +43,13 @@ object DefaultPillarbox {
context: Context,
seekIncrement: SeekIncrement = defaultSeekIncrement,
maxSeekToPreviousPosition: Duration = DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION,
mediaItemTrackerRepository: MediaItemTrackerProvider = DefaultMediaItemTrackerRepository(),
mediaCompositionService: MediaCompositionService = HttpMediaCompositionService(),
loadControl: LoadControl = PillarboxLoadControl(),
): PillarboxExoPlayer {
return DefaultPillarbox(
context = context,
seekIncrement = seekIncrement,
maxSeekToPreviousPosition = maxSeekToPreviousPosition,
mediaItemTrackerRepository = mediaItemTrackerRepository,
mediaCompositionService = mediaCompositionService,
loadControl = loadControl,
clock = Clock.DEFAULT,
Expand All @@ -68,7 +63,6 @@ object DefaultPillarbox {
* @param context The context.
* @param seekIncrement The seek increment.
* @param maxSeekToPreviousPosition The [Player.getMaxSeekToPreviousPosition] value.
* @param mediaItemTrackerRepository The provider of MediaItemTracker, by default [DefaultMediaItemTrackerRepository].
* @param loadControl The load control, by default [DefaultLoadControl].
* @param mediaCompositionService The [MediaCompositionService] to use, by default [HttpMediaCompositionService].
* @param clock The internal clock used by the player.
Expand All @@ -80,7 +74,6 @@ object DefaultPillarbox {
context: Context,
seekIncrement: SeekIncrement = defaultSeekIncrement,
maxSeekToPreviousPosition: Duration = DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION,
mediaItemTrackerRepository: MediaItemTrackerProvider = DefaultMediaItemTrackerRepository(),
loadControl: LoadControl = DefaultLoadControl(),
mediaCompositionService: MediaCompositionService = HttpMediaCompositionService(),
clock: Clock,
Expand All @@ -93,7 +86,6 @@ object DefaultPillarbox {
mediaSourceFactory = PillarboxMediaSourceFactory(context).apply {
addAssetLoader(SRGAssetLoader(context, mediaCompositionService))
},
mediaItemTrackerProvider = mediaItemTrackerRepository,
loadControl = loadControl,
clock = clock,
coroutineContext = coroutineContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import androidx.media3.common.MediaMetadata
import androidx.media3.common.MimeTypes
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import ch.srgssr.pillarbox.analytics.SRGAnalytics
import ch.srgssr.pillarbox.analytics.commandersact.CommandersAct
import ch.srgssr.pillarbox.core.business.HttpResultException
import ch.srgssr.pillarbox.core.business.akamai.AkamaiTokenDataSource
import ch.srgssr.pillarbox.core.business.akamai.AkamaiTokenProvider
Expand All @@ -31,10 +33,13 @@ import ch.srgssr.pillarbox.core.business.tracker.commandersact.CommandersActTrac
import ch.srgssr.pillarbox.core.business.tracker.comscore.ComScoreTracker
import ch.srgssr.pillarbox.player.asset.Asset
import ch.srgssr.pillarbox.player.asset.AssetLoader
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerData
import ch.srgssr.pillarbox.player.tracker.FactoryData
import ch.srgssr.pillarbox.player.tracker.MutableMediaItemTrackerData
import io.ktor.client.plugins.ClientRequestException
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.SerializationException
import java.io.IOException
import kotlin.coroutines.CoroutineContext

/**
* Mime Type for representing SRG SSR content
Expand All @@ -46,13 +51,18 @@ const val MimeTypeSrg = "${MimeTypes.BASE_TYPE_APPLICATION}/srg-ssr"
*
* @param context The context.
* @param mediaCompositionService The service to load a [MediaComposition].
* @param commandersAct The CommandersAct implementation to use with [CommandersActTracker].
* @param coroutineContext The [CoroutineContext] to use with [CommandersActTracker]
*/
class SRGAssetLoader(
class SRGAssetLoader internal constructor(
context: Context,
private val mediaCompositionService: MediaCompositionService = HttpMediaCompositionService()
private val mediaCompositionService: MediaCompositionService,
private val commandersAct: CommandersAct,
private val coroutineContext: CoroutineContext,
) : AssetLoader(
mediaSourceFactory = DefaultMediaSourceFactory(AkamaiTokenDataSource.Factory(AkamaiTokenProvider(), DefaultDataSource.Factory(context)))
) {

/**
* An interface to customize how [SRGAssetLoader] should fill [MediaMetadata].
*/
Expand Down Expand Up @@ -80,13 +90,13 @@ class SRGAssetLoader(
/**
* Provide Tracker Data to the [Asset]. The official SRG trackers are always setup by [SRGAssetLoader].
*
* @param trackerDataBuilder The [MediaItemTrackerData.Builder] to add trackers data.
* @param trackerDataBuilder The [MutableMediaItemTrackerData] to add tracker data.
* @param resource The [Resource] the player will play.
* @param chapter The main [Chapter] from the mediaComposition.
* @param mediaComposition The [MediaComposition] loaded from [MediaCompositionService].
*/
fun provide(
trackerDataBuilder: MediaItemTrackerData.Builder,
trackerDataBuilder: MutableMediaItemTrackerData,
resource: Resource,
chapter: Chapter,
mediaComposition: MediaComposition
Expand All @@ -105,6 +115,11 @@ class SRGAssetLoader(
*/
var trackerDataProvider: TrackerDataProvider? = null

constructor(
context: Context,
mediaCompositionService: MediaCompositionService = HttpMediaCompositionService(),
) : this(context, mediaCompositionService, SRGAnalytics.commandersAct, Dispatchers.Default)

override fun canLoadAsset(mediaItem: MediaItem): Boolean {
val localConfiguration = mediaItem.localConfiguration ?: return false

Expand Down Expand Up @@ -139,24 +154,23 @@ class SRGAssetLoader(
if (resource.tokenType == Resource.TokenType.AKAMAI) {
uri = AkamaiTokenDataSource.appendTokenQueryToUri(uri)
}
val trackerData = MediaItemTrackerData.Builder().apply {
trackerDataProvider?.provide(this, resource, chapter, result)
putData(SRGEventLoggerTracker::class.java)
getComScoreData(result, chapter, resource)?.let {
putData(ComScoreTracker::class.java, it)
}
getCommandersActData(result, chapter, resource)?.let {
putData(CommandersActTracker::class.java, it)
}
}.build()
val trackerData = MutableMediaItemTrackerData()
trackerDataProvider?.provide(trackerData, resource, chapter, result)
trackerData[SRGEventLoggerTracker::class.java] = FactoryData(SRGEventLoggerTracker.Factory(), Unit)
getComScoreData(result, chapter, resource)?.let {
trackerData[ComScoreTracker::class.java] = FactoryData(ComScoreTracker.Factory(), it)
}
getCommandersActData(result, chapter, resource)?.let {
trackerData[CommandersActTracker::class.java] = FactoryData(CommandersActTracker.Factory(commandersAct, coroutineContext), it)
}

val loadingMediaItem = MediaItem.Builder()
.setDrmConfiguration(fillDrmConfiguration(resource))
.setUri(uri)
.build()
return Asset(
mediaSource = mediaSourceFactory.createMediaSource(loadingMediaItem),
trackersData = trackerData,
trackersData = trackerData.toMediaItemTrackerData(),
mediaMetadata = mediaItem.mediaMetadata.buildUpon().apply {
mediaMetadataProvider.provide(
this,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,30 @@
*/
package ch.srgssr.pillarbox.core.business.tracker

import android.util.Log
import androidx.media3.exoplayer.ExoPlayer
import ch.srgssr.pillarbox.player.tracker.MediaItemTracker
import ch.srgssr.pillarbox.player.utils.PillarboxEventLogger
import kotlin.time.Duration.Companion.milliseconds

/**
* Enable/Disable EventLogger when item is currently active.
*/
class SRGEventLoggerTracker : MediaItemTracker {
class SRGEventLoggerTracker : MediaItemTracker<Unit> {
private val eventLogger = PillarboxEventLogger(TAG)

override fun start(player: ExoPlayer, initialData: Any?) {
Log.w(TAG, "---- Start")
override fun start(player: ExoPlayer, data: Unit) {
player.addAnalyticsListener(eventLogger)
}

override fun stop(player: ExoPlayer, reason: MediaItemTracker.StopReason, positionMs: Long) {
Log.w(TAG, "---- Stop because $reason at ${positionMs.milliseconds}")
override fun stop(player: ExoPlayer) {
player.removeAnalyticsListener(eventLogger)
}

/**
* Factory for a [SRGEventLoggerTracker]
*/
class Factory : MediaItemTracker.Factory {
class Factory : MediaItemTracker.Factory<Unit> {

override fun create(): MediaItemTracker {
override fun create(): MediaItemTracker<Unit> {
return SRGEventLoggerTracker()
}
}
Expand Down
Loading