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
This commit is contained in:
olly 2022-02-15 16:53:12 +00:00 committed by Ian Baker
parent 3cd23de1cb
commit 9137947797
4 changed files with 91 additions and 30 deletions

View File

@ -1037,7 +1037,12 @@ public final class CastPlayer extends BasePlayer {
int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE}; int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE};
final boolean[] trackSelected = new boolean[] {selected}; final boolean[] trackSelected = new boolean[] {selected};
trackGroupInfos[i] = 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); TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);

View File

@ -49,30 +49,35 @@ public final class TracksInfo implements Bundleable {
/** The number of tracks in the group. */ /** The number of tracks in the group. */
public final int length; public final int length;
private final TrackGroup trackGroup;
private final @C.FormatSupport int[] trackSupport;
private final @C.TrackType int trackType; private final @C.TrackType int trackType;
private final TrackGroup trackGroup;
private final boolean adaptiveSupported;
private final @C.FormatSupport int[] trackSupport;
private final boolean[] trackSelected; private final boolean[] trackSelected;
/** /**
* Constructs a TrackGroupInfo. * 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 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}. * @param tracksSelected Whether a track is selected for each track in {@code trackGroup}.
*/ */
@UnstableApi @UnstableApi
public TrackGroupInfo( public TrackGroupInfo(
TrackGroup trackGroup,
@C.FormatSupport int[] trackSupport,
@C.TrackType int trackType, @C.TrackType int trackType,
TrackGroup trackGroup,
boolean adaptiveSupported,
@C.FormatSupport int[] trackSupport,
boolean[] tracksSelected) { boolean[] tracksSelected) {
length = trackGroup.length; length = trackGroup.length;
checkArgument(length == trackSupport.length && length == tracksSelected.length); checkArgument(length == trackSupport.length && length == tracksSelected.length);
this.trackGroup = trackGroup;
this.trackSupport = trackSupport.clone();
this.trackType = trackType; this.trackType = trackType;
this.trackGroup = trackGroup;
this.adaptiveSupported = adaptiveSupported && length > 1;
this.trackSupport = trackSupport.clone();
this.trackSelected = tracksSelected.clone(); this.trackSelected = tracksSelected.clone();
} }
@ -135,6 +140,11 @@ public final class TracksInfo implements Bundleable {
return Booleans.contains(trackSelected, true); 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 * 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)}. * the advertised capabilities of the device. Equivalent to {@code isSupported(false)}.
@ -217,6 +227,7 @@ public final class TracksInfo implements Bundleable {
FIELD_TRACK_SUPPORT, FIELD_TRACK_SUPPORT,
FIELD_TRACK_TYPE, FIELD_TRACK_TYPE,
FIELD_TRACK_SELECTED, FIELD_TRACK_SELECTED,
FIELD_ADAPTIVE_SUPPORTED,
}) })
private @interface FieldNumber {} private @interface FieldNumber {}
@ -224,6 +235,7 @@ public final class TracksInfo implements Bundleable {
private static final int FIELD_TRACK_SUPPORT = 1; private static final int FIELD_TRACK_SUPPORT = 1;
private static final int FIELD_TRACK_TYPE = 2; private static final int FIELD_TRACK_TYPE = 2;
private static final int FIELD_TRACK_SELECTED = 3; private static final int FIELD_TRACK_SELECTED = 3;
private static final int FIELD_ADAPTIVE_SUPPORTED = 4;
@Override @Override
public Bundle toBundle() { public Bundle toBundle() {
@ -232,6 +244,7 @@ public final class TracksInfo implements Bundleable {
bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport); bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport);
bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType); bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType);
bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected); bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected);
bundle.putBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), adaptiveSupported);
return bundle; return bundle;
} }
@ -252,7 +265,10 @@ public final class TracksInfo implements Bundleable {
MoreObjects.firstNonNull( MoreObjects.firstNonNull(
bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)), bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)),
new boolean[trackGroup.length]); 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) { private static String keyForField(@FieldNumber int field) {
@ -281,6 +297,16 @@ public final class TracksInfo implements Bundleable {
return trackGroupInfos; 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 * 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. * TrackGroupInfo#isTrackSupported(int) supported} or if there are no tracks of this type.

View File

@ -17,6 +17,7 @@ package androidx.media3.common;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import androidx.media3.common.TracksInfo.TrackGroupInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.junit.Test; import org.junit.Test;
@ -38,16 +39,18 @@ public class TracksInfoTest {
TracksInfo before = TracksInfo before =
new TracksInfo( new TracksInfo(
ImmutableList.of( ImmutableList.of(
new TracksInfo.TrackGroupInfo( new TrackGroupInfo(
new TrackGroup(new Format.Builder().build()),
new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_AUDIO,
new boolean[] {true}), new TrackGroup(new Format.Builder().build()),
new TracksInfo.TrackGroupInfo( /* adaptiveSupported= */ false,
new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, /* tracksSelected= */ new boolean[] {true}),
new TrackGroupInfo(
C.TRACK_TYPE_VIDEO, 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()); TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle());
assertThat(after).isEqualTo(before); assertThat(after).isEqualTo(before);
} }
@ -58,7 +61,7 @@ public class TracksInfoTest {
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
ImmutableList<TracksInfo.TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos(); ImmutableList<TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).isEmpty(); assertThat(trackGroupInfos).isEmpty();
} }
@ -72,26 +75,31 @@ public class TracksInfoTest {
@Test @Test
public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() { public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() {
TracksInfo.TrackGroupInfo trackGroupInfo0 = TrackGroupInfo trackGroupInfo0 =
new TracksInfo.TrackGroupInfo( new TrackGroupInfo(
new TrackGroup(new Format.Builder().build()),
new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_AUDIO,
new TrackGroup(new Format.Builder().build()),
/* adaptiveSupported= */ false,
new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
/* tracksSelected= */ new boolean[] {false}); /* tracksSelected= */ new boolean[] {false});
TracksInfo.TrackGroupInfo trackGroupInfo1 = TrackGroupInfo trackGroupInfo1 =
new TracksInfo.TrackGroupInfo( new TrackGroupInfo(
new TrackGroup(new Format.Builder().build(), new Format.Builder().build()),
new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED},
C.TRACK_TYPE_VIDEO, 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}); /* tracksSelected= */ new boolean[] {false, true});
TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1)); 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_AUDIO)).isFalse();
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue();
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue();
ImmutableList<TracksInfo.TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos(); ImmutableList<TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).hasSize(2); assertThat(trackGroupInfos).hasSize(2);
assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0); assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0);
assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1); assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1);
@ -107,4 +115,20 @@ public class TracksInfoTest {
assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO); assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO); 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();
}
} }

View File

@ -573,6 +573,10 @@ public abstract class MappingTrackSelector extends TrackSelector {
@Nullable TrackSelection trackSelection = selections[rendererIndex]; @Nullable TrackSelection trackSelection = selections[rendererIndex];
for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) { for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) {
TrackGroup trackGroup = trackGroupArray.get(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]; @C.FormatSupport int[] trackSupport = new int[trackGroup.length];
boolean[] selected = new boolean[trackGroup.length]; boolean[] selected = new boolean[trackGroup.length];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
@ -586,7 +590,8 @@ public abstract class MappingTrackSelector extends TrackSelector {
} }
@C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex); @C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex);
builder.add( builder.add(
new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); new TracksInfo.TrackGroupInfo(
trackGroupType, trackGroup, adaptiveSupported, trackSupport, selected));
} }
} }
TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups(); TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups();
@ -599,7 +604,8 @@ public abstract class MappingTrackSelector extends TrackSelector {
int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType);
boolean[] selected = new boolean[trackGroup.length]; // Initialized to false. boolean[] selected = new boolean[trackGroup.length]; // Initialized to false.
builder.add( builder.add(
new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); new TracksInfo.TrackGroupInfo(
trackGroupType, trackGroup, /* adaptiveSupported= */ false, trackSupport, selected));
} }
return new TracksInfo(builder.build()); return new TracksInfo(builder.build());
} }