mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Throw exception when TimestampAdjuster
initialization hits timeout
Add `HlsMediaSource.Factory.setTimestampAdjusterInitializationTimeoutMs(long)` to set the timeout for the loading thread to wait for the `TimestampAdjuster` to initialize. If the initialization doesn't complete before the timeout, a `PlaybackException` is thrown to avoid the playback endless stalling. The timeout is set to zero by default. This can avoid HLS playback endlessly stalls when manifest has missing discontinuities. According to the HLS spec, all variants and renditions have discontinuities at the same points in time. If not, the one with discontinuities will have a new `TimestampAdjuster` not shared by the others. When the loading thread of that variant is waiting for the other threads to initialize the timestamp and hits the timeout, the playback will stall. Issue: androidx/media#323 #minor-release PiperOrigin-RevId: 539108886 (cherry picked from commit db3e662bdc17945acbe835120806b6aa597dee8a)
This commit is contained in:
parent
21fb8c9942
commit
23e92805a1
@ -85,6 +85,13 @@
|
|||||||
produced a `IndexOutOfBoundsException`
|
produced a `IndexOutOfBoundsException`
|
||||||
([#10838](https://github.com/google/ExoPlayer/issues/10838)).
|
([#10838](https://github.com/google/ExoPlayer/issues/10838)).
|
||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
|
* Add
|
||||||
|
`HlsMediaSource.Factory.setTimestampAdjusterInitializationTimeoutMs(long)`
|
||||||
|
to set a timeout for the loading thread to wait for the
|
||||||
|
`TimestampAdjuster` to initialize. If the initialization doesn't
|
||||||
|
complete before the timeout, a `PlaybackException` is thrown to avoid
|
||||||
|
the playback endless stalling. The timeout is set to zero by default
|
||||||
|
([#323](https://github.com/androidx/media/issues//323)).
|
||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
|
@ -15,8 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.common.util;
|
package androidx.media3.common.util;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts and offsets sample timestamps. MPEG-2 TS timestamps scaling and adjustment is supported,
|
* Adjusts and offsets sample timestamps. MPEG-2 TS timestamps scaling and adjustment is supported,
|
||||||
@ -100,20 +105,40 @@ public final class TimestampAdjuster {
|
|||||||
* @param canInitialize Whether the caller is able to initialize the adjuster, if needed.
|
* @param canInitialize Whether the caller is able to initialize the adjuster, if needed.
|
||||||
* @param nextSampleTimestampUs The desired timestamp for the next sample loaded by the calling
|
* @param nextSampleTimestampUs The desired timestamp for the next sample loaded by the calling
|
||||||
* thread, in microseconds. Only used if {@code canInitialize} is {@code true}.
|
* thread, in microseconds. Only used if {@code canInitialize} is {@code true}.
|
||||||
|
* @param timeoutMs The timeout for the thread to wait for the timestamp adjuster to initialize,
|
||||||
|
* in milliseconds. A timeout of zero is interpreted as an infinite timeout.
|
||||||
* @throws InterruptedException If the thread is interrupted whilst blocked waiting for
|
* @throws InterruptedException If the thread is interrupted whilst blocked waiting for
|
||||||
* initialization to complete.
|
* initialization to complete.
|
||||||
|
* @throws TimeoutException If the thread is timeout whilst blocked waiting for initialization to
|
||||||
|
* complete.
|
||||||
*/
|
*/
|
||||||
public synchronized void sharedInitializeOrWait(boolean canInitialize, long nextSampleTimestampUs)
|
public synchronized void sharedInitializeOrWait(
|
||||||
throws InterruptedException {
|
boolean canInitialize, long nextSampleTimestampUs, long timeoutMs)
|
||||||
Assertions.checkState(firstSampleTimestampUs == MODE_SHARED);
|
throws InterruptedException, TimeoutException {
|
||||||
|
checkState(firstSampleTimestampUs == MODE_SHARED);
|
||||||
if (isInitialized()) {
|
if (isInitialized()) {
|
||||||
return;
|
return;
|
||||||
} else if (canInitialize) {
|
} else if (canInitialize) {
|
||||||
this.nextSampleTimestampUs.set(nextSampleTimestampUs);
|
this.nextSampleTimestampUs.set(nextSampleTimestampUs);
|
||||||
} else {
|
} else {
|
||||||
// Wait for another calling thread to complete initialization.
|
// Wait for another calling thread to complete initialization.
|
||||||
|
long totalWaitDurationMs = 0;
|
||||||
|
long remainingTimeoutMs = timeoutMs;
|
||||||
while (!isInitialized()) {
|
while (!isInitialized()) {
|
||||||
|
if (timeoutMs == 0) {
|
||||||
wait();
|
wait();
|
||||||
|
} else {
|
||||||
|
checkState(remainingTimeoutMs > 0);
|
||||||
|
long waitStartingTimeMs = SystemClock.elapsedRealtime();
|
||||||
|
wait(remainingTimeoutMs);
|
||||||
|
totalWaitDurationMs += SystemClock.elapsedRealtime() - waitStartingTimeMs;
|
||||||
|
if (totalWaitDurationMs >= timeoutMs && !isInitialized()) {
|
||||||
|
String message =
|
||||||
|
"TimestampAdjuster failed to initialize in " + timeoutMs + " milliseconds";
|
||||||
|
throw new TimeoutException(message);
|
||||||
|
}
|
||||||
|
remainingTimeoutMs = timeoutMs - totalWaitDurationMs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,7 +222,7 @@ public final class TimestampAdjuster {
|
|||||||
if (!isInitialized()) {
|
if (!isInitialized()) {
|
||||||
long desiredSampleTimestampUs =
|
long desiredSampleTimestampUs =
|
||||||
firstSampleTimestampUs == MODE_SHARED
|
firstSampleTimestampUs == MODE_SHARED
|
||||||
? Assertions.checkNotNull(nextSampleTimestampUs.get())
|
? checkNotNull(nextSampleTimestampUs.get())
|
||||||
: firstSampleTimestampUs;
|
: firstSampleTimestampUs;
|
||||||
timestampOffsetUs = desiredSampleTimestampUs - timeUs;
|
timestampOffsetUs = desiredSampleTimestampUs - timeUs;
|
||||||
// Notify threads waiting for the timestamp offset to be determined.
|
// Notify threads waiting for the timestamp offset to be determined.
|
||||||
|
@ -133,6 +133,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final FullSegmentEncryptionKeyCache keyCache;
|
private final FullSegmentEncryptionKeyCache keyCache;
|
||||||
private final PlayerId playerId;
|
private final PlayerId playerId;
|
||||||
@Nullable private final CmcdConfiguration cmcdConfiguration;
|
@Nullable private final CmcdConfiguration cmcdConfiguration;
|
||||||
|
private final long timestampAdjusterInitializationTimeoutMs;
|
||||||
|
|
||||||
private boolean isPrimaryTimestampSource;
|
private boolean isPrimaryTimestampSource;
|
||||||
private byte[] scratchSpace;
|
private byte[] scratchSpace;
|
||||||
@ -161,6 +162,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If multiple
|
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If multiple
|
||||||
* {@link HlsChunkSource}s are used for a single playback, they should all share the same
|
* {@link HlsChunkSource}s are used for a single playback, they should all share the same
|
||||||
* provider.
|
* provider.
|
||||||
|
* @param timestampAdjusterInitializationTimeoutMs The timeout for the loading thread to wait for
|
||||||
|
* the timestamp adjuster to initialize, in milliseconds. A timeout of zero is interpreted as
|
||||||
|
* an infinite timeout.
|
||||||
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
|
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
|
||||||
* information is available in the multivariant playlist.
|
* information is available in the multivariant playlist.
|
||||||
*/
|
*/
|
||||||
@ -172,6 +176,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
HlsDataSourceFactory dataSourceFactory,
|
HlsDataSourceFactory dataSourceFactory,
|
||||||
@Nullable TransferListener mediaTransferListener,
|
@Nullable TransferListener mediaTransferListener,
|
||||||
TimestampAdjusterProvider timestampAdjusterProvider,
|
TimestampAdjusterProvider timestampAdjusterProvider,
|
||||||
|
long timestampAdjusterInitializationTimeoutMs,
|
||||||
@Nullable List<Format> muxedCaptionFormats,
|
@Nullable List<Format> muxedCaptionFormats,
|
||||||
PlayerId playerId,
|
PlayerId playerId,
|
||||||
@Nullable CmcdConfiguration cmcdConfiguration) {
|
@Nullable CmcdConfiguration cmcdConfiguration) {
|
||||||
@ -180,6 +185,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
this.playlistUrls = playlistUrls;
|
this.playlistUrls = playlistUrls;
|
||||||
this.playlistFormats = playlistFormats;
|
this.playlistFormats = playlistFormats;
|
||||||
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
||||||
|
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||||
this.muxedCaptionFormats = muxedCaptionFormats;
|
this.muxedCaptionFormats = muxedCaptionFormats;
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
this.cmcdConfiguration = cmcdConfiguration;
|
this.cmcdConfiguration = cmcdConfiguration;
|
||||||
@ -519,6 +525,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
trackSelection.getSelectionData(),
|
trackSelection.getSelectionData(),
|
||||||
isPrimaryTimestampSource,
|
isPrimaryTimestampSource,
|
||||||
timestampAdjusterProvider,
|
timestampAdjusterProvider,
|
||||||
|
timestampAdjusterInitializationTimeoutMs,
|
||||||
previous,
|
previous,
|
||||||
/* mediaSegmentKey= */ keyCache.get(mediaSegmentKeyUri),
|
/* mediaSegmentKey= */ keyCache.get(mediaSegmentKeyUri),
|
||||||
/* initSegmentKey= */ keyCache.get(initSegmentKeyUri),
|
/* initSegmentKey= */ keyCache.get(initSegmentKeyUri),
|
||||||
|
@ -47,6 +47,7 @@ import java.io.IOException;
|
|||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@ -73,6 +74,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
* @param isPrimaryTimestampSource True if the chunk can initialize the timestamp adjuster.
|
* @param isPrimaryTimestampSource True if the chunk can initialize the timestamp adjuster.
|
||||||
* @param timestampAdjusterProvider The provider from which to obtain the {@link
|
* @param timestampAdjusterProvider The provider from which to obtain the {@link
|
||||||
* TimestampAdjuster}.
|
* TimestampAdjuster}.
|
||||||
|
* @param timestampAdjusterInitializationTimeoutMs The timeout for the loading thread to wait for
|
||||||
|
* the timestamp adjuster to initialize, in milliseconds. A timeout of zero is interpreted as
|
||||||
|
* an infinite timeout.
|
||||||
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
||||||
* @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
|
* @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
|
||||||
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
|
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
|
||||||
@ -92,6 +96,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
@Nullable Object trackSelectionData,
|
@Nullable Object trackSelectionData,
|
||||||
boolean isPrimaryTimestampSource,
|
boolean isPrimaryTimestampSource,
|
||||||
TimestampAdjusterProvider timestampAdjusterProvider,
|
TimestampAdjusterProvider timestampAdjusterProvider,
|
||||||
|
long timestampAdjusterInitializationTimeoutMs,
|
||||||
@Nullable HlsMediaChunk previousChunk,
|
@Nullable HlsMediaChunk previousChunk,
|
||||||
@Nullable byte[] mediaSegmentKey,
|
@Nullable byte[] mediaSegmentKey,
|
||||||
@Nullable byte[] initSegmentKey,
|
@Nullable byte[] initSegmentKey,
|
||||||
@ -189,6 +194,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
mediaSegment.hasGapTag,
|
mediaSegment.hasGapTag,
|
||||||
isPrimaryTimestampSource,
|
isPrimaryTimestampSource,
|
||||||
/* timestampAdjuster= */ timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber),
|
/* timestampAdjuster= */ timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber),
|
||||||
|
timestampAdjusterInitializationTimeoutMs,
|
||||||
mediaSegment.drmInitData,
|
mediaSegment.drmInitData,
|
||||||
previousExtractor,
|
previousExtractor,
|
||||||
id3Decoder,
|
id3Decoder,
|
||||||
@ -267,6 +273,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
private final boolean mediaSegmentEncrypted;
|
private final boolean mediaSegmentEncrypted;
|
||||||
private final boolean initSegmentEncrypted;
|
private final boolean initSegmentEncrypted;
|
||||||
private final PlayerId playerId;
|
private final PlayerId playerId;
|
||||||
|
private final long timestampAdjusterInitializationTimeoutMs;
|
||||||
|
|
||||||
private @MonotonicNonNull HlsMediaChunkExtractor extractor;
|
private @MonotonicNonNull HlsMediaChunkExtractor extractor;
|
||||||
private @MonotonicNonNull HlsSampleStreamWrapper output;
|
private @MonotonicNonNull HlsSampleStreamWrapper output;
|
||||||
@ -302,6 +309,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
boolean hasGapTag,
|
boolean hasGapTag,
|
||||||
boolean isPrimaryTimestampSource,
|
boolean isPrimaryTimestampSource,
|
||||||
TimestampAdjuster timestampAdjuster,
|
TimestampAdjuster timestampAdjuster,
|
||||||
|
long timestampAdjusterInitializationTimeoutMs,
|
||||||
@Nullable DrmInitData drmInitData,
|
@Nullable DrmInitData drmInitData,
|
||||||
@Nullable HlsMediaChunkExtractor previousExtractor,
|
@Nullable HlsMediaChunkExtractor previousExtractor,
|
||||||
Id3Decoder id3Decoder,
|
Id3Decoder id3Decoder,
|
||||||
@ -328,6 +336,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
this.playlistUrl = playlistUrl;
|
this.playlistUrl = playlistUrl;
|
||||||
this.isPrimaryTimestampSource = isPrimaryTimestampSource;
|
this.isPrimaryTimestampSource = isPrimaryTimestampSource;
|
||||||
this.timestampAdjuster = timestampAdjuster;
|
this.timestampAdjuster = timestampAdjuster;
|
||||||
|
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||||
this.hasGapTag = hasGapTag;
|
this.hasGapTag = hasGapTag;
|
||||||
this.extractorFactory = extractorFactory;
|
this.extractorFactory = extractorFactory;
|
||||||
this.muxedCaptionFormats = muxedCaptionFormats;
|
this.muxedCaptionFormats = muxedCaptionFormats;
|
||||||
@ -502,9 +511,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
long bytesToRead = dataSource.open(dataSpec);
|
long bytesToRead = dataSource.open(dataSpec);
|
||||||
if (initializeTimestampAdjuster) {
|
if (initializeTimestampAdjuster) {
|
||||||
try {
|
try {
|
||||||
timestampAdjuster.sharedInitializeOrWait(isPrimaryTimestampSource, startTimeUs);
|
timestampAdjuster.sharedInitializeOrWait(
|
||||||
|
isPrimaryTimestampSource, startTimeUs, timestampAdjusterInitializationTimeoutMs);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new InterruptedIOException();
|
throw new InterruptedIOException();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefaultExtractorInput extractorInput =
|
DefaultExtractorInput extractorInput =
|
||||||
|
@ -84,6 +84,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsPlaylistTracker.Pla
|
|||||||
private final boolean useSessionKeys;
|
private final boolean useSessionKeys;
|
||||||
private final PlayerId playerId;
|
private final PlayerId playerId;
|
||||||
private final HlsSampleStreamWrapper.Callback sampleStreamWrapperCallback;
|
private final HlsSampleStreamWrapper.Callback sampleStreamWrapperCallback;
|
||||||
|
private final long timestampAdjusterInitializationTimeoutMs;
|
||||||
|
|
||||||
@Nullable private MediaPeriod.Callback mediaPeriodCallback;
|
@Nullable private MediaPeriod.Callback mediaPeriodCallback;
|
||||||
private int pendingPrepareCount;
|
private int pendingPrepareCount;
|
||||||
@ -118,6 +119,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsPlaylistTracker.Pla
|
|||||||
* @param metadataType The type of metadata to extract from the period.
|
* @param metadataType The type of metadata to extract from the period.
|
||||||
* @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags.
|
* @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags.
|
||||||
* @param playerId The ID of the current player.
|
* @param playerId The ID of the current player.
|
||||||
|
* @param timestampAdjusterInitializationTimeoutMs The timeout for the loading thread to wait for
|
||||||
|
* the timestamp adjuster to initialize, in milliseconds. A timeout of zero is interpreted as
|
||||||
|
* an infinite timeout.
|
||||||
*/
|
*/
|
||||||
public HlsMediaPeriod(
|
public HlsMediaPeriod(
|
||||||
HlsExtractorFactory extractorFactory,
|
HlsExtractorFactory extractorFactory,
|
||||||
@ -134,7 +138,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsPlaylistTracker.Pla
|
|||||||
boolean allowChunklessPreparation,
|
boolean allowChunklessPreparation,
|
||||||
@HlsMediaSource.MetadataType int metadataType,
|
@HlsMediaSource.MetadataType int metadataType,
|
||||||
boolean useSessionKeys,
|
boolean useSessionKeys,
|
||||||
PlayerId playerId) {
|
PlayerId playerId,
|
||||||
|
long timestampAdjusterInitializationTimeoutMs) {
|
||||||
this.extractorFactory = extractorFactory;
|
this.extractorFactory = extractorFactory;
|
||||||
this.playlistTracker = playlistTracker;
|
this.playlistTracker = playlistTracker;
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
@ -150,6 +155,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsPlaylistTracker.Pla
|
|||||||
this.metadataType = metadataType;
|
this.metadataType = metadataType;
|
||||||
this.useSessionKeys = useSessionKeys;
|
this.useSessionKeys = useSessionKeys;
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
|
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||||
sampleStreamWrapperCallback = new SampleStreamWrapperCallback();
|
sampleStreamWrapperCallback = new SampleStreamWrapperCallback();
|
||||||
compositeSequenceableLoader =
|
compositeSequenceableLoader =
|
||||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
|
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
|
||||||
@ -781,6 +787,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsPlaylistTracker.Pla
|
|||||||
dataSourceFactory,
|
dataSourceFactory,
|
||||||
mediaTransferListener,
|
mediaTransferListener,
|
||||||
timestampAdjusterProvider,
|
timestampAdjusterProvider,
|
||||||
|
timestampAdjusterInitializationTimeoutMs,
|
||||||
muxedCaptionFormats,
|
muxedCaptionFormats,
|
||||||
playerId,
|
playerId,
|
||||||
cmcdConfiguration);
|
cmcdConfiguration);
|
||||||
|
@ -113,6 +113,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
private @MetadataType int metadataType;
|
private @MetadataType int metadataType;
|
||||||
private boolean useSessionKeys;
|
private boolean useSessionKeys;
|
||||||
private long elapsedRealTimeOffsetMs;
|
private long elapsedRealTimeOffsetMs;
|
||||||
|
private long timestampAdjusterInitializationTimeoutMs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new factory for {@link HlsMediaSource}s.
|
* Creates a new factory for {@link HlsMediaSource}s.
|
||||||
@ -322,6 +323,21 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout for the loading thread to wait for the timestamp adjuster to initialize, in
|
||||||
|
* milliseconds.The default value is zero, which is interpreted as an infinite timeout.
|
||||||
|
*
|
||||||
|
* @param timestampAdjusterInitializationTimeoutMs The timeout in milliseconds. A timeout of
|
||||||
|
* zero is interpreted as an infinite timeout.
|
||||||
|
* @return This factory, for convenience.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Factory setTimestampAdjusterInitializationTimeoutMs(
|
||||||
|
long timestampAdjusterInitializationTimeoutMs) {
|
||||||
|
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the offset between {@link SystemClock#elapsedRealtime()} and the time since the Unix
|
* Sets the offset between {@link SystemClock#elapsedRealtime()} and the time since the Unix
|
||||||
* epoch. By default, is it set to {@link C#TIME_UNSET}.
|
* epoch. By default, is it set to {@link C#TIME_UNSET}.
|
||||||
@ -372,7 +388,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
elapsedRealTimeOffsetMs,
|
elapsedRealTimeOffsetMs,
|
||||||
allowChunklessPreparation,
|
allowChunklessPreparation,
|
||||||
metadataType,
|
metadataType,
|
||||||
useSessionKeys);
|
useSessionKeys,
|
||||||
|
timestampAdjusterInitializationTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -394,6 +411,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
private final HlsPlaylistTracker playlistTracker;
|
private final HlsPlaylistTracker playlistTracker;
|
||||||
private final long elapsedRealTimeOffsetMs;
|
private final long elapsedRealTimeOffsetMs;
|
||||||
private final MediaItem mediaItem;
|
private final MediaItem mediaItem;
|
||||||
|
private final long timestampAdjusterInitializationTimeoutMs;
|
||||||
|
|
||||||
private MediaItem.LiveConfiguration liveConfiguration;
|
private MediaItem.LiveConfiguration liveConfiguration;
|
||||||
@Nullable private TransferListener mediaTransferListener;
|
@Nullable private TransferListener mediaTransferListener;
|
||||||
@ -410,7 +428,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
long elapsedRealTimeOffsetMs,
|
long elapsedRealTimeOffsetMs,
|
||||||
boolean allowChunklessPreparation,
|
boolean allowChunklessPreparation,
|
||||||
@MetadataType int metadataType,
|
@MetadataType int metadataType,
|
||||||
boolean useSessionKeys) {
|
boolean useSessionKeys,
|
||||||
|
long timestampAdjusterInitializationTimeoutMs) {
|
||||||
this.localConfiguration = checkNotNull(mediaItem.localConfiguration);
|
this.localConfiguration = checkNotNull(mediaItem.localConfiguration);
|
||||||
this.mediaItem = mediaItem;
|
this.mediaItem = mediaItem;
|
||||||
this.liveConfiguration = mediaItem.liveConfiguration;
|
this.liveConfiguration = mediaItem.liveConfiguration;
|
||||||
@ -425,6 +444,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
this.allowChunklessPreparation = allowChunklessPreparation;
|
this.allowChunklessPreparation = allowChunklessPreparation;
|
||||||
this.metadataType = metadataType;
|
this.metadataType = metadataType;
|
||||||
this.useSessionKeys = useSessionKeys;
|
this.useSessionKeys = useSessionKeys;
|
||||||
|
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -468,7 +488,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
allowChunklessPreparation,
|
allowChunklessPreparation,
|
||||||
metadataType,
|
metadataType,
|
||||||
useSessionKeys,
|
useSessionKeys,
|
||||||
getPlayerId());
|
getPlayerId(),
|
||||||
|
timestampAdjusterInitializationTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,6 +321,7 @@ public class HlsChunkSourceTest {
|
|||||||
new DefaultHlsDataSourceFactory(new FakeDataSource.Factory()),
|
new DefaultHlsDataSourceFactory(new FakeDataSource.Factory()),
|
||||||
/* mediaTransferListener= */ null,
|
/* mediaTransferListener= */ null,
|
||||||
new TimestampAdjusterProvider(),
|
new TimestampAdjusterProvider(),
|
||||||
|
/* timestampAdjusterInitializationTimeoutMs= */ 0,
|
||||||
/* muxedCaptionFormats= */ null,
|
/* muxedCaptionFormats= */ null,
|
||||||
PlayerId.UNSET,
|
PlayerId.UNSET,
|
||||||
cmcdConfiguration);
|
cmcdConfiguration);
|
||||||
|
@ -96,7 +96,8 @@ public final class HlsMediaPeriodTest {
|
|||||||
/* allowChunklessPreparation= */ true,
|
/* allowChunklessPreparation= */ true,
|
||||||
HlsMediaSource.METADATA_TYPE_ID3,
|
HlsMediaSource.METADATA_TYPE_ID3,
|
||||||
/* useSessionKeys= */ false,
|
/* useSessionKeys= */ false,
|
||||||
PlayerId.UNSET);
|
PlayerId.UNSET,
|
||||||
|
/* timestampAdjusterInitializationTimeoutMs= */ 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(
|
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user