mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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