Merge pull request #574 from hugohlln:main

PiperOrigin-RevId: 570037211
(cherry picked from commit b06d82323865870e5c3572e867d3cc165200e497)
This commit is contained in:
Copybara-Service 2023-10-02 05:36:28 -07:00 committed by oceanjules
parent ecfddb9aeb
commit 948491f665
3 changed files with 68 additions and 44 deletions

View File

@ -24,6 +24,8 @@
* Cronet Extension: * Cronet Extension:
* RTMP Extension: * RTMP Extension:
* HLS Extension: * HLS Extension:
* DASH Extension:
* Allow multiple of the same DASH identifier in segment template url.
* Smooth Streaming Extension: * Smooth Streaming Extension:
* RTSP Extension: * RTSP Extension:
* Decoder Extensions (FFmpeg, VP9, AV1, etc.): * Decoder Extensions (FFmpeg, VP9, AV1, etc.):

View File

@ -16,6 +16,8 @@
package androidx.media3.exoplayer.dash.manifest; package androidx.media3.exoplayer.dash.manifest;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
@ -38,10 +40,9 @@ public final class UrlTemplate {
private static final int BANDWIDTH_ID = 3; private static final int BANDWIDTH_ID = 3;
private static final int TIME_ID = 4; private static final int TIME_ID = 4;
private final String[] urlPieces; private final List<String> urlPieces;
private final int[] identifiers; private final List<Integer> identifiers;
private final String[] identifierFormatTags; private final List<String> identifierFormatTags;
private final int identifierCount;
/** /**
* Compile an instance from the provided template string. * Compile an instance from the provided template string.
@ -51,22 +52,20 @@ public final class UrlTemplate {
* @throws IllegalArgumentException If the template string is malformed. * @throws IllegalArgumentException If the template string is malformed.
*/ */
public static UrlTemplate compile(String template) { public static UrlTemplate compile(String template) {
// These arrays are sizes assuming each of the four possible identifiers will be present at List<String> urlPieces = new ArrayList<>();
// most once in the template, which seems like a reasonable assumption. List<Integer> identifiers = new ArrayList<>();
String[] urlPieces = new String[5]; List<String> identifierFormatTags = new ArrayList<>();
int[] identifiers = new int[4];
String[] identifierFormatTags = new String[4]; parseTemplate(template, urlPieces, identifiers, identifierFormatTags);
int identifierCount = parseTemplate(template, urlPieces, identifiers, identifierFormatTags); return new UrlTemplate(urlPieces, identifiers, identifierFormatTags);
return new UrlTemplate(urlPieces, identifiers, identifierFormatTags, identifierCount);
} }
/** Internal constructor. Use {@link #compile(String)} to build instances of this class. */ /** Internal constructor. Use {@link #compile(String)} to build instances of this class. */
private UrlTemplate( private UrlTemplate(
String[] urlPieces, int[] identifiers, String[] identifierFormatTags, int identifierCount) { List<String> urlPieces, List<Integer> identifiers, List<String> identifierFormatTags) {
this.urlPieces = urlPieces; this.urlPieces = urlPieces;
this.identifiers = identifiers; this.identifiers = identifiers;
this.identifierFormatTags = identifierFormatTags; this.identifierFormatTags = identifierFormatTags;
this.identifierCount = identifierCount;
} }
/** /**
@ -82,58 +81,65 @@ public final class UrlTemplate {
*/ */
public String buildUri(String representationId, long segmentNumber, int bandwidth, long time) { public String buildUri(String representationId, long segmentNumber, int bandwidth, long time) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i = 0; i < identifierCount; i++) { for (int i = 0; i < identifiers.size(); i++) {
builder.append(urlPieces[i]); builder.append(urlPieces.get(i));
if (identifiers[i] == REPRESENTATION_ID) { if (identifiers.get(i) == REPRESENTATION_ID) {
builder.append(representationId); builder.append(representationId);
} else if (identifiers[i] == NUMBER_ID) { } else if (identifiers.get(i) == NUMBER_ID) {
builder.append(String.format(Locale.US, identifierFormatTags[i], segmentNumber)); builder.append(String.format(Locale.US, identifierFormatTags.get(i), segmentNumber));
} else if (identifiers[i] == BANDWIDTH_ID) { } else if (identifiers.get(i) == BANDWIDTH_ID) {
builder.append(String.format(Locale.US, identifierFormatTags[i], bandwidth)); builder.append(String.format(Locale.US, identifierFormatTags.get(i), bandwidth));
} else if (identifiers[i] == TIME_ID) { } else if (identifiers.get(i) == TIME_ID) {
builder.append(String.format(Locale.US, identifierFormatTags[i], time)); builder.append(String.format(Locale.US, identifierFormatTags.get(i), time));
} }
} }
builder.append(urlPieces[identifierCount]); builder.append(urlPieces.get(identifiers.size()));
return builder.toString(); return builder.toString();
} }
/** /**
* Parses {@code template}, placing the decomposed components into the provided arrays. * Parses {@code template}, placing the decomposed components into the provided lists.
* *
* <p>If the return value is N, {@code urlPieces} will contain (N+1) strings that must be * <p>If the number of identifiers in the {@code template} is N, {@code urlPieces} will contain
* interleaved with N arguments in order to construct a url. The N identifiers that correspond to * (N+1) strings that must be interleaved with those N arguments in order to construct a url. The
* the required arguments, together with the tags that define their required formatting, are * N identifiers that correspond to the required arguments, together with the tags that define
* returned in {@code identifiers} and {@code identifierFormatTags} respectively. * their required formatting, are returned in {@code identifiers} and {@code identifierFormatTags}
* respectively.
* *
* @param template The template to parse. * @param template The template to parse.
* @param urlPieces A holder for pieces of url parsed from the template. * @param urlPieces A holder for pieces of url parsed from the template.
* @param identifiers A holder for identifiers 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. * @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. * @throws IllegalArgumentException If the template string is malformed.
*/ */
private static int parseTemplate( private static void parseTemplate(
String template, String[] urlPieces, int[] identifiers, String[] identifierFormatTags) { String template,
urlPieces[0] = ""; List<String> urlPieces,
List<Integer> identifiers,
List<String> identifierFormatTags) {
urlPieces.add("");
int templateIndex = 0; int templateIndex = 0;
int identifierCount = 0;
while (templateIndex < template.length()) { while (templateIndex < template.length()) {
int dollarIndex = template.indexOf("$", templateIndex); int dollarIndex = template.indexOf("$", templateIndex);
if (dollarIndex == -1) { if (dollarIndex == -1) {
urlPieces[identifierCount] += template.substring(templateIndex); urlPieces.set(
identifiers.size(),
urlPieces.get(identifiers.size()) + template.substring(templateIndex));
templateIndex = template.length(); templateIndex = template.length();
} else if (dollarIndex != templateIndex) { } else if (dollarIndex != templateIndex) {
urlPieces[identifierCount] += template.substring(templateIndex, dollarIndex); urlPieces.set(
identifiers.size(),
urlPieces.get(identifiers.size()) + template.substring(templateIndex, dollarIndex));
templateIndex = dollarIndex; templateIndex = dollarIndex;
} else if (template.startsWith(ESCAPED_DOLLAR, templateIndex)) { } else if (template.startsWith(ESCAPED_DOLLAR, templateIndex)) {
urlPieces[identifierCount] += "$"; urlPieces.set(identifiers.size(), urlPieces.get(identifiers.size()) + "$");
templateIndex += 2; templateIndex += 2;
} else { } else {
identifierFormatTags.add("");
int secondIndex = template.indexOf("$", templateIndex + 1); int secondIndex = template.indexOf("$", templateIndex + 1);
String identifier = template.substring(templateIndex + 1, secondIndex); String identifier = template.substring(templateIndex + 1, secondIndex);
if (identifier.equals(REPRESENTATION)) { if (identifier.equals(REPRESENTATION)) {
identifiers[identifierCount] = REPRESENTATION_ID; identifiers.add(REPRESENTATION_ID);
} else { } else {
int formatTagIndex = identifier.indexOf("%0"); int formatTagIndex = identifier.indexOf("%0");
String formatTag = DEFAULT_FORMAT_TAG; String formatTag = DEFAULT_FORMAT_TAG;
@ -149,24 +155,22 @@ public final class UrlTemplate {
} }
switch (identifier) { switch (identifier) {
case NUMBER: case NUMBER:
identifiers[identifierCount] = NUMBER_ID; identifiers.add(NUMBER_ID);
break; break;
case BANDWIDTH: case BANDWIDTH:
identifiers[identifierCount] = BANDWIDTH_ID; identifiers.add(BANDWIDTH_ID);
break; break;
case TIME: case TIME:
identifiers[identifierCount] = TIME_ID; identifiers.add(TIME_ID);
break; break;
default: default:
throw new IllegalArgumentException("Invalid template: " + template); throw new IllegalArgumentException("Invalid template: " + template);
} }
identifierFormatTags[identifierCount] = formatTag; identifierFormatTags.set(identifiers.size() - 1, formatTag);
} }
identifierCount++; urlPieces.add("");
urlPieces[identifierCount] = "";
templateIndex = secondIndex + 1; templateIndex = secondIndex + 1;
} }
} }
return identifierCount;
} }
} }

View File

@ -70,4 +70,22 @@ public class UrlTemplateTest {
// Expected. // Expected.
} }
} }
@Test
public void fullWithMultipleOccurrences() {
String template =
"$Bandwidth$_a1_$RepresentationID$_b1_$Time$_c1_$Number$_$Bandwidth$_a2_$RepresentationID$_b2_$Time$_c2_$Number$";
UrlTemplate urlTemplate = UrlTemplate.compile(template);
String url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
assertThat(url).isEqualTo("650000_a1_abc1_b1_5000_c1_10_650000_a2_abc1_b2_5000_c2_10");
}
@Test
public void fullWithMultipleOccurrencesAndDollarEscaping() {
String template =
"$$$Bandwidth$$$_a1$$_$RepresentationID$_b1_$Time$_c1_$Number$$$_$$$Bandwidth$$$_a2$$_$RepresentationID$_b2_$Time$_c2_$Number$$$";
UrlTemplate urlTemplate = UrlTemplate.compile(template);
String url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
assertThat(url).isEqualTo("$650000$_a1$_abc1_b1_5000_c1_10$_$650000$_a2$_abc1_b2_5000_c2_10$");
}
} }