Skip to content

Commit 516a25f

Browse files
StaehliJMGaetan89
andauthored
Custom blocked segments (#539)
Co-authored-by: Gaëtan Muller <[email protected]>
1 parent 950ce8f commit 516a25f

File tree

4 files changed

+125
-1
lines changed

4 files changed

+125
-1
lines changed

pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/Playlist.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package ch.srgssr.pillarbox.demo.shared.data
88

99
import androidx.media3.common.MediaItem
1010
import androidx.media3.common.MediaMetadata
11+
import ch.srgssr.pillarbox.demo.shared.source.BlockedTimeRangeAssetLoader
1112
import java.io.Serializable
1213

1314
/**
@@ -464,7 +465,10 @@ data class Playlist(val title: String, val items: List<DemoItem>, val descriptio
464465
title = "Custom MediaSource",
465466
uri = "https://custom-media.ch/fondue",
466467
description = "Using a custom CustomMediaSource"
467-
)
468+
),
469+
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeAtStartAndEnd,
470+
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeOverlaps,
471+
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeIncluded,
468472
)
469473
)
470474

pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/di/PlayerModule.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ch.srgssr.dataprovider.paging.DataProviderPaging
1111
import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost
1212
import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader
1313
import ch.srgssr.pillarbox.core.business.tracker.DefaultMediaItemTrackerRepository
14+
import ch.srgssr.pillarbox.demo.shared.source.BlockedTimeRangeAssetLoader
1415
import ch.srgssr.pillarbox.demo.shared.source.CustomAssetLoader
1516
import ch.srgssr.pillarbox.demo.shared.ui.integrationLayer.data.ILRepository
1617
import ch.srgssr.pillarbox.player.PillarboxExoPlayer
@@ -31,6 +32,7 @@ object PlayerModule {
3132
mediaSourceFactory = PillarboxMediaSourceFactory(context).apply {
3233
addAssetLoader(SRGAssetLoader(context))
3334
addAssetLoader(CustomAssetLoader(context))
35+
addAssetLoader(BlockedTimeRangeAssetLoader(context))
3436
},
3537
mediaItemTrackerProvider = DefaultMediaItemTrackerRepository()
3638
)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) SRG SSR. All rights reserved.
3+
* License information is available from the LICENSE file.
4+
*/
5+
package ch.srgssr.pillarbox.demo.shared.source
6+
7+
import android.content.Context
8+
import androidx.media3.common.MediaItem
9+
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
10+
import ch.srgssr.pillarbox.demo.shared.data.DemoItem
11+
import ch.srgssr.pillarbox.player.asset.Asset
12+
import ch.srgssr.pillarbox.player.asset.AssetLoader
13+
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
14+
import kotlin.time.Duration.Companion.minutes
15+
import kotlin.time.Duration.Companion.seconds
16+
17+
/**
18+
* An AssetLoader to demonstrate some edge cases with [BlockedTimeRange].
19+
*/
20+
class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSourceFactory(context)) {
21+
22+
override fun canLoadAsset(mediaItem: MediaItem): Boolean {
23+
return mediaItem.localConfiguration?.uri?.toString()?.startsWith("blocked:") ?: false
24+
}
25+
26+
override suspend fun loadAsset(mediaItem: MediaItem): Asset {
27+
return Asset(
28+
mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(URL)),
29+
blockedTimeRanges = createBlockedTimeRangesFromId(mediaItem.mediaId),
30+
)
31+
}
32+
33+
@Suppress("MagicNumber")
34+
private fun createBlockedTimeRangesFromId(mediaId: String): List<BlockedTimeRange> {
35+
return when (mediaId) {
36+
ID_START_END -> {
37+
listOf(
38+
BlockedTimeRange(
39+
start = 0L,
40+
end = 10.seconds.inWholeMilliseconds,
41+
),
42+
BlockedTimeRange(
43+
start = (videoDuration - 5.minutes).inWholeMilliseconds,
44+
end = videoDuration.inWholeMilliseconds,
45+
)
46+
)
47+
}
48+
49+
ID_OVERLAP -> {
50+
listOf(
51+
BlockedTimeRange(
52+
start = 10.seconds.inWholeMilliseconds,
53+
end = 50.seconds.inWholeMilliseconds,
54+
),
55+
BlockedTimeRange(
56+
start = 15.seconds.inWholeMilliseconds,
57+
end = 5.minutes.inWholeMilliseconds,
58+
)
59+
)
60+
}
61+
62+
ID_INCLUDED -> {
63+
listOf(
64+
BlockedTimeRange(
65+
start = 15.seconds.inWholeMilliseconds,
66+
end = 30.seconds.inWholeMilliseconds,
67+
reason = "contained",
68+
),
69+
BlockedTimeRange(
70+
start = 10.seconds.inWholeMilliseconds,
71+
end = 1.minutes.inWholeMilliseconds,
72+
reason = "big",
73+
),
74+
)
75+
}
76+
77+
else -> emptyList()
78+
}
79+
}
80+
81+
companion object {
82+
private val URL = DemoItem.AppleBasic_16_9_TS_HLS.uri
83+
private val videoDuration = 1800.05.seconds
84+
85+
private const val ID_START_END = "blocked://StartEnd"
86+
private const val ID_OVERLAP = "blocked://Overlap"
87+
private const val ID_INCLUDED = "blocked://Included"
88+
89+
/**
90+
* [DemoItem] to test [BlockedTimeRange] at start and end of the media.
91+
*/
92+
val DemoItemBlockedTimeRangeAtStartAndEnd = DemoItem(
93+
title = "Starts and ends with a blocked time range",
94+
uri = ID_START_END,
95+
description = "Blocked times ranges at 00:00 - 00:10 and 25:00 - 30:00",
96+
)
97+
98+
/**
99+
* [DemoItem] to test overlapping [BlockedTimeRange].
100+
*/
101+
val DemoItemBlockedTimeRangeOverlaps = DemoItem(
102+
title = "Blocked time ranges are overlapping",
103+
uri = ID_OVERLAP,
104+
description = "Blocked times ranges at 00:10 to 00:50 and 00:15 to 05:00"
105+
)
106+
107+
/**
108+
* [DemoItem] to test included [BlockedTimeRange].
109+
*/
110+
val DemoItemBlockedTimeRangeIncluded = DemoItem(
111+
title = "Blocked time range is included in an other one",
112+
uri = ID_INCLUDED,
113+
description = "Blocked times ranges at 00:15 - 00:30 and 00:10 - 01:00"
114+
)
115+
}
116+
}

pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/TimeRangeTracker.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ private class BlockedTimeRangeTracker(
188188
override fun onEvents(player: Player, events: Player.Events) {
189189
val blockedInterval = timeRanges.firstOrNullAtPosition(player.currentPosition)
190190
blockedInterval?.let {
191+
// Ignore blocked time ranges that end at the same time as the media. Otherwise infinite seek operations.
192+
if (player.currentPosition >= player.duration) return@let
191193
callback(it)
192194
}
193195
}

0 commit comments

Comments
 (0)