Turn on parallel video and audio adaptation by default.

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
This commit is contained in:
tonihei 2021-01-06 10:48:58 +00:00 committed by Ian Baker
parent 51d90a40ba
commit aa2beb080c
3 changed files with 96 additions and 13 deletions

View File

@ -43,6 +43,8 @@
allow decoder capability checks based on codec profile/level
([#8393](https://github.com/google/ExoPlayer/issues/8393)).
* Track selection:
* Allow parallel adaptation for video and audio
([#5111](https://github.com/google/ExoPlayer/issues/5111)).
* Add option to specify multiple preferred audio or text languages.
* Forward `Timeline` and `MediaPeriodId` to `TrackSelection.Factory`.
* DASH:

View File

@ -195,6 +195,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private boolean forceHighestSupportedBitrate;
private boolean exceedRendererCapabilitiesIfNecessary;
private int tunnelingAudioSessionId;
private boolean allowMultipleAdaptiveSelections;
private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
selectionOverrides;
@ -261,6 +262,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
allowMultipleAdaptiveSelections = initialValues.allowMultipleAdaptiveSelections;
// Overrides
selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides);
rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
@ -645,6 +647,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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
/**
@ -799,6 +813,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate,
exceedRendererCapabilitiesIfNecessary,
tunnelingAudioSessionId,
allowMultipleAdaptiveSelections,
selectionOverrides,
rendererDisabledFlags);
}
@ -827,6 +842,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate = false;
exceedRendererCapabilitiesIfNecessary = true;
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
allowMultipleAdaptiveSelections = true;
}
private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
@ -1007,6 +1023,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* 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>>
@ -1047,6 +1072,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
boolean forceHighestSupportedBitrate,
boolean exceedRendererCapabilitiesIfNecessary,
int tunnelingAudioSessionId,
boolean allowMultipleAdaptiveSelections,
// Overrides
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides,
SparseBooleanArray rendererDisabledFlags) {
@ -1083,6 +1109,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
this.allowMultipleAdaptiveSelections = allowMultipleAdaptiveSelections;
// Overrides
this.selectionOverrides = selectionOverrides;
this.rendererDisabledFlags = rendererDisabledFlags;
@ -1117,6 +1144,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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());
@ -1203,6 +1231,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId
&& allowMultipleAdaptiveSelections == other.allowMultipleAdaptiveSelections
// Overrides
&& areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
&& areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides);
@ -1238,6 +1267,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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;
}
@ -1279,6 +1309,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Util.writeBoolean(dest, forceHighestSupportedBitrate);
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
dest.writeInt(tunnelingAudioSessionId);
Util.writeBoolean(dest, allowMultipleAdaptiveSelections);
// Overrides
writeSelectionOverridesToParcel(dest, selectionOverrides);
dest.writeSparseBooleanArray(rendererDisabledFlags);
@ -1517,8 +1548,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private final TrackSelection.Factory trackSelectionFactory;
private final AtomicReference<Parameters> parametersReference;
private boolean allowMultipleAdaptiveSelections;
/** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */
@Deprecated
public DefaultTrackSelector() {
@ -1588,15 +1617,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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
@ -1719,7 +1739,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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(
@ -2207,7 +2227,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
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))

View File

@ -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();
@ -1107,6 +1108,21 @@ public final class DefaultTrackSelectorTest {
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();
@ -1411,6 +1427,48 @@ public final class DefaultTrackSelectorTest {
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++) {
@ -1478,6 +1536,7 @@ public final class DefaultTrackSelectorTest {
.setSampleMimeType(mimeType)
.setChannelCount(channelCount)
.setSampleRate(sampleRate)
.setAverageBitrate(128000)
.build();
}
@ -1531,6 +1590,7 @@ public final class DefaultTrackSelectorTest {
/* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ 13,
/* allowMultipleAdaptiveSelections= */ true,
// Overrides
selectionOverrides,
rendererDisabledFlags);