50
50
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
51
51
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilPosition;
52
52
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilStartOfMediaItem;
53
+ import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilError;
53
54
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled;
54
55
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
55
56
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPositionDiscontinuity;
88
89
import android.graphics.SurfaceTexture;
89
90
import android.media.AudioManager;
90
91
import android.net.Uri;
92
+ import android.os.Handler;
91
93
import android.os.Looper;
92
94
import android.view.Surface;
93
95
import androidx.annotation.Nullable;
99
101
import com.google.android.exoplayer2.Timeline.Window;
100
102
import com.google.android.exoplayer2.analytics.AnalyticsListener;
101
103
import com.google.android.exoplayer2.audio.AudioAttributes;
104
+ import com.google.android.exoplayer2.audio.AudioRendererEventListener;
102
105
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
103
106
import com.google.android.exoplayer2.drm.DrmSessionManager;
104
107
import com.google.android.exoplayer2.metadata.Metadata;
108
+ import com.google.android.exoplayer2.metadata.MetadataOutput;
105
109
import com.google.android.exoplayer2.metadata.id3.BinaryFrame;
106
110
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
107
111
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
126
130
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
127
131
import com.google.android.exoplayer2.testutil.FakeAdaptiveDataSet;
128
132
import com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource;
133
+ import com.google.android.exoplayer2.testutil.FakeAudioRenderer;
129
134
import com.google.android.exoplayer2.testutil.FakeChunkSource;
130
135
import com.google.android.exoplayer2.testutil.FakeClock;
131
136
import com.google.android.exoplayer2.testutil.FakeDataSource;
143
148
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
144
149
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
145
150
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
151
+ import com.google.android.exoplayer2.text.TextOutput;
146
152
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
147
153
import com.google.android.exoplayer2.upstream.Allocation;
148
154
import com.google.android.exoplayer2.upstream.Allocator;
149
155
import com.google.android.exoplayer2.upstream.Loader;
150
156
import com.google.android.exoplayer2.upstream.TransferListener;
151
157
import com.google.android.exoplayer2.util.Assertions;
152
158
import com.google.android.exoplayer2.util.Clock;
159
+ import com.google.android.exoplayer2.util.HandlerWrapper;
153
160
import com.google.android.exoplayer2.util.MimeTypes;
154
161
import com.google.android.exoplayer2.util.SystemClock;
155
162
import com.google.android.exoplayer2.util.Util;
163
+ import com.google.android.exoplayer2.video.VideoRendererEventListener;
156
164
import com.google.common.collect.ImmutableList;
157
165
import com.google.common.collect.Iterables;
158
166
import com.google.common.collect.Range;
@@ -9732,24 +9740,30 @@ public void rendererError_isReportedWithReadingMediaPeriodId() throws Exception
9732
9740
FakeMediaSource source1 =
9733
9741
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT);
9734
9742
RenderersFactory renderersFactory =
9735
- (eventHandler, videoListener, audioListener, textOutput, metadataOutput) ->
9736
- new Renderer[] {
9737
- new FakeRenderer(C.TRACK_TYPE_VIDEO),
9738
- new FakeRenderer(C.TRACK_TYPE_AUDIO) {
9739
- @Override
9740
- protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
9741
- throws ExoPlaybackException {
9742
- // Fail when enabling the renderer. This will happen during the period
9743
- // transition while the reading and playing period are different.
9744
- throw createRendererException(
9745
- new IllegalStateException(),
9746
- ExoPlayerTestRunner.AUDIO_FORMAT,
9747
- PlaybackException.ERROR_CODE_UNSPECIFIED);
9748
- }
9743
+ (eventHandler, videoListener, audioListener, textOutput, metadataOutput) -> {
9744
+ HandlerWrapper handler =
9745
+ SystemClock.DEFAULT.createHandler(eventHandler.getLooper(), /* callback= */ null);
9746
+ return new Renderer[] {
9747
+ new FakeVideoRenderer(handler, videoListener),
9748
+ new FakeAudioRenderer(handler, audioListener) {
9749
+ @Override
9750
+ protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
9751
+ throws ExoPlaybackException {
9752
+ super.onEnabled(joining, mayRenderStartOfStream);
9753
+ // Fail when enabling the renderer. This will happen during the period
9754
+ // transition while the reading and playing period are different.
9755
+ throw createRendererException(
9756
+ new IllegalStateException(),
9757
+ ExoPlayerTestRunner.AUDIO_FORMAT,
9758
+ PlaybackException.ERROR_CODE_UNSPECIFIED);
9749
9759
}
9750
- };
9760
+ }
9761
+ };
9762
+ };
9751
9763
ExoPlayer player =
9752
9764
new TestExoPlayerBuilder(context).setRenderersFactory(renderersFactory).build();
9765
+ AnalyticsListener mockListener = mock(AnalyticsListener.class);
9766
+ player.addAnalyticsListener(mockListener);
9753
9767
player.setMediaSources(ImmutableList.of(source0, source1));
9754
9768
player.prepare();
9755
9769
player.play();
@@ -9762,8 +9776,12 @@ protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
9762
9776
.getPeriod(/* periodIndex= */ 1, new Timeline.Period(), /* setIds= */ true)
9763
9777
.uid;
9764
9778
assertThat(error.mediaPeriodId.periodUid).isEqualTo(period1Uid);
9765
- // Verify test setup by checking that playing period was indeed different.
9766
- assertThat(player.getCurrentMediaItemIndex()).isEqualTo(0);
9779
+ // Verify test setup by checking that enabling the renderer happened before the transition.
9780
+ InOrder inOrderEvents = inOrder(mockListener);
9781
+ inOrderEvents.verify(mockListener).onAudioEnabled(any(), any());
9782
+ inOrderEvents
9783
+ .verify(mockListener)
9784
+ .onMediaItemTransition(any(), any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
9767
9785
}
9768
9786
9769
9787
@Test
@@ -12219,6 +12237,84 @@ public void loadControlBackBuffer_withInsufficientMemoryLimits_stillContinuesPla
12219
12237
// Assert that playing works without getting stuck due to the memory used by the back buffer.
12220
12238
}
12221
12239
12240
+ @Test
12241
+ public void rendererError_whileReadingAhead_isReportedAfterMediaItemTransition()
12242
+ throws Exception {
12243
+ // Throw an exception as soon as we try to process a buffer for the second item. This happens
12244
+ // while the player is still playing the first item.
12245
+ ExoPlayer player =
12246
+ new TestExoPlayerBuilder(context)
12247
+ .setRenderersFactory(
12248
+ new RenderersFactory() {
12249
+ @Override
12250
+ public Renderer[] createRenderers(
12251
+ Handler handler,
12252
+ VideoRendererEventListener videoListener,
12253
+ AudioRendererEventListener audioListener,
12254
+ TextOutput textOutput,
12255
+ MetadataOutput metadataOutput) {
12256
+ return new Renderer[] {
12257
+ new FakeVideoRenderer(
12258
+ SystemClock.DEFAULT.createHandler(
12259
+ handler.getLooper(), /* callback= */ null),
12260
+ videoListener) {
12261
+ int streamChangeCount = 0;
12262
+
12263
+ @Override
12264
+ protected void onStreamChanged(
12265
+ Format[] formats, long startPositionUs, long offsetUs)
12266
+ throws ExoPlaybackException {
12267
+ super.onStreamChanged(formats, startPositionUs, offsetUs);
12268
+ streamChangeCount++;
12269
+ }
12270
+
12271
+ @Override
12272
+ protected boolean shouldProcessBuffer(
12273
+ long bufferTimeUs, long playbackPositionUs) {
12274
+ boolean shouldProcess =
12275
+ super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
12276
+ if (streamChangeCount == 2 && shouldProcess) {
12277
+ Util.sneakyThrow(
12278
+ createRendererException(
12279
+ new IllegalStateException(),
12280
+ /* format= */ null,
12281
+ PlaybackException.ERROR_CODE_DECODING_FAILED));
12282
+ }
12283
+ return shouldProcess;
12284
+ }
12285
+ }
12286
+ };
12287
+ }
12288
+ })
12289
+ .build();
12290
+ AnalyticsListener mockListener = mock(AnalyticsListener.class);
12291
+ player.addAnalyticsListener(mockListener);
12292
+
12293
+ player.setMediaSources(
12294
+ ImmutableList.of(
12295
+ new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
12296
+ new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)));
12297
+ player.prepare();
12298
+ player.play();
12299
+ runUntilError(player);
12300
+ int mediaItemIndexAfterError = player.getCurrentMediaItemIndex();
12301
+ player.release();
12302
+
12303
+ assertThat(mediaItemIndexAfterError).isEqualTo(1);
12304
+ InOrder eventsInOrder = inOrder(mockListener);
12305
+ // Verify the test setup by checking that the renderer format change happened before the
12306
+ // position discontinuity.
12307
+ eventsInOrder.verify(mockListener, times(2)).onDownstreamFormatChanged(any(), any());
12308
+ eventsInOrder
12309
+ .verify(mockListener)
12310
+ .onPositionDiscontinuity(
12311
+ any(), any(), any(), eq(Player.DISCONTINUITY_REASON_AUTO_TRANSITION));
12312
+ eventsInOrder
12313
+ .verify(mockListener)
12314
+ .onMediaItemTransition(any(), any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
12315
+ eventsInOrder.verify(mockListener).onPlayerError(any(), any());
12316
+ }
12317
+
12222
12318
// Internal methods.
12223
12319
12224
12320
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
0 commit comments