mirror of
https://github.com/samsonjs/media.git
synced 2026-04-09 11:55:46 +00:00
Add support for audio adaptation
When no video tracks or renderers are present, attempt audio adaptation. Issue:#1975 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152708422
This commit is contained in:
parent
2a4df60b01
commit
f36500c200
1 changed files with 158 additions and 47 deletions
|
|
@ -372,24 +372,24 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
private static final int[] NO_TRACKS = new int[0];
|
||||
private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000;
|
||||
|
||||
private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory;
|
||||
private final TrackSelection.Factory adaptiveTrackSelectionFactory;
|
||||
private final AtomicReference<Parameters> paramsReference;
|
||||
|
||||
/**
|
||||
* Constructs an instance that does not support adaptive video.
|
||||
* Constructs an instance that does not support adaptive tracks.
|
||||
*/
|
||||
public DefaultTrackSelector() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance that uses a factory to create adaptive video track selections.
|
||||
* Constructs an instance that uses a factory to create adaptive track selections.
|
||||
*
|
||||
* @param adaptiveVideoTrackSelectionFactory A factory for adaptive video {@link TrackSelection}s,
|
||||
* or null if the selector should not support adaptive video.
|
||||
* @param adaptiveTrackSelectionFactory A factory for adaptive {@link TrackSelection}s, or null if
|
||||
* the selector should not support adaptive tracks.
|
||||
*/
|
||||
public DefaultTrackSelector(TrackSelection.Factory adaptiveVideoTrackSelectionFactory) {
|
||||
this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory;
|
||||
public DefaultTrackSelector(TrackSelection.Factory adaptiveTrackSelectionFactory) {
|
||||
this.adaptiveTrackSelectionFactory = adaptiveTrackSelectionFactory;
|
||||
paramsReference = new AtomicReference<>(new Parameters());
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +424,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
int rendererCount = rendererCapabilities.length;
|
||||
TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount];
|
||||
Parameters params = paramsReference.get();
|
||||
boolean videoTrackAndRendererPresent = false;
|
||||
|
||||
for (int i = 0; i < rendererCount; i++) {
|
||||
if (C.TRACK_TYPE_VIDEO == rendererCapabilities[i].getTrackType()) {
|
||||
|
|
@ -431,8 +432,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth,
|
||||
params.maxVideoHeight, params.maxVideoBitrate, params.allowNonSeamlessAdaptiveness,
|
||||
params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight,
|
||||
params.orientationMayChange, adaptiveVideoTrackSelectionFactory,
|
||||
params.orientationMayChange, adaptiveTrackSelectionFactory,
|
||||
params.exceedVideoConstraintsIfNecessary, params.exceedRendererCapabilitiesIfNecessary);
|
||||
videoTrackAndRendererPresent |= rendererTrackGroupArrays[i].length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +446,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
case C.TRACK_TYPE_AUDIO:
|
||||
rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i],
|
||||
rendererFormatSupports[i], params.preferredAudioLanguage,
|
||||
params.exceedRendererCapabilitiesIfNecessary);
|
||||
params.exceedRendererCapabilitiesIfNecessary, params.allowMixedMimeAdaptiveness,
|
||||
videoTrackAndRendererPresent ? null : adaptiveTrackSelectionFactory);
|
||||
break;
|
||||
case C.TRACK_TYPE_TEXT:
|
||||
rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i],
|
||||
|
|
@ -467,15 +470,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight,
|
||||
int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness,
|
||||
int viewportWidth, int viewportHeight, boolean orientationMayChange,
|
||||
TrackSelection.Factory adaptiveVideoTrackSelectionFactory,
|
||||
boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary)
|
||||
throws ExoPlaybackException {
|
||||
TrackSelection.Factory adaptiveTrackSelectionFactory, boolean exceedConstraintsIfNecessary,
|
||||
boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException {
|
||||
TrackSelection selection = null;
|
||||
if (adaptiveVideoTrackSelectionFactory != null) {
|
||||
if (adaptiveTrackSelectionFactory != null) {
|
||||
selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport,
|
||||
maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness,
|
||||
allowMixedMimeAdaptiveness, viewportWidth, viewportHeight,
|
||||
orientationMayChange, adaptiveVideoTrackSelectionFactory);
|
||||
orientationMayChange, adaptiveTrackSelectionFactory);
|
||||
}
|
||||
if (selection == null) {
|
||||
selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight,
|
||||
|
|
@ -489,7 +491,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight,
|
||||
int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness,
|
||||
int viewportWidth, int viewportHeight, boolean orientationMayChange,
|
||||
TrackSelection.Factory adaptiveVideoTrackSelectionFactory) throws ExoPlaybackException {
|
||||
TrackSelection.Factory adaptiveTrackSelectionFactory) throws ExoPlaybackException {
|
||||
int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness
|
||||
? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
|
||||
: RendererCapabilities.ADAPTIVE_SEAMLESS;
|
||||
|
|
@ -497,17 +499,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
&& (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0;
|
||||
for (int i = 0; i < groups.length; i++) {
|
||||
TrackGroup group = groups.get(i);
|
||||
int[] adaptiveTracks = getAdaptiveTracksForGroup(group, formatSupport[i],
|
||||
int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i],
|
||||
allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight,
|
||||
maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange);
|
||||
if (adaptiveTracks.length > 0) {
|
||||
return adaptiveVideoTrackSelectionFactory.createTrackSelection(group, adaptiveTracks);
|
||||
return adaptiveTrackSelectionFactory.createTrackSelection(group, adaptiveTracks);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int[] getAdaptiveTracksForGroup(TrackGroup group, int[] formatSupport,
|
||||
private static int[] getAdaptiveVideoTracksForGroup(TrackGroup group, int[] formatSupport,
|
||||
boolean allowMixedMimeTypes, int requiredAdaptiveSupport, int maxVideoWidth,
|
||||
int maxVideoHeight, int maxVideoBitrate, int viewportWidth, int viewportHeight,
|
||||
boolean orientationMayChange) {
|
||||
|
|
@ -530,7 +532,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
int trackIndex = selectedTrackIndices.get(i);
|
||||
String sampleMimeType = group.getFormat(trackIndex).sampleMimeType;
|
||||
if (seenMimeTypes.add(sampleMimeType)) {
|
||||
int countForMimeType = getAdaptiveTrackCountForMimeType(group, formatSupport,
|
||||
int countForMimeType = getAdaptiveVideoTrackCountForMimeType(group, formatSupport,
|
||||
requiredAdaptiveSupport, sampleMimeType, maxVideoWidth, maxVideoHeight,
|
||||
maxVideoBitrate, selectedTrackIndices);
|
||||
if (countForMimeType > selectedMimeTypeTrackCount) {
|
||||
|
|
@ -542,13 +544,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
}
|
||||
|
||||
// Filter by the selected mime type.
|
||||
filterAdaptiveTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport,
|
||||
filterAdaptiveVideoTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport,
|
||||
selectedMimeType, maxVideoWidth, maxVideoHeight, maxVideoBitrate, selectedTrackIndices);
|
||||
|
||||
return selectedTrackIndices.size() < 2 ? NO_TRACKS : Util.toArray(selectedTrackIndices);
|
||||
}
|
||||
|
||||
private static int getAdaptiveTrackCountForMimeType(TrackGroup group, int[] formatSupport,
|
||||
private static int getAdaptiveVideoTrackCountForMimeType(TrackGroup group, int[] formatSupport,
|
||||
int requiredAdaptiveSupport, String mimeType, int maxVideoWidth, int maxVideoHeight,
|
||||
int maxVideoBitrate, List<Integer> selectedTrackIndices) {
|
||||
int adaptiveTrackCount = 0;
|
||||
|
|
@ -563,9 +565,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
return adaptiveTrackCount;
|
||||
}
|
||||
|
||||
private static void filterAdaptiveTrackCountForMimeType(TrackGroup group, int[] formatSupport,
|
||||
int requiredAdaptiveSupport, String mimeType, int maxVideoWidth, int maxVideoHeight,
|
||||
int maxVideoBitrate, List<Integer> selectedTrackIndices) {
|
||||
private static void filterAdaptiveVideoTrackCountForMimeType(TrackGroup group,
|
||||
int[] formatSupport, int requiredAdaptiveSupport, String mimeType, int maxVideoWidth,
|
||||
int maxVideoHeight, int maxVideoBitrate, List<Integer> selectedTrackIndices) {
|
||||
for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) {
|
||||
int trackIndex = selectedTrackIndices.get(i);
|
||||
if (!isSupportedAdaptiveVideoTrack(group.getFormat(trackIndex), mimeType,
|
||||
|
|
@ -661,9 +663,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
// Audio track selection implementation.
|
||||
|
||||
protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport,
|
||||
String preferredAudioLanguage, boolean exceedRendererCapabilitiesIfNecessary) {
|
||||
TrackGroup selectedGroup = null;
|
||||
int selectedTrackIndex = 0;
|
||||
String preferredAudioLanguage, boolean exceedRendererCapabilitiesIfNecessary,
|
||||
boolean allowMixedMimeAdaptiveness, TrackSelection.Factory adaptiveTrackSelectionFactory) {
|
||||
int selectedGroupIndex = C.INDEX_UNSET;
|
||||
int selectedTrackIndex = C.INDEX_UNSET;
|
||||
int selectedTrackScore = 0;
|
||||
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||
TrackGroup trackGroup = groups.get(groupIndex);
|
||||
|
|
@ -671,32 +674,105 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||
if (isSupported(trackFormatSupport[trackIndex], exceedRendererCapabilitiesIfNecessary)) {
|
||||
Format format = trackGroup.getFormat(trackIndex);
|
||||
boolean isDefault = (format.selectionFlags & C.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 (isSupported(trackFormatSupport[trackIndex], false)) {
|
||||
trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
|
||||
}
|
||||
int trackScore = getAudioTrackScore(trackFormatSupport[trackIndex],
|
||||
preferredAudioLanguage, format);
|
||||
if (trackScore > selectedTrackScore) {
|
||||
selectedGroup = trackGroup;
|
||||
selectedGroupIndex = groupIndex;
|
||||
selectedTrackIndex = trackIndex;
|
||||
selectedTrackScore = trackScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedGroup == null ? null
|
||||
: new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||
|
||||
if (selectedGroupIndex == C.INDEX_UNSET) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TrackGroup selectedGroup = groups.get(selectedGroupIndex);
|
||||
if (adaptiveTrackSelectionFactory != null) {
|
||||
// If the group of the track with the highest score allows it, try to enable adaptation.
|
||||
int[] adaptiveTracks = getAdaptiveAudioTracks(selectedGroup,
|
||||
formatSupport[selectedGroupIndex], allowMixedMimeAdaptiveness);
|
||||
if (adaptiveTracks.length > 0) {
|
||||
return adaptiveTrackSelectionFactory.createTrackSelection(selectedGroup,
|
||||
adaptiveTracks);
|
||||
}
|
||||
}
|
||||
return new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||
}
|
||||
|
||||
private static int getAudioTrackScore(int formatSupport, String preferredLanguage,
|
||||
Format format) {
|
||||
boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
|
||||
int trackScore;
|
||||
if (formatHasLanguage(format, preferredLanguage)) {
|
||||
if (isDefault) {
|
||||
trackScore = 4;
|
||||
} else {
|
||||
trackScore = 3;
|
||||
}
|
||||
} else if (isDefault) {
|
||||
trackScore = 2;
|
||||
} else {
|
||||
trackScore = 1;
|
||||
}
|
||||
if (isSupported(formatSupport, false)) {
|
||||
trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
|
||||
}
|
||||
return trackScore;
|
||||
}
|
||||
|
||||
private static int[] getAdaptiveAudioTracks(TrackGroup group, int[] formatSupport,
|
||||
boolean allowMixedMimeTypes) {
|
||||
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);
|
||||
if (seenConfigurationTuples.add(configuration)) {
|
||||
int configurationCount = getAdaptiveAudioTrackCount(group, formatSupport, configuration);
|
||||
if (configurationCount > selectedConfigurationTrackCount) {
|
||||
selectedConfiguration = configuration;
|
||||
selectedConfigurationTrackCount = configurationCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedConfigurationTrackCount > 1) {
|
||||
int[] adaptiveIndices = new int[selectedConfigurationTrackCount];
|
||||
int index = 0;
|
||||
for (int i = 0; i < group.length; i++) {
|
||||
if (isSupportedAdaptiveAudioTrack(group.getFormat(i), formatSupport[i],
|
||||
selectedConfiguration)) {
|
||||
adaptiveIndices[index++] = i;
|
||||
}
|
||||
}
|
||||
return adaptiveIndices;
|
||||
}
|
||||
return NO_TRACKS;
|
||||
}
|
||||
|
||||
private static int getAdaptiveAudioTrackCount(TrackGroup group, int[] formatSupport,
|
||||
AudioConfigurationTuple configuration) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < group.length; i++) {
|
||||
if (isSupportedAdaptiveAudioTrack(group.getFormat(i), formatSupport[i], configuration)) {
|
||||
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));
|
||||
}
|
||||
|
||||
// Text track selection implementation.
|
||||
|
|
@ -791,7 +867,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
}
|
||||
|
||||
protected static boolean formatHasLanguage(Format format, String language) {
|
||||
return language != null && language.equals(Util.normalizeLanguageCode(format.language));
|
||||
return TextUtils.equals(language, Util.normalizeLanguageCode(format.language));
|
||||
}
|
||||
|
||||
// Viewport size util methods.
|
||||
|
|
@ -865,4 +941,39 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class AudioConfigurationTuple {
|
||||
|
||||
public final int channelCount;
|
||||
public final int sampleRate;
|
||||
public final String mimeType;
|
||||
|
||||
public AudioConfigurationTuple(int channelCount, int sampleRate, String mimeType) {
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AudioConfigurationTuple other = (AudioConfigurationTuple) obj;
|
||||
return channelCount == other.channelCount && sampleRate == other.sampleRate
|
||||
&& TextUtils.equals(mimeType, other.mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = channelCount;
|
||||
result = 31 * result + sampleRate;
|
||||
result = 31 * result + (mimeType != null ? mimeType.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue