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:
parent
51d90a40ba
commit
aa2beb080c
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user