Suppress no-op timeline changes in ExtractorMediaSource
When an ExtractorMediaSource is used in a concatenation, and probably when using repeat modes, it needs to produce multiple ExtractorMediaPeriods during usage. Currently we fire a source info refresh every time a new ExtractorMediaPeriod instance prepares, which triggers ExoPlayer.EventListener's onTimelineChanged method. In nearly all cases the timeline is unchanged after the first ExtractorMediaPeriod is prepared. This change suppresses these no-op changes. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=162484234
This commit is contained in:
parent
e5952d4859
commit
108f4f14f3
@ -50,6 +50,21 @@ import java.util.Arrays;
|
|||||||
Loader.Callback<ExtractorMediaPeriod.ExtractingLoadable>, Loader.ReleaseCallback,
|
Loader.Callback<ExtractorMediaPeriod.ExtractingLoadable>, Loader.ReleaseCallback,
|
||||||
UpstreamFormatChangedListener {
|
UpstreamFormatChangedListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for information about the period.
|
||||||
|
*/
|
||||||
|
interface Listener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the duration or ability to seek within the period changes.
|
||||||
|
*
|
||||||
|
* @param durationUs The duration of the period, or {@link C#TIME_UNSET}.
|
||||||
|
* @param isSeekable Whether the period is seekable.
|
||||||
|
*/
|
||||||
|
void onSourceInfoRefreshed(long durationUs, boolean isSeekable);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the source's duration is unknown, it is calculated by adding this value to the largest
|
* When the source's duration is unknown, it is calculated by adding this value to the largest
|
||||||
* sample timestamp seen when buffering completes.
|
* sample timestamp seen when buffering completes.
|
||||||
@ -61,7 +76,7 @@ import java.util.Arrays;
|
|||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
private final Handler eventHandler;
|
private final Handler eventHandler;
|
||||||
private final ExtractorMediaSource.EventListener eventListener;
|
private final ExtractorMediaSource.EventListener eventListener;
|
||||||
private final MediaSource.Listener sourceListener;
|
private final Listener listener;
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
private final String customCacheKey;
|
private final String customCacheKey;
|
||||||
private final long continueLoadingCheckIntervalBytes;
|
private final long continueLoadingCheckIntervalBytes;
|
||||||
@ -103,7 +118,7 @@ import java.util.Arrays;
|
|||||||
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
||||||
* @param eventHandler A handler for events. May be null if delivery of events is not required.
|
* @param eventHandler A handler for events. May be null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
* @param sourceListener A listener to notify when the timeline has been loaded.
|
* @param listener A listener to notify when information about the period changes.
|
||||||
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
|
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
|
||||||
* @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache
|
* @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache
|
||||||
* indexing. May be null.
|
* indexing. May be null.
|
||||||
@ -112,14 +127,14 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors,
|
public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors,
|
||||||
int minLoadableRetryCount, Handler eventHandler,
|
int minLoadableRetryCount, Handler eventHandler,
|
||||||
ExtractorMediaSource.EventListener eventListener, MediaSource.Listener sourceListener,
|
ExtractorMediaSource.EventListener eventListener, Listener listener,
|
||||||
Allocator allocator, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
Allocator allocator, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.sourceListener = sourceListener;
|
this.listener = listener;
|
||||||
this.allocator = allocator;
|
this.allocator = allocator;
|
||||||
this.customCacheKey = customCacheKey;
|
this.customCacheKey = customCacheKey;
|
||||||
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
||||||
@ -376,8 +391,7 @@ import java.util.Arrays;
|
|||||||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
||||||
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
|
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
|
||||||
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
|
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
|
||||||
sourceListener.onSourceInfoRefreshed(
|
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
|
||||||
new SinglePeriodTimeline(durationUs, seekMap.isSeekable()), null);
|
|
||||||
}
|
}
|
||||||
callback.onContinueLoadingRequested(this);
|
callback.onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
@ -477,8 +491,7 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
tracks = new TrackGroupArray(trackArray);
|
tracks = new TrackGroupArray(trackArray);
|
||||||
prepared = true;
|
prepared = true;
|
||||||
sourceListener.onSourceInfoRefreshed(
|
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
|
||||||
new SinglePeriodTimeline(durationUs, seekMap.isSeekable()), null);
|
|
||||||
callback.onPrepared(this);
|
callback.onPrepared(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ import java.io.IOException;
|
|||||||
* <p>
|
* <p>
|
||||||
* Note that the built-in extractors for AAC, MPEG PS/TS and FLV streams do not support seeking.
|
* Note that the built-in extractors for AAC, MPEG PS/TS and FLV streams do not support seeking.
|
||||||
*/
|
*/
|
||||||
public final class ExtractorMediaSource implements MediaSource, MediaSource.Listener {
|
public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPeriod.Listener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener of {@link ExtractorMediaSource} events.
|
* Listener of {@link ExtractorMediaSource} events.
|
||||||
@ -89,8 +89,8 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
|
|||||||
private final int continueLoadingCheckIntervalBytes;
|
private final int continueLoadingCheckIntervalBytes;
|
||||||
|
|
||||||
private MediaSource.Listener sourceListener;
|
private MediaSource.Listener sourceListener;
|
||||||
private Timeline timeline;
|
private long timelineDurationUs;
|
||||||
private boolean timelineHasDuration;
|
private boolean timelineIsSeekable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param uri The {@link Uri} of the media stream.
|
* @param uri The {@link Uri} of the media stream.
|
||||||
@ -155,8 +155,7 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
|
|||||||
@Override
|
@Override
|
||||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
sourceListener = listener;
|
sourceListener = listener;
|
||||||
timeline = new SinglePeriodTimeline(C.TIME_UNSET, false);
|
notifySourceInfoRefreshed(C.TIME_UNSET, false);
|
||||||
listener.onSourceInfoRefreshed(timeline, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -182,19 +181,27 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
|
|||||||
sourceListener = null;
|
sourceListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MediaSource.Listener implementation.
|
// ExtractorMediaPeriod.Listener implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSourceInfoRefreshed(Timeline newTimeline, Object manifest) {
|
public void onSourceInfoRefreshed(long durationUs, boolean isSeekable) {
|
||||||
long newTimelineDurationUs = newTimeline.getPeriod(0, period).getDurationUs();
|
// If we already have the duration from a previous source info refresh, use it.
|
||||||
boolean newTimelineHasDuration = newTimelineDurationUs != C.TIME_UNSET;
|
durationUs = durationUs == C.TIME_UNSET ? timelineDurationUs : durationUs;
|
||||||
if (timelineHasDuration && !newTimelineHasDuration) {
|
if (timelineDurationUs == durationUs && timelineIsSeekable == isSeekable
|
||||||
// Suppress source info changes that would make the duration unknown when it is already known.
|
|| (timelineDurationUs != C.TIME_UNSET && durationUs == C.TIME_UNSET)) {
|
||||||
|
// Suppress no-op source info changes.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
timeline = newTimeline;
|
notifySourceInfoRefreshed(durationUs, isSeekable);
|
||||||
timelineHasDuration = newTimelineHasDuration;
|
}
|
||||||
sourceListener.onSourceInfoRefreshed(timeline, null);
|
|
||||||
|
// Internal methods.
|
||||||
|
|
||||||
|
private void notifySourceInfoRefreshed(long durationUs, boolean isSeekable) {
|
||||||
|
timelineDurationUs = durationUs;
|
||||||
|
timelineIsSeekable = isSeekable;
|
||||||
|
sourceListener.onSourceInfoRefreshed(
|
||||||
|
new SinglePeriodTimeline(timelineDurationUs, timelineIsSeekable), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user