diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 9875333dad..686a6d10ba 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -29,6 +29,8 @@
([#3149](https://github.com/google/ExoPlayer/issues/3149)).
* DefaultTrackSelector: Replace `DefaultTrackSelector.Parameters` copy methods
with a builder.
+* DefaultTrackSelector: Support disabling of individual text track selection
+ flags.
* New Cast extension: Simplifies toggling between local and Cast playbacks.
### 2.6.1 ###
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
index 2f0dc8f04e..09bd81416c 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
@@ -82,6 +82,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private String preferredAudioLanguage;
private String preferredTextLanguage;
private boolean selectUndeterminedTextLanguage;
+ private int disabledTextTrackSelectionFlags;
private boolean forceLowestBitrate;
private boolean allowMixedMimeAdaptiveness;
private boolean allowNonSeamlessAdaptiveness;
@@ -109,6 +110,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
preferredAudioLanguage = initialValues.preferredAudioLanguage;
preferredTextLanguage = initialValues.preferredTextLanguage;
selectUndeterminedTextLanguage = initialValues.selectUndeterminedTextLanguage;
+ disabledTextTrackSelectionFlags = initialValues.disabledTextTrackSelectionFlags;
forceLowestBitrate = initialValues.forceLowestBitrate;
allowMixedMimeAdaptiveness = initialValues.allowMixedMimeAdaptiveness;
allowNonSeamlessAdaptiveness = initialValues.allowNonSeamlessAdaptiveness;
@@ -153,6 +155,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this;
}
+ /**
+ * See {@link Parameters#disabledTextTrackSelectionFlags}.
+ *
+ * @return This builder.
+ */
+ public ParametersBuilder setDisabledTextTrackSelectionFlags(
+ int disabledTextTrackSelectionFlags) {
+ this.disabledTextTrackSelectionFlags = disabledTextTrackSelectionFlags;
+ return this;
+ }
+
/**
* See {@link Parameters#forceLowestBitrate}.
*
@@ -287,11 +300,22 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* Builds a {@link Parameters} instance with the selected values.
*/
public Parameters build() {
- return new Parameters(preferredAudioLanguage, preferredTextLanguage,
- selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
- allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
- exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
- viewportHeight, viewportOrientationMayChange);
+ return new Parameters(
+ preferredAudioLanguage,
+ preferredTextLanguage,
+ selectUndeterminedTextLanguage,
+ disabledTextTrackSelectionFlags,
+ forceLowestBitrate,
+ allowMixedMimeAdaptiveness,
+ allowNonSeamlessAdaptiveness,
+ maxVideoWidth,
+ maxVideoHeight,
+ maxVideoBitrate,
+ exceedVideoConstraintsIfNecessary,
+ exceedRendererCapabilitiesIfNecessary,
+ viewportWidth,
+ viewportHeight,
+ viewportOrientationMayChange);
}
}
@@ -303,19 +327,21 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/**
* An instance with default values:
+ *
*
- * - No preferred audio language.
- * - No preferred text language.
- * - Text tracks with undetermined language are not selected if no track with
- * {@link #preferredTextLanguage} is available.
- * - Lowest bitrate track selections are not forced.
- * - Adaptation between different mime types is not allowed.
- * - Non seamless adaptation is allowed.
- * - No max limit for video width/height.
- * - No max video bitrate.
- * - Video constraints are exceeded if no supported selection can be made otherwise.
- * - Renderer capabilities are exceeded if no supported selection can be made.
- * - No viewport constraints.
+ * - No preferred audio language.
+ *
- No preferred text language.
+ *
- Text tracks with undetermined language are not selected if no track with {@link
+ * #preferredTextLanguage} is available.
+ *
- All selection flags are considered for text track selections.
+ *
- Lowest bitrate track selections are not forced.
+ *
- Adaptation between different mime types is not allowed.
+ *
- Non seamless adaptation is allowed.
+ *
- No max limit for video width/height.
+ *
- No max video bitrate.
+ *
- Video constraints are exceeded if no supported selection can be made otherwise.
+ *
- Renderer capabilities are exceeded if no supported selection can be made.
+ *
- No viewport constraints.
*
*/
public static final Parameters DEFAULT = new Parameters();
@@ -338,6 +364,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* {@link #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset.
*/
public final boolean selectUndeterminedTextLanguage;
+ /**
+ * Bitmask of selection flags that are disabled for text track selections. See {@link
+ * C.SelectionFlags}.
+ */
+ public final int disabledTextTrackSelectionFlags;
// Video
/**
@@ -392,19 +423,44 @@ public class DefaultTrackSelector extends MappingTrackSelector {
public final boolean exceedRendererCapabilitiesIfNecessary;
private Parameters() {
- this(null, null, false, false, false, true, Integer.MAX_VALUE, Integer.MAX_VALUE,
- Integer.MAX_VALUE, true, true, Integer.MAX_VALUE, Integer.MAX_VALUE, true);
+ this(
+ null,
+ null,
+ false,
+ 0,
+ false,
+ false,
+ true,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ true,
+ true,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ true);
}
- private Parameters(String preferredAudioLanguage, String preferredTextLanguage,
- boolean selectUndeterminedTextLanguage, boolean forceLowestBitrate,
- boolean allowMixedMimeAdaptiveness, boolean allowNonSeamlessAdaptiveness, int maxVideoWidth,
- int maxVideoHeight, int maxVideoBitrate, boolean exceedVideoConstraintsIfNecessary,
- boolean exceedRendererCapabilitiesIfNecessary, int viewportWidth, int viewportHeight,
+ private Parameters(
+ String preferredAudioLanguage,
+ String preferredTextLanguage,
+ boolean selectUndeterminedTextLanguage,
+ int disabledTextTrackSelectionFlags,
+ boolean forceLowestBitrate,
+ boolean allowMixedMimeAdaptiveness,
+ boolean allowNonSeamlessAdaptiveness,
+ int maxVideoWidth,
+ int maxVideoHeight,
+ int maxVideoBitrate,
+ boolean exceedVideoConstraintsIfNecessary,
+ boolean exceedRendererCapabilitiesIfNecessary,
+ int viewportWidth,
+ int viewportHeight,
boolean viewportOrientationMayChange) {
this.preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage);
this.preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage);
this.selectUndeterminedTextLanguage = selectUndeterminedTextLanguage;
+ this.disabledTextTrackSelectionFlags = disabledTextTrackSelectionFlags;
this.forceLowestBitrate = forceLowestBitrate;
this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness;
this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness;
@@ -434,14 +490,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return false;
}
Parameters other = (Parameters) obj;
- return forceLowestBitrate == other.forceLowestBitrate
+ return selectUndeterminedTextLanguage == other.selectUndeterminedTextLanguage
+ && disabledTextTrackSelectionFlags == other.disabledTextTrackSelectionFlags
+ && forceLowestBitrate == other.forceLowestBitrate
&& allowMixedMimeAdaptiveness == other.allowMixedMimeAdaptiveness
&& allowNonSeamlessAdaptiveness == other.allowNonSeamlessAdaptiveness
- && maxVideoWidth == other.maxVideoWidth && maxVideoHeight == other.maxVideoHeight
+ && maxVideoWidth == other.maxVideoWidth
+ && maxVideoHeight == other.maxVideoHeight
&& exceedVideoConstraintsIfNecessary == other.exceedVideoConstraintsIfNecessary
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
&& viewportOrientationMayChange == other.viewportOrientationMayChange
- && viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight
+ && viewportWidth == other.viewportWidth
+ && viewportHeight == other.viewportHeight
&& maxVideoBitrate == other.maxVideoBitrate
&& TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage)
&& TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage);
@@ -449,19 +509,21 @@ public class DefaultTrackSelector extends MappingTrackSelector {
@Override
public int hashCode() {
- int result = preferredAudioLanguage.hashCode();
- result = 31 * result + preferredTextLanguage.hashCode();
+ int result = selectUndeterminedTextLanguage ? 1 : 0;
+ result = 31 * result + disabledTextTrackSelectionFlags;
result = 31 * result + (forceLowestBitrate ? 1 : 0);
result = 31 * result + (allowMixedMimeAdaptiveness ? 1 : 0);
result = 31 * result + (allowNonSeamlessAdaptiveness ? 1 : 0);
result = 31 * result + maxVideoWidth;
result = 31 * result + maxVideoHeight;
- result = 31 * result + maxVideoBitrate;
result = 31 * result + (exceedVideoConstraintsIfNecessary ? 1 : 0);
result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
result = 31 * result + (viewportOrientationMayChange ? 1 : 0);
result = 31 * result + viewportWidth;
result = 31 * result + viewportHeight;
+ result = 31 * result + maxVideoBitrate;
+ result = 31 * result + preferredAudioLanguage.hashCode();
+ result = 31 * result + preferredTextLanguage.hashCode();
return result;
}
@@ -923,8 +985,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
if (isSupported(trackFormatSupport[trackIndex],
params.exceedRendererCapabilitiesIfNecessary)) {
Format format = trackGroup.getFormat(trackIndex);
- boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
- boolean isForced = (format.selectionFlags & C.SELECTION_FLAG_FORCED) != 0;
+ int maskedSelectionFlags =
+ format.selectionFlags & ~params.disabledTextTrackSelectionFlags;
+ boolean isDefault = (maskedSelectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
+ boolean isForced = (maskedSelectionFlags & C.SELECTION_FLAG_FORCED) != 0;
int trackScore;
boolean preferredLanguageFound = formatHasLanguage(format, params.preferredTextLanguage);
if (preferredLanguageFound
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
index 1eff48b730..24362d1570 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
@@ -529,6 +529,134 @@ public final class DefaultTrackSelectorTest {
.isEqualTo(lowerSampleRateHigherBitrateFormat);
}
+ /** Tests text track selection flags. */
+ @Test
+ public void testsTextTrackSelectionFlags() throws ExoPlaybackException {
+ Format forcedOnly =
+ Format.createTextContainerFormat(
+ "forcedOnly",
+ null,
+ MimeTypes.TEXT_VTT,
+ null,
+ Format.NO_VALUE,
+ C.SELECTION_FLAG_FORCED,
+ "eng");
+ Format forcedDefault =
+ Format.createTextContainerFormat(
+ "forcedDefault",
+ null,
+ MimeTypes.TEXT_VTT,
+ null,
+ Format.NO_VALUE,
+ C.SELECTION_FLAG_FORCED | C.SELECTION_FLAG_DEFAULT,
+ "eng");
+ Format defaultOnly =
+ Format.createTextContainerFormat(
+ "defaultOnly",
+ null,
+ MimeTypes.TEXT_VTT,
+ null,
+ Format.NO_VALUE,
+ C.SELECTION_FLAG_DEFAULT,
+ "eng");
+ Format forcedOnlySpanish =
+ Format.createTextContainerFormat(
+ "forcedOnlySpanish",
+ null,
+ MimeTypes.TEXT_VTT,
+ null,
+ Format.NO_VALUE,
+ C.SELECTION_FLAG_FORCED,
+ "spa");
+ Format noFlag =
+ Format.createTextContainerFormat(
+ "noFlag", null, MimeTypes.TEXT_VTT, null, Format.NO_VALUE, 0, "eng");
+
+ RendererCapabilities[] textRendererCapabilities =
+ new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES};
+
+ TrackSelectorResult result;
+
+ // There is no text language preference, the first track flagged as default should be selected.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(forcedOnly, forcedDefault, defaultOnly, noFlag));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedDefault);
+
+ // Ditto.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(forcedOnly, noFlag, defaultOnly));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(defaultOnly);
+
+ // With no language preference and no text track flagged as default, the first forced should be
+ // selected.
+ result = trackSelector.selectTracks(textRendererCapabilities, wrapFormats(forcedOnly, noFlag));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedOnly);
+
+ trackSelector.setParameters(
+ Parameters.DEFAULT
+ .buildUpon()
+ .setDisabledTextTrackSelectionFlags(C.SELECTION_FLAG_DEFAULT)
+ .build());
+
+ // Default flags are disabled, so the first track flagged as forced should be selected.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(defaultOnly, noFlag, forcedOnly, forcedDefault));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedOnly);
+
+ trackSelector.setParameters(
+ trackSelector.getParameters().buildUpon().setPreferredAudioLanguage("spa").build());
+
+ // Default flags are disabled, but there is a text track flagged as forced whose language
+ // matches the preferred audio language.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities,
+ wrapFormats(forcedDefault, forcedOnly, defaultOnly, noFlag, forcedOnlySpanish));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedOnlySpanish);
+
+ trackSelector.setParameters(
+ trackSelector
+ .getParameters()
+ .buildUpon()
+ .setDisabledTextTrackSelectionFlags(C.SELECTION_FLAG_DEFAULT | C.SELECTION_FLAG_FORCED)
+ .build());
+
+ // All selection flags are disabled and there is no language preference, so nothing should be
+ // selected.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(forcedOnly, forcedDefault, defaultOnly, noFlag));
+ assertThat(result.selections.get(0)).isNull();
+
+ trackSelector.setParameters(
+ Parameters.DEFAULT.buildUpon().setPreferredTextLanguage("eng").build());
+
+ // There is a preferred language, so the first language-matching track flagged as default should
+ // be selected.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(forcedOnly, forcedDefault, defaultOnly, noFlag));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedDefault);
+
+ trackSelector.setParameters(
+ trackSelector
+ .getParameters()
+ .buildUpon()
+ .setDisabledTextTrackSelectionFlags(C.SELECTION_FLAG_DEFAULT)
+ .build());
+
+ // Same as above, but the default flag is disabled. If multiple tracks match the preferred
+ // language, those not flagged as forced are preferred, as they likely include the contents of
+ // forced subtitles.
+ result =
+ trackSelector.selectTracks(
+ textRendererCapabilities, wrapFormats(noFlag, forcedOnly, forcedDefault, defaultOnly));
+ assertThat(result.selections.get(0).getFormat(0)).isSameAs(noFlag);
+ }
+
/**
* Tests that the default track selector will select a text track with undetermined language if no
* text track with the preferred language is available but