Commit 86dc31f2 by bachinger Committed by Christos Tsilopoulos

Reset only renderers that have been enabled

#exofixit

PiperOrigin-RevId: 396938258
parent 5a2fd983
...@@ -57,10 +57,12 @@ import com.google.android.exoplayer2.util.TraceUtil; ...@@ -57,10 +57,12 @@ import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** Implements the internal behavior of {@link ExoPlayerImpl}. */ /** Implements the internal behavior of {@link ExoPlayerImpl}. */
...@@ -165,6 +167,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -165,6 +167,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final long MIN_RENDERER_SLEEP_DURATION_MS = 2000; private static final long MIN_RENDERER_SLEEP_DURATION_MS = 2000;
private final Renderer[] renderers; private final Renderer[] renderers;
private final Set<Renderer> renderersToReset;
private final RendererCapabilities[] rendererCapabilities; private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector trackSelector; private final TrackSelector trackSelector;
private final TrackSelectorResult emptyTrackSelectorResult; private final TrackSelectorResult emptyTrackSelectorResult;
...@@ -254,6 +257,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -254,6 +257,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
mediaClock = new DefaultMediaClock(this, clock); mediaClock = new DefaultMediaClock(this, clock);
pendingMessages = new ArrayList<>(); pendingMessages = new ArrayList<>();
renderersToReset = Sets.newIdentityHashSet();
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period(); period = new Timeline.Period();
trackSelector.init(/* listener= */ this, bandwidthMeter); trackSelector.init(/* listener= */ this, bandwidthMeter);
...@@ -1322,7 +1326,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1322,7 +1326,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
this.foregroundMode = foregroundMode; this.foregroundMode = foregroundMode;
if (!foregroundMode) { if (!foregroundMode) {
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (!isRendererEnabled(renderer)) { if (!isRendererEnabled(renderer) && renderersToReset.remove(renderer)) {
renderer.reset(); renderer.reset();
} }
} }
...@@ -1382,11 +1386,13 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1382,11 +1386,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
if (resetRenderers) { if (resetRenderers) {
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
try { if (renderersToReset.remove(renderer)) {
renderer.reset(); try {
} catch (RuntimeException e) { renderer.reset();
// There's nothing we can do. } catch (RuntimeException e) {
Log.e(TAG, "Reset failed.", e); // There's nothing we can do.
Log.e(TAG, "Reset failed.", e);
}
} }
} }
} }
...@@ -2385,7 +2391,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -2385,7 +2391,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Reset all disabled renderers before enabling any new ones. This makes sure resources released // Reset all disabled renderers before enabling any new ones. This makes sure resources released
// by the disabled renderers will be available to renderers that are being enabled. // by the disabled renderers will be available to renderers that are being enabled.
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
if (!trackSelectorResult.isRendererEnabled(i)) { if (!trackSelectorResult.isRendererEnabled(i) && renderersToReset.remove(renderers[i])) {
renderers[i].reset(); renderers[i].reset();
} }
} }
...@@ -2417,6 +2423,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -2417,6 +2423,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean joining = !wasRendererEnabled && playing; boolean joining = !wasRendererEnabled && playing;
// Enable the renderer. // Enable the renderer.
enabledRendererCount++; enabledRendererCount++;
renderersToReset.add(renderer);
renderer.enable( renderer.enable(
rendererConfiguration, rendererConfiguration,
formats, formats,
...@@ -2426,7 +2433,6 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -2426,7 +2433,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
mayRenderStartOfStream, mayRenderStartOfStream,
periodHolder.getStartPositionRendererTime(), periodHolder.getStartPositionRendererTime(),
periodHolder.getRendererOffset()); periodHolder.getRendererOffset());
renderer.handleMessage( renderer.handleMessage(
Renderer.MSG_SET_WAKEUP_LISTENER, Renderer.MSG_SET_WAKEUP_LISTENER,
new Renderer.WakeupListener() { new Renderer.WakeupListener() {
......
...@@ -335,6 +335,146 @@ public final class ExoPlayerTest { ...@@ -335,6 +335,146 @@ public final class ExoPlayerTest {
assertThat(renderer.isEnded).isTrue(); assertThat(renderer.isEnded).isTrue();
} }
@Test
public void renderersLifecycle_renderersThatAreNeverEnabled_areNotReset() throws Exception {
Timeline timeline = new FakeTimeline();
final FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
final FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
SimpleExoPlayer player =
new TestExoPlayerBuilder(context).setRenderers(videoRenderer, audioRenderer).build();
Player.Listener mockPlayerListener = mock(Player.Listener.class);
player.addListener(mockPlayerListener);
player.setMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT));
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
assertThat(audioRenderer.enabledCount).isEqualTo(1);
assertThat(audioRenderer.resetCount).isEqualTo(1);
assertThat(videoRenderer.enabledCount).isEqualTo(0);
assertThat(videoRenderer.resetCount).isEqualTo(0);
}
@Test
public void renderersLifecycle_setForegroundMode_resetsDisabledRenderersThatHaveBeenEnabled()
throws Exception {
Timeline timeline = new FakeTimeline();
final FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
final FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
final FakeRenderer textRenderer = new FakeRenderer(C.TRACK_TYPE_TEXT);
SimpleExoPlayer player =
new TestExoPlayerBuilder(context).setRenderers(videoRenderer, audioRenderer).build();
Player.Listener mockPlayerListener = mock(Player.Listener.class);
player.addListener(mockPlayerListener);
player.setMediaSources(
ImmutableList.of(
new FakeMediaSource(
timeline, ExoPlayerTestRunner.AUDIO_FORMAT, ExoPlayerTestRunner.VIDEO_FORMAT),
new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT)));
player.prepare();
player.play();
runUntilPositionDiscontinuity(player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
player.setForegroundMode(/* foregroundMode= */ true);
// Only the video renderer that is disabled in the second window has been reset.
assertThat(audioRenderer.resetCount).isEqualTo(0);
assertThat(videoRenderer.resetCount).isEqualTo(1);
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
// After release the audio renderer is reset as well.
assertThat(audioRenderer.enabledCount).isEqualTo(1);
assertThat(audioRenderer.resetCount).isEqualTo(1);
assertThat(videoRenderer.enabledCount).isEqualTo(1);
assertThat(videoRenderer.resetCount).isEqualTo(1);
assertThat(textRenderer.enabledCount).isEqualTo(0);
assertThat(textRenderer.resetCount).isEqualTo(0);
}
@Test
public void renderersLifecycle_selectTextTracksWhilePlaying_textRendererEnabledAndReset()
throws Exception {
Timeline timeline = new FakeTimeline();
final FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
final FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
final FakeRenderer textRenderer = new FakeRenderer(C.TRACK_TYPE_TEXT);
Format textFormat =
new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).setLanguage("en").build();
SimpleExoPlayer player =
new TestExoPlayerBuilder(context).setRenderers(audioRenderer, textRenderer).build();
Player.Listener mockPlayerListener = mock(Player.Listener.class);
player.addListener(mockPlayerListener);
player.setMediaSources(
ImmutableList.of(
new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT),
new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT, textFormat)));
player.prepare();
player.play();
runUntilPositionDiscontinuity(player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
// Only the audio renderer enabled so far.
assertThat(audioRenderer.enabledCount).isEqualTo(1);
assertThat(textRenderer.enabledCount).isEqualTo(0);
player.setTrackSelectionParameters(
player.getTrackSelectionParameters().buildUpon().setPreferredTextLanguage("en").build());
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
assertThat(audioRenderer.enabledCount).isEqualTo(1);
assertThat(audioRenderer.resetCount).isEqualTo(1);
assertThat(textRenderer.enabledCount).isEqualTo(1);
assertThat(textRenderer.resetCount).isEqualTo(1);
assertThat(videoRenderer.enabledCount).isEqualTo(0);
assertThat(videoRenderer.resetCount).isEqualTo(0);
}
@Test
public void renderersLifecycle_seekTo_resetsDisabledRenderersIfRequired() throws Exception {
Timeline timeline = new FakeTimeline();
final FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
final FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
final FakeRenderer textRenderer = new FakeRenderer(C.TRACK_TYPE_TEXT);
Format textFormat =
new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).setLanguage("en").build();
SimpleExoPlayer player =
new TestExoPlayerBuilder(context)
.setRenderers(videoRenderer, audioRenderer, textRenderer)
.build();
Player.Listener mockPlayerListener = mock(Player.Listener.class);
player.addListener(mockPlayerListener);
player.setTrackSelectionParameters(
player.getTrackSelectionParameters().buildUpon().setPreferredTextLanguage("en").build());
player.setMediaSources(
ImmutableList.of(
new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT),
new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT, textFormat)));
player.prepare();
player.play();
runUntilPositionDiscontinuity(player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
// Disable text renderer by selecting a language that is not available.
player.setTrackSelectionParameters(
player.getTrackSelectionParameters().buildUpon().setPreferredTextLanguage("de").build());
player.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1000);
runUntilPlaybackState(player, Player.STATE_READY);
// Expect formerly enabled renderers to be reset after seek.
assertThat(textRenderer.resetCount).isEqualTo(1);
assertThat(audioRenderer.resetCount).isEqualTo(0);
assertThat(videoRenderer.resetCount).isEqualTo(0);
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
// Verify that the text renderer has not been reset a second time.
assertThat(audioRenderer.enabledCount).isEqualTo(2);
assertThat(audioRenderer.resetCount).isEqualTo(1);
assertThat(textRenderer.enabledCount).isEqualTo(1);
assertThat(textRenderer.resetCount).isEqualTo(1);
assertThat(videoRenderer.enabledCount).isEqualTo(0);
assertThat(videoRenderer.resetCount).isEqualTo(0);
}
/** /**
* Tests that the player does not unnecessarily reset renderers when playing a multi-period * Tests that the player does not unnecessarily reset renderers when playing a multi-period
* source. * source.
......
...@@ -61,6 +61,8 @@ public class FakeRenderer extends BaseRenderer { ...@@ -61,6 +61,8 @@ public class FakeRenderer extends BaseRenderer {
public boolean isEnded; public boolean isEnded;
public int positionResetCount; public int positionResetCount;
public int sampleBufferReadCount; public int sampleBufferReadCount;
public int enabledCount;
public int resetCount;
public FakeRenderer(@C.TrackType int trackType) { public FakeRenderer(@C.TrackType int trackType) {
super(trackType); super(trackType);
...@@ -137,6 +139,17 @@ public class FakeRenderer extends BaseRenderer { ...@@ -137,6 +139,17 @@ public class FakeRenderer extends BaseRenderer {
} }
@Override @Override
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
throws ExoPlaybackException {
enabledCount++;
}
@Override
protected void onReset() {
resetCount++;
}
@Override
public boolean isReady() { public boolean isReady() {
return lastSamplePositionUs >= playbackPositionUs || hasPendingBuffer || isSourceReady(); return lastSamplePositionUs >= playbackPositionUs || hasPendingBuffer || isSourceReady();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment