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