Refactor #6.HLS.3

- Pull loading of the initial manifest up to HlsSampleSource.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123305312
This commit is contained in:
olly 2016-05-26 04:00:31 -07:00 committed by Oliver Woodman
parent e74fc80aab
commit 1ea149a4d2
3 changed files with 76 additions and 89 deletions

View File

@ -36,7 +36,6 @@ import com.google.android.exoplayer.hls.playlist.Variant;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException; import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.UriUtil; import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
@ -51,7 +50,6 @@ import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -72,7 +70,6 @@ public class HlsChunkSource {
private static final String VTT_FILE_EXTENSION = ".vtt"; private static final String VTT_FILE_EXTENSION = ".vtt";
private static final String WEBVTT_FILE_EXTENSION = ".webvtt"; private static final String WEBVTT_FILE_EXTENSION = ".webvtt";
private final ManifestFetcher<HlsPlaylist> manifestFetcher;
private final int type; private final int type;
private final DataSource dataSource; private final DataSource dataSource;
private final FormatEvaluator adaptiveFormatEvaluator; private final FormatEvaluator adaptiveFormatEvaluator;
@ -84,8 +81,9 @@ public class HlsChunkSource {
private boolean live; private boolean live;
private long durationUs; private long durationUs;
private IOException fatalError; private IOException fatalError;
private HlsMasterPlaylist masterPlaylist;
private String baseUri; private String baseUri;
private Format muxedAudioFormat;
private Format muxedCaptionFormat;
private Uri encryptionKeyUri; private Uri encryptionKeyUri;
private byte[] encryptionKey; private byte[] encryptionKey;
@ -103,7 +101,6 @@ public class HlsChunkSource {
private boolean[] enabledVariantBlacklistFlags; private boolean[] enabledVariantBlacklistFlags;
/** /**
* @param manifestFetcher A fetcher for the playlist.
* @param type The type of chunk provided by the source. One of {@link C#TRACK_TYPE_DEFAULT}, * @param type The type of chunk provided by the source. One of {@link C#TRACK_TYPE_DEFAULT},
* {@link C#TRACK_TYPE_AUDIO} and {@link C#TRACK_TYPE_TEXT}. * {@link C#TRACK_TYPE_AUDIO} and {@link C#TRACK_TYPE_TEXT}.
* @param dataSource A {@link DataSource} suitable for loading the media data. * @param dataSource A {@link DataSource} suitable for loading the media data.
@ -112,10 +109,9 @@ public class HlsChunkSource {
* same provider. * same provider.
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
*/ */
public HlsChunkSource(ManifestFetcher<HlsPlaylist> manifestFetcher, int type, public HlsChunkSource(int type, DataSource dataSource,
DataSource dataSource, PtsTimestampAdjusterProvider timestampAdjusterProvider, PtsTimestampAdjusterProvider timestampAdjusterProvider,
FormatEvaluator adaptiveFormatEvaluator) { FormatEvaluator adaptiveFormatEvaluator) {
this.manifestFetcher = manifestFetcher;
this.type = type; this.type = type;
this.dataSource = dataSource; this.dataSource = dataSource;
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator; this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
@ -148,38 +144,14 @@ public class HlsChunkSource {
/** /**
* Prepares the source. * Prepares the source.
* *
* @return True if the source was prepared, false otherwise. * @param playlist A {@link HlsPlaylist}.
*/ */
public boolean prepare() throws IOException { public void prepare(HlsPlaylist playlist) {
if (masterPlaylist == null) { processPlaylist(playlist);
HlsPlaylist playlist = manifestFetcher.getManifest(); if (variants.length > 0) {
if (playlist == null) { // Select the first variant listed in the master playlist.
manifestFetcher.maybeThrowError(); selectTracks(new int[] {0});
manifestFetcher.requestRefresh();
return false;
} else {
baseUri = playlist.baseUri;
if (playlist.type == HlsPlaylist.TYPE_MASTER) {
masterPlaylist = (HlsMasterPlaylist) playlist;
} else {
Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null,
Format.NO_VALUE);
List<Variant> variants = new ArrayList<>();
variants.add(new Variant(baseUri, format, null));
masterPlaylist = new HlsMasterPlaylist(baseUri, variants,
Collections.<Variant>emptyList(), Collections.<Variant>emptyList(), null, null);
}
processMasterPlaylist(masterPlaylist);
if (variants.length > 0) {
if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
}
// Select the first variant listed in the master playlist.
selectTracks(new int[] {0});
}
}
} }
return true;
} }
/** /**
@ -235,7 +207,7 @@ public class HlsChunkSource {
* @return The format of the audio muxed into variants, or null if unknown. * @return The format of the audio muxed into variants, or null if unknown.
*/ */
public Format getMuxedAudioFormat() { public Format getMuxedAudioFormat() {
return masterPlaylist.muxedAudioFormat; return muxedAudioFormat;
} }
/** /**
@ -246,7 +218,7 @@ public class HlsChunkSource {
* @return The format of the captions muxed into variants, or null if unknown. * @return The format of the captions muxed into variants, or null if unknown.
*/ */
public Format getMuxedCaptionFormat() { public Format getMuxedCaptionFormat() {
return masterPlaylist.muxedCaptionFormat; return muxedCaptionFormat;
} }
/** /**
@ -521,9 +493,33 @@ public class HlsChunkSource {
// Private methods. // Private methods.
private void processMasterPlaylist(HlsMasterPlaylist playlist) { private void processPlaylist(HlsPlaylist playlist) {
baseUri = playlist.baseUri;
if (playlist instanceof HlsMediaPlaylist) {
if (type == C.TRACK_TYPE_TEXT || type == C.TRACK_TYPE_AUDIO) {
variants = new Variant[0];
variantPlaylists = new HlsMediaPlaylist[variants.length];
variantLastPlaylistLoadTimesMs = new long[variants.length];
return;
}
// type == C.TRACK_TYPE_DEFAULT
Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null,
Format.NO_VALUE);
variants = new Variant[] {new Variant(baseUri, format, null)};
variantPlaylists = new HlsMediaPlaylist[variants.length];
variantLastPlaylistLoadTimesMs = new long[variants.length];
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
return;
}
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
muxedAudioFormat = masterPlaylist.muxedAudioFormat;
muxedCaptionFormat = masterPlaylist.muxedCaptionFormat;
if (type == C.TRACK_TYPE_TEXT || type == C.TRACK_TYPE_AUDIO) { if (type == C.TRACK_TYPE_TEXT || type == C.TRACK_TYPE_AUDIO) {
List<Variant> variantList = type == C.TRACK_TYPE_AUDIO ? playlist.audios : playlist.subtitles; List<Variant> variantList = type == C.TRACK_TYPE_AUDIO ? masterPlaylist.audios
: masterPlaylist.subtitles;
if (variantList != null && !variantList.isEmpty()) { if (variantList != null && !variantList.isEmpty()) {
variants = new Variant[variantList.size()]; variants = new Variant[variantList.size()];
variantList.toArray(variants); variantList.toArray(variants);
@ -535,8 +531,8 @@ public class HlsChunkSource {
return; return;
} }
// Type is TYPE_DEFAULT. // type == C.TRACK_TYPE_DEFAULT
List<Variant> enabledVariantList = new ArrayList<>(playlist.variants); List<Variant> enabledVariantList = new ArrayList<>(masterPlaylist.variants);
ArrayList<Variant> definiteVideoVariants = new ArrayList<>(); ArrayList<Variant> definiteVideoVariants = new ArrayList<>();
ArrayList<Variant> definiteAudioOnlyVariants = new ArrayList<>(); ArrayList<Variant> definiteAudioOnlyVariants = new ArrayList<>();
for (int i = 0; i < enabledVariantList.size(); i++) { for (int i = 0; i < enabledVariantList.size(); i++) {

View File

@ -48,7 +48,14 @@ import java.util.List;
*/ */
public final class HlsSampleSource implements SampleSource { public final class HlsSampleSource implements SampleSource {
/**
* The minimum number of times to retry loading data prior to failing.
*/
// TODO: Use this for playlist loads as well.
private static final int MIN_LOADABLE_RETRY_COUNT = 3;
private final ManifestFetcher<HlsPlaylist> manifestFetcher; private final ManifestFetcher<HlsPlaylist> manifestFetcher;
private final HlsChunkSource[] chunkSources;
private final HlsTrackStreamWrapper[] trackStreamWrappers; private final HlsTrackStreamWrapper[] trackStreamWrappers;
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources; private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
private final int[] selectedTrackCounts; private final int[] selectedTrackCounts;
@ -56,6 +63,7 @@ public final class HlsSampleSource implements SampleSource {
private boolean prepared; private boolean prepared;
private boolean seenFirstTrackSelection; private boolean seenFirstTrackSelection;
private long durationUs; private long durationUs;
private HlsPlaylist playlist;
private TrackGroupArray trackGroups; private TrackGroupArray trackGroups;
private HlsTrackStreamWrapper[] enabledTrackStreamWrappers; private HlsTrackStreamWrapper[] enabledTrackStreamWrappers;
@ -71,27 +79,30 @@ public final class HlsSampleSource implements SampleSource {
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_DEFAULT, HlsChunkSource defaultChunkSource = new HlsChunkSource(C.TRACK_TYPE_DEFAULT,
defaultDataSource, timestampAdjusterProvider, defaultDataSource, timestampAdjusterProvider,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter)); new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
HlsTrackStreamWrapper defaultTrackStreamWrapper = new HlsTrackStreamWrapper(defaultChunkSource, HlsTrackStreamWrapper defaultTrackStreamWrapper = new HlsTrackStreamWrapper(defaultChunkSource,
loadControl, C.DEFAULT_MUXED_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO); loadControl, C.DEFAULT_MUXED_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO,
MIN_LOADABLE_RETRY_COUNT);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource audioChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_AUDIO, HlsChunkSource audioChunkSource = new HlsChunkSource(C.TRACK_TYPE_AUDIO,
audioDataSource, timestampAdjusterProvider, null); audioDataSource, timestampAdjusterProvider, null);
HlsTrackStreamWrapper audioTrackStreamWrapper = new HlsTrackStreamWrapper(audioChunkSource, HlsTrackStreamWrapper audioTrackStreamWrapper = new HlsTrackStreamWrapper(audioChunkSource,
loadControl, C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO); loadControl, C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO,
MIN_LOADABLE_RETRY_COUNT);
DataSource subtitleDataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource subtitleChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT, HlsChunkSource textChunkSource = new HlsChunkSource(C.TRACK_TYPE_TEXT, textDataSource,
subtitleDataSource, timestampAdjusterProvider, null); timestampAdjusterProvider, null);
HlsTrackStreamWrapper subtitleTrackStreamWrapper = new HlsTrackStreamWrapper( HlsTrackStreamWrapper textTrackStreamWrapper = new HlsTrackStreamWrapper(
subtitleChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener, textChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener,
C.TRACK_TYPE_TEXT); C.TRACK_TYPE_TEXT, MIN_LOADABLE_RETRY_COUNT);
chunkSources = new HlsChunkSource[] {defaultChunkSource, audioChunkSource, textChunkSource};
trackStreamWrappers = new HlsTrackStreamWrapper[] {defaultTrackStreamWrapper, trackStreamWrappers = new HlsTrackStreamWrapper[] {defaultTrackStreamWrapper,
audioTrackStreamWrapper, subtitleTrackStreamWrapper}; audioTrackStreamWrapper, textTrackStreamWrapper};
selectedTrackCounts = new int[trackStreamWrappers.length]; selectedTrackCounts = new int[trackStreamWrappers.length];
trackStreamSources = new IdentityHashMap<>(); trackStreamSources = new IdentityHashMap<>();
} }
@ -101,6 +112,19 @@ public final class HlsSampleSource implements SampleSource {
if (prepared) { if (prepared) {
return true; return true;
} }
if (playlist == null) {
playlist = manifestFetcher.getManifest();
if (playlist == null) {
manifestFetcher.maybeThrowError();
manifestFetcher.requestRefresh();
return false;
}
for (HlsChunkSource chunkSource : chunkSources) {
chunkSource.prepare(playlist);
}
}
boolean trackStreamWrappersPrepared = true; boolean trackStreamWrappersPrepared = true;
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) { for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
trackStreamWrappersPrepared &= trackStreamWrapper.prepare(positionUs); trackStreamWrappersPrepared &= trackStreamWrapper.prepare(positionUs);
@ -108,6 +132,7 @@ public final class HlsSampleSource implements SampleSource {
if (!trackStreamWrappersPrepared) { if (!trackStreamWrappersPrepared) {
return false; return false;
} }
durationUs = 0; durationUs = 0;
int totalTrackGroupCount = 0; int totalTrackGroupCount = 0;
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) { for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {

View File

@ -49,11 +49,6 @@ import java.util.List;
*/ */
/* package */ final class HlsTrackStreamWrapper implements Loader.Callback<Chunk>, ExtractorOutput { /* package */ final class HlsTrackStreamWrapper implements Loader.Callback<Chunk>, ExtractorOutput {
/**
* The default minimum number of times to retry loading data prior to failing.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
private static final int PRIMARY_TYPE_NONE = 0; private static final int PRIMARY_TYPE_NONE = 0;
private static final int PRIMARY_TYPE_TEXT = 1; private static final int PRIMARY_TYPE_TEXT = 1;
private static final int PRIMARY_TYPE_AUDIO = 2; private static final int PRIMARY_TYPE_AUDIO = 2;
@ -89,32 +84,6 @@ import java.util.List;
private boolean loadingFinished; private boolean loadingFinished;
/**
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @param loadControl Controls when the source is permitted to load data.
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
*/
public HlsTrackStreamWrapper(HlsChunkSource chunkSource, LoadControl loadControl,
int bufferSizeContribution) {
this(chunkSource, loadControl, bufferSizeContribution, null, null, 0);
}
/**
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @param loadControl Controls when the source is permitted to load data.
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param eventSourceId An identifier that gets passed to {@code eventListener} methods.
*/
public HlsTrackStreamWrapper(HlsChunkSource chunkSource, LoadControl loadControl,
int bufferSizeContribution, Handler eventHandler,
ChunkTrackStreamEventListener eventListener, int eventSourceId) {
this(chunkSource, loadControl, bufferSizeContribution, eventHandler, eventListener,
eventSourceId, DEFAULT_MIN_LOADABLE_RETRY_COUNT);
}
/** /**
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @param loadControl Controls when the source is permitted to load data. * @param loadControl Controls when the source is permitted to load data.
@ -144,9 +113,6 @@ import java.util.List;
if (prepared) { if (prepared) {
return true; return true;
} }
if (!chunkSource.prepare()) {
return false;
}
if (chunkSource.getTrackCount() == 0) { if (chunkSource.getTrackCount() == 0) {
trackGroups = new TrackGroupArray(); trackGroups = new TrackGroupArray();
prepared = true; prepared = true;