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.
This commit is contained in:
parent
a7032ede38
commit
39cb3f932e
@ -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<HlsMasterPlaylist.HlsUrl>() {
|
||||
@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<HlsMasterPlaylist.HlsUrl>() {
|
||||
@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);
|
||||
|
@ -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<HlsMasterPlaylist.HlsUrl> 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<HlsMasterPlaylist.HlsUrl>() {
|
||||
@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);
|
||||
}
|
||||
}
|
@ -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<HlsPlaylist> 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<HlsPlaylist> 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();
|
||||
}
|
||||
|
@ -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<HlsUrl> variantComparator) {
|
||||
return new HlsMasterPlaylist(baseUri, tags, filterVariants(variants, variantComparator), audios,
|
||||
subtitles, muxedAudioFormat, muxedCaptionFormats);
|
||||
}
|
||||
|
||||
private List<HlsUrl> filterVariants(List<HlsUrl> variants, Comparator<HlsUrl> variantComparator) {
|
||||
List<HlsUrl> reorderedList = new ArrayList<>(variants);
|
||||
Collections.sort(reorderedList, variantComparator);
|
||||
return reorderedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a playlist with a single variant.
|
||||
*
|
||||
|
@ -120,7 +120,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
||||
|
||||
private final Uri initialPlaylistUri;
|
||||
private final HlsDataSourceFactory dataSourceFactory;
|
||||
private final HlsPlaylistParser playlistParser;
|
||||
private final ParsingLoadable.Parser<HlsPlaylist> playlistParser;
|
||||
private final int minRetryCount;
|
||||
private final IdentityHashMap<HlsUrl, MediaPlaylistBundle> playlistBundles;
|
||||
private final Handler playlistRefreshHandler;
|
||||
@ -145,7 +145,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
||||
*/
|
||||
public HlsPlaylistTracker(Uri initialPlaylistUri, HlsDataSourceFactory dataSourceFactory,
|
||||
EventDispatcher eventDispatcher, int minRetryCount,
|
||||
PrimaryPlaylistListener primaryPlaylistListener) {
|
||||
PrimaryPlaylistListener primaryPlaylistListener, ParsingLoadable.Parser<HlsPlaylist> playlistParser) {
|
||||
this.initialPlaylistUri = initialPlaylistUri;
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
@ -153,7 +153,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
||||
this.primaryPlaylistListener = primaryPlaylistListener;
|
||||
listeners = new ArrayList<>();
|
||||
initialPlaylistLoader = new Loader("HlsPlaylistTracker:MasterPlaylist");
|
||||
playlistParser = new HlsPlaylistParser();
|
||||
this.playlistParser = playlistParser;
|
||||
playlistBundles = new IdentityHashMap<>();
|
||||
playlistRefreshHandler = new Handler();
|
||||
}
|
||||
|
@ -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<HlsPlaylist> {
|
||||
private final ParsingLoadable.Parser<HlsPlaylist> playlistParser;
|
||||
private final Comparator<HlsMasterPlaylist.HlsUrl> 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<HlsPlaylist> playlistParser,
|
||||
Comparator<HlsMasterPlaylist.HlsUrl> 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user