From 7b9b7c6e3b69831a1a8826ce49efc860e048b74d Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 2 Feb 2015 19:51:37 +0000 Subject: [PATCH] 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 --- .../exoplayer/dash/DashChunkSource.java | 2 +- .../dash/DashSingleSegmentIndex.java | 60 +++++++++++++++++++ .../MediaPresentationDescriptionParser.java | 2 +- .../exoplayer/dash/mpd/Representation.java | 12 +++- .../exoplayer/dash/mpd/SegmentBase.java | 18 +++++- 5 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer/dash/DashSingleSegmentIndex.java diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index dc7ae4aad3..2a1d768048 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -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. diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashSingleSegmentIndex.java b/library/src/main/java/com/google/android/exoplayer/dash/DashSingleSegmentIndex.java new file mode 100644 index 0000000000..726b3d47e0 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashSingleSegmentIndex.java @@ -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; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java index edc73e2284..67806bbb68 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java @@ -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, diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/Representation.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/Representation.java index fa58775f27..afae71de23 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/Representation.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/Representation.java @@ -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; } } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java index ab12ff2b9c..f93ce33743 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java @@ -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); } }