mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Avoid time discontinuity when retrying a live stream.
Issue: #227 Issue: #389
This commit is contained in:
parent
faf0e2c147
commit
55e08ff712
@ -80,6 +80,10 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
|||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
private long pendingResetPositionUs;
|
private long pendingResetPositionUs;
|
||||||
|
|
||||||
|
private boolean havePendingNextSampleUs;
|
||||||
|
private long pendingNextSampleUs;
|
||||||
|
private long sampleTimeOffsetUs;
|
||||||
|
|
||||||
private Loader loader;
|
private Loader loader;
|
||||||
private ExtractingLoadable loadable;
|
private ExtractingLoadable loadable;
|
||||||
private IOException currentLoadableException;
|
private IOException currentLoadableException;
|
||||||
@ -235,6 +239,12 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
|||||||
if (sampleQueue.getSample(sampleHolder)) {
|
if (sampleQueue.getSample(sampleHolder)) {
|
||||||
boolean decodeOnly = frameAccurateSeeking && sampleHolder.timeUs < lastSeekPositionUs;
|
boolean decodeOnly = frameAccurateSeeking && sampleHolder.timeUs < lastSeekPositionUs;
|
||||||
sampleHolder.flags |= decodeOnly ? C.SAMPLE_FLAG_DECODE_ONLY : 0;
|
sampleHolder.flags |= decodeOnly ? C.SAMPLE_FLAG_DECODE_ONLY : 0;
|
||||||
|
if (havePendingNextSampleUs) {
|
||||||
|
// Set the offset to make the timestamp of this sample equal to pendingNextSampleUs.
|
||||||
|
sampleTimeOffsetUs = pendingNextSampleUs - sampleHolder.timeUs;
|
||||||
|
havePendingNextSampleUs = false;
|
||||||
|
}
|
||||||
|
sampleHolder.timeUs += sampleTimeOffsetUs;
|
||||||
return SAMPLE_READ;
|
return SAMPLE_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,16 +402,27 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
|||||||
long elapsedMillis = SystemClock.elapsedRealtime() - currentLoadableExceptionTimestamp;
|
long elapsedMillis = SystemClock.elapsedRealtime() - currentLoadableExceptionTimestamp;
|
||||||
if (elapsedMillis >= getRetryDelayMillis(currentLoadableExceptionCount)) {
|
if (elapsedMillis >= getRetryDelayMillis(currentLoadableExceptionCount)) {
|
||||||
currentLoadableException = null;
|
currentLoadableException = null;
|
||||||
if (!prepared || !seekMap.isSeekable()) {
|
if (!prepared) {
|
||||||
// One of two cases applies:
|
// We don't know whether we're playing an on-demand or a live stream. For a live stream
|
||||||
// 1. We're not prepared. We don't know whether we're playing an on-demand or a live
|
// we need to load from the start, as outlined below. Since we might be playing a live
|
||||||
// stream. Play it safe and start from scratch.
|
// stream, play it safe and load from the start.
|
||||||
// 2. We're playing a non-seekable stream. Assume it's a live stream. In such cases it's
|
|
||||||
// best to discard the pending buffer and start from scratch.
|
|
||||||
for (int i = 0; i < sampleQueues.size(); i++) {
|
for (int i = 0; i < sampleQueues.size(); i++) {
|
||||||
sampleQueues.valueAt(i).clear();
|
sampleQueues.valueAt(i).clear();
|
||||||
}
|
}
|
||||||
loadable = createPreparationLoadable();
|
loadable = createLoadableFromStart();
|
||||||
|
} else if (!seekMap.isSeekable()) {
|
||||||
|
// We're playing a non-seekable stream. Assume it's live, and therefore that the data at
|
||||||
|
// the uri is a continuously shifting window of the latest available media. For this case
|
||||||
|
// there's no way to continue loading from where a previous load finished, and hence it's
|
||||||
|
// necessary to load from the start whenever commencing a new load.
|
||||||
|
for (int i = 0; i < sampleQueues.size(); i++) {
|
||||||
|
sampleQueues.valueAt(i).clear();
|
||||||
|
}
|
||||||
|
loadable = createLoadableFromStart();
|
||||||
|
// To avoid introducing a discontinuity, we shift the sample timestamps so that they will
|
||||||
|
// continue from the current downstream position.
|
||||||
|
pendingNextSampleUs = downstreamPositionUs;
|
||||||
|
havePendingNextSampleUs = true;
|
||||||
} else {
|
} else {
|
||||||
// We're playing a seekable on-demand stream. Resume the current loadable, which will
|
// We're playing a seekable on-demand stream. Resume the current loadable, which will
|
||||||
// request data starting from the point it left off.
|
// request data starting from the point it left off.
|
||||||
@ -411,11 +432,17 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're not retrying, so we're either starting a playback or responding to an explicit seek.
|
||||||
|
// In both cases sampleTimeOffsetUs should be reset to zero, and any pending adjustment to
|
||||||
|
// sample timestamps should be discarded.
|
||||||
|
sampleTimeOffsetUs = 0;
|
||||||
|
havePendingNextSampleUs = false;
|
||||||
|
|
||||||
if (!prepared) {
|
if (!prepared) {
|
||||||
loadable = createPreparationLoadable();
|
loadable = createLoadableFromStart();
|
||||||
} else {
|
} else {
|
||||||
Assertions.checkState(isPendingReset());
|
Assertions.checkState(isPendingReset());
|
||||||
loadable = createLoadableForPosition(pendingResetPositionUs);
|
loadable = createLoadableFromPositionUs(pendingResetPositionUs);
|
||||||
pendingResetPositionUs = NO_RESET_PENDING;
|
pendingResetPositionUs = NO_RESET_PENDING;
|
||||||
}
|
}
|
||||||
loader.startLoading(loadable, this);
|
loader.startLoading(loadable, this);
|
||||||
@ -441,11 +468,11 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractingLoadable createPreparationLoadable() {
|
private ExtractingLoadable createLoadableFromStart() {
|
||||||
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize, 0);
|
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractingLoadable createLoadableForPosition(long positionUs) {
|
private ExtractingLoadable createLoadableFromPositionUs(long positionUs) {
|
||||||
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize,
|
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize,
|
||||||
seekMap.getPosition(positionUs));
|
seekMap.getPosition(positionUs));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user