mirror of
https://github.com/androidx/media.git
synced 2025-05-11 01:31:40 +08:00
Align Chunk/HLS sample sources.
Having moved HLS to use single sample queues per track, these classes have become relatively similar. This CL aligns the two to make this more obvious. It remains unclear whether it'll ever be sensible to merge them; there are still some niggly complications for HLS. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=120429618
This commit is contained in:
parent
ae8f95f64d
commit
e390bdf98c
@ -51,31 +51,31 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
|
||||
|
||||
private final Loader loader;
|
||||
private final LoadControl loadControl;
|
||||
private final ChunkSource chunkSource;
|
||||
private final ChunkHolder nextChunkHolder;
|
||||
private final LinkedList<BaseMediaChunk> mediaChunks;
|
||||
private final List<BaseMediaChunk> readOnlyMediaChunks;
|
||||
private final DefaultTrackOutput sampleQueue;
|
||||
private final int bufferSizeContribution;
|
||||
private final ChunkHolder nextChunkHolder;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final LoadControl loadControl;
|
||||
|
||||
private boolean prepared;
|
||||
private long lastPreferredQueueSizeEvaluationTimeMs;
|
||||
private Format downstreamFormat;
|
||||
|
||||
private TrackGroupArray trackGroups;
|
||||
private boolean trackEnabled;
|
||||
private boolean pendingReset;
|
||||
private Format downstreamSampleFormat;
|
||||
|
||||
private long downstreamPositionUs;
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingResetPositionUs;
|
||||
private long lastPreferredQueueSizeEvaluationTimeMs;
|
||||
private boolean pendingReset;
|
||||
|
||||
private TrackGroupArray trackGroups;
|
||||
private long durationUs;
|
||||
private boolean loadingFinished;
|
||||
private boolean trackEnabled;
|
||||
private long currentLoadStartTimeMs;
|
||||
|
||||
private Chunk currentLoadable;
|
||||
private Format downstreamFormat;
|
||||
private Format downstreamSampleFormat;
|
||||
private long currentLoadStartTimeMs;
|
||||
private boolean loadingFinished;
|
||||
|
||||
/**
|
||||
* @param chunkSource A {@link ChunkSource} from which chunks to load are obtained.
|
||||
@ -139,7 +139,6 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
if (!chunkSource.prepare()) {
|
||||
return false;
|
||||
}
|
||||
durationUs = chunkSource.getDurationUs();
|
||||
TrackGroup tracks = chunkSource.getTracks();
|
||||
if (tracks != null) {
|
||||
trackGroups = new TrackGroupArray(tracks);
|
||||
@ -152,7 +151,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
|
||||
@Override
|
||||
public long getDurationUs() {
|
||||
return durationUs;
|
||||
return chunkSource.getDurationUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -222,9 +221,14 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
} else if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else {
|
||||
long largestQueuedTimestampUs = sampleQueue.getLargestQueuedTimestampUs();
|
||||
return largestQueuedTimestampUs == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestQueuedTimestampUs;
|
||||
long bufferedPositionUs = downstreamPositionUs;
|
||||
BaseMediaChunk lastMediaChunk = mediaChunks.getLast();
|
||||
BaseMediaChunk lastCompletedMediaChunk = lastMediaChunk != currentLoadable ? lastMediaChunk
|
||||
: mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null;
|
||||
if (lastCompletedMediaChunk != null) {
|
||||
bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs);
|
||||
}
|
||||
return Math.max(bufferedPositionUs, sampleQueue.getLargestQueuedTimestampUs());
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,8 +240,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
boolean seekInsideBuffer = !isPendingReset() && sampleQueue.skipToKeyframeBefore(positionUs);
|
||||
if (seekInsideBuffer) {
|
||||
// We succeeded. All we need to do is discard any chunks that we've moved past.
|
||||
boolean haveSamples = !sampleQueue.isEmpty();
|
||||
while (haveSamples && mediaChunks.size() > 1
|
||||
while (mediaChunks.size() > 1
|
||||
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
|
||||
mediaChunks.removeFirst();
|
||||
}
|
||||
@ -286,9 +289,8 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
boolean haveSamples = !sampleQueue.isEmpty();
|
||||
BaseMediaChunk currentChunk = mediaChunks.getFirst();
|
||||
while (haveSamples && mediaChunks.size() > 1
|
||||
while (mediaChunks.size() > 1
|
||||
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
|
||||
mediaChunks.removeFirst();
|
||||
currentChunk = mediaChunks.getFirst();
|
||||
@ -300,15 +302,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
downstreamFormat = currentChunk.format;
|
||||
}
|
||||
|
||||
Format sampleFormat = sampleQueue.getDownstreamFormat();
|
||||
if (sampleFormat != null && !sampleFormat.equals(downstreamSampleFormat)) {
|
||||
formatHolder.format = sampleFormat;
|
||||
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
||||
downstreamSampleFormat = sampleFormat;
|
||||
return FORMAT_READ;
|
||||
}
|
||||
|
||||
if (!haveSamples) {
|
||||
if (sampleQueue.isEmpty()) {
|
||||
if (loadingFinished) {
|
||||
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||
return BUFFER_READ;
|
||||
@ -316,6 +310,14 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
Format sampleFormat = sampleQueue.getDownstreamFormat();
|
||||
if (!sampleFormat.equals(downstreamSampleFormat)) {
|
||||
formatHolder.format = sampleFormat;
|
||||
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
||||
downstreamSampleFormat = sampleFormat;
|
||||
return FORMAT_READ;
|
||||
}
|
||||
|
||||
if (sampleQueue.readSample(buffer)) {
|
||||
if (buffer.timeUs < lastSeekPositionUs) {
|
||||
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||
@ -327,7 +329,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
// Loadable.Callback implementation.
|
||||
// Loader.Callback implementation.
|
||||
|
||||
@Override
|
||||
public void onLoadCompleted(Loadable loadable) {
|
||||
@ -451,12 +453,10 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
currentLoadStartTimeMs = now;
|
||||
currentLoadable = nextLoadable;
|
||||
if (isMediaChunk(currentLoadable)) {
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
BaseMediaChunk mediaChunk = (BaseMediaChunk) currentLoadable;
|
||||
mediaChunk.init(sampleQueue);
|
||||
mediaChunks.add(mediaChunk);
|
||||
if (isPendingReset()) {
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
}
|
||||
eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger,
|
||||
mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs);
|
||||
} else {
|
||||
@ -480,6 +480,14 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMediaChunk(Chunk chunk) {
|
||||
return chunk instanceof BaseMediaChunk;
|
||||
}
|
||||
|
||||
private boolean isPendingReset() {
|
||||
return pendingResetPositionUs != C.UNSET_TIME_US;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard upstream media chunks until the queue length is equal to the length specified.
|
||||
*
|
||||
@ -504,12 +512,4 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isMediaChunk(Chunk chunk) {
|
||||
return chunk instanceof BaseMediaChunk;
|
||||
}
|
||||
|
||||
private boolean isPendingReset() {
|
||||
return pendingResetPositionUs != C.UNSET_TIME_US;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
private final Loader loader;
|
||||
private final HlsChunkSource chunkSource;
|
||||
private final LinkedList<HlsMediaChunk> mediaChunks = new LinkedList<>();
|
||||
private final LinkedList<HlsMediaChunk> mediaChunks;
|
||||
private final HlsOutput output;
|
||||
private final int bufferSizeContribution;
|
||||
private final ChunkHolder nextChunkHolder;
|
||||
@ -70,7 +70,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
private boolean prepared;
|
||||
private boolean seenFirstTrackSelection;
|
||||
private int enabledTrackCount;
|
||||
|
||||
private DefaultTrackOutput[] sampleQueues;
|
||||
private Format downstreamFormat;
|
||||
|
||||
@ -87,18 +86,29 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingResetPositionUs;
|
||||
|
||||
private boolean loadingFinished;
|
||||
private Chunk currentLoadable;
|
||||
private HlsMediaChunk currentMediaChunkLoadable;
|
||||
private HlsMediaChunk previousMediaChunkLoadable;
|
||||
|
||||
private long currentLoadStartTimeMs;
|
||||
private boolean loadingFinished;
|
||||
|
||||
/**
|
||||
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
|
||||
* @param loadControl Controls when the source is permitted to load data.
|
||||
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
|
||||
*/
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, LoadControl loadControl,
|
||||
int bufferSizeContribution) {
|
||||
this(chunkSource, loadControl, bufferSizeContribution, null, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
|
||||
* @param loadControl Controls when the source is permitted to load data.
|
||||
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. 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 eventSourceId An identifier that gets passed to {@code eventListener} methods.
|
||||
*/
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, LoadControl loadControl,
|
||||
int bufferSizeContribution, Handler eventHandler,
|
||||
ChunkSampleSourceEventListener eventListener, int eventSourceId) {
|
||||
@ -106,17 +116,29 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
eventSourceId, DEFAULT_MIN_LOADABLE_RETRY_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
|
||||
* @param loadControl Controls when the source is permitted to load data.
|
||||
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. 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 eventSourceId An identifier that gets passed to {@code eventListener} methods.
|
||||
* @param minLoadableRetryCount The minimum number of times that the source should retry a load
|
||||
* before propagating an error.
|
||||
*/
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, LoadControl loadControl,
|
||||
int bufferSizeContribution, Handler eventHandler,
|
||||
ChunkSampleSourceEventListener eventListener, int eventSourceId, int minLoadableRetryCount) {
|
||||
this.chunkSource = chunkSource;
|
||||
this.loadControl = loadControl;
|
||||
this.bufferSizeContribution = bufferSizeContribution;
|
||||
this.pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
loader = new Loader("Loader:HLS", minLoadableRetryCount);
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId);
|
||||
output = new HlsOutput(loadControl.getAllocator());
|
||||
nextChunkHolder = new ChunkHolder();
|
||||
mediaChunks = new LinkedList<>();
|
||||
output = new HlsOutput(loadControl.getAllocator());
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
}
|
||||
|
||||
// SampleSource implementation.
|
||||
@ -225,16 +247,17 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else if (loadingFinished) {
|
||||
if (loadingFinished) {
|
||||
return C.END_OF_SOURCE_US;
|
||||
} else if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else {
|
||||
long bufferedPositionUs = downstreamPositionUs;
|
||||
if (previousMediaChunkLoadable != null) {
|
||||
// Buffered position should be at least as large as the end time of the previously loaded
|
||||
// chunk.
|
||||
bufferedPositionUs = Math.max(previousMediaChunkLoadable.endTimeUs, bufferedPositionUs);
|
||||
HlsMediaChunk lastMediaChunk = mediaChunks.getLast();
|
||||
HlsMediaChunk lastCompletedMediaChunk = lastMediaChunk != currentLoadable ? lastMediaChunk
|
||||
: mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null;
|
||||
if (lastCompletedMediaChunk != null) {
|
||||
bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs);
|
||||
}
|
||||
for (DefaultTrackOutput sampleQueue : sampleQueues) {
|
||||
bufferedPositionUs = Math.max(bufferedPositionUs,
|
||||
@ -258,7 +281,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
loader.release();
|
||||
}
|
||||
|
||||
// TrackStream methods.
|
||||
// TrackStream implementation.
|
||||
|
||||
/* package */ boolean isReady(int group) {
|
||||
Assertions.checkState(groupEnabledStates[group]);
|
||||
@ -325,16 +348,13 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
@Override
|
||||
public void onLoadCompleted(Loadable loadable) {
|
||||
Assertions.checkState(loadable == currentLoadable);
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
long loadDurationMs = now - currentLoadStartTimeMs;
|
||||
chunkSource.onChunkLoadCompleted(currentLoadable);
|
||||
if (isMediaChunk(currentLoadable)) {
|
||||
Assertions.checkState(currentLoadable == currentMediaChunkLoadable);
|
||||
previousMediaChunkLoadable = currentMediaChunkLoadable;
|
||||
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), currentMediaChunkLoadable.type,
|
||||
currentMediaChunkLoadable.trigger, currentMediaChunkLoadable.format,
|
||||
currentMediaChunkLoadable.startTimeUs, currentMediaChunkLoadable.endTimeUs, now,
|
||||
HlsMediaChunk mediaChunk = (HlsMediaChunk) currentLoadable;
|
||||
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), mediaChunk.type,
|
||||
mediaChunk.trigger, mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs, now,
|
||||
loadDurationMs);
|
||||
} else {
|
||||
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), currentLoadable.type,
|
||||
@ -358,14 +378,19 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
@Override
|
||||
public int onLoadError(Loadable loadable, IOException e) {
|
||||
long bytesLoaded = currentLoadable.bytesLoaded();
|
||||
boolean cancelable = !isMediaChunk(currentLoadable) || bytesLoaded == 0;
|
||||
boolean isMediaChunk = isMediaChunk(currentLoadable);
|
||||
boolean cancelable = !isMediaChunk || bytesLoaded == 0;
|
||||
if (chunkSource.onChunkLoadError(currentLoadable, cancelable, e)) {
|
||||
if (isMediaChunk) {
|
||||
HlsMediaChunk removed = mediaChunks.removeLast();
|
||||
Assertions.checkState(removed == currentLoadable);
|
||||
if (mediaChunks.isEmpty()) {
|
||||
pendingResetPositionUs = lastSeekPositionUs;
|
||||
}
|
||||
}
|
||||
clearCurrentLoadable();
|
||||
eventDispatcher.loadError(e);
|
||||
eventDispatcher.loadCanceled(bytesLoaded);
|
||||
clearCurrentLoadable();
|
||||
if (previousMediaChunkLoadable == null && !isPendingReset()) {
|
||||
pendingResetPositionUs = lastSeekPositionUs;
|
||||
}
|
||||
maybeStartLoading();
|
||||
return Loader.DONT_RETRY;
|
||||
} else {
|
||||
@ -534,14 +559,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
private void clearState() {
|
||||
mediaChunks.clear();
|
||||
output.clear();
|
||||
mediaChunks.clear();
|
||||
clearCurrentLoadable();
|
||||
previousMediaChunkLoadable = null;
|
||||
}
|
||||
|
||||
private void clearCurrentLoadable() {
|
||||
currentMediaChunkLoadable = null;
|
||||
currentLoadable = null;
|
||||
}
|
||||
|
||||
@ -552,7 +575,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
return;
|
||||
}
|
||||
|
||||
chunkSource.getNextChunk(previousMediaChunkLoadable,
|
||||
chunkSource.getNextChunk(mediaChunks.isEmpty() ? null : mediaChunks.getLast(),
|
||||
pendingResetPositionUs != C.UNSET_TIME_US ? pendingResetPositionUs : downstreamPositionUs,
|
||||
nextChunkHolder);
|
||||
boolean endOfStream = nextChunkHolder.endOfStream;
|
||||
@ -574,15 +597,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
currentLoadStartTimeMs = SystemClock.elapsedRealtime();
|
||||
currentLoadable = nextLoadable;
|
||||
if (isMediaChunk(currentLoadable)) {
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
HlsMediaChunk mediaChunk = (HlsMediaChunk) currentLoadable;
|
||||
if (isPendingReset()) {
|
||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||
}
|
||||
mediaChunk.init(output);
|
||||
mediaChunks.addLast(mediaChunk);
|
||||
eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger,
|
||||
mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs);
|
||||
currentMediaChunkLoadable = mediaChunk;
|
||||
} else {
|
||||
eventDispatcher.loadStarted(currentLoadable.dataSpec.length, currentLoadable.type,
|
||||
currentLoadable.trigger, currentLoadable.format, -1, -1);
|
||||
@ -602,8 +622,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
} else {
|
||||
return loadingFinished ? C.UNSET_TIME_US : (currentMediaChunkLoadable != null
|
||||
? currentMediaChunkLoadable.endTimeUs : previousMediaChunkLoadable.endTimeUs);
|
||||
return loadingFinished ? C.UNSET_TIME_US : mediaChunks.getLast().endTimeUs;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user