mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Enhance DefaultTrackSelector part 2
- Support specifying a preferred text language. - Score based selection for text/audio/other tracks. - Prefer default tracks to non-default ones. - Allow overriding of base select*Track methods. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=129626994
This commit is contained in:
parent
95f4113456
commit
b15ceba780
2 changed files with 155 additions and 76 deletions
|
|
@ -34,7 +34,6 @@ import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MappingTrackSelector} that allows configuration of common parameters.
|
* A {@link MappingTrackSelector} that allows configuration of common parameters.
|
||||||
|
|
@ -52,10 +51,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory;
|
private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory;
|
||||||
|
|
||||||
// Audio and text.
|
// Audio.
|
||||||
private String preferredLanguage;
|
private String preferredAudioLanguage;
|
||||||
|
|
||||||
//Video.
|
// Text.
|
||||||
|
private String preferredTextLanguage;
|
||||||
|
|
||||||
|
// Video.
|
||||||
private boolean allowMixedMimeAdaptiveness;
|
private boolean allowMixedMimeAdaptiveness;
|
||||||
private boolean allowNonSeamlessAdaptiveness;
|
private boolean allowNonSeamlessAdaptiveness;
|
||||||
private int maxVideoWidth;
|
private int maxVideoWidth;
|
||||||
|
|
@ -97,14 +99,29 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the preferred language for audio and text tracks.
|
* Sets the preferred language for audio, as well as for forced text tracks.
|
||||||
*
|
*
|
||||||
* @param preferredLanguage The language as defined by RFC 5646.
|
* @param preferredAudioLanguage The preferred language as defined by RFC 5646. {@code null} to
|
||||||
|
* select the default track, or first track if there's no default.
|
||||||
*/
|
*/
|
||||||
public void setPreferredLanguage(String preferredLanguage) {
|
public void setPreferredLanguages(String preferredAudioLanguage) {
|
||||||
String adjustedPreferredLanguage = new Locale(preferredLanguage).getLanguage();
|
preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage);
|
||||||
if (!Util.areEqual(this.preferredLanguage, adjustedPreferredLanguage)) {
|
if (!Util.areEqual(this.preferredAudioLanguage, preferredAudioLanguage)) {
|
||||||
this.preferredLanguage = adjustedPreferredLanguage;
|
this.preferredAudioLanguage = preferredAudioLanguage;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the preferred language for text tracks.
|
||||||
|
*
|
||||||
|
* @param preferredTextLanguage The preferred language as defined by RFC 5646. {@code null} to
|
||||||
|
* select the default track, or no track if there's no default.
|
||||||
|
*/
|
||||||
|
public void setPreferredTextLanguage(String preferredTextLanguage) {
|
||||||
|
preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage);
|
||||||
|
if (!Util.areEqual(this.preferredTextLanguage, preferredTextLanguage)) {
|
||||||
|
this.preferredTextLanguage = preferredTextLanguage;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,23 +238,23 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
for (int i = 0; i < rendererCapabilities.length; i++) {
|
for (int i = 0; i < rendererCapabilities.length; i++) {
|
||||||
switch (rendererCapabilities[i].getTrackType()) {
|
switch (rendererCapabilities[i].getTrackType()) {
|
||||||
case C.TRACK_TYPE_VIDEO:
|
case C.TRACK_TYPE_VIDEO:
|
||||||
rendererTrackSelections[i] = selectTrackForVideoRenderer(rendererCapabilities[i],
|
rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i],
|
||||||
rendererTrackGroupArrays[i], rendererFormatSupports[i], maxVideoWidth, maxVideoHeight,
|
rendererTrackGroupArrays[i], rendererFormatSupports[i], maxVideoWidth, maxVideoHeight,
|
||||||
allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth,
|
allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth,
|
||||||
viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory,
|
viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory,
|
||||||
exceedVideoConstraintsIfNecessary);
|
exceedVideoConstraintsIfNecessary);
|
||||||
break;
|
break;
|
||||||
case C.TRACK_TYPE_AUDIO:
|
case C.TRACK_TYPE_AUDIO:
|
||||||
rendererTrackSelections[i] = selectTrackForAudioRenderer(rendererTrackGroupArrays[i],
|
rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i],
|
||||||
rendererFormatSupports[i], preferredLanguage);
|
rendererFormatSupports[i], preferredAudioLanguage);
|
||||||
break;
|
break;
|
||||||
case C.TRACK_TYPE_TEXT:
|
case C.TRACK_TYPE_TEXT:
|
||||||
rendererTrackSelections[i] = selectTrackForTextRenderer(rendererTrackGroupArrays[i],
|
rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i],
|
||||||
rendererFormatSupports[i], preferredLanguage);
|
rendererFormatSupports[i], preferredTextLanguage, preferredAudioLanguage);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rendererTrackSelections[i] = selectFirstSupportedTrack(rendererTrackGroupArrays[i],
|
rendererTrackSelections[i] = selectOtherTrack(rendererCapabilities[i].getTrackType(),
|
||||||
rendererFormatSupports[i]);
|
rendererTrackGroupArrays[i], rendererFormatSupports[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,11 +263,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
// Video track selection implementation.
|
// Video track selection implementation.
|
||||||
|
|
||||||
private static TrackSelection selectTrackForVideoRenderer(
|
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities,
|
||||||
RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport,
|
TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight,
|
||||||
int maxVideoWidth, int maxVideoHeight, boolean allowNonSeamlessAdaptiveness,
|
boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth,
|
||||||
boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight,
|
int viewportHeight, boolean orientationMayChange,
|
||||||
boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory,
|
TrackSelection.Factory adaptiveVideoTrackSelectionFactory,
|
||||||
boolean exceedConstraintsIfNecessary) throws ExoPlaybackException {
|
boolean exceedConstraintsIfNecessary) throws ExoPlaybackException {
|
||||||
TrackSelection selection = null;
|
TrackSelection selection = null;
|
||||||
if (adaptiveVideoTrackSelectionFactory != null) {
|
if (adaptiveVideoTrackSelectionFactory != null) {
|
||||||
|
|
@ -371,7 +388,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int viewportWidth,
|
int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int viewportWidth,
|
||||||
int viewportHeight, boolean orientationMayChange, boolean exceedConstraintsIfNecessary) {
|
int viewportHeight, boolean orientationMayChange, boolean exceedConstraintsIfNecessary) {
|
||||||
TrackGroup selectedGroup = null;
|
TrackGroup selectedGroup = null;
|
||||||
int selectedTrackIndex = -1;
|
int selectedTrackIndex = 0;
|
||||||
int selectedPixelCount = Format.NO_VALUE;
|
int selectedPixelCount = Format.NO_VALUE;
|
||||||
boolean selectedIsWithinConstraints = false;
|
boolean selectedIsWithinConstraints = false;
|
||||||
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||||
|
|
@ -425,65 +442,117 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
// Audio track selection implementation.
|
// Audio track selection implementation.
|
||||||
|
|
||||||
private static TrackSelection selectTrackForAudioRenderer(TrackGroupArray groups,
|
protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport,
|
||||||
int[][] formatSupport, String preferredLanguage) {
|
String preferredAudioLanguage) {
|
||||||
if (preferredLanguage != null) {
|
TrackGroup selectedGroup = null;
|
||||||
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
int selectedTrackIndex = 0;
|
||||||
TrackGroup trackGroup = groups.get(groupIndex);
|
int selectedTrackScore = 0;
|
||||||
int[] trackFormatSupport = formatSupport[groupIndex];
|
|
||||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
|
||||||
if (isSupported(trackFormatSupport[trackIndex])
|
|
||||||
&& formatHasLanguage(trackGroup.getFormat(trackIndex), preferredLanguage)) {
|
|
||||||
return new FixedTrackSelection(trackGroup, trackIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No preferred language was selected or no audio track presented the preferred language.
|
|
||||||
return selectFirstSupportedTrack(groups, formatSupport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text track selection implementation.
|
|
||||||
|
|
||||||
private static TrackSelection selectTrackForTextRenderer(TrackGroupArray groups,
|
|
||||||
int[][] formatSupport, String preferredLanguage) {
|
|
||||||
TrackGroup firstForcedGroup = null;
|
|
||||||
int firstForcedTrack = -1;
|
|
||||||
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
|
||||||
TrackGroup trackGroup = groups.get(groupIndex);
|
|
||||||
int[] trackFormatSupport = formatSupport[groupIndex];
|
|
||||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
|
||||||
if (isSupported(trackFormatSupport[trackIndex])
|
|
||||||
&& (trackGroup.getFormat(trackIndex).selectionFlags
|
|
||||||
& Format.SELECTION_FLAG_FORCED) != 0) {
|
|
||||||
if (firstForcedGroup == null) {
|
|
||||||
firstForcedGroup = trackGroup;
|
|
||||||
firstForcedTrack = trackIndex;
|
|
||||||
}
|
|
||||||
if (formatHasLanguage(trackGroup.getFormat(trackIndex), preferredLanguage)) {
|
|
||||||
return new FixedTrackSelection(trackGroup, trackIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstForcedGroup == null ? null
|
|
||||||
: new FixedTrackSelection(firstForcedGroup, firstForcedTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// General track selection methods.
|
|
||||||
|
|
||||||
private static TrackSelection selectFirstSupportedTrack(TrackGroupArray groups,
|
|
||||||
int[][] formatSupport) {
|
|
||||||
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||||
TrackGroup trackGroup = groups.get(groupIndex);
|
TrackGroup trackGroup = groups.get(groupIndex);
|
||||||
int[] trackFormatSupport = formatSupport[groupIndex];
|
int[] trackFormatSupport = formatSupport[groupIndex];
|
||||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||||
if (isSupported(trackFormatSupport[trackIndex])) {
|
if (isSupported(trackFormatSupport[trackIndex])) {
|
||||||
return new FixedTrackSelection(trackGroup, trackIndex);
|
Format format = trackGroup.getFormat(trackIndex);
|
||||||
|
boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0;
|
||||||
|
int trackScore;
|
||||||
|
if (formatHasLanguage(format, preferredAudioLanguage)) {
|
||||||
|
if (isDefault) {
|
||||||
|
trackScore = 4;
|
||||||
|
} else {
|
||||||
|
trackScore = 3;
|
||||||
|
}
|
||||||
|
} else if (isDefault) {
|
||||||
|
trackScore = 2;
|
||||||
|
} else {
|
||||||
|
trackScore = 1;
|
||||||
|
}
|
||||||
|
if (trackScore > selectedTrackScore) {
|
||||||
|
selectedGroup = trackGroup;
|
||||||
|
selectedTrackIndex = trackIndex;
|
||||||
|
selectedTrackScore = trackScore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return selectedGroup == null ? null
|
||||||
|
: new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text track selection implementation.
|
||||||
|
|
||||||
|
protected TrackSelection selectTextTrack(TrackGroupArray groups, int[][] formatSupport,
|
||||||
|
String preferredTextLanguage, String preferredAudioLanguage) {
|
||||||
|
TrackGroup selectedGroup = null;
|
||||||
|
int selectedTrackIndex = 0;
|
||||||
|
int selectedTrackScore = 0;
|
||||||
|
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||||
|
TrackGroup trackGroup = groups.get(groupIndex);
|
||||||
|
int[] trackFormatSupport = formatSupport[groupIndex];
|
||||||
|
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||||
|
if (isSupported(trackFormatSupport[trackIndex])) {
|
||||||
|
Format format = trackGroup.getFormat(trackIndex);
|
||||||
|
boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0;
|
||||||
|
boolean isForced = (format.selectionFlags & Format.SELECTION_FLAG_FORCED) != 0;
|
||||||
|
int trackScore;
|
||||||
|
if (formatHasLanguage(format, preferredTextLanguage)) {
|
||||||
|
if (isDefault) {
|
||||||
|
trackScore = 6;
|
||||||
|
} 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;
|
||||||
|
} else {
|
||||||
|
trackScore = 4;
|
||||||
|
}
|
||||||
|
} else if (isDefault) {
|
||||||
|
trackScore = 3;
|
||||||
|
} else if (isForced) {
|
||||||
|
if (formatHasLanguage(format, preferredAudioLanguage)) {
|
||||||
|
trackScore = 2;
|
||||||
|
} else {
|
||||||
|
trackScore = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trackScore = 0;
|
||||||
|
}
|
||||||
|
if (trackScore > selectedTrackScore) {
|
||||||
|
selectedGroup = trackGroup;
|
||||||
|
selectedTrackIndex = trackIndex;
|
||||||
|
selectedTrackScore = trackScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedGroup == null ? null
|
||||||
|
: new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// General track selection methods.
|
||||||
|
|
||||||
|
protected TrackSelection selectOtherTrack(int trackType, TrackGroupArray groups,
|
||||||
|
int[][] formatSupport) {
|
||||||
|
TrackGroup selectedGroup = null;
|
||||||
|
int selectedTrackIndex = 0;
|
||||||
|
int selectedTrackScore = 0;
|
||||||
|
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||||
|
TrackGroup trackGroup = groups.get(groupIndex);
|
||||||
|
int[] trackFormatSupport = formatSupport[groupIndex];
|
||||||
|
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||||
|
if (isSupported(trackFormatSupport[trackIndex])) {
|
||||||
|
Format format = trackGroup.getFormat(trackIndex);
|
||||||
|
boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0;
|
||||||
|
int trackScore = isDefault ? 2 : 1;
|
||||||
|
if (trackScore > selectedTrackScore) {
|
||||||
|
selectedGroup = trackGroup;
|
||||||
|
selectedTrackIndex = trackIndex;
|
||||||
|
selectedTrackScore = trackScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedGroup == null ? null
|
||||||
|
: new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSupported(int formatSupport) {
|
private static boolean isSupported(int formatSupport) {
|
||||||
|
|
@ -492,7 +561,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean formatHasLanguage(Format format, String language) {
|
private static boolean formatHasLanguage(Format format, String language) {
|
||||||
return language != null && language.equals(new Locale(format.language).getLanguage());
|
return language != null && language.equals(Util.normalizeLanguageCode(format.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Viewport size util methods.
|
// Viewport size util methods.
|
||||||
|
|
@ -605,7 +674,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
Display display = windowManager.getDefaultDisplay();
|
Display display = windowManager.getDefaultDisplay();
|
||||||
Point displaySize = new Point();
|
Point displaySize = new Point();
|
||||||
if (Util.SDK_INT >= 23) {
|
if (Util.SDK_INT >= 23) {
|
||||||
getDisplaySizeV23(display, displaySize);
|
getDisplaySizeV23(display, displaySize);
|
||||||
} else if (Util.SDK_INT >= 17) {
|
} else if (Util.SDK_INT >= 17) {
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,16 @@ public final class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a normalized RFC 5646 language code.
|
||||||
|
*
|
||||||
|
* @param language A possibly non-normalized RFC 5646 language code.
|
||||||
|
* @return The normalized code, or null if the input was null.
|
||||||
|
*/
|
||||||
|
public static String normalizeLanguageCode(String language) {
|
||||||
|
return language == null ? null : new Locale(language).getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
|
* Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue