commit
553a1d2ec1
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.dash;
|
||||
|
||||
import com.google.android.exoplayer.dash.mpd.RangedUri;
|
||||
|
||||
/**
|
||||
* Indexes the segments within a media stream.
|
||||
*
|
||||
* TODO: Generalize to cover all chunk streaming modes (e.g. SmoothStreaming) if possible.
|
||||
*/
|
||||
public interface DashSegmentIndex {
|
||||
|
||||
/**
|
||||
* Returns the segment number of the segment containing a given media time.
|
||||
*
|
||||
* @param timeUs The time in microseconds.
|
||||
* @return The segment number of the corresponding segment.
|
||||
*/
|
||||
int getSegmentNum(long timeUs);
|
||||
|
||||
/**
|
||||
* Returns the start time of a segment.
|
||||
*
|
||||
* @param segmentNum The segment number.
|
||||
* @return The corresponding start time in microseconds.
|
||||
*/
|
||||
long getTimeUs(int segmentNum);
|
||||
|
||||
/**
|
||||
* Returns the duration of a segment.
|
||||
*
|
||||
* @param segmentNum The segment number.
|
||||
* @return The duration of the segment, in microseconds.
|
||||
*/
|
||||
long getDurationUs(int segmentNum);
|
||||
|
||||
/**
|
||||
* Returns a {@link RangedUri} defining the location of a segment.
|
||||
*
|
||||
* @param segmentNum The segment number.
|
||||
* @return The {@link RangedUri} defining the location of the data.
|
||||
*/
|
||||
RangedUri getSegmentUrl(int segmentNum);
|
||||
|
||||
/**
|
||||
* Returns the segment number of the first segment.
|
||||
*
|
||||
* @return The segment number of the first segment.
|
||||
*/
|
||||
int getFirstSegmentNum();
|
||||
|
||||
/**
|
||||
* Returns the segment number of the last segment.
|
||||
*
|
||||
* @return The segment number of the last segment.
|
||||
*/
|
||||
int getLastSegmentNum();
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.dash;
|
||||
|
||||
import com.google.android.exoplayer.dash.mpd.RangedUri;
|
||||
import com.google.android.exoplayer.parser.SegmentIndex;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* An implementation of {@link DashSegmentIndex} that wraps a {@link SegmentIndex} parsed from a
|
||||
* media stream.
|
||||
*/
|
||||
public class DashWrappingSegmentIndex implements DashSegmentIndex {
|
||||
|
||||
private final SegmentIndex segmentIndex;
|
||||
private final Uri uri;
|
||||
private final long indexAnchor;
|
||||
|
||||
/**
|
||||
* @param segmentIndex The {@link SegmentIndex} to wrap.
|
||||
* @param uri The {@link Uri} where the data is located.
|
||||
* @param indexAnchor The index anchor point. This value is added to the byte offsets specified
|
||||
* in the wrapped {@link SegmentIndex}.
|
||||
*/
|
||||
public DashWrappingSegmentIndex(SegmentIndex segmentIndex, Uri uri, long indexAnchor) {
|
||||
this.segmentIndex = segmentIndex;
|
||||
this.uri = uri;
|
||||
this.indexAnchor = indexAnchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstSegmentNum() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastSegmentNum() {
|
||||
return segmentIndex.length - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeUs(int segmentNum) {
|
||||
return segmentIndex.timesUs[segmentNum];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDurationUs(int segmentNum) {
|
||||
return segmentIndex.durationsUs[segmentNum];
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangedUri getSegmentUrl(int segmentNum) {
|
||||
return new RangedUri(uri, null, indexAnchor + segmentIndex.offsets[segmentNum],
|
||||
segmentIndex.sizes[segmentNum]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSegmentNum(long timeUs) {
|
||||
return Util.binarySearchFloor(segmentIndex.timesUs, timeUs, true, true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.dash.mpd;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An approximate representation of a SegmentBase manifest element.
|
||||
*/
|
||||
public abstract class SegmentBase {
|
||||
|
||||
/* package */ final RangedUri initialization;
|
||||
/* package */ final long timescale;
|
||||
/* package */ final long presentationTimeOffset;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
* exists.
|
||||
* @param timescale The timescale in units per second.
|
||||
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
|
||||
* division of this value and {@code timescale}.
|
||||
*/
|
||||
public SegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset) {
|
||||
this.initialization = initialization;
|
||||
this.timescale = timescale;
|
||||
this.presentationTimeOffset = presentationTimeOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RangedUri} defining the location of initialization data for a given
|
||||
* representation. May be null if no initialization data exists.
|
||||
*
|
||||
* @param representation The {@link Representation} for which initialization data is required.
|
||||
* @return A {@link RangedUri} defining the location of the initialization data, or null.
|
||||
*/
|
||||
public RangedUri getInitialization(Representation representation) {
|
||||
return initialization;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link SegmentBase} that defines a single segment.
|
||||
*/
|
||||
public static class SingleSegmentBase extends SegmentBase {
|
||||
|
||||
/**
|
||||
* The uri of the segment.
|
||||
*/
|
||||
public final Uri uri;
|
||||
|
||||
/* package */ final long indexStart;
|
||||
/* package */ final long indexLength;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
* exists.
|
||||
* @param timescale The timescale in units per second.
|
||||
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
|
||||
* division of this value and {@code timescale}.
|
||||
* @param uri The uri of the segment.
|
||||
* @param indexStart The byte offset of the index data in the segment.
|
||||
* @param indexLength The length of the index data in bytes.
|
||||
*/
|
||||
public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
Uri uri, long indexStart, long indexLength) {
|
||||
super(initialization, timescale, presentationTimeOffset);
|
||||
this.uri = uri;
|
||||
this.indexStart = indexStart;
|
||||
this.indexLength = indexLength;
|
||||
}
|
||||
|
||||
public RangedUri getIndex() {
|
||||
return new RangedUri(uri, null, indexStart, indexLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link SegmentBase} that consists of multiple segments.
|
||||
*/
|
||||
public abstract static class MultiSegmentBase extends SegmentBase {
|
||||
|
||||
/* package */ final long periodDurationMs;
|
||||
/* package */ final int startNumber;
|
||||
/* package */ final long duration;
|
||||
/* package */ final List<SegmentTimelineElement> segmentTimeline;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
* exists.
|
||||
* @param timescale The timescale in units per second.
|
||||
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
|
||||
* 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 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
|
||||
* {@code segmentTimeline} is non-null then this parameter is ignored.
|
||||
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then
|
||||
* segments are assumed to be of fixed duration as specified by the {@code duration}
|
||||
* parameter.
|
||||
*/
|
||||
public MultiSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
long periodDurationMs, int startNumber, long duration,
|
||||
List<SegmentTimelineElement> segmentTimeline) {
|
||||
super(initialization, timescale, presentationTimeOffset);
|
||||
this.periodDurationMs = periodDurationMs;
|
||||
this.startNumber = startNumber;
|
||||
this.duration = duration;
|
||||
this.segmentTimeline = segmentTimeline;
|
||||
}
|
||||
|
||||
public final int getSegmentNum(long timeUs) {
|
||||
// TODO: Optimize this
|
||||
int index = startNumber;
|
||||
while (index + 1 <= getLastSegmentNum()) {
|
||||
if (getSegmentTimeUs(index + 1) <= timeUs) {
|
||||
index++;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public final long getSegmentDurationUs(int sequenceNumber) {
|
||||
if (segmentTimeline != null) {
|
||||
return (segmentTimeline.get(sequenceNumber - startNumber).duration * 1000000) / timescale;
|
||||
} else {
|
||||
return sequenceNumber == getLastSegmentNum()
|
||||
? (periodDurationMs * 1000) - getSegmentTimeUs(sequenceNumber)
|
||||
: ((duration * 1000000L) / timescale);
|
||||
}
|
||||
}
|
||||
|
||||
public final long getSegmentTimeUs(int sequenceNumber) {
|
||||
long unscaledSegmentTime;
|
||||
if (segmentTimeline != null) {
|
||||
unscaledSegmentTime = segmentTimeline.get(sequenceNumber - startNumber).startTime
|
||||
- presentationTimeOffset;
|
||||
} else {
|
||||
unscaledSegmentTime = (sequenceNumber - startNumber) * duration;
|
||||
}
|
||||
return (unscaledSegmentTime * 1000000) / timescale;
|
||||
}
|
||||
|
||||
public abstract RangedUri getSegmentUrl(Representation representation, int index);
|
||||
|
||||
public int getFirstSegmentNum() {
|
||||
return startNumber;
|
||||
}
|
||||
|
||||
public abstract int getLastSegmentNum();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MultiSegmentBase} that uses a SegmentList to define its segments.
|
||||
*/
|
||||
public static class SegmentList extends MultiSegmentBase {
|
||||
|
||||
/* package */ final List<RangedUri> mediaSegments;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
* exists.
|
||||
* @param timescale The timescale in units per second.
|
||||
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
|
||||
* 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 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
|
||||
* {@code segmentTimeline} is non-null then this parameter is ignored.
|
||||
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then
|
||||
* segments are assumed to be of fixed duration as specified by the {@code duration}
|
||||
* parameter.
|
||||
* @param mediaSegments A list of {@link RangedUri}s indicating the locations of the segments.
|
||||
*/
|
||||
public SegmentList(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
long periodDurationMs, int startNumber, long duration,
|
||||
List<SegmentTimelineElement> segmentTimeline, List<RangedUri> mediaSegments) {
|
||||
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
|
||||
duration, segmentTimeline);
|
||||
this.mediaSegments = mediaSegments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangedUri getSegmentUrl(Representation representation, int sequenceNumber) {
|
||||
return mediaSegments.get(sequenceNumber - startNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastSegmentNum() {
|
||||
return startNumber + mediaSegments.size() - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MultiSegmentBase} that uses a SegmentTemplate to define its segments.
|
||||
*/
|
||||
public static class SegmentTemplate extends MultiSegmentBase {
|
||||
|
||||
/* package */ final UrlTemplate initializationTemplate;
|
||||
/* package */ final UrlTemplate mediaTemplate;
|
||||
|
||||
private final Uri baseUrl;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
* exists. The value of this parameter is ignored if {@code initializationTemplate} is
|
||||
* non-null.
|
||||
* @param timescale The timescale in units per second.
|
||||
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
|
||||
* 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 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
|
||||
* {@code segmentTimeline} is non-null then this parameter is ignored.
|
||||
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then
|
||||
* segments are assumed to be of fixed duration as specified by the {@code duration}
|
||||
* parameter.
|
||||
* @param initializationTemplate A template defining the location of initialization data, if
|
||||
* such data exists. If non-null then the {@code initialization} parameter is ignored. If
|
||||
* null then {@code initialization} will be used.
|
||||
* @param mediaTemplate A template defining the location of each media segment.
|
||||
* @param baseUrl A url to use as the base for relative urls generated by the templates.
|
||||
*/
|
||||
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
long periodDurationMs, int startNumber, long duration,
|
||||
List<SegmentTimelineElement> segmentTimeline, UrlTemplate initializationTemplate,
|
||||
UrlTemplate mediaTemplate, Uri baseUrl) {
|
||||
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
|
||||
duration, segmentTimeline);
|
||||
this.initializationTemplate = initializationTemplate;
|
||||
this.mediaTemplate = mediaTemplate;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangedUri getInitialization(Representation representation) {
|
||||
if (initializationTemplate != null) {
|
||||
String urlString = initializationTemplate.buildUri(representation.format.id, 0,
|
||||
representation.format.bitrate, 0);
|
||||
return new RangedUri(baseUrl, urlString, 0, -1);
|
||||
} else {
|
||||
return super.getInitialization(representation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangedUri getSegmentUrl(Representation representation, int sequenceNumber) {
|
||||
long time = 0;
|
||||
if (segmentTimeline != null) {
|
||||
time = segmentTimeline.get(sequenceNumber - startNumber).startTime;
|
||||
} else {
|
||||
time = (sequenceNumber - startNumber) * duration;
|
||||
}
|
||||
String uriString = mediaTemplate.buildUri(representation.format.id, sequenceNumber,
|
||||
representation.format.bitrate, time);
|
||||
return new RangedUri(baseUrl, uriString, 0, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastSegmentNum() {
|
||||
if (segmentTimeline != null) {
|
||||
return segmentTimeline.size() + startNumber - 1;
|
||||
} else {
|
||||
long durationMs = (duration * 1000) / timescale;
|
||||
return startNumber + (int) (periodDurationMs / durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a timeline segment from the MPD's SegmentTimeline list.
|
||||
*/
|
||||
public static class SegmentTimelineElement {
|
||||
|
||||
/* package */ long startTime;
|
||||
/* package */ long duration;
|
||||
|
||||
/**
|
||||
* @param startTime The start time of the element. The value in seconds is the division of this
|
||||
* value and the {@code timescale} of the enclosing element.
|
||||
* @param duration The duration of the element. The value in seconds is the division of this
|
||||
* value and the {@code timescale} of the enclosing element.
|
||||
*/
|
||||
public SegmentTimelineElement(long startTime, long duration) {
|
||||
this.startTime = startTime;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user