Handle non-indexed representations.

These may occur in VOD streams where a representation's data
is small enough not to require segmentation or an index. For
example subtitle files.

Issue: #268
This commit is contained in:
Oliver Woodman 2015-02-02 19:51:37 +00:00
parent d1fe33cdf8
commit 7b9b7c6e3b
5 changed files with 88 additions and 6 deletions

View File

@ -451,7 +451,7 @@ public class DashChunkSource implements ChunkSource {
DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length,
representation.getCacheKey()); representation.getCacheKey());
long presentationTimeOffsetUs = representation.presentationTimeOffsetMs * 1000; long presentationTimeOffsetUs = representation.presentationTimeOffsetUs;
if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) { if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) {
if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) { if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) {
// Update the VTT header. // Update the VTT header.

View File

@ -0,0 +1,60 @@
package com.google.android.exoplayer.dash;
import com.google.android.exoplayer.dash.mpd.RangedUri;
/**
* A {@link DashSegmentIndex} that defines a single segment.
*/
public 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;
this.uri = uri;
}
@Override
public int getSegmentNum(long timeUs) {
return 0;
}
@Override
public long getTimeUs(int segmentNum) {
return startTimeUs;
}
@Override
public long getDurationUs(int segmentNum) {
return durationUs;
}
@Override
public RangedUri getSegmentUrl(int segmentNum) {
return uri;
}
@Override
public int getFirstSegmentNum() {
return 0;
}
@Override
public int getLastSegmentNum() {
return 0;
}
@Override
public boolean isExplicit() {
return true;
}
}

View File

@ -305,7 +305,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
Format format = buildFormat(id, mimeType, width, height, numChannels, audioSamplingRate, Format format = buildFormat(id, mimeType, width, height, numChannels, audioSamplingRate,
bandwidth, language, codecs); bandwidth, language, codecs);
return buildRepresentation(periodStartMs, periodDurationMs, contentId, -1, format, return buildRepresentation(periodStartMs, periodDurationMs, contentId, -1, format,
segmentBase); segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl));
} }
protected Format buildFormat(String id, String mimeType, int width, int height, int numChannels, protected Format buildFormat(String id, String mimeType, int width, int height, int numChannels,

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.chunk.Format; import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.dash.DashSegmentIndex; import com.google.android.exoplayer.dash.DashSegmentIndex;
import com.google.android.exoplayer.dash.DashSingleSegmentIndex;
import com.google.android.exoplayer.dash.mpd.SegmentBase.MultiSegmentBase; import com.google.android.exoplayer.dash.mpd.SegmentBase.MultiSegmentBase;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase; import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
@ -63,7 +64,7 @@ public abstract class Representation {
/** /**
* 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.
*/ */
public final long presentationTimeOffsetMs; public final long presentationTimeOffsetUs;
private final RangedUri initializationUri; private final RangedUri initializationUri;
@ -101,7 +102,7 @@ public abstract class Representation {
this.revisionId = revisionId; this.revisionId = revisionId;
this.format = format; this.format = format;
initializationUri = segmentBase.getInitialization(this); initializationUri = segmentBase.getInitialization(this);
presentationTimeOffsetMs = (segmentBase.presentationTimeOffset * 1000) / segmentBase.timescale; presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs();
} }
/** /**
@ -156,6 +157,7 @@ public abstract class Representation {
public final long contentLength; public final long contentLength;
private final RangedUri indexUri; private final RangedUri indexUri;
private final DashSingleSegmentIndex segmentIndex;
/** /**
* @param periodStartMs The start time of the enclosing period in milliseconds. * @param periodStartMs The start time of the enclosing period in milliseconds.
@ -198,6 +200,10 @@ public abstract class Representation {
this.uri = segmentBase.uri; this.uri = 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
// 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(uri, null, 0, -1));
} }
@Override @Override
@ -207,7 +213,7 @@ public abstract class Representation {
@Override @Override
public DashSegmentIndex getIndex() { public DashSegmentIndex getIndex() {
return null; return segmentIndex;
} }
} }

View File

@ -56,6 +56,15 @@ public abstract class SegmentBase {
return initialization; return initialization;
} }
/**
* Gets the presentation time offset, in microseconds.
*
* @return The presentation time offset, in microseconds.
*/
public long getPresentationTimeOffsetUs() {
return Util.scaleLargeTimestamp(presentationTimeOffset, C.MICROS_PER_SECOND, timescale);
}
/** /**
* A {@link SegmentBase} that defines a single segment. * A {@link SegmentBase} that defines a single segment.
*/ */
@ -87,8 +96,15 @@ public abstract class SegmentBase {
this.indexLength = indexLength; this.indexLength = indexLength;
} }
/**
* @param uri The uri of the segment.
*/
public SingleSegmentBase(Uri uri) {
this(null, 1, 0, uri, 0, -1);
}
public RangedUri getIndex() { public RangedUri getIndex() {
return new RangedUri(uri, null, indexStart, indexLength); return indexLength <= 0 ? null : new RangedUri(uri, null, indexStart, indexLength);
} }
} }