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:
aquilescanta 2017-04-10 11:06:32 -07:00 committed by Oliver Woodman
parent 2a4df60b01
commit f36500c200

View file

@ -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;
}
}
}