Merge pull request #7661 from TiA4f8R/livestreams-improvements

Increase playlist stuck target duration coefficient and catch BehindLiveWindowExceptions properly
This commit is contained in:
Robin 2022-02-01 11:38:12 +01:00 committed by GitHub
commit e865c4350e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 23 deletions

View File

@ -2517,23 +2517,11 @@ public final class Player implements
Log.e(TAG, "ExoPlayer - onPlayerError() called with:", error);
saveStreamProgressState();
// create error notification
final ErrorInfo errorInfo;
if (currentMetadata == null) {
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
"Player error[type=" + error.type + "] occurred, currentMetadata is null");
} else {
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
"Player error[type=" + error.type + "] occurred while playing "
+ currentMetadata.getMetadata().getUrl(),
currentMetadata.getMetadata());
}
ErrorUtil.createNotification(context, errorInfo);
boolean isCatchableException = false;
switch (error.type) {
case ExoPlaybackException.TYPE_SOURCE:
processSourceError(error.getSourceException());
isCatchableException = processSourceError(error.getSourceException());
break;
case ExoPlaybackException.TYPE_UNEXPECTED:
setRecovery();
@ -2546,22 +2534,60 @@ public final class Player implements
break;
}
if (isCatchableException) {
return;
}
createErrorNotification(error);
if (fragmentListener != null) {
fragmentListener.onPlayerError(error);
}
}
private void processSourceError(final IOException error) {
if (exoPlayerIsNull() || playQueue == null) {
return;
private void createErrorNotification(@NonNull final ExoPlaybackException error) {
final ErrorInfo errorInfo;
if (currentMetadata == null) {
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
"Player error[type=" + error.type + "] occurred, currentMetadata is null");
} else {
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
"Player error[type=" + error.type + "] occurred while playing "
+ currentMetadata.getMetadata().getUrl(),
currentMetadata.getMetadata());
}
ErrorUtil.createNotification(context, errorInfo);
}
/**
* Process an {@link IOException} returned by {@link ExoPlaybackException#getSourceException()}
* for {@link ExoPlaybackException#TYPE_SOURCE} exceptions.
*
* <p>
* This method sets the recovery position and sends an error message to the play queue if the
* exception is not a {@link BehindLiveWindowException}.
* </p>
* @param error the source error which was thrown by ExoPlayer
* @return whether the exception thrown is a {@link BehindLiveWindowException} ({@code false}
* is always returned if ExoPlayer or the play queue is null)
*/
private boolean processSourceError(final IOException error) {
if (exoPlayerIsNull() || playQueue == null) {
return false;
}
setRecovery();
if (error instanceof BehindLiveWindowException) {
reloadPlayQueueManager();
} else {
playQueue.error();
simpleExoPlayer.seekToDefaultPosition();
simpleExoPlayer.prepare();
// Inform the user that we are reloading the stream by switching to the buffering state
onBuffering();
return true;
}
playQueue.error();
return false;
}
//endregion

View File

@ -9,6 +9,7 @@ import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTracker;
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
@ -17,9 +18,18 @@ import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener;
public class PlayerDataSource {
public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
/**
* An approximately 4.3 times greater value than the
* {@link DefaultHlsPlaylistTracker#DEFAULT_PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT default}
* to ensure that (very) low latency livestreams which got stuck for a moment don't crash too
* early.
*/
private static final double PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT = 15;
private static final int MANIFEST_MINIMUM_RETRY = 5;
private static final int EXTRACTOR_MINIMUM_RETRY = Integer.MAX_VALUE;
public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
private final DataSource.Factory cacheDataSourceFactory;
private final DataSource.Factory cachelessDataSourceFactory;
@ -44,8 +54,13 @@ public class PlayerDataSource {
public HlsMediaSource.Factory getLiveHlsMediaSourceFactory() {
return new HlsMediaSource.Factory(cachelessDataSourceFactory)
.setAllowChunklessPreparation(true)
.setLoadErrorHandlingPolicy(
new DefaultLoadErrorHandlingPolicy(MANIFEST_MINIMUM_RETRY));
.setLoadErrorHandlingPolicy(new DefaultLoadErrorHandlingPolicy(
MANIFEST_MINIMUM_RETRY))
.setPlaylistTrackerFactory((dataSourceFactory, loadErrorHandlingPolicy,
playlistParserFactory) ->
new DefaultHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy,
playlistParserFactory, PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT)
);
}
public DashMediaSource.Factory getLiveDashMediaSourceFactory() {