diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 8ebbeea2bf..5bb5b268d0 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -1035,7 +1035,12 @@ public final class CastPlayer extends BasePlayer { int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE}; final boolean[] trackSelected = new boolean[] {selected}; trackGroupInfos[i] = - new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected); + new TracksInfo.TrackGroupInfo( + trackType, + trackGroups[i], + /* adaptiveSupported= */ false, + trackSupport, + trackSelected); } TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); diff --git a/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java b/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java index 462fc47482..12c1153ffd 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java @@ -49,29 +49,34 @@ public final class TracksInfo implements Bundleable { /** The number of tracks in the group. */ public final int length; - private final TrackGroup trackGroup; - private final @C.FormatSupport int[] trackSupport; private final @C.TrackType int trackType; + private final TrackGroup trackGroup; + private final boolean adaptiveSupported; + private final @C.FormatSupport int[] trackSupport; private final boolean[] trackSelected; /** * Constructs a TrackGroupInfo. * - * @param trackGroup The {@link TrackGroup} described. - * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. * @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}. + * @param trackGroup The {@link TrackGroup} described. + * @param adaptiveSupported Whether adaptive selections containing more than one track are + * supported. + * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. * @param tracksSelected Whether a track is selected for each track in {@code trackGroup}. */ public TrackGroupInfo( - TrackGroup trackGroup, - @C.FormatSupport int[] trackSupport, @C.TrackType int trackType, + TrackGroup trackGroup, + boolean adaptiveSupported, + @C.FormatSupport int[] trackSupport, boolean[] tracksSelected) { length = trackGroup.length; checkArgument(length == trackSupport.length && length == tracksSelected.length); - this.trackGroup = trackGroup; - this.trackSupport = trackSupport.clone(); this.trackType = trackType; + this.trackGroup = trackGroup; + this.adaptiveSupported = adaptiveSupported && length > 1; + this.trackSupport = trackSupport.clone(); this.trackSelected = tracksSelected.clone(); } @@ -133,6 +138,11 @@ public final class TracksInfo implements Bundleable { return Booleans.contains(trackSelected, true); } + /** Returns whether adaptive selections containing more than one track are supported. */ + public boolean isAdaptiveSupported() { + return adaptiveSupported; + } + /** * Returns whether at least one track in the group is supported for playback, without exceeding * the advertised capabilities of the device. Equivalent to {@code isSupported(false)}. @@ -215,6 +225,7 @@ public final class TracksInfo implements Bundleable { FIELD_TRACK_SUPPORT, FIELD_TRACK_TYPE, FIELD_TRACK_SELECTED, + FIELD_ADAPTIVE_SUPPORTED, }) private @interface FieldNumber {} @@ -222,6 +233,7 @@ public final class TracksInfo implements Bundleable { private static final int FIELD_TRACK_SUPPORT = 1; private static final int FIELD_TRACK_TYPE = 2; private static final int FIELD_TRACK_SELECTED = 3; + private static final int FIELD_ADAPTIVE_SUPPORTED = 4; @Override public Bundle toBundle() { @@ -230,6 +242,7 @@ public final class TracksInfo implements Bundleable { bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport); bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType); bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected); + bundle.putBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), adaptiveSupported); return bundle; } @@ -249,7 +262,10 @@ public final class TracksInfo implements Bundleable { MoreObjects.firstNonNull( bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)), new boolean[trackGroup.length]); - return new TrackGroupInfo(trackGroup, trackSupport, trackType, selected); + boolean adaptiveSupported = + bundle.getBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), false); + return new TrackGroupInfo( + trackType, trackGroup, adaptiveSupported, trackSupport, selected); }; private static String keyForField(@FieldNumber int field) { @@ -277,6 +293,16 @@ public final class TracksInfo implements Bundleable { return trackGroupInfos; } + /** Returns true if there are tracks of type {@code trackType}, and false otherwise. */ + public boolean hasTracksOfType(@C.TrackType int trackType) { + for (int i = 0; i < trackGroupInfos.size(); i++) { + if (trackGroupInfos.get(i).trackType == trackType) { + return true; + } + } + return false; + } + /** * Returns true if at least one track of type {@code trackType} is {@link * TrackGroupInfo#isTrackSupported(int) supported} or if there are no tracks of this type. diff --git a/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java b/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java index 3e285c9b35..b56a433832 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2; import static com.google.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.TracksInfo.TrackGroupInfo; import com.google.android.exoplayer2.source.TrackGroup; import com.google.common.collect.ImmutableList; import org.junit.Test; @@ -39,16 +40,18 @@ public class TracksInfoTest { TracksInfo before = new TracksInfo( ImmutableList.of( - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build()), - new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + new TrackGroupInfo( C.TRACK_TYPE_AUDIO, - new boolean[] {true}), - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), - new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + /* tracksSelected= */ new boolean[] {true}), + new TrackGroupInfo( C.TRACK_TYPE_VIDEO, - new boolean[] {false, true}))); + new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, + /* tracksSelected= */ new boolean[] {false, true}))); TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle()); assertThat(after).isEqualTo(before); } @@ -59,7 +62,7 @@ public class TracksInfoTest { assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); - ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); + ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).isEmpty(); } @@ -73,26 +76,31 @@ public class TracksInfoTest { @Test public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() { - TracksInfo.TrackGroupInfo trackGroupInfo0 = - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build()), - new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + TrackGroupInfo trackGroupInfo0 = + new TrackGroupInfo( C.TRACK_TYPE_AUDIO, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, /* tracksSelected= */ new boolean[] {false}); - TracksInfo.TrackGroupInfo trackGroupInfo1 = - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), - new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED}, + TrackGroupInfo trackGroupInfo1 = + new TrackGroupInfo( C.TRACK_TYPE_VIDEO, + new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED}, /* tracksSelected= */ new boolean[] {false, true}); TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1)); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_AUDIO)).isTrue(); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_VIDEO)).isTrue(); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_TEXT)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue(); - ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); + ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).hasSize(2); assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0); assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1); @@ -108,4 +116,20 @@ public class TracksInfoTest { assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO); assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO); } + + /** + * Tests that {@link TrackGroupInfo#isAdaptiveSupported} returns false if the group only contains + * a single track, even if true is passed to the constructor. + */ + @Test + public void trackGroupInfo_withSingleTrack_isNotAdaptive() { + TrackGroupInfo trackGroupInfo0 = + new TrackGroupInfo( + C.TRACK_TYPE_AUDIO, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + /* tracksSelected= */ new boolean[] {false}); + assertThat(trackGroupInfo0.isAdaptiveSupported()).isFalse(); + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 91f050899e..c98d05946f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -570,6 +570,10 @@ public abstract class MappingTrackSelector extends TrackSelector { @Nullable TrackSelection trackSelection = selections[rendererIndex]; for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) { TrackGroup trackGroup = trackGroupArray.get(groupIndex); + boolean adaptiveSupported = + mappedTrackInfo.getAdaptiveSupport( + rendererIndex, groupIndex, /* includeCapabilitiesExceededTracks= */ false) + != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED; @C.FormatSupport int[] trackSupport = new int[trackGroup.length]; boolean[] selected = new boolean[trackGroup.length]; for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { @@ -583,7 +587,8 @@ public abstract class MappingTrackSelector extends TrackSelector { } @C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex); builder.add( - new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); + new TracksInfo.TrackGroupInfo( + trackGroupType, trackGroup, adaptiveSupported, trackSupport, selected)); } } TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups(); @@ -596,7 +601,8 @@ public abstract class MappingTrackSelector extends TrackSelector { int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); boolean[] selected = new boolean[trackGroup.length]; // Initialized to false. builder.add( - new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); + new TracksInfo.TrackGroupInfo( + trackGroupType, trackGroup, /* adaptiveSupported= */ false, trackSupport, selected)); } return new TracksInfo(builder.build()); }