mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Move playback error into PlaybackInfo.
The error is closely related to the playback state IDLE and should be updated in sync with the state to prevent unexpected event ordering and/or keeping the error after re-preparation. Issue:#5407 PiperOrigin-RevId: 265014630
This commit is contained in:
parent
7883eabb38
commit
29af6899fe
5 changed files with 77 additions and 27 deletions
|
|
@ -74,7 +74,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
private int pendingSetPlaybackParametersAcks;
|
||||
private PlaybackParameters playbackParameters;
|
||||
private SeekParameters seekParameters;
|
||||
@Nullable private ExoPlaybackException playbackError;
|
||||
|
||||
// Playback information when there is no pending seek/set source operation.
|
||||
private PlaybackInfo playbackInfo;
|
||||
|
|
@ -202,13 +201,12 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
@Override
|
||||
@Nullable
|
||||
public ExoPlaybackException getPlaybackError() {
|
||||
return playbackError;
|
||||
return playbackInfo.playbackError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retry() {
|
||||
if (mediaSource != null
|
||||
&& (playbackError != null || playbackInfo.playbackState == Player.STATE_IDLE)) {
|
||||
if (mediaSource != null && playbackInfo.playbackState == Player.STATE_IDLE) {
|
||||
prepare(mediaSource, /* resetPosition= */ false, /* resetState= */ false);
|
||||
}
|
||||
}
|
||||
|
|
@ -220,11 +218,13 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
|
||||
@Override
|
||||
public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) {
|
||||
playbackError = null;
|
||||
this.mediaSource = mediaSource;
|
||||
PlaybackInfo playbackInfo =
|
||||
getResetPlaybackInfo(
|
||||
resetPosition, resetState, /* playbackState= */ Player.STATE_BUFFERING);
|
||||
resetPosition,
|
||||
resetState,
|
||||
/* resetError= */ true,
|
||||
/* playbackState= */ Player.STATE_BUFFERING);
|
||||
// Trigger internal prepare first before updating the playback info and notifying external
|
||||
// listeners to ensure that new operations issued in the listener notifications reach the
|
||||
// player after this prepare. The internal player can't change the playback info immediately
|
||||
|
|
@ -381,13 +381,13 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
@Override
|
||||
public void stop(boolean reset) {
|
||||
if (reset) {
|
||||
playbackError = null;
|
||||
mediaSource = null;
|
||||
}
|
||||
PlaybackInfo playbackInfo =
|
||||
getResetPlaybackInfo(
|
||||
/* resetPosition= */ reset,
|
||||
/* resetState= */ reset,
|
||||
/* resetError= */ reset,
|
||||
/* playbackState= */ Player.STATE_IDLE);
|
||||
// Trigger internal stop first before updating the playback info and notifying external
|
||||
// listeners to ensure that new operations issued in the listener notifications reach the
|
||||
|
|
@ -415,6 +415,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
getResetPlaybackInfo(
|
||||
/* resetPosition= */ false,
|
||||
/* resetState= */ false,
|
||||
/* resetError= */ false,
|
||||
/* playbackState= */ Player.STATE_IDLE);
|
||||
}
|
||||
|
||||
|
|
@ -572,11 +573,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
case ExoPlayerImplInternal.MSG_PLAYBACK_PARAMETERS_CHANGED:
|
||||
handlePlaybackParameters((PlaybackParameters) msg.obj, /* operationAck= */ msg.arg1 != 0);
|
||||
break;
|
||||
case ExoPlayerImplInternal.MSG_ERROR:
|
||||
ExoPlaybackException playbackError = (ExoPlaybackException) msg.obj;
|
||||
this.playbackError = playbackError;
|
||||
notifyListeners(listener -> listener.onPlayerError(playbackError));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
|
@ -635,7 +631,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
}
|
||||
|
||||
private PlaybackInfo getResetPlaybackInfo(
|
||||
boolean resetPosition, boolean resetState, @Player.State int playbackState) {
|
||||
boolean resetPosition,
|
||||
boolean resetState,
|
||||
boolean resetError,
|
||||
@Player.State int playbackState) {
|
||||
if (resetPosition) {
|
||||
maskingWindowIndex = 0;
|
||||
maskingPeriodIndex = 0;
|
||||
|
|
@ -659,6 +658,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
resetError ? null : playbackInfo.playbackError,
|
||||
/* isLoading= */ false,
|
||||
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
|
||||
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
|
||||
|
|
@ -728,6 +728,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
private final @Player.TimelineChangeReason int timelineChangeReason;
|
||||
private final boolean seekProcessed;
|
||||
private final boolean playbackStateChanged;
|
||||
private final boolean playbackErrorChanged;
|
||||
private final boolean timelineChanged;
|
||||
private final boolean isLoadingChanged;
|
||||
private final boolean trackSelectorResultChanged;
|
||||
|
|
@ -752,6 +753,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
this.seekProcessed = seekProcessed;
|
||||
this.playWhenReady = playWhenReady;
|
||||
playbackStateChanged = previousPlaybackInfo.playbackState != playbackInfo.playbackState;
|
||||
playbackErrorChanged =
|
||||
previousPlaybackInfo.playbackError != playbackInfo.playbackError
|
||||
&& playbackInfo.playbackError != null;
|
||||
timelineChanged = previousPlaybackInfo.timeline != playbackInfo.timeline;
|
||||
isLoadingChanged = previousPlaybackInfo.isLoading != playbackInfo.isLoading;
|
||||
trackSelectorResultChanged =
|
||||
|
|
@ -770,6 +774,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
listenerSnapshot,
|
||||
listener -> listener.onPositionDiscontinuity(positionDiscontinuityReason));
|
||||
}
|
||||
if (playbackErrorChanged) {
|
||||
invokeAll(listenerSnapshot, listener -> listener.onPlayerError(playbackInfo.playbackError));
|
||||
}
|
||||
if (trackSelectorResultChanged) {
|
||||
trackSelector.onSelectionActivated(playbackInfo.trackSelectorResult.info);
|
||||
invokeAll(
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
// External messages
|
||||
public static final int MSG_PLAYBACK_INFO_CHANGED = 0;
|
||||
public static final int MSG_PLAYBACK_PARAMETERS_CHANGED = 1;
|
||||
public static final int MSG_ERROR = 2;
|
||||
|
||||
// Internal messages
|
||||
private static final int MSG_PREPARE = 0;
|
||||
|
|
@ -374,19 +373,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
maybeNotifyPlaybackInfoChanged();
|
||||
} catch (ExoPlaybackException e) {
|
||||
Log.e(TAG, "Playback error.", e);
|
||||
eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget();
|
||||
stopInternal(
|
||||
/* forceResetRenderers= */ true,
|
||||
/* resetPositionAndState= */ false,
|
||||
/* acknowledgeStop= */ false);
|
||||
playbackInfo = playbackInfo.copyWithPlaybackError(e);
|
||||
maybeNotifyPlaybackInfoChanged();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Source error.", e);
|
||||
eventHandler.obtainMessage(MSG_ERROR, ExoPlaybackException.createForSource(e)).sendToTarget();
|
||||
stopInternal(
|
||||
/* forceResetRenderers= */ false,
|
||||
/* resetPositionAndState= */ false,
|
||||
/* acknowledgeStop= */ false);
|
||||
playbackInfo = playbackInfo.copyWithPlaybackError(ExoPlaybackException.createForSource(e));
|
||||
maybeNotifyPlaybackInfoChanged();
|
||||
} catch (RuntimeException | OutOfMemoryError e) {
|
||||
Log.e(TAG, "Internal runtime error.", e);
|
||||
|
|
@ -394,11 +393,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
e instanceof OutOfMemoryError
|
||||
? ExoPlaybackException.createForOutOfMemoryError((OutOfMemoryError) e)
|
||||
: ExoPlaybackException.createForUnexpected((RuntimeException) e);
|
||||
eventHandler.obtainMessage(MSG_ERROR, error).sendToTarget();
|
||||
stopInternal(
|
||||
/* forceResetRenderers= */ true,
|
||||
/* resetPositionAndState= */ false,
|
||||
/* acknowledgeStop= */ false);
|
||||
playbackInfo = playbackInfo.copyWithPlaybackError(error);
|
||||
maybeNotifyPlaybackInfoChanged();
|
||||
}
|
||||
return true;
|
||||
|
|
@ -436,7 +435,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
private void prepareInternal(MediaSource mediaSource, boolean resetPosition, boolean resetState) {
|
||||
pendingPrepareCount++;
|
||||
resetInternal(
|
||||
/* resetRenderers= */ false, /* releaseMediaSource= */ true, resetPosition, resetState);
|
||||
/* resetRenderers= */ false,
|
||||
/* releaseMediaSource= */ true,
|
||||
resetPosition,
|
||||
resetState,
|
||||
/* resetError= */ true);
|
||||
loadControl.onPrepared();
|
||||
this.mediaSource = mediaSource;
|
||||
setState(Player.STATE_BUFFERING);
|
||||
|
|
@ -688,7 +691,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* resetRenderers= */ false,
|
||||
/* releaseMediaSource= */ false,
|
||||
/* resetPosition= */ true,
|
||||
/* resetState= */ false);
|
||||
/* resetState= */ false,
|
||||
/* resetError= */ true);
|
||||
} else {
|
||||
// Execute the seek in the current media periods.
|
||||
long newPeriodPositionUs = periodPositionUs;
|
||||
|
|
@ -834,7 +838,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* resetRenderers= */ forceResetRenderers || !foregroundMode,
|
||||
/* releaseMediaSource= */ true,
|
||||
/* resetPosition= */ resetPositionAndState,
|
||||
/* resetState= */ resetPositionAndState);
|
||||
/* resetState= */ resetPositionAndState,
|
||||
/* resetError= */ resetPositionAndState);
|
||||
playbackInfoUpdate.incrementPendingOperationAcks(
|
||||
pendingPrepareCount + (acknowledgeStop ? 1 : 0));
|
||||
pendingPrepareCount = 0;
|
||||
|
|
@ -847,7 +852,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* resetRenderers= */ true,
|
||||
/* releaseMediaSource= */ true,
|
||||
/* resetPosition= */ true,
|
||||
/* resetState= */ true);
|
||||
/* resetState= */ true,
|
||||
/* resetError= */ false);
|
||||
loadControl.onReleased();
|
||||
setState(Player.STATE_IDLE);
|
||||
internalPlaybackThread.quit();
|
||||
|
|
@ -861,7 +867,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
boolean resetRenderers,
|
||||
boolean releaseMediaSource,
|
||||
boolean resetPosition,
|
||||
boolean resetState) {
|
||||
boolean resetState,
|
||||
boolean resetError) {
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
rebuffering = false;
|
||||
mediaClock.stop();
|
||||
|
|
@ -924,6 +931,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackInfo.playbackState,
|
||||
resetError ? null : playbackInfo.playbackError,
|
||||
/* isLoading= */ false,
|
||||
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
|
||||
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
|
||||
|
|
@ -1382,7 +1390,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* resetRenderers= */ false,
|
||||
/* releaseMediaSource= */ false,
|
||||
/* resetPosition= */ true,
|
||||
/* resetState= */ false);
|
||||
/* resetState= */ false,
|
||||
/* resetError= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2;
|
||||
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
||||
|
|
@ -51,6 +52,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
public final long contentPositionUs;
|
||||
/** The current playback state. One of the {@link Player}.STATE_ constants. */
|
||||
@Player.State public final int playbackState;
|
||||
/** The current playback error, or null if this is not an error state. */
|
||||
@Nullable public final ExoPlaybackException playbackError;
|
||||
/** Whether the player is currently loading. */
|
||||
public final boolean isLoading;
|
||||
/** The currently available track groups. */
|
||||
|
|
@ -93,6 +96,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
/* contentPositionUs= */ C.TIME_UNSET,
|
||||
Player.STATE_IDLE,
|
||||
/* playbackError= */ null,
|
||||
/* isLoading= */ false,
|
||||
TrackGroupArray.EMPTY,
|
||||
emptyTrackSelectorResult,
|
||||
|
|
@ -124,6 +128,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
long startPositionUs,
|
||||
long contentPositionUs,
|
||||
@Player.State int playbackState,
|
||||
@Nullable ExoPlaybackException playbackError,
|
||||
boolean isLoading,
|
||||
TrackGroupArray trackGroups,
|
||||
TrackSelectorResult trackSelectorResult,
|
||||
|
|
@ -136,6 +141,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
this.startPositionUs = startPositionUs;
|
||||
this.contentPositionUs = contentPositionUs;
|
||||
this.playbackState = playbackState;
|
||||
this.playbackError = playbackError;
|
||||
this.isLoading = isLoading;
|
||||
this.trackGroups = trackGroups;
|
||||
this.trackSelectorResult = trackSelectorResult;
|
||||
|
|
@ -194,6 +200,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
positionUs,
|
||||
periodId.isAd() ? contentPositionUs : C.TIME_UNSET,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
@ -217,6 +224,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
@ -240,6 +248,31 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
loadingMediaPeriodId,
|
||||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies playback info with a playback error.
|
||||
*
|
||||
* @param playbackError The error. See {@link #playbackError}.
|
||||
* @return Copied playback info with the playback error.
|
||||
*/
|
||||
@CheckResult
|
||||
public PlaybackInfo copyWithPlaybackError(@Nullable ExoPlaybackException playbackError) {
|
||||
return new PlaybackInfo(
|
||||
timeline,
|
||||
periodId,
|
||||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
@ -263,6 +296,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
@ -288,6 +322,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
@ -311,6 +346,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||
startPositionUs,
|
||||
contentPositionUs,
|
||||
playbackState,
|
||||
playbackError,
|
||||
isLoading,
|
||||
trackGroups,
|
||||
trackSelectorResult,
|
||||
|
|
|
|||
|
|
@ -465,10 +465,7 @@ public class AnalyticsCollector
|
|||
|
||||
@Override
|
||||
public final void onPlayerError(ExoPlaybackException error) {
|
||||
EventTime eventTime =
|
||||
error.type == ExoPlaybackException.TYPE_SOURCE
|
||||
? generateLoadingMediaPeriodEventTime()
|
||||
: generatePlayingMediaPeriodEventTime();
|
||||
EventTime eventTime = generateLastReportedPlayingMediaPeriodEventTime();
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onPlayerError(eventTime, error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ public final class MediaPeriodQueueTest {
|
|||
/* startPositionUs= */ 0,
|
||||
/* contentPositionUs= */ 0,
|
||||
Player.STATE_READY,
|
||||
/* playbackError= */ null,
|
||||
/* isLoading= */ false,
|
||||
/* trackGroups= */ null,
|
||||
/* trackSelectorResult= */ null,
|
||||
|
|
|
|||
Loading…
Reference in a new issue