From b321c8d3bd4169a7c900f35cdfbd39a0b57c732d Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 8 Jan 2025 07:44:36 -0800 Subject: [PATCH] Discard already written sample data for clipped DASH periods 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: androidx/media#1698 PiperOrigin-RevId: 713288221 --- RELEASENOTES.md | 3 ++ .../source/chunk/ChunkSampleStream.java | 33 +++++++++++++++++++ .../exoplayer/dash/DashMediaPeriod.java | 6 ++++ 3 files changed, 42 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7d789eb7de..6909025ec1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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.): diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java index 1b83ab70d2..392dcb4fc8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java @@ -722,6 +722,39 @@ public class ChunkSampleStream } } + /** + * 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()); diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index 11640b171f..8ae8dbc367 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -324,6 +324,12 @@ import java.util.regex.Pattern; @Override public void reevaluateBuffer(long positionUs) { + for (ChunkSampleStream sampleStream : sampleStreams) { + if (!sampleStream.isLoading()) { + long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); + sampleStream.discardUpstreamSamplesForClippedDuration(periodDurationUs); + } + } compositeSequenceableLoader.reevaluateBuffer(positionUs); }