mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Merge pull request #528 from zgzong:patch-2
PiperOrigin-RevId: 554869426
This commit is contained in:
commit
ef54364478
4 changed files with 152 additions and 1 deletions
|
|
@ -410,7 +410,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
long periodDurationUs = representationHolder.periodDurationUs;
|
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) {
|
if (representationHolder.getSegmentCount() == 0) {
|
||||||
// The index doesn't define any segments.
|
// The index doesn't define any segments.
|
||||||
|
|
@ -420,6 +422,16 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||||
|
|
||||||
long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs);
|
long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs);
|
||||||
long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(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 =
|
long segmentNum =
|
||||||
getSegmentNum(
|
getSegmentNum(
|
||||||
representationHolder,
|
representationHolder,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import androidx.media3.exoplayer.source.MediaLoadData;
|
||||||
import androidx.media3.exoplayer.source.chunk.BundledChunkExtractor;
|
import androidx.media3.exoplayer.source.chunk.BundledChunkExtractor;
|
||||||
import androidx.media3.exoplayer.source.chunk.Chunk;
|
import androidx.media3.exoplayer.source.chunk.Chunk;
|
||||||
import androidx.media3.exoplayer.source.chunk.ChunkHolder;
|
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.AdaptiveTrackSelection;
|
||||||
import androidx.media3.exoplayer.trackselection.FixedTrackSelection;
|
import androidx.media3.exoplayer.trackselection.FixedTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||||
|
|
@ -414,6 +415,104 @@ public class DefaultDashChunkSourceTest {
|
||||||
"key4=5.0");
|
"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(
|
private DashChunkSource createDashChunkSource(
|
||||||
int numberOfTracks, @Nullable CmcdConfiguration cmcdConfiguration) throws IOException {
|
int numberOfTracks, @Nullable CmcdConfiguration cmcdConfiguration) throws IOException {
|
||||||
Assertions.checkArgument(numberOfTracks < 6);
|
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