Commit 2f4c9678 by Oliver Woodman

Refactor handling of period start times and durations.

- It's not possible to determine a period's duration just from the
corresponding Period element in a DASH manifest. It's necessary
to look at the start time of the next period (or the duration of the
manifest for the last period) to determine how long a period is. We
don't currently do this in the parser, and hence set duration incorrectly.
We also set period start times incorrectly because we don't set it to
equal to sum of the durations of prior periods in the case where it's
not explicitly defined.

- We're currently propagating these (incorrect) values all over the place
through data-structures that we build when parsing the Period element.

- This CL removes this redundancy, storing only the start time of each
period in Period elements, and not propagating it elsewhere. It's then
used when required in DashChunkSource.
parent cb85dc25
......@@ -188,7 +188,7 @@ public class DashRendererBuilder implements RendererBuilder {
}
private void buildRenderers() {
Period period = manifest.periods.get(0);
Period period = manifest.getPeriod(0);
Handler mainHandler = player.getMainHandler();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
......
......@@ -24,7 +24,6 @@ import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.ChunkSource;
import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.chunk.MultiTrackChunkSource;
import com.google.android.exoplayer.dash.DashChunkSource;
import com.google.android.exoplayer.dash.mpd.AdaptationSet;
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription;
......@@ -83,16 +82,16 @@ public class DashRendererBuilder implements ManifestCallback<MediaPresentationDe
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(null, null);
// Obtain Representations for playback.
ArrayList<Representation> audioRepresentationsList = new ArrayList<>();
Representation audioRepresentation = null;
ArrayList<Representation> videoRepresentationsList = new ArrayList<>();
Period period = manifest.periods.get(0);
Period period = manifest.getPeriod(0);
for (int i = 0; i < period.adaptationSets.size(); i++) {
AdaptationSet adaptationSet = period.adaptationSets.get(i);
int adaptationSetType = adaptationSet.type;
for (int j = 0; j < adaptationSet.representations.size(); j++) {
Representation representation = adaptationSet.representations.get(j);
if (adaptationSetType == AdaptationSet.TYPE_AUDIO) {
audioRepresentationsList.add(representation);
if (adaptationSetType == AdaptationSet.TYPE_AUDIO && audioRepresentation == null) {
audioRepresentation = representation;
} else if (adaptationSetType == AdaptationSet.TYPE_VIDEO) {
videoRepresentationsList.add(representation);
}
......@@ -109,7 +108,8 @@ public class DashRendererBuilder implements ManifestCallback<MediaPresentationDe
String mimeType = videoRepresentations[0].format.mimeType;
if (mimeType.equals(MimeTypes.VIDEO_WEBM)) {
videoChunkSource = new DashChunkSource(videoDataSource,
new AdaptiveEvaluator(bandwidthMeter), videoRepresentations);
new AdaptiveEvaluator(bandwidthMeter), manifest.getPeriodDuration(0),
videoRepresentations);
} else {
throw new IllegalStateException("Unexpected mime type: " + mimeType);
}
......@@ -120,21 +120,17 @@ public class DashRendererBuilder implements ManifestCallback<MediaPresentationDe
}
// Build the audio renderer.
MultiTrackChunkSource audioChunkSource = null;
TrackRenderer audioRenderer = null;
if (!audioRepresentationsList.isEmpty()) {
TrackRenderer audioRenderer;
if (audioRepresentation == null) {
audioRenderer = null;
} else {
DataSource audioDataSource = new DefaultUriDataSource(player, bandwidthMeter, userAgent);
ChunkSource[] audioChunkSources = new ChunkSource[audioRepresentationsList.size()];
FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator();
for (int i = 0; i < audioRepresentationsList.size(); i++) {
Representation representation = audioRepresentationsList.get(i);
audioChunkSources[i] = new DashChunkSource(audioDataSource,
audioEvaluator, representation);
}
audioChunkSource = new MultiTrackChunkSource(audioChunkSources);
DashChunkSource audioChunkSource = new DashChunkSource(audioDataSource, audioEvaluator,
manifest.getPeriodDuration(0), audioRepresentation);
SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE);
if (manifestUrl.contains("opus")) { // TODO: Need a better logic here.
if ("opus".equals(audioRepresentation.format.codecs)) {
audioRenderer = new LibopusAudioTrackRenderer(audioSampleSource);
} else {
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource);
......
......@@ -61,7 +61,6 @@ public class VideoPlayer extends Activity implements OnClickListener,
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 160;
private boolean isDash;
private String manifestUrl;
private boolean useOpenGL;
......
......@@ -30,12 +30,11 @@ public class RepresentationTest extends TestCase {
String uri = "http://www.google.com";
SegmentBase base = new SingleSegmentBase(new RangedUri(uri, null, 0, 1), 1, 0, uri, 1, 1);
Format format = new Format("0", MimeTypes.VIDEO_MP4, 1920, 1080, -1, 0, 0, 2500000);
Representation representation = Representation.newInstance(-1, -1, "test_stream_1", 3,
format, base);
Representation representation = Representation.newInstance("test_stream_1", 3, format, base);
assertEquals("test_stream_1.0.3", representation.getCacheKey());
format = new Format("150", MimeTypes.VIDEO_MP4, 1920, 1080, -1, 0, 0, 2500000);
representation = Representation.newInstance(-1, -1, "test_stream_1", -1, format, base);
representation = Representation.newInstance("test_stream_1", -1, format, base);
assertEquals("test_stream_1.150.-1", representation.getCacheKey());
}
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.dash;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.dash.mpd.RangedUri;
/**
......@@ -24,20 +25,22 @@ import com.google.android.exoplayer.dash.mpd.RangedUri;
*/
public interface DashSegmentIndex {
public static final int INDEX_UNBOUNDED = -1;
int INDEX_UNBOUNDED = -1;
/**
* Returns the segment number of the segment containing a given media time.
* <p>
* If the given media time is outside the range of the index, then the returned segment number is
* clamped to {@link #getFirstSegmentNum()} (if the given media time is earlier the start of the
* first segment) or {@link #getLastSegmentNum()} (if the given media time is later then the end
* of the last segment).
* first segment) or {@link #getLastSegmentNum(long)} (if the given media time is later then the
* end of the last segment).
*
* @param timeUs The time in microseconds.
* @param periodDurationUs The duration of the enclosing period in microseconds, or
* {@link C#UNKNOWN_TIME_US} if the period's duration is not yet known.
* @return The segment number of the corresponding segment.
*/
int getSegmentNum(long timeUs);
int getSegmentNum(long timeUs, long periodDurationUs);
/**
* Returns the start time of a segment.
......@@ -51,9 +54,11 @@ public interface DashSegmentIndex {
* Returns the duration of a segment.
*
* @param segmentNum The segment number.
* @param periodDurationUs The duration of the enclosing period in microseconds, or
* {@link C#UNKNOWN_TIME_US} if the period's duration is not yet known.
* @return The duration of the segment, in microseconds.
*/
long getDurationUs(int segmentNum);
long getDurationUs(int segmentNum, long periodDurationUs);
/**
* Returns a {@link RangedUri} defining the location of a segment.
......@@ -73,15 +78,15 @@ public interface DashSegmentIndex {
/**
* Returns the segment number of the last segment, or {@link #INDEX_UNBOUNDED}.
* <p>
* An unbounded index occurs if a live stream manifest uses SegmentTemplate elements without a
* SegmentTimeline element. In this case the manifest can be used to derive information about
* segments arbitrarily far into the future. This means that the manifest does not need to be
* refreshed as frequently (if at all) during playback, however it is necessary for a player to
* manually calculate the window of currently available segments.
* An unbounded index occurs if a dynamic manifest uses SegmentTemplate elements without a
* SegmentTimeline element, and if the period duration is not yet known. In this case the caller
* must manually determine the window of currently available segments.
*
* @param periodDurationUs The duration of the enclosing period in microseconds, or
* {@link C#UNKNOWN_TIME_US} if the period's duration is not yet known.
* @return The segment number of the last segment, or {@link #INDEX_UNBOUNDED}.
*/
int getLastSegmentNum();
int getLastSegmentNum(long periodDurationUs);
/**
* Returns true if segments are defined explicitly by the index.
......
......@@ -26,17 +26,14 @@ import com.google.android.exoplayer.extractor.ChunkIndex;
private final ChunkIndex chunkIndex;
private final String uri;
private final long startTimeUs;
/**
* @param chunkIndex The {@link ChunkIndex} to wrap.
* @param uri The URI where the data is located.
* @param startTimeUs The start time of the index, in microseconds.
*/
public DashWrappingSegmentIndex(ChunkIndex chunkIndex, String uri, long startTimeUs) {
public DashWrappingSegmentIndex(ChunkIndex chunkIndex, String uri) {
this.chunkIndex = chunkIndex;
this.uri = uri;
this.startTimeUs = startTimeUs;
}
@Override
......@@ -45,17 +42,17 @@ import com.google.android.exoplayer.extractor.ChunkIndex;
}
@Override
public int getLastSegmentNum() {
public int getLastSegmentNum(long periodDurationUs) {
return chunkIndex.length - 1;
}
@Override
public long getTimeUs(int segmentNum) {
return chunkIndex.timesUs[segmentNum] + startTimeUs;
return chunkIndex.timesUs[segmentNum];
}
@Override
public long getDurationUs(int segmentNum) {
public long getDurationUs(int segmentNum, long periodDurationUs) {
return chunkIndex.durationsUs[segmentNum];
}
......@@ -65,8 +62,8 @@ import com.google.android.exoplayer.extractor.ChunkIndex;
}
@Override
public int getSegmentNum(long timeUs) {
return chunkIndex.getChunkIndex(timeUs - startTimeUs);
public int getSegmentNum(long timeUs, long periodDurationUs) {
return chunkIndex.getChunkIndex(timeUs);
}
@Override
......
......@@ -22,34 +22,28 @@ import com.google.android.exoplayer.dash.DashSegmentIndex;
*/
/* package */ final class DashSingleSegmentIndex implements DashSegmentIndex {
private final long startTimeUs;
private final long durationUs;
private final RangedUri uri;
/**
* @param startTimeUs The start time of the segment, in microseconds.
* @param durationUs The duration of the segment, in microseconds.
* @param uri A {@link RangedUri} defining the location of the segment data.
*/
public DashSingleSegmentIndex(long startTimeUs, long durationUs, RangedUri uri) {
this.startTimeUs = startTimeUs;
this.durationUs = durationUs;
public DashSingleSegmentIndex(RangedUri uri) {
this.uri = uri;
}
@Override
public int getSegmentNum(long timeUs) {
public int getSegmentNum(long timeUs, long periodDurationUs) {
return 0;
}
@Override
public long getTimeUs(int segmentNum) {
return startTimeUs;
return 0;
}
@Override
public long getDurationUs(int segmentNum) {
return durationUs;
public long getDurationUs(int segmentNum, long periodDurationUs) {
return periodDurationUs;
}
@Override
......@@ -63,7 +57,7 @@ import com.google.android.exoplayer.dash.DashSegmentIndex;
}
@Override
public int getLastSegmentNum() {
public int getLastSegmentNum(long periodDurationUs) {
return 0;
}
......
......@@ -41,7 +41,7 @@ public class MediaPresentationDescription implements RedirectingManifest {
public final String location;
public final List<Period> periods;
private final List<Period> periods;
public MediaPresentationDescription(long availabilityStartTime, long duration, long minBufferTime,
boolean dynamic, long minUpdatePeriod, long timeShiftBufferDepth, UtcTimingElement utcTiming,
......@@ -54,12 +54,26 @@ public class MediaPresentationDescription implements RedirectingManifest {
this.timeShiftBufferDepth = timeShiftBufferDepth;
this.utcTiming = utcTiming;
this.location = location;
this.periods = Collections.unmodifiableList(periods);
this.periods = periods == null ? Collections.<Period>emptyList() : periods;
}
@Override
public String getNextManifestUri() {
public final String getNextManifestUri() {
return location;
}
public final int getPeriodCount() {
return periods.size();
}
public final Period getPeriod(int index) {
return periods.get(index);
}
public final long getPeriodDuration(int index) {
return index == periods.size() - 1
? (duration == -1 ? -1 : duration - periods.get(index).startMs)
: periods.get(index + 1).startMs - periods.get(index).startMs;
}
}
......@@ -34,11 +34,6 @@ public class Period {
public final long startMs;
/**
* The duration of the period in milliseconds, or -1 if the duration is unknown.
*/
public final long durationMs;
/**
* The adaptation sets belonging to the period.
*/
public final List<AdaptationSet> adaptationSets;
......@@ -46,13 +41,11 @@ public class Period {
/**
* @param id The period identifier. May be null.
* @param start The start time of the period in milliseconds.
* @param duration The duration of the period in milliseconds, or -1 if the duration is unknown.
* @param adaptationSets The adaptation sets belonging to the period.
*/
public Period(String id, long start, long duration, List<AdaptationSet> adaptationSets) {
public Period(String id, long start, List<AdaptationSet> adaptationSets) {
this.id = id;
this.startMs = start;
this.durationMs = duration;
this.adaptationSets = Collections.unmodifiableList(adaptationSets);
}
......
......@@ -35,7 +35,6 @@ public abstract class Representation implements FormatWrapper {
* {@link #contentId}, which should uniquely identify that video.
*/
public final String contentId;
/**
* Identifies the revision of the content.
* <p>
......@@ -45,22 +44,10 @@ public abstract class Representation implements FormatWrapper {
* timestamp at which the media was encoded is often a suitable.
*/
public final long revisionId;
/**
* The format of the representation.
*/
public final Format format;
/**
* The start time of the enclosing period in milliseconds since the epoch.
*/
public final long periodStartMs;
/**
* The duration of the enclosing period in milliseconds.
*/
public final long periodDurationMs;
/**
* The offset of the presentation timestamps in the media stream relative to media time.
*/
......@@ -71,33 +58,28 @@ public abstract class Representation implements FormatWrapper {
/**
* Constructs a new instance.
*
* @param periodStartMs The start time of the enclosing period in milliseconds.
* @param periodDurationMs The duration of the enclosing period in milliseconds, or -1 if the
* duration is unknown.
* @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content.
* @param format The format of the representation.
* @param segmentBase A segment base element for the representation.
* @return The constructed instance.
*/
public static Representation newInstance(long periodStartMs, long periodDurationMs,
String contentId, long revisionId, Format format, SegmentBase segmentBase) {
public static Representation newInstance(String contentId, long revisionId, Format format,
SegmentBase segmentBase) {
if (segmentBase instanceof SingleSegmentBase) {
return new SingleSegmentRepresentation(periodStartMs, periodDurationMs, contentId, revisionId,
format, (SingleSegmentBase) segmentBase, -1);
return new SingleSegmentRepresentation(contentId, revisionId, format,
(SingleSegmentBase) segmentBase, -1);
} else if (segmentBase instanceof MultiSegmentBase) {
return new MultiSegmentRepresentation(periodStartMs, periodDurationMs, contentId, revisionId,
format, (MultiSegmentBase) segmentBase);
return new MultiSegmentRepresentation(contentId, revisionId, format,
(MultiSegmentBase) segmentBase);
} else {
throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or "
+ "MultiSegmentBase");
}
}
private Representation(long periodStartMs, long periodDurationMs, String contentId,
long revisionId, Format format, SegmentBase segmentBase) {
this.periodStartMs = periodStartMs;
this.periodDurationMs = periodDurationMs;
private Representation(String contentId, long revisionId, Format format,
SegmentBase segmentBase) {
this.contentId = contentId;
this.revisionId = revisionId;
this.format = format;
......@@ -165,9 +147,6 @@ public abstract class Representation implements FormatWrapper {
private final DashSingleSegmentIndex segmentIndex;
/**
* @param periodStartMs The start time of the enclosing period in milliseconds.
* @param periodDurationMs The duration of the enclosing period in milliseconds, or -1 if the
* duration is unknown.
* @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content.
* @param format The format of the representation.
......@@ -178,37 +157,34 @@ public abstract class Representation implements FormatWrapper {
* @param indexEnd The offset of the last byte of index data.
* @param contentLength The content length, or -1 if unknown.
*/
public static SingleSegmentRepresentation newInstance(long periodStartMs, long periodDurationMs,
String contentId, long revisionId, Format format, String uri, long initializationStart,
long initializationEnd, long indexStart, long indexEnd, long contentLength) {
public static SingleSegmentRepresentation newInstance(String contentId, long revisionId,
Format format, String uri, long initializationStart, long initializationEnd,
long indexStart, long indexEnd, long contentLength) {
RangedUri rangedUri = new RangedUri(uri, null, initializationStart,
initializationEnd - initializationStart + 1);
SingleSegmentBase segmentBase = new SingleSegmentBase(rangedUri, 1, 0, uri, indexStart,
indexEnd - indexStart + 1);
return new SingleSegmentRepresentation(periodStartMs, periodDurationMs, contentId, revisionId,
return new SingleSegmentRepresentation(contentId, revisionId,
format, segmentBase, contentLength);
}
/**
* @param periodStartMs The start time of the enclosing period in milliseconds.
* @param periodDurationMs The duration of the enclosing period in milliseconds, or -1 if the
* duration is unknown.
* @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content.
* @param format The format of the representation.
* @param segmentBase The segment base underlying the representation.
* @param contentLength The content length, or -1 if unknown.
*/
public SingleSegmentRepresentation(long periodStartMs, long periodDurationMs, String contentId,
long revisionId, Format format, SingleSegmentBase segmentBase, long contentLength) {
super(periodStartMs, periodDurationMs, contentId, revisionId, format, segmentBase);
public SingleSegmentRepresentation(String contentId, long revisionId, Format format,
SingleSegmentBase segmentBase, long contentLength) {
super(contentId, revisionId, format, segmentBase);
this.uri = Uri.parse(segmentBase.uri);
this.indexUri = segmentBase.getIndex();
this.contentLength = contentLength;
// If we have an index uri then the index is defined externally, and we shouldn't return one
// directly. If we don't, then we can't do better than an index defining a single segment.
segmentIndex = indexUri != null ? null : new DashSingleSegmentIndex(periodStartMs * 1000,
periodDurationMs * 1000, new RangedUri(segmentBase.uri, null, 0, -1));
segmentIndex = indexUri != null ? null
: new DashSingleSegmentIndex(new RangedUri(segmentBase.uri, null, 0, contentLength));
}
@Override
......@@ -232,17 +208,14 @@ public abstract class Representation implements FormatWrapper {
private final MultiSegmentBase segmentBase;
/**
* @param periodStartMs The start time of the enclosing period in milliseconds.
* @param periodDurationMs The duration of the enclosing period in milliseconds, or -1 if the
* duration is unknown.
* @param contentId Identifies the piece of content to which this representation belongs.
* @param revisionId Identifies the revision of the content.
* @param format The format of the representation.
* @param segmentBase The segment base underlying the representation.
*/
public MultiSegmentRepresentation(long periodStartMs, long periodDurationMs, String contentId,
long revisionId, Format format, MultiSegmentBase segmentBase) {
super(periodStartMs, periodDurationMs, contentId, revisionId, format, segmentBase);
public MultiSegmentRepresentation(String contentId, long revisionId, Format format,
MultiSegmentBase segmentBase) {
super(contentId, revisionId, format, segmentBase);
this.segmentBase = segmentBase;
}
......@@ -264,18 +237,18 @@ public abstract class Representation implements FormatWrapper {
}
@Override
public int getSegmentNum(long timeUs) {
return segmentBase.getSegmentNum(timeUs - periodStartMs * 1000);
public int getSegmentNum(long timeUs, long periodDurationUs) {
return segmentBase.getSegmentNum(timeUs, periodDurationUs);
}
@Override
public long getTimeUs(int segmentIndex) {
return segmentBase.getSegmentTimeUs(segmentIndex) + periodStartMs * 1000;
return segmentBase.getSegmentTimeUs(segmentIndex);
}
@Override
public long getDurationUs(int segmentIndex) {
return segmentBase.getSegmentDurationUs(segmentIndex);
public long getDurationUs(int segmentIndex, long periodDurationUs) {
return segmentBase.getSegmentDurationUs(segmentIndex, periodDurationUs);
}
@Override
......@@ -284,8 +257,8 @@ public abstract class Representation implements FormatWrapper {
}
@Override
public int getLastSegmentNum() {
return segmentBase.getLastSegmentNum();
public int getLastSegmentNum(long periodDurationUs) {
return segmentBase.getLastSegmentNum(periodDurationUs);
}
@Override
......
......@@ -112,7 +112,6 @@ public abstract class SegmentBase {
*/
public abstract static class MultiSegmentBase extends SegmentBase {
/* package */ final long periodDurationMs;
/* package */ final int startNumber;
/* package */ final long duration;
/* package */ final List<SegmentTimelineElement> segmentTimeline;
......@@ -123,7 +122,6 @@ public abstract class SegmentBase {
* @param timescale The timescale in units per second.
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
* division of this value and {@code timescale}.
* @param periodDurationMs The duration of the enclosing period in milliseconds.
* @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If
......@@ -133,22 +131,20 @@ public abstract class SegmentBase {
* parameter.
*/
public MultiSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
long periodDurationMs, int startNumber, long duration,
List<SegmentTimelineElement> segmentTimeline) {
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline) {
super(initialization, timescale, presentationTimeOffset);
this.periodDurationMs = periodDurationMs;
this.startNumber = startNumber;
this.duration = duration;
this.segmentTimeline = segmentTimeline;
}
/**
* @see DashSegmentIndex#getSegmentNum(long)
* @see DashSegmentIndex#getSegmentNum(long, long)
*/
public int getSegmentNum(long timeUs) {
public int getSegmentNum(long timeUs, long periodDurationUs) {
final int firstSegmentNum = getFirstSegmentNum();
int lowIndex = firstSegmentNum;
int highIndex = getLastSegmentNum();
int highIndex = getLastSegmentNum(periodDurationUs);
if (segmentTimeline == null) {
// All segments are of equal duration (with the possible exception of the last one).
long durationUs = (duration * C.MICROS_PER_SECOND) / timescale;
......@@ -175,15 +171,15 @@ public abstract class SegmentBase {
}
/**
* @see DashSegmentIndex#getDurationUs(int)
* @see DashSegmentIndex#getDurationUs(int, long)
*/
public final long getSegmentDurationUs(int sequenceNumber) {
public final long getSegmentDurationUs(int sequenceNumber, long periodDurationUs) {
if (segmentTimeline != null) {
long duration = segmentTimeline.get(sequenceNumber - startNumber).duration;
return (duration * C.MICROS_PER_SECOND) / timescale;
} else {
return sequenceNumber == getLastSegmentNum()
? ((periodDurationMs * 1000) - getSegmentTimeUs(sequenceNumber))
return sequenceNumber == getLastSegmentNum(periodDurationUs)
? (periodDurationUs - getSegmentTimeUs(sequenceNumber))
: ((duration * C.MICROS_PER_SECOND) / timescale);
}
}
......@@ -218,9 +214,9 @@ public abstract class SegmentBase {
}
/**
* @see DashSegmentIndex#getLastSegmentNum()
* @see DashSegmentIndex#getLastSegmentNum(long)
*/
public abstract int getLastSegmentNum();
public abstract int getLastSegmentNum(long periodDurationUs);
/**
* @see DashSegmentIndex#isExplicit()
......@@ -244,7 +240,6 @@ public abstract class SegmentBase {
* @param timescale The timescale in units per second.
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
* division of this value and {@code timescale}.
* @param periodDurationMs The duration of the enclosing period in milliseconds.
* @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If
......@@ -255,10 +250,10 @@ public abstract class SegmentBase {
* @param mediaSegments A list of {@link RangedUri}s indicating the locations of the segments.
*/
public SegmentList(RangedUri initialization, long timescale, long presentationTimeOffset,
long periodDurationMs, int startNumber, long duration,
List<SegmentTimelineElement> segmentTimeline, List<RangedUri> mediaSegments) {
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
duration, segmentTimeline);
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline,
List<RangedUri> mediaSegments) {
super(initialization, timescale, presentationTimeOffset, startNumber, duration,
segmentTimeline);
this.mediaSegments = mediaSegments;
}
......@@ -268,7 +263,7 @@ public abstract class SegmentBase {
}
@Override
public int getLastSegmentNum() {
public int getLastSegmentNum(long periodDurationUs) {
return startNumber + mediaSegments.size() - 1;
}
......@@ -296,7 +291,6 @@ public abstract class SegmentBase {
* @param timescale The timescale in units per second.
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
* division of this value and {@code timescale}.
* @param periodDurationMs The duration of the enclosing period in milliseconds.
* @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If
......@@ -311,10 +305,9 @@ public abstract class SegmentBase {
* @param baseUrl A url to use as the base for relative urls generated by the templates.
*/
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset,
long periodDurationMs, int startNumber, long duration,
List<SegmentTimelineElement> segmentTimeline, UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate, String baseUrl) {
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline,
UrlTemplate initializationTemplate, UrlTemplate mediaTemplate, String baseUrl) {
super(initialization, timescale, presentationTimeOffset, startNumber,
duration, segmentTimeline);
this.initializationTemplate = initializationTemplate;
this.mediaTemplate = mediaTemplate;
......@@ -346,14 +339,14 @@ public abstract class SegmentBase {
}
@Override
public int getLastSegmentNum() {
public int getLastSegmentNum(long periodDurationUs) {
if (segmentTimeline != null) {
return segmentTimeline.size() + startNumber - 1;
} else if (periodDurationMs == -1) {
} else if (periodDurationUs == C.UNKNOWN_TIME_US) {
return DashSegmentIndex.INDEX_UNBOUNDED;
} else {
long durationMs = (duration * 1000) / timescale;
return startNumber + (int) Util.ceilDivide(periodDurationMs, durationMs) - 1;
long durationUs = (duration * C.MICROS_PER_SECOND) / timescale;
return startNumber + (int) Util.ceilDivide(periodDurationUs, durationUs) - 1;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment