Skip to content

Commit

Permalink
Custom blocked segments (#539)
Browse files Browse the repository at this point in the history
Co-authored-by: Gaëtan Muller <[email protected]>
  • Loading branch information
StaehliJ and MGaetan89 authored May 10, 2024
1 parent 950ce8f commit 516a25f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package ch.srgssr.pillarbox.demo.shared.data

import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import ch.srgssr.pillarbox.demo.shared.source.BlockedTimeRangeAssetLoader
import java.io.Serializable

/**
Expand Down Expand Up @@ -464,7 +465,10 @@ data class Playlist(val title: String, val items: List<DemoItem>, val descriptio
title = "Custom MediaSource",
uri = "https://custom-media.ch/fondue",
description = "Using a custom CustomMediaSource"
)
),
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeAtStartAndEnd,
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeOverlaps,
BlockedTimeRangeAssetLoader.DemoItemBlockedTimeRangeIncluded,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ch.srgssr.dataprovider.paging.DataProviderPaging
import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost
import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader
import ch.srgssr.pillarbox.core.business.tracker.DefaultMediaItemTrackerRepository
import ch.srgssr.pillarbox.demo.shared.source.BlockedTimeRangeAssetLoader
import ch.srgssr.pillarbox.demo.shared.source.CustomAssetLoader
import ch.srgssr.pillarbox.demo.shared.ui.integrationLayer.data.ILRepository
import ch.srgssr.pillarbox.player.PillarboxExoPlayer
Expand All @@ -31,6 +32,7 @@ object PlayerModule {
mediaSourceFactory = PillarboxMediaSourceFactory(context).apply {
addAssetLoader(SRGAssetLoader(context))
addAssetLoader(CustomAssetLoader(context))
addAssetLoader(BlockedTimeRangeAssetLoader(context))
},
mediaItemTrackerProvider = DefaultMediaItemTrackerRepository()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.demo.shared.source

import android.content.Context
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import ch.srgssr.pillarbox.demo.shared.data.DemoItem
import ch.srgssr.pillarbox.player.asset.Asset
import ch.srgssr.pillarbox.player.asset.AssetLoader
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

/**
* An AssetLoader to demonstrate some edge cases with [BlockedTimeRange].
*/
class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSourceFactory(context)) {

override fun canLoadAsset(mediaItem: MediaItem): Boolean {
return mediaItem.localConfiguration?.uri?.toString()?.startsWith("blocked:") ?: false
}

override suspend fun loadAsset(mediaItem: MediaItem): Asset {
return Asset(
mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(URL)),
blockedTimeRanges = createBlockedTimeRangesFromId(mediaItem.mediaId),
)
}

@Suppress("MagicNumber")
private fun createBlockedTimeRangesFromId(mediaId: String): List<BlockedTimeRange> {
return when (mediaId) {
ID_START_END -> {
listOf(
BlockedTimeRange(
start = 0L,
end = 10.seconds.inWholeMilliseconds,
),
BlockedTimeRange(
start = (videoDuration - 5.minutes).inWholeMilliseconds,
end = videoDuration.inWholeMilliseconds,
)
)
}

ID_OVERLAP -> {
listOf(
BlockedTimeRange(
start = 10.seconds.inWholeMilliseconds,
end = 50.seconds.inWholeMilliseconds,
),
BlockedTimeRange(
start = 15.seconds.inWholeMilliseconds,
end = 5.minutes.inWholeMilliseconds,
)
)
}

ID_INCLUDED -> {
listOf(
BlockedTimeRange(
start = 15.seconds.inWholeMilliseconds,
end = 30.seconds.inWholeMilliseconds,
reason = "contained",
),
BlockedTimeRange(
start = 10.seconds.inWholeMilliseconds,
end = 1.minutes.inWholeMilliseconds,
reason = "big",
),
)
}

else -> emptyList()
}
}

companion object {
private val URL = DemoItem.AppleBasic_16_9_TS_HLS.uri
private val videoDuration = 1800.05.seconds

private const val ID_START_END = "blocked://StartEnd"
private const val ID_OVERLAP = "blocked://Overlap"
private const val ID_INCLUDED = "blocked://Included"

/**
* [DemoItem] to test [BlockedTimeRange] at start and end of the media.
*/
val DemoItemBlockedTimeRangeAtStartAndEnd = DemoItem(
title = "Starts and ends with a blocked time range",
uri = ID_START_END,
description = "Blocked times ranges at 00:00 - 00:10 and 25:00 - 30:00",
)

/**
* [DemoItem] to test overlapping [BlockedTimeRange].
*/
val DemoItemBlockedTimeRangeOverlaps = DemoItem(
title = "Blocked time ranges are overlapping",
uri = ID_OVERLAP,
description = "Blocked times ranges at 00:10 to 00:50 and 00:15 to 05:00"
)

/**
* [DemoItem] to test included [BlockedTimeRange].
*/
val DemoItemBlockedTimeRangeIncluded = DemoItem(
title = "Blocked time range is included in an other one",
uri = ID_INCLUDED,
description = "Blocked times ranges at 00:15 - 00:30 and 00:10 - 01:00"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ private class BlockedTimeRangeTracker(
override fun onEvents(player: Player, events: Player.Events) {
val blockedInterval = timeRanges.firstOrNullAtPosition(player.currentPosition)
blockedInterval?.let {
// Ignore blocked time ranges that end at the same time as the media. Otherwise infinite seek operations.
if (player.currentPosition >= player.duration) return@let
callback(it)
}
}
Expand Down

0 comments on commit 516a25f

Please sign in to comment.