Skip to content

Commit

Permalink
Allow HlsPlaylistTracker to change the primaryHlsUrl
Browse files Browse the repository at this point in the history
When the primary url is blacklisted (due to a 404, for example) or
the selected variant is different from primary url, allow the tracker
to change the url.

Issue:#87

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=141291435
  • Loading branch information
AquilesCanta authored and ojw28 committed Dec 12, 2016
1 parent 8765b19 commit 1cbc0fc
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public static boolean maybeBlacklistTrack(TrackSelection trackSelection, int tra

/**
* Blacklists {@code trackSelectionIndex} in {@code trackSelection} for
* {@code blacklistDurationMs} if {@code e} is an {@link InvalidResponseCodeException} with
* {@link InvalidResponseCodeException#responseCode} equal to 404 or 410. Else does nothing. Note
* that blacklisting will fail if the track is the only non-blacklisted track in the selection.
* {@code blacklistDurationMs} if calling {@link #shouldBlacklist(Exception)} for {@code e}
* returns true. Else does nothing. Note that blacklisting will fail if the track is the only
* non-blacklisted track in the selection.
*
* @param trackSelection The track selection.
* @param trackSelectionIndex The index in the selection to consider blacklisting.
Expand All @@ -63,24 +63,33 @@ public static boolean maybeBlacklistTrack(TrackSelection trackSelection, int tra
*/
public static boolean maybeBlacklistTrack(TrackSelection trackSelection, int trackSelectionIndex,
Exception e, long blacklistDurationMs) {
if (trackSelection.length() == 1) {
// Blacklisting won't ever work if there's only one track in the selection.
return false;
if (shouldBlacklist(e)) {
boolean blacklisted = trackSelection.blacklist(trackSelectionIndex, blacklistDurationMs);
int responseCode = ((InvalidResponseCodeException) e).responseCode;
if (blacklisted) {
Log.w(TAG, "Blacklisted: duration=" + blacklistDurationMs + ", responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
} else {
Log.w(TAG, "Blacklisting failed (cannot blacklist last enabled track): responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
}
return blacklisted;
}
return false;
}

/**
* Returns whether a loading error is an {@link InvalidResponseCodeException} with
* {@link InvalidResponseCodeException#responseCode} equal to 404 or 410.
*
* @param e The loading error.
* @return Wheter the loading error is an {@link InvalidResponseCodeException} with
* {@link InvalidResponseCodeException#responseCode} equal to 404 or 410.
*/
public static boolean shouldBlacklist(Exception e) {
if (e instanceof InvalidResponseCodeException) {
InvalidResponseCodeException responseCodeException = (InvalidResponseCodeException) e;
int responseCode = responseCodeException.responseCode;
if (responseCode == 404 || responseCode == 410) {
boolean blacklisted = trackSelection.blacklist(trackSelectionIndex, blacklistDurationMs);
if (blacklisted) {
Log.w(TAG, "Blacklisted: duration=" + blacklistDurationMs + ", responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
} else {
Log.w(TAG, "Blacklisting failed (cannot blacklist last enabled track): responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
}
return blacklisted;
}
int responseCode = ((InvalidResponseCodeException) e).responseCode;
return responseCode == 404 || responseCode == 410;
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,10 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
null);
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, variants[newVariantIndex],
trackSelection.getSelectionReason(), trackSelection.getSelectionData(), startTimeUs,
startTimeUs + segment.durationUs, chunkMediaSequence, segment.discontinuitySequenceNumber,
isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv);
trackSelection.getSelectionReason(), trackSelection.getSelectionData(),
startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence,
segment.discontinuitySequenceNumber, isTimestampMaster, timestampAdjuster, previous,
encryptionKey, encryptionIv);
}

/**
Expand Down Expand Up @@ -317,19 +318,19 @@ public boolean onChunkLoadError(Chunk chunk, boolean cancelable, IOException err
}

/**
* Called when an error is encountered while loading a playlist.
* Called when a playlist is blacklisted.
*
* @param url The url that references the playlist whose load encountered the error.
* @param error The error.
* @param url The url that references the blacklisted playlist.
* @param blacklistMs The amount of milliseconds for which the playlist was blacklisted.
*/
public void onPlaylistLoadError(HlsUrl url, IOException error) {
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
int trackGroupIndex = trackGroup.indexOf(url.format);
if (trackGroupIndex == C.INDEX_UNSET) {
// The url is not handled by this chunk source.
return;
if (trackGroupIndex != C.INDEX_UNSET) {
int trackSelectionIndex = trackSelection.indexOf(trackGroupIndex);
if (trackSelectionIndex != C.INDEX_UNSET) {
trackSelection.blacklist(trackSelectionIndex, blacklistMs);
}
}
ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection,
trackSelection.indexOf(trackGroupIndex), error);
}

// Private methods.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -42,7 +41,7 @@
* A {@link MediaPeriod} that loads an HLS stream.
*/
public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper.Callback,
HlsPlaylistTracker.PlaylistRefreshCallback {
HlsPlaylistTracker.PlaylistEventListener {

private final HlsPlaylistTracker playlistTracker;
private final DataSource.Factory dataSourceFactory;
Expand All @@ -52,7 +51,6 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private final IdentityHashMap<SampleStream, Integer> streamWrapperIndices;
private final TimestampAdjusterProvider timestampAdjusterProvider;
private final Handler continueLoadingHandler;
private final Loader manifestFetcher;
private final long preparePositionUs;

private Callback callback;
Expand All @@ -74,13 +72,12 @@ public HlsMediaPeriod(HlsPlaylistTracker playlistTracker, DataSource.Factory dat
streamWrapperIndices = new IdentityHashMap<>();
timestampAdjusterProvider = new TimestampAdjusterProvider();
continueLoadingHandler = new Handler();
manifestFetcher = new Loader("Loader:ManifestFetcher");
preparePositionUs = positionUs;
}

public void release() {
playlistTracker.removeListener(this);
continueLoadingHandler.removeCallbacksAndMessages(null);
manifestFetcher.release();
if (sampleStreamWrappers != null) {
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
sampleStreamWrapper.release();
Expand All @@ -90,15 +87,14 @@ public void release() {

@Override
public void prepare(Callback callback) {
playlistTracker.addListener(this);
this.callback = callback;
buildAndPrepareSampleStreamWrappers();
}

@Override
public void maybeThrowPrepareError() throws IOException {
if (sampleStreamWrappers == null) {
manifestFetcher.maybeThrowError();
} else {
if (sampleStreamWrappers != null) {
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
sampleStreamWrapper.maybeThrowPrepareError();
}
Expand Down Expand Up @@ -255,7 +251,7 @@ public void onPrepared() {

@Override
public void onPlaylistRefreshRequired(HlsUrl url) {
playlistTracker.refreshPlaylist(url, this);
playlistTracker.refreshPlaylist(url);
}

@Override
Expand All @@ -271,22 +267,15 @@ public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrappe

@Override
public void onPlaylistChanged() {
if (trackGroups != null) {
callback.onContinueLoadingRequested(this);
} else {
// Some of the wrappers were waiting for their media playlist to prepare.
for (HlsSampleStreamWrapper wrapper : sampleStreamWrappers) {
wrapper.continuePreparing();
}
}
continuePreparingOrLoading();
}

@Override
public void onPlaylistLoadError(HlsUrl url, IOException error) {
for (HlsSampleStreamWrapper sampleStreamWrapper : enabledSampleStreamWrappers) {
sampleStreamWrapper.onPlaylistLoadError(url, error);
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
for (HlsSampleStreamWrapper streamWrapper : sampleStreamWrappers) {
streamWrapper.onPlaylistBlacklisted(url, blacklistMs);
}
callback.onContinueLoadingRequested(this);
continuePreparingOrLoading();
}

// Internal methods.
Expand Down Expand Up @@ -363,6 +352,17 @@ private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, HlsUrl[]
eventDispatcher);
}

private void continuePreparingOrLoading() {
if (trackGroups != null) {
callback.onContinueLoadingRequested(this);
} else {
// Some of the wrappers were waiting for their media playlist to prepare.
for (HlsSampleStreamWrapper wrapper : sampleStreamWrappers) {
wrapper.continuePreparing();
}
}
}

private static boolean variantHasExplicitCodecWithPrefix(HlsUrl variant, String prefix) {
String codecs = variant.format.codecs;
if (TextUtils.isEmpty(codecs)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener l

@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
playlistTracker.maybeThrowPrimaryPlaylistRefreshError();
playlistTracker.maybeThrowPlaylistRefreshError();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ public void setIsTimestampMaster(boolean isTimestampMaster) {
chunkSource.setIsTimestampMaster(isTimestampMaster);
}

public void onPlaylistLoadError(HlsUrl url, IOException error) {
chunkSource.onPlaylistLoadError(url, error);
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
chunkSource.onPlaylistBlacklisted(url, blacklistMs);
}

// SampleStream implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,21 @@ public int compareTo(Long relativeStartTimeUs) {
public final long startTimeUs;
public final int mediaSequence;
public final int version;
public final Segment initializationSegment;
public final List<Segment> segments;
public final long targetDurationUs;
public final boolean hasEndTag;
public final boolean hasProgramDateTime;
public final Segment initializationSegment;
public final List<Segment> segments;
public final long durationUs;

public HlsMediaPlaylist(String baseUri, long startTimeUs, int mediaSequence, int version,
boolean hasEndTag, boolean hasProgramDateTime, Segment initializationSegment,
List<Segment> segments) {
public HlsMediaPlaylist(String baseUri, long startTimeUs, int mediaSequence,
int version, long targetDurationUs, boolean hasEndTag, boolean hasProgramDateTime,
Segment initializationSegment, List<Segment> segments) {
super(baseUri, HlsPlaylist.TYPE_MEDIA);
this.startTimeUs = startTimeUs;
this.mediaSequence = mediaSequence;
this.version = version;
this.targetDurationUs = targetDurationUs;
this.hasEndTag = hasEndTag;
this.hasProgramDateTime = hasProgramDateTime;
this.initializationSegment = initializationSegment;
Expand All @@ -105,8 +107,8 @@ public long getEndTimeUs() {
}

public HlsMediaPlaylist copyWithStartTimeUs(long startTimeUs) {
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, hasEndTag,
hasProgramDateTime, initializationSegment, segments);
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, targetDurationUs,
hasEndTag, hasProgramDateTime, initializationSegment, segments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_BANDWIDTH = Pattern.compile("BANDWIDTH=(\\d+)\\b");
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
private static final Pattern REGEX_TARGET_DURATION = Pattern.compile(TAG_TARGET_DURATION
+ ":(\\d+)\\b");
private static final Pattern REGEX_VERSION = Pattern.compile(TAG_VERSION + ":(\\d+)\\b");
private static final Pattern REGEX_MEDIA_SEQUENCE = Pattern.compile(TAG_MEDIA_SEQUENCE
+ ":(\\d+)\\b");
Expand Down Expand Up @@ -207,6 +209,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String
throws IOException {
int mediaSequence = 0;
int version = 1; // Default version == 1.
long targetDurationUs = C.TIME_UNSET;
boolean hasEndTag = false;
Segment initializationSegment = null;
List<Segment> segments = new ArrayList<>();
Expand Down Expand Up @@ -239,6 +242,8 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String
initializationSegment = new Segment(uri, segmentByteRangeOffset, segmentByteRangeLength);
segmentByteRangeOffset = 0;
segmentByteRangeLength = C.LENGTH_UNSET;
} else if (line.startsWith(TAG_TARGET_DURATION)) {
targetDurationUs = parseIntAttr(line, REGEX_TARGET_DURATION) * C.MICROS_PER_SECOND;
} else if (line.startsWith(TAG_MEDIA_SEQUENCE)) {
mediaSequence = parseIntAttr(line, REGEX_MEDIA_SEQUENCE);
segmentMediaSequence = mediaSequence;
Expand Down Expand Up @@ -300,8 +305,8 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String
hasEndTag = true;
}
}
return new HlsMediaPlaylist(baseUri, playlistStartTimeUs, mediaSequence, version, hasEndTag,
playlistStartTimeUs != 0, initializationSegment, segments);
return new HlsMediaPlaylist(baseUri, playlistStartTimeUs, mediaSequence, version,
targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments);
}

private static String parseStringAttr(String line, Pattern pattern) throws ParserException {
Expand Down
Loading

0 comments on commit 1cbc0fc

Please sign in to comment.