From 8e7ab9c8118bf48672b8d8d288a5eecb15f39e15 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 15 Feb 2022 16:53:12 +0000 Subject: [PATCH] Add TracksInfo methods for track selection UI We need TracksInfo.hasTracksOfType to determine which tabs to display in TrackSelectionDialog. We need TrackGroupInfo.isAdaptiveSupported to determine whether to allow multiple selection (check boxes) or not (radio buttons). PiperOrigin-RevId: 428793739 --- .../exoplayer2/ext/cast/CastPlayer.java | 7 ++- .../google/android/exoplayer2/TracksInfo.java | 44 +++++++++++--- .../android/exoplayer2/TracksInfoTest.java | 60 +++++++++++++------ .../trackselection/MappingTrackSelector.java | 10 +++- 4 files changed, 91 insertions(+), 30 deletions(-) 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()); }