• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.trackselection;
17 
18 import android.content.Context;
19 import android.graphics.Point;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.text.TextUtils;
23 import android.util.Pair;
24 import android.util.SparseArray;
25 import android.util.SparseBooleanArray;
26 import androidx.annotation.Nullable;
27 import com.google.android.exoplayer2.C;
28 import com.google.android.exoplayer2.ExoPlaybackException;
29 import com.google.android.exoplayer2.Format;
30 import com.google.android.exoplayer2.Player;
31 import com.google.android.exoplayer2.Renderer;
32 import com.google.android.exoplayer2.RendererCapabilities;
33 import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
34 import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
35 import com.google.android.exoplayer2.RendererCapabilities.FormatSupport;
36 import com.google.android.exoplayer2.RendererConfiguration;
37 import com.google.android.exoplayer2.source.TrackGroup;
38 import com.google.android.exoplayer2.source.TrackGroupArray;
39 import com.google.android.exoplayer2.upstream.BandwidthMeter;
40 import com.google.android.exoplayer2.util.Assertions;
41 import com.google.android.exoplayer2.util.Util;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.concurrent.atomic.AtomicReference;
49 import org.checkerframework.checker.initialization.qual.UnderInitialization;
50 import org.checkerframework.checker.nullness.compatqual.NullableType;
51 
52 /**
53  * A default {@link TrackSelector} suitable for most use cases. Track selections are made according
54  * to configurable {@link Parameters}, which can be set by calling {@link
55  * #setParameters(Parameters)}.
56  *
57  * <h3>Modifying parameters</h3>
58  *
59  * To modify only some aspects of the parameters currently used by a selector, it's possible to
60  * obtain a {@link ParametersBuilder} initialized with the current {@link Parameters}. The desired
61  * modifications can be made on the builder, and the resulting {@link Parameters} can then be built
62  * and set on the selector. For example the following code modifies the parameters to restrict video
63  * track selections to SD, and to select a German audio track if there is one:
64  *
65  * <pre>{@code
66  * // Build on the current parameters.
67  * Parameters currentParameters = trackSelector.getParameters();
68  * // Build the resulting parameters.
69  * Parameters newParameters = currentParameters
70  *     .buildUpon()
71  *     .setMaxVideoSizeSd()
72  *     .setPreferredAudioLanguage("deu")
73  *     .build();
74  * // Set the new parameters.
75  * trackSelector.setParameters(newParameters);
76  * }</pre>
77  *
78  * Convenience methods and chaining allow this to be written more concisely as:
79  *
80  * <pre>{@code
81  * trackSelector.setParameters(
82  *     trackSelector
83  *         .buildUponParameters()
84  *         .setMaxVideoSizeSd()
85  *         .setPreferredAudioLanguage("deu"));
86  * }</pre>
87  *
88  * Selection {@link Parameters} support many different options, some of which are described below.
89  *
90  * <h3>Selecting specific tracks</h3>
91  *
92  * Track selection overrides can be used to select specific tracks. To specify an override for a
93  * renderer, it's first necessary to obtain the tracks that have been mapped to it:
94  *
95  * <pre>{@code
96  * MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
97  * TrackGroupArray rendererTrackGroups = mappedTrackInfo == null ? null
98  *     : mappedTrackInfo.getTrackGroups(rendererIndex);
99  * }</pre>
100  *
101  * If {@code rendererTrackGroups} is null then there aren't any currently mapped tracks, and so
102  * setting an override isn't possible. Note that a {@link Player.EventListener} registered on the
103  * player can be used to determine when the current tracks (and therefore the mapping) changes. If
104  * {@code rendererTrackGroups} is non-null then an override can be set. The next step is to query
105  * the properties of the available tracks to determine the {@code groupIndex} and the {@code
106  * trackIndices} within the group it that should be selected. The override can then be specified
107  * using {@link ParametersBuilder#setSelectionOverride}:
108  *
109  * <pre>{@code
110  * SelectionOverride selectionOverride = new SelectionOverride(groupIndex, trackIndices);
111  * trackSelector.setParameters(
112  *     trackSelector
113  *         .buildUponParameters()
114  *         .setSelectionOverride(rendererIndex, rendererTrackGroups, selectionOverride));
115  * }</pre>
116  *
117  * <h3>Constraint based track selection</h3>
118  *
119  * Whilst track selection overrides make it possible to select specific tracks, the recommended way
120  * of controlling which tracks are selected is by specifying constraints. For example consider the
121  * case of wanting to restrict video track selections to SD, and preferring German audio tracks.
122  * Track selection overrides could be used to select specific tracks meeting these criteria, however
123  * a simpler and more flexible approach is to specify these constraints directly:
124  *
125  * <pre>{@code
126  * trackSelector.setParameters(
127  *     trackSelector
128  *         .buildUponParameters()
129  *         .setMaxVideoSizeSd()
130  *         .setPreferredAudioLanguage("deu"));
131  * }</pre>
132  *
133  * There are several benefits to using constraint based track selection instead of specific track
134  * overrides:
135  *
136  * <ul>
137  *   <li>You can specify constraints before knowing what tracks the media provides. This can
138  *       simplify track selection code (e.g. you don't have to listen for changes in the available
139  *       tracks before configuring the selector).
140  *   <li>Constraints can be applied consistently across all periods in a complex piece of media,
141  *       even if those periods contain different tracks. In contrast, a specific track override is
142  *       only applied to periods whose tracks match those for which the override was set.
143  * </ul>
144  *
145  * <h3>Disabling renderers</h3>
146  *
147  * Renderers can be disabled using {@link ParametersBuilder#setRendererDisabled}. Disabling a
148  * renderer differs from setting a {@code null} override because the renderer is disabled
149  * unconditionally, whereas a {@code null} override is applied only when the track groups available
150  * to the renderer match the {@link TrackGroupArray} for which it was specified.
151  *
152  * <h3>Tunneling</h3>
153  *
154  * Tunneled playback can be enabled in cases where the combination of renderers and selected tracks
155  * support it. Tunneled playback is enabled by passing an audio session ID to {@link
156  * ParametersBuilder#setTunnelingAudioSessionId(int)}.
157  */
158 public class DefaultTrackSelector extends MappingTrackSelector {
159 
160   /**
161    * A builder for {@link Parameters}. See the {@link Parameters} documentation for explanations of
162    * the parameters that can be configured using this builder.
163    */
164   public static final class ParametersBuilder extends TrackSelectionParameters.Builder {
165 
166     // Video
167     private int maxVideoWidth;
168     private int maxVideoHeight;
169     private int maxVideoFrameRate;
170     private int maxVideoBitrate;
171     private boolean exceedVideoConstraintsIfNecessary;
172     private boolean allowVideoMixedMimeTypeAdaptiveness;
173     private boolean allowVideoNonSeamlessAdaptiveness;
174     private int viewportWidth;
175     private int viewportHeight;
176     private boolean viewportOrientationMayChange;
177     // Audio
178     private int maxAudioChannelCount;
179     private int maxAudioBitrate;
180     private boolean exceedAudioConstraintsIfNecessary;
181     private boolean allowAudioMixedMimeTypeAdaptiveness;
182     private boolean allowAudioMixedSampleRateAdaptiveness;
183     private boolean allowAudioMixedChannelCountAdaptiveness;
184     // General
185     private boolean forceLowestBitrate;
186     private boolean forceHighestSupportedBitrate;
187     private boolean exceedRendererCapabilitiesIfNecessary;
188     private int tunnelingAudioSessionId;
189 
190     private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
191         selectionOverrides;
192     private final SparseBooleanArray rendererDisabledFlags;
193 
194     /**
195      * @deprecated {@link Context} constraints will not be set using this constructor. Use {@link
196      *     #ParametersBuilder(Context)} instead.
197      */
198     @Deprecated
199     @SuppressWarnings({"deprecation"})
ParametersBuilder()200     public ParametersBuilder() {
201       super();
202       setInitialValuesWithoutContext();
203       selectionOverrides = new SparseArray<>();
204       rendererDisabledFlags = new SparseBooleanArray();
205     }
206 
207     /**
208      * Creates a builder with default initial values.
209      *
210      * @param context Any context.
211      */
212 
ParametersBuilder(Context context)213     public ParametersBuilder(Context context) {
214       super(context);
215       setInitialValuesWithoutContext();
216       selectionOverrides = new SparseArray<>();
217       rendererDisabledFlags = new SparseBooleanArray();
218       setViewportSizeToPhysicalDisplaySize(context, /* viewportOrientationMayChange= */ true);
219     }
220 
221     /**
222      * @param initialValues The {@link Parameters} from which the initial values of the builder are
223      *     obtained.
224      */
ParametersBuilder(Parameters initialValues)225     private ParametersBuilder(Parameters initialValues) {
226       super(initialValues);
227       // Video
228       maxVideoWidth = initialValues.maxVideoWidth;
229       maxVideoHeight = initialValues.maxVideoHeight;
230       maxVideoFrameRate = initialValues.maxVideoFrameRate;
231       maxVideoBitrate = initialValues.maxVideoBitrate;
232       exceedVideoConstraintsIfNecessary = initialValues.exceedVideoConstraintsIfNecessary;
233       allowVideoMixedMimeTypeAdaptiveness = initialValues.allowVideoMixedMimeTypeAdaptiveness;
234       allowVideoNonSeamlessAdaptiveness = initialValues.allowVideoNonSeamlessAdaptiveness;
235       viewportWidth = initialValues.viewportWidth;
236       viewportHeight = initialValues.viewportHeight;
237       viewportOrientationMayChange = initialValues.viewportOrientationMayChange;
238       // Audio
239       maxAudioChannelCount = initialValues.maxAudioChannelCount;
240       maxAudioBitrate = initialValues.maxAudioBitrate;
241       exceedAudioConstraintsIfNecessary = initialValues.exceedAudioConstraintsIfNecessary;
242       allowAudioMixedMimeTypeAdaptiveness = initialValues.allowAudioMixedMimeTypeAdaptiveness;
243       allowAudioMixedSampleRateAdaptiveness = initialValues.allowAudioMixedSampleRateAdaptiveness;
244       allowAudioMixedChannelCountAdaptiveness =
245           initialValues.allowAudioMixedChannelCountAdaptiveness;
246       // General
247       forceLowestBitrate = initialValues.forceLowestBitrate;
248       forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
249       exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
250       tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
251       // Overrides
252       selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides);
253       rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
254     }
255 
256     // Video
257 
258     /**
259      * Equivalent to {@link #setMaxVideoSize setMaxVideoSize(1279, 719)}.
260      *
261      * @return This builder.
262      */
setMaxVideoSizeSd()263     public ParametersBuilder setMaxVideoSizeSd() {
264       return setMaxVideoSize(1279, 719);
265     }
266 
267     /**
268      * Equivalent to {@link #setMaxVideoSize setMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE)}.
269      *
270      * @return This builder.
271      */
clearVideoSizeConstraints()272     public ParametersBuilder clearVideoSizeConstraints() {
273       return setMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
274     }
275 
276     /**
277      * Sets the maximum allowed video width and height.
278      *
279      * @param maxVideoWidth Maximum allowed video width in pixels.
280      * @param maxVideoHeight Maximum allowed video height in pixels.
281      * @return This builder.
282      */
setMaxVideoSize(int maxVideoWidth, int maxVideoHeight)283     public ParametersBuilder setMaxVideoSize(int maxVideoWidth, int maxVideoHeight) {
284       this.maxVideoWidth = maxVideoWidth;
285       this.maxVideoHeight = maxVideoHeight;
286       return this;
287     }
288 
289     /**
290      * Sets the maximum allowed video frame rate.
291      *
292      * @param maxVideoFrameRate Maximum allowed video frame rate in hertz.
293      * @return This builder.
294      */
setMaxVideoFrameRate(int maxVideoFrameRate)295     public ParametersBuilder setMaxVideoFrameRate(int maxVideoFrameRate) {
296       this.maxVideoFrameRate = maxVideoFrameRate;
297       return this;
298     }
299 
300     /**
301      * Sets the maximum allowed video bitrate.
302      *
303      * @param maxVideoBitrate Maximum allowed video bitrate in bits per second.
304      * @return This builder.
305      */
setMaxVideoBitrate(int maxVideoBitrate)306     public ParametersBuilder setMaxVideoBitrate(int maxVideoBitrate) {
307       this.maxVideoBitrate = maxVideoBitrate;
308       return this;
309     }
310 
311     /**
312      * Sets whether to exceed the {@link #setMaxVideoSize(int, int)} and {@link
313      * #setMaxAudioBitrate(int)} constraints when no selection can be made otherwise.
314      *
315      * @param exceedVideoConstraintsIfNecessary Whether to exceed video constraints when no
316      *     selection can be made otherwise.
317      * @return This builder.
318      */
setExceedVideoConstraintsIfNecessary( boolean exceedVideoConstraintsIfNecessary)319     public ParametersBuilder setExceedVideoConstraintsIfNecessary(
320         boolean exceedVideoConstraintsIfNecessary) {
321       this.exceedVideoConstraintsIfNecessary = exceedVideoConstraintsIfNecessary;
322       return this;
323     }
324 
325     /**
326      * Sets whether to allow adaptive video selections containing mixed MIME types.
327      *
328      * <p>Adaptations between different MIME types may not be completely seamless, in which case
329      * {@link #setAllowVideoNonSeamlessAdaptiveness(boolean)} also needs to be {@code true} for
330      * mixed MIME type selections to be made.
331      *
332      * @param allowVideoMixedMimeTypeAdaptiveness Whether to allow adaptive video selections
333      *     containing mixed MIME types.
334      * @return This builder.
335      */
setAllowVideoMixedMimeTypeAdaptiveness( boolean allowVideoMixedMimeTypeAdaptiveness)336     public ParametersBuilder setAllowVideoMixedMimeTypeAdaptiveness(
337         boolean allowVideoMixedMimeTypeAdaptiveness) {
338       this.allowVideoMixedMimeTypeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
339       return this;
340     }
341 
342     /**
343      * Sets whether to allow adaptive video selections where adaptation may not be completely
344      * seamless.
345      *
346      * @param allowVideoNonSeamlessAdaptiveness Whether to allow adaptive video selections where
347      *     adaptation may not be completely seamless.
348      * @return This builder.
349      */
setAllowVideoNonSeamlessAdaptiveness( boolean allowVideoNonSeamlessAdaptiveness)350     public ParametersBuilder setAllowVideoNonSeamlessAdaptiveness(
351         boolean allowVideoNonSeamlessAdaptiveness) {
352       this.allowVideoNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
353       return this;
354     }
355 
356     /**
357      * Equivalent to calling {@link #setViewportSize(int, int, boolean)} with the viewport size
358      * obtained from {@link Util#getCurrentDisplayModeSize(Context)}.
359      *
360      * @param context Any context.
361      * @param viewportOrientationMayChange Whether the viewport orientation may change during
362      *     playback.
363      * @return This builder.
364      */
setViewportSizeToPhysicalDisplaySize( Context context, boolean viewportOrientationMayChange)365     public ParametersBuilder setViewportSizeToPhysicalDisplaySize(
366         Context context, boolean viewportOrientationMayChange) {
367       // Assume the viewport is fullscreen.
368       Point viewportSize = Util.getCurrentDisplayModeSize(context);
369       return setViewportSize(viewportSize.x, viewportSize.y, viewportOrientationMayChange);
370     }
371 
372     /**
373      * Equivalent to {@link #setViewportSize setViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE,
374      * true)}.
375      *
376      * @return This builder.
377      */
clearViewportSizeConstraints()378     public ParametersBuilder clearViewportSizeConstraints() {
379       return setViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE, true);
380     }
381 
382     /**
383      * Sets the viewport size to constrain adaptive video selections so that only tracks suitable
384      * for the viewport are selected.
385      *
386      * @param viewportWidth Viewport width in pixels.
387      * @param viewportHeight Viewport height in pixels.
388      * @param viewportOrientationMayChange Whether the viewport orientation may change during
389      *     playback.
390      * @return This builder.
391      */
setViewportSize( int viewportWidth, int viewportHeight, boolean viewportOrientationMayChange)392     public ParametersBuilder setViewportSize(
393         int viewportWidth, int viewportHeight, boolean viewportOrientationMayChange) {
394       this.viewportWidth = viewportWidth;
395       this.viewportHeight = viewportHeight;
396       this.viewportOrientationMayChange = viewportOrientationMayChange;
397       return this;
398     }
399 
400     // Audio
401 
402     @Override
setPreferredAudioLanguage(@ullable String preferredAudioLanguage)403     public ParametersBuilder setPreferredAudioLanguage(@Nullable String preferredAudioLanguage) {
404       super.setPreferredAudioLanguage(preferredAudioLanguage);
405       return this;
406     }
407 
408     /**
409      * Sets the maximum allowed audio channel count.
410      *
411      * @param maxAudioChannelCount Maximum allowed audio channel count.
412      * @return This builder.
413      */
setMaxAudioChannelCount(int maxAudioChannelCount)414     public ParametersBuilder setMaxAudioChannelCount(int maxAudioChannelCount) {
415       this.maxAudioChannelCount = maxAudioChannelCount;
416       return this;
417     }
418 
419     /**
420      * Sets the maximum allowed audio bitrate.
421      *
422      * @param maxAudioBitrate Maximum allowed audio bitrate in bits per second.
423      * @return This builder.
424      */
setMaxAudioBitrate(int maxAudioBitrate)425     public ParametersBuilder setMaxAudioBitrate(int maxAudioBitrate) {
426       this.maxAudioBitrate = maxAudioBitrate;
427       return this;
428     }
429 
430     /**
431      * Sets whether to exceed the {@link #setMaxAudioChannelCount(int)} and {@link
432      * #setMaxAudioBitrate(int)} constraints when no selection can be made otherwise.
433      *
434      * @param exceedAudioConstraintsIfNecessary Whether to exceed audio constraints when no
435      *     selection can be made otherwise.
436      * @return This builder.
437      */
setExceedAudioConstraintsIfNecessary( boolean exceedAudioConstraintsIfNecessary)438     public ParametersBuilder setExceedAudioConstraintsIfNecessary(
439         boolean exceedAudioConstraintsIfNecessary) {
440       this.exceedAudioConstraintsIfNecessary = exceedAudioConstraintsIfNecessary;
441       return this;
442     }
443 
444     /**
445      * Sets whether to allow adaptive audio selections containing mixed MIME types.
446      *
447      * <p>Adaptations between different MIME types may not be completely seamless.
448      *
449      * @param allowAudioMixedMimeTypeAdaptiveness Whether to allow adaptive audio selections
450      *     containing mixed MIME types.
451      * @return This builder.
452      */
setAllowAudioMixedMimeTypeAdaptiveness( boolean allowAudioMixedMimeTypeAdaptiveness)453     public ParametersBuilder setAllowAudioMixedMimeTypeAdaptiveness(
454         boolean allowAudioMixedMimeTypeAdaptiveness) {
455       this.allowAudioMixedMimeTypeAdaptiveness = allowAudioMixedMimeTypeAdaptiveness;
456       return this;
457     }
458 
459     /**
460      * Sets whether to allow adaptive audio selections containing mixed sample rates.
461      *
462      * <p>Adaptations between different sample rates may not be completely seamless.
463      *
464      * @param allowAudioMixedSampleRateAdaptiveness Whether to allow adaptive audio selections
465      *     containing mixed sample rates.
466      * @return This builder.
467      */
setAllowAudioMixedSampleRateAdaptiveness( boolean allowAudioMixedSampleRateAdaptiveness)468     public ParametersBuilder setAllowAudioMixedSampleRateAdaptiveness(
469         boolean allowAudioMixedSampleRateAdaptiveness) {
470       this.allowAudioMixedSampleRateAdaptiveness = allowAudioMixedSampleRateAdaptiveness;
471       return this;
472     }
473 
474     /**
475      * Sets whether to allow adaptive audio selections containing mixed channel counts.
476      *
477      * <p>Adaptations between different channel counts may not be completely seamless.
478      *
479      * @param allowAudioMixedChannelCountAdaptiveness Whether to allow adaptive audio selections
480      *     containing mixed channel counts.
481      * @return This builder.
482      */
setAllowAudioMixedChannelCountAdaptiveness( boolean allowAudioMixedChannelCountAdaptiveness)483     public ParametersBuilder setAllowAudioMixedChannelCountAdaptiveness(
484         boolean allowAudioMixedChannelCountAdaptiveness) {
485       this.allowAudioMixedChannelCountAdaptiveness = allowAudioMixedChannelCountAdaptiveness;
486       return this;
487     }
488 
489     // Text
490 
491     @Override
setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings( Context context)492     public ParametersBuilder setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings(
493         Context context) {
494       super.setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings(context);
495       return this;
496     }
497 
498     @Override
setPreferredTextLanguage(@ullable String preferredTextLanguage)499     public ParametersBuilder setPreferredTextLanguage(@Nullable String preferredTextLanguage) {
500       super.setPreferredTextLanguage(preferredTextLanguage);
501       return this;
502     }
503 
504     @Override
setPreferredTextRoleFlags(@.RoleFlags int preferredTextRoleFlags)505     public ParametersBuilder setPreferredTextRoleFlags(@C.RoleFlags int preferredTextRoleFlags) {
506       super.setPreferredTextRoleFlags(preferredTextRoleFlags);
507       return this;
508     }
509 
510     @Override
setSelectUndeterminedTextLanguage( boolean selectUndeterminedTextLanguage)511     public ParametersBuilder setSelectUndeterminedTextLanguage(
512         boolean selectUndeterminedTextLanguage) {
513       super.setSelectUndeterminedTextLanguage(selectUndeterminedTextLanguage);
514       return this;
515     }
516 
517     @Override
setDisabledTextTrackSelectionFlags( @.SelectionFlags int disabledTextTrackSelectionFlags)518     public ParametersBuilder setDisabledTextTrackSelectionFlags(
519         @C.SelectionFlags int disabledTextTrackSelectionFlags) {
520       super.setDisabledTextTrackSelectionFlags(disabledTextTrackSelectionFlags);
521       return this;
522     }
523 
524     // General
525 
526     /**
527      * Sets whether to force selection of the single lowest bitrate audio and video tracks that
528      * comply with all other constraints.
529      *
530      * @param forceLowestBitrate Whether to force selection of the single lowest bitrate audio and
531      *     video tracks.
532      * @return This builder.
533      */
setForceLowestBitrate(boolean forceLowestBitrate)534     public ParametersBuilder setForceLowestBitrate(boolean forceLowestBitrate) {
535       this.forceLowestBitrate = forceLowestBitrate;
536       return this;
537     }
538 
539     /**
540      * Sets whether to force selection of the highest bitrate audio and video tracks that comply
541      * with all other constraints.
542      *
543      * @param forceHighestSupportedBitrate Whether to force selection of the highest bitrate audio
544      *     and video tracks.
545      * @return This builder.
546      */
setForceHighestSupportedBitrate(boolean forceHighestSupportedBitrate)547     public ParametersBuilder setForceHighestSupportedBitrate(boolean forceHighestSupportedBitrate) {
548       this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
549       return this;
550     }
551 
552     /**
553      * Sets whether to exceed renderer capabilities when no selection can be made otherwise.
554      *
555      * <p>This parameter applies when all of the tracks available for a renderer exceed the
556      * renderer's reported capabilities. If the parameter is {@code true} then the lowest quality
557      * track will still be selected. Playback may succeed if the renderer has under-reported its
558      * true capabilities. If {@code false} then no track will be selected.
559      *
560      * @param exceedRendererCapabilitiesIfNecessary Whether to exceed renderer capabilities when no
561      *     selection can be made otherwise.
562      * @return This builder.
563      */
setExceedRendererCapabilitiesIfNecessary( boolean exceedRendererCapabilitiesIfNecessary)564     public ParametersBuilder setExceedRendererCapabilitiesIfNecessary(
565         boolean exceedRendererCapabilitiesIfNecessary) {
566       this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
567       return this;
568     }
569 
570     /**
571      * Sets the audio session id to use when tunneling.
572      *
573      * <p>Enables or disables tunneling. To enable tunneling, pass an audio session id to use when
574      * in tunneling mode. Session ids can be generated using {@link
575      * C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
576      * C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and
577      * supported by the audio and video renderers for the selected tracks.
578      *
579      * @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
580      *     C#AUDIO_SESSION_ID_UNSET} to disable tunneling.
581      * @return This builder.
582      */
setTunnelingAudioSessionId(int tunnelingAudioSessionId)583     public ParametersBuilder setTunnelingAudioSessionId(int tunnelingAudioSessionId) {
584       this.tunnelingAudioSessionId = tunnelingAudioSessionId;
585       return this;
586     }
587 
588     // Overrides
589 
590     /**
591      * Sets whether the renderer at the specified index is disabled. Disabling a renderer prevents
592      * the selector from selecting any tracks for it.
593      *
594      * @param rendererIndex The renderer index.
595      * @param disabled Whether the renderer is disabled.
596      * @return This builder.
597      */
setRendererDisabled(int rendererIndex, boolean disabled)598     public final ParametersBuilder setRendererDisabled(int rendererIndex, boolean disabled) {
599       if (rendererDisabledFlags.get(rendererIndex) == disabled) {
600         // The disabled flag is unchanged.
601         return this;
602       }
603       // Only true values are placed in the array to make it easier to check for equality.
604       if (disabled) {
605         rendererDisabledFlags.put(rendererIndex, true);
606       } else {
607         rendererDisabledFlags.delete(rendererIndex);
608       }
609       return this;
610     }
611 
612     /**
613      * Overrides the track selection for the renderer at the specified index.
614      *
615      * <p>When the {@link TrackGroupArray} mapped to the renderer matches the one provided, the
616      * override is applied. When the {@link TrackGroupArray} does not match, the override has no
617      * effect. The override replaces any previous override for the specified {@link TrackGroupArray}
618      * for the specified {@link Renderer}.
619      *
620      * <p>Passing a {@code null} override will cause the renderer to be disabled when the {@link
621      * TrackGroupArray} mapped to it matches the one provided. When the {@link TrackGroupArray} does
622      * not match a {@code null} override has no effect. Hence a {@code null} override differs from
623      * disabling the renderer using {@link #setRendererDisabled(int, boolean)} because the renderer
624      * is disabled conditionally on the {@link TrackGroupArray} mapped to it, where-as {@link
625      * #setRendererDisabled(int, boolean)} disables the renderer unconditionally.
626      *
627      * <p>To remove overrides use {@link #clearSelectionOverride(int, TrackGroupArray)}, {@link
628      * #clearSelectionOverrides(int)} or {@link #clearSelectionOverrides()}.
629      *
630      * @param rendererIndex The renderer index.
631      * @param groups The {@link TrackGroupArray} for which the override should be applied.
632      * @param override The override.
633      * @return This builder.
634      */
setSelectionOverride( int rendererIndex, TrackGroupArray groups, @Nullable SelectionOverride override)635     public final ParametersBuilder setSelectionOverride(
636         int rendererIndex, TrackGroupArray groups, @Nullable SelectionOverride override) {
637       Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
638           selectionOverrides.get(rendererIndex);
639       if (overrides == null) {
640         overrides = new HashMap<>();
641         selectionOverrides.put(rendererIndex, overrides);
642       }
643       if (overrides.containsKey(groups) && Util.areEqual(overrides.get(groups), override)) {
644         // The override is unchanged.
645         return this;
646       }
647       overrides.put(groups, override);
648       return this;
649     }
650 
651     /**
652      * Clears a track selection override for the specified renderer and {@link TrackGroupArray}.
653      *
654      * @param rendererIndex The renderer index.
655      * @param groups The {@link TrackGroupArray} for which the override should be cleared.
656      * @return This builder.
657      */
clearSelectionOverride( int rendererIndex, TrackGroupArray groups)658     public final ParametersBuilder clearSelectionOverride(
659         int rendererIndex, TrackGroupArray groups) {
660       Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
661           selectionOverrides.get(rendererIndex);
662       if (overrides == null || !overrides.containsKey(groups)) {
663         // Nothing to clear.
664         return this;
665       }
666       overrides.remove(groups);
667       if (overrides.isEmpty()) {
668         selectionOverrides.remove(rendererIndex);
669       }
670       return this;
671     }
672 
673     /**
674      * Clears all track selection overrides for the specified renderer.
675      *
676      * @param rendererIndex The renderer index.
677      * @return This builder.
678      */
clearSelectionOverrides(int rendererIndex)679     public final ParametersBuilder clearSelectionOverrides(int rendererIndex) {
680       Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
681           selectionOverrides.get(rendererIndex);
682       if (overrides == null || overrides.isEmpty()) {
683         // Nothing to clear.
684         return this;
685       }
686       selectionOverrides.remove(rendererIndex);
687       return this;
688     }
689 
690     /**
691      * Clears all track selection overrides for all renderers.
692      *
693      * @return This builder.
694      */
clearSelectionOverrides()695     public final ParametersBuilder clearSelectionOverrides() {
696       if (selectionOverrides.size() == 0) {
697         // Nothing to clear.
698         return this;
699       }
700       selectionOverrides.clear();
701       return this;
702     }
703 
704     /**
705      * Builds a {@link Parameters} instance with the selected values.
706      */
build()707     public Parameters build() {
708       return new Parameters(
709           // Video
710           maxVideoWidth,
711           maxVideoHeight,
712           maxVideoFrameRate,
713           maxVideoBitrate,
714           exceedVideoConstraintsIfNecessary,
715           allowVideoMixedMimeTypeAdaptiveness,
716           allowVideoNonSeamlessAdaptiveness,
717           viewportWidth,
718           viewportHeight,
719           viewportOrientationMayChange,
720           // Audio
721           preferredAudioLanguage,
722           maxAudioChannelCount,
723           maxAudioBitrate,
724           exceedAudioConstraintsIfNecessary,
725           allowAudioMixedMimeTypeAdaptiveness,
726           allowAudioMixedSampleRateAdaptiveness,
727           allowAudioMixedChannelCountAdaptiveness,
728           // Text
729           preferredTextLanguage,
730           preferredTextRoleFlags,
731           selectUndeterminedTextLanguage,
732           disabledTextTrackSelectionFlags,
733           // General
734           forceLowestBitrate,
735           forceHighestSupportedBitrate,
736           exceedRendererCapabilitiesIfNecessary,
737           tunnelingAudioSessionId,
738           selectionOverrides,
739           rendererDisabledFlags);
740     }
741 
setInitialValuesWithoutContext(@nderInitialization ParametersBuilder this)742     private void setInitialValuesWithoutContext(@UnderInitialization ParametersBuilder this) {
743       // Video
744       maxVideoWidth = Integer.MAX_VALUE;
745       maxVideoHeight = Integer.MAX_VALUE;
746       maxVideoFrameRate = Integer.MAX_VALUE;
747       maxVideoBitrate = Integer.MAX_VALUE;
748       exceedVideoConstraintsIfNecessary = true;
749       allowVideoMixedMimeTypeAdaptiveness = false;
750       allowVideoNonSeamlessAdaptiveness = true;
751       viewportWidth = Integer.MAX_VALUE;
752       viewportHeight = Integer.MAX_VALUE;
753       viewportOrientationMayChange = true;
754       // Audio
755       maxAudioChannelCount = Integer.MAX_VALUE;
756       maxAudioBitrate = Integer.MAX_VALUE;
757       exceedAudioConstraintsIfNecessary = true;
758       allowAudioMixedMimeTypeAdaptiveness = false;
759       allowAudioMixedSampleRateAdaptiveness = false;
760       allowAudioMixedChannelCountAdaptiveness = false;
761       // General
762       forceLowestBitrate = false;
763       forceHighestSupportedBitrate = false;
764       exceedRendererCapabilitiesIfNecessary = true;
765       tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
766     }
767 
768     private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
cloneSelectionOverrides( SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides)769         cloneSelectionOverrides(
770             SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides) {
771       SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> clone =
772           new SparseArray<>();
773       for (int i = 0; i < selectionOverrides.size(); i++) {
774         clone.put(selectionOverrides.keyAt(i), new HashMap<>(selectionOverrides.valueAt(i)));
775       }
776       return clone;
777     }
778   }
779 
780   /**
781    * Extends {@link TrackSelectionParameters} by adding fields that are specific to {@link
782    * DefaultTrackSelector}.
783    */
784   public static final class Parameters extends TrackSelectionParameters {
785 
786     /**
787      * An instance with default values, except those obtained from the {@link Context}.
788      *
789      * <p>If possible, use {@link #getDefaults(Context)} instead.
790      *
791      * <p>This instance will not have the following settings:
792      *
793      * <ul>
794      *   <li>{@link ParametersBuilder#setViewportSizeToPhysicalDisplaySize(Context, boolean)
795      *       Viewport constraints} configured for the primary display.
796      *   <li>{@link
797      *       ParametersBuilder#setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings(Context)
798      *       Preferred text language and role flags} configured to the accessibility settings of
799      *       {@link android.view.accessibility.CaptioningManager}.
800      * </ul>
801      */
802     @SuppressWarnings("deprecation")
803     public static final Parameters DEFAULT_WITHOUT_CONTEXT = new ParametersBuilder().build();
804 
805     /** Returns an instance configured with default values. */
getDefaults(Context context)806     public static Parameters getDefaults(Context context) {
807       return new ParametersBuilder(context).build();
808     }
809 
810     // Video
811     /**
812      * Maximum allowed video width in pixels. The default value is {@link Integer#MAX_VALUE} (i.e.
813      * no constraint).
814      *
815      * <p>To constrain adaptive video track selections to be suitable for a given viewport (the
816      * region of the display within which video will be played), use ({@link #viewportWidth}, {@link
817      * #viewportHeight} and {@link #viewportOrientationMayChange}) instead.
818      */
819     public final int maxVideoWidth;
820     /**
821      * Maximum allowed video height in pixels. The default value is {@link Integer#MAX_VALUE} (i.e.
822      * no constraint).
823      *
824      * <p>To constrain adaptive video track selections to be suitable for a given viewport (the
825      * region of the display within which video will be played), use ({@link #viewportWidth}, {@link
826      * #viewportHeight} and {@link #viewportOrientationMayChange}) instead.
827      */
828     public final int maxVideoHeight;
829     /**
830      * Maximum allowed video frame rate in hertz. The default value is {@link Integer#MAX_VALUE}
831      * (i.e. no constraint).
832      */
833     public final int maxVideoFrameRate;
834     /**
835      * Maximum allowed video bitrate in bits per second. The default value is {@link
836      * Integer#MAX_VALUE} (i.e. no constraint).
837      */
838     public final int maxVideoBitrate;
839     /**
840      * Whether to exceed the {@link #maxVideoWidth}, {@link #maxVideoHeight} and {@link
841      * #maxVideoBitrate} constraints when no selection can be made otherwise. The default value is
842      * {@code true}.
843      */
844     public final boolean exceedVideoConstraintsIfNecessary;
845     /**
846      * Whether to allow adaptive video selections containing mixed MIME types. Adaptations between
847      * different MIME types may not be completely seamless, in which case {@link
848      * #allowVideoNonSeamlessAdaptiveness} also needs to be {@code true} for mixed MIME type
849      * selections to be made. The default value is {@code false}.
850      */
851     public final boolean allowVideoMixedMimeTypeAdaptiveness;
852     /**
853      * Whether to allow adaptive video selections where adaptation may not be completely seamless.
854      * The default value is {@code true}.
855      */
856     public final boolean allowVideoNonSeamlessAdaptiveness;
857     /**
858      * Viewport width in pixels. Constrains video track selections for adaptive content so that only
859      * tracks suitable for the viewport are selected. The default value is the physical width of the
860      * primary display, in pixels.
861      */
862     public final int viewportWidth;
863     /**
864      * Viewport height in pixels. Constrains video track selections for adaptive content so that
865      * only tracks suitable for the viewport are selected. The default value is the physical height
866      * of the primary display, in pixels.
867      */
868     public final int viewportHeight;
869     /**
870      * Whether the viewport orientation may change during playback. Constrains video track
871      * selections for adaptive content so that only tracks suitable for the viewport are selected.
872      * The default value is {@code true}.
873      */
874     public final boolean viewportOrientationMayChange;
875     // Audio
876     /**
877      * Maximum allowed audio channel count. The default value is {@link Integer#MAX_VALUE} (i.e. no
878      * constraint).
879      */
880     public final int maxAudioChannelCount;
881     /**
882      * Maximum allowed audio bitrate in bits per second. The default value is {@link
883      * Integer#MAX_VALUE} (i.e. no constraint).
884      */
885     public final int maxAudioBitrate;
886     /**
887      * Whether to exceed the {@link #maxAudioChannelCount} and {@link #maxAudioBitrate} constraints
888      * when no selection can be made otherwise. The default value is {@code true}.
889      */
890     public final boolean exceedAudioConstraintsIfNecessary;
891     /**
892      * Whether to allow adaptive audio selections containing mixed MIME types. Adaptations between
893      * different MIME types may not be completely seamless. The default value is {@code false}.
894      */
895     public final boolean allowAudioMixedMimeTypeAdaptiveness;
896     /**
897      * Whether to allow adaptive audio selections containing mixed sample rates. Adaptations between
898      * different sample rates may not be completely seamless. The default value is {@code false}.
899      */
900     public final boolean allowAudioMixedSampleRateAdaptiveness;
901     /**
902      * Whether to allow adaptive audio selections containing mixed channel counts. Adaptations
903      * between different channel counts may not be completely seamless. The default value is {@code
904      * false}.
905      */
906     public final boolean allowAudioMixedChannelCountAdaptiveness;
907 
908     // General
909     /**
910      * Whether to force selection of the single lowest bitrate audio and video tracks that comply
911      * with all other constraints. The default value is {@code false}.
912      */
913     public final boolean forceLowestBitrate;
914     /**
915      * Whether to force selection of the highest bitrate audio and video tracks that comply with all
916      * other constraints. The default value is {@code false}.
917      */
918     public final boolean forceHighestSupportedBitrate;
919     /**
920      * Whether to exceed renderer capabilities when no selection can be made otherwise.
921      *
922      * <p>This parameter applies when all of the tracks available for a renderer exceed the
923      * renderer's reported capabilities. If the parameter is {@code true} then the lowest quality
924      * track will still be selected. Playback may succeed if the renderer has under-reported its
925      * true capabilities. If {@code false} then no track will be selected. The default value is
926      * {@code true}.
927      */
928     public final boolean exceedRendererCapabilitiesIfNecessary;
929     /**
930      * The audio session id to use when tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
931      * is disabled. The default value is {@link C#AUDIO_SESSION_ID_UNSET} (i.e. tunneling is
932      * disabled).
933      */
934     public final int tunnelingAudioSessionId;
935 
936     // Overrides
937     private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
938         selectionOverrides;
939     private final SparseBooleanArray rendererDisabledFlags;
940 
Parameters( int maxVideoWidth, int maxVideoHeight, int maxVideoFrameRate, int maxVideoBitrate, boolean exceedVideoConstraintsIfNecessary, boolean allowVideoMixedMimeTypeAdaptiveness, boolean allowVideoNonSeamlessAdaptiveness, int viewportWidth, int viewportHeight, boolean viewportOrientationMayChange, @Nullable String preferredAudioLanguage, int maxAudioChannelCount, int maxAudioBitrate, boolean exceedAudioConstraintsIfNecessary, boolean allowAudioMixedMimeTypeAdaptiveness, boolean allowAudioMixedSampleRateAdaptiveness, boolean allowAudioMixedChannelCountAdaptiveness, @Nullable String preferredTextLanguage, @C.RoleFlags int preferredTextRoleFlags, boolean selectUndeterminedTextLanguage, @C.SelectionFlags int disabledTextTrackSelectionFlags, boolean forceLowestBitrate, boolean forceHighestSupportedBitrate, boolean exceedRendererCapabilitiesIfNecessary, int tunnelingAudioSessionId, SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides, SparseBooleanArray rendererDisabledFlags)941     /* package */ Parameters(
942         // Video
943         int maxVideoWidth,
944         int maxVideoHeight,
945         int maxVideoFrameRate,
946         int maxVideoBitrate,
947         boolean exceedVideoConstraintsIfNecessary,
948         boolean allowVideoMixedMimeTypeAdaptiveness,
949         boolean allowVideoNonSeamlessAdaptiveness,
950         int viewportWidth,
951         int viewportHeight,
952         boolean viewportOrientationMayChange,
953         // Audio
954         @Nullable String preferredAudioLanguage,
955         int maxAudioChannelCount,
956         int maxAudioBitrate,
957         boolean exceedAudioConstraintsIfNecessary,
958         boolean allowAudioMixedMimeTypeAdaptiveness,
959         boolean allowAudioMixedSampleRateAdaptiveness,
960         boolean allowAudioMixedChannelCountAdaptiveness,
961         // Text
962         @Nullable String preferredTextLanguage,
963         @C.RoleFlags int preferredTextRoleFlags,
964         boolean selectUndeterminedTextLanguage,
965         @C.SelectionFlags int disabledTextTrackSelectionFlags,
966         // General
967         boolean forceLowestBitrate,
968         boolean forceHighestSupportedBitrate,
969         boolean exceedRendererCapabilitiesIfNecessary,
970         int tunnelingAudioSessionId,
971         // Overrides
972         SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides,
973         SparseBooleanArray rendererDisabledFlags) {
974       super(
975           preferredAudioLanguage,
976           preferredTextLanguage,
977           preferredTextRoleFlags,
978           selectUndeterminedTextLanguage,
979           disabledTextTrackSelectionFlags);
980       // Video
981       this.maxVideoWidth = maxVideoWidth;
982       this.maxVideoHeight = maxVideoHeight;
983       this.maxVideoFrameRate = maxVideoFrameRate;
984       this.maxVideoBitrate = maxVideoBitrate;
985       this.exceedVideoConstraintsIfNecessary = exceedVideoConstraintsIfNecessary;
986       this.allowVideoMixedMimeTypeAdaptiveness = allowVideoMixedMimeTypeAdaptiveness;
987       this.allowVideoNonSeamlessAdaptiveness = allowVideoNonSeamlessAdaptiveness;
988       this.viewportWidth = viewportWidth;
989       this.viewportHeight = viewportHeight;
990       this.viewportOrientationMayChange = viewportOrientationMayChange;
991       // Audio
992       this.maxAudioChannelCount = maxAudioChannelCount;
993       this.maxAudioBitrate = maxAudioBitrate;
994       this.exceedAudioConstraintsIfNecessary = exceedAudioConstraintsIfNecessary;
995       this.allowAudioMixedMimeTypeAdaptiveness = allowAudioMixedMimeTypeAdaptiveness;
996       this.allowAudioMixedSampleRateAdaptiveness = allowAudioMixedSampleRateAdaptiveness;
997       this.allowAudioMixedChannelCountAdaptiveness = allowAudioMixedChannelCountAdaptiveness;
998       // General
999       this.forceLowestBitrate = forceLowestBitrate;
1000       this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
1001       this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
1002       this.tunnelingAudioSessionId = tunnelingAudioSessionId;
1003       // Overrides
1004       this.selectionOverrides = selectionOverrides;
1005       this.rendererDisabledFlags = rendererDisabledFlags;
1006     }
1007 
1008     /* package */
Parameters(Parcel in)1009     Parameters(Parcel in) {
1010       super(in);
1011       // Video
1012       this.maxVideoWidth = in.readInt();
1013       this.maxVideoHeight = in.readInt();
1014       this.maxVideoFrameRate = in.readInt();
1015       this.maxVideoBitrate = in.readInt();
1016       this.exceedVideoConstraintsIfNecessary = Util.readBoolean(in);
1017       this.allowVideoMixedMimeTypeAdaptiveness = Util.readBoolean(in);
1018       this.allowVideoNonSeamlessAdaptiveness = Util.readBoolean(in);
1019       this.viewportWidth = in.readInt();
1020       this.viewportHeight = in.readInt();
1021       this.viewportOrientationMayChange = Util.readBoolean(in);
1022       // Audio
1023       this.maxAudioChannelCount = in.readInt();
1024       this.maxAudioBitrate = in.readInt();
1025       this.exceedAudioConstraintsIfNecessary = Util.readBoolean(in);
1026       this.allowAudioMixedMimeTypeAdaptiveness = Util.readBoolean(in);
1027       this.allowAudioMixedSampleRateAdaptiveness = Util.readBoolean(in);
1028       this.allowAudioMixedChannelCountAdaptiveness = Util.readBoolean(in);
1029       // General
1030       this.forceLowestBitrate = Util.readBoolean(in);
1031       this.forceHighestSupportedBitrate = Util.readBoolean(in);
1032       this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
1033       this.tunnelingAudioSessionId = in.readInt();
1034       // Overrides
1035       this.selectionOverrides = readSelectionOverrides(in);
1036       this.rendererDisabledFlags = Util.castNonNull(in.readSparseBooleanArray());
1037     }
1038 
1039     /**
1040      * Returns whether the renderer is disabled.
1041      *
1042      * @param rendererIndex The renderer index.
1043      * @return Whether the renderer is disabled.
1044      */
getRendererDisabled(int rendererIndex)1045     public final boolean getRendererDisabled(int rendererIndex) {
1046       return rendererDisabledFlags.get(rendererIndex);
1047     }
1048 
1049     /**
1050      * Returns whether there is an override for the specified renderer and {@link TrackGroupArray}.
1051      *
1052      * @param rendererIndex The renderer index.
1053      * @param groups The {@link TrackGroupArray}.
1054      * @return Whether there is an override.
1055      */
hasSelectionOverride(int rendererIndex, TrackGroupArray groups)1056     public final boolean hasSelectionOverride(int rendererIndex, TrackGroupArray groups) {
1057       Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
1058           selectionOverrides.get(rendererIndex);
1059       return overrides != null && overrides.containsKey(groups);
1060     }
1061 
1062     /**
1063      * Returns the override for the specified renderer and {@link TrackGroupArray}.
1064      *
1065      * @param rendererIndex The renderer index.
1066      * @param groups The {@link TrackGroupArray}.
1067      * @return The override, or null if no override exists.
1068      */
1069     @Nullable
getSelectionOverride(int rendererIndex, TrackGroupArray groups)1070     public final SelectionOverride getSelectionOverride(int rendererIndex, TrackGroupArray groups) {
1071       Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
1072           selectionOverrides.get(rendererIndex);
1073       return overrides != null ? overrides.get(groups) : null;
1074     }
1075 
1076     /** Creates a new {@link ParametersBuilder}, copying the initial values from this instance. */
1077     @Override
buildUpon()1078     public ParametersBuilder buildUpon() {
1079       return new ParametersBuilder(this);
1080     }
1081 
1082     @Override
equals(@ullable Object obj)1083     public boolean equals(@Nullable Object obj) {
1084       if (this == obj) {
1085         return true;
1086       }
1087       if (obj == null || getClass() != obj.getClass()) {
1088         return false;
1089       }
1090       Parameters other = (Parameters) obj;
1091       return super.equals(obj)
1092           // Video
1093           && maxVideoWidth == other.maxVideoWidth
1094           && maxVideoHeight == other.maxVideoHeight
1095           && maxVideoFrameRate == other.maxVideoFrameRate
1096           && maxVideoBitrate == other.maxVideoBitrate
1097           && exceedVideoConstraintsIfNecessary == other.exceedVideoConstraintsIfNecessary
1098           && allowVideoMixedMimeTypeAdaptiveness == other.allowVideoMixedMimeTypeAdaptiveness
1099           && allowVideoNonSeamlessAdaptiveness == other.allowVideoNonSeamlessAdaptiveness
1100           && viewportOrientationMayChange == other.viewportOrientationMayChange
1101           && viewportWidth == other.viewportWidth
1102           && viewportHeight == other.viewportHeight
1103           // Audio
1104           && maxAudioChannelCount == other.maxAudioChannelCount
1105           && maxAudioBitrate == other.maxAudioBitrate
1106           && exceedAudioConstraintsIfNecessary == other.exceedAudioConstraintsIfNecessary
1107           && allowAudioMixedMimeTypeAdaptiveness == other.allowAudioMixedMimeTypeAdaptiveness
1108           && allowAudioMixedSampleRateAdaptiveness == other.allowAudioMixedSampleRateAdaptiveness
1109           && allowAudioMixedChannelCountAdaptiveness
1110               == other.allowAudioMixedChannelCountAdaptiveness
1111           // General
1112           && forceLowestBitrate == other.forceLowestBitrate
1113           && forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
1114           && exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
1115           && tunnelingAudioSessionId == other.tunnelingAudioSessionId
1116           // Overrides
1117           && areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
1118           && areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides);
1119     }
1120 
1121     @Override
hashCode()1122     public int hashCode() {
1123       int result = super.hashCode();
1124       // Video
1125       result = 31 * result + maxVideoWidth;
1126       result = 31 * result + maxVideoHeight;
1127       result = 31 * result + maxVideoFrameRate;
1128       result = 31 * result + maxVideoBitrate;
1129       result = 31 * result + (exceedVideoConstraintsIfNecessary ? 1 : 0);
1130       result = 31 * result + (allowVideoMixedMimeTypeAdaptiveness ? 1 : 0);
1131       result = 31 * result + (allowVideoNonSeamlessAdaptiveness ? 1 : 0);
1132       result = 31 * result + (viewportOrientationMayChange ? 1 : 0);
1133       result = 31 * result + viewportWidth;
1134       result = 31 * result + viewportHeight;
1135       // Audio
1136       result = 31 * result + maxAudioChannelCount;
1137       result = 31 * result + maxAudioBitrate;
1138       result = 31 * result + (exceedAudioConstraintsIfNecessary ? 1 : 0);
1139       result = 31 * result + (allowAudioMixedMimeTypeAdaptiveness ? 1 : 0);
1140       result = 31 * result + (allowAudioMixedSampleRateAdaptiveness ? 1 : 0);
1141       result = 31 * result + (allowAudioMixedChannelCountAdaptiveness ? 1 : 0);
1142       // General
1143       result = 31 * result + (forceLowestBitrate ? 1 : 0);
1144       result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
1145       result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
1146       result = 31 * result + tunnelingAudioSessionId;
1147       // Overrides (omitted from hashCode).
1148       return result;
1149     }
1150 
1151     // Parcelable implementation.
1152 
1153     @Override
describeContents()1154     public int describeContents() {
1155       return 0;
1156     }
1157 
1158     @Override
writeToParcel(Parcel dest, int flags)1159     public void writeToParcel(Parcel dest, int flags) {
1160       super.writeToParcel(dest, flags);
1161       // Video
1162       dest.writeInt(maxVideoWidth);
1163       dest.writeInt(maxVideoHeight);
1164       dest.writeInt(maxVideoFrameRate);
1165       dest.writeInt(maxVideoBitrate);
1166       Util.writeBoolean(dest, exceedVideoConstraintsIfNecessary);
1167       Util.writeBoolean(dest, allowVideoMixedMimeTypeAdaptiveness);
1168       Util.writeBoolean(dest, allowVideoNonSeamlessAdaptiveness);
1169       dest.writeInt(viewportWidth);
1170       dest.writeInt(viewportHeight);
1171       Util.writeBoolean(dest, viewportOrientationMayChange);
1172       // Audio
1173       dest.writeInt(maxAudioChannelCount);
1174       dest.writeInt(maxAudioBitrate);
1175       Util.writeBoolean(dest, exceedAudioConstraintsIfNecessary);
1176       Util.writeBoolean(dest, allowAudioMixedMimeTypeAdaptiveness);
1177       Util.writeBoolean(dest, allowAudioMixedSampleRateAdaptiveness);
1178       Util.writeBoolean(dest, allowAudioMixedChannelCountAdaptiveness);
1179       // General
1180       Util.writeBoolean(dest, forceLowestBitrate);
1181       Util.writeBoolean(dest, forceHighestSupportedBitrate);
1182       Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
1183       dest.writeInt(tunnelingAudioSessionId);
1184       // Overrides
1185       writeSelectionOverridesToParcel(dest, selectionOverrides);
1186       dest.writeSparseBooleanArray(rendererDisabledFlags);
1187     }
1188 
1189     public static final Parcelable.Creator<Parameters> CREATOR =
1190         new Parcelable.Creator<Parameters>() {
1191 
1192           @Override
1193           public Parameters createFromParcel(Parcel in) {
1194             return new Parameters(in);
1195           }
1196 
1197           @Override
1198           public Parameters[] newArray(int size) {
1199             return new Parameters[size];
1200           }
1201         };
1202 
1203     // Static utility methods.
1204 
1205     private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
readSelectionOverrides(Parcel in)1206         readSelectionOverrides(Parcel in) {
1207       int renderersWithOverridesCount = in.readInt();
1208       SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides =
1209           new SparseArray<>(renderersWithOverridesCount);
1210       for (int i = 0; i < renderersWithOverridesCount; i++) {
1211         int rendererIndex = in.readInt();
1212         int overrideCount = in.readInt();
1213         Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
1214             new HashMap<>(overrideCount);
1215         for (int j = 0; j < overrideCount; j++) {
1216           TrackGroupArray trackGroups =
1217               Assertions.checkNotNull(in.readParcelable(TrackGroupArray.class.getClassLoader()));
1218           @Nullable
1219           SelectionOverride override = in.readParcelable(SelectionOverride.class.getClassLoader());
1220           overrides.put(trackGroups, override);
1221         }
1222         selectionOverrides.put(rendererIndex, overrides);
1223       }
1224       return selectionOverrides;
1225     }
1226 
writeSelectionOverridesToParcel( Parcel dest, SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides)1227     private static void writeSelectionOverridesToParcel(
1228         Parcel dest,
1229         SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides) {
1230       int renderersWithOverridesCount = selectionOverrides.size();
1231       dest.writeInt(renderersWithOverridesCount);
1232       for (int i = 0; i < renderersWithOverridesCount; i++) {
1233         int rendererIndex = selectionOverrides.keyAt(i);
1234         Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
1235             selectionOverrides.valueAt(i);
1236         int overrideCount = overrides.size();
1237         dest.writeInt(rendererIndex);
1238         dest.writeInt(overrideCount);
1239         for (Map.Entry<TrackGroupArray, @NullableType SelectionOverride> override :
1240             overrides.entrySet()) {
1241           dest.writeParcelable(override.getKey(), /* parcelableFlags= */ 0);
1242           dest.writeParcelable(override.getValue(), /* parcelableFlags= */ 0);
1243         }
1244       }
1245     }
1246 
areRendererDisabledFlagsEqual( SparseBooleanArray first, SparseBooleanArray second)1247     private static boolean areRendererDisabledFlagsEqual(
1248         SparseBooleanArray first, SparseBooleanArray second) {
1249       int firstSize = first.size();
1250       if (second.size() != firstSize) {
1251         return false;
1252       }
1253       // Only true values are put into rendererDisabledFlags, so we don't need to compare values.
1254       for (int indexInFirst = 0; indexInFirst < firstSize; indexInFirst++) {
1255         if (second.indexOfKey(first.keyAt(indexInFirst)) < 0) {
1256           return false;
1257         }
1258       }
1259       return true;
1260     }
1261 
areSelectionOverridesEqual( SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> first, SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> second)1262     private static boolean areSelectionOverridesEqual(
1263         SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> first,
1264         SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> second) {
1265       int firstSize = first.size();
1266       if (second.size() != firstSize) {
1267         return false;
1268       }
1269       for (int indexInFirst = 0; indexInFirst < firstSize; indexInFirst++) {
1270         int indexInSecond = second.indexOfKey(first.keyAt(indexInFirst));
1271         if (indexInSecond < 0
1272             || !areSelectionOverridesEqual(
1273                 first.valueAt(indexInFirst), second.valueAt(indexInSecond))) {
1274           return false;
1275         }
1276       }
1277       return true;
1278     }
1279 
areSelectionOverridesEqual( Map<TrackGroupArray, @NullableType SelectionOverride> first, Map<TrackGroupArray, @NullableType SelectionOverride> second)1280     private static boolean areSelectionOverridesEqual(
1281         Map<TrackGroupArray, @NullableType SelectionOverride> first,
1282         Map<TrackGroupArray, @NullableType SelectionOverride> second) {
1283       int firstSize = first.size();
1284       if (second.size() != firstSize) {
1285         return false;
1286       }
1287       for (Map.Entry<TrackGroupArray, @NullableType SelectionOverride> firstEntry :
1288           first.entrySet()) {
1289         TrackGroupArray key = firstEntry.getKey();
1290         if (!second.containsKey(key) || !Util.areEqual(firstEntry.getValue(), second.get(key))) {
1291           return false;
1292         }
1293       }
1294       return true;
1295     }
1296   }
1297 
1298   /** A track selection override. */
1299   public static final class SelectionOverride implements Parcelable {
1300 
1301     public final int groupIndex;
1302     public final int[] tracks;
1303     public final int length;
1304     public final int reason;
1305     public final int data;
1306 
1307     /**
1308      * @param groupIndex The overriding track group index.
1309      * @param tracks The overriding track indices within the track group.
1310      */
SelectionOverride(int groupIndex, int... tracks)1311     public SelectionOverride(int groupIndex, int... tracks) {
1312       this(groupIndex, tracks, C.SELECTION_REASON_MANUAL, /* data= */ 0);
1313     }
1314 
1315     /**
1316      * @param groupIndex The overriding track group index.
1317      * @param tracks The overriding track indices within the track group.
1318      * @param reason The reason for the override. One of the {@link C} SELECTION_REASON_ constants.
1319      * @param data Optional data associated with this override.
1320      */
SelectionOverride(int groupIndex, int[] tracks, int reason, int data)1321     public SelectionOverride(int groupIndex, int[] tracks, int reason, int data) {
1322       this.groupIndex = groupIndex;
1323       this.tracks = Arrays.copyOf(tracks, tracks.length);
1324       this.length = tracks.length;
1325       this.reason = reason;
1326       this.data = data;
1327       Arrays.sort(this.tracks);
1328     }
1329 
SelectionOverride(Parcel in)1330     /* package */ SelectionOverride(Parcel in) {
1331       groupIndex = in.readInt();
1332       length = in.readByte();
1333       tracks = new int[length];
1334       in.readIntArray(tracks);
1335       reason = in.readInt();
1336       data = in.readInt();
1337     }
1338 
1339     /** Returns whether this override contains the specified track index. */
containsTrack(int track)1340     public boolean containsTrack(int track) {
1341       for (int overrideTrack : tracks) {
1342         if (overrideTrack == track) {
1343           return true;
1344         }
1345       }
1346       return false;
1347     }
1348 
1349     @Override
hashCode()1350     public int hashCode() {
1351       int hash = 31 * groupIndex + Arrays.hashCode(tracks);
1352       hash = 31 * hash + reason;
1353       return 31 * hash + data;
1354     }
1355 
1356     @Override
equals(@ullable Object obj)1357     public boolean equals(@Nullable Object obj) {
1358       if (this == obj) {
1359         return true;
1360       }
1361       if (obj == null || getClass() != obj.getClass()) {
1362         return false;
1363       }
1364       SelectionOverride other = (SelectionOverride) obj;
1365       return groupIndex == other.groupIndex
1366           && Arrays.equals(tracks, other.tracks)
1367           && reason == other.reason
1368           && data == other.data;
1369     }
1370 
1371     // Parcelable implementation.
1372 
1373     @Override
describeContents()1374     public int describeContents() {
1375       return 0;
1376     }
1377 
1378     @Override
writeToParcel(Parcel dest, int flags)1379     public void writeToParcel(Parcel dest, int flags) {
1380       dest.writeInt(groupIndex);
1381       dest.writeInt(tracks.length);
1382       dest.writeIntArray(tracks);
1383       dest.writeInt(reason);
1384       dest.writeInt(data);
1385     }
1386 
1387     public static final Parcelable.Creator<SelectionOverride> CREATOR =
1388         new Parcelable.Creator<SelectionOverride>() {
1389 
1390           @Override
1391           public SelectionOverride createFromParcel(Parcel in) {
1392             return new SelectionOverride(in);
1393           }
1394 
1395           @Override
1396           public SelectionOverride[] newArray(int size) {
1397             return new SelectionOverride[size];
1398           }
1399         };
1400   }
1401 
1402   /**
1403    * If a dimension (i.e. width or height) of a video is greater or equal to this fraction of the
1404    * corresponding viewport dimension, then the video is considered as filling the viewport (in that
1405    * dimension).
1406    */
1407   private static final float FRACTION_TO_CONSIDER_FULLSCREEN = 0.98f;
1408   private static final int[] NO_TRACKS = new int[0];
1409   private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000;
1410 
1411   private final TrackSelection.Factory trackSelectionFactory;
1412   private final AtomicReference<Parameters> parametersReference;
1413 
1414   private boolean allowMultipleAdaptiveSelections;
1415 
1416   /** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */
1417   @Deprecated
1418   @SuppressWarnings("deprecation")
DefaultTrackSelector()1419   public DefaultTrackSelector() {
1420     this(new AdaptiveTrackSelection.Factory());
1421   }
1422 
1423   /**
1424    * @deprecated Use {@link #DefaultTrackSelector(Context)} instead. The bandwidth meter should be
1425    *     passed directly to the player in {@link
1426    *     com.google.android.exoplayer2.SimpleExoPlayer.Builder}.
1427    */
1428   @Deprecated
1429   @SuppressWarnings("deprecation")
DefaultTrackSelector(BandwidthMeter bandwidthMeter)1430   public DefaultTrackSelector(BandwidthMeter bandwidthMeter) {
1431     this(new AdaptiveTrackSelection.Factory(bandwidthMeter));
1432   }
1433 
1434   /** @deprecated Use {@link #DefaultTrackSelector(Context, TrackSelection.Factory)}. */
1435   @Deprecated
DefaultTrackSelector(TrackSelection.Factory trackSelectionFactory)1436   public DefaultTrackSelector(TrackSelection.Factory trackSelectionFactory) {
1437     this(Parameters.DEFAULT_WITHOUT_CONTEXT, trackSelectionFactory);
1438   }
1439 
1440   /** @param context Any {@link Context}. */
DefaultTrackSelector(Context context)1441   public DefaultTrackSelector(Context context) {
1442     this(context, new AdaptiveTrackSelection.Factory());
1443   }
1444 
1445   /**
1446    * @param context Any {@link Context}.
1447    * @param trackSelectionFactory A factory for {@link TrackSelection}s.
1448    */
DefaultTrackSelector(Context context, TrackSelection.Factory trackSelectionFactory)1449   public DefaultTrackSelector(Context context, TrackSelection.Factory trackSelectionFactory) {
1450     this(Parameters.getDefaults(context), trackSelectionFactory);
1451   }
1452 
1453   /**
1454    * @param parameters Initial {@link Parameters}.
1455    * @param trackSelectionFactory A factory for {@link TrackSelection}s.
1456    */
DefaultTrackSelector(Parameters parameters, TrackSelection.Factory trackSelectionFactory)1457   public DefaultTrackSelector(Parameters parameters, TrackSelection.Factory trackSelectionFactory) {
1458     this.trackSelectionFactory = trackSelectionFactory;
1459     parametersReference = new AtomicReference<>(parameters);
1460   }
1461 
1462   /**
1463    * Atomically sets the provided parameters for track selection.
1464    *
1465    * @param parameters The parameters for track selection.
1466    */
setParameters(Parameters parameters)1467   public void setParameters(Parameters parameters) {
1468     Assertions.checkNotNull(parameters);
1469     if (!parametersReference.getAndSet(parameters).equals(parameters)) {
1470       invalidate();
1471     }
1472   }
1473 
1474   /**
1475    * Atomically sets the provided parameters for track selection.
1476    *
1477    * @param parametersBuilder A builder from which to obtain the parameters for track selection.
1478    */
setParameters(ParametersBuilder parametersBuilder)1479   public void setParameters(ParametersBuilder parametersBuilder) {
1480     setParameters(parametersBuilder.build());
1481   }
1482 
1483   /**
1484    * Gets the current selection parameters.
1485    *
1486    * @return The current selection parameters.
1487    */
getParameters()1488   public Parameters getParameters() {
1489     return parametersReference.get();
1490   }
1491 
1492   /** Returns a new {@link ParametersBuilder} initialized with the current selection parameters. */
buildUponParameters()1493   public ParametersBuilder buildUponParameters() {
1494     return getParameters().buildUpon();
1495   }
1496 
1497   /**
1498    * Allows the creation of multiple adaptive track selections.
1499    *
1500    * <p>This method is experimental, and will be renamed or removed in a future release.
1501    */
experimental_allowMultipleAdaptiveSelections()1502   public void experimental_allowMultipleAdaptiveSelections() {
1503     this.allowMultipleAdaptiveSelections = true;
1504   }
1505 
1506   // MappingTrackSelector implementation.
1507 
1508   @Override
1509   protected final Pair<@NullableType RendererConfiguration[], @NullableType TrackSelection[]>
selectTracks( MappedTrackInfo mappedTrackInfo, @Capabilities int[][][] rendererFormatSupports, @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports)1510       selectTracks(
1511           MappedTrackInfo mappedTrackInfo,
1512           @Capabilities int[][][] rendererFormatSupports,
1513           @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports)
1514           throws ExoPlaybackException {
1515     Parameters params = parametersReference.get();
1516     int rendererCount = mappedTrackInfo.getRendererCount();
1517     TrackSelection.@NullableType Definition[] definitions =
1518         selectAllTracks(
1519             mappedTrackInfo,
1520             rendererFormatSupports,
1521             rendererMixedMimeTypeAdaptationSupports,
1522             params);
1523 
1524     // Apply track disabling and overriding.
1525     for (int i = 0; i < rendererCount; i++) {
1526       if (params.getRendererDisabled(i)) {
1527         definitions[i] = null;
1528         continue;
1529       }
1530       TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(i);
1531       if (params.hasSelectionOverride(i, rendererTrackGroups)) {
1532         SelectionOverride override = params.getSelectionOverride(i, rendererTrackGroups);
1533         definitions[i] =
1534             override == null
1535                 ? null
1536                 : new TrackSelection.Definition(
1537                     rendererTrackGroups.get(override.groupIndex),
1538                     override.tracks,
1539                     override.reason,
1540                     override.data);
1541       }
1542     }
1543 
1544     @NullableType
1545     TrackSelection[] rendererTrackSelections =
1546         trackSelectionFactory.createTrackSelections(definitions, getBandwidthMeter());
1547 
1548     // Initialize the renderer configurations to the default configuration for all renderers with
1549     // selections, and null otherwise.
1550     @NullableType RendererConfiguration[] rendererConfigurations =
1551         new RendererConfiguration[rendererCount];
1552     for (int i = 0; i < rendererCount; i++) {
1553       boolean forceRendererDisabled = params.getRendererDisabled(i);
1554       boolean rendererEnabled =
1555           !forceRendererDisabled
1556               && (mappedTrackInfo.getRendererType(i) == C.TRACK_TYPE_NONE
1557                   || rendererTrackSelections[i] != null);
1558       rendererConfigurations[i] = rendererEnabled ? RendererConfiguration.DEFAULT : null;
1559     }
1560 
1561     // Configure audio and video renderers to use tunneling if appropriate.
1562     maybeConfigureRenderersForTunneling(
1563         mappedTrackInfo,
1564         rendererFormatSupports,
1565         rendererConfigurations,
1566         rendererTrackSelections,
1567         params.tunnelingAudioSessionId);
1568 
1569     return Pair.create(rendererConfigurations, rendererTrackSelections);
1570   }
1571 
1572   // Track selection prior to overrides and disabled flags being applied.
1573 
1574   /**
1575    * Called from {@link #selectTracks(MappedTrackInfo, int[][][], int[])} to make a track selection
1576    * for each renderer, prior to overrides and disabled flags being applied.
1577    *
1578    * <p>The implementation should not account for overrides and disabled flags. Track selections
1579    * generated by this method will be overridden to account for these properties.
1580    *
1581    * @param mappedTrackInfo Mapped track information.
1582    * @param rendererFormatSupports The {@link Capabilities} for each mapped track, indexed by
1583    *     renderer, track group and track (in that order).
1584    * @param rendererMixedMimeTypeAdaptationSupports The {@link AdaptiveSupport} for mixed MIME type
1585    *     adaptation for the renderer.
1586    * @return The {@link TrackSelection.Definition}s for the renderers. A null entry indicates no
1587    *     selection was made.
1588    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
1589    */
selectAllTracks( MappedTrackInfo mappedTrackInfo, @Capabilities int[][][] rendererFormatSupports, @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports, Parameters params)1590   protected TrackSelection.@NullableType Definition[] selectAllTracks(
1591       MappedTrackInfo mappedTrackInfo,
1592       @Capabilities int[][][] rendererFormatSupports,
1593       @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports,
1594       Parameters params)
1595       throws ExoPlaybackException {
1596     int rendererCount = mappedTrackInfo.getRendererCount();
1597     TrackSelection.@NullableType Definition[] definitions =
1598         new TrackSelection.Definition[rendererCount];
1599 
1600     boolean seenVideoRendererWithMappedTracks = false;
1601     boolean selectedVideoTracks = false;
1602     for (int i = 0; i < rendererCount; i++) {
1603       if (C.TRACK_TYPE_VIDEO == mappedTrackInfo.getRendererType(i)) {
1604         if (!selectedVideoTracks) {
1605           definitions[i] =
1606               selectVideoTrack(
1607                   mappedTrackInfo.getTrackGroups(i),
1608                   rendererFormatSupports[i],
1609                   rendererMixedMimeTypeAdaptationSupports[i],
1610                   params,
1611                   /* enableAdaptiveTrackSelection= */ true);
1612           selectedVideoTracks = definitions[i] != null;
1613         }
1614         seenVideoRendererWithMappedTracks |= mappedTrackInfo.getTrackGroups(i).length > 0;
1615       }
1616     }
1617 
1618     AudioTrackScore selectedAudioTrackScore = null;
1619     String selectedAudioLanguage = null;
1620     int selectedAudioRendererIndex = C.INDEX_UNSET;
1621     for (int i = 0; i < rendererCount; i++) {
1622       if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) {
1623         boolean enableAdaptiveTrackSelection =
1624             allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks;
1625         Pair<TrackSelection.Definition, AudioTrackScore> audioSelection =
1626             selectAudioTrack(
1627                 mappedTrackInfo.getTrackGroups(i),
1628                 rendererFormatSupports[i],
1629                 rendererMixedMimeTypeAdaptationSupports[i],
1630                 params,
1631                 enableAdaptiveTrackSelection);
1632         if (audioSelection != null
1633             && (selectedAudioTrackScore == null
1634                 || audioSelection.second.compareTo(selectedAudioTrackScore) > 0)) {
1635           if (selectedAudioRendererIndex != C.INDEX_UNSET) {
1636             // We've already made a selection for another audio renderer, but it had a lower
1637             // score. Clear the selection for that renderer.
1638             definitions[selectedAudioRendererIndex] = null;
1639           }
1640           TrackSelection.Definition definition = audioSelection.first;
1641           definitions[i] = definition;
1642           // We assume that audio tracks in the same group have matching language.
1643           selectedAudioLanguage = definition.group.getFormat(definition.tracks[0]).language;
1644           selectedAudioTrackScore = audioSelection.second;
1645           selectedAudioRendererIndex = i;
1646         }
1647       }
1648     }
1649 
1650     TextTrackScore selectedTextTrackScore = null;
1651     int selectedTextRendererIndex = C.INDEX_UNSET;
1652     for (int i = 0; i < rendererCount; i++) {
1653       int trackType = mappedTrackInfo.getRendererType(i);
1654       switch (trackType) {
1655         case C.TRACK_TYPE_VIDEO:
1656         case C.TRACK_TYPE_AUDIO:
1657           // Already done. Do nothing.
1658           break;
1659         case C.TRACK_TYPE_TEXT:
1660           Pair<TrackSelection.Definition, TextTrackScore> textSelection =
1661               selectTextTrack(
1662                   mappedTrackInfo.getTrackGroups(i),
1663                   rendererFormatSupports[i],
1664                   params,
1665                   selectedAudioLanguage);
1666           if (textSelection != null
1667               && (selectedTextTrackScore == null
1668                   || textSelection.second.compareTo(selectedTextTrackScore) > 0)) {
1669             if (selectedTextRendererIndex != C.INDEX_UNSET) {
1670               // We've already made a selection for another text renderer, but it had a lower score.
1671               // Clear the selection for that renderer.
1672               definitions[selectedTextRendererIndex] = null;
1673             }
1674             definitions[i] = textSelection.first;
1675             selectedTextTrackScore = textSelection.second;
1676             selectedTextRendererIndex = i;
1677           }
1678           break;
1679         default:
1680           definitions[i] =
1681               selectOtherTrack(
1682                   trackType, mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params);
1683           break;
1684       }
1685     }
1686 
1687     return definitions;
1688   }
1689 
1690   // Video track selection implementation.
1691 
1692   /**
1693    * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
1694    * {@link TrackSelection} for a video renderer.
1695    *
1696    * @param groups The {@link TrackGroupArray} mapped to the renderer.
1697    * @param formatSupports The {@link Capabilities} for each mapped track, indexed by renderer,
1698    *     track group and track (in that order).
1699    * @param mixedMimeTypeAdaptationSupports The {@link AdaptiveSupport} for mixed MIME type
1700    *     adaptation for the renderer.
1701    * @param params The selector's current constraint parameters.
1702    * @param enableAdaptiveTrackSelection Whether adaptive track selection is allowed.
1703    * @return The {@link TrackSelection.Definition} for the renderer, or null if no selection was
1704    *     made.
1705    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
1706    */
1707   @Nullable
selectVideoTrack( TrackGroupArray groups, @Capabilities int[][] formatSupports, @AdaptiveSupport int mixedMimeTypeAdaptationSupports, Parameters params, boolean enableAdaptiveTrackSelection)1708   protected TrackSelection.Definition selectVideoTrack(
1709       TrackGroupArray groups,
1710       @Capabilities int[][] formatSupports,
1711       @AdaptiveSupport int mixedMimeTypeAdaptationSupports,
1712       Parameters params,
1713       boolean enableAdaptiveTrackSelection)
1714       throws ExoPlaybackException {
1715     TrackSelection.Definition definition = null;
1716     if (!params.forceHighestSupportedBitrate
1717         && !params.forceLowestBitrate
1718         && enableAdaptiveTrackSelection) {
1719       definition =
1720           selectAdaptiveVideoTrack(groups, formatSupports, mixedMimeTypeAdaptationSupports, params);
1721     }
1722     if (definition == null) {
1723       definition = selectFixedVideoTrack(groups, formatSupports, params);
1724     }
1725     return definition;
1726   }
1727 
1728   @Nullable
selectAdaptiveVideoTrack( TrackGroupArray groups, @Capabilities int[][] formatSupport, @AdaptiveSupport int mixedMimeTypeAdaptationSupports, Parameters params)1729   private static TrackSelection.Definition selectAdaptiveVideoTrack(
1730       TrackGroupArray groups,
1731       @Capabilities int[][] formatSupport,
1732       @AdaptiveSupport int mixedMimeTypeAdaptationSupports,
1733       Parameters params) {
1734     int requiredAdaptiveSupport =
1735         params.allowVideoNonSeamlessAdaptiveness
1736             ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
1737             : RendererCapabilities.ADAPTIVE_SEAMLESS;
1738     boolean allowMixedMimeTypes =
1739         params.allowVideoMixedMimeTypeAdaptiveness
1740             && (mixedMimeTypeAdaptationSupports & requiredAdaptiveSupport) != 0;
1741     for (int i = 0; i < groups.length; i++) {
1742       TrackGroup group = groups.get(i);
1743       int[] adaptiveTracks =
1744           getAdaptiveVideoTracksForGroup(
1745               group,
1746               formatSupport[i],
1747               allowMixedMimeTypes,
1748               requiredAdaptiveSupport,
1749               params.maxVideoWidth,
1750               params.maxVideoHeight,
1751               params.maxVideoFrameRate,
1752               params.maxVideoBitrate,
1753               params.viewportWidth,
1754               params.viewportHeight,
1755               params.viewportOrientationMayChange);
1756       if (adaptiveTracks.length > 0) {
1757         return new TrackSelection.Definition(group, adaptiveTracks);
1758       }
1759     }
1760     return null;
1761   }
1762 
getAdaptiveVideoTracksForGroup( TrackGroup group, @Capabilities int[] formatSupport, boolean allowMixedMimeTypes, int requiredAdaptiveSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoFrameRate, int maxVideoBitrate, int viewportWidth, int viewportHeight, boolean viewportOrientationMayChange)1763   private static int[] getAdaptiveVideoTracksForGroup(
1764       TrackGroup group,
1765       @Capabilities int[] formatSupport,
1766       boolean allowMixedMimeTypes,
1767       int requiredAdaptiveSupport,
1768       int maxVideoWidth,
1769       int maxVideoHeight,
1770       int maxVideoFrameRate,
1771       int maxVideoBitrate,
1772       int viewportWidth,
1773       int viewportHeight,
1774       boolean viewportOrientationMayChange) {
1775     if (group.length < 2) {
1776       return NO_TRACKS;
1777     }
1778 
1779     List<Integer> selectedTrackIndices = getViewportFilteredTrackIndices(group, viewportWidth,
1780         viewportHeight, viewportOrientationMayChange);
1781     if (selectedTrackIndices.size() < 2) {
1782       return NO_TRACKS;
1783     }
1784 
1785     String selectedMimeType = null;
1786     if (!allowMixedMimeTypes) {
1787       // Select the mime type for which we have the most adaptive tracks.
1788       HashSet<@NullableType String> seenMimeTypes = new HashSet<>();
1789       int selectedMimeTypeTrackCount = 0;
1790       for (int i = 0; i < selectedTrackIndices.size(); i++) {
1791         int trackIndex = selectedTrackIndices.get(i);
1792         String sampleMimeType = group.getFormat(trackIndex).sampleMimeType;
1793         if (seenMimeTypes.add(sampleMimeType)) {
1794           int countForMimeType =
1795               getAdaptiveVideoTrackCountForMimeType(
1796                   group,
1797                   formatSupport,
1798                   requiredAdaptiveSupport,
1799                   sampleMimeType,
1800                   maxVideoWidth,
1801                   maxVideoHeight,
1802                   maxVideoFrameRate,
1803                   maxVideoBitrate,
1804                   selectedTrackIndices);
1805           if (countForMimeType > selectedMimeTypeTrackCount) {
1806             selectedMimeType = sampleMimeType;
1807             selectedMimeTypeTrackCount = countForMimeType;
1808           }
1809         }
1810       }
1811     }
1812 
1813     // Filter by the selected mime type.
1814     filterAdaptiveVideoTrackCountForMimeType(
1815         group,
1816         formatSupport,
1817         requiredAdaptiveSupport,
1818         selectedMimeType,
1819         maxVideoWidth,
1820         maxVideoHeight,
1821         maxVideoFrameRate,
1822         maxVideoBitrate,
1823         selectedTrackIndices);
1824 
1825     return selectedTrackIndices.size() < 2 ? NO_TRACKS : Util.toArray(selectedTrackIndices);
1826   }
1827 
getAdaptiveVideoTrackCountForMimeType( TrackGroup group, @Capabilities int[] formatSupport, int requiredAdaptiveSupport, @Nullable String mimeType, int maxVideoWidth, int maxVideoHeight, int maxVideoFrameRate, int maxVideoBitrate, List<Integer> selectedTrackIndices)1828   private static int getAdaptiveVideoTrackCountForMimeType(
1829       TrackGroup group,
1830       @Capabilities int[] formatSupport,
1831       int requiredAdaptiveSupport,
1832       @Nullable String mimeType,
1833       int maxVideoWidth,
1834       int maxVideoHeight,
1835       int maxVideoFrameRate,
1836       int maxVideoBitrate,
1837       List<Integer> selectedTrackIndices) {
1838     int adaptiveTrackCount = 0;
1839     for (int i = 0; i < selectedTrackIndices.size(); i++) {
1840       int trackIndex = selectedTrackIndices.get(i);
1841       if (isSupportedAdaptiveVideoTrack(
1842           group.getFormat(trackIndex),
1843           mimeType,
1844           formatSupport[trackIndex],
1845           requiredAdaptiveSupport,
1846           maxVideoWidth,
1847           maxVideoHeight,
1848           maxVideoFrameRate,
1849           maxVideoBitrate)) {
1850         adaptiveTrackCount++;
1851       }
1852     }
1853     return adaptiveTrackCount;
1854   }
1855 
filterAdaptiveVideoTrackCountForMimeType( TrackGroup group, @Capabilities int[] formatSupport, int requiredAdaptiveSupport, @Nullable String mimeType, int maxVideoWidth, int maxVideoHeight, int maxVideoFrameRate, int maxVideoBitrate, List<Integer> selectedTrackIndices)1856   private static void filterAdaptiveVideoTrackCountForMimeType(
1857       TrackGroup group,
1858       @Capabilities int[] formatSupport,
1859       int requiredAdaptiveSupport,
1860       @Nullable String mimeType,
1861       int maxVideoWidth,
1862       int maxVideoHeight,
1863       int maxVideoFrameRate,
1864       int maxVideoBitrate,
1865       List<Integer> selectedTrackIndices) {
1866     for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) {
1867       int trackIndex = selectedTrackIndices.get(i);
1868       if (!isSupportedAdaptiveVideoTrack(
1869           group.getFormat(trackIndex),
1870           mimeType,
1871           formatSupport[trackIndex],
1872           requiredAdaptiveSupport,
1873           maxVideoWidth,
1874           maxVideoHeight,
1875           maxVideoFrameRate,
1876           maxVideoBitrate)) {
1877         selectedTrackIndices.remove(i);
1878       }
1879     }
1880   }
1881 
isSupportedAdaptiveVideoTrack( Format format, @Nullable String mimeType, @Capabilities int formatSupport, int requiredAdaptiveSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoFrameRate, int maxVideoBitrate)1882   private static boolean isSupportedAdaptiveVideoTrack(
1883       Format format,
1884       @Nullable String mimeType,
1885       @Capabilities int formatSupport,
1886       int requiredAdaptiveSupport,
1887       int maxVideoWidth,
1888       int maxVideoHeight,
1889       int maxVideoFrameRate,
1890       int maxVideoBitrate) {
1891     if ((format.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) {
1892       // Ignore trick-play tracks for now.
1893       return false;
1894     }
1895     return isSupported(formatSupport, false)
1896         && ((formatSupport & requiredAdaptiveSupport) != 0)
1897         && (mimeType == null || Util.areEqual(format.sampleMimeType, mimeType))
1898         && (format.width == Format.NO_VALUE || format.width <= maxVideoWidth)
1899         && (format.height == Format.NO_VALUE || format.height <= maxVideoHeight)
1900         && (format.frameRate == Format.NO_VALUE || format.frameRate <= maxVideoFrameRate)
1901         && (format.bitrate == Format.NO_VALUE || format.bitrate <= maxVideoBitrate);
1902   }
1903 
1904   @Nullable
selectFixedVideoTrack( TrackGroupArray groups, @Capabilities int[][] formatSupports, Parameters params)1905   private static TrackSelection.Definition selectFixedVideoTrack(
1906       TrackGroupArray groups, @Capabilities int[][] formatSupports, Parameters params) {
1907     TrackGroup selectedGroup = null;
1908     int selectedTrackIndex = 0;
1909     int selectedTrackScore = 0;
1910     int selectedBitrate = Format.NO_VALUE;
1911     int selectedPixelCount = Format.NO_VALUE;
1912     for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
1913       TrackGroup trackGroup = groups.get(groupIndex);
1914       List<Integer> selectedTrackIndices = getViewportFilteredTrackIndices(trackGroup,
1915           params.viewportWidth, params.viewportHeight, params.viewportOrientationMayChange);
1916       @Capabilities int[] trackFormatSupport = formatSupports[groupIndex];
1917       for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
1918         Format format = trackGroup.getFormat(trackIndex);
1919         if ((format.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) {
1920           // Ignore trick-play tracks for now.
1921           continue;
1922         }
1923         if (isSupported(trackFormatSupport[trackIndex],
1924             params.exceedRendererCapabilitiesIfNecessary)) {
1925           boolean isWithinConstraints =
1926               selectedTrackIndices.contains(trackIndex)
1927                   && (format.width == Format.NO_VALUE || format.width <= params.maxVideoWidth)
1928                   && (format.height == Format.NO_VALUE || format.height <= params.maxVideoHeight)
1929                   && (format.frameRate == Format.NO_VALUE
1930                       || format.frameRate <= params.maxVideoFrameRate)
1931                   && (format.bitrate == Format.NO_VALUE
1932                       || format.bitrate <= params.maxVideoBitrate);
1933           if (!isWithinConstraints && !params.exceedVideoConstraintsIfNecessary) {
1934             // Track should not be selected.
1935             continue;
1936           }
1937           int trackScore = isWithinConstraints ? 2 : 1;
1938           boolean isWithinCapabilities = isSupported(trackFormatSupport[trackIndex], false);
1939           if (isWithinCapabilities) {
1940             trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
1941           }
1942           boolean selectTrack = trackScore > selectedTrackScore;
1943           if (trackScore == selectedTrackScore) {
1944             int bitrateComparison = compareFormatValues(format.bitrate, selectedBitrate);
1945             if (params.forceLowestBitrate && bitrateComparison != 0) {
1946               // Use bitrate as a tie breaker, preferring the lower bitrate.
1947               selectTrack = bitrateComparison < 0;
1948             } else {
1949               // Use the pixel count as a tie breaker (or bitrate if pixel counts are tied). If
1950               // we're within constraints prefer a higher pixel count (or bitrate), else prefer a
1951               // lower count (or bitrate). If still tied then prefer the first track (i.e. the one
1952               // that's already selected).
1953               int formatPixelCount = format.getPixelCount();
1954               int comparisonResult = formatPixelCount != selectedPixelCount
1955                   ? compareFormatValues(formatPixelCount, selectedPixelCount)
1956                   : compareFormatValues(format.bitrate, selectedBitrate);
1957               selectTrack = isWithinCapabilities && isWithinConstraints
1958                   ? comparisonResult > 0 : comparisonResult < 0;
1959             }
1960           }
1961           if (selectTrack) {
1962             selectedGroup = trackGroup;
1963             selectedTrackIndex = trackIndex;
1964             selectedTrackScore = trackScore;
1965             selectedBitrate = format.bitrate;
1966             selectedPixelCount = format.getPixelCount();
1967           }
1968         }
1969       }
1970     }
1971     return selectedGroup == null
1972         ? null
1973         : new TrackSelection.Definition(selectedGroup, selectedTrackIndex);
1974   }
1975 
1976   // Audio track selection implementation.
1977 
1978   /**
1979    * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
1980    * {@link TrackSelection} for an audio renderer.
1981    *
1982    * @param groups The {@link TrackGroupArray} mapped to the renderer.
1983    * @param formatSupports The {@link Capabilities} for each mapped track, indexed by renderer,
1984    *     track group and track (in that order).
1985    * @param mixedMimeTypeAdaptationSupports The {@link AdaptiveSupport} for mixed MIME type
1986    *     adaptation for the renderer.
1987    * @param params The selector's current constraint parameters.
1988    * @param enableAdaptiveTrackSelection Whether adaptive track selection is allowed.
1989    * @return The {@link TrackSelection.Definition} and corresponding {@link AudioTrackScore}, or
1990    *     null if no selection was made.
1991    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
1992    */
1993   @SuppressWarnings("unused")
1994   @Nullable
1995   protected Pair<TrackSelection.Definition, AudioTrackScore> selectAudioTrack(
1996       TrackGroupArray groups,
1997       @Capabilities int[][] formatSupports,
1998       @AdaptiveSupport int mixedMimeTypeAdaptationSupports,
1999       Parameters params,
2000       boolean enableAdaptiveTrackSelection)
2001       throws ExoPlaybackException {
2002     int selectedTrackIndex = C.INDEX_UNSET;
2003     int selectedGroupIndex = C.INDEX_UNSET;
2004     AudioTrackScore selectedTrackScore = null;
2005     for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
2006       TrackGroup trackGroup = groups.get(groupIndex);
2007       @Capabilities int[] trackFormatSupport = formatSupports[groupIndex];
2008       for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
2009         if (isSupported(trackFormatSupport[trackIndex],
2010             params.exceedRendererCapabilitiesIfNecessary)) {
2011           Format format = trackGroup.getFormat(trackIndex);
2012           AudioTrackScore trackScore =
2013               new AudioTrackScore(format, params, trackFormatSupport[trackIndex]);
2014           if (!trackScore.isWithinConstraints && !params.exceedAudioConstraintsIfNecessary) {
2015             // Track should not be selected.
2016             continue;
2017           }
2018           if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
2019             selectedGroupIndex = groupIndex;
2020             selectedTrackIndex = trackIndex;
2021             selectedTrackScore = trackScore;
2022           }
2023         }
2024       }
2025     }
2026 
2027     if (selectedGroupIndex == C.INDEX_UNSET) {
2028       return null;
2029     }
2030 
2031     TrackGroup selectedGroup = groups.get(selectedGroupIndex);
2032 
2033     TrackSelection.Definition definition = null;
2034     if (!params.forceHighestSupportedBitrate
2035         && !params.forceLowestBitrate
2036         && enableAdaptiveTrackSelection) {
2037       // If the group of the track with the highest score allows it, try to enable adaptation.
2038       int[] adaptiveTracks =
2039           getAdaptiveAudioTracks(
2040               selectedGroup,
2041               formatSupports[selectedGroupIndex],
2042               selectedTrackIndex,
2043               params.maxAudioBitrate,
2044               params.allowAudioMixedMimeTypeAdaptiveness,
2045               params.allowAudioMixedSampleRateAdaptiveness,
2046               params.allowAudioMixedChannelCountAdaptiveness);
2047       if (adaptiveTracks.length > 1) {
2048         definition = new TrackSelection.Definition(selectedGroup, adaptiveTracks);
2049       }
2050     }
2051     if (definition == null) {
2052       // We didn't make an adaptive selection, so make a fixed one instead.
2053       definition = new TrackSelection.Definition(selectedGroup, selectedTrackIndex);
2054     }
2055 
2056     return Pair.create(definition, Assertions.checkNotNull(selectedTrackScore));
2057   }
2058 
2059   private static int[] getAdaptiveAudioTracks(
2060       TrackGroup group,
2061       @Capabilities int[] formatSupport,
2062       int primaryTrackIndex,
2063       int maxAudioBitrate,
2064       boolean allowMixedMimeTypeAdaptiveness,
2065       boolean allowMixedSampleRateAdaptiveness,
2066       boolean allowAudioMixedChannelCountAdaptiveness) {
2067     Format primaryFormat = group.getFormat(primaryTrackIndex);
2068     int[] adaptiveIndices = new int[group.length];
2069     int count = 0;
2070     for (int i = 0; i < group.length; i++) {
2071       if (i == primaryTrackIndex
2072           || isSupportedAdaptiveAudioTrack(
2073               group.getFormat(i),
2074               formatSupport[i],
2075               primaryFormat,
2076               maxAudioBitrate,
2077               allowMixedMimeTypeAdaptiveness,
2078               allowMixedSampleRateAdaptiveness,
2079               allowAudioMixedChannelCountAdaptiveness)) {
2080         adaptiveIndices[count++] = i;
2081       }
2082     }
2083     return Arrays.copyOf(adaptiveIndices, count);
2084   }
2085 
2086   private static boolean isSupportedAdaptiveAudioTrack(
2087       Format format,
2088       @Capabilities int formatSupport,
2089       Format primaryFormat,
2090       int maxAudioBitrate,
2091       boolean allowMixedMimeTypeAdaptiveness,
2092       boolean allowMixedSampleRateAdaptiveness,
2093       boolean allowAudioMixedChannelCountAdaptiveness) {
2094     return isSupported(formatSupport, /* allowExceedsCapabilities= */ false)
2095         && (format.bitrate == Format.NO_VALUE || format.bitrate <= maxAudioBitrate)
2096         && (allowAudioMixedChannelCountAdaptiveness
2097             || (format.channelCount != Format.NO_VALUE
2098                 && format.channelCount == primaryFormat.channelCount))
2099         && (allowMixedMimeTypeAdaptiveness
2100             || (format.sampleMimeType != null
2101                 && TextUtils.equals(format.sampleMimeType, primaryFormat.sampleMimeType)))
2102         && (allowMixedSampleRateAdaptiveness
2103             || (format.sampleRate != Format.NO_VALUE
2104                 && format.sampleRate == primaryFormat.sampleRate));
2105   }
2106 
2107   // Text track selection implementation.
2108 
2109   /**
2110    * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
2111    * {@link TrackSelection} for a text renderer.
2112    *
2113    * @param groups The {@link TrackGroupArray} mapped to the renderer.
2114    * @param formatSupport The {@link Capabilities} for each mapped track, indexed by renderer, track
2115    *     group and track (in that order).
2116    * @param params The selector's current constraint parameters.
2117    * @param selectedAudioLanguage The language of the selected audio track. May be null if the
2118    *     selected text track declares no language or no text track was selected.
2119    * @return The {@link TrackSelection.Definition} and corresponding {@link TextTrackScore}, or null
2120    *     if no selection was made.
2121    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
2122    */
2123   @Nullable
2124   protected Pair<TrackSelection.Definition, TextTrackScore> selectTextTrack(
2125       TrackGroupArray groups,
2126       @Capabilities int[][] formatSupport,
2127       Parameters params,
2128       @Nullable String selectedAudioLanguage)
2129       throws ExoPlaybackException {
2130     TrackGroup selectedGroup = null;
2131     int selectedTrackIndex = C.INDEX_UNSET;
2132     TextTrackScore selectedTrackScore = null;
2133     for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
2134       TrackGroup trackGroup = groups.get(groupIndex);
2135       @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
2136       for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
2137         if (isSupported(trackFormatSupport[trackIndex],
2138             params.exceedRendererCapabilitiesIfNecessary)) {
2139           Format format = trackGroup.getFormat(trackIndex);
2140           TextTrackScore trackScore =
2141               new TextTrackScore(
2142                   format, params, trackFormatSupport[trackIndex], selectedAudioLanguage);
2143           if (trackScore.isWithinConstraints
2144               && (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0)) {
2145             selectedGroup = trackGroup;
2146             selectedTrackIndex = trackIndex;
2147             selectedTrackScore = trackScore;
2148           }
2149         }
2150       }
2151     }
2152     return selectedGroup == null
2153         ? null
2154         : Pair.create(
2155             new TrackSelection.Definition(selectedGroup, selectedTrackIndex),
2156             Assertions.checkNotNull(selectedTrackScore));
2157   }
2158 
2159   // General track selection methods.
2160 
2161   /**
2162    * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
2163    * {@link TrackSelection} for a renderer whose type is neither video, audio or text.
2164    *
2165    * @param trackType The type of the renderer.
2166    * @param groups The {@link TrackGroupArray} mapped to the renderer.
2167    * @param formatSupport The {@link Capabilities} for each mapped track, indexed by renderer, track
2168    *     group and track (in that order).
2169    * @param params The selector's current constraint parameters.
2170    * @return The {@link TrackSelection} for the renderer, or null if no selection was made.
2171    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
2172    */
2173   @Nullable
2174   protected TrackSelection.Definition selectOtherTrack(
2175       int trackType, TrackGroupArray groups, @Capabilities int[][] formatSupport, Parameters params)
2176       throws ExoPlaybackException {
2177     TrackGroup selectedGroup = null;
2178     int selectedTrackIndex = 0;
2179     int selectedTrackScore = 0;
2180     for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
2181       TrackGroup trackGroup = groups.get(groupIndex);
2182       @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
2183       for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
2184         if (isSupported(trackFormatSupport[trackIndex],
2185             params.exceedRendererCapabilitiesIfNecessary)) {
2186           Format format = trackGroup.getFormat(trackIndex);
2187           boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
2188           int trackScore = isDefault ? 2 : 1;
2189           if (isSupported(trackFormatSupport[trackIndex], false)) {
2190             trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
2191           }
2192           if (trackScore > selectedTrackScore) {
2193             selectedGroup = trackGroup;
2194             selectedTrackIndex = trackIndex;
2195             selectedTrackScore = trackScore;
2196           }
2197         }
2198       }
2199     }
2200     return selectedGroup == null
2201         ? null
2202         : new TrackSelection.Definition(selectedGroup, selectedTrackIndex);
2203   }
2204 
2205   // Utility methods.
2206 
2207   /**
2208    * Determines whether tunneling should be enabled, replacing {@link RendererConfiguration}s in
2209    * {@code rendererConfigurations} with configurations that enable tunneling on the appropriate
2210    * renderers if so.
2211    *
2212    * @param mappedTrackInfo Mapped track information.
2213    * @param renderererFormatSupports The {@link Capabilities} for each mapped track, indexed by
2214    *     renderer, track group and track (in that order).
2215    * @param rendererConfigurations The renderer configurations. Configurations may be replaced with
2216    *     ones that enable tunneling as a result of this call.
2217    * @param trackSelections The renderer track selections.
2218    * @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
2219    *     C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
2220    */
2221   private static void maybeConfigureRenderersForTunneling(
2222       MappedTrackInfo mappedTrackInfo,
2223       @Capabilities int[][][] renderererFormatSupports,
2224       @NullableType RendererConfiguration[] rendererConfigurations,
2225       @NullableType TrackSelection[] trackSelections,
2226       int tunnelingAudioSessionId) {
2227     if (tunnelingAudioSessionId == C.AUDIO_SESSION_ID_UNSET) {
2228       return;
2229     }
2230     // Check whether we can enable tunneling. To enable tunneling we require exactly one audio and
2231     // one video renderer to support tunneling and have a selection.
2232     int tunnelingAudioRendererIndex = -1;
2233     int tunnelingVideoRendererIndex = -1;
2234     boolean enableTunneling = true;
2235     for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
2236       int rendererType = mappedTrackInfo.getRendererType(i);
2237       TrackSelection trackSelection = trackSelections[i];
2238       if ((rendererType == C.TRACK_TYPE_AUDIO || rendererType == C.TRACK_TYPE_VIDEO)
2239           && trackSelection != null) {
2240         if (rendererSupportsTunneling(
2241             renderererFormatSupports[i], mappedTrackInfo.getTrackGroups(i), trackSelection)) {
2242           if (rendererType == C.TRACK_TYPE_AUDIO) {
2243             if (tunnelingAudioRendererIndex != -1) {
2244               enableTunneling = false;
2245               break;
2246             } else {
2247               tunnelingAudioRendererIndex = i;
2248             }
2249           } else {
2250             if (tunnelingVideoRendererIndex != -1) {
2251               enableTunneling = false;
2252               break;
2253             } else {
2254               tunnelingVideoRendererIndex = i;
2255             }
2256           }
2257         }
2258       }
2259     }
2260     enableTunneling &= tunnelingAudioRendererIndex != -1 && tunnelingVideoRendererIndex != -1;
2261     if (enableTunneling) {
2262       RendererConfiguration tunnelingRendererConfiguration =
2263           new RendererConfiguration(tunnelingAudioSessionId);
2264       rendererConfigurations[tunnelingAudioRendererIndex] = tunnelingRendererConfiguration;
2265       rendererConfigurations[tunnelingVideoRendererIndex] = tunnelingRendererConfiguration;
2266     }
2267   }
2268 
2269   /**
2270    * Returns whether a renderer supports tunneling for a {@link TrackSelection}.
2271    *
2272    * @param formatSupports The {@link Capabilities} for each track, indexed by group index and track
2273    *     index (in that order).
2274    * @param trackGroups The {@link TrackGroupArray}s for the renderer.
2275    * @param selection The track selection.
2276    * @return Whether the renderer supports tunneling for the {@link TrackSelection}.
2277    */
2278   private static boolean rendererSupportsTunneling(
2279       @Capabilities int[][] formatSupports, TrackGroupArray trackGroups, TrackSelection selection) {
2280     if (selection == null) {
2281       return false;
2282     }
2283     int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup());
2284     for (int i = 0; i < selection.length(); i++) {
2285       @Capabilities
2286       int trackFormatSupport = formatSupports[trackGroupIndex][selection.getIndexInTrackGroup(i)];
2287       if (RendererCapabilities.getTunnelingSupport(trackFormatSupport)
2288           != RendererCapabilities.TUNNELING_SUPPORTED) {
2289         return false;
2290       }
2291     }
2292     return true;
2293   }
2294 
2295   /**
2296    * Compares two format values for order. A known value is considered greater than {@link
2297    * Format#NO_VALUE}.
2298    *
2299    * @param first The first value.
2300    * @param second The second value.
2301    * @return A negative integer if the first value is less than the second. Zero if they are equal.
2302    *     A positive integer if the first value is greater than the second.
2303    */
2304   private static int compareFormatValues(int first, int second) {
2305     return first == Format.NO_VALUE
2306         ? (second == Format.NO_VALUE ? 0 : -1)
2307         : (second == Format.NO_VALUE ? 1 : (first - second));
2308   }
2309 
2310   /**
2311    * Returns true if the {@link FormatSupport} in the given {@link Capabilities} is {@link
2312    * RendererCapabilities#FORMAT_HANDLED} or if {@code allowExceedsCapabilities} is set and the
2313    * format support is {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}.
2314    *
2315    * @param formatSupport {@link Capabilities}.
2316    * @param allowExceedsCapabilities Whether to return true if {@link FormatSupport} is {@link
2317    *     RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}.
2318    * @return True if {@link FormatSupport} is {@link RendererCapabilities#FORMAT_HANDLED}, or if
2319    *     {@code allowExceedsCapabilities} is set and the format support is {@link
2320    *     RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}.
2321    */
2322   protected static boolean isSupported(
2323       @Capabilities int formatSupport, boolean allowExceedsCapabilities) {
2324     @FormatSupport int maskedSupport = RendererCapabilities.getFormatSupport(formatSupport);
2325     return maskedSupport == RendererCapabilities.FORMAT_HANDLED || (allowExceedsCapabilities
2326         && maskedSupport == RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES);
2327   }
2328 
2329   /**
2330    * Normalizes the input string to null if it does not define a language, or returns it otherwise.
2331    *
2332    * @param language The string.
2333    * @return The string, optionally normalized to null if it does not define a language.
2334    */
2335   @Nullable
2336   protected static String normalizeUndeterminedLanguageToNull(@Nullable String language) {
2337     return TextUtils.isEmpty(language) || TextUtils.equals(language, C.LANGUAGE_UNDETERMINED)
2338         ? null
2339         : language;
2340   }
2341 
2342   /**
2343    * Returns a score for how well a language specified in a {@link Format} matches a given language.
2344    *
2345    * @param format The {@link Format}.
2346    * @param language The language, or null.
2347    * @param allowUndeterminedFormatLanguage Whether matches with an empty or undetermined format
2348    *     language tag are allowed.
2349    * @return A score of 4 if the languages match fully, a score of 3 if the languages match partly,
2350    *     a score of 2 if the languages don't match but belong to the same main language, a score of
2351    *     1 if the format language is undetermined and such a match is allowed, and a score of 0 if
2352    *     the languages don't match at all.
2353    */
2354   protected static int getFormatLanguageScore(
2355       Format format, @Nullable String language, boolean allowUndeterminedFormatLanguage) {
2356     if (!TextUtils.isEmpty(language) && language.equals(format.language)) {
2357       // Full literal match of non-empty languages, including matches of an explicit "und" query.
2358       return 4;
2359     }
2360     language = normalizeUndeterminedLanguageToNull(language);
2361     String formatLanguage = normalizeUndeterminedLanguageToNull(format.language);
2362     if (formatLanguage == null || language == null) {
2363       // At least one of the languages is undetermined.
2364       return allowUndeterminedFormatLanguage && formatLanguage == null ? 1 : 0;
2365     }
2366     if (formatLanguage.startsWith(language) || language.startsWith(formatLanguage)) {
2367       // Partial match where one language is a subset of the other (e.g. "zh-hans" and "zh-hans-hk")
2368       return 3;
2369     }
2370     String formatMainLanguage = Util.splitAtFirst(formatLanguage, "-")[0];
2371     String queryMainLanguage = Util.splitAtFirst(language, "-")[0];
2372     if (formatMainLanguage.equals(queryMainLanguage)) {
2373       // Partial match where only the main language tag is the same (e.g. "fr-fr" and "fr-ca")
2374       return 2;
2375     }
2376     return 0;
2377   }
2378 
2379   private static List<Integer> getViewportFilteredTrackIndices(TrackGroup group, int viewportWidth,
2380       int viewportHeight, boolean orientationMayChange) {
2381     // Initially include all indices.
2382     ArrayList<Integer> selectedTrackIndices = new ArrayList<>(group.length);
2383     for (int i = 0; i < group.length; i++) {
2384       selectedTrackIndices.add(i);
2385     }
2386 
2387     if (viewportWidth == Integer.MAX_VALUE || viewportHeight == Integer.MAX_VALUE) {
2388       // Viewport dimensions not set. Return the full set of indices.
2389       return selectedTrackIndices;
2390     }
2391 
2392     int maxVideoPixelsToRetain = Integer.MAX_VALUE;
2393     for (int i = 0; i < group.length; i++) {
2394       Format format = group.getFormat(i);
2395       // Keep track of the number of pixels of the selected format whose resolution is the
2396       // smallest to exceed the maximum size at which it can be displayed within the viewport.
2397       // We'll discard formats of higher resolution.
2398       if (format.width > 0 && format.height > 0) {
2399         Point maxVideoSizeInViewport = getMaxVideoSizeInViewport(orientationMayChange,
2400             viewportWidth, viewportHeight, format.width, format.height);
2401         int videoPixels = format.width * format.height;
2402         if (format.width >= (int) (maxVideoSizeInViewport.x * FRACTION_TO_CONSIDER_FULLSCREEN)
2403             && format.height >= (int) (maxVideoSizeInViewport.y * FRACTION_TO_CONSIDER_FULLSCREEN)
2404             && videoPixels < maxVideoPixelsToRetain) {
2405           maxVideoPixelsToRetain = videoPixels;
2406         }
2407       }
2408     }
2409 
2410     // Filter out formats that exceed maxVideoPixelsToRetain. These formats have an unnecessarily
2411     // high resolution given the size at which the video will be displayed within the viewport. Also
2412     // filter out formats with unknown dimensions, since we have some whose dimensions are known.
2413     if (maxVideoPixelsToRetain != Integer.MAX_VALUE) {
2414       for (int i = selectedTrackIndices.size() - 1; i >= 0; i--) {
2415         Format format = group.getFormat(selectedTrackIndices.get(i));
2416         int pixelCount = format.getPixelCount();
2417         if (pixelCount == Format.NO_VALUE || pixelCount > maxVideoPixelsToRetain) {
2418           selectedTrackIndices.remove(i);
2419         }
2420       }
2421     }
2422 
2423     return selectedTrackIndices;
2424   }
2425 
2426   /**
2427    * Given viewport dimensions and video dimensions, computes the maximum size of the video as it
2428    * will be rendered to fit inside of the viewport.
2429    */
2430   private static Point getMaxVideoSizeInViewport(boolean orientationMayChange, int viewportWidth,
2431       int viewportHeight, int videoWidth, int videoHeight) {
2432     if (orientationMayChange && (videoWidth > videoHeight) != (viewportWidth > viewportHeight)) {
2433       // Rotation is allowed, and the video will be larger in the rotated viewport.
2434       int tempViewportWidth = viewportWidth;
2435       viewportWidth = viewportHeight;
2436       viewportHeight = tempViewportWidth;
2437     }
2438 
2439     if (videoWidth * viewportHeight >= videoHeight * viewportWidth) {
2440       // Horizontal letter-boxing along top and bottom.
2441       return new Point(viewportWidth, Util.ceilDivide(viewportWidth * videoHeight, videoWidth));
2442     } else {
2443       // Vertical letter-boxing along edges.
2444       return new Point(Util.ceilDivide(viewportHeight * videoWidth, videoHeight), viewportHeight);
2445     }
2446   }
2447 
2448   /**
2449    * Compares two integers in a safe way avoiding potential overflow.
2450    *
2451    * @param first The first value.
2452    * @param second The second value.
2453    * @return A negative integer if the first value is less than the second. Zero if they are equal.
2454    *     A positive integer if the first value is greater than the second.
2455    */
2456   private static int compareInts(int first, int second) {
2457     return first > second ? 1 : (second > first ? -1 : 0);
2458   }
2459 
2460   /** Represents how well an audio track matches the selection {@link Parameters}. */
2461   protected static final class AudioTrackScore implements Comparable<AudioTrackScore> {
2462 
2463     /**
2464      * Whether the provided format is within the parameter constraints. If {@code false}, the format
2465      * should not be selected.
2466      */
2467     public final boolean isWithinConstraints;
2468 
2469     @Nullable private final String language;
2470     private final Parameters parameters;
2471     private final boolean isWithinRendererCapabilities;
2472     private final int preferredLanguageScore;
2473     private final int localeLanguageMatchIndex;
2474     private final int localeLanguageScore;
2475     private final boolean isDefaultSelectionFlag;
2476     private final int channelCount;
2477     private final int sampleRate;
2478     private final int bitrate;
2479 
AudioTrackScore(Format format, Parameters parameters, @Capabilities int formatSupport)2480     public AudioTrackScore(Format format, Parameters parameters, @Capabilities int formatSupport) {
2481       this.parameters = parameters;
2482       this.language = normalizeUndeterminedLanguageToNull(format.language);
2483       isWithinRendererCapabilities = isSupported(formatSupport, false);
2484       preferredLanguageScore =
2485           getFormatLanguageScore(
2486               format,
2487               parameters.preferredAudioLanguage,
2488               /* allowUndeterminedFormatLanguage= */ false);
2489       isDefaultSelectionFlag = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
2490       channelCount = format.channelCount;
2491       sampleRate = format.sampleRate;
2492       bitrate = format.bitrate;
2493       isWithinConstraints =
2494           (format.bitrate == Format.NO_VALUE || format.bitrate <= parameters.maxAudioBitrate)
2495               && (format.channelCount == Format.NO_VALUE
2496                   || format.channelCount <= parameters.maxAudioChannelCount);
2497       String[] localeLanguages = Util.getSystemLanguageCodes();
2498       int bestMatchIndex = Integer.MAX_VALUE;
2499       int bestMatchScore = 0;
2500       for (int i = 0; i < localeLanguages.length; i++) {
2501         int score =
2502             getFormatLanguageScore(
2503                 format, localeLanguages[i], /* allowUndeterminedFormatLanguage= */ false);
2504         if (score > 0) {
2505           bestMatchIndex = i;
2506           bestMatchScore = score;
2507           break;
2508         }
2509       }
2510       localeLanguageMatchIndex = bestMatchIndex;
2511       localeLanguageScore = bestMatchScore;
2512     }
2513 
2514     /**
2515      * Compares this score with another.
2516      *
2517      * @param other The other score to compare to.
2518      * @return A positive integer if this score is better than the other. Zero if they are equal. A
2519      *     negative integer if this score is worse than the other.
2520      */
2521     @Override
compareTo(AudioTrackScore other)2522     public int compareTo(AudioTrackScore other) {
2523       if (this.isWithinRendererCapabilities != other.isWithinRendererCapabilities) {
2524         return this.isWithinRendererCapabilities ? 1 : -1;
2525       }
2526       if (this.preferredLanguageScore != other.preferredLanguageScore) {
2527         return compareInts(this.preferredLanguageScore, other.preferredLanguageScore);
2528       }
2529       if (this.isWithinConstraints != other.isWithinConstraints) {
2530         return this.isWithinConstraints ? 1 : -1;
2531       }
2532       if (parameters.forceLowestBitrate) {
2533         int bitrateComparison = compareFormatValues(bitrate, other.bitrate);
2534         if (bitrateComparison != 0) {
2535           return bitrateComparison > 0 ? -1 : 1;
2536         }
2537       }
2538       if (this.isDefaultSelectionFlag != other.isDefaultSelectionFlag) {
2539         return this.isDefaultSelectionFlag ? 1 : -1;
2540       }
2541       if (this.localeLanguageMatchIndex != other.localeLanguageMatchIndex) {
2542         return -compareInts(this.localeLanguageMatchIndex, other.localeLanguageMatchIndex);
2543       }
2544       if (this.localeLanguageScore != other.localeLanguageScore) {
2545         return compareInts(this.localeLanguageScore, other.localeLanguageScore);
2546       }
2547       // If the formats are within constraints and renderer capabilities then prefer higher values
2548       // of channel count, sample rate and bit rate in that order. Otherwise, prefer lower values.
2549       int resultSign = isWithinConstraints && isWithinRendererCapabilities ? 1 : -1;
2550       if (this.channelCount != other.channelCount) {
2551         return resultSign * compareInts(this.channelCount, other.channelCount);
2552       }
2553       if (this.sampleRate != other.sampleRate) {
2554         return resultSign * compareInts(this.sampleRate, other.sampleRate);
2555       }
2556       if (Util.areEqual(this.language, other.language)) {
2557         // Only compare bit rates of tracks with the same or unknown language.
2558         return resultSign * compareInts(this.bitrate, other.bitrate);
2559       }
2560       return 0;
2561     }
2562   }
2563 
2564   /** Represents how well a text track matches the selection {@link Parameters}. */
2565   protected static final class TextTrackScore implements Comparable<TextTrackScore> {
2566 
2567     /**
2568      * Whether the provided format is within the parameter constraints. If {@code false}, the format
2569      * should not be selected.
2570      */
2571     public final boolean isWithinConstraints;
2572 
2573     private final boolean isWithinRendererCapabilities;
2574     private final boolean isDefault;
2575     private final boolean hasPreferredIsForcedFlag;
2576     private final int preferredLanguageScore;
2577     private final int preferredRoleFlagsScore;
2578     private final int selectedAudioLanguageScore;
2579     private final boolean hasCaptionRoleFlags;
2580 
TextTrackScore( Format format, Parameters parameters, @Capabilities int trackFormatSupport, @Nullable String selectedAudioLanguage)2581     public TextTrackScore(
2582         Format format,
2583         Parameters parameters,
2584         @Capabilities int trackFormatSupport,
2585         @Nullable String selectedAudioLanguage) {
2586       isWithinRendererCapabilities =
2587           isSupported(trackFormatSupport, /* allowExceedsCapabilities= */ false);
2588       int maskedSelectionFlags =
2589           format.selectionFlags & ~parameters.disabledTextTrackSelectionFlags;
2590       isDefault = (maskedSelectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
2591       boolean isForced = (maskedSelectionFlags & C.SELECTION_FLAG_FORCED) != 0;
2592       preferredLanguageScore =
2593           getFormatLanguageScore(
2594               format, parameters.preferredTextLanguage, parameters.selectUndeterminedTextLanguage);
2595       preferredRoleFlagsScore =
2596           Integer.bitCount(format.roleFlags & parameters.preferredTextRoleFlags);
2597       hasCaptionRoleFlags =
2598           (format.roleFlags & (C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND)) != 0;
2599       // Prefer non-forced to forced if a preferred text language has been matched. Where both are
2600       // provided the non-forced track will usually contain the forced subtitles as a subset.
2601       // Otherwise, prefer a forced track.
2602       hasPreferredIsForcedFlag =
2603           (preferredLanguageScore > 0 && !isForced) || (preferredLanguageScore == 0 && isForced);
2604       boolean selectedAudioLanguageUndetermined =
2605           normalizeUndeterminedLanguageToNull(selectedAudioLanguage) == null;
2606       selectedAudioLanguageScore =
2607           getFormatLanguageScore(format, selectedAudioLanguage, selectedAudioLanguageUndetermined);
2608       isWithinConstraints =
2609           preferredLanguageScore > 0
2610               || (parameters.preferredTextLanguage == null && preferredRoleFlagsScore > 0)
2611               || isDefault
2612               || (isForced && selectedAudioLanguageScore > 0);
2613     }
2614 
2615     /**
2616      * Compares this score with another.
2617      *
2618      * @param other The other score to compare to.
2619      * @return A positive integer if this score is better than the other. Zero if they are equal. A
2620      *     negative integer if this score is worse than the other.
2621      */
2622     @Override
compareTo(TextTrackScore other)2623     public int compareTo(TextTrackScore other) {
2624       if (this.isWithinRendererCapabilities != other.isWithinRendererCapabilities) {
2625         return this.isWithinRendererCapabilities ? 1 : -1;
2626       }
2627       if (this.preferredLanguageScore != other.preferredLanguageScore) {
2628         return compareInts(this.preferredLanguageScore, other.preferredLanguageScore);
2629       }
2630       if (this.preferredRoleFlagsScore != other.preferredRoleFlagsScore) {
2631         return compareInts(this.preferredRoleFlagsScore, other.preferredRoleFlagsScore);
2632       }
2633       if (this.isDefault != other.isDefault) {
2634         return this.isDefault ? 1 : -1;
2635       }
2636       if (this.hasPreferredIsForcedFlag != other.hasPreferredIsForcedFlag) {
2637         return this.hasPreferredIsForcedFlag ? 1 : -1;
2638       }
2639       if (this.selectedAudioLanguageScore != other.selectedAudioLanguageScore) {
2640         return compareInts(this.selectedAudioLanguageScore, other.selectedAudioLanguageScore);
2641       }
2642       if (preferredRoleFlagsScore == 0 && this.hasCaptionRoleFlags != other.hasCaptionRoleFlags) {
2643         return this.hasCaptionRoleFlags ? -1 : 1;
2644       }
2645       return 0;
2646     }
2647   }
2648 }
2649