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:
parent
d1fe33cdf8
commit
7b9b7c6e3b
@ -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.
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user