Skip to content

Commit 21514ba

Browse files
toniheicopybara-github
authored andcommitted
Add missing check for TRACK_TYPE_NONE before accessing selections
The track selections will be null if the track type is NONE even if the renderer is enabled. PiperOrigin-RevId: 747818128
1 parent 117db48 commit 21514ba

File tree

4 files changed

+90
-15
lines changed

4 files changed

+90
-15
lines changed

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -2651,7 +2651,8 @@ private void maybeUpdateReadingPeriod() throws ExoPlaybackException {
26512651
hasSecondaryRenderers && !isPrewarmingDisabledUntilNextTransition;
26522652
if (arePrewarmingRenderersHandlingDiscontinuity) {
26532653
for (int i = 0; i < renderers.length; i++) {
2654-
if (!newTrackSelectorResult.isRendererEnabled(i)) {
2654+
if (!newTrackSelectorResult.isRendererEnabled(i)
2655+
|| renderers[i].getTrackType() == C.TRACK_TYPE_NONE) {
26552656
continue;
26562657
}
26572658
// TODO: This check should ideally be replaced by a per-stream discontinuity check

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -679,9 +679,9 @@ public void renderersLifecycle_seekTo_resetsDisabledRenderersIfRequired() throws
679679
@Test
680680
public void renderersLifecycle_onlyRenderersThatAreEnabled_areSetToFinal() throws Exception {
681681
AtomicInteger videoStreamSetToFinalCount = new AtomicInteger();
682-
final FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
683-
final FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
684-
final ForwardingRenderer forwardingVideoRenderer =
682+
FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
683+
FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
684+
ForwardingRenderer forwardingVideoRenderer =
685685
new ForwardingRenderer(videoRenderer) {
686686
@Override
687687
public void setCurrentStreamFinal() {
@@ -694,18 +694,18 @@ public void setCurrentStreamFinal() {
694694
new TestExoPlayerBuilder(context)
695695
.setRenderers(forwardingVideoRenderer, audioRenderer))
696696
.build();
697-
// Use media sources with discontinuities so that enabled streams are set to final.
697+
// Use media sources with discontinuities so that enabled streams are set to final. Also ensure
698+
// to use Formats that are not guaranteed to be only sync samples, so ClippingMediaSource
699+
// maintains the initial discontinuity.
700+
Format videoFormat = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_AV1).build();
701+
Format audioFormat = new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_DTS).build();
698702
ClippingMediaSource clippedFakeAudioSource =
699-
new ClippingMediaSource.Builder(
700-
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT))
703+
new ClippingMediaSource.Builder(new FakeMediaSource(new FakeTimeline(), audioFormat))
701704
.setEndPositionMs(300)
702705
.build();
703706
ClippingMediaSource clippedFakeAudioVideoSource =
704707
new ClippingMediaSource.Builder(
705-
new FakeMediaSource(
706-
new FakeTimeline(),
707-
ExoPlayerTestRunner.VIDEO_FORMAT,
708-
ExoPlayerTestRunner.AUDIO_FORMAT))
708+
new FakeMediaSource(new FakeTimeline(), videoFormat, audioFormat))
709709
.setEndPositionMs(300)
710710
.build();
711711
player.setMediaSources(

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerWithPrewarmingRenderersTest.java

+77-4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import androidx.test.core.app.ApplicationProvider;
6868
import androidx.test.ext.junit.runners.AndroidJUnit4;
6969
import com.google.common.collect.ImmutableList;
70+
import java.util.Arrays;
7071
import java.util.List;
7172
import java.util.concurrent.atomic.AtomicBoolean;
7273
import java.util.concurrent.atomic.AtomicReference;
@@ -1609,6 +1610,56 @@ public void render(long positionUs, long elapsedRealtimeUs)
16091610
assertThat(secondaryVideoState3).isEqualTo(Renderer.STATE_STARTED);
16101611
}
16111612

1613+
@Test
1614+
public void
1615+
play_withNoSampleRendererAndInitialDiscontinuity_usesPrewarmingAndTransitionsWithoutError()
1616+
throws Exception {
1617+
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
1618+
RenderersFactory renderersFactoryWithNoSampleRenderer =
1619+
new FakeRenderersFactorySupportingSecondaryVideoRenderer(
1620+
fakeClock, /* includeNoSampleRenderer= */ true);
1621+
ExoPlayer player =
1622+
new TestExoPlayerBuilder(context)
1623+
.setClock(fakeClock)
1624+
.setRenderersFactory(renderersFactoryWithNoSampleRenderer)
1625+
.build();
1626+
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
1627+
Renderer noSampleRenderer = player.getRenderer(/* index= */ 2);
1628+
assertThat(secondaryVideoRenderer.getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO);
1629+
assertThat(noSampleRenderer.getTrackType()).isEqualTo(C.TRACK_TYPE_NONE);
1630+
// Set a playlist that allows a new renderer to be enabled early and uses an initial
1631+
// discontinuity.
1632+
player.setMediaSources(
1633+
ImmutableList.of(
1634+
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
1635+
new FakeMediaSource(
1636+
new FakeTimeline(),
1637+
ExoPlayerTestRunner.VIDEO_FORMAT,
1638+
ExoPlayerTestRunner.AUDIO_FORMAT) {
1639+
@Override
1640+
public MediaPeriod createPeriod(
1641+
MediaPeriodId id, Allocator allocator, long startPositionUs) {
1642+
FakeMediaPeriod fakeMediaPeriod =
1643+
(FakeMediaPeriod) super.createPeriod(id, allocator, startPositionUs);
1644+
fakeMediaPeriod.setDiscontinuityPositionUs(100);
1645+
return fakeMediaPeriod;
1646+
}
1647+
}));
1648+
player.prepare();
1649+
1650+
// Play until secondary video renderer item is started, verifying its being used for prewarming.
1651+
// This step should not have any influence on the NoSampleRenderer and it should not throw.
1652+
player.play();
1653+
advance(player)
1654+
.untilBackgroundThreadCondition(
1655+
() -> secondaryVideoRenderer.getState() == Renderer.STATE_STARTED);
1656+
@Renderer.State int noSampleRendererState = noSampleRenderer.getState();
1657+
advance(player).untilState(Player.STATE_ENDED);
1658+
player.release();
1659+
1660+
assertThat(noSampleRendererState).isEqualTo(Renderer.STATE_STARTED);
1661+
}
1662+
16121663
/** {@link FakeMediaSource} that prevents any reading of samples off the sample queue. */
16131664
private static final class FakeBlockingMediaSource extends FakeMediaSource {
16141665

@@ -1670,9 +1721,16 @@ public int readData(
16701721
private static class FakeRenderersFactorySupportingSecondaryVideoRenderer
16711722
implements RenderersFactory {
16721723
protected final Clock clock;
1724+
private final boolean includeNoSampleRenderer;
16731725

16741726
public FakeRenderersFactorySupportingSecondaryVideoRenderer(Clock clock) {
1727+
this(clock, /* includeNoSampleRenderer= */ false);
1728+
}
1729+
1730+
public FakeRenderersFactorySupportingSecondaryVideoRenderer(
1731+
Clock clock, boolean includeNoSampleRenderer) {
16751732
this.clock = clock;
1733+
this.includeNoSampleRenderer = includeNoSampleRenderer;
16761734
}
16771735

16781736
@Override
@@ -1684,10 +1742,25 @@ public Renderer[] createRenderers(
16841742
MetadataOutput metadataRendererOutput) {
16851743
HandlerWrapper clockAwareHandler =
16861744
clock.createHandler(eventHandler.getLooper(), /* callback= */ null);
1687-
return new Renderer[] {
1688-
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener),
1689-
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
1690-
};
1745+
Renderer[] renderers =
1746+
new Renderer[] {
1747+
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener),
1748+
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
1749+
};
1750+
if (includeNoSampleRenderer) {
1751+
renderers = Arrays.copyOf(renderers, renderers.length + 1);
1752+
renderers[renderers.length - 1] =
1753+
new NoSampleRenderer() {
1754+
@Override
1755+
public String getName() {
1756+
return "NoSampleRenderer";
1757+
}
1758+
1759+
@Override
1760+
public void render(long positionUs, long elapsedRealtimeUs) {}
1761+
};
1762+
}
1763+
return renderers;
16911764
}
16921765

16931766
@Override

libraries/test_utils/src/main/java/androidx/media3/test/utils/ExoPlayerTestRunner.java

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
7070
public static final Format AUDIO_FORMAT =
7171
new Format.Builder()
7272
.setSampleMimeType(MimeTypes.AUDIO_AAC)
73+
.setCodecs("mp4a.40.2")
7374
.setAverageBitrate(100_000)
7475
.setChannelCount(2)
7576
.setSampleRate(44100)

0 commit comments

Comments
 (0)