diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java index 48b94c2040..b63441ebc1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java @@ -25,17 +25,14 @@ import androidx.media3.common.util.Assertions; import androidx.media3.common.util.NullableType; import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; -import androidx.media3.exoplayer.source.chunk.Chunk; -import androidx.media3.exoplayer.source.chunk.MediaChunk; -import androidx.media3.exoplayer.source.chunk.MediaChunkIterator; import androidx.media3.exoplayer.trackselection.ExoTrackSelection; +import androidx.media3.exoplayer.trackselection.ForwardingTrackSelection; import com.google.common.collect.Lists; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.List; /** Merges multiple {@link MediaPeriod}s. */ /* package */ final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callback { @@ -140,7 +137,8 @@ import java.util.List; TrackGroup mergedTrackGroup = mergedTrackSelection.getTrackGroup(); TrackGroup childTrackGroup = checkNotNull(childTrackGroupByMergedTrackGroup.get(mergedTrackGroup)); - childSelections[j] = new ForwardingTrackSelection(mergedTrackSelection, childTrackGroup); + childSelections[j] = + new MergingMediaPeriodTrackSelection(mergedTrackSelection, childTrackGroup); } else { childSelections[j] = null; } @@ -313,162 +311,47 @@ import java.util.List; Assertions.checkNotNull(callback).onContinueLoadingRequested(this); } - private static final class ForwardingTrackSelection implements ExoTrackSelection { - - private final ExoTrackSelection trackSelection; + private static final class MergingMediaPeriodTrackSelection extends ForwardingTrackSelection { private final TrackGroup trackGroup; - public ForwardingTrackSelection(ExoTrackSelection trackSelection, TrackGroup trackGroup) { - this.trackSelection = trackSelection; + public MergingMediaPeriodTrackSelection( + ExoTrackSelection trackSelection, TrackGroup trackGroup) { + super(trackSelection); this.trackGroup = trackGroup; } - @Override - public @Type int getType() { - return trackSelection.getType(); - } - @Override public TrackGroup getTrackGroup() { return trackGroup; } - @Override - public int length() { - return trackSelection.length(); - } - @Override public Format getFormat(int index) { - return trackGroup.getFormat(trackSelection.getIndexInTrackGroup(index)); - } - - @Override - public int getIndexInTrackGroup(int index) { - return trackSelection.getIndexInTrackGroup(index); + return trackGroup.getFormat(getWrappedInstance().getIndexInTrackGroup(index)); } @Override public int indexOf(Format format) { - return trackSelection.indexOf(trackGroup.indexOf(format)); - } - - @Override - public int indexOf(int indexInTrackGroup) { - return trackSelection.indexOf(indexInTrackGroup); - } - - @Override - public void enable() { - trackSelection.enable(); - } - - @Override - public void disable() { - trackSelection.disable(); + return getWrappedInstance().indexOf(trackGroup.indexOf(format)); } @Override public Format getSelectedFormat() { - return trackGroup.getFormat(trackSelection.getSelectedIndexInTrackGroup()); + return trackGroup.getFormat(getWrappedInstance().getSelectedIndexInTrackGroup()); } @Override - public int getSelectedIndexInTrackGroup() { - return trackSelection.getSelectedIndexInTrackGroup(); - } - - @Override - public int getSelectedIndex() { - return trackSelection.getSelectedIndex(); - } - - @Override - public @C.SelectionReason int getSelectionReason() { - return trackSelection.getSelectionReason(); - } - - @Nullable - @Override - public Object getSelectionData() { - return trackSelection.getSelectionData(); - } - - @Override - public void onPlaybackSpeed(float playbackSpeed) { - trackSelection.onPlaybackSpeed(playbackSpeed); - } - - @Override - public void onDiscontinuity() { - trackSelection.onDiscontinuity(); - } - - @Override - public void onRebuffer() { - trackSelection.onRebuffer(); - } - - @Override - public void onPlayWhenReadyChanged(boolean playWhenReady) { - trackSelection.onPlayWhenReadyChanged(playWhenReady); - } - - @Override - public void updateSelectedTrack( - long playbackPositionUs, - long bufferedDurationUs, - long availableDurationUs, - List queue, - MediaChunkIterator[] mediaChunkIterators) { - trackSelection.updateSelectedTrack( - playbackPositionUs, bufferedDurationUs, availableDurationUs, queue, mediaChunkIterators); - } - - @Override - public int evaluateQueueSize(long playbackPositionUs, List queue) { - return trackSelection.evaluateQueueSize(playbackPositionUs, queue); - } - - @Override - public boolean shouldCancelChunkLoad( - long playbackPositionUs, Chunk loadingChunk, List queue) { - return trackSelection.shouldCancelChunkLoad(playbackPositionUs, loadingChunk, queue); - } - - @Override - public boolean excludeTrack(int index, long exclusionDurationMs) { - return trackSelection.excludeTrack(index, exclusionDurationMs); - } - - @Override - public boolean isTrackExcluded(int index, long nowMs) { - return trackSelection.isTrackExcluded(index, nowMs); - } - - @Override - public long getLatestBitrateEstimate() { - return trackSelection.getLatestBitrateEstimate(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ForwardingTrackSelection)) { + public boolean equals(@Nullable Object other) { + if (!super.equals(other) || !(other instanceof MergingMediaPeriodTrackSelection)) { return false; } - ForwardingTrackSelection that = (ForwardingTrackSelection) o; - return trackSelection.equals(that.trackSelection) && trackGroup.equals(that.trackGroup); + MergingMediaPeriodTrackSelection that = (MergingMediaPeriodTrackSelection) other; + return trackGroup.equals(that.trackGroup); } @Override public int hashCode() { - int result = 17; - result = 31 * result + trackGroup.hashCode(); - result = 31 * result + trackSelection.hashCode(); - return result; + return 31 * super.hashCode() + trackGroup.hashCode(); } } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelection.java new file mode 100644 index 0000000000..70cab50f9e --- /dev/null +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelection.java @@ -0,0 +1,191 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.trackselection; + +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.TrackGroup; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.source.chunk.Chunk; +import androidx.media3.exoplayer.source.chunk.MediaChunk; +import androidx.media3.exoplayer.source.chunk.MediaChunkIterator; +import java.util.List; + +/** An {@link ExoTrackSelection} forwarding all calls to a wrapped instance. */ +@UnstableApi +public class ForwardingTrackSelection implements ExoTrackSelection { + private final ExoTrackSelection trackSelection; + + /** + * Creates the forwarding track selection. + * + * @param trackSelection The wrapped {@link ExoTrackSelection}. + */ + public ForwardingTrackSelection(ExoTrackSelection trackSelection) { + this.trackSelection = trackSelection; + } + + /** Returns the wrapped {@link ExoTrackSelection}. */ + public ExoTrackSelection getWrappedInstance() { + return trackSelection; + } + + @Override + public void enable() { + trackSelection.enable(); + } + + @Override + public void disable() { + trackSelection.disable(); + } + + @Override + public Format getSelectedFormat() { + return trackSelection.getSelectedFormat(); + } + + @Override + public int getSelectedIndexInTrackGroup() { + return trackSelection.getSelectedIndexInTrackGroup(); + } + + @Override + public int getSelectedIndex() { + return trackSelection.getSelectedIndex(); + } + + @Override + public @C.SelectionReason int getSelectionReason() { + return trackSelection.getSelectionReason(); + } + + @Nullable + @Override + public Object getSelectionData() { + return trackSelection.getSelectionData(); + } + + @Override + public void onPlaybackSpeed(float playbackSpeed) { + trackSelection.onPlaybackSpeed(playbackSpeed); + } + + @Override + public void onDiscontinuity() { + trackSelection.onDiscontinuity(); + } + + @Override + public void onRebuffer() { + trackSelection.onRebuffer(); + } + + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady) { + trackSelection.onPlayWhenReadyChanged(playWhenReady); + } + + @Override + public void updateSelectedTrack( + long playbackPositionUs, + long bufferedDurationUs, + long availableDurationUs, + List queue, + MediaChunkIterator[] mediaChunkIterators) { + trackSelection.updateSelectedTrack( + playbackPositionUs, bufferedDurationUs, availableDurationUs, queue, mediaChunkIterators); + } + + @Override + public int evaluateQueueSize(long playbackPositionUs, List queue) { + return trackSelection.evaluateQueueSize(playbackPositionUs, queue); + } + + @Override + public boolean shouldCancelChunkLoad( + long playbackPositionUs, Chunk loadingChunk, List queue) { + return trackSelection.shouldCancelChunkLoad(playbackPositionUs, loadingChunk, queue); + } + + @Override + public boolean excludeTrack(int index, long exclusionDurationMs) { + return trackSelection.excludeTrack(index, exclusionDurationMs); + } + + @Override + public boolean isTrackExcluded(int index, long nowMs) { + return trackSelection.isTrackExcluded(index, nowMs); + } + + @Override + public long getLatestBitrateEstimate() { + return trackSelection.getLatestBitrateEstimate(); + } + + @Override + public @Type int getType() { + return trackSelection.getType(); + } + + @Override + public TrackGroup getTrackGroup() { + return trackSelection.getTrackGroup(); + } + + @Override + public int length() { + return trackSelection.length(); + } + + @Override + public Format getFormat(int index) { + return trackSelection.getFormat(index); + } + + @Override + public int getIndexInTrackGroup(int index) { + return trackSelection.getIndexInTrackGroup(index); + } + + @Override + public int indexOf(Format format) { + return trackSelection.indexOf(format); + } + + @Override + public int indexOf(int indexInTrackGroup) { + return trackSelection.indexOf(indexInTrackGroup); + } + + @Override + public int hashCode() { + return trackSelection.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ForwardingTrackSelection)) { + return false; + } + ForwardingTrackSelection other = (ForwardingTrackSelection) obj; + return trackSelection.equals(other.trackSelection); + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelectionTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelectionTest.java new file mode 100644 index 0000000000..3f6c8a5529 --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/ForwardingTrackSelectionTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.trackselection; + +import static androidx.media3.test.utils.TestUtil.assertForwardingClassForwardsAllMethods; +import static androidx.media3.test.utils.TestUtil.assertSubclassOverridesAllMethods; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit test for {@link ForwardingTrackSelection}. */ +@RunWith(AndroidJUnit4.class) +public class ForwardingTrackSelectionTest { + + @Test + public void overridesAllMethods() throws NoSuchMethodException { + assertSubclassOverridesAllMethods(ExoTrackSelection.class, ForwardingTrackSelection.class); + } + + @Test + public void forwardsAllMethods() throws Exception { + assertForwardingClassForwardsAllMethods(ExoTrackSelection.class, ForwardingTrackSelection::new); + } +}