Skip to content

Commit

Permalink
Discard already written sample data for clipped DASH periods
Browse files Browse the repository at this point in the history
DASH periods can have a duration that is less than the end of the
last chunk in the period. In these cases, the sample data needs to
be clipped to the declared period duration. This already happens
IF the period duration is known at the point where we start loading
the media chunk. However, if the duration becomes known later or is
reduced (e.g. in a live stream), the existing media chunks are not
clipped. This causes unclean transitions across periods where the
player tries to transition to the next period, but renderers struggle
to output all the remaining surplus samples that should have been
clipped.

This can be fixed by asking ChunkSampleStream to discard surplus
samples that were loaded beyond a clipped duration when evaluating
the sample queue between chunk loads.

Issue: #1698
PiperOrigin-RevId: 713288221
  • Loading branch information
tonihei authored and copybara-github committed Jan 8, 2025
1 parent bb3b85a commit b321c8d
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
* Parse `scte214:supplementalCodecs` attribute from DASH manifest to
detect Dolby Vision formats
([#1785](https://github.com/androidx/media/pull/1785)).
* Improve handling of period transitions in live streams where the period
contains media samples beyond the declared period duration
([#1698](https://github.com/androidx/media/issues/1698)).
* Smooth Streaming Extension:
* RTSP Extension:
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,39 @@ public boolean consumeInitialDiscontinuity() {
}
}

/**
* Discards upstream samples that exceed the given clipped duration of the stream.
*
* @param clippedDurationUs The clipped duration of the stream in microseconds, or {@link
* C#TIME_UNSET} if not known.
*/
public void discardUpstreamSamplesForClippedDuration(long clippedDurationUs) {
Assertions.checkState(!loader.isLoading());
if (isPendingReset() || clippedDurationUs == C.TIME_UNSET || mediaChunks.isEmpty()) {
return;
}
BaseMediaChunk lastMediaChunk = getLastMediaChunk();
long lastMediaChunkEndTimeUs =
lastMediaChunk.clippedEndTimeUs != C.TIME_UNSET
? lastMediaChunk.clippedEndTimeUs
: lastMediaChunk.endTimeUs;
if (lastMediaChunkEndTimeUs <= clippedDurationUs) {
// Last chunk doesn't need to be clipped further.
return;
}
long largestQueuedTimestampUs = primarySampleQueue.getLargestQueuedTimestampUs();
if (largestQueuedTimestampUs <= clippedDurationUs) {
// No data beyond new duration that needs to be clipped.
return;
}
primarySampleQueue.discardUpstreamFrom(clippedDurationUs);
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.discardUpstreamFrom(clippedDurationUs);
}
mediaSourceEventDispatcher.upstreamDiscarded(
primaryTrackType, clippedDurationUs, largestQueuedTimestampUs);
}

private void discardUpstream(int preferredQueueSize) {
Assertions.checkState(!loader.isLoading());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ public void discardBuffer(long positionUs, boolean toKeyframe) {

@Override
public void reevaluateBuffer(long positionUs) {
for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) {
if (!sampleStream.isLoading()) {
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
sampleStream.discardUpstreamSamplesForClippedDuration(periodDurationUs);
}
}
compositeSequenceableLoader.reevaluateBuffer(positionUs);
}

Expand Down

0 comments on commit b321c8d

Please sign in to comment.