From f36500c20079dd965e4946ada18d936233ef0857 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Mon, 10 Apr 2017 11:06:32 -0700 Subject: [PATCH] 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 --- .../trackselection/DefaultTrackSelector.java | 205 ++++++++++++++---- 1 file changed, 158 insertions(+), 47 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 1fa372ca0a..9db77fd7ad 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -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 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 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 selectedTrackIndices) { + private static void filterAdaptiveVideoTrackCountForMimeType(TrackGroup group, + int[] formatSupport, int requiredAdaptiveSupport, String mimeType, int maxVideoWidth, + int maxVideoHeight, int maxVideoBitrate, List 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 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; + } + + } + }