Add utility classes for SegmentTemplate/SegmentList refactor.
A step towards supporting SegmentTemplate style MPDs.
This commit is contained in:
parent
058333565d
commit
d7d14037b8
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a range of data located at a {@link Uri}.
|
||||||
|
*/
|
||||||
|
public final class RangedUri {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (zero based) index of the first byte of the range.
|
||||||
|
*/
|
||||||
|
public final long start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the range, or -1 to indicate that the range is unbounded.
|
||||||
|
*/
|
||||||
|
public final long length;
|
||||||
|
|
||||||
|
// The {@link Uri} is stored internally in two parts, {@link #baseUri} and {@link uriString}.
|
||||||
|
// This helps optimize memory usage in the same way that DASH manifests allow many URLs to be
|
||||||
|
// expressed concisely in the form of a single BaseURL and many relative paths. Note that this
|
||||||
|
// optimization relies on the same {@code Uri} being passed as the {@link #baseUri} to many
|
||||||
|
// instances of this class.
|
||||||
|
private final Uri baseUri;
|
||||||
|
private final String stringUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an ranged uri.
|
||||||
|
* <p>
|
||||||
|
* The uri is built according to the following rules:
|
||||||
|
* <ul>
|
||||||
|
* <li>If {@code baseUri} is null or if {@code stringUri} is absolute, then {@code baseUri} is
|
||||||
|
* ignored and the url consists solely of {@code stringUri}.
|
||||||
|
* <li>If {@code stringUri} is null, then the url consists solely of {@code baseUrl}.
|
||||||
|
* <li>Otherwise, the url consists of the concatenation of {@code baseUri} and {@code stringUri}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param baseUri An uri that can form the base of the uri defined by the instance.
|
||||||
|
* @param stringUri A relative or absolute uri in string form.
|
||||||
|
* @param start The (zero based) index of the first byte of the range.
|
||||||
|
* @param length The length of the range, or -1 to indicate that the range is unbounded.
|
||||||
|
*/
|
||||||
|
public RangedUri(Uri baseUri, String stringUri, long start, long length) {
|
||||||
|
Assertions.checkArgument(baseUri != null || stringUri != null);
|
||||||
|
this.baseUri = baseUri;
|
||||||
|
this.stringUri = stringUri;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Uri} represented by the instance.
|
||||||
|
*
|
||||||
|
* @return The {@link Uri} represented by the instance.
|
||||||
|
*/
|
||||||
|
public Uri getUri() {
|
||||||
|
if (stringUri == null) {
|
||||||
|
return baseUri;
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(stringUri);
|
||||||
|
if (!uri.isAbsolute() && baseUri != null) {
|
||||||
|
uri = Uri.withAppendedPath(baseUri, stringUri);
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A template from which URLs can be built.
|
||||||
|
* <p>
|
||||||
|
* URLs are built according to the substitution rules defined in ISO/IEC 23009-1:2014 5.3.9.4.4.
|
||||||
|
*/
|
||||||
|
public final class UrlTemplate {
|
||||||
|
|
||||||
|
private static final String REPRESENTATION = "RepresentationID";
|
||||||
|
private static final String NUMBER = "Number";
|
||||||
|
private static final String BANDWIDTH = "Bandwidth";
|
||||||
|
private static final String TIME = "Time";
|
||||||
|
private static final String ESCAPED_DOLLAR = "$$";
|
||||||
|
private static final String DEFAULT_FORMAT_TAG = "%01d";
|
||||||
|
|
||||||
|
private static final int REPRESENTATION_ID = 1;
|
||||||
|
private static final int NUMBER_ID = 2;
|
||||||
|
private static final int BANDWIDTH_ID = 3;
|
||||||
|
private static final int TIME_ID = 4;
|
||||||
|
|
||||||
|
private final String[] urlPieces;
|
||||||
|
private final int[] identifiers;
|
||||||
|
private final String[] identifierFormatTags;
|
||||||
|
private final int identifierCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile an instance from the provided template string.
|
||||||
|
*
|
||||||
|
* @param template The template.
|
||||||
|
* @return The compiled instance.
|
||||||
|
* @throws IllegalArgumentException If the template string is malformed.
|
||||||
|
*/
|
||||||
|
public static UrlTemplate compile(String template) {
|
||||||
|
// These arrays are sizes assuming each of the four possible identifiers will be present at
|
||||||
|
// most once in the template, which seems like a reasonable assumption.
|
||||||
|
String[] urlPieces = new String[5];
|
||||||
|
int[] identifiers = new int[4];
|
||||||
|
String[] identifierFormatTags = new String[4];
|
||||||
|
int identifierCount = parseTemplate(template, urlPieces, identifiers, identifierFormatTags);
|
||||||
|
return new UrlTemplate(urlPieces, identifiers, identifierFormatTags, identifierCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal constructor. Use {@link #compile(String)} to build instances of this class.
|
||||||
|
*/
|
||||||
|
private UrlTemplate(String[] urlPieces, int[] identifiers, String[] identifierFormatTags,
|
||||||
|
int identifierCount) {
|
||||||
|
this.urlPieces = urlPieces;
|
||||||
|
this.identifiers = identifiers;
|
||||||
|
this.identifierFormatTags = identifierFormatTags;
|
||||||
|
this.identifierCount = identifierCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a Uri from the template, substituting in the provided arguments.
|
||||||
|
* <p>
|
||||||
|
* Arguments whose corresponding identifiers are not present in the template will be ignored.
|
||||||
|
*
|
||||||
|
* @param representationId The representation identifier.
|
||||||
|
* @param segmentNumber The segment number.
|
||||||
|
* @param bandwidth The bandwidth.
|
||||||
|
* @param time The time as specified by the segment timeline.
|
||||||
|
* @return The built Uri.
|
||||||
|
*/
|
||||||
|
public String buildUri(String representationId, int segmentNumber, int bandwidth, long time) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < identifierCount; i++) {
|
||||||
|
builder.append(urlPieces[i]);
|
||||||
|
if (identifiers[i] == REPRESENTATION_ID) {
|
||||||
|
builder.append(representationId);
|
||||||
|
} else if (identifiers[i] == NUMBER_ID) {
|
||||||
|
builder.append(String.format(identifierFormatTags[i], segmentNumber));
|
||||||
|
} else if (identifiers[i] == BANDWIDTH_ID) {
|
||||||
|
builder.append(String.format(identifierFormatTags[i], bandwidth));
|
||||||
|
} else if (identifiers[i] == TIME_ID) {
|
||||||
|
builder.append(String.format(identifierFormatTags[i], time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append(urlPieces[identifierCount]);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses {@code template}, placing the decomposed components into the provided arrays.
|
||||||
|
* <p>
|
||||||
|
* If the return value is N, {@code urlPieces} will contain (N+1) strings that must be
|
||||||
|
* interleaved with N arguments in order to construct a url. The N identifiers that correspond to
|
||||||
|
* the required arguments, together with the tags that define their required formatting, are
|
||||||
|
* returned in {@code identifiers} and {@code identifierFormatTags} respectively.
|
||||||
|
*
|
||||||
|
* @param template The template to parse.
|
||||||
|
* @param urlPieces A holder for pieces of url parsed from the template.
|
||||||
|
* @param identifiers A holder for identifiers parsed from the template.
|
||||||
|
* @param identifierFormatTags A holder for format tags corresponding to the parsed identifiers.
|
||||||
|
* @return The number of identifiers in the template url.
|
||||||
|
* @throws IllegalArgumentException If the template string is malformed.
|
||||||
|
*/
|
||||||
|
private static int parseTemplate(String template, String[] urlPieces, int[] identifiers,
|
||||||
|
String[] identifierFormatTags) {
|
||||||
|
urlPieces[0] = "";
|
||||||
|
int templateIndex = 0;
|
||||||
|
int identifierCount = 0;
|
||||||
|
while (templateIndex < template.length()) {
|
||||||
|
int dollarIndex = template.indexOf("$", templateIndex);
|
||||||
|
if (dollarIndex == -1) {
|
||||||
|
urlPieces[identifierCount] += template.substring(templateIndex);
|
||||||
|
templateIndex = template.length();
|
||||||
|
} else if (dollarIndex != templateIndex) {
|
||||||
|
urlPieces[identifierCount] += template.substring(templateIndex, dollarIndex);
|
||||||
|
templateIndex = dollarIndex;
|
||||||
|
} else if (template.startsWith(ESCAPED_DOLLAR, templateIndex)) {
|
||||||
|
urlPieces[identifierCount] += "$";
|
||||||
|
templateIndex += 2;
|
||||||
|
} else {
|
||||||
|
int secondIndex = template.indexOf("$", templateIndex + 1);
|
||||||
|
String identifier = template.substring(templateIndex + 1, secondIndex);
|
||||||
|
if (identifier.equals(REPRESENTATION)) {
|
||||||
|
identifiers[identifierCount] = REPRESENTATION_ID;
|
||||||
|
} else {
|
||||||
|
int formatTagIndex = identifier.indexOf("%0");
|
||||||
|
String formatTag = DEFAULT_FORMAT_TAG;
|
||||||
|
if (formatTagIndex != -1) {
|
||||||
|
formatTag = identifier.substring(formatTagIndex);
|
||||||
|
if (!formatTag.endsWith("d")) {
|
||||||
|
formatTag += "d";
|
||||||
|
}
|
||||||
|
identifier = identifier.substring(0, formatTagIndex);
|
||||||
|
}
|
||||||
|
if (identifier.equals(NUMBER)) {
|
||||||
|
identifiers[identifierCount] = NUMBER_ID;
|
||||||
|
} else if (identifier.equals(BANDWIDTH)) {
|
||||||
|
identifiers[identifierCount] = BANDWIDTH_ID;
|
||||||
|
} else if (identifier.equals(TIME)) {
|
||||||
|
identifiers[identifierCount] = TIME_ID;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid template: " + template);
|
||||||
|
}
|
||||||
|
identifierFormatTags[identifierCount] = formatTag;
|
||||||
|
}
|
||||||
|
identifierCount++;
|
||||||
|
urlPieces[identifierCount] = "";
|
||||||
|
templateIndex = secondIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identifierCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -103,6 +103,7 @@ import java.nio.ByteBuffer;
|
|||||||
* <li>{@link EbmlReader#readBytes(NonBlockingInputStream, ByteBuffer, int)}.
|
* <li>{@link EbmlReader#readBytes(NonBlockingInputStream, ByteBuffer, int)}.
|
||||||
* <li>{@link EbmlReader#skipBytes(NonBlockingInputStream, int)}.
|
* <li>{@link EbmlReader#skipBytes(NonBlockingInputStream, int)}.
|
||||||
* <li>{@link EbmlReader#getBytesRead()}.
|
* <li>{@link EbmlReader#getBytesRead()}.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param id The integer ID of this element
|
* @param id The integer ID of this element
|
||||||
* @param elementOffsetBytes The byte offset where this element starts
|
* @param elementOffsetBytes The byte offset where this element starts
|
||||||
|
Loading…
x
Reference in New Issue
Block a user