Skip to content

Commit

Permalink
Turn on parallel video and audio adaptation by default.
Browse files Browse the repository at this point in the history
The experimental setting shows positive results and can be turned
on by default. To avoid adaptation between HLS audio formats without
bitrates, we need to ensure that only formats with bitrates are
considered for adaptation.

Also added tests for these features.

Issue: #5111
PiperOrigin-RevId: 350315296
  • Loading branch information
tonihei authored and icbaker committed Jan 7, 2021
1 parent 51d90a4 commit aa2beb0
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 13 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
allow decoder capability checks based on codec profile/level
([#8393](https:/google/ExoPlayer/issues/8393)).
* Track selection:
* Allow parallel adaptation for video and audio
([#5111](https:/google/ExoPlayer/issues/5111)).
* Add option to specify multiple preferred audio or text languages.
* Forward `Timeline` and `MediaPeriodId` to `TrackSelection.Factory`.
* DASH:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ public static final class ParametersBuilder extends TrackSelectionParameters.Bui
private boolean forceHighestSupportedBitrate;
private boolean exceedRendererCapabilitiesIfNecessary;
private int tunnelingAudioSessionId;
private boolean allowMultipleAdaptiveSelections;

private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
selectionOverrides;
Expand Down Expand Up @@ -261,6 +262,7 @@ private ParametersBuilder(Parameters initialValues) {
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
allowMultipleAdaptiveSelections = initialValues.allowMultipleAdaptiveSelections;
// Overrides
selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides);
rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
Expand Down Expand Up @@ -645,6 +647,18 @@ public ParametersBuilder setTunnelingAudioSessionId(int tunnelingAudioSessionId)
return this;
}

/**
* Sets whether multiple adaptive selections with more than one track are allowed.
*
* @param allowMultipleAdaptiveSelections Whether multiple adaptive selections are allowed.
* @return This builder.
*/
public ParametersBuilder setAllowMultipleAdaptiveSelections(
boolean allowMultipleAdaptiveSelections) {
this.allowMultipleAdaptiveSelections = allowMultipleAdaptiveSelections;
return this;
}

// Overrides

/**
Expand Down Expand Up @@ -799,6 +813,7 @@ public Parameters build() {
forceHighestSupportedBitrate,
exceedRendererCapabilitiesIfNecessary,
tunnelingAudioSessionId,
allowMultipleAdaptiveSelections,
selectionOverrides,
rendererDisabledFlags);
}
Expand Down Expand Up @@ -827,6 +842,7 @@ private void setInitialValuesWithoutContext(@UnderInitialization ParametersBuild
forceHighestSupportedBitrate = false;
exceedRendererCapabilitiesIfNecessary = true;
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
allowMultipleAdaptiveSelections = true;
}

private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
Expand Down Expand Up @@ -1007,6 +1023,15 @@ public static Parameters getDefaults(Context context) {
* disabled).
*/
public final int tunnelingAudioSessionId;
/**
* Whether multiple adaptive selections with more than one track are allowed. The default value
* is {@code true}.
*
* <p>Note that tracks are only eligible for adaptation if they define a bitrate, the renderers
* support the tracks and allow adaptation between them, and they are not excluded based on
* other track selection parameters.
*/
public final boolean allowMultipleAdaptiveSelections;

// Overrides
private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
Expand Down Expand Up @@ -1047,6 +1072,7 @@ public static Parameters getDefaults(Context context) {
boolean forceHighestSupportedBitrate,
boolean exceedRendererCapabilitiesIfNecessary,
int tunnelingAudioSessionId,
boolean allowMultipleAdaptiveSelections,
// Overrides
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides,
SparseBooleanArray rendererDisabledFlags) {
Expand Down Expand Up @@ -1083,6 +1109,7 @@ public static Parameters getDefaults(Context context) {
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
this.allowMultipleAdaptiveSelections = allowMultipleAdaptiveSelections;
// Overrides
this.selectionOverrides = selectionOverrides;
this.rendererDisabledFlags = rendererDisabledFlags;
Expand Down Expand Up @@ -1117,6 +1144,7 @@ public static Parameters getDefaults(Context context) {
this.forceHighestSupportedBitrate = Util.readBoolean(in);
this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
this.tunnelingAudioSessionId = in.readInt();
this.allowMultipleAdaptiveSelections = Util.readBoolean(in);
// Overrides
this.selectionOverrides = readSelectionOverrides(in);
this.rendererDisabledFlags = Util.castNonNull(in.readSparseBooleanArray());
Expand Down Expand Up @@ -1203,6 +1231,7 @@ public boolean equals(@Nullable Object obj) {
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId
&& allowMultipleAdaptiveSelections == other.allowMultipleAdaptiveSelections
// Overrides
&& areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
&& areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides);
Expand Down Expand Up @@ -1238,6 +1267,7 @@ public int hashCode() {
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
result = 31 * result + tunnelingAudioSessionId;
result = 31 * result + (allowMultipleAdaptiveSelections ? 1 : 0);
// Overrides (omitted from hashCode).
return result;
}
Expand Down Expand Up @@ -1279,6 +1309,7 @@ public void writeToParcel(Parcel dest, int flags) {
Util.writeBoolean(dest, forceHighestSupportedBitrate);
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
dest.writeInt(tunnelingAudioSessionId);
Util.writeBoolean(dest, allowMultipleAdaptiveSelections);
// Overrides
writeSelectionOverridesToParcel(dest, selectionOverrides);
dest.writeSparseBooleanArray(rendererDisabledFlags);
Expand Down Expand Up @@ -1517,8 +1548,6 @@ public SelectionOverride[] newArray(int size) {
private final TrackSelection.Factory trackSelectionFactory;
private final AtomicReference<Parameters> parametersReference;

private boolean allowMultipleAdaptiveSelections;

/** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */
@Deprecated
public DefaultTrackSelector() {
Expand Down Expand Up @@ -1588,15 +1617,6 @@ public ParametersBuilder buildUponParameters() {
return getParameters().buildUpon();
}

/**
* Allows the creation of multiple adaptive track selections.
*
* <p>This method is experimental, and will be renamed or removed in a future release.
*/
public void experimentalAllowMultipleAdaptiveSelections() {
this.allowMultipleAdaptiveSelections = true;
}

// MappingTrackSelector implementation.

@Override
Expand Down Expand Up @@ -1719,7 +1739,7 @@ public void experimentalAllowMultipleAdaptiveSelections() {
for (int i = 0; i < rendererCount; i++) {
if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) {
boolean enableAdaptiveTrackSelection =
allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks;
params.allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks;
@Nullable
Pair<TrackSelection.Definition, AudioTrackScore> audioSelection =
selectAudioTrack(
Expand Down Expand Up @@ -2207,7 +2227,8 @@ private static boolean isSupportedAdaptiveAudioTrack(
boolean allowMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness) {
return isSupported(formatSupport, /* allowExceedsCapabilities= */ false)
&& (format.bitrate == Format.NO_VALUE || format.bitrate <= maxAudioBitrate)
&& format.bitrate != Format.NO_VALUE
&& format.bitrate <= maxAudioBitrate
&& (allowAudioMixedChannelCountAdaptiveness
|| (format.channelCount != Format.NO_VALUE
&& format.channelCount == primaryFormat.channelCount))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public final class DefaultTrackSelectorTest {
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setChannelCount(2)
.setSampleRate(44100)
.setAverageBitrate(128000)
.build();
private static final Format TEXT_FORMAT =
new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).build();
Expand Down Expand Up @@ -1107,6 +1108,21 @@ public void selectTracks_multipleAudioTracks_selectsAllTracksInBestConfiguration
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 6);
}

@Test
public void selectTracks_multipleAudioTracksWithoutBitrate_onlySelectsSingleTrack()
throws Exception {
TrackGroupArray trackGroups =
singleTrackGroup(
AUDIO_FORMAT.buildUpon().setId("0").setAverageBitrate(Format.NO_VALUE).build(),
AUDIO_FORMAT.buildUpon().setId("1").setAverageBitrate(Format.NO_VALUE).build());
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);

assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups.get(0), /* expectedTrack= */ 0);
}

@Test
public void selectTracksWithMultipleAudioTracksWithMixedSampleRates() throws Exception {
Format.Builder formatBuilder = AUDIO_FORMAT.buildUpon();
Expand Down Expand Up @@ -1411,6 +1427,48 @@ public void selectTracksWithMultipleVideoTracksOverrideReturnsAdaptiveTrackSelec
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 1, 2);
}

@Test
public void selectTracks_multipleVideoAndAudioTracks() throws Exception {
Format videoFormat1 = VIDEO_FORMAT.buildUpon().setAverageBitrate(1000).build();
Format videoFormat2 = VIDEO_FORMAT.buildUpon().setAverageBitrate(2000).build();
Format audioFormat1 = AUDIO_FORMAT.buildUpon().setAverageBitrate(100).build();
Format audioFormat2 = AUDIO_FORMAT.buildUpon().setAverageBitrate(200).build();
TrackGroupArray trackGroups =
new TrackGroupArray(
new TrackGroup(videoFormat1, videoFormat2), new TrackGroup(audioFormat1, audioFormat2));

// Multiple adaptive selections allowed.
trackSelector.setParameters(
trackSelector.buildUponParameters().setAllowMultipleAdaptiveSelections(true));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES},
trackGroups,
periodId,
TIMELINE);

assertThat(result.length).isEqualTo(2);
assertAdaptiveSelection(
result.selections.get(0), trackGroups.get(0), /* expectedTracks...= */ 1, 0);
assertAdaptiveSelection(
result.selections.get(1), trackGroups.get(1), /* expectedTracks...= */ 1, 0);

// Multiple adaptive selection disallowed.
trackSelector.setParameters(
trackSelector.buildUponParameters().setAllowMultipleAdaptiveSelections(false));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES},
trackGroups,
periodId,
TIMELINE);

assertThat(result.length).isEqualTo(2);
assertAdaptiveSelection(
result.selections.get(0), trackGroups.get(0), /* expectedTracks...= */ 1, 0);
assertFixedSelection(result.selections.get(1), trackGroups.get(1), /* expectedTrack= */ 1);
}

private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) {
assertThat(result.length).isEqualTo(expected.length);
for (int i = 0; i < expected.length; i++) {
Expand Down Expand Up @@ -1478,6 +1536,7 @@ private static Format buildAudioFormatWithConfiguration(
.setSampleMimeType(mimeType)
.setChannelCount(channelCount)
.setSampleRate(sampleRate)
.setAverageBitrate(128000)
.build();
}

Expand Down Expand Up @@ -1531,6 +1590,7 @@ private static Parameters buildParametersForEqualsTest() {
/* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ 13,
/* allowMultipleAdaptiveSelections= */ true,
// Overrides
selectionOverrides,
rendererDisabledFlags);
Expand Down

0 comments on commit aa2beb0

Please sign in to comment.