Increase flexibility of ISM URL handling

PiperOrigin-RevId: 326025335
This commit is contained in:
olly 2020-08-11 16:27:02 +01:00 committed by kim-vde
parent 607e11718b
commit 511a6c729e
5 changed files with 56 additions and 43 deletions

View file

@ -142,8 +142,7 @@ public final class Util {
private static final Pattern ESCAPED_CHARACTER_PATTERN = Pattern.compile("%([A-Fa-f0-9]{2})");
// https://docs.microsoft.com/en-us/azure/media-services/previous/media-services-deliver-content-overview#URLs.
private static final Pattern ISM_URL_PATTERN =
Pattern.compile(".*\\.ism(?:l)?(?:/(?:manifest(?:\\((.+)\\))?)?)?");
private static final Pattern ISM_URL_PATTERN = Pattern.compile(".*\\.isml?(?:/(manifest(.*))?)?");
private static final String ISM_HLS_FORMAT_EXTENSION = "format=m3u8-aapl";
private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf";
@ -1723,7 +1722,7 @@ public final class Util {
}
Matcher ismMatcher = ISM_URL_PATTERN.matcher(fileName);
if (ismMatcher.matches()) {
@Nullable String extensions = ismMatcher.group(1);
@Nullable String extensions = ismMatcher.group(2);
if (extensions != null) {
if (extensions.contains(ISM_DASH_FORMAT_EXTENSION)) {
return C.TYPE_DASH;
@ -1779,6 +1778,27 @@ public final class Util {
}
}
/**
* If the provided URI is an ISM Presentation URI, returns the URI with "Manifest" appended to its
* path (i.e., the corresponding default manifest URI). Else returns the provided URI without
* modification. See [MS-SSTR] v20180912, section 2.2.1.
*
* @param uri The original URI.
* @return The fixed URI.
*/
public static Uri fixSmoothStreamingIsmManifestUri(Uri uri) {
@Nullable String path = toLowerInvariant(uri.getPath());
if (path == null) {
return uri;
}
Matcher ismMatcher = ISM_URL_PATTERN.matcher(path);
if (ismMatcher.matches() && ismMatcher.group(1) == null) {
// Add missing "Manifest" suffix.
return Uri.withAppendedPath(uri, "Manifest");
}
return uri;
}
/**
* Returns the specified millisecond time formatted as a string.
*

View file

@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.StrikethroughSpan;
@ -127,13 +128,39 @@ public class UtilTest {
assertThat(Util.inferContentType("http://a.b/c.ism/Manifest")).isEqualTo(C.TYPE_SS);
assertThat(Util.inferContentType("http://a.b/c.isml/manifest")).isEqualTo(C.TYPE_SS);
assertThat(Util.inferContentType("http://a.b/c.isml/manifest(filter=x)")).isEqualTo(C.TYPE_SS);
assertThat(Util.inferContentType("http://a.b/c.isml/manifest_hd")).isEqualTo(C.TYPE_SS);
}
@Test
public void inferContentType_handlesOtherIsmUris() {
assertThat(Util.inferContentType("http://a.b/c.ism/video.mp4")).isEqualTo(C.TYPE_OTHER);
assertThat(Util.inferContentType("http://a.b/c.ism/prefix-manifest")).isEqualTo(C.TYPE_OTHER);
assertThat(Util.inferContentType("http://a.b/c.ism/manifest-suffix")).isEqualTo(C.TYPE_OTHER);
}
@Test
public void fixSmoothStreamingIsmManifestUri_addsManifestSuffix() {
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.ism")))
.isEqualTo(Uri.parse("http://a.b/c.ism/Manifest"));
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.isml")))
.isEqualTo(Uri.parse("http://a.b/c.isml/Manifest"));
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.ism/")))
.isEqualTo(Uri.parse("http://a.b/c.ism/Manifest"));
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.isml/")))
.isEqualTo(Uri.parse("http://a.b/c.isml/Manifest"));
}
@Test
public void fixSmoothStreamingIsmManifestUri_doesNotAlterManifestUri() {
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.ism/Manifest")))
.isEqualTo(Uri.parse("http://a.b/c.ism/Manifest"));
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.isml/Manifest")))
.isEqualTo(Uri.parse("http://a.b/c.isml/Manifest"));
assertThat(
Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.ism/Manifest(filter=x)")))
.isEqualTo(Uri.parse("http://a.b/c.ism/Manifest(filter=x)"));
assertThat(Util.fixSmoothStreamingIsmManifestUri(Uri.parse("http://a.b/c.ism/Manifest_hd")))
.isEqualTo(Uri.parse("http://a.b/c.ism/Manifest_hd"));
}
@Test

View file

@ -47,7 +47,6 @@ import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
@ -581,7 +580,7 @@ public final class SsMediaSource extends BaseMediaSource
this.manifestUri =
playbackProperties.uri.equals(Uri.EMPTY)
? null
: SsUtil.fixManifestUri(playbackProperties.uri);
: Util.fixSmoothStreamingIsmManifestUri(playbackProperties.uri);
this.manifestDataSourceFactory = manifestDataSourceFactory;
this.manifestParser = manifestParser;
this.chunkSourceFactory = chunkSourceFactory;

View file

@ -1,35 +0,0 @@
/*
* Copyright (C) 2018 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.exoplayer2.source.smoothstreaming.manifest;
import android.net.Uri;
import com.google.android.exoplayer2.util.Util;
/** SmoothStreaming related utility methods. */
public final class SsUtil {
/** Returns a fixed SmoothStreaming client manifest {@link Uri}. */
public static Uri fixManifestUri(Uri manifestUri) {
String lastPathSegment = manifestUri.getLastPathSegment();
if (lastPathSegment != null
&& Util.toLowerInvariant(lastPathSegment).matches("manifest(\\(.+\\))?")) {
return manifestUri;
}
return Uri.withAppendedPath(manifestUri, "Manifest");
}
private SsUtil() {}
}

View file

@ -24,11 +24,11 @@ import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.ParsingLoadable.Parser;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@ -112,7 +112,9 @@ public final class SsDownloader extends SegmentDownloader<SsManifest> {
this(
mediaItem
.buildUpon()
.setUri(SsUtil.fixManifestUri(checkNotNull(mediaItem.playbackProperties).uri))
.setUri(
Util.fixSmoothStreamingIsmManifestUri(
checkNotNull(mediaItem.playbackProperties).uri))
.build(),
new SsManifestParser(),
cacheDataSourceFactory,