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 599020e5b3..dd382d77b1 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 @@ -181,6 +181,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private int viewportHeight; private boolean viewportOrientationMayChange; private int tunnelingAudioSessionId; + private int maxFrameRate; /** Creates a builder with default initial values. */ public ParametersBuilder() { @@ -211,6 +212,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { viewportHeight = initialValues.viewportHeight; viewportOrientationMayChange = initialValues.viewportOrientationMayChange; tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId; + maxFrameRate = initialValues.maxFrameRate; } /** @@ -521,6 +523,16 @@ public class DefaultTrackSelector extends MappingTrackSelector { return this; } + /** + * See {@link Parameters#maxFrameRate}. + * + * @return This builder. + */ + public ParametersBuilder setMaxFrameRate(int maxFrameRate) { + this.maxFrameRate = maxFrameRate; + return this; + } + /** * Builds a {@link Parameters} instance with the selected values. */ @@ -544,7 +556,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { viewportWidth, viewportHeight, viewportOrientationMayChange, - tunnelingAudioSessionId); + tunnelingAudioSessionId, + maxFrameRate); } private static SparseArray> cloneSelectionOverrides( @@ -641,6 +654,10 @@ public class DefaultTrackSelector extends MappingTrackSelector { * The default value is {@code true}. */ public final boolean viewportOrientationMayChange; + /** + * Maximum video frame rate. The default value is {@link Integer#MAX_VALUE} (i.e. no constraint). + */ + public final int maxFrameRate; // General /** @@ -700,7 +717,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { /* viewportWidth= */ Integer.MAX_VALUE, /* viewportHeight= */ Integer.MAX_VALUE, /* viewportOrientationMayChange= */ true, - /* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET); + /* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET, + /* maxFrameRate= */ Integer.MAX_VALUE); } /* package */ Parameters( @@ -722,7 +740,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { int viewportWidth, int viewportHeight, boolean viewportOrientationMayChange, - int tunnelingAudioSessionId) { + int tunnelingAudioSessionId, + int maxFrameRate) { this.selectionOverrides = selectionOverrides; this.rendererDisabledFlags = rendererDisabledFlags; this.preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage); @@ -742,6 +761,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { this.viewportHeight = viewportHeight; this.viewportOrientationMayChange = viewportOrientationMayChange; this.tunnelingAudioSessionId = tunnelingAudioSessionId; + this.maxFrameRate = maxFrameRate; } /* package */ Parameters(Parcel in) { @@ -764,6 +784,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { this.viewportHeight = in.readInt(); this.viewportOrientationMayChange = Util.readBoolean(in); this.tunnelingAudioSessionId = in.readInt(); + this.maxFrameRate = in.readInt(); } /** @@ -832,6 +853,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { && viewportHeight == other.viewportHeight && maxVideoBitrate == other.maxVideoBitrate && tunnelingAudioSessionId == other.tunnelingAudioSessionId + && maxFrameRate == other.maxFrameRate && TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage) && TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage) && areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags) @@ -855,6 +877,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { result = 31 * result + viewportHeight; result = 31 * result + maxVideoBitrate; result = 31 * result + tunnelingAudioSessionId; + result = 31 * result + maxFrameRate; result = 31 * result + (preferredAudioLanguage == null ? 0 : preferredAudioLanguage.hashCode()); result = 31 * result + (preferredTextLanguage == null ? 0 : preferredTextLanguage.hashCode()); @@ -889,6 +912,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { dest.writeInt(viewportHeight); Util.writeBoolean(dest, viewportOrientationMayChange); dest.writeInt(tunnelingAudioSessionId); + dest.writeInt(maxFrameRate); } public static final Parcelable.Creator CREATOR = @@ -1435,7 +1459,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, params.maxVideoWidth, params.maxVideoHeight, - params.maxVideoBitrate, params.viewportWidth, params.viewportHeight, + params.maxVideoBitrate, params.maxFrameRate, params.viewportWidth, params.viewportHeight, params.viewportOrientationMayChange); if (adaptiveTracks.length > 0) { return Assertions.checkNotNull(adaptiveTrackSelectionFactory) @@ -1447,8 +1471,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { private static int[] getAdaptiveVideoTracksForGroup(TrackGroup group, int[] formatSupport, boolean allowMixedMimeTypes, int requiredAdaptiveSupport, int maxVideoWidth, - int maxVideoHeight, int maxVideoBitrate, int viewportWidth, int viewportHeight, - boolean viewportOrientationMayChange) { + int maxVideoHeight, int maxVideoBitrate, int maxFrameRate, int viewportWidth, + int viewportHeight, boolean viewportOrientationMayChange) { if (group.length < 2) { return NO_TRACKS; } @@ -1470,7 +1494,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { if (seenMimeTypes.add(sampleMimeType)) { int countForMimeType = getAdaptiveVideoTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport, sampleMimeType, maxVideoWidth, maxVideoHeight, - maxVideoBitrate, selectedTrackIndices); + maxVideoBitrate, maxFrameRate, selectedTrackIndices); if (countForMimeType > selectedMimeTypeTrackCount) { selectedMimeType = sampleMimeType; selectedMimeTypeTrackCount = countForMimeType; @@ -1481,7 +1505,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { // Filter by the selected mime type. filterAdaptiveVideoTrackCountForMimeType(group, formatSupport, requiredAdaptiveSupport, - selectedMimeType, maxVideoWidth, maxVideoHeight, maxVideoBitrate, selectedTrackIndices); + selectedMimeType, maxVideoWidth, maxVideoHeight, maxVideoBitrate, maxFrameRate, + selectedTrackIndices); return selectedTrackIndices.size() < 2 ? NO_TRACKS : Util.toArray(selectedTrackIndices); } @@ -1494,13 +1519,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, + int maxFrameRate, List selectedTrackIndices) { int adaptiveTrackCount = 0; for (int i = 0; i < selectedTrackIndices.size(); i++) { int trackIndex = selectedTrackIndices.get(i); if (isSupportedAdaptiveVideoTrack(group.getFormat(trackIndex), mimeType, formatSupport[trackIndex], requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, - maxVideoBitrate)) { + maxVideoBitrate, maxFrameRate)) { adaptiveTrackCount++; } } @@ -1515,12 +1541,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, + int maxFrameRate, List selectedTrackIndices) { for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) { int trackIndex = selectedTrackIndices.get(i); if (!isSupportedAdaptiveVideoTrack(group.getFormat(trackIndex), mimeType, formatSupport[trackIndex], requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, - maxVideoBitrate)) { + maxVideoBitrate, maxFrameRate)) { selectedTrackIndices.remove(i); } } @@ -1533,12 +1560,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { int requiredAdaptiveSupport, int maxVideoWidth, int maxVideoHeight, - int maxVideoBitrate) { + int maxVideoBitrate, + int maxFrameRate) { return isSupported(formatSupport, false) && ((formatSupport & requiredAdaptiveSupport) != 0) && (mimeType == null || Util.areEqual(format.sampleMimeType, mimeType)) && (format.width == Format.NO_VALUE || format.width <= maxVideoWidth) && (format.height == Format.NO_VALUE || format.height <= maxVideoHeight) - && (format.bitrate == Format.NO_VALUE || format.bitrate <= maxVideoBitrate); + && (format.bitrate == Format.NO_VALUE || format.bitrate <= maxVideoBitrate) + && (format.frameRate == Format.NO_VALUE || format.frameRate <= maxFrameRate); } private static @Nullable TrackSelection selectFixedVideoTrack( @@ -1560,7 +1589,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { boolean isWithinConstraints = selectedTrackIndices.contains(trackIndex) && (format.width == Format.NO_VALUE || format.width <= params.maxVideoWidth) && (format.height == Format.NO_VALUE || format.height <= params.maxVideoHeight) - && (format.bitrate == Format.NO_VALUE || format.bitrate <= params.maxVideoBitrate); + && (format.bitrate == Format.NO_VALUE || format.bitrate <= params.maxVideoBitrate) + && (format.frameRate == Format.NO_VALUE || format.frameRate <= params.maxFrameRate); if (!isWithinConstraints && !params.exceedVideoConstraintsIfNecessary) { // Track should not be selected. continue; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java index b650b2ece4..62ae60da85 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java @@ -136,7 +136,8 @@ public final class DefaultTrackSelectorTest { /* viewportWidth= */ 4, /* viewportHeight= */ 5, /* viewportOrientationMayChange= */ false, - /* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET); + /* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET, + /* maxFrameRate= */ 6); Parcel parcel = Parcel.obtain(); parametersToParcel.writeToParcel(parcel, 0);