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,
representation.getCacheKey());
long presentationTimeOffsetUs = representation.presentationTimeOffsetMs * 1000;
long presentationTimeOffsetUs = representation.presentationTimeOffsetUs;
if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) {
if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) {
// 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,
bandwidth, language, codecs);
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,

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.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.SingleSegmentBase;
@ -63,7 +64,7 @@ public abstract class Representation {
/**
* 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;
@ -101,7 +102,7 @@ public abstract class Representation {
this.revisionId = revisionId;
this.format = format;
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;
private final RangedUri indexUri;
private final DashSingleSegmentIndex segmentIndex;
/**
* @param periodStartMs The start time of the enclosing period in milliseconds.
@ -198,6 +200,10 @@ public abstract class Representation {
this.uri = 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(uri, null, 0, -1));
}
@Override
@ -207,7 +213,7 @@ public abstract class Representation {
@Override
public DashSegmentIndex getIndex() {
return null;
return segmentIndex;
}
}

View File

@ -56,6 +56,15 @@ public abstract class SegmentBase {
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.
*/
@ -87,8 +96,15 @@ public abstract class SegmentBase {
this.indexLength = indexLength;
}
/**
* @param uri The uri of the segment.
*/
public SingleSegmentBase(Uri uri) {
this(null, 1, 0, uri, 0, -1);
}
public RangedUri getIndex() {
return new RangedUri(uri, null, indexStart, indexLength);
return indexLength <= 0 ? null : new RangedUri(uri, null, indexStart, indexLength);
}
}