Commit f36500c2 by aquilescanta Committed by Oliver Woodman

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
parent 2a4df60b
...@@ -372,24 +372,24 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -372,24 +372,24 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private static final int[] NO_TRACKS = new int[0]; private static final int[] NO_TRACKS = new int[0];
private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000; private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000;
private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory; private final TrackSelection.Factory adaptiveTrackSelectionFactory;
private final AtomicReference<Parameters> paramsReference; 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() { public DefaultTrackSelector() {
this(null); 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, * @param adaptiveTrackSelectionFactory A factory for adaptive {@link TrackSelection}s, or null if
* or null if the selector should not support adaptive video. * the selector should not support adaptive tracks.
*/ */
public DefaultTrackSelector(TrackSelection.Factory adaptiveVideoTrackSelectionFactory) { public DefaultTrackSelector(TrackSelection.Factory adaptiveTrackSelectionFactory) {
this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory; this.adaptiveTrackSelectionFactory = adaptiveTrackSelectionFactory;
paramsReference = new AtomicReference<>(new Parameters()); paramsReference = new AtomicReference<>(new Parameters());
} }
...@@ -424,6 +424,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -424,6 +424,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int rendererCount = rendererCapabilities.length; int rendererCount = rendererCapabilities.length;
TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount]; TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount];
Parameters params = paramsReference.get(); Parameters params = paramsReference.get();
boolean videoTrackAndRendererPresent = false;
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
if (C.TRACK_TYPE_VIDEO == rendererCapabilities[i].getTrackType()) { if (C.TRACK_TYPE_VIDEO == rendererCapabilities[i].getTrackType()) {
...@@ -431,8 +432,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -431,8 +432,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth, rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth,
params.maxVideoHeight, params.maxVideoBitrate, params.allowNonSeamlessAdaptiveness, params.maxVideoHeight, params.maxVideoBitrate, params.allowNonSeamlessAdaptiveness,
params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight, params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight,
params.orientationMayChange, adaptiveVideoTrackSelectionFactory, params.orientationMayChange, adaptiveTrackSelectionFactory,
params.exceedVideoConstraintsIfNecessary, params.exceedRendererCapabilitiesIfNecessary); params.exceedVideoConstraintsIfNecessary, params.exceedRendererCapabilitiesIfNecessary);
videoTrackAndRendererPresent |= rendererTrackGroupArrays[i].length > 0;
} }
} }
...@@ -444,7 +446,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -444,7 +446,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
case C.TRACK_TYPE_AUDIO: case C.TRACK_TYPE_AUDIO:
rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i], rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i],
rendererFormatSupports[i], params.preferredAudioLanguage, rendererFormatSupports[i], params.preferredAudioLanguage,
params.exceedRendererCapabilitiesIfNecessary); params.exceedRendererCapabilitiesIfNecessary, params.allowMixedMimeAdaptiveness,
videoTrackAndRendererPresent ? null : adaptiveTrackSelectionFactory);
break; break;
case C.TRACK_TYPE_TEXT: case C.TRACK_TYPE_TEXT:
rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i], rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i],
...@@ -467,15 +470,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -467,15 +470,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight,
int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness,
int viewportWidth, int viewportHeight, boolean orientationMayChange, int viewportWidth, int viewportHeight, boolean orientationMayChange,
TrackSelection.Factory adaptiveVideoTrackSelectionFactory, TrackSelection.Factory adaptiveTrackSelectionFactory, boolean exceedConstraintsIfNecessary,
boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary) boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException {
throws ExoPlaybackException {
TrackSelection selection = null; TrackSelection selection = null;
if (adaptiveVideoTrackSelectionFactory != null) { if (adaptiveTrackSelectionFactory != null) {
selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport,
maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness,
allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight,
orientationMayChange, adaptiveVideoTrackSelectionFactory); orientationMayChange, adaptiveTrackSelectionFactory);
} }
if (selection == null) { if (selection == null) {
selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight,
...@@ -489,7 +491,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -489,7 +491,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight,
int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness,
int viewportWidth, int viewportHeight, boolean orientationMayChange, int viewportWidth, int viewportHeight, boolean orientationMayChange,
TrackSelection.Factory adaptiveVideoTrackSelectionFactory) throws ExoPlaybackException { TrackSelection.Factory adaptiveTrackSelectionFactory) throws ExoPlaybackException {
int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness
? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
: RendererCapabilities.ADAPTIVE_SEAMLESS; : RendererCapabilities.ADAPTIVE_SEAMLESS;
...@@ -497,17 +499,17 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -497,17 +499,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0;
for (int i = 0; i < groups.length; i++) { for (int i = 0; i < groups.length; i++) {
TrackGroup group = groups.get(i); TrackGroup group = groups.get(i);
int[] adaptiveTracks = getAdaptiveTracksForGroup(group, formatSupport[i], int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i],
allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange); maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange);
if (adaptiveTracks.length > 0) { if (adaptiveTracks.length > 0) {
return adaptiveVideoTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); return adaptiveTrackSelectionFactory.createTrackSelection(group, adaptiveTracks);
} }
} }
return null; return null;
} }
private static int[] getAdaptiveTracksForGroup(TrackGroup group, int[] formatSupport, private static int[] getAdaptiveVideoTracksForGroup(TrackGroup group, int[] formatSupport,
boolean allowMixedMimeTypes, int requiredAdaptiveSupport, int maxVideoWidth, boolean allowMixedMimeTypes, int requiredAdaptiveSupport, int maxVideoWidth,
int maxVideoHeight, int maxVideoBitrate, int viewportWidth, int viewportHeight, int maxVideoHeight, int maxVideoBitrate, int viewportWidth, int viewportHeight,
boolean orientationMayChange) { boolean orientationMayChange) {
...@@ -530,7 +532,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -530,7 +532,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int trackIndex = selectedTrackIndices.get(i); int trackIndex = selectedTrackIndices.get(i);
String sampleMimeType = group.getFormat(trackIndex).sampleMimeType; String sampleMimeType = group.getFormat(trackIndex).sampleMimeType;
if (seenMimeTypes.add(sampleMimeType)) { if (seenMimeTypes.add(sampleMimeType)) {
int countForMimeType = getAdaptiveTrackCountForMimeType(group, formatSupport, int countForMimeType = getAdaptiveVideoTrackCountForMimeType(group, formatSupport,
requiredAdaptiveSupport, sampleMimeType, maxVideoWidth, maxVideoHeight, requiredAdaptiveSupport, sampleMimeType, maxVideoWidth, maxVideoHeight,
maxVideoBitrate, selectedTrackIndices); maxVideoBitrate, selectedTrackIndices);
if (countForMimeType > selectedMimeTypeTrackCount) { if (countForMimeType > selectedMimeTypeTrackCount) {
...@@ -542,13 +544,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -542,13 +544,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
// Filter by the selected mime type. // Filter by the selected mime type.
filterAdaptiveTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport, filterAdaptiveVideoTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport,
selectedMimeType, maxVideoWidth, maxVideoHeight, maxVideoBitrate, selectedTrackIndices); selectedMimeType, maxVideoWidth, maxVideoHeight, maxVideoBitrate, selectedTrackIndices);
return selectedTrackIndices.size() < 2 ? NO_TRACKS : Util.toArray(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 requiredAdaptiveSupport, String mimeType, int maxVideoWidth, int maxVideoHeight,
int maxVideoBitrate, List<Integer> selectedTrackIndices) { int maxVideoBitrate, List<Integer> selectedTrackIndices) {
int adaptiveTrackCount = 0; int adaptiveTrackCount = 0;
...@@ -563,9 +565,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -563,9 +565,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return adaptiveTrackCount; return adaptiveTrackCount;
} }
private static void filterAdaptiveTrackCountForMimeType(TrackGroup group, int[] formatSupport, private static void filterAdaptiveVideoTrackCountForMimeType(TrackGroup group,
int requiredAdaptiveSupport, String mimeType, int maxVideoWidth, int maxVideoHeight, int[] formatSupport, int requiredAdaptiveSupport, String mimeType, int maxVideoWidth,
int maxVideoBitrate, List<Integer> selectedTrackIndices) { int maxVideoHeight, int maxVideoBitrate, List<Integer> selectedTrackIndices) {
for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) { for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) {
int trackIndex = selectedTrackIndices.get(i); int trackIndex = selectedTrackIndices.get(i);
if (!isSupportedAdaptiveVideoTrack(group.getFormat(trackIndex), mimeType, if (!isSupportedAdaptiveVideoTrack(group.getFormat(trackIndex), mimeType,
...@@ -661,9 +663,10 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -661,9 +663,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Audio track selection implementation. // Audio track selection implementation.
protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport, protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport,
String preferredAudioLanguage, boolean exceedRendererCapabilitiesIfNecessary) { String preferredAudioLanguage, boolean exceedRendererCapabilitiesIfNecessary,
TrackGroup selectedGroup = null; boolean allowMixedMimeAdaptiveness, TrackSelection.Factory adaptiveTrackSelectionFactory) {
int selectedTrackIndex = 0; int selectedGroupIndex = C.INDEX_UNSET;
int selectedTrackIndex = C.INDEX_UNSET;
int selectedTrackScore = 0; int selectedTrackScore = 0;
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);
...@@ -671,32 +674,105 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -671,32 +674,105 @@ public class DefaultTrackSelector extends MappingTrackSelector {
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
if (isSupported(trackFormatSupport[trackIndex], exceedRendererCapabilitiesIfNecessary)) { if (isSupported(trackFormatSupport[trackIndex], exceedRendererCapabilitiesIfNecessary)) {
Format format = trackGroup.getFormat(trackIndex); Format format = trackGroup.getFormat(trackIndex);
boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; int trackScore = getAudioTrackScore(trackFormatSupport[trackIndex],
int trackScore; preferredAudioLanguage, format);
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;
}
if (trackScore > selectedTrackScore) { if (trackScore > selectedTrackScore) {
selectedGroup = trackGroup; selectedGroupIndex = groupIndex;
selectedTrackIndex = trackIndex; selectedTrackIndex = trackIndex;
selectedTrackScore = trackScore; 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. // Text track selection implementation.
...@@ -791,7 +867,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -791,7 +867,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
protected static boolean formatHasLanguage(Format format, String language) { 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. // Viewport size util methods.
...@@ -865,4 +941,39 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -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;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment