From d4e0a8d13d9dead5bb062af0b5a0e42e362a31b4 Mon Sep 17 00:00:00 2001 From: Tofunmi Adigun-Hameed Date: Tue, 6 Jun 2023 18:11:48 +0000 Subject: [PATCH] Merge pull request #425 from vishnuchilakala:set_min_live_position PiperOrigin-RevId: 538173603 (cherry picked from commit 9ca6e5d90dfda78a74609721a9a4e6a901bd7a92) --- .../exoplayer/dash/DashMediaSource.java | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java index 4936899d9d..5ebcc34690 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java @@ -110,6 +110,7 @@ public final class DashMediaSource extends BaseMediaSource { private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private long fallbackTargetLiveOffsetMs; + private long minLiveStartPositionUs; @Nullable private ParsingLoadable.Parser manifestParser; /** @@ -145,7 +146,7 @@ public final class DashMediaSource extends BaseMediaSource { * @param chunkSourceFactory A factory for {@link DashChunkSource} instances. * @param manifestDataSourceFactory A factory for {@link DataSource} instances that will be used * to load (and refresh) the manifest. May be {@code null} if the factory will only ever be - * used to create create media sources with sideloaded manifests via {@link + * used to create media sources with sideloaded manifests via {@link * #createMediaSource(DashManifest, MediaItem)}. */ public Factory( @@ -156,6 +157,7 @@ public final class DashMediaSource extends BaseMediaSource { drmSessionManagerProvider = new DefaultDrmSessionManagerProvider(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); fallbackTargetLiveOffsetMs = DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS; + minLiveStartPositionUs = MIN_LIVE_DEFAULT_START_POSITION_US; compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); } @@ -199,6 +201,25 @@ public final class DashMediaSource extends BaseMediaSource { return this; } + /** + * Sets the minimum position to start playback from in a live stream, in microseconds relative + * to the start of the live window. + * + *

This value will override any suggested value from the manifest and helps to prevent {@link + * androidx.media3.exoplayer.source.BehindLiveWindowException} issues. + * + *

The default value is {@link #MIN_LIVE_DEFAULT_START_POSITION_US}. + * + * @param minLiveStartPositionUs The minimum live start position, in microseconds relative to + * the start of the live window. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + public Factory setMinLiveStartPositionUs(long minLiveStartPositionUs) { + this.minLiveStartPositionUs = minLiveStartPositionUs; + return this; + } + /** * Sets the manifest parser to parse loaded manifest data when loading a manifest URI. * @@ -278,7 +299,8 @@ public final class DashMediaSource extends BaseMediaSource { compositeSequenceableLoaderFactory, drmSessionManagerProvider.get(mediaItem), loadErrorHandlingPolicy, - fallbackTargetLiveOffsetMs); + fallbackTargetLiveOffsetMs, + minLiveStartPositionUs); } /** @@ -309,7 +331,8 @@ public final class DashMediaSource extends BaseMediaSource { compositeSequenceableLoaderFactory, drmSessionManagerProvider.get(mediaItem), loadErrorHandlingPolicy, - fallbackTargetLiveOffsetMs); + fallbackTargetLiveOffsetMs, + minLiveStartPositionUs); } @Override @@ -330,16 +353,18 @@ public final class DashMediaSource extends BaseMediaSource { /** The media id used by media items of dash media sources without a manifest URI. */ public static final String DEFAULT_MEDIA_ID = "DashMediaSource"; + /** + * The minimum default start position for live streams, in microseconds relative to the start of + * the live window. + */ + public static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5_000_000; + /** * The interval in milliseconds between invocations of {@link * MediaSourceCaller#onSourceInfoRefreshed(MediaSource, Timeline)} when the source's {@link * Timeline} is changing dynamically (for example, for incomplete live streams). */ private static final long DEFAULT_NOTIFY_MANIFEST_INTERVAL_MS = 5000; - /** - * The minimum default start position for live streams, relative to the start of the live window. - */ - private static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5_000_000; private static final String TAG = "DashMediaSource"; @@ -352,6 +377,7 @@ public final class DashMediaSource extends BaseMediaSource { private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final BaseUrlExclusionList baseUrlExclusionList; private final long fallbackTargetLiveOffsetMs; + private final long minLiveStartPositionUs; private final EventDispatcher manifestEventDispatcher; private final ParsingLoadable.Parser manifestParser; private final ManifestCallback manifestCallback; @@ -392,7 +418,8 @@ public final class DashMediaSource extends BaseMediaSource { CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, DrmSessionManager drmSessionManager, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - long fallbackTargetLiveOffsetMs) { + long fallbackTargetLiveOffsetMs, + long minLiveStartPositionUs) { this.mediaItem = mediaItem; this.liveConfiguration = mediaItem.liveConfiguration; this.manifestUri = checkNotNull(mediaItem.localConfiguration).uri; @@ -404,6 +431,7 @@ public final class DashMediaSource extends BaseMediaSource { this.drmSessionManager = drmSessionManager; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.fallbackTargetLiveOffsetMs = fallbackTargetLiveOffsetMs; + this.minLiveStartPositionUs = minLiveStartPositionUs; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; baseUrlExclusionList = new BaseUrlExclusionList(); sideloadedManifest = manifest != null; @@ -829,8 +857,7 @@ public final class DashMediaSource extends BaseMediaSource { windowStartUnixTimeMs = manifest.availabilityStartTimeMs + Util.usToMs(windowStartTimeInManifestUs); windowDefaultPositionUs = nowInWindowUs - Util.msToUs(liveConfiguration.targetOffsetMs); - long minimumWindowDefaultPositionUs = - min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2); + long minimumWindowDefaultPositionUs = min(minLiveStartPositionUs, windowDurationUs / 2); if (windowDefaultPositionUs < minimumWindowDefaultPositionUs) { // The default position is too close to the start of the live window. Set it to the minimum // default position provided the window is at least twice as big. Else set it to the middle @@ -939,8 +966,7 @@ public final class DashMediaSource extends BaseMediaSource { targetOffsetMs = minLiveOffsetMs; } if (targetOffsetMs > maxLiveOffsetMs) { - long safeDistanceFromWindowStartUs = - min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2); + long safeDistanceFromWindowStartUs = min(minLiveStartPositionUs, windowDurationUs / 2); long maxTargetOffsetForSafeDistanceToWindowStartMs = usToMs(nowInWindowUs - safeDistanceFromWindowStartUs); targetOffsetMs =