mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
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:
parent
e74fc80aab
commit
1ea149a4d2
@ -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,38 +144,14 @@ 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);
|
||||
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});
|
||||
}
|
||||
}
|
||||
public void prepare(HlsPlaylist playlist) {
|
||||
processPlaylist(playlist);
|
||||
if (variants.length > 0) {
|
||||
// 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.
|
||||
*/
|
||||
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) {
|
||||
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 ? playlist.audios : playlist.subtitles;
|
||||
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++) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user