Skip to content

Commit

Permalink
Add support for audio adaptation
Browse files Browse the repository at this point in the history
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
  • Loading branch information
AquilesCanta authored and ojw28 committed Apr 11, 2017
1 parent 2a4df60 commit f36500c
Showing 1 changed file with 158 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -372,24 +372,24 @@ public int hashCode() {
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());
}

Expand Down Expand Up @@ -424,15 +424,17 @@ protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilit
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()) {
rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i],
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;
}
}

Expand All @@ -444,7 +446,8 @@ protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilit
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],
Expand All @@ -467,15 +470,14 @@ protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilit
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,
Expand All @@ -489,25 +491,25 @@ private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rend
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;
boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness
&& (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) {
Expand All @@ -530,7 +532,7 @@ private static int[] getAdaptiveTracksForGroup(TrackGroup group, int[] formatSup
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) {
Expand All @@ -542,13 +544,13 @@ private static int[] getAdaptiveTracksForGroup(TrackGroup group, int[] formatSup
}

// 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;
Expand All @@ -563,9 +565,9 @@ private static int getAdaptiveTrackCountForMimeType(TrackGroup group, int[] form
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,
Expand Down Expand Up @@ -661,42 +663,116 @@ private static int compareFormatValues(int first, int second) {
// 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);
int[] trackFormatSupport = formatSupport[groupIndex];
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.
Expand Down Expand Up @@ -791,7 +867,7 @@ protected static boolean isSupported(int formatSupport, boolean allowExceedsCapa
}

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.
Expand Down Expand Up @@ -865,4 +941,39 @@ private static Point getMaxVideoSizeInViewport(boolean orientationMayChange, int
}
}

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

}

}

0 comments on commit f36500c

Please # to comment.