Skip to content

Commit

Permalink
Support undefined text track language when preferred is not available
Browse files Browse the repository at this point in the history
Also slightly improve language normalization/documentation.

For this CL, it is assumed that null and "und" languages are different
entities. Once we fully tackle language tag normalization, we can decide
whether to normalize the "undefined" language.

Issue:#2867
Issue:#2980

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=177008509
  • Loading branch information
AquilesCanta authored and ojw28 committed Dec 12, 2017
1 parent ce557b1 commit c1c892f
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 55 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
use this with `FfmpegAudioRenderer`.
* Support extraction and decoding of Dolby Atmos
([#2465](https://github.com/google/ExoPlayer/issues/2465)).
* DefaultTrackSelector: Support undefined language text track selection when the
preferred language is not available
([#2980](https://github.com/google/ExoPlayer/issues/2980)).

### 2.6.0 ###

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,11 @@ private C() {}
*/
public static final int SELECTION_FLAG_AUTOSELECT = 4;

/**
* Represents an undetermined language as an ISO 639 alpha-3 language code.
*/
public static final String LANGUAGE_UNDETERMINED = "und";

/**
* Represents a streaming or other media type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
* Parameters currentParameters = trackSelector.getParameters();
* // Generate new parameters to prefer German audio and impose a maximum video size constraint.
* Parameters newParameters = currentParameters
* .withPreferredAudioLanguage("de")
* .withPreferredAudioLanguage("deu")
* .withMaxVideoSize(1024, 768);
* // Set the new parameters on the selector.
* trackSelector.setParameters(newParameters);}
Expand Down Expand Up @@ -81,17 +81,22 @@ public static final class Parameters {

// Audio
/**
* The preferred language for audio, as well as for forced text tracks as defined by RFC 5646.
* The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag.
* {@code null} selects the default track, or the first track if there's no default.
*/
public final String preferredAudioLanguage;

// Text
/**
* The preferred language for text tracks as defined by RFC 5646. {@code null} selects the
* The preferred language for text tracks as an ISO 639-2/T tag. {@code null} selects the
* default track if there is one, or no track otherwise.
*/
public final String preferredTextLanguage;
/**
* Whether a text track with undetermined language should be selected if no track with
* {@link #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset.
*/
public final boolean selectUndeterminedTextLanguage;

// Video
/**
Expand Down Expand Up @@ -150,6 +155,8 @@ public static final class Parameters {
* <ul>
* <li>No preferred audio language is set.</li>
* <li>No preferred text language is set.</li>
* <li>Text tracks with undetermined language are not selected if no track with
* {@link #preferredTextLanguage} is available.</li>
* <li>Lowest bitrate track selections are not forced.</li>
* <li>Adaptation between different mime types is not allowed.</li>
* <li>Non seamless adaptation is allowed.</li>
Expand All @@ -161,13 +168,14 @@ public static final class Parameters {
* </ul>
*/
public Parameters() {
this(null, null, 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, false, false, true, Integer.MAX_VALUE, Integer.MAX_VALUE,
Integer.MAX_VALUE, true, true, Integer.MAX_VALUE, Integer.MAX_VALUE, true);
}

/**
* @param preferredAudioLanguage See {@link #preferredAudioLanguage}
* @param preferredTextLanguage See {@link #preferredTextLanguage}
* @param selectUndeterminedTextLanguage See {@link #selectUndeterminedTextLanguage}.
* @param forceLowestBitrate See {@link #forceLowestBitrate}.
* @param allowMixedMimeAdaptiveness See {@link #allowMixedMimeAdaptiveness}
* @param allowNonSeamlessAdaptiveness See {@link #allowNonSeamlessAdaptiveness}
Expand All @@ -181,13 +189,14 @@ public Parameters() {
* @param viewportOrientationMayChange See {@link #viewportOrientationMayChange}
*/
public Parameters(String preferredAudioLanguage, String preferredTextLanguage,
boolean forceLowestBitrate, boolean allowMixedMimeAdaptiveness,
boolean allowNonSeamlessAdaptiveness, int maxVideoWidth, int maxVideoHeight,
int maxVideoBitrate, boolean exceedVideoConstraintsIfNecessary,
boolean selectUndeterminedTextLanguage, boolean forceLowestBitrate,
boolean allowMixedMimeAdaptiveness, boolean allowNonSeamlessAdaptiveness, int maxVideoWidth,
int maxVideoHeight, int maxVideoBitrate, boolean exceedVideoConstraintsIfNecessary,
boolean exceedRendererCapabilitiesIfNecessary, int viewportWidth, int viewportHeight,
boolean viewportOrientationMayChange) {
this.preferredAudioLanguage = preferredAudioLanguage;
this.preferredTextLanguage = preferredTextLanguage;
this.selectUndeterminedTextLanguage = selectUndeterminedTextLanguage;
this.forceLowestBitrate = forceLowestBitrate;
this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness;
this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness;
Expand All @@ -209,10 +218,11 @@ public Parameters withPreferredAudioLanguage(String preferredAudioLanguage) {
if (TextUtils.equals(preferredAudioLanguage, this.preferredAudioLanguage)) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -223,10 +233,26 @@ public Parameters withPreferredTextLanguage(String preferredTextLanguage) {
if (TextUtils.equals(preferredTextLanguage, this.preferredTextLanguage)) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
* Returns an instance with the provided {@link #selectUndeterminedTextLanguage}.
*/
public Parameters withSelectUndeterminedTextLanguageAsFallback(
boolean selectUndeterminedTextLanguage) {
if (selectUndeterminedTextLanguage == this.selectUndeterminedTextLanguage) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -236,10 +262,11 @@ public Parameters withForceLowestBitrate(boolean forceLowestBitrate) {
if (forceLowestBitrate == this.forceLowestBitrate) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -249,10 +276,11 @@ public Parameters withAllowMixedMimeAdaptiveness(boolean allowMixedMimeAdaptiven
if (allowMixedMimeAdaptiveness == this.allowMixedMimeAdaptiveness) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -262,10 +290,11 @@ public Parameters withAllowNonSeamlessAdaptiveness(boolean allowNonSeamlessAdapt
if (allowNonSeamlessAdaptiveness == this.allowNonSeamlessAdaptiveness) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -275,10 +304,11 @@ public Parameters withMaxVideoSize(int maxVideoWidth, int maxVideoHeight) {
if (maxVideoWidth == this.maxVideoWidth && maxVideoHeight == this.maxVideoHeight) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -288,10 +318,11 @@ public Parameters withMaxVideoBitrate(int maxVideoBitrate) {
if (maxVideoBitrate == this.maxVideoBitrate) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand Down Expand Up @@ -320,10 +351,11 @@ public Parameters withExceedVideoConstraintsIfNecessary(
if (exceedVideoConstraintsIfNecessary == this.exceedVideoConstraintsIfNecessary) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -334,10 +366,11 @@ public Parameters withExceedRendererCapabilitiesIfNecessary(
if (exceedRendererCapabilitiesIfNecessary == this.exceedRendererCapabilitiesIfNecessary) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand All @@ -350,10 +383,11 @@ public Parameters withViewportSize(int viewportWidth, int viewportHeight,
&& viewportOrientationMayChange == this.viewportOrientationMayChange) {
return this;
}
return new Parameters(preferredAudioLanguage, preferredTextLanguage, forceLowestBitrate,
allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportHeight, viewportOrientationMayChange);
return new Parameters(preferredAudioLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, forceLowestBitrate, allowMixedMimeAdaptiveness,
allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate,
exceedVideoConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary, viewportWidth,
viewportHeight, viewportOrientationMayChange);
}

/**
Expand Down Expand Up @@ -880,17 +914,20 @@ protected TrackSelection selectTextTrack(TrackGroupArray groups, int[][] formatS
boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
boolean isForced = (format.selectionFlags & C.SELECTION_FLAG_FORCED) != 0;
int trackScore;
if (formatHasLanguage(format, params.preferredTextLanguage)) {
boolean preferredLanguageFound = formatHasLanguage(format, params.preferredTextLanguage);
if (preferredLanguageFound
|| (params.selectUndeterminedTextLanguage && formatHasNoLanguage(format))) {
if (isDefault) {
trackScore = 6;
trackScore = 8;
} else if (!isForced) {
// Prefer non-forced to forced if a preferred text language has been specified. Where
// both are provided the non-forced track will usually contain the forced subtitles as
// a subset.
trackScore = 5;
trackScore = 6;
} else {
trackScore = 4;
}
trackScore += preferredLanguageFound ? 1 : 0;
} else if (isDefault) {
trackScore = 3;
} else if (isForced) {
Expand Down Expand Up @@ -980,6 +1017,16 @@ protected static boolean isSupported(int formatSupport, boolean allowExceedsCapa
&& maskedSupport == RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES);
}

/**
* Returns whether a {@link Format} does not define a language.
*
* @param format The {@link Format}.
* @return Whether the {@link Format} does not define a language.
*/
protected static boolean formatHasNoLanguage(Format format) {
return TextUtils.isEmpty(format.language) || formatHasLanguage(format, C.LANGUAGE_UNDETERMINED);
}

/**
* Returns whether a {@link Format} specifies a particular language, or {@code false} if
* {@code language} is null.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -238,13 +239,18 @@ public static void closeQuietly(Closeable closeable) {
}

/**
* Returns a normalized RFC 5646 language code.
* Returns a normalized RFC 639-2/T code for {@code language}.
*
* @param language A possibly non-normalized RFC 5646 language code.
* @return The normalized code, or null if the input was null.
* @param language A case-insensitive ISO 639 alpha-2 or alpha-3 language code.
* @return The all-lowercase normalized code, or null if the input was null, or
* {@code language.toLowerCase()} if the language could not be normalized.
*/
public static String normalizeLanguageCode(String language) {
return language == null ? null : new Locale(language).getLanguage();
try {
return language == null ? null : new Locale(language).getISO3Language();
} catch (MissingResourceException e) {
return language.toLowerCase();
}
}

/**
Expand Down
Loading

0 comments on commit c1c892f

Please sign in to comment.