mirror of
https://github.com/samsonjs/media.git
synced 2026-04-09 11:55:46 +00:00
Adjust periodEnded logic further
Periods that are not the last in the manifest are always ended and non-dynamic periods are also always ended by definition. Also, some periods may not be able to declare their final duration accurately and we need to account for some inaccuracy. This can be done by testing whether no further full segment within the duration can be expected.
This commit is contained in:
parent
88e921ced1
commit
71591782f0
4 changed files with 149 additions and 3 deletions
|
|
@ -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,10 +422,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
|
||||
long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs);
|
||||
long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(nowUnixTimeUs);
|
||||
if (manifest.dynamic) {
|
||||
if (isLastPeriodInDynamicManifest) {
|
||||
long lastAvailableSegmentEndTimeUs =
|
||||
representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum);
|
||||
periodEnded &= (lastAvailableSegmentEndTimeUs >= periodDurationUs);
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD
|
||||
type="dynamic"
|
||||
timeShiftBufferDepth="PT16S"
|
||||
minimumUpdatePeriod="PT4M"
|
||||
availabilityStartTime="2024-01-01T00:01:00Z">
|
||||
<UTCTiming
|
||||
schemeIdUri="urn:mpeg:dash:utc:direct:2014"
|
||||
value="2024-01-01T00:01:00Z" />
|
||||
<Period id="1" start="PT0S" duration="PT10S">
|
||||
<AdaptationSet id="0" contentType="video">
|
||||
<SegmentTemplate presentationTimeOffset="0" timescale="1000" startNumber="1" media="video_$Time$.m4s">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="4000" r="1"/>
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
<Representation id="0"/>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD
|
||||
type="dynamic"
|
||||
timeShiftBufferDepth="PT16S"
|
||||
minimumUpdatePeriod="PT4M"
|
||||
availabilityStartTime="2024-01-01T00:01:00Z">
|
||||
<UTCTiming
|
||||
schemeIdUri="urn:mpeg:dash:utc:direct:2014"
|
||||
value="2024-01-01T00:01:00Z" />
|
||||
<Period id="1" start="PT0S" duration="PT10S">
|
||||
<AdaptationSet id="0" contentType="video">
|
||||
<SegmentTemplate presentationTimeOffset="0" timescale="1000" startNumber="1" media="video_$Time$.m4s">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="4000"/>
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
<Representation id="0"/>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
Loading…
Reference in a new issue