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.DataSpec;
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.UriUtil;
import com.google.android.exoplayer.util.Util;
@ -51,7 +50,6 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
@ -72,7 +70,6 @@ public class HlsChunkSource {
private static final String VTT_FILE_EXTENSION = ".vtt";
private static final String WEBVTT_FILE_EXTENSION = ".webvtt";
private final ManifestFetcher<HlsPlaylist> manifestFetcher;
private final int type;
private final DataSource dataSource;
private final FormatEvaluator adaptiveFormatEvaluator;
@ -84,8 +81,9 @@ public class HlsChunkSource {
private boolean live;
private long durationUs;
private IOException fatalError;
private HlsMasterPlaylist masterPlaylist;
private String baseUri;
private Format muxedAudioFormat;
private Format muxedCaptionFormat;
private Uri encryptionKeyUri;
private byte[] encryptionKey;
@ -103,7 +101,6 @@ public class HlsChunkSource {
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},
* {@link C#TRACK_TYPE_AUDIO} and {@link C#TRACK_TYPE_TEXT}.
* @param dataSource A {@link DataSource} suitable for loading the media data.
@ -112,10 +109,9 @@ public class HlsChunkSource {
* same provider.
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
*/
public HlsChunkSource(ManifestFetcher<HlsPlaylist> manifestFetcher, int type,
DataSource dataSource, PtsTimestampAdjusterProvider timestampAdjusterProvider,
public HlsChunkSource(int type, DataSource dataSource,
PtsTimestampAdjusterProvider timestampAdjusterProvider,
FormatEvaluator adaptiveFormatEvaluator) {
this.manifestFetcher = manifestFetcher;
this.type = type;
this.dataSource = dataSource;
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
@ -148,39 +144,15 @@ public class HlsChunkSource {
/**
* Prepares the source.
*
* @return True if the source was prepared, false otherwise.
* @param playlist A {@link HlsPlaylist}.
*/
public boolean prepare() throws IOException {
if (masterPlaylist == null) {
HlsPlaylist playlist = manifestFetcher.getManifest();
if (playlist == null) {
manifestFetcher.maybeThrowError();
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);
public void prepare(HlsPlaylist playlist) {
processPlaylist(playlist);
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;
}
/**
* Returns whether this is a live playback.
@ -235,7 +207,7 @@ public class HlsChunkSource {
* @return The format of the audio muxed into variants, or null if unknown.
*/
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.
*/
public Format getMuxedCaptionFormat() {
return masterPlaylist.muxedCaptionFormat;
return muxedCaptionFormat;
}
/**
@ -521,9 +493,33 @@ public class HlsChunkSource {
// 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) {
List<Variant> variantList = type == C.TRACK_TYPE_AUDIO ? playlist.audios : playlist.subtitles;
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) {
List<Variant> variantList = type == C.TRACK_TYPE_AUDIO ? masterPlaylist.audios
: masterPlaylist.subtitles;
if (variantList != null && !variantList.isEmpty()) {
variants = new Variant[variantList.size()];
variantList.toArray(variants);
@ -535,8 +531,8 @@ public class HlsChunkSource {
return;
}
// Type is TYPE_DEFAULT.
List<Variant> enabledVariantList = new ArrayList<>(playlist.variants);
// type == C.TRACK_TYPE_DEFAULT
List<Variant> enabledVariantList = new ArrayList<>(masterPlaylist.variants);
ArrayList<Variant> definiteVideoVariants = new ArrayList<>();
ArrayList<Variant> definiteAudioOnlyVariants = new ArrayList<>();
for (int i = 0; i < enabledVariantList.size(); i++) {

View File

@ -48,7 +48,14 @@ import java.util.List;
*/
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 HlsChunkSource[] chunkSources;
private final HlsTrackStreamWrapper[] trackStreamWrappers;
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
private final int[] selectedTrackCounts;
@ -56,6 +63,7 @@ public final class HlsSampleSource implements SampleSource {
private boolean prepared;
private boolean seenFirstTrackSelection;
private long durationUs;
private HlsPlaylist playlist;
private TrackGroupArray trackGroups;
private HlsTrackStreamWrapper[] enabledTrackStreamWrappers;
@ -71,27 +79,30 @@ public final class HlsSampleSource implements SampleSource {
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_DEFAULT,
HlsChunkSource defaultChunkSource = new HlsChunkSource(C.TRACK_TYPE_DEFAULT,
defaultDataSource, timestampAdjusterProvider,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
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);
HlsChunkSource audioChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_AUDIO,
HlsChunkSource audioChunkSource = new HlsChunkSource(C.TRACK_TYPE_AUDIO,
audioDataSource, timestampAdjusterProvider, null);
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);
HlsChunkSource subtitleChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT,
subtitleDataSource, timestampAdjusterProvider, null);
HlsTrackStreamWrapper subtitleTrackStreamWrapper = new HlsTrackStreamWrapper(
subtitleChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener,
C.TRACK_TYPE_TEXT);
DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource textChunkSource = new HlsChunkSource(C.TRACK_TYPE_TEXT, textDataSource,
timestampAdjusterProvider, null);
HlsTrackStreamWrapper textTrackStreamWrapper = new HlsTrackStreamWrapper(
textChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener,
C.TRACK_TYPE_TEXT, MIN_LOADABLE_RETRY_COUNT);
chunkSources = new HlsChunkSource[] {defaultChunkSource, audioChunkSource, textChunkSource};
trackStreamWrappers = new HlsTrackStreamWrapper[] {defaultTrackStreamWrapper,
audioTrackStreamWrapper, subtitleTrackStreamWrapper};
audioTrackStreamWrapper, textTrackStreamWrapper};
selectedTrackCounts = new int[trackStreamWrappers.length];
trackStreamSources = new IdentityHashMap<>();
}
@ -101,6 +112,19 @@ public final class HlsSampleSource implements SampleSource {
if (prepared) {
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;
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
trackStreamWrappersPrepared &= trackStreamWrapper.prepare(positionUs);
@ -108,6 +132,7 @@ public final class HlsSampleSource implements SampleSource {
if (!trackStreamWrappersPrepared) {
return false;
}
durationUs = 0;
int totalTrackGroupCount = 0;
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {

View File

@ -49,11 +49,6 @@ import java.util.List;
*/
/* 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_TEXT = 1;
private static final int PRIMARY_TYPE_AUDIO = 2;
@ -89,32 +84,6 @@ import java.util.List;
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 loadControl Controls when the source is permitted to load data.
@ -144,9 +113,6 @@ import java.util.List;
if (prepared) {
return true;
}
if (!chunkSource.prepare()) {
return false;
}
if (chunkSource.getTrackCount() == 0) {
trackGroups = new TrackGroupArray();
prepared = true;