Add options for controlling audio track selection

Issue: #3314

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=219158729
This commit is contained in:
olly 2018-10-29 11:08:22 -07:00 committed by Oliver Woodman
parent 069e3cbf7d
commit 7eeeb40d24
3 changed files with 519 additions and 77 deletions

View File

@ -6,6 +6,8 @@
here ([#2826](https://github.com/google/ExoPlayer/issues/2826)).
* Improve initial bandwidth meter estimates using the current country and
network type.
* Add options for controlling audio track selections to `DefaultTrackSelector`
([#3314](https://github.com/google/ExoPlayer/issues/3314)).
* Do not retry failed loads whose error is `FileNotFoundException`.
* Add convenience methods `Player.next`, `Player.previous`, `Player.hasNext`
and `Player.hasPrevious`

View File

@ -170,11 +170,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private int maxVideoFrameRate;
private int maxVideoBitrate;
private boolean exceedVideoConstraintsIfNecessary;
private boolean allowVideoMixedMimeTypeAdaptiveness;
private boolean allowVideoNonSeamlessAdaptiveness;
private int viewportWidth;
private int viewportHeight;
private boolean viewportOrientationMayChange;
// Audio
@Nullable private String preferredAudioLanguage;
private int maxAudioChannelCount;
private int maxAudioBitrate;
private boolean exceedAudioConstraintsIfNecessary;
private boolean allowAudioMixedMimeTypeAdaptiveness;
private boolean allowAudioMixedSampleRateAdaptiveness;
// Text
@Nullable private String preferredTextLanguage;
private boolean selectUndeterminedTextLanguage;
@ -182,8 +189,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
private boolean forceLowestBitrate;
private boolean forceHighestSupportedBitrate;
private boolean allowMixedMimeAdaptiveness;
private boolean allowNonSeamlessAdaptiveness;
private boolean exceedRendererCapabilitiesIfNecessary;
private int tunnelingAudioSessionId;
@ -203,11 +208,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
maxVideoFrameRate = initialValues.maxVideoFrameRate;
maxVideoBitrate = initialValues.maxVideoBitrate;
exceedVideoConstraintsIfNecessary = initialValues.exceedVideoConstraintsIfNecessary;
allowVideoMixedMimeTypeAdaptiveness = initialValues.allowVideoMixedMimeTypeAdaptiveness;
allowVideoNonSeamlessAdaptiveness = initialValues.allowVideoNonSeamlessAdaptiveness;
viewportWidth = initialValues.viewportWidth;
viewportHeight = initialValues.viewportHeight;
viewportOrientationMayChange = initialValues.viewportOrientationMayChange;
// Audio
preferredAudioLanguage = initialValues.preferredAudioLanguage;
maxAudioChannelCount = initialValues.maxAudioChannelCount;
maxAudioBitrate = initialValues.maxAudioBitrate;
exceedAudioConstraintsIfNecessary = initialValues.exceedAudioConstraintsIfNecessary;
allowAudioMixedMimeTypeAdaptiveness = initialValues.allowAudioMixedMimeTypeAdaptiveness;
allowAudioMixedSampleRateAdaptiveness = initialValues.allowAudioMixedSampleRateAdaptiveness;
// Text
preferredTextLanguage = initialValues.preferredTextLanguage;
selectUndeterminedTextLanguage = initialValues.selectUndeterminedTextLanguage;
@ -215,8 +227,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
forceLowestBitrate = initialValues.forceLowestBitrate;
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
allowMixedMimeAdaptiveness = initialValues.allowMixedMimeAdaptiveness;
allowNonSeamlessAdaptiveness = initialValues.allowNonSeamlessAdaptiveness;
exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
// Overrides
@ -286,6 +296,28 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this;
}
/**
* See {@link Parameters#allowVideoMixedMimeTypeAdaptiveness}.
*
* @return This builder.
*/
public ParametersBuilder setAllowVideoMixedMimeTypeAdaptiveness(
boolean allowVideoMixedMimeTypeAdaptiveness) {
this.allowVideoMixedMimeTypeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
return this;
}
/**
* See {@link Parameters#allowVideoNonSeamlessAdaptiveness}.
*
* @return This builder.
*/
public ParametersBuilder setAllowVideoNonSeamlessAdaptiveness(
boolean allowVideoNonSeamlessAdaptiveness) {
this.allowVideoNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
return this;
}
/**
* Equivalent to calling {@link #setViewportSize(int, int, boolean)} with the viewport size
* obtained from {@link Util#getPhysicalDisplaySize(Context)}.
@ -340,6 +372,59 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this;
}
/**
* See {@link Parameters#maxAudioChannelCount}.
*
* @return This builder.
*/
public ParametersBuilder setMaxAudioChannelCount(int maxAudioChannelCount) {
this.maxAudioChannelCount = maxAudioChannelCount;
return this;
}
/**
* See {@link Parameters#maxAudioBitrate}.
*
* @return This builder.
*/
public ParametersBuilder setMaxAudioBitrate(int maxAudioBitrate) {
this.maxAudioBitrate = maxAudioBitrate;
return this;
}
/**
* See {@link Parameters#exceedAudioConstraintsIfNecessary}.
*
* @return This builder.
*/
public ParametersBuilder setExceedAudioConstraintsIfNecessary(
boolean exceedAudioConstraintsIfNecessary) {
this.exceedAudioConstraintsIfNecessary = exceedAudioConstraintsIfNecessary;
return this;
}
/**
* See {@link Parameters#allowAudioMixedMimeTypeAdaptiveness}.
*
* @return This builder.
*/
public ParametersBuilder setAllowAudioMixedMimeTypeAdaptiveness(
boolean allowAudioMixedMimeTypeAdaptiveness) {
this.allowAudioMixedMimeTypeAdaptiveness = allowAudioMixedMimeTypeAdaptiveness;
return this;
}
/**
* See {@link Parameters#allowAudioMixedSampleRateAdaptiveness}.
*
* @return This builder.
*/
public ParametersBuilder setAllowAudioMixedSampleRateAdaptiveness(
boolean allowAudioMixedSampleRateAdaptiveness) {
this.allowAudioMixedSampleRateAdaptiveness = allowAudioMixedSampleRateAdaptiveness;
return this;
}
// Text
/**
@ -397,23 +482,20 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#allowMixedMimeAdaptiveness}.
*
* @return This builder.
* @deprecated Use {@link #setAllowVideoMixedMimeTypeAdaptiveness(boolean)} and {@link
* #setAllowAudioMixedMimeTypeAdaptiveness(boolean)}.
*/
@Deprecated
public ParametersBuilder setAllowMixedMimeAdaptiveness(boolean allowMixedMimeAdaptiveness) {
this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness;
setAllowAudioMixedMimeTypeAdaptiveness(allowMixedMimeAdaptiveness);
setAllowVideoMixedMimeTypeAdaptiveness(allowMixedMimeAdaptiveness);
return this;
}
/**
* See {@link Parameters#allowNonSeamlessAdaptiveness}.
*
* @return This builder.
*/
/** @deprecated Use {@link #setAllowVideoNonSeamlessAdaptiveness(boolean)} */
@Deprecated
public ParametersBuilder setAllowNonSeamlessAdaptiveness(boolean allowNonSeamlessAdaptiveness) {
this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness;
return this;
return setAllowVideoNonSeamlessAdaptiveness(allowNonSeamlessAdaptiveness);
}
/**
@ -563,11 +645,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
maxVideoFrameRate,
maxVideoBitrate,
exceedVideoConstraintsIfNecessary,
allowVideoMixedMimeTypeAdaptiveness,
allowVideoNonSeamlessAdaptiveness,
viewportWidth,
viewportHeight,
viewportOrientationMayChange,
// Audio
preferredAudioLanguage,
maxAudioChannelCount,
maxAudioBitrate,
exceedAudioConstraintsIfNecessary,
allowAudioMixedMimeTypeAdaptiveness,
allowAudioMixedSampleRateAdaptiveness,
// Text
preferredTextLanguage,
selectUndeterminedTextLanguage,
@ -575,8 +664,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
forceLowestBitrate,
forceHighestSupportedBitrate,
allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness,
exceedRendererCapabilitiesIfNecessary,
tunnelingAudioSessionId,
// Overrides
@ -638,6 +725,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* {@code true}.
*/
public final boolean exceedVideoConstraintsIfNecessary;
/**
* Whether to allow adaptive video selections containing mixed mime types. Adaptations between
* different mime types may not be completely seamless, in which case {@link
* #allowVideoNonSeamlessAdaptiveness} also needs to be {@code true} for mixed mime type
* selections to be made. The default value is {@code false}.
*/
public final boolean allowVideoMixedMimeTypeAdaptiveness;
/**
* Whether to allow adaptive video selections where adaptation may not be completely seamless.
* The default value is {@code true}.
*/
public final boolean allowVideoNonSeamlessAdaptiveness;
/**
* Viewport width in pixels. Constrains video track selections for adaptive content so that only
* tracks suitable for the viewport are selected. The default value is {@link Integer#MAX_VALUE}
@ -664,6 +763,30 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* {@code null}.
*/
@Nullable public final String preferredAudioLanguage;
/**
* Maximum allowed audio channel count. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
*/
public final int maxAudioChannelCount;
/**
* Maximum audio bitrate. The default value is {@link Integer#MAX_VALUE} (i.e. no constraint).
*/
public final int maxAudioBitrate;
/**
* Whether to exceed the {@link #maxAudioChannelCount} and {@link #maxAudioBitrate} constraints
* when no selection can be made otherwise. The default value is {@code true}.
*/
public final boolean exceedAudioConstraintsIfNecessary;
/**
* Whether to allow adaptive audio selections containing mixed mime types. Adaptations between
* different mime types may not be completely seamless. The default value is {@code false}.
*/
public final boolean allowAudioMixedMimeTypeAdaptiveness;
/**
* Whether to allow adaptive audio selections containing mixed sample rates. Adaptations between
* different sample rates may not be completely seamless. The default value is {@code false}.
*/
public final boolean allowAudioMixedSampleRateAdaptiveness;
// Text
/**
@ -695,15 +818,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final boolean forceHighestSupportedBitrate;
/**
* Whether to allow adaptive selections containing mixed mime types. The default value is {@code
* false}.
* @deprecated Use {@link #allowVideoMixedMimeTypeAdaptiveness} and {@link
* #allowAudioMixedMimeTypeAdaptiveness}.
*/
public final boolean allowMixedMimeAdaptiveness;
/**
* Whether to allow adaptive selections where adaptation may not be completely seamless. The
* default value is {@code true}.
*/
public final boolean allowNonSeamlessAdaptiveness;
@Deprecated public final boolean allowMixedMimeAdaptiveness;
/** @deprecated Use {@link #allowVideoNonSeamlessAdaptiveness}. */
@Deprecated public final boolean allowNonSeamlessAdaptiveness;
/**
* Whether to exceed renderer capabilities when no selection can be made otherwise.
*
@ -729,11 +849,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/* maxVideoFrameRate= */ Integer.MAX_VALUE,
/* maxVideoBitrate= */ Integer.MAX_VALUE,
/* exceedVideoConstraintsIfNecessary= */ true,
/* allowVideoMixedMimeTypeAdaptiveness= */ false,
/* allowVideoNonSeamlessAdaptiveness= */ true,
/* viewportWidth= */ Integer.MAX_VALUE,
/* viewportHeight= */ Integer.MAX_VALUE,
/* viewportOrientationMayChange= */ true,
// Audio
/* preferredAudioLanguage= */ null,
/* maxAudioChannelCount= */ Integer.MAX_VALUE,
/* maxAudioBitrate= */ Integer.MAX_VALUE,
/* exceedAudioConstraintsIfNecessary= */ true,
/* allowAudioMixedMimeTypeAdaptiveness= */ false,
/* allowAudioMixedSampleRateAdaptiveness= */ false,
// Text
/* preferredTextLanguage= */ null,
/* selectUndeterminedTextLanguage= */ false,
@ -741,8 +868,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
/* forceLowestBitrate= */ false,
/* forceHighestSupportedBitrate= */ false,
/* allowMixedMimeAdaptiveness= */ false,
/* allowNonSeamlessAdaptiveness= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ true,
/* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET,
// Overrides
@ -757,11 +882,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int maxVideoFrameRate,
int maxVideoBitrate,
boolean exceedVideoConstraintsIfNecessary,
boolean allowVideoMixedMimeTypeAdaptiveness,
boolean allowVideoNonSeamlessAdaptiveness,
int viewportWidth,
int viewportHeight,
boolean viewportOrientationMayChange,
// Audio
@Nullable String preferredAudioLanguage,
int maxAudioChannelCount,
int maxAudioBitrate,
boolean exceedAudioConstraintsIfNecessary,
boolean allowAudioMixedMimeTypeAdaptiveness,
boolean allowAudioMixedSampleRateAdaptiveness,
// Text
@Nullable String preferredTextLanguage,
boolean selectUndeterminedTextLanguage,
@ -769,8 +901,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
boolean forceLowestBitrate,
boolean forceHighestSupportedBitrate,
boolean allowMixedMimeAdaptiveness,
boolean allowNonSeamlessAdaptiveness,
boolean exceedRendererCapabilitiesIfNecessary,
int tunnelingAudioSessionId,
// Overrides
@ -782,11 +912,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.maxVideoFrameRate = maxVideoFrameRate;
this.maxVideoBitrate = maxVideoBitrate;
this.exceedVideoConstraintsIfNecessary = exceedVideoConstraintsIfNecessary;
this.allowVideoMixedMimeTypeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
this.allowVideoNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight;
this.viewportOrientationMayChange = viewportOrientationMayChange;
// Audio
this.preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage);
this.maxAudioChannelCount = maxAudioChannelCount;
this.maxAudioBitrate = maxAudioBitrate;
this.exceedAudioConstraintsIfNecessary = exceedAudioConstraintsIfNecessary;
this.allowAudioMixedMimeTypeAdaptiveness = allowAudioMixedMimeTypeAdaptiveness;
this.allowAudioMixedSampleRateAdaptiveness = allowAudioMixedSampleRateAdaptiveness;
// Text
this.preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage);
this.selectUndeterminedTextLanguage = selectUndeterminedTextLanguage;
@ -794,13 +931,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
this.forceLowestBitrate = forceLowestBitrate;
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness;
this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness;
this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
// Overrides
this.selectionOverrides = selectionOverrides;
this.rendererDisabledFlags = rendererDisabledFlags;
// Deprecated fields.
this.allowMixedMimeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
this.allowNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
}
/* package */ Parameters(Parcel in) {
@ -810,11 +948,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.maxVideoFrameRate = in.readInt();
this.maxVideoBitrate = in.readInt();
this.exceedVideoConstraintsIfNecessary = Util.readBoolean(in);
this.allowVideoMixedMimeTypeAdaptiveness = Util.readBoolean(in);
this.allowVideoNonSeamlessAdaptiveness = Util.readBoolean(in);
this.viewportWidth = in.readInt();
this.viewportHeight = in.readInt();
this.viewportOrientationMayChange = Util.readBoolean(in);
// Audio
this.preferredAudioLanguage = in.readString();
this.maxAudioChannelCount = in.readInt();
this.maxAudioBitrate = in.readInt();
this.exceedAudioConstraintsIfNecessary = Util.readBoolean(in);
this.allowAudioMixedMimeTypeAdaptiveness = Util.readBoolean(in);
this.allowAudioMixedSampleRateAdaptiveness = Util.readBoolean(in);
// Text
this.preferredTextLanguage = in.readString();
this.selectUndeterminedTextLanguage = Util.readBoolean(in);
@ -822,13 +967,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
this.forceLowestBitrate = Util.readBoolean(in);
this.forceHighestSupportedBitrate = Util.readBoolean(in);
this.allowMixedMimeAdaptiveness = Util.readBoolean(in);
this.allowNonSeamlessAdaptiveness = Util.readBoolean(in);
this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
this.tunnelingAudioSessionId = in.readInt();
// Overrides
this.selectionOverrides = readSelectionOverrides(in);
this.rendererDisabledFlags = in.readSparseBooleanArray();
// Deprecated fields.
this.allowMixedMimeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
this.allowNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
}
/**
@ -887,11 +1033,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& maxVideoFrameRate == other.maxVideoFrameRate
&& maxVideoBitrate == other.maxVideoBitrate
&& exceedVideoConstraintsIfNecessary == other.exceedVideoConstraintsIfNecessary
&& allowVideoMixedMimeTypeAdaptiveness == other.allowVideoMixedMimeTypeAdaptiveness
&& allowVideoNonSeamlessAdaptiveness == other.allowVideoNonSeamlessAdaptiveness
&& viewportOrientationMayChange == other.viewportOrientationMayChange
&& viewportWidth == other.viewportWidth
&& viewportHeight == other.viewportHeight
// Audio
&& TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage)
&& maxAudioChannelCount == other.maxAudioChannelCount
&& maxAudioBitrate == other.maxAudioBitrate
&& exceedAudioConstraintsIfNecessary == other.exceedAudioConstraintsIfNecessary
&& allowAudioMixedMimeTypeAdaptiveness == other.allowAudioMixedMimeTypeAdaptiveness
&& allowAudioMixedSampleRateAdaptiveness == other.allowAudioMixedSampleRateAdaptiveness
// Text
&& TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage)
&& selectUndeterminedTextLanguage == other.selectUndeterminedTextLanguage
@ -899,8 +1052,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
&& forceLowestBitrate == other.forceLowestBitrate
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
&& allowMixedMimeAdaptiveness == other.allowMixedMimeAdaptiveness
&& allowNonSeamlessAdaptiveness == other.allowNonSeamlessAdaptiveness
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId
// Overrides
@ -917,12 +1068,19 @@ public class DefaultTrackSelector extends MappingTrackSelector {
result = 31 * result + maxVideoFrameRate;
result = 31 * result + maxVideoBitrate;
result = 31 * result + (exceedVideoConstraintsIfNecessary ? 1 : 0);
result = 31 * result + (allowVideoMixedMimeTypeAdaptiveness ? 1 : 0);
result = 31 * result + (allowVideoNonSeamlessAdaptiveness ? 1 : 0);
result = 31 * result + (viewportOrientationMayChange ? 1 : 0);
result = 31 * result + viewportWidth;
result = 31 * result + viewportHeight;
// Audio
result =
31 * result + (preferredAudioLanguage == null ? 0 : preferredAudioLanguage.hashCode());
result = 31 * result + maxAudioChannelCount;
result = 31 * result + maxAudioBitrate;
result = 31 * result + (exceedAudioConstraintsIfNecessary ? 1 : 0);
result = 31 * result + (allowAudioMixedMimeTypeAdaptiveness ? 1 : 0);
result = 31 * result + (allowAudioMixedSampleRateAdaptiveness ? 1 : 0);
// Text
result = 31 * result + (preferredTextLanguage == null ? 0 : preferredTextLanguage.hashCode());
result = 31 * result + (selectUndeterminedTextLanguage ? 1 : 0);
@ -930,8 +1088,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
result = 31 * result + (forceLowestBitrate ? 1 : 0);
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
result = 31 * result + (allowMixedMimeAdaptiveness ? 1 : 0);
result = 31 * result + (allowNonSeamlessAdaptiveness ? 1 : 0);
result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
result = 31 * result + tunnelingAudioSessionId;
// Overrides (omitted from hashCode).
@ -953,11 +1109,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
dest.writeInt(maxVideoFrameRate);
dest.writeInt(maxVideoBitrate);
Util.writeBoolean(dest, exceedVideoConstraintsIfNecessary);
Util.writeBoolean(dest, allowVideoMixedMimeTypeAdaptiveness);
Util.writeBoolean(dest, allowVideoNonSeamlessAdaptiveness);
dest.writeInt(viewportWidth);
dest.writeInt(viewportHeight);
Util.writeBoolean(dest, viewportOrientationMayChange);
// Audio
dest.writeString(preferredAudioLanguage);
dest.writeInt(maxAudioChannelCount);
dest.writeInt(maxAudioBitrate);
Util.writeBoolean(dest, exceedAudioConstraintsIfNecessary);
Util.writeBoolean(dest, allowAudioMixedMimeTypeAdaptiveness);
Util.writeBoolean(dest, allowAudioMixedSampleRateAdaptiveness);
// Text
dest.writeString(preferredTextLanguage);
Util.writeBoolean(dest, selectUndeterminedTextLanguage);
@ -965,8 +1128,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
Util.writeBoolean(dest, forceLowestBitrate);
Util.writeBoolean(dest, forceHighestSupportedBitrate);
Util.writeBoolean(dest, allowMixedMimeAdaptiveness);
Util.writeBoolean(dest, allowNonSeamlessAdaptiveness);
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
dest.writeInt(tunnelingAudioSessionId);
// Overrides
@ -1322,11 +1483,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererTrackGroups.get(override.groupIndex), override.tracks[0]);
} else {
rendererTrackSelections[i] =
Assertions.checkNotNull(adaptiveTrackSelectionFactory)
.createTrackSelection(
rendererTrackGroups.get(override.groupIndex),
getBandwidthMeter(),
override.tracks);
adaptiveTrackSelectionFactory.createTrackSelection(
rendererTrackGroups.get(override.groupIndex),
getBandwidthMeter(),
override.tracks);
}
}
}
@ -1508,11 +1668,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
TrackSelection.Factory adaptiveTrackSelectionFactory,
BandwidthMeter bandwidthMeter)
throws ExoPlaybackException {
int requiredAdaptiveSupport = params.allowNonSeamlessAdaptiveness
? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
: RendererCapabilities.ADAPTIVE_SEAMLESS;
int requiredAdaptiveSupport =
params.allowVideoNonSeamlessAdaptiveness
? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
: RendererCapabilities.ADAPTIVE_SEAMLESS;
boolean allowMixedMimeTypes =
params.allowMixedMimeAdaptiveness
params.allowVideoMixedMimeTypeAdaptiveness
&& (mixedMimeTypeAdaptationSupports & requiredAdaptiveSupport) != 0;
for (int i = 0; i < groups.length; i++) {
TrackGroup group = groups.get(i);
@ -1530,8 +1691,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
params.viewportHeight,
params.viewportOrientationMayChange);
if (adaptiveTracks.length > 0) {
return Assertions.checkNotNull(adaptiveTrackSelectionFactory)
.createTrackSelection(group, bandwidthMeter, adaptiveTracks);
return adaptiveTrackSelectionFactory.createTrackSelection(
group, bandwidthMeter, adaptiveTracks);
}
}
return null;
@ -1758,6 +1919,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* selection was made.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
@SuppressWarnings("unused")
protected @Nullable Pair<TrackSelection, AudioTrackScore> selectAudioTrack(
TrackGroupArray groups,
int[][] formatSupports,
@ -1777,6 +1939,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Format format = trackGroup.getFormat(trackIndex);
AudioTrackScore trackScore =
new AudioTrackScore(format, params, trackFormatSupport[trackIndex]);
if (!trackScore.isWithinConstraints && !params.exceedAudioConstraintsIfNecessary) {
// Track should not be selected.
continue;
}
if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
selectedGroupIndex = groupIndex;
selectedTrackIndex = trackIndex;
@ -1799,7 +1965,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// If the group of the track with the highest score allows it, try to enable adaptation.
int[] adaptiveTracks =
getAdaptiveAudioTracks(
selectedGroup, formatSupports[selectedGroupIndex], params.allowMixedMimeAdaptiveness);
selectedGroup,
formatSupports[selectedGroupIndex],
params.allowAudioMixedMimeTypeAdaptiveness,
params.allowAudioMixedSampleRateAdaptiveness);
if (adaptiveTracks.length > 0) {
selection =
adaptiveTrackSelectionFactory.createTrackSelection(
@ -1814,18 +1983,27 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return Pair.create(selection, Assertions.checkNotNull(selectedTrackScore));
}
private static int[] getAdaptiveAudioTracks(TrackGroup group, int[] formatSupport,
boolean allowMixedMimeTypes) {
private static int[] getAdaptiveAudioTracks(
TrackGroup group,
int[] formatSupport,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
int selectedConfigurationTrackCount = 0;
AudioConfigurationTuple selectedConfiguration = null;
HashSet<AudioConfigurationTuple> seenConfigurationTuples = new HashSet<>();
for (int i = 0; i < group.length; i++) {
Format format = group.getFormat(i);
AudioConfigurationTuple configuration = new AudioConfigurationTuple(
format.channelCount, format.sampleRate,
allowMixedMimeTypes ? null : format.sampleMimeType);
AudioConfigurationTuple configuration =
new AudioConfigurationTuple(
format.channelCount, format.sampleRate, format.sampleMimeType);
if (seenConfigurationTuples.add(configuration)) {
int configurationCount = getAdaptiveAudioTrackCount(group, formatSupport, configuration);
int configurationCount =
getAdaptiveAudioTrackCount(
group,
formatSupport,
configuration,
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness);
if (configurationCount > selectedConfigurationTrackCount) {
selectedConfiguration = configuration;
selectedConfigurationTrackCount = configurationCount;
@ -1838,7 +2016,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int index = 0;
for (int i = 0; i < group.length; i++) {
if (isSupportedAdaptiveAudioTrack(
group.getFormat(i), formatSupport[i], Assertions.checkNotNull(selectedConfiguration))) {
group.getFormat(i),
formatSupport[i],
Assertions.checkNotNull(selectedConfiguration),
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness)) {
adaptiveIndices[index++] = i;
}
}
@ -1847,23 +2029,41 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return NO_TRACKS;
}
private static int getAdaptiveAudioTrackCount(TrackGroup group, int[] formatSupport,
AudioConfigurationTuple configuration) {
private static int getAdaptiveAudioTrackCount(
TrackGroup group,
int[] formatSupport,
AudioConfigurationTuple configuration,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
int count = 0;
for (int i = 0; i < group.length; i++) {
if (isSupportedAdaptiveAudioTrack(group.getFormat(i), formatSupport[i], configuration)) {
if (isSupportedAdaptiveAudioTrack(
group.getFormat(i),
formatSupport[i],
configuration,
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness)) {
count++;
}
}
return count;
}
private static boolean isSupportedAdaptiveAudioTrack(Format format, int formatSupport,
AudioConfigurationTuple configuration) {
return isSupported(formatSupport, false) && format.channelCount == configuration.channelCount
&& format.sampleRate == configuration.sampleRate
&& (configuration.mimeType == null
|| TextUtils.equals(configuration.mimeType, format.sampleMimeType));
private static boolean isSupportedAdaptiveAudioTrack(
Format format,
int formatSupport,
AudioConfigurationTuple configuration,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
return isSupported(formatSupport, false)
&& (format.channelCount != Format.NO_VALUE
&& format.channelCount == configuration.channelCount)
&& (allowMixedMimeTypeAdaptiveness
|| (format.sampleMimeType != null
&& TextUtils.equals(format.sampleMimeType, configuration.mimeType)))
&& (allowMixedSampleRateAdaptiveness
|| (format.sampleRate != Format.NO_VALUE
&& format.sampleRate == configuration.sampleRate));
}
// Text track selection implementation.
@ -2202,6 +2402,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/** Represents how well an audio track matches the selection {@link Parameters}. */
protected static final class AudioTrackScore implements Comparable<AudioTrackScore> {
public final boolean isWithinConstraints;
private final Parameters parameters;
private final int withinRendererCapabilitiesScore;
private final int matchLanguageScore;
@ -2218,6 +2420,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
channelCount = format.channelCount;
sampleRate = format.sampleRate;
bitrate = format.bitrate;
isWithinConstraints =
(format.bitrate == Format.NO_VALUE || format.bitrate <= parameters.maxAudioBitrate)
&& (format.channelCount == Format.NO_VALUE
|| format.channelCount <= parameters.maxAudioChannelCount);
}
/**
@ -2236,6 +2442,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
if (this.matchLanguageScore != other.matchLanguageScore) {
return compareInts(this.matchLanguageScore, other.matchLanguageScore);
}
if (this.isWithinConstraints != other.isWithinConstraints) {
return this.isWithinConstraints ? 1 : -1;
}
if (parameters.forceLowestBitrate) {
int bitrateComparison = compareFormatValues(bitrate, other.bitrate);
if (bitrateComparison != 0) {
@ -2245,9 +2454,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
if (this.defaultSelectionFlagScore != other.defaultSelectionFlagScore) {
return compareInts(this.defaultSelectionFlagScore, other.defaultSelectionFlagScore);
}
// If the formats are within renderer capabilities then prefer higher values of channel count,
// sample rate and bit rate in that order. Otherwise, prefer lower values.
int resultSign = withinRendererCapabilitiesScore == 1 ? 1 : -1;
// If the formats are within constraints and renderer capabilities then prefer higher values
// of channel count, sample rate and bit rate in that order. Otherwise, prefer lower values.
int resultSign = isWithinConstraints && withinRendererCapabilitiesScore == 1 ? 1 : -1;
if (this.channelCount != other.channelCount) {
return resultSign * compareInts(this.channelCount, other.channelCount);
}

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.trackselection;
import static com.google.android.exoplayer2.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES;
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED;
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE;
@ -132,21 +133,26 @@ public final class DefaultTrackSelectorTest {
/* maxVideoFrameRate= */ 2,
/* maxVideoBitrate= */ 3,
/* exceedVideoConstraintsIfNecessary= */ false,
/* allowVideoMixedMimeTypeAdaptiveness= */ true,
/* allowVideoNonSeamlessAdaptiveness= */ false,
/* viewportWidth= */ 4,
/* viewportHeight= */ 5,
/* viewportOrientationMayChange= */ true,
// Audio
/* preferredAudioLanguage= */ "en",
/* maxAudioChannelCount= */ 6,
/* maxAudioBitrate= */ 7,
/* exceedAudioConstraintsIfNecessary= */ false,
/* allowAudioMixedMimeTypeAdaptiveness= */ true,
/* allowAudioMixedSampleRateAdaptiveness= */ false,
// Text
/* preferredTextLanguage= */ "de",
/* selectUndeterminedTextLanguage= */ false,
/* disabledTextTrackSelectionFlags= */ 6,
/* selectUndeterminedTextLanguage= */ true,
/* disabledTextTrackSelectionFlags= */ 8,
// General
/* forceLowestBitrate= */ true,
/* forceHighestSupportedBitrate= */ false,
/* allowMixedMimeAdaptiveness= */ true,
/* allowNonSeamlessAdaptiveness= */ false,
/* exceedRendererCapabilitiesIfNecessary= */ true,
/* forceLowestBitrate= */ false,
/* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET,
// Overrides
selectionOverrides,
@ -1090,6 +1096,137 @@ public final class DefaultTrackSelectorTest {
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
}
@Test
public void testSelectTracksWithMultipleAudioTracksWithMixedSampleRates() throws Exception {
Format highSampleRateAudioFormat =
buildAudioFormatWithSampleRate("44100", /* sampleRate= */ 44100);
Format lowSampleRateAudioFormat =
buildAudioFormatWithSampleRate("22050", /* sampleRate= */ 22050);
// Should not adapt between mixed sample rates by default, so we expect a fixed selection
// containing the higher sample rate stream.
TrackGroupArray trackGroups =
singleTrackGroup(highSampleRateAudioFormat, lowSampleRateAudioFormat);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, highSampleRateAudioFormat);
// The same applies if the tracks are provided in the opposite order.
trackGroups = singleTrackGroup(lowSampleRateAudioFormat, highSampleRateAudioFormat);
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, highSampleRateAudioFormat);
// If we explicitly enable mixed sample rate adaptiveness, expect an adaptive selection.
trackSelector.setParameters(
Parameters.DEFAULT.buildUpon().setAllowAudioMixedSampleRateAdaptiveness(true));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
}
@Test
public void testSelectTracksWithMultipleAudioTracksWithMixedMimeTypes() throws Exception {
Format aacAudioFormat = buildAudioFormatWithMimeType("aac", MimeTypes.AUDIO_AAC);
Format opusAudioFormat = buildAudioFormatWithMimeType("opus", MimeTypes.AUDIO_OPUS);
// Should not adapt between mixed mime types by default, so we expect a fixed selection
// containing the first stream.
TrackGroupArray trackGroups = singleTrackGroup(aacAudioFormat, opusAudioFormat);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, aacAudioFormat);
// The same applies if the tracks are provided in the opposite order.
trackGroups = singleTrackGroup(opusAudioFormat, aacAudioFormat);
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, opusAudioFormat);
// If we explicitly enable mixed mime type adaptiveness, expect an adaptive selection.
trackSelector.setParameters(
Parameters.DEFAULT.buildUpon().setAllowAudioMixedMimeTypeAdaptiveness(true));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
}
@Test
public void testSelectTracksWithMultipleAudioTracksWithMixedChannelCounts() throws Exception {
Format stereoAudioFormat =
buildAudioFormatWithChannelCount("2-channels", /* channelCount= */ 2);
Format surroundAudioFormat =
buildAudioFormatWithChannelCount("5-channels", /* channelCount= */ 5);
// Should not adapt between different channel counts, so we expect a fixed selection containing
// the track with more channels.
TrackGroupArray trackGroups = singleTrackGroup(stereoAudioFormat, surroundAudioFormat);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, surroundAudioFormat);
// The same applies if the tracks are provided in the opposite order.
trackGroups = singleTrackGroup(surroundAudioFormat, stereoAudioFormat);
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, surroundAudioFormat);
// If we constrain the channel count to 4 we expect a fixed selection containing the track with
// fewer channels.
trackSelector.setParameters(Parameters.DEFAULT.buildUpon().setMaxAudioChannelCount(4));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, stereoAudioFormat);
// If we constrain the channel count to 2 we expect a fixed selection containing the track with
// fewer channels.
trackSelector.setParameters(Parameters.DEFAULT.buildUpon().setMaxAudioChannelCount(2));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, stereoAudioFormat);
// If we constrain the channel count to 1 we expect a fixed selection containing the track with
// fewer channels.
trackSelector.setParameters(Parameters.DEFAULT.buildUpon().setMaxAudioChannelCount(1));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, stereoAudioFormat);
// If we disable exceeding of constraints we expect no selection.
trackSelector.setParameters(
Parameters.DEFAULT
.buildUpon()
.setMaxAudioChannelCount(1)
.setExceedAudioConstraintsIfNecessary(false));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertNoSelection(result.selections.get(0));
}
@Test
public void testSelectTracksWithMultipleAudioTracksOverrideReturnsAdaptiveTrackSelection()
throws Exception {
@ -1164,6 +1301,70 @@ public final class DefaultTrackSelectorTest {
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
}
@Test
public void testSelectTracksWithMultipleVideoTracksWithNonSeamlessAdaptiveness()
throws Exception {
FakeRendererCapabilities nonSeamlessVideoCapabilities =
new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO, FORMAT_HANDLED | ADAPTIVE_NOT_SEAMLESS);
// Should do non-seamless adaptiveness by default, so expect an adaptive selection.
TrackGroupArray trackGroups = singleTrackGroup(buildVideoFormat("0"), buildVideoFormat("1"));
trackSelector.setParameters(
Parameters.DEFAULT.buildUpon().setAllowVideoNonSeamlessAdaptiveness(true));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {nonSeamlessVideoCapabilities},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(1);
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
// If we explicitly disable non-seamless adaptiveness, expect a fixed selection.
trackSelector.setParameters(
Parameters.DEFAULT.buildUpon().setAllowVideoNonSeamlessAdaptiveness(false));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {nonSeamlessVideoCapabilities},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups.get(0), 0);
}
@Test
public void testSelectTracksWithMultipleVideoTracksWithMixedMimeTypes() throws Exception {
Format h264VideoFormat = buildVideoFormatWithMimeType("h264", MimeTypes.VIDEO_H264);
Format h265VideoFormat = buildVideoFormatWithMimeType("h265", MimeTypes.VIDEO_H265);
// Should not adapt between mixed mime types by default, so we expect a fixed selection
// containing the first stream.
TrackGroupArray trackGroups = singleTrackGroup(h264VideoFormat, h265VideoFormat);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, h264VideoFormat);
// The same applies if the tracks are provided in the opposite order.
trackGroups = singleTrackGroup(h265VideoFormat, h264VideoFormat);
result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections.get(0), trackGroups, h265VideoFormat);
// If we explicitly enable mixed mime type adaptiveness, expect an adaptive selection.
trackSelector.setParameters(
Parameters.DEFAULT.buildUpon().setAllowVideoMixedMimeTypeAdaptiveness(true));
result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(1);
assertAdaptiveSelection(result.selections.get(0), trackGroups.get(0), 0, 1);
}
@Test
public void testSelectTracksWithMultipleVideoTracksOverrideReturnsAdaptiveTrackSelection()
throws Exception {
@ -1277,6 +1478,36 @@ public final class DefaultTrackSelectorTest {
/* sampleRate= */ 44100);
}
private static Format buildAudioFormatWithSampleRate(String id, int sampleRate) {
return buildAudioFormat(
id,
MimeTypes.AUDIO_AAC,
/* language= */ null,
/* selectionFlags= */ 0,
/* channelCount= */ 2,
sampleRate);
}
private static Format buildAudioFormatWithChannelCount(String id, int channelCount) {
return buildAudioFormat(
id,
MimeTypes.AUDIO_AAC,
/* language= */ null,
/* selectionFlags= */ 0,
channelCount,
/* sampleRate= */ 44100);
}
private static Format buildAudioFormatWithMimeType(String id, String mimeType) {
return buildAudioFormat(
id,
mimeType,
/* language= */ null,
/* selectionFlags= */ 0,
/* channelCount= */ 2,
/* sampleRate= */ 44100);
}
private static Format buildAudioFormat(String id) {
return buildAudioFormat(
id,