mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add video language to DefaultTrackSelector
PiperOrigin-RevId: 688155680
This commit is contained in:
parent
e926b0df1e
commit
b04b37074b
5 changed files with 218 additions and 21 deletions
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
* Common Library:
|
* Common Library:
|
||||||
* ExoPlayer:
|
* ExoPlayer:
|
||||||
|
* Consider language when selecting a video track. By default select a
|
||||||
|
'main' video track that matches the language of the selected audio
|
||||||
|
track, if available. Explicit video language preferences can be
|
||||||
|
expressed with
|
||||||
|
`TrackSelectionParameters.Builder.setPreferredVideoLanguage(s)`.
|
||||||
|
* Add `selectedAudioLanguage` parameter to
|
||||||
|
`DefaultTrackSelector.selectVideoTrack()` method.
|
||||||
* Transformer:
|
* Transformer:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
* Fix media duration parsing in `mdhd` box of MP4 files to handle `-1`
|
* Fix media duration parsing in `mdhd` box of MP4 files to handle `-1`
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ public class TrackSelectionParameters {
|
||||||
private int viewportHeight;
|
private int viewportHeight;
|
||||||
private boolean viewportOrientationMayChange;
|
private boolean viewportOrientationMayChange;
|
||||||
private ImmutableList<String> preferredVideoMimeTypes;
|
private ImmutableList<String> preferredVideoMimeTypes;
|
||||||
|
private ImmutableList<String> preferredVideoLanguages;
|
||||||
private @C.RoleFlags int preferredVideoRoleFlags;
|
private @C.RoleFlags int preferredVideoRoleFlags;
|
||||||
// Audio
|
// Audio
|
||||||
private ImmutableList<String> preferredAudioLanguages;
|
private ImmutableList<String> preferredAudioLanguages;
|
||||||
|
|
@ -127,6 +128,7 @@ public class TrackSelectionParameters {
|
||||||
viewportHeight = Integer.MAX_VALUE;
|
viewportHeight = Integer.MAX_VALUE;
|
||||||
viewportOrientationMayChange = true;
|
viewportOrientationMayChange = true;
|
||||||
preferredVideoMimeTypes = ImmutableList.of();
|
preferredVideoMimeTypes = ImmutableList.of();
|
||||||
|
preferredVideoLanguages = ImmutableList.of();
|
||||||
preferredVideoRoleFlags = 0;
|
preferredVideoRoleFlags = 0;
|
||||||
// Audio
|
// Audio
|
||||||
preferredAudioLanguages = ImmutableList.of();
|
preferredAudioLanguages = ImmutableList.of();
|
||||||
|
|
@ -194,6 +196,9 @@ public class TrackSelectionParameters {
|
||||||
preferredVideoMimeTypes =
|
preferredVideoMimeTypes =
|
||||||
ImmutableList.copyOf(
|
ImmutableList.copyOf(
|
||||||
firstNonNull(bundle.getStringArray(FIELD_PREFERRED_VIDEO_MIMETYPES), new String[0]));
|
firstNonNull(bundle.getStringArray(FIELD_PREFERRED_VIDEO_MIMETYPES), new String[0]));
|
||||||
|
preferredVideoLanguages =
|
||||||
|
ImmutableList.copyOf(
|
||||||
|
firstNonNull(bundle.getStringArray(FIELD_PREFERRED_VIDEO_LANGUAGES), new String[0]));
|
||||||
preferredVideoRoleFlags =
|
preferredVideoRoleFlags =
|
||||||
bundle.getInt(
|
bundle.getInt(
|
||||||
FIELD_PREFERRED_VIDEO_ROLE_FLAGS, DEFAULT_WITHOUT_CONTEXT.preferredVideoRoleFlags);
|
FIELD_PREFERRED_VIDEO_ROLE_FLAGS, DEFAULT_WITHOUT_CONTEXT.preferredVideoRoleFlags);
|
||||||
|
|
@ -284,6 +289,7 @@ public class TrackSelectionParameters {
|
||||||
/** Overrides the value of the builder with the value of {@link TrackSelectionParameters}. */
|
/** Overrides the value of the builder with the value of {@link TrackSelectionParameters}. */
|
||||||
@EnsuresNonNull({
|
@EnsuresNonNull({
|
||||||
"preferredVideoMimeTypes",
|
"preferredVideoMimeTypes",
|
||||||
|
"preferredVideoLanguages",
|
||||||
"preferredAudioLanguages",
|
"preferredAudioLanguages",
|
||||||
"preferredAudioMimeTypes",
|
"preferredAudioMimeTypes",
|
||||||
"audioOffloadPreferences",
|
"audioOffloadPreferences",
|
||||||
|
|
@ -305,6 +311,7 @@ public class TrackSelectionParameters {
|
||||||
viewportHeight = parameters.viewportHeight;
|
viewportHeight = parameters.viewportHeight;
|
||||||
viewportOrientationMayChange = parameters.viewportOrientationMayChange;
|
viewportOrientationMayChange = parameters.viewportOrientationMayChange;
|
||||||
preferredVideoMimeTypes = parameters.preferredVideoMimeTypes;
|
preferredVideoMimeTypes = parameters.preferredVideoMimeTypes;
|
||||||
|
preferredVideoLanguages = parameters.preferredVideoLanguages;
|
||||||
preferredVideoRoleFlags = parameters.preferredVideoRoleFlags;
|
preferredVideoRoleFlags = parameters.preferredVideoRoleFlags;
|
||||||
// Audio
|
// Audio
|
||||||
preferredAudioLanguages = parameters.preferredAudioLanguages;
|
preferredAudioLanguages = parameters.preferredAudioLanguages;
|
||||||
|
|
@ -504,6 +511,36 @@ public class TrackSelectionParameters {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the preferred language for video tracks.
|
||||||
|
*
|
||||||
|
* @param preferredVideoLanguage Preferred video language as an IETF BCP 47 conformant tag, or
|
||||||
|
* {@code null} to express no language preference for video track selection.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setPreferredVideoLanguage(@Nullable String preferredVideoLanguage) {
|
||||||
|
return preferredVideoLanguage == null
|
||||||
|
? setPreferredVideoLanguages()
|
||||||
|
: setPreferredVideoLanguages(preferredVideoLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the preferred languages for video tracks.
|
||||||
|
*
|
||||||
|
* @param preferredVideoLanguages Preferred video languages as IETF BCP 47 conformant tags in
|
||||||
|
* order of preference, or an empty array to express no language preference for video track
|
||||||
|
* selection.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setPreferredVideoLanguages(String... preferredVideoLanguages) {
|
||||||
|
this.preferredVideoLanguages = normalizeLanguageCodes(preferredVideoLanguages);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the preferred {@link C.RoleFlags} for video tracks.
|
* Sets the preferred {@link C.RoleFlags} for video tracks.
|
||||||
*
|
*
|
||||||
|
|
@ -1148,6 +1185,11 @@ public class TrackSelectionParameters {
|
||||||
*/
|
*/
|
||||||
public final ImmutableList<String> preferredVideoMimeTypes;
|
public final ImmutableList<String> preferredVideoMimeTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred languages for video tracks as IETF BCP 47 conformant tags in order of preference.
|
||||||
|
*/
|
||||||
|
@UnstableApi public final ImmutableList<String> preferredVideoLanguages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The preferred {@link C.RoleFlags} for video tracks. {@code 0} selects the default track if
|
* The preferred {@link C.RoleFlags} for video tracks. {@code 0} selects the default track if
|
||||||
* there is one, or the first track if there's no default. The default value is {@code 0}.
|
* there is one, or the first track if there's no default. The default value is {@code 0}.
|
||||||
|
|
@ -1267,6 +1309,7 @@ public class TrackSelectionParameters {
|
||||||
this.viewportHeight = builder.viewportHeight;
|
this.viewportHeight = builder.viewportHeight;
|
||||||
this.viewportOrientationMayChange = builder.viewportOrientationMayChange;
|
this.viewportOrientationMayChange = builder.viewportOrientationMayChange;
|
||||||
this.preferredVideoMimeTypes = builder.preferredVideoMimeTypes;
|
this.preferredVideoMimeTypes = builder.preferredVideoMimeTypes;
|
||||||
|
this.preferredVideoLanguages = builder.preferredVideoLanguages;
|
||||||
this.preferredVideoRoleFlags = builder.preferredVideoRoleFlags;
|
this.preferredVideoRoleFlags = builder.preferredVideoRoleFlags;
|
||||||
// Audio
|
// Audio
|
||||||
this.preferredAudioLanguages = builder.preferredAudioLanguages;
|
this.preferredAudioLanguages = builder.preferredAudioLanguages;
|
||||||
|
|
@ -1317,6 +1360,7 @@ public class TrackSelectionParameters {
|
||||||
&& viewportWidth == other.viewportWidth
|
&& viewportWidth == other.viewportWidth
|
||||||
&& viewportHeight == other.viewportHeight
|
&& viewportHeight == other.viewportHeight
|
||||||
&& preferredVideoMimeTypes.equals(other.preferredVideoMimeTypes)
|
&& preferredVideoMimeTypes.equals(other.preferredVideoMimeTypes)
|
||||||
|
&& preferredVideoLanguages.equals(other.preferredVideoLanguages)
|
||||||
&& preferredVideoRoleFlags == other.preferredVideoRoleFlags
|
&& preferredVideoRoleFlags == other.preferredVideoRoleFlags
|
||||||
// Audio
|
// Audio
|
||||||
&& preferredAudioLanguages.equals(other.preferredAudioLanguages)
|
&& preferredAudioLanguages.equals(other.preferredAudioLanguages)
|
||||||
|
|
@ -1355,6 +1399,7 @@ public class TrackSelectionParameters {
|
||||||
result = 31 * result + viewportWidth;
|
result = 31 * result + viewportWidth;
|
||||||
result = 31 * result + viewportHeight;
|
result = 31 * result + viewportHeight;
|
||||||
result = 31 * result + preferredVideoMimeTypes.hashCode();
|
result = 31 * result + preferredVideoMimeTypes.hashCode();
|
||||||
|
result = 31 * result + preferredVideoLanguages.hashCode();
|
||||||
result = 31 * result + preferredVideoRoleFlags;
|
result = 31 * result + preferredVideoRoleFlags;
|
||||||
// Audio
|
// Audio
|
||||||
result = 31 * result + preferredAudioLanguages.hashCode();
|
result = 31 * result + preferredAudioLanguages.hashCode();
|
||||||
|
|
@ -1410,6 +1455,7 @@ public class TrackSelectionParameters {
|
||||||
private static final String FIELD_AUDIO_OFFLOAD_PREFERENCES = Util.intToStringMaxRadix(30);
|
private static final String FIELD_AUDIO_OFFLOAD_PREFERENCES = Util.intToStringMaxRadix(30);
|
||||||
private static final String FIELD_IS_PREFER_IMAGE_OVER_VIDEO_ENABLED =
|
private static final String FIELD_IS_PREFER_IMAGE_OVER_VIDEO_ENABLED =
|
||||||
Util.intToStringMaxRadix(31);
|
Util.intToStringMaxRadix(31);
|
||||||
|
private static final String FIELD_PREFERRED_VIDEO_LANGUAGES = Util.intToStringMaxRadix(32);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a minimum field ID value for subclasses to use when implementing {@link #toBundle()}
|
* Defines a minimum field ID value for subclasses to use when implementing {@link #toBundle()}
|
||||||
|
|
@ -1439,6 +1485,8 @@ public class TrackSelectionParameters {
|
||||||
bundle.putBoolean(FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE, viewportOrientationMayChange);
|
bundle.putBoolean(FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE, viewportOrientationMayChange);
|
||||||
bundle.putStringArray(
|
bundle.putStringArray(
|
||||||
FIELD_PREFERRED_VIDEO_MIMETYPES, preferredVideoMimeTypes.toArray(new String[0]));
|
FIELD_PREFERRED_VIDEO_MIMETYPES, preferredVideoMimeTypes.toArray(new String[0]));
|
||||||
|
bundle.putStringArray(
|
||||||
|
FIELD_PREFERRED_VIDEO_LANGUAGES, preferredVideoLanguages.toArray(new String[0]));
|
||||||
bundle.putInt(FIELD_PREFERRED_VIDEO_ROLE_FLAGS, preferredVideoRoleFlags);
|
bundle.putInt(FIELD_PREFERRED_VIDEO_ROLE_FLAGS, preferredVideoRoleFlags);
|
||||||
// Audio
|
// Audio
|
||||||
bundle.putStringArray(
|
bundle.putStringArray(
|
||||||
|
|
|
||||||
|
|
@ -343,6 +343,20 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Override
|
||||||
|
public ParametersBuilder setPreferredVideoLanguage(@Nullable String preferredVideoLanguage) {
|
||||||
|
super.setPreferredVideoLanguage(preferredVideoLanguage);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Override
|
||||||
|
public ParametersBuilder setPreferredVideoLanguages(String... preferredVideoLanguages) {
|
||||||
|
super.setPreferredVideoLanguages(preferredVideoLanguages);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Intentionally returning deprecated type
|
@SuppressWarnings("deprecation") // Intentionally returning deprecated type
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1153,6 +1167,20 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Override
|
||||||
|
public Builder setPreferredVideoLanguage(@Nullable String preferredVideoLanguage) {
|
||||||
|
super.setPreferredVideoLanguage(preferredVideoLanguage);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Override
|
||||||
|
public Builder setPreferredVideoLanguages(String... preferredVideoLanguages) {
|
||||||
|
super.setPreferredVideoLanguages(preferredVideoLanguages);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@Override
|
@Override
|
||||||
public Builder setPreferredVideoRoleFlags(@RoleFlags int preferredVideoRoleFlags) {
|
public Builder setPreferredVideoRoleFlags(@RoleFlags int preferredVideoRoleFlags) {
|
||||||
|
|
@ -2663,13 +2691,30 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
ExoTrackSelection.@NullableType Definition[] definitions =
|
ExoTrackSelection.@NullableType Definition[] definitions =
|
||||||
new ExoTrackSelection.Definition[rendererCount];
|
new ExoTrackSelection.Definition[rendererCount];
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Pair<ExoTrackSelection.Definition, Integer> selectedAudio =
|
||||||
|
selectAudioTrack(
|
||||||
|
mappedTrackInfo,
|
||||||
|
rendererFormatSupports,
|
||||||
|
rendererMixedMimeTypeAdaptationSupports,
|
||||||
|
params);
|
||||||
|
if (selectedAudio != null) {
|
||||||
|
definitions[selectedAudio.second] = selectedAudio.first;
|
||||||
|
}
|
||||||
|
@Nullable
|
||||||
|
String selectedAudioLanguage =
|
||||||
|
selectedAudio == null
|
||||||
|
? null
|
||||||
|
: selectedAudio.first.group.getFormat(selectedAudio.first.tracks[0]).language;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Pair<ExoTrackSelection.Definition, Integer> selectedVideo =
|
Pair<ExoTrackSelection.Definition, Integer> selectedVideo =
|
||||||
selectVideoTrack(
|
selectVideoTrack(
|
||||||
mappedTrackInfo,
|
mappedTrackInfo,
|
||||||
rendererFormatSupports,
|
rendererFormatSupports,
|
||||||
rendererMixedMimeTypeAdaptationSupports,
|
rendererMixedMimeTypeAdaptationSupports,
|
||||||
params);
|
params,
|
||||||
|
selectedAudioLanguage);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Pair<ExoTrackSelection.Definition, Integer> selectedImage =
|
Pair<ExoTrackSelection.Definition, Integer> selectedImage =
|
||||||
|
|
@ -2683,22 +2728,6 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
definitions[selectedVideo.second] = selectedVideo.first;
|
definitions[selectedVideo.second] = selectedVideo.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Pair<ExoTrackSelection.Definition, Integer> selectedAudio =
|
|
||||||
selectAudioTrack(
|
|
||||||
mappedTrackInfo,
|
|
||||||
rendererFormatSupports,
|
|
||||||
rendererMixedMimeTypeAdaptationSupports,
|
|
||||||
params);
|
|
||||||
if (selectedAudio != null) {
|
|
||||||
definitions[selectedAudio.second] = selectedAudio.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String selectedAudioLanguage =
|
|
||||||
selectedAudio == null
|
|
||||||
? null
|
|
||||||
: selectedAudio.first.group.getFormat(selectedAudio.first.tracks[0]).language;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Pair<ExoTrackSelection.Definition, Integer> selectedText =
|
Pair<ExoTrackSelection.Definition, Integer> selectedText =
|
||||||
selectTextTrack(mappedTrackInfo, rendererFormatSupports, params, selectedAudioLanguage);
|
selectTextTrack(mappedTrackInfo, rendererFormatSupports, params, selectedAudioLanguage);
|
||||||
|
|
@ -2732,6 +2761,8 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
* @param mixedMimeTypeSupports The {@link AdaptiveSupport} for mixed MIME type adaptation for the
|
* @param mixedMimeTypeSupports The {@link AdaptiveSupport} for mixed MIME type adaptation for the
|
||||||
* renderer.
|
* renderer.
|
||||||
* @param params The selector's current constraint parameters.
|
* @param params The selector's current constraint parameters.
|
||||||
|
* @param selectedAudioLanguage The language of the selected audio track. May be null if the
|
||||||
|
* selected audio track declares no language or no audio track was selected.
|
||||||
* @return A pair of the selected {@link ExoTrackSelection.Definition} and the corresponding
|
* @return A pair of the selected {@link ExoTrackSelection.Definition} and the corresponding
|
||||||
* renderer index, or null if no selection was made.
|
* renderer index, or null if no selection was made.
|
||||||
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
||||||
|
|
@ -2741,7 +2772,8 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
MappedTrackInfo mappedTrackInfo,
|
MappedTrackInfo mappedTrackInfo,
|
||||||
@Capabilities int[][][] rendererFormatSupports,
|
@Capabilities int[][][] rendererFormatSupports,
|
||||||
@AdaptiveSupport int[] mixedMimeTypeSupports,
|
@AdaptiveSupport int[] mixedMimeTypeSupports,
|
||||||
Parameters params)
|
Parameters params,
|
||||||
|
@Nullable String selectedAudioLanguage)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
if (params.audioOffloadPreferences.audioOffloadMode == AUDIO_OFFLOAD_MODE_REQUIRED) {
|
if (params.audioOffloadPreferences.audioOffloadMode == AUDIO_OFFLOAD_MODE_REQUIRED) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -2752,7 +2784,12 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
rendererFormatSupports,
|
rendererFormatSupports,
|
||||||
(int rendererIndex, TrackGroup group, @Capabilities int[] support) ->
|
(int rendererIndex, TrackGroup group, @Capabilities int[] support) ->
|
||||||
VideoTrackInfo.createForTrackGroup(
|
VideoTrackInfo.createForTrackGroup(
|
||||||
rendererIndex, group, params, support, mixedMimeTypeSupports[rendererIndex]),
|
rendererIndex,
|
||||||
|
group,
|
||||||
|
params,
|
||||||
|
support,
|
||||||
|
selectedAudioLanguage,
|
||||||
|
mixedMimeTypeSupports[rendererIndex]),
|
||||||
VideoTrackInfo::compareSelections);
|
VideoTrackInfo::compareSelections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3486,6 +3523,7 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
TrackGroup trackGroup,
|
TrackGroup trackGroup,
|
||||||
Parameters params,
|
Parameters params,
|
||||||
@Capabilities int[] formatSupport,
|
@Capabilities int[] formatSupport,
|
||||||
|
@Nullable String selectedAudioLanguage,
|
||||||
@AdaptiveSupport int mixedMimeTypeAdaptationSupport) {
|
@AdaptiveSupport int mixedMimeTypeAdaptationSupport) {
|
||||||
int maxPixelsToRetainForViewport =
|
int maxPixelsToRetainForViewport =
|
||||||
getMaxVideoPixelsToRetainForViewport(
|
getMaxVideoPixelsToRetainForViewport(
|
||||||
|
|
@ -3506,6 +3544,7 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
/* trackIndex= */ i,
|
/* trackIndex= */ i,
|
||||||
params,
|
params,
|
||||||
formatSupport[i],
|
formatSupport[i],
|
||||||
|
selectedAudioLanguage,
|
||||||
mixedMimeTypeAdaptationSupport,
|
mixedMimeTypeAdaptationSupport,
|
||||||
isSuitableForViewport));
|
isSuitableForViewport));
|
||||||
}
|
}
|
||||||
|
|
@ -3525,8 +3564,11 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
private final int bitrate;
|
private final int bitrate;
|
||||||
private final int pixelCount;
|
private final int pixelCount;
|
||||||
private final int preferredMimeTypeMatchIndex;
|
private final int preferredMimeTypeMatchIndex;
|
||||||
|
private final int preferredLanguageIndex;
|
||||||
|
private final int preferredLanguageScore;
|
||||||
private final int preferredRoleFlagsScore;
|
private final int preferredRoleFlagsScore;
|
||||||
private final boolean hasMainOrNoRoleFlag;
|
private final boolean hasMainOrNoRoleFlag;
|
||||||
|
private final int selectedAudioLanguageScore;
|
||||||
private final boolean allowMixedMimeTypes;
|
private final boolean allowMixedMimeTypes;
|
||||||
private final @SelectionEligibility int selectionEligibility;
|
private final @SelectionEligibility int selectionEligibility;
|
||||||
private final boolean usesPrimaryDecoder;
|
private final boolean usesPrimaryDecoder;
|
||||||
|
|
@ -3539,6 +3581,7 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
int trackIndex,
|
int trackIndex,
|
||||||
Parameters parameters,
|
Parameters parameters,
|
||||||
@Capabilities int formatSupport,
|
@Capabilities int formatSupport,
|
||||||
|
@Nullable String selectedAudioLanguage,
|
||||||
@AdaptiveSupport int mixedMimeTypeAdaptationSupport,
|
@AdaptiveSupport int mixedMimeTypeAdaptationSupport,
|
||||||
boolean isSuitableForViewport) {
|
boolean isSuitableForViewport) {
|
||||||
super(rendererIndex, trackGroup, trackIndex);
|
super(rendererIndex, trackGroup, trackIndex);
|
||||||
|
|
@ -3574,9 +3617,29 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
format.frameRate != Format.NO_VALUE && format.frameRate >= MIN_REASONABLE_FRAME_RATE;
|
format.frameRate != Format.NO_VALUE && format.frameRate >= MIN_REASONABLE_FRAME_RATE;
|
||||||
bitrate = format.bitrate;
|
bitrate = format.bitrate;
|
||||||
pixelCount = format.getPixelCount();
|
pixelCount = format.getPixelCount();
|
||||||
|
int bestLanguageIndex = Integer.MAX_VALUE;
|
||||||
|
int bestLanguageScore = 0;
|
||||||
|
for (int i = 0; i < parameters.preferredVideoLanguages.size(); i++) {
|
||||||
|
int score =
|
||||||
|
getFormatLanguageScore(
|
||||||
|
format,
|
||||||
|
parameters.preferredVideoLanguages.get(i),
|
||||||
|
/* allowUndeterminedFormatLanguage= */ false);
|
||||||
|
if (score > 0) {
|
||||||
|
bestLanguageIndex = i;
|
||||||
|
bestLanguageScore = score;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preferredLanguageIndex = bestLanguageIndex;
|
||||||
|
preferredLanguageScore = bestLanguageScore;
|
||||||
preferredRoleFlagsScore =
|
preferredRoleFlagsScore =
|
||||||
getRoleFlagMatchScore(format.roleFlags, parameters.preferredVideoRoleFlags);
|
getRoleFlagMatchScore(format.roleFlags, parameters.preferredVideoRoleFlags);
|
||||||
hasMainOrNoRoleFlag = format.roleFlags == 0 || (format.roleFlags & C.ROLE_FLAG_MAIN) != 0;
|
hasMainOrNoRoleFlag = format.roleFlags == 0 || (format.roleFlags & C.ROLE_FLAG_MAIN) != 0;
|
||||||
|
boolean selectedAudioLanguageUndetermined =
|
||||||
|
normalizeUndeterminedLanguageToNull(selectedAudioLanguage) == null;
|
||||||
|
selectedAudioLanguageScore =
|
||||||
|
getFormatLanguageScore(format, selectedAudioLanguage, selectedAudioLanguageUndetermined);
|
||||||
int bestMimeTypeMatchIndex = Integer.MAX_VALUE;
|
int bestMimeTypeMatchIndex = Integer.MAX_VALUE;
|
||||||
for (int i = 0; i < parameters.preferredVideoMimeTypes.size(); i++) {
|
for (int i = 0; i < parameters.preferredVideoMimeTypes.size(); i++) {
|
||||||
if (format.sampleMimeType != null
|
if (format.sampleMimeType != null
|
||||||
|
|
@ -3639,9 +3702,15 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||||
.compareFalseFirst(
|
.compareFalseFirst(
|
||||||
info1.isWithinRendererCapabilities, info2.isWithinRendererCapabilities)
|
info1.isWithinRendererCapabilities, info2.isWithinRendererCapabilities)
|
||||||
// 1. Compare match with specific content preferences set by the parameters.
|
// 1. Compare match with specific content preferences set by the parameters.
|
||||||
|
.compare(
|
||||||
|
info1.preferredLanguageIndex,
|
||||||
|
info2.preferredLanguageIndex,
|
||||||
|
Ordering.natural().reverse())
|
||||||
|
.compare(info1.preferredLanguageScore, info2.preferredLanguageScore)
|
||||||
.compare(info1.preferredRoleFlagsScore, info2.preferredRoleFlagsScore)
|
.compare(info1.preferredRoleFlagsScore, info2.preferredRoleFlagsScore)
|
||||||
// 2. Compare match with implicit content preferences set by the media.
|
// 2. Compare match with implicit content preferences set by the media.
|
||||||
.compareFalseFirst(info1.hasMainOrNoRoleFlag, info2.hasMainOrNoRoleFlag)
|
.compareFalseFirst(info1.hasMainOrNoRoleFlag, info2.hasMainOrNoRoleFlag)
|
||||||
|
.compare(info1.selectedAudioLanguageScore, info2.selectedAudioLanguageScore)
|
||||||
// 3. Compare match with 'reasonable' frame rate threshold.
|
// 3. Compare match with 'reasonable' frame rate threshold.
|
||||||
.compareFalseFirst(info1.hasReasonableFrameRate, info2.hasReasonableFrameRate)
|
.compareFalseFirst(info1.hasReasonableFrameRate, info2.hasReasonableFrameRate)
|
||||||
// 4. Compare match with technical preferences set by the parameters.
|
// 4. Compare match with technical preferences set by the parameters.
|
||||||
|
|
|
||||||
|
|
@ -743,6 +743,74 @@ public final class DefaultTrackSelectorTest {
|
||||||
assertFixedSelection(result.selections[0], trackGroups, enNonDefaultFormat);
|
assertFixedSelection(result.selections[0], trackGroups, enNonDefaultFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that track selector will select a video track with a language that matches the preferred
|
||||||
|
* language given by {@link Parameters}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void selectTracksSelectPreferredAudioVideoLanguage() throws Exception {
|
||||||
|
Format.Builder formatBuilder = VIDEO_FORMAT.buildUpon();
|
||||||
|
Format frVideoFormat = formatBuilder.setLanguage("fra").build();
|
||||||
|
Format enVideoFormat = formatBuilder.setLanguage("eng").build();
|
||||||
|
TrackGroupArray trackGroups = wrapFormats(frVideoFormat, enVideoFormat);
|
||||||
|
|
||||||
|
trackSelector.setParameters(defaultParameters.buildUpon().setPreferredVideoLanguage("eng"));
|
||||||
|
TrackSelectorResult result =
|
||||||
|
trackSelector.selectTracks(
|
||||||
|
new RendererCapabilities[] {ALL_VIDEO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
|
||||||
|
wrapFormats(frVideoFormat, enVideoFormat),
|
||||||
|
periodId,
|
||||||
|
TIMELINE);
|
||||||
|
assertFixedSelection(result.selections[0], trackGroups, enVideoFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the default track selector will select:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>A main video track matching the selected audio language when a main video track in
|
||||||
|
* another language is present.
|
||||||
|
* <li>A main video track that doesn't match the selected audio language when a main video track
|
||||||
|
* in the selected audio language is not present (but alternate video tracks in this
|
||||||
|
* language are present).
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void defaultVideoTracksInteractWithSelectedAudioLanguageAsExpected()
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
Format.Builder mainVideoBuilder = VIDEO_FORMAT.buildUpon().setRoleFlags(C.ROLE_FLAG_MAIN);
|
||||||
|
Format mainEnglish = mainVideoBuilder.setLanguage("eng").build();
|
||||||
|
Format mainGerman = mainVideoBuilder.setLanguage("deu").build();
|
||||||
|
Format mainNoLanguage = mainVideoBuilder.setLanguage(C.LANGUAGE_UNDETERMINED).build();
|
||||||
|
Format alternateGerman =
|
||||||
|
VIDEO_FORMAT.buildUpon().setRoleFlags(C.ROLE_FLAG_ALTERNATE).setLanguage("deu").build();
|
||||||
|
|
||||||
|
Format noLanguageAudio = AUDIO_FORMAT.buildUpon().setLanguage(null).build();
|
||||||
|
Format germanAudio = AUDIO_FORMAT.buildUpon().setLanguage("deu").build();
|
||||||
|
|
||||||
|
RendererCapabilities[] rendererCapabilities =
|
||||||
|
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES};
|
||||||
|
|
||||||
|
// Neither the audio nor the forced text track define a language. We select them both under the
|
||||||
|
// assumption that they have matching language.
|
||||||
|
TrackGroupArray trackGroups = wrapFormats(noLanguageAudio, mainNoLanguage);
|
||||||
|
TrackSelectorResult result =
|
||||||
|
trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||||
|
assertFixedSelection(result.selections[0], trackGroups, mainNoLanguage);
|
||||||
|
|
||||||
|
// The audio declares german. The main german track should be selected (in favour of the main
|
||||||
|
// english track).
|
||||||
|
trackGroups = wrapFormats(germanAudio, mainGerman, mainEnglish);
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||||
|
assertFixedSelection(result.selections[0], trackGroups, mainGerman);
|
||||||
|
|
||||||
|
// The audio declares german. The main english track should be selected because there's no
|
||||||
|
// main german track.
|
||||||
|
trackGroups = wrapFormats(germanAudio, alternateGerman, mainEnglish);
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||||
|
assertFixedSelection(result.selections[0], trackGroups, mainEnglish);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that track selector will prefer tracks that are within renderer's capabilities over track
|
* Tests that track selector will prefer tracks that are within renderer's capabilities over track
|
||||||
* that exceed renderer's capabilities.
|
* that exceed renderer's capabilities.
|
||||||
|
|
|
||||||
|
|
@ -1155,13 +1155,18 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||||
MappedTrackInfo mappedTrackInfo,
|
MappedTrackInfo mappedTrackInfo,
|
||||||
@RendererCapabilities.Capabilities int[][][] rendererFormatSupports,
|
@RendererCapabilities.Capabilities int[][][] rendererFormatSupports,
|
||||||
@RendererCapabilities.AdaptiveSupport int[] mixedMimeTypeSupports,
|
@RendererCapabilities.AdaptiveSupport int[] mixedMimeTypeSupports,
|
||||||
Parameters params)
|
Parameters params,
|
||||||
|
@Nullable String selectedAudioLanguage)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
if (disableVideoPlayback) {
|
if (disableVideoPlayback) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return super.selectVideoTrack(
|
return super.selectVideoTrack(
|
||||||
mappedTrackInfo, rendererFormatSupports, mixedMimeTypeSupports, params);
|
mappedTrackInfo,
|
||||||
|
rendererFormatSupports,
|
||||||
|
mixedMimeTypeSupports,
|
||||||
|
params,
|
||||||
|
selectedAudioLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue