From 0a2f485079066e83c9c4b3508da1897eba34f0e9 Mon Sep 17 00:00:00 2001 From: eguven Date: Wed, 26 Jul 2017 08:58:02 -0700 Subject: [PATCH 01/24] Add HlsMasterPlaylist.copy method Creates a copy of this playlist which includes only the variants identified by the given variantUrls. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163212562 --- .../hls/playlist/HlsMasterPlaylist.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index b38763f7e8..0b237e75e7 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.util.MimeTypes; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -108,6 +109,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ? Collections.unmodifiableList(muxedCaptionFormats) : null; } + /** + * Returns a copy of this playlist which includes only the renditions identified by the given + * urls. + * + * @param renditionUrls List of rendition urls. + * @return A copy of this playlist which includes only the renditions identified by the given + * urls. + */ + public HlsMasterPlaylist copy(List renditionUrls) { + return new HlsMasterPlaylist(baseUri, tags, copyRenditionList(variants, renditionUrls), + copyRenditionList(audios, renditionUrls), copyRenditionList(subtitles, renditionUrls), + muxedAudioFormat, muxedCaptionFormats); + } + /** * Creates a playlist with a single variant. * @@ -121,4 +136,15 @@ public final class HlsMasterPlaylist extends HlsPlaylist { emptyList, null, null); } + private static List copyRenditionList(List variants, List variantUrls) { + List copyVariants = new ArrayList<>(); + for (int i = 0; i < variants.size(); i++) { + HlsUrl variant = variants.get(i); + if (variantUrls.contains(variant.url)) { + copyVariants.add(variant); + } + } + return copyVariants; + } + } From a7032ede38a1428eb89fdbb34158b7ef77945c77 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 27 Jul 2017 06:20:24 -0700 Subject: [PATCH 02/24] Change copyRenditionsList parameters names Also instantiate the resulting list with a predicted size to minimize list resizing. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163332285 --- .../source/hls/playlist/HlsMasterPlaylist.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index 0b237e75e7..04192def9d 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -118,8 +118,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { * urls. */ public HlsMasterPlaylist copy(List renditionUrls) { - return new HlsMasterPlaylist(baseUri, tags, copyRenditionList(variants, renditionUrls), - copyRenditionList(audios, renditionUrls), copyRenditionList(subtitles, renditionUrls), + return new HlsMasterPlaylist(baseUri, tags, copyRenditionsList(variants, renditionUrls), + copyRenditionsList(audios, renditionUrls), copyRenditionsList(subtitles, renditionUrls), muxedAudioFormat, muxedCaptionFormats); } @@ -136,15 +136,15 @@ public final class HlsMasterPlaylist extends HlsPlaylist { emptyList, null, null); } - private static List copyRenditionList(List variants, List variantUrls) { - List copyVariants = new ArrayList<>(); - for (int i = 0; i < variants.size(); i++) { - HlsUrl variant = variants.get(i); - if (variantUrls.contains(variant.url)) { - copyVariants.add(variant); + private static List copyRenditionsList(List renditions, List urls) { + List copiedRenditions = new ArrayList<>(urls.size()); + for (int i = 0; i < renditions.size(); i++) { + HlsUrl rendition = renditions.get(i); + if (urls.contains(rendition.url)) { + copiedRenditions.add(rendition); } } - return copyVariants; + return copiedRenditions; } } From 39cb3f932e984f76494c13ce940a6f8c5b31a57e Mon Sep 17 00:00:00 2001 From: Nate Roy Date: Mon, 2 Oct 2017 15:43:19 -0400 Subject: [PATCH 03/24] Allow passing of HlsPlaylistParser to HlsMediaSource. Also create helper method in HlsMasterPlaylist to allow the copying of the playlist to another, but with the variants reordered based on a passed comparator. Also added an implementation of HlsPlaylistParser which will reorder the variants returned. --- .../playlist/HlsMasterPlaylistParserTest.java | 44 ++++++- .../ReorderingHlsPlaylistParserTest.java | 107 ++++++++++++++++++ .../exoplayer2/source/hls/HlsMediaSource.java | 14 ++- .../hls/playlist/HlsMasterPlaylist.java | 20 ++++ .../hls/playlist/HlsPlaylistTracker.java | 6 +- .../playlist/ReorderingHlsPlaylistParser.java | 39 +++++++ 6 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java create mode 100644 library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index f835c87466..40663fa236 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -16,16 +16,20 @@ package com.google.android.exoplayer2.source.hls.playlist; import android.net.Uri; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.util.MimeTypes; + +import junit.framework.TestCase; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.Collections; +import java.util.Comparator; import java.util.List; -import junit.framework.TestCase; /** * Test for {@link HlsMasterPlaylistParserTest} @@ -144,6 +148,44 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertEquals(Collections.emptyList(), playlist.muxedCaptionFormats); } + public void testReorderedVariantCopy() throws IOException { + HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST); + HlsMasterPlaylist nonReorderedPlaylist = + playlist.copyWithReorderedVariants(new Comparator() { + @Override + public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { + return 0; + } + }); + assertEquals(playlist.variants, nonReorderedPlaylist.variants); + HlsMasterPlaylist.HlsUrl preferred = null; + for (HlsMasterPlaylist.HlsUrl url : playlist.variants) { + if (preferred == null || url.format.bitrate > preferred.format.bitrate) { + preferred = url; + } + } + + assertNotNull(preferred); + + final Comparator comparator = Collections.reverseOrder(new Comparator() { + @Override + public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { + if (url1.format.bitrate > url2.format.bitrate) { + return 1; + } + + if (url2.format.bitrate > url1.format.bitrate) { + return -1; + } + + return 0; + } + }); + HlsMasterPlaylist reorderedPlaylist = playlist.copyWithReorderedVariants(comparator); + + assertEquals(reorderedPlaylist.variants.get(0), preferred); + } + private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) throws IOException { Uri playlistUri = Uri.parse(uri); diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java new file mode 100644 index 0000000000..2816832704 --- /dev/null +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java @@ -0,0 +1,107 @@ +package com.google.android.exoplayer2.source.hls.playlist; + +import android.net.Uri; + +import com.google.android.exoplayer2.C; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Comparator; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class ReorderingHlsPlaylistParserTest extends TestCase { + private static final String MASTER_PLAYLIST = " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" + + "http://example.com/mid.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" + + "http://example.com/hi.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" + + "http://example.com/audio-only.m3u8"; + + public void testReorderingWithNonMasterPlaylist() throws IOException { + Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); + String playlistString = "#EXTM3U\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXT-X-START:TIME-OFFSET=-25" + + "#EXT-X-TARGETDURATION:8\n" + + "#EXT-X-MEDIA-SEQUENCE:2679\n" + + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" + + "#EXT-X-ALLOW-CACHE:YES\n" + + "\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51370@0\n" + + "https://priv.example.com/fileSequence2679.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51501@2147483648\n" + + "https://priv.example.com/fileSequence2680.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=NONE\n" + + "#EXTINF:7.941,\n" + + "#EXT-X-BYTERANGE:51501\n" // @2147535149 + + "https://priv.example.com/fileSequence2681.ts\n" + + "\n" + + "#EXT-X-DISCONTINUITY\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51740\n" // @2147586650 + + "https://priv.example.com/fileSequence2682.ts\n" + + "\n" + + "#EXTINF:7.975,\n" + + "https://priv.example.com/fileSequence2683.ts\n" + + "#EXT-X-ENDLIST"; + InputStream inputStream = new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + Comparator comparator = mock(Comparator.class); + ReorderingHlsPlaylistParser playlistParser = new ReorderingHlsPlaylistParser(new HlsPlaylistParser(), + comparator); + final HlsMediaPlaylist playlist = (HlsMediaPlaylist) playlistParser.parse(playlistUri, inputStream); + assertNotNull(playlist); + // We should never compare the variants for a media level playlist. + verify(comparator, never()).compare(any(HlsMasterPlaylist.HlsUrl.class), any(HlsMasterPlaylist.HlsUrl.class)); + } + + public void testReorderingForMasterPlaylist() throws IOException { + Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); + ByteArrayInputStream inputStream = new ByteArrayInputStream( + MASTER_PLAYLIST.getBytes(Charset.forName(C.UTF8_NAME))); + final Comparator comparator = Collections.reverseOrder(new Comparator() { + @Override + public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { + if (url1.format.bitrate > url2.format.bitrate) { + return 1; + } + + if (url2.format.bitrate > url1.format.bitrate) { + return -1; + } + + return 0; + } + }); + ReorderingHlsPlaylistParser playlistParser = new ReorderingHlsPlaylistParser(new HlsPlaylistParser(), + comparator); + final HlsMasterPlaylist reorderedPlaylist = (HlsMasterPlaylist) playlistParser.parse(playlistUri, inputStream); + assertNotNull(reorderedPlaylist); + + inputStream.reset(); + final HlsMasterPlaylist playlist = (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); + assertEquals(reorderedPlaylist.variants.get(0).format, playlist.variants.get(2).format); + } +} \ No newline at end of file diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index fd3d533337..b7f7124e44 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -26,9 +26,12 @@ import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; +import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; +import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.List; @@ -52,6 +55,7 @@ public final class HlsMediaSource implements MediaSource, private final HlsDataSourceFactory dataSourceFactory; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; + private final ParsingLoadable.Parser playlistParser; private HlsPlaylistTracker playlistTracker; private Listener sourceListener; @@ -72,9 +76,17 @@ public final class HlsMediaSource implements MediaSource, public HlsMediaSource(Uri manifestUri, HlsDataSourceFactory dataSourceFactory, int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { + this(manifestUri, dataSourceFactory, minLoadableRetryCount, eventHandler, eventListener, new HlsPlaylistParser()); + } + + public HlsMediaSource(Uri manifestUri, HlsDataSourceFactory dataSourceFactory, + int minLoadableRetryCount, Handler eventHandler, + AdaptiveMediaSourceEventListener eventListener, + ParsingLoadable.Parser playlistParser) { this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; + this.playlistParser = playlistParser; eventDispatcher = new EventDispatcher(eventHandler, eventListener); } @@ -82,7 +94,7 @@ public final class HlsMediaSource implements MediaSource, public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { Assertions.checkState(playlistTracker == null); playlistTracker = new HlsPlaylistTracker(manifestUri, dataSourceFactory, eventDispatcher, - minLoadableRetryCount, this); + minLoadableRetryCount, this, playlistParser); sourceListener = listener; playlistTracker.start(); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index 04192def9d..5ded975f88 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.util.MimeTypes; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** @@ -123,6 +124,25 @@ public final class HlsMasterPlaylist extends HlsPlaylist { muxedAudioFormat, muxedCaptionFormats); } + /** + * Returns a copy of this playlist which includes the variants sorted using the passed comparator. NOTE: the variants + * will be sorted in ascending order by default. If you wish to use descending order, you can wrap your comparator in + * {@link Collections#reverseOrder(Comparator)}. + * + * @param variantComparator the comparator to use to sort the variant list. + * @return a copy of this playlist which includes the variants sorted using the passed comparator. + */ + public HlsMasterPlaylist copyWithReorderedVariants(Comparator variantComparator) { + return new HlsMasterPlaylist(baseUri, tags, filterVariants(variants, variantComparator), audios, + subtitles, muxedAudioFormat, muxedCaptionFormats); + } + + private List filterVariants(List variants, Comparator variantComparator) { + List reorderedList = new ArrayList<>(variants); + Collections.sort(reorderedList, variantComparator); + return reorderedList; + } + /** * Creates a playlist with a single variant. * diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 567dbd4af6..a0e299632d 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -120,7 +120,7 @@ public final class HlsPlaylistTracker implements Loader.Callback playlistParser; private final int minRetryCount; private final IdentityHashMap playlistBundles; private final Handler playlistRefreshHandler; @@ -145,7 +145,7 @@ public final class HlsPlaylistTracker implements Loader.Callback playlistParser) { this.initialPlaylistUri = initialPlaylistUri; this.dataSourceFactory = dataSourceFactory; this.eventDispatcher = eventDispatcher; @@ -153,7 +153,7 @@ public final class HlsPlaylistTracker implements Loader.Callback(); initialPlaylistLoader = new Loader("HlsPlaylistTracker:MasterPlaylist"); - playlistParser = new HlsPlaylistParser(); + this.playlistParser = playlistParser; playlistBundles = new IdentityHashMap<>(); playlistRefreshHandler = new Handler(); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java new file mode 100644 index 0000000000..bdd47e8c28 --- /dev/null +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java @@ -0,0 +1,39 @@ +package com.google.android.exoplayer2.source.hls.playlist; + +import android.net.Uri; + +import com.google.android.exoplayer2.upstream.ParsingLoadable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Comparator; + +/** + * Parser for {@link HlsPlaylist}s that reorders the variants based on the comparator passed. + */ +public class ReorderingHlsPlaylistParser implements ParsingLoadable.Parser { + private final ParsingLoadable.Parser playlistParser; + private final Comparator variantComparator; + + /** + * @param playlistParser the {@link ParsingLoadable.Parser} to wrap. + * @param variantComparator the {@link Comparator} to use to reorder the variants. + * See {@link HlsMasterPlaylist#copyWithReorderedVariants(Comparator)} for more details. + */ + public ReorderingHlsPlaylistParser(ParsingLoadable.Parser playlistParser, + Comparator variantComparator) { + this.playlistParser = playlistParser; + this.variantComparator = variantComparator; + } + + @Override + public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException { + final HlsPlaylist playlist = playlistParser.parse(uri, inputStream); + + if (playlist instanceof HlsMasterPlaylist) { + return ((HlsMasterPlaylist) playlist).copyWithReorderedVariants(variantComparator); + } + + return playlist; + } +} From 1c594b4cdb6c4fd1362c00a9b523b38ed640531c Mon Sep 17 00:00:00 2001 From: Nate Roy Date: Fri, 6 Oct 2017 14:35:18 -0400 Subject: [PATCH 04/24] remove reordering playlist parser --- .../ReorderingHlsPlaylistParserTest.java | 107 ------------------ .../playlist/ReorderingHlsPlaylistParser.java | 39 ------- 2 files changed, 146 deletions(-) delete mode 100644 library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java delete mode 100644 library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java deleted file mode 100644 index 2816832704..0000000000 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParserTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.google.android.exoplayer2.source.hls.playlist; - -import android.net.Uri; - -import com.google.android.exoplayer2.C; - -import junit.framework.TestCase; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.Comparator; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -public class ReorderingHlsPlaylistParserTest extends TestCase { - private static final String MASTER_PLAYLIST = " #EXTM3U \n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" - + "http://example.com/mid.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" - + "http://example.com/hi.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" - + "http://example.com/audio-only.m3u8"; - - public void testReorderingWithNonMasterPlaylist() throws IOException { - Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); - String playlistString = "#EXTM3U\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXT-X-START:TIME-OFFSET=-25" - + "#EXT-X-TARGETDURATION:8\n" - + "#EXT-X-MEDIA-SEQUENCE:2679\n" - + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" - + "#EXT-X-ALLOW-CACHE:YES\n" - + "\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51370@0\n" - + "https://priv.example.com/fileSequence2679.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51501@2147483648\n" - + "https://priv.example.com/fileSequence2680.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=NONE\n" - + "#EXTINF:7.941,\n" - + "#EXT-X-BYTERANGE:51501\n" // @2147535149 - + "https://priv.example.com/fileSequence2681.ts\n" - + "\n" - + "#EXT-X-DISCONTINUITY\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51740\n" // @2147586650 - + "https://priv.example.com/fileSequence2682.ts\n" - + "\n" - + "#EXTINF:7.975,\n" - + "https://priv.example.com/fileSequence2683.ts\n" - + "#EXT-X-ENDLIST"; - InputStream inputStream = new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); - Comparator comparator = mock(Comparator.class); - ReorderingHlsPlaylistParser playlistParser = new ReorderingHlsPlaylistParser(new HlsPlaylistParser(), - comparator); - final HlsMediaPlaylist playlist = (HlsMediaPlaylist) playlistParser.parse(playlistUri, inputStream); - assertNotNull(playlist); - // We should never compare the variants for a media level playlist. - verify(comparator, never()).compare(any(HlsMasterPlaylist.HlsUrl.class), any(HlsMasterPlaylist.HlsUrl.class)); - } - - public void testReorderingForMasterPlaylist() throws IOException { - Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); - ByteArrayInputStream inputStream = new ByteArrayInputStream( - MASTER_PLAYLIST.getBytes(Charset.forName(C.UTF8_NAME))); - final Comparator comparator = Collections.reverseOrder(new Comparator() { - @Override - public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { - if (url1.format.bitrate > url2.format.bitrate) { - return 1; - } - - if (url2.format.bitrate > url1.format.bitrate) { - return -1; - } - - return 0; - } - }); - ReorderingHlsPlaylistParser playlistParser = new ReorderingHlsPlaylistParser(new HlsPlaylistParser(), - comparator); - final HlsMasterPlaylist reorderedPlaylist = (HlsMasterPlaylist) playlistParser.parse(playlistUri, inputStream); - assertNotNull(reorderedPlaylist); - - inputStream.reset(); - final HlsMasterPlaylist playlist = (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); - assertEquals(reorderedPlaylist.variants.get(0).format, playlist.variants.get(2).format); - } -} \ No newline at end of file diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java deleted file mode 100644 index bdd47e8c28..0000000000 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/ReorderingHlsPlaylistParser.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.google.android.exoplayer2.source.hls.playlist; - -import android.net.Uri; - -import com.google.android.exoplayer2.upstream.ParsingLoadable; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Comparator; - -/** - * Parser for {@link HlsPlaylist}s that reorders the variants based on the comparator passed. - */ -public class ReorderingHlsPlaylistParser implements ParsingLoadable.Parser { - private final ParsingLoadable.Parser playlistParser; - private final Comparator variantComparator; - - /** - * @param playlistParser the {@link ParsingLoadable.Parser} to wrap. - * @param variantComparator the {@link Comparator} to use to reorder the variants. - * See {@link HlsMasterPlaylist#copyWithReorderedVariants(Comparator)} for more details. - */ - public ReorderingHlsPlaylistParser(ParsingLoadable.Parser playlistParser, - Comparator variantComparator) { - this.playlistParser = playlistParser; - this.variantComparator = variantComparator; - } - - @Override - public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException { - final HlsPlaylist playlist = playlistParser.parse(uri, inputStream); - - if (playlist instanceof HlsMasterPlaylist) { - return ((HlsMasterPlaylist) playlist).copyWithReorderedVariants(variantComparator); - } - - return playlist; - } -} From feceabadebb7275cc69f85c2854876f3a2045170 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Fri, 13 Oct 2017 20:40:08 +0100 Subject: [PATCH 05/24] Tweak recently merged pull requests --- .../playlist/HlsMasterPlaylistParserTest.java | 46 +------------------ .../playlist/HlsMediaPlaylistParserTest.java | 2 +- .../exoplayer2/source/hls/HlsMediaSource.java | 9 ++-- .../hls/playlist/HlsMasterPlaylist.java | 20 -------- .../hls/playlist/HlsPlaylistTracker.java | 5 +- 5 files changed, 11 insertions(+), 71 deletions(-) diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index 40663fa236..6bbcaecd1f 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -16,23 +16,19 @@ package com.google.android.exoplayer2.source.hls.playlist; import android.net.Uri; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.util.MimeTypes; - -import junit.framework.TestCase; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.Collections; -import java.util.Comparator; import java.util.List; +import junit.framework.TestCase; /** - * Test for {@link HlsMasterPlaylistParserTest} + * Test for {@link HlsMasterPlaylistParserTest}. */ public class HlsMasterPlaylistParserTest extends TestCase { @@ -148,44 +144,6 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertEquals(Collections.emptyList(), playlist.muxedCaptionFormats); } - public void testReorderedVariantCopy() throws IOException { - HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST); - HlsMasterPlaylist nonReorderedPlaylist = - playlist.copyWithReorderedVariants(new Comparator() { - @Override - public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { - return 0; - } - }); - assertEquals(playlist.variants, nonReorderedPlaylist.variants); - HlsMasterPlaylist.HlsUrl preferred = null; - for (HlsMasterPlaylist.HlsUrl url : playlist.variants) { - if (preferred == null || url.format.bitrate > preferred.format.bitrate) { - preferred = url; - } - } - - assertNotNull(preferred); - - final Comparator comparator = Collections.reverseOrder(new Comparator() { - @Override - public int compare(HlsMasterPlaylist.HlsUrl url1, HlsMasterPlaylist.HlsUrl url2) { - if (url1.format.bitrate > url2.format.bitrate) { - return 1; - } - - if (url2.format.bitrate > url1.format.bitrate) { - return -1; - } - - return 0; - } - }); - HlsMasterPlaylist reorderedPlaylist = playlist.copyWithReorderedVariants(comparator); - - assertEquals(reorderedPlaylist.variants.get(0), preferred); - } - private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) throws IOException { Uri playlistUri = Uri.parse(uri); diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index e2eb173df8..6855f786ec 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -27,7 +27,7 @@ import java.util.Locale; import junit.framework.TestCase; /** - * Test for {@link HlsMediaPlaylistParserTest} + * Test for {@link HlsMediaPlaylistParserTest}. */ public class HlsMediaPlaylistParserTest extends TestCase { diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index b7f7124e44..10a0536612 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -76,13 +76,14 @@ public final class HlsMediaSource implements MediaSource, public HlsMediaSource(Uri manifestUri, HlsDataSourceFactory dataSourceFactory, int minLoadableRetryCount, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { - this(manifestUri, dataSourceFactory, minLoadableRetryCount, eventHandler, eventListener, new HlsPlaylistParser()); + this(manifestUri, dataSourceFactory, minLoadableRetryCount, eventHandler, eventListener, + new HlsPlaylistParser()); } public HlsMediaSource(Uri manifestUri, HlsDataSourceFactory dataSourceFactory, - int minLoadableRetryCount, Handler eventHandler, - AdaptiveMediaSourceEventListener eventListener, - ParsingLoadable.Parser playlistParser) { + int minLoadableRetryCount, Handler eventHandler, + AdaptiveMediaSourceEventListener eventListener, + ParsingLoadable.Parser playlistParser) { this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index 5ded975f88..04192def9d 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -19,7 +19,6 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.util.MimeTypes; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; /** @@ -124,25 +123,6 @@ public final class HlsMasterPlaylist extends HlsPlaylist { muxedAudioFormat, muxedCaptionFormats); } - /** - * Returns a copy of this playlist which includes the variants sorted using the passed comparator. NOTE: the variants - * will be sorted in ascending order by default. If you wish to use descending order, you can wrap your comparator in - * {@link Collections#reverseOrder(Comparator)}. - * - * @param variantComparator the comparator to use to sort the variant list. - * @return a copy of this playlist which includes the variants sorted using the passed comparator. - */ - public HlsMasterPlaylist copyWithReorderedVariants(Comparator variantComparator) { - return new HlsMasterPlaylist(baseUri, tags, filterVariants(variants, variantComparator), audios, - subtitles, muxedAudioFormat, muxedCaptionFormats); - } - - private List filterVariants(List variants, Comparator variantComparator) { - List reorderedList = new ArrayList<>(variants); - Collections.sort(reorderedList, variantComparator); - return reorderedList; - } - /** * Creates a playlist with a single variant. * diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index a0e299632d..3d8d4eb3af 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -145,15 +145,16 @@ public final class HlsPlaylistTracker implements Loader.Callback playlistParser) { + PrimaryPlaylistListener primaryPlaylistListener, + ParsingLoadable.Parser playlistParser) { this.initialPlaylistUri = initialPlaylistUri; this.dataSourceFactory = dataSourceFactory; this.eventDispatcher = eventDispatcher; this.minRetryCount = minRetryCount; this.primaryPlaylistListener = primaryPlaylistListener; + this.playlistParser = playlistParser; listeners = new ArrayList<>(); initialPlaylistLoader = new Loader("HlsPlaylistTracker:MasterPlaylist"); - this.playlistParser = playlistParser; playlistBundles = new IdentityHashMap<>(); playlistRefreshHandler = new Handler(); } From dbdae4ca25fd023e2d13874ca52236b521c9a8a5 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 13 Oct 2017 11:38:47 -0700 Subject: [PATCH 06/24] Only parse common-encryption sinf boxes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172124807 --- .../exoplayer2/extractor/mp4/AtomParsersTest.java | 8 ++++---- .../exoplayer2/extractor/mp4/AtomParsers.java | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java index d0213337b8..d644af2677 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java @@ -34,18 +34,18 @@ public final class AtomParsersTest extends TestCase { + SAMPLE_COUNT + "0001000200030004"); public void testStz2Parsing4BitFieldSize() { - verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(FOUR_BIT_STZ2))); + verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(FOUR_BIT_STZ2))); } public void testStz2Parsing8BitFieldSize() { - verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(EIGHT_BIT_STZ2))); + verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(EIGHT_BIT_STZ2))); } public void testStz2Parsing16BitFieldSize() { - verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(SIXTEEN_BIT_STZ2))); + verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(SIXTEEN_BIT_STZ2))); } - private void verifyParsing(Atom.LeafAtom stz2Atom) { + private static void verifyStz2Parsing(Atom.LeafAtom stz2Atom) { AtomParsers.Stz2SampleSizeBox box = new AtomParsers.Stz2SampleSizeBox(stz2Atom); assertEquals(4, box.getSampleCount()); assertFalse(box.isFixedSampleSize()); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 9a03311ccf..84538b8122 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -1060,8 +1060,8 @@ import java.util.List; Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); int childAtomType = parent.readInt(); if (childAtomType == Atom.TYPE_sinf) { - Pair result = parseSinfFromParent(parent, childPosition, - childAtomSize); + Pair result = parseCommonEncryptionSinfFromParent(parent, + childPosition, childAtomSize); if (result != null) { return result; } @@ -1071,8 +1071,8 @@ import java.util.List; return null; } - private static Pair parseSinfFromParent(ParsableByteArray parent, - int position, int size) { + /* package */ static Pair parseCommonEncryptionSinfFromParent( + ParsableByteArray parent, int position, int size) { int childPosition = position + Atom.HEADER_SIZE; int schemeInformationBoxPosition = C.POSITION_UNSET; int schemeInformationBoxSize = 0; @@ -1086,7 +1086,7 @@ import java.util.List; dataFormat = parent.readInt(); } else if (childAtomType == Atom.TYPE_schm) { parent.skipBytes(4); - // scheme_type field. Defined in ISO/IEC 23001-7:2016, section 4.1. + // Common encryption scheme_type values are defined in ISO/IEC 23001-7:2016, section 4.1. schemeType = parent.readString(4); } else if (childAtomType == Atom.TYPE_schi) { schemeInformationBoxPosition = childPosition; @@ -1095,7 +1095,8 @@ import java.util.List; childPosition += childAtomSize; } - if (schemeType != null) { + if (C.CENC_TYPE_cenc.equals(schemeType) || C.CENC_TYPE_cbc1.equals(schemeType) + || C.CENC_TYPE_cens.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)) { Assertions.checkArgument(dataFormat != null, "frma atom is mandatory"); Assertions.checkArgument(schemeInformationBoxPosition != C.POSITION_UNSET, "schi atom is mandatory"); From 04d10ea67db38035d3b7eef9a2111b32c1a88468 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 10 Oct 2017 12:40:02 -0700 Subject: [PATCH 07/24] Expose public constructors for FrameworkMediaCrypto MediaCodecRenderer implementations require DrmSessionManager, but it's currently not possible for an app to provide a custom implementation due to FrameworkMediaCrypto having a package private constructor. This change exposes public FrameworkMediaCrypto constructors, hence removing this restriction. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171718853 --- .../exoplayer2/drm/FrameworkMediaCrypto.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java index 5bee85f449..4e58ed6a31 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java @@ -28,12 +28,29 @@ public final class FrameworkMediaCrypto implements ExoMediaCrypto { private final MediaCrypto mediaCrypto; private final boolean forceAllowInsecureDecoderComponents; - /* package */ FrameworkMediaCrypto(MediaCrypto mediaCrypto, + /** + * @param mediaCrypto The {@link MediaCrypto} to wrap. + */ + public FrameworkMediaCrypto(MediaCrypto mediaCrypto) { + this(mediaCrypto, false); + } + + /** + * @param mediaCrypto The {@link MediaCrypto} to wrap. + * @param forceAllowInsecureDecoderComponents Whether to force + * {@link #requiresSecureDecoderComponent(String)} to return {@code false}, rather than + * {@link MediaCrypto#requiresSecureDecoderComponent(String)} of the wrapped + * {@link MediaCrypto}. + */ + public FrameworkMediaCrypto(MediaCrypto mediaCrypto, boolean forceAllowInsecureDecoderComponents) { this.mediaCrypto = Assertions.checkNotNull(mediaCrypto); this.forceAllowInsecureDecoderComponents = forceAllowInsecureDecoderComponents; } + /** + * Returns the wrapped {@link MediaCrypto}. + */ public MediaCrypto getWrappedMediaCrypto() { return mediaCrypto; } From e6bf3736122a4a989e85d90b97b70b0451e6fc8f Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 10 Oct 2017 12:39:34 -0700 Subject: [PATCH 08/24] Allow overriding of DefaultDashChunkSource.getNextChunk ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171718775 --- .../source/dash/DefaultDashChunkSource.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index dd62d47621..ec1c90bf75 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -173,7 +173,7 @@ public class DefaultDashChunkSource implements DashChunkSource { } @Override - public final void getNextChunk(MediaChunk previous, long playbackPositionUs, ChunkHolder out) { + public void getNextChunk(MediaChunk previous, long playbackPositionUs, ChunkHolder out) { if (fatalError != null) { return; } @@ -300,7 +300,7 @@ public class DefaultDashChunkSource implements DashChunkSource { trackSelection.indexOf(chunk.trackFormat), e); } - // Private methods. + // Internal methods. private ArrayList getRepresentations() { List manifestAdapationSets = manifest.getPeriod(periodIndex).adaptationSets; @@ -319,7 +319,12 @@ public class DefaultDashChunkSource implements DashChunkSource { } } - private static Chunk newInitializationChunk(RepresentationHolder representationHolder, + private long resolveTimeToLiveEdgeUs(long playbackPositionUs) { + boolean resolveTimeToLiveEdgePossible = manifest.dynamic && liveEdgeTimeUs != C.TIME_UNSET; + return resolveTimeToLiveEdgePossible ? liveEdgeTimeUs - playbackPositionUs : C.TIME_UNSET; + } + + protected static Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { RangedUri requestUri; @@ -340,7 +345,7 @@ public class DefaultDashChunkSource implements DashChunkSource { trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); } - private static Chunk newMediaChunk(RepresentationHolder representationHolder, + protected static Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, int firstSegmentNum, int maxSegmentCount) { Representation representation = representationHolder.representation; From 758de818db009bf0e21372976cf2ffc172523397 Mon Sep 17 00:00:00 2001 From: yqritc Date: Thu, 21 Sep 2017 12:01:25 +0900 Subject: [PATCH 09/24] fix primarySnapshotAccessAgeMs --- .../exoplayer2/source/hls/playlist/HlsPlaylistTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 3d8d4eb3af..63498989c7 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -352,7 +352,7 @@ public final class HlsPlaylistTracker implements Loader.Callback PRIMARY_URL_KEEPALIVE_MS) { primaryHlsUrl = url; playlistBundles.get(primaryHlsUrl).loadPlaylist(); From 3b8fd4985cd879e9734b775739779efec1b73746 Mon Sep 17 00:00:00 2001 From: yqritc Date: Thu, 21 Sep 2017 14:18:59 +0900 Subject: [PATCH 10/24] remove keep alive check for updating primary url to avoid redundant playlist loading --- .../source/hls/playlist/HlsPlaylistTracker.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 63498989c7..c3e903334a 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -112,11 +112,6 @@ public final class HlsPlaylistTracker implements Loader.Callback PRIMARY_URL_KEEPALIVE_MS) { - primaryHlsUrl = url; - playlistBundles.get(primaryHlsUrl).loadPlaylist(); - } + + primaryHlsUrl = url; + playlistBundles.get(primaryHlsUrl).loadPlaylist(); } private void createBundles(List urls) { From 98f6e89efd368e17f65a19c02491674b5d7d0092 Mon Sep 17 00:00:00 2001 From: yqritc Date: Thu, 21 Sep 2017 16:01:32 +0900 Subject: [PATCH 11/24] remove space --- .../exoplayer2/source/hls/playlist/HlsPlaylistTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index c3e903334a..81d3de7307 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -201,7 +201,7 @@ public final class HlsPlaylistTracker implements Loader.Callback Date: Tue, 26 Sep 2017 17:06:36 +0100 Subject: [PATCH 12/24] Clean up HlsPlaylistTracker --- .../hls/playlist/HlsPlaylistTracker.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 81d3de7307..fdb9fb1e4e 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -201,7 +201,7 @@ public final class HlsPlaylistTracker implements Loader.Callback urls) { int listSize = urls.size(); - long currentTimeMs = SystemClock.elapsedRealtime(); for (int i = 0; i < listSize; i++) { HlsUrl url = urls.get(i); - MediaPlaylistBundle bundle = new MediaPlaylistBundle(url, currentTimeMs); + MediaPlaylistBundle bundle = new MediaPlaylistBundle(url); playlistBundles.put(url, bundle); } } @@ -471,14 +470,12 @@ public final class HlsPlaylistTracker implements Loader.Callback( dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST), @@ -487,7 +484,6 @@ public final class HlsPlaylistTracker implements Loader.Callback Date: Tue, 26 Sep 2017 11:09:45 -0700 Subject: [PATCH 13/24] Prevent unnecessary consecutive playlist loads ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170078933 --- .../hls/playlist/HlsPlaylistTracker.java | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index fdb9fb1e4e..da73aa3996 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -364,9 +364,8 @@ public final class HlsPlaylistTracker implements Loader.Callback C.usToMs(playlistSnapshot.targetDurationUs) * PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT) { - // The playlist seems to be stuck, we blacklist it. + // The playlist seems to be stuck. Blacklist it. playlistError = new PlaylistStuckException(playlistUrl.url); blacklistPlaylist(); - } else if (loadedPlaylist.mediaSequence + loadedPlaylist.segments.size() - < playlistSnapshot.mediaSequence) { - // The media sequence has jumped backwards. The server has likely reset. - playlistError = new PlaylistResetException(playlistUrl.url); } - refreshDelayUs = playlistSnapshot.targetDurationUs / 2; } - if (refreshDelayUs != C.TIME_UNSET) { - // See HLS spec v20, section 6.3.4 for more information on media playlist refreshing. - pendingRefresh = playlistRefreshHandler.postDelayed(this, C.usToMs(refreshDelayUs)); + // Do not allow the playlist to load again within the target duration if we obtained a new + // snapshot, or half the target duration otherwise. + earliestNextLoadTimeMs = currentTimeMs + C.usToMs(playlistSnapshot != oldPlaylist + ? playlistSnapshot.targetDurationUs : (playlistSnapshot.targetDurationUs / 2)); + // Schedule a load if this is the primary playlist and it doesn't have an end tag. Else the + // next load will be scheduled when refreshPlaylist is called, or when this playlist becomes + // the primary. + if (playlistUrl == primaryHlsUrl && !playlistSnapshot.hasEndTag) { + loadPlaylist(); } } - private void blacklistPlaylist() { + /** + * Blacklists the playlist. + * + * @return Whether the playlist is the primary, despite being blacklisted. + */ + private boolean blacklistPlaylist() { blacklistUntilMs = SystemClock.elapsedRealtime() + ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS; notifyPlaylistBlacklisting(playlistUrl, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS); + return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl(); } } From 51fd3365bb325d9580e7034ede488c80528a56fe Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 17 Oct 2017 17:57:27 +0100 Subject: [PATCH 14/24] Remove spurious method --- .../exoplayer2/source/dash/DefaultDashChunkSource.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index ec1c90bf75..b715b487b9 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -319,11 +319,6 @@ public class DefaultDashChunkSource implements DashChunkSource { } } - private long resolveTimeToLiveEdgeUs(long playbackPositionUs) { - boolean resolveTimeToLiveEdgePossible = manifest.dynamic && liveEdgeTimeUs != C.TIME_UNSET; - return resolveTimeToLiveEdgePossible ? liveEdgeTimeUs - playbackPositionUs : C.TIME_UNSET; - } - protected static Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { From f6d0dae50e4dd21ad6c10a3774db4da6ba1f5f1e Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 13 Oct 2017 09:31:00 -0700 Subject: [PATCH 15/24] Workaround/Fix #3351 1. Ignore edit list where the sequence doesn't contain a sync sample, rather than failing. 2. Make Mp4Extractor.readAtomPayload so it doesn't try and read the same payload twice if a failure occurs parsing it. 3. Make processAtomEnded so that it doesn't pop the moov if parsing it fails. Issue: #3351 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172106244 --- .../android/exoplayer2/extractor/mp4/AtomParsers.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 84538b8122..e527f310b3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -395,7 +395,11 @@ import java.util.List; hasSyncSample |= (editedFlags[i] & C.BUFFER_FLAG_KEY_FRAME) != 0; } if (!hasSyncSample) { - throw new ParserException("The edited sample sequence does not contain a sync sample."); + // We don't support edit lists where the edited sample sequence doesn't contain a sync sample. + // Such edit lists are often (although not always) broken, so we ignore it and continue. + Log.w(TAG, "Ignoring edit list: Edited sample sequence does not contain a sync sample."); + Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale); + return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags); } return new TrackSampleTable(editedOffsets, editedSizes, editedMaximumSize, editedTimestamps, From aa42d3c910fbee0497d94d1896c0748d57794ab5 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 17 Oct 2017 03:49:49 -0700 Subject: [PATCH 16/24] We're not playing an ad if the timeline is empty. Issue: #3334 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172447125 --- .../java/com/google/android/exoplayer2/ExoPlayerImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index b53cce1f74..cc63d57cae 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -356,17 +356,17 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public boolean isPlayingAd() { - return pendingSeekAcks == 0 && playbackInfo.periodId.isAd(); + return !timeline.isEmpty() && pendingSeekAcks == 0 && playbackInfo.periodId.isAd(); } @Override public int getCurrentAdGroupIndex() { - return pendingSeekAcks == 0 ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; + return isPlayingAd() ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; } @Override public int getCurrentAdIndexInAdGroup() { - return pendingSeekAcks == 0 ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; + return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; } @Override From ce0a03dbdf8f0d83ac98001805f0c6cea47f6722 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 17 Oct 2017 07:33:19 -0700 Subject: [PATCH 17/24] Add an extractor flag for ignoring edit lists Issue: #3358 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172464053 --- .../extractor/DefaultExtractorsFactory.java | 15 +++++++++- .../exoplayer2/extractor/mp4/AtomParsers.java | 16 +++++++--- .../extractor/mp4/FragmentedMp4Extractor.java | 8 +++-- .../extractor/mp4/Mp4Extractor.java | 30 ++++++++++++++++++- .../exoplayer2/extractor/mp4/Track.java | 6 ++-- 5 files changed, 64 insertions(+), 11 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java index ccc5c0eb3e..87165e7a9b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java @@ -67,6 +67,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { } private @MatroskaExtractor.Flags int matroskaFlags; + private @Mp4Extractor.Flags int mp4Flags; private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; private @Mp3Extractor.Flags int mp3Flags; private @TsExtractor.Mode int tsMode; @@ -89,6 +90,18 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { return this; } + /** + * Sets flags for {@link Mp4Extractor} instances created by the factory. + * + * @see Mp4Extractor#Mp4Extractor(int) + * @param flags The flags to use. + * @return The factory, for convenience. + */ + public synchronized DefaultExtractorsFactory setMp4ExtractorFlags(@Mp4Extractor.Flags int flags) { + this.mp4Flags = flags; + return this; + } + /** * Sets flags for {@link FragmentedMp4Extractor} instances created by the factory. * @@ -145,7 +158,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { Extractor[] extractors = new Extractor[FLAC_EXTRACTOR_CONSTRUCTOR == null ? 11 : 12]; extractors[0] = new MatroskaExtractor(matroskaFlags); extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags); - extractors[2] = new Mp4Extractor(); + extractors[2] = new Mp4Extractor(mp4Flags); extractors[3] = new Mp3Extractor(mp3Flags); extractors[4] = new AdtsExtractor(); extractors[5] = new Ac3Extractor(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index e527f310b3..52149f10ef 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -60,11 +60,13 @@ import java.util.List; * @param duration The duration in units of the timescale declared in the mvhd atom, or * {@link C#TIME_UNSET} if the duration should be parsed from the tkhd atom. * @param drmInitData {@link DrmInitData} to be included in the format. + * @param ignoreEditLists Whether to ignore any edit lists in the trak box. * @param isQuickTime True for QuickTime media. False otherwise. * @return A {@link Track} instance, or {@code null} if the track's type isn't supported. */ public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration, - DrmInitData drmInitData, boolean isQuickTime) throws ParserException { + DrmInitData drmInitData, boolean ignoreEditLists, boolean isQuickTime) + throws ParserException { Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data); if (trackType == C.TRACK_TYPE_UNKNOWN) { @@ -88,11 +90,17 @@ import java.util.List; Pair mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id, tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime); - Pair edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); + long[] editListDurations = null; + long[] editListMediaTimes = null; + if (!ignoreEditLists) { + Pair edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); + editListDurations = edtsData.first; + editListMediaTimes = edtsData.second; + } return stsdData.format == null ? null : new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs, stsdData.format, stsdData.requiredSampleTransformation, stsdData.trackEncryptionBoxes, - stsdData.nalUnitLengthFieldLength, edtsData.first, edtsData.second); + stsdData.nalUnitLengthFieldLength, editListDurations, editListMediaTimes); } /** @@ -783,7 +791,7 @@ import java.util.List; * * @param edtsAtom edts (edit box) atom to decode. * @return Pair of edit list durations and edit list media times, or a pair of nulls if they are - * not present. + * not present. */ private static Pair parseEdts(Atom.ContainerAtom edtsAtom) { Atom.LeafAtom elst; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index c3f2a9fb38..3f33a4dd95 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -74,7 +74,7 @@ public final class FragmentedMp4Extractor implements Extractor { @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = {FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME, FLAG_WORKAROUND_IGNORE_TFDT_BOX, FLAG_ENABLE_EMSG_TRACK, FLAG_ENABLE_CEA608_TRACK, - FLAG_SIDELOADED}) + FLAG_SIDELOADED, FLAG_WORKAROUND_IGNORE_EDIT_LISTS}) public @interface Flags {} /** * Flag to work around an issue in some video streams where every frame is marked as a sync frame. @@ -103,6 +103,10 @@ public final class FragmentedMp4Extractor implements Extractor { * container. */ private static final int FLAG_SIDELOADED = 16; + /** + * Flag to ignore any edit lists in the stream. + */ + public static final int FLAG_WORKAROUND_IGNORE_EDIT_LISTS = 32; private static final String TAG = "FragmentedMp4Extractor"; private static final int SAMPLE_GROUP_TYPE_seig = Util.getIntegerCodeForString("seig"); @@ -426,7 +430,7 @@ public final class FragmentedMp4Extractor implements Extractor { Atom.ContainerAtom atom = moov.containerChildren.get(i); if (atom.type == Atom.TYPE_trak) { Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), duration, - drmInitData, false); + drmInitData, (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, false); if (track != null) { tracks.put(track.id, track); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java index d3fe9e0d05..473e17e3e0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java @@ -28,6 +28,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom; +import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor.Flags; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.NalUnitUtil; @@ -57,6 +58,17 @@ public final class Mp4Extractor implements Extractor, SeekMap { }; + /** + * Flags controlling the behavior of the extractor. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {FLAG_WORKAROUND_IGNORE_EDIT_LISTS}) + public @interface Flags {} + /** + * Flag to ignore any edit lists in the stream. + */ + public static final int FLAG_WORKAROUND_IGNORE_EDIT_LISTS = 1; + /** * Parser states. */ @@ -76,6 +88,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { */ private static final long RELOAD_MINIMUM_SEEK_DISTANCE = 256 * 1024; + private final @Flags int flags; + // Temporary arrays. private final ParsableByteArray nalStartCode; private final ParsableByteArray nalLength; @@ -98,7 +112,21 @@ public final class Mp4Extractor implements Extractor, SeekMap { private long durationUs; private boolean isQuickTime; + /** + * Creates a new extractor for unfragmented MP4 streams. + */ public Mp4Extractor() { + this(0); + } + + /** + * Creates a new extractor for unfragmented MP4 streams, using the specified flags to control the + * extractor's behavior. + * + * @param flags Flags that control the extractor's behavior. + */ + public Mp4Extractor(@Flags int flags) { + this.flags = flags; atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); containerAtoms = new Stack<>(); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); @@ -345,7 +373,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { } Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), - C.TIME_UNSET, null, isQuickTime); + C.TIME_UNSET, null, (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, isQuickTime); if (track == null) { continue; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java index 7ac3158794..3adc5a8972 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java @@ -81,12 +81,12 @@ public final class Track { /** * Durations of edit list segments in the movie timescale. Null if there is no edit list. */ - public final long[] editListDurations; + @Nullable public final long[] editListDurations; /** * Media times for edit list segments in the track timescale. Null if there is no edit list. */ - public final long[] editListMediaTimes; + @Nullable public final long[] editListMediaTimes; /** * For H264 video tracks, the length in bytes of the NALUnitLength field in each sample. 0 for @@ -99,7 +99,7 @@ public final class Track { public Track(int id, int type, long timescale, long movieTimescale, long durationUs, Format format, @Transformation int sampleTransformation, @Nullable TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength, - long[] editListDurations, long[] editListMediaTimes) { + @Nullable long[] editListDurations, @Nullable long[] editListMediaTimes) { this.id = id; this.type = type; this.timescale = timescale; From 006263e9836ad929f6cfa169a299407f613fdb1c Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 19 Oct 2017 06:34:02 -0700 Subject: [PATCH 18/24] Retain playback position on re-preparation ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172736350 --- .../com/google/android/exoplayer2/ExoPlayerImplInternal.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index b8274126b5..1e486a06d0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -430,6 +430,10 @@ import java.io.IOException; loadControl.onPrepared(); if (resetPosition) { playbackInfo = new PlaybackInfo(0, C.TIME_UNSET); + } else { + // The new start position is the current playback position. + playbackInfo = new PlaybackInfo(playbackInfo.periodId, playbackInfo.positionUs, + playbackInfo.contentPositionUs); } this.mediaSource = mediaSource; mediaSource.prepareSource(player, true, this); From 9ea8a8a7148cdf9adefcbfee109d54e711d1baac Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 19 Oct 2017 03:00:44 -0700 Subject: [PATCH 19/24] Update HLS sample streams ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172722536 --- demo/src/main/assets/media.exolist.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/src/main/assets/media.exolist.json b/demo/src/main/assets/media.exolist.json index 59d8259d37..38a0c577ae 100644 --- a/demo/src/main/assets/media.exolist.json +++ b/demo/src/main/assets/media.exolist.json @@ -344,11 +344,11 @@ "samples": [ { "name": "Apple 4x3 basic stream", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8" }, { "name": "Apple 16x9 basic stream", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8" }, { "name": "Apple master playlist advanced (TS)", @@ -360,11 +360,11 @@ }, { "name": "Apple TS media playlist", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8" }, { "name": "Apple AAC media playlist", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8" }, { "name": "Apple ID3 metadata", @@ -381,11 +381,11 @@ }, { "name": "Apple AAC 10s", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear0/fileSequence0.aac" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear0/fileSequence0.aac" }, { "name": "Apple TS 10s", - "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts" + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts" }, { "name": "Android screens (Matroska)", From e54841436b185007924d5515c00bd2b1ff169bf3 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 19 Sep 2017 09:57:07 -0700 Subject: [PATCH 20/24] Workaround Samsung tablet reboot playing adaptive secure content ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=169256059 --- .../mediacodec/MediaCodecRenderer.java | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 01229c1104..ffe6313360 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -158,9 +158,26 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2; + @Retention(RetentionPolicy.SOURCE) + @IntDef({ADAPTATION_WORKAROUND_MODE_NEVER, ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION, + ADAPTATION_WORKAROUND_MODE_ALWAYS}) + private @interface AdaptationWorkaroundMode {} + /** + * The adaptation workaround is never used. + */ + private static final int ADAPTATION_WORKAROUND_MODE_NEVER = 0; + /** + * The adaptation workaround is used when adapting between formats of the same resolution only. + */ + private static final int ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION = 1; + /** + * The adaptation workaround is always used when adapting between formats. + */ + private static final int ADAPTATION_WORKAROUND_MODE_ALWAYS = 2; + /** * H.264/AVC buffer to queue when using the adaptation workaround (see - * {@link #codecNeedsAdaptationWorkaround(String)}. Consists of three NAL units with start codes: + * {@link #codecAdaptationWorkaroundMode(String)}. Consists of three NAL units with start codes: * Baseline sequence/picture parameter sets and a 32 * 32 pixel IDR slice. This stream can be * queued to force a resolution change when adapting to a new format. */ @@ -182,9 +199,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private DrmSession pendingDrmSession; private MediaCodec codec; private MediaCodecInfo codecInfo; + private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsFlushWorkaround; - private boolean codecNeedsAdaptationWorkaround; private boolean codecNeedsEosPropagationWorkaround; private boolean codecNeedsEosFlushWorkaround; private boolean codecNeedsEosOutputExceptionWorkaround; @@ -355,9 +372,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } String codecName = codecInfo.name; + codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName); codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); - codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName); codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName); @@ -458,7 +475,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { codecReceivedBuffers = false; codecNeedsDiscardToSpsWorkaround = false; codecNeedsFlushWorkaround = false; - codecNeedsAdaptationWorkaround = false; + codecAdaptationWorkaroundMode = ADAPTATION_WORKAROUND_MODE_NEVER; codecNeedsEosPropagationWorkaround = false; codecNeedsEosFlushWorkaround = false; codecNeedsMonoChannelCountWorkaround = false; @@ -802,8 +819,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { && canReconfigureCodec(codec, codecInfo.adaptive, oldFormat, format)) { codecReconfigured = true; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; - codecNeedsAdaptationWorkaroundBuffer = codecNeedsAdaptationWorkaround - && format.width == oldFormat.width && format.height == oldFormat.height; + codecNeedsAdaptationWorkaroundBuffer = + codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_ALWAYS + || (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION + && format.width == oldFormat.width && format.height == oldFormat.height); } else { if (codecReceivedBuffers) { // Signal end of stream and wait for any final output buffers before re-initialization. @@ -989,7 +1008,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private void processOutputFormat() throws ExoPlaybackException { MediaFormat format = codec.getOutputFormat(); - if (codecNeedsAdaptationWorkaround + if (codecAdaptationWorkaroundMode != ADAPTATION_WORKAROUND_MODE_NEVER && format.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT && format.getInteger(MediaFormat.KEY_HEIGHT) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT) { // We assume this format changed event was caused by the adaptation workaround. @@ -1122,22 +1141,30 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } /** - * Returns whether the decoder is known to get stuck during some adaptations where the resolution - * does not change. + * Returns a mode that specifies when the adaptation workaround should be enabled. *

- * If true is returned, the renderer will work around the issue by queueing and discarding a blank - * frame at a different resolution, which resets the codec's internal state. + * When enabled, the workaround queues and discards a blank frame with a resolution whose width + * and height both equal {@link #ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT}, to reset the codec's + * internal state when a format change occurs. *

* See [Internal: b/27807182]. + * See GitHub issue #3257. * * @param name The name of the decoder. - * @return True if the decoder is known to get stuck during some adaptations. + * @return The mode specifying when the adaptation workaround should be enabled. */ - private static boolean codecNeedsAdaptationWorkaround(String name) { - return Util.SDK_INT < 24 + private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode(String name) { + if (Util.SDK_INT <= 24 && "OMX.Exynos.avc.dec.secure".equals(name) + && Util.MODEL.startsWith("SM-T585")) { + return ADAPTATION_WORKAROUND_MODE_ALWAYS; + } else if (Util.SDK_INT < 24 && ("OMX.Nvidia.h264.decode".equals(name) || "OMX.Nvidia.h264.decode.secure".equals(name)) && ("flounder".equals(Util.DEVICE) || "flounder_lte".equals(Util.DEVICE) - || "grouper".equals(Util.DEVICE) || "tilapia".equals(Util.DEVICE)); + || "grouper".equals(Util.DEVICE) || "tilapia".equals(Util.DEVICE))) { + return ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION; + } else { + return ADAPTATION_WORKAROUND_MODE_NEVER; + } } /** From 5357726a8cc4765d3d72da61c6f8d0a864b075bd Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 18 Oct 2017 04:46:05 -0700 Subject: [PATCH 21/24] Add some additional device specific workarounds Issue: #3355 Issue: #3257 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172587141 --- .../android/exoplayer2/mediacodec/MediaCodecRenderer.java | 2 +- .../android/exoplayer2/video/MediaCodecVideoRenderer.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index ffe6313360..f556269e3a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -1155,7 +1155,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode(String name) { if (Util.SDK_INT <= 24 && "OMX.Exynos.avc.dec.secure".equals(name) - && Util.MODEL.startsWith("SM-T585")) { + && (Util.MODEL.startsWith("SM-T585") || Util.MODEL.startsWith("SM-A520"))) { return ADAPTATION_WORKAROUND_MODE_ALWAYS; } else if (Util.SDK_INT < 24 && ("OMX.Nvidia.h264.decode".equals(name) || "OMX.Nvidia.h264.decode.secure".equals(name)) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 9d769b2050..1215fb00f3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -973,9 +973,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { * If true is returned then we fall back to releasing and re-instantiating the codec instead. */ private static boolean codecNeedsSetOutputSurfaceWorkaround(String name) { - // Work around https://github.com/google/ExoPlayer/issues/3236 - return ("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) - && "OMX.qcom.video.decoder.avc".equals(name); + // Work around https://github.com/google/ExoPlayer/issues/3236 and + // https://github.com/google/ExoPlayer/issues/3355. + return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) + && "OMX.qcom.video.decoder.avc".equals(name)) + || ("tcl_eu".equals(Util.DEVICE) && "OMX.MTK.VIDEO.DECODER.AVC".equals(name)); } /** From 69ec60e0f1406c25705940013c82d80dc4faa158 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 18 Oct 2017 05:59:14 -0700 Subject: [PATCH 22/24] Fix seeking with repeated periods newPlayingPeriodHolder could be set then updated if seeking to a repeated period that was loaded more than once. This led to MediaPeriodHolders leaking. Only set newPlayingPeriodHolder once so that any later holders with the same period identifier get released. Also add a regression test. FakeMediaSource checks that all created MediaPeriods were released when it is released. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172591937 --- .../com/google/android/exoplayer2/ExoPlayerImplInternal.java | 3 ++- .../google/android/exoplayer2/testutil/FakeMediaSource.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 1e486a06d0..38b9d2afcd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -723,7 +723,8 @@ import java.io.IOException; // Clear the timeline, but keep the requested period if it is already prepared. MediaPeriodHolder periodHolder = playingPeriodHolder; while (periodHolder != null) { - if (shouldKeepPeriodHolder(periodId, periodPositionUs, periodHolder)) { + if (newPlayingPeriodHolder == null + && shouldKeepPeriodHolder(periodId, periodPositionUs, periodHolder)) { newPlayingPeriodHolder = periodHolder; } else { periodHolder.release(); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java index a2c1e9879e..93134bf312 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java @@ -111,4 +111,5 @@ public class FakeMediaSource implements MediaSource { } return new TrackGroupArray(trackGroups); } + } From 9a52d63271d5fcd7924a3f503eb19713bda472d2 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 19 Oct 2017 17:48:07 +0100 Subject: [PATCH 23/24] Fix build --- .../android/exoplayer2/mediacodec/MediaCodecRenderer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index f556269e3a..070c59a80a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -23,6 +23,7 @@ import android.media.MediaCrypto; import android.media.MediaFormat; import android.os.Looper; import android.os.SystemClock; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.util.Log; import com.google.android.exoplayer2.BaseRenderer; @@ -43,6 +44,9 @@ import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.Util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; From 04862bccc88d5f1c53fe8394c707fcd1fae488a1 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 19 Oct 2017 09:52:16 -0700 Subject: [PATCH 24/24] Bump to 2.5.4 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172758309 --- RELEASENOTES.md | 18 ++++++++++++++++++ constants.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../exoplayer2/ExoPlayerLibraryInfo.java | 6 +++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c63a20ba94..90b3d15e08 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,23 @@ # Release notes # +### r2.5.4 ### + +* Remove unnecessary media playlist fetches during playback of live HLS streams. +* Add the ability to inject a HLS playlist parser through `HlsMediaSource`. +* Fix potential `IndexOutOfBoundsException` when using `ImaMediaSource` + ([#3334](https://github.com/google/ExoPlayer/issues/3334)). +* Fix an issue parsing MP4 content containing non-CENC sinf boxes. +* Fix memory leak when seeking with repeated periods. +* Fix playback position when `ExoPlayer.prepare` is called with `resetPosition` + set to false. +* Ignore MP4 edit lists that seem invalid + ([#3351](https://github.com/google/ExoPlayer/issues/3351)). +* Add extractor flag for ignoring all MP4 edit lists + ([#3358](https://github.com/google/ExoPlayer/issues/3358)). +* Improve extensibility by exposing public constructors for + `FrameworkMediaCrypto` and by making `DefaultDashChunkSource.getNextChunk` + non-final. + ### r2.5.3 ### * IMA extension: Support skipping of skippable ads on AndroidTV and other diff --git a/constants.gradle b/constants.gradle index db7b12acf0..3402375e87 100644 --- a/constants.gradle +++ b/constants.gradle @@ -24,7 +24,7 @@ project.ext { supportLibraryVersion = '25.4.0' dexmakerVersion = '1.2' mockitoVersion = '1.9.5' - releaseVersion = 'r2.5.3' + releaseVersion = 'r2.5.4' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 612044762f..3fec020c3b 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2504" + android:versionName="2.5.4"> diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 90385ed6c0..62ee8c4873 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -31,13 +31,13 @@ public final class ExoPlayerLibraryInfo { * The version of the library expressed as a string, for example "1.2.3". */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. - public static final String VERSION = "2.5.3"; + public static final String VERSION = "2.5.4"; /** * The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final String VERSION_SLASHY = "ExoPlayerLib/2.5.3"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.5.4"; /** * The version of the library expressed as an integer, for example 1002003. @@ -47,7 +47,7 @@ public final class ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final int VERSION_INT = 2005003; + public static final int VERSION_INT = 2005004; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}