diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java index 9f3ed2f487..213745f93d 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -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. * diff --git a/library/common/src/test/java/com/google/android/exoplayer2/util/UtilTest.java b/library/common/src/test/java/com/google/android/exoplayer2/util/UtilTest.java index 0bf5028282..162dcbae9d 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/util/UtilTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/util/UtilTest.java @@ -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 diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 717e62fe5e..4a1f5c353c 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -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; diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsUtil.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsUtil.java deleted file mode 100644 index b54b2abc74..0000000000 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsUtil.java +++ /dev/null @@ -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() {} -} diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloader.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloader.java index 05828703f6..998820de4b 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloader.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloader.java @@ -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 { this( mediaItem .buildUpon() - .setUri(SsUtil.fixManifestUri(checkNotNull(mediaItem.playbackProperties).uri)) + .setUri( + Util.fixSmoothStreamingIsmManifestUri( + checkNotNull(mediaItem.playbackProperties).uri)) .build(), new SsManifestParser(), cacheDataSourceFactory,