Commit aa2beb08 by tonihei Committed by Ian Baker

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
parent 51d90a40
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
allow decoder capability checks based on codec profile/level allow decoder capability checks based on codec profile/level
([#8393](https://github.com/google/ExoPlayer/issues/8393)). ([#8393](https://github.com/google/ExoPlayer/issues/8393)).
* Track selection: * 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. * Add option to specify multiple preferred audio or text languages.
* Forward `Timeline` and `MediaPeriodId` to `TrackSelection.Factory`. * Forward `Timeline` and `MediaPeriodId` to `TrackSelection.Factory`.
* DASH: * DASH:
......
...@@ -195,6 +195,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -195,6 +195,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private boolean forceHighestSupportedBitrate; private boolean forceHighestSupportedBitrate;
private boolean exceedRendererCapabilitiesIfNecessary; private boolean exceedRendererCapabilitiesIfNecessary;
private int tunnelingAudioSessionId; private int tunnelingAudioSessionId;
private boolean allowMultipleAdaptiveSelections;
private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
selectionOverrides; selectionOverrides;
...@@ -261,6 +262,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -261,6 +262,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate; forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary; exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId; tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
allowMultipleAdaptiveSelections = initialValues.allowMultipleAdaptiveSelections;
// Overrides // Overrides
selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides); selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides);
rendererDisabledFlags = initialValues.rendererDisabledFlags.clone(); rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
...@@ -645,6 +647,18 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -645,6 +647,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this; 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 // Overrides
/** /**
...@@ -799,6 +813,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -799,6 +813,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate, forceHighestSupportedBitrate,
exceedRendererCapabilitiesIfNecessary, exceedRendererCapabilitiesIfNecessary,
tunnelingAudioSessionId, tunnelingAudioSessionId,
allowMultipleAdaptiveSelections,
selectionOverrides, selectionOverrides,
rendererDisabledFlags); rendererDisabledFlags);
} }
...@@ -827,6 +842,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -827,6 +842,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
forceHighestSupportedBitrate = false; forceHighestSupportedBitrate = false;
exceedRendererCapabilitiesIfNecessary = true; exceedRendererCapabilitiesIfNecessary = true;
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET; tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
allowMultipleAdaptiveSelections = true;
} }
private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
...@@ -1007,6 +1023,15 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1007,6 +1023,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* disabled). * disabled).
*/ */
public final int tunnelingAudioSessionId; 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 // Overrides
private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
...@@ -1047,6 +1072,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1047,6 +1072,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
boolean forceHighestSupportedBitrate, boolean forceHighestSupportedBitrate,
boolean exceedRendererCapabilitiesIfNecessary, boolean exceedRendererCapabilitiesIfNecessary,
int tunnelingAudioSessionId, int tunnelingAudioSessionId,
boolean allowMultipleAdaptiveSelections,
// Overrides // Overrides
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides, SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides,
SparseBooleanArray rendererDisabledFlags) { SparseBooleanArray rendererDisabledFlags) {
...@@ -1083,6 +1109,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1083,6 +1109,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate; this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary; this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
this.tunnelingAudioSessionId = tunnelingAudioSessionId; this.tunnelingAudioSessionId = tunnelingAudioSessionId;
this.allowMultipleAdaptiveSelections = allowMultipleAdaptiveSelections;
// Overrides // Overrides
this.selectionOverrides = selectionOverrides; this.selectionOverrides = selectionOverrides;
this.rendererDisabledFlags = rendererDisabledFlags; this.rendererDisabledFlags = rendererDisabledFlags;
...@@ -1117,6 +1144,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1117,6 +1144,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.forceHighestSupportedBitrate = Util.readBoolean(in); this.forceHighestSupportedBitrate = Util.readBoolean(in);
this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in); this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
this.tunnelingAudioSessionId = in.readInt(); this.tunnelingAudioSessionId = in.readInt();
this.allowMultipleAdaptiveSelections = Util.readBoolean(in);
// Overrides // Overrides
this.selectionOverrides = readSelectionOverrides(in); this.selectionOverrides = readSelectionOverrides(in);
this.rendererDisabledFlags = Util.castNonNull(in.readSparseBooleanArray()); this.rendererDisabledFlags = Util.castNonNull(in.readSparseBooleanArray());
...@@ -1203,6 +1231,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1203,6 +1231,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate && forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary && exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId && tunnelingAudioSessionId == other.tunnelingAudioSessionId
&& allowMultipleAdaptiveSelections == other.allowMultipleAdaptiveSelections
// Overrides // Overrides
&& areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags) && areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
&& areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides); && areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides);
...@@ -1238,6 +1267,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1238,6 +1267,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0); result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0); result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
result = 31 * result + tunnelingAudioSessionId; result = 31 * result + tunnelingAudioSessionId;
result = 31 * result + (allowMultipleAdaptiveSelections ? 1 : 0);
// Overrides (omitted from hashCode). // Overrides (omitted from hashCode).
return result; return result;
} }
...@@ -1279,6 +1309,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1279,6 +1309,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Util.writeBoolean(dest, forceHighestSupportedBitrate); Util.writeBoolean(dest, forceHighestSupportedBitrate);
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary); Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
dest.writeInt(tunnelingAudioSessionId); dest.writeInt(tunnelingAudioSessionId);
Util.writeBoolean(dest, allowMultipleAdaptiveSelections);
// Overrides // Overrides
writeSelectionOverridesToParcel(dest, selectionOverrides); writeSelectionOverridesToParcel(dest, selectionOverrides);
dest.writeSparseBooleanArray(rendererDisabledFlags); dest.writeSparseBooleanArray(rendererDisabledFlags);
...@@ -1517,8 +1548,6 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1517,8 +1548,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private final TrackSelection.Factory trackSelectionFactory; private final TrackSelection.Factory trackSelectionFactory;
private final AtomicReference<Parameters> parametersReference; private final AtomicReference<Parameters> parametersReference;
private boolean allowMultipleAdaptiveSelections;
/** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */ /** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */
@Deprecated @Deprecated
public DefaultTrackSelector() { public DefaultTrackSelector() {
...@@ -1588,15 +1617,6 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1588,15 +1617,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return getParameters().buildUpon(); 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. // MappingTrackSelector implementation.
@Override @Override
...@@ -1719,7 +1739,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1719,7 +1739,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) { if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) {
boolean enableAdaptiveTrackSelection = boolean enableAdaptiveTrackSelection =
allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks; params.allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks;
@Nullable @Nullable
Pair<TrackSelection.Definition, AudioTrackScore> audioSelection = Pair<TrackSelection.Definition, AudioTrackScore> audioSelection =
selectAudioTrack( selectAudioTrack(
...@@ -2207,7 +2227,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2207,7 +2227,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
boolean allowMixedSampleRateAdaptiveness, boolean allowMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness) { boolean allowAudioMixedChannelCountAdaptiveness) {
return isSupported(formatSupport, /* allowExceedsCapabilities= */ false) return isSupported(formatSupport, /* allowExceedsCapabilities= */ false)
&& (format.bitrate == Format.NO_VALUE || format.bitrate <= maxAudioBitrate) && format.bitrate != Format.NO_VALUE
&& format.bitrate <= maxAudioBitrate
&& (allowAudioMixedChannelCountAdaptiveness && (allowAudioMixedChannelCountAdaptiveness
|| (format.channelCount != Format.NO_VALUE || (format.channelCount != Format.NO_VALUE
&& format.channelCount == primaryFormat.channelCount)) && format.channelCount == primaryFormat.channelCount))
......
...@@ -94,6 +94,7 @@ public final class DefaultTrackSelectorTest { ...@@ -94,6 +94,7 @@ public final class DefaultTrackSelectorTest {
.setSampleMimeType(MimeTypes.AUDIO_AAC) .setSampleMimeType(MimeTypes.AUDIO_AAC)
.setChannelCount(2) .setChannelCount(2)
.setSampleRate(44100) .setSampleRate(44100)
.setAverageBitrate(128000)
.build(); .build();
private static final Format TEXT_FORMAT = private static final Format TEXT_FORMAT =
new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).build(); new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).build();
...@@ -1108,6 +1109,21 @@ public final class DefaultTrackSelectorTest { ...@@ -1108,6 +1109,21 @@ public final class DefaultTrackSelectorTest {
} }
@Test @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 { public void selectTracksWithMultipleAudioTracksWithMixedSampleRates() throws Exception {
Format.Builder formatBuilder = AUDIO_FORMAT.buildUpon(); Format.Builder formatBuilder = AUDIO_FORMAT.buildUpon();
Format highSampleRateAudioFormat = formatBuilder.setSampleRate(44100).build(); Format highSampleRateAudioFormat = formatBuilder.setSampleRate(44100).build();
...@@ -1411,6 +1427,48 @@ public final class DefaultTrackSelectorTest { ...@@ -1411,6 +1427,48 @@ public final class DefaultTrackSelectorTest {
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 1, 2); 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) { private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) {
assertThat(result.length).isEqualTo(expected.length); assertThat(result.length).isEqualTo(expected.length);
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {
...@@ -1478,6 +1536,7 @@ public final class DefaultTrackSelectorTest { ...@@ -1478,6 +1536,7 @@ public final class DefaultTrackSelectorTest {
.setSampleMimeType(mimeType) .setSampleMimeType(mimeType)
.setChannelCount(channelCount) .setChannelCount(channelCount)
.setSampleRate(sampleRate) .setSampleRate(sampleRate)
.setAverageBitrate(128000)
.build(); .build();
} }
...@@ -1531,6 +1590,7 @@ public final class DefaultTrackSelectorTest { ...@@ -1531,6 +1590,7 @@ public final class DefaultTrackSelectorTest {
/* forceHighestSupportedBitrate= */ true, /* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false, /* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ 13, /* tunnelingAudioSessionId= */ 13,
/* allowMultipleAdaptiveSelections= */ true,
// Overrides // Overrides
selectionOverrides, selectionOverrides,
rendererDisabledFlags); rendererDisabledFlags);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment