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