From 294fa261b73ecf3cdb5f2f187ab8cb9648e29443 Mon Sep 17 00:00:00 2001 From: Tianyi Feng Date: Thu, 10 Aug 2023 12:09:43 +0000 Subject: [PATCH] Merge pull request #528 from zgzong:patch-2 PiperOrigin-RevId: 554869426 (cherry picked from commit ef543644782798a5c0bbdf1464b29e240f058324) --- .../dash/DefaultDashChunkSource.java | 14 ++- .../dash/DefaultDashChunkSourceTest.java | 99 +++++++++++++++++++ .../mpd/sample_mpd_live_known_duration_ended | 20 ++++ .../sample_mpd_live_known_duration_not_ended | 20 ++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_ended create mode 100644 libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_not_ended diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java index 096f3d2269..a2e967503b 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java @@ -410,7 +410,9 @@ public class DefaultDashChunkSource implements DashChunkSource { } long periodDurationUs = representationHolder.periodDurationUs; - boolean periodEnded = periodDurationUs != C.TIME_UNSET; + boolean isLastPeriodInDynamicManifest = + manifest.dynamic && periodIndex == manifest.getPeriodCount() - 1; + boolean periodEnded = !isLastPeriodInDynamicManifest || periodDurationUs != C.TIME_UNSET; if (representationHolder.getSegmentCount() == 0) { // The index doesn't define any segments. @@ -420,6 +422,16 @@ public class DefaultDashChunkSource implements DashChunkSource { long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs); long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(nowUnixTimeUs); + if (isLastPeriodInDynamicManifest) { + long lastAvailableSegmentEndTimeUs = + representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum); + long lastSegmentDurationUs = + lastAvailableSegmentEndTimeUs + - representationHolder.getSegmentStartTimeUs(lastAvailableSegmentNum); + // Account for some inaccuracy in the overall period duration value by assuming that the + // period is finished once no further full sample fits into the overall duration. + periodEnded &= (lastAvailableSegmentEndTimeUs + lastSegmentDurationUs >= periodDurationUs); + } long segmentNum = getSegmentNum( representationHolder, diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java index c19432cd38..c6387ed254 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java @@ -38,6 +38,7 @@ import androidx.media3.exoplayer.source.MediaLoadData; import androidx.media3.exoplayer.source.chunk.BundledChunkExtractor; import androidx.media3.exoplayer.source.chunk.Chunk; import androidx.media3.exoplayer.source.chunk.ChunkHolder; +import androidx.media3.exoplayer.source.chunk.MediaChunk; import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection; import androidx.media3.exoplayer.trackselection.FixedTrackSelection; import androidx.media3.exoplayer.upstream.CmcdConfiguration; @@ -414,6 +415,104 @@ public class DefaultDashChunkSourceTest { "key4=5.0"); } + @Test + public void + getNextChunk_afterLastAvailableButBeforeEndOfLiveManifestWithKnownDuration_doesNotReturnEndOfStream() + throws Exception { + DashManifest manifest = + new DashManifestParser() + .parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), + "media/mpd/sample_mpd_live_known_duration_not_ended")); + DefaultDashChunkSource chunkSource = + new DefaultDashChunkSource( + BundledChunkExtractor.FACTORY, + new LoaderErrorThrower.Placeholder(), + manifest, + new BaseUrlExclusionList(), + /* periodIndex= */ 0, + /* adaptationSetIndices= */ new int[] {0}, + new FixedTrackSelection(new TrackGroup(new Format.Builder().build()), /* track= */ 0), + C.TRACK_TYPE_VIDEO, + new FakeDataSource(), + /* elapsedRealtimeOffsetMs= */ 0, + /* maxSegmentsPerLoad= */ 1, + /* enableEventMessageTrack= */ false, + /* closedCaptionFormats= */ ImmutableList.of(), + /* playerTrackEmsgHandler= */ null, + PlayerId.UNSET, + /* cmcdConfiguration= */ null); + ChunkHolder output = new ChunkHolder(); + // Populate with last available media chunk + chunkSource.getNextChunk( + /* playbackPositionUs= */ 0, + /* loadPositionUs= */ 0, + /* queue= */ ImmutableList.of(), + output); + Chunk previousChunk = output.chunk; + output.clear(); + + // Request another chunk + chunkSource.getNextChunk( + /* playbackPositionUs= */ 0, + /* loadPositionUs= */ 4_000_000, + /* queue= */ ImmutableList.of((MediaChunk) previousChunk), + output); + + assertThat(output.endOfStream).isFalse(); + assertThat(output.chunk).isNull(); + } + + @Test + public void getNextChunk_atEndOfLiveManifestWithKnownDuration_returnsEndOfStream() + throws Exception { + DashManifest manifest = + new DashManifestParser() + .parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), + "media/mpd/sample_mpd_live_known_duration_ended")); + DefaultDashChunkSource chunkSource = + new DefaultDashChunkSource( + BundledChunkExtractor.FACTORY, + new LoaderErrorThrower.Placeholder(), + manifest, + new BaseUrlExclusionList(), + /* periodIndex= */ 0, + /* adaptationSetIndices= */ new int[] {0}, + new FixedTrackSelection(new TrackGroup(new Format.Builder().build()), /* track= */ 0), + C.TRACK_TYPE_VIDEO, + new FakeDataSource(), + /* elapsedRealtimeOffsetMs= */ 0, + /* maxSegmentsPerLoad= */ 1, + /* enableEventMessageTrack= */ false, + /* closedCaptionFormats= */ ImmutableList.of(), + /* playerTrackEmsgHandler= */ null, + PlayerId.UNSET, + /* cmcdConfiguration= */ null); + ChunkHolder output = new ChunkHolder(); + // Populate with last media chunk + chunkSource.getNextChunk( + /* playbackPositionUs= */ 0, + /* loadPositionUs= */ 4_000_000, + /* queue= */ ImmutableList.of(), + output); + Chunk previousChunk = output.chunk; + output.clear(); + + // Request next chunk + chunkSource.getNextChunk( + /* playbackPositionUs= */ 0, + /* loadPositionUs= */ 8_000_000, + /* queue= */ ImmutableList.of((MediaChunk) previousChunk), + output); + + assertThat(output.endOfStream).isTrue(); + } + private DashChunkSource createDashChunkSource( int numberOfTracks, @Nullable CmcdConfiguration cmcdConfiguration) throws IOException { Assertions.checkArgument(numberOfTracks < 6); diff --git a/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_ended b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_ended new file mode 100644 index 0000000000..cad0e8da01 --- /dev/null +++ b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_ended @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_not_ended b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_not_ended new file mode 100644 index 0000000000..ddf8882e87 --- /dev/null +++ b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_known_duration_not_ended @@ -0,0 +1,20 @@ + + + + + + + + + + + + + +