diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 3da976991..427c97741 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -134,6 +134,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds protected final static int PROGRESS_LOOP_INTERVAL = 500; + protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds protected SimpleExoPlayer simpleExoPlayer; protected AudioReactor audioReactor; @@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen final PlayQueueItem currentSourceItem = playQueue.getItem(); // Check if already playing correct window - final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex; + final boolean isCurrentWindowCorrect = + simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex; // Check if recovering - if (isCurrentWindowCorrect && currentSourceItem != null && - currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) { + if (isCurrentWindowCorrect && currentSourceItem != null) { /* Recovering with sub-second position may cause a long buffer delay in ExoPlayer, * rounding this position to the nearest second will help alleviate this.*/ final long position = currentSourceItem.getRecoveryPosition(); - if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)position)); + /* Skip recovering if the recovery position is not set.*/ + if (position == PlayQueueItem.RECOVERY_UNSET) return; + + if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + + " at: " + getTimeString((int)position)); simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition()); playQueue.unsetRecovery(currentSourceIndex); } @@ -514,10 +519,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen } break; case Player.STATE_READY: //3 + recover(); if (!isPrepared) { isPrepared = true; onPrepared(playWhenReady); - recover(); break; } if (currentState == STATE_PAUSED_SEEK) break; @@ -544,14 +549,18 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen * an error to the play queue based on if the current error can be skipped. * * This is done because ExoPlayer reports the source exceptions before window is - * transitioned on seamless playback. + * transitioned on seamless playback. Because player error causes ExoPlayer to go + * back to {@link Player#STATE_IDLE STATE_IDLE}, we reset and prepare the media source + * again to resume playback. * - * Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE}, - * we reset and prepare the media source again to resume playback.

+ * In the event that this error is produced during a valid stream playback, we save the + * current position so the playback may be recovered and resumed manually by the user. This + * happens only if the playback is {@link #RECOVERY_SKIP_THRESHOLD} milliseconds until complete. + *

* * {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}:

* If a runtime error occurred, then we can try to recover it by restarting the playback - * after setting the timestamp recovery. + * after setting the timestamp recovery.

* * {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}:

* If the renderer failed, treat the error as unrecoverable. @@ -568,6 +577,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen switch (error.type) { case ExoPlaybackException.TYPE_SOURCE: + if (simpleExoPlayer.getCurrentPosition() < + simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) { + setRecovery(); + } playQueue.error(isCurrentWindowValid()); showStreamError(error); break;