Allow playback regardless buffered duration when loading fails
It is possible for playback to be stuck when there is failure in loading further data, while the player is required to load more due to the buffered duration being under `DefaultLoadControl.bufferForPlayback`. Therefore, we check if there is any loading error in `isLoadingPossible`, so that the player will allow the playback of the existing data rather than waiting forever for the data that can never be loaded. Issue: androidx/media#1571 #cherrypick PiperOrigin-RevId: 665801674
This commit is contained in:
parent
875953f971
commit
351593a250
@ -39,6 +39,8 @@
|
||||
handling is enabled. This ensures the blocking call isn't done if audio
|
||||
focus handling is not enabled
|
||||
([#1616](https://github.com/androidx/media/pull/1616)).
|
||||
* Allow playback regardless of buffered duration when loading fails
|
||||
([#1571](https://github.com/androidx/media/issues/1571)).
|
||||
* Transformer:
|
||||
* Add `SurfaceAssetLoader`, which supports queueing video data to
|
||||
Transformer via a `Surface`.
|
||||
|
@ -2633,6 +2633,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
if (loadingPeriodHolder == null) {
|
||||
return false;
|
||||
}
|
||||
if (loadingPeriodHolder.hasLoadingError()) {
|
||||
return false;
|
||||
}
|
||||
long nextLoadPositionUs = loadingPeriodHolder.getNextLoadPositionUs();
|
||||
if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
return false;
|
||||
|
@ -35,6 +35,7 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
||||
import androidx.media3.exoplayer.upstream.Allocator;
|
||||
import java.io.IOException;
|
||||
|
||||
/** Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */
|
||||
/* package */ final class MediaPeriodHolder {
|
||||
@ -394,6 +395,27 @@ import androidx.media3.exoplayer.upstream.Allocator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the media period has encountered an error that prevents it from being prepared
|
||||
* or reading data.
|
||||
*/
|
||||
public boolean hasLoadingError() {
|
||||
try {
|
||||
if (!prepared) {
|
||||
mediaPeriod.maybeThrowPrepareError();
|
||||
} else {
|
||||
for (SampleStream sampleStream : sampleStreams) {
|
||||
if (sampleStream != null) {
|
||||
sampleStream.maybeThrowError();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void enableTrackSelectionsInResult() {
|
||||
if (!isLoadingMediaPeriod()) {
|
||||
return;
|
||||
|
@ -10794,6 +10794,62 @@ public class ExoPlayerTest {
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
mediaPeriodMaybeThrowPrepareError_bufferedDurationUnderMinimumBufferForPlayback_keepPlayingUntilBufferedDataExhausts()
|
||||
throws Exception {
|
||||
ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
|
||||
// Define a timeline that has a short duration of 1 second for the first item, which is smaller
|
||||
// than the default buffer duration for playback in DefaultLoadControl (2.5 seconds).
|
||||
Timeline timeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs= */ 1 * C.MICROS_PER_SECOND));
|
||||
player.addMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT));
|
||||
player.addMediaSource(
|
||||
new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT) {
|
||||
@Override
|
||||
protected MediaPeriod createMediaPeriod(
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
|
||||
DrmSessionManager drmSessionManager,
|
||||
DrmSessionEventListener.EventDispatcher drmEventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(
|
||||
trackGroupArray,
|
||||
allocator,
|
||||
/* singleSampleTimeUs= */ 0,
|
||||
mediaSourceEventDispatcher,
|
||||
DrmSessionManager.DRM_UNSUPPORTED,
|
||||
drmEventDispatcher,
|
||||
/* deferOnPrepared= */ true) {
|
||||
@Override
|
||||
public void maybeThrowPrepareError() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
ExoPlaybackException error = TestPlayerRunHelper.runUntilError(player);
|
||||
|
||||
Object period1Uid =
|
||||
player
|
||||
.getCurrentTimeline()
|
||||
.getPeriod(/* periodIndex= */ 1, new Timeline.Period(), /* setIds= */ true)
|
||||
.uid;
|
||||
assertThat(error.mediaPeriodId.periodUid).isEqualTo(period1Uid);
|
||||
assertThat(player.getCurrentMediaItemIndex()).isEqualTo(1);
|
||||
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sampleStreamMaybeThrowError_isNotThrownUntilPlaybackReachedFailingItem()
|
||||
throws Exception {
|
||||
@ -10859,6 +10915,79 @@ public class ExoPlayerTest {
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
sampleStreamMaybeThrowError_bufferedDurationUnderMinimumBufferForPlayback_keepPlayingUntilBufferedDataExhausts()
|
||||
throws Exception {
|
||||
ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
|
||||
// Define a timeline that has a short duration of 1 second for the first item, which is smaller
|
||||
// than the default buffer duration for playback in DefaultLoadControl (2.5 seconds).
|
||||
Timeline timeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs= */ 1 * C.MICROS_PER_SECOND));
|
||||
player.addMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT));
|
||||
player.addMediaSource(
|
||||
new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT) {
|
||||
@Override
|
||||
protected MediaPeriod createMediaPeriod(
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
|
||||
DrmSessionManager drmSessionManager,
|
||||
DrmSessionEventListener.EventDispatcher drmEventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(
|
||||
trackGroupArray,
|
||||
allocator,
|
||||
/* trackDataFactory= */ (format, mediaPeriodId) -> ImmutableList.of(),
|
||||
mediaSourceEventDispatcher,
|
||||
drmSessionManager,
|
||||
drmEventDispatcher,
|
||||
/* deferOnPrepared= */ false) {
|
||||
@Override
|
||||
protected FakeSampleStream createSampleStream(
|
||||
Allocator allocator,
|
||||
@Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
|
||||
DrmSessionManager drmSessionManager,
|
||||
DrmSessionEventListener.EventDispatcher drmEventDispatcher,
|
||||
Format initialFormat,
|
||||
List<FakeSampleStreamItem> fakeSampleStreamItems) {
|
||||
return new FakeSampleStream(
|
||||
allocator,
|
||||
mediaSourceEventDispatcher,
|
||||
drmSessionManager,
|
||||
drmEventDispatcher,
|
||||
initialFormat,
|
||||
fakeSampleStreamItems) {
|
||||
@Override
|
||||
public void maybeThrowError() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
ExoPlaybackException error = TestPlayerRunHelper.runUntilError(player);
|
||||
|
||||
Object period1Uid =
|
||||
player
|
||||
.getCurrentTimeline()
|
||||
.getPeriod(/* periodIndex= */ 1, new Timeline.Period(), /* setIds= */ true)
|
||||
.uid;
|
||||
assertThat(error.mediaPeriodId.periodUid).isEqualTo(period1Uid);
|
||||
assertThat(player.getCurrentMediaItemIndex()).isEqualTo(1);
|
||||
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rendererError_isReportedWithReadingMediaPeriodId() throws Exception {
|
||||
FakeMediaSource source0 =
|
||||
|
Loading…
x
Reference in New Issue
Block a user