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 Oliver Woodman
parent 590e81e5ae
commit 1c7c6fb90d
6 changed files with 56 additions and 43 deletions

View file

@ -16,6 +16,7 @@
boxes.
* FLV: Ignore `SCRIPTDATA` segments with invalid name types, rather than
failing playback ([#7675](https://github.com/google/ExoPlayer/issues/7675)).
* Better infer content type for `.ism` and `.isml` streaming URLs.
* Workaround an issue on Broadcom based devices where playbacks would not
transition to `STATE_ENDED` when using video tunneling mode
([#7647](https://github.com/google/ExoPlayer/issues/7647)).

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

@ -136,8 +136,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";
@ -1608,7 +1607,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;
@ -1621,6 +1620,27 @@ public final class Util {
return C.TYPE_OTHER;
}
/**
* 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

@ -39,7 +39,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;
@ -50,6 +49,7 @@ import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -531,7 +531,7 @@ public final class SsMediaSource extends BaseMediaSource
@Nullable Object tag) {
Assertions.checkState(manifest == null || !manifest.isLive);
this.manifest = manifest;
this.manifestUri = manifestUri == null ? null : SsUtil.fixManifestUri(manifestUri);
this.manifestUri = manifestUri == null ? null : Util.fixSmoothStreamingIsmManifestUri(manifestUri);
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

@ -23,10 +23,10 @@ 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;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -64,7 +64,7 @@ public final class SsDownloader extends SegmentDownloader<SsManifest> {
*/
public SsDownloader(
Uri manifestUri, List<StreamKey> streamKeys, DownloaderConstructorHelper constructorHelper) {
super(SsUtil.fixManifestUri(manifestUri), streamKeys, constructorHelper);
super(Util.fixSmoothStreamingIsmManifestUri(manifestUri), streamKeys, constructorHelper);
}
@Override