diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9921db756e..bb68a80e80 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,9 @@ ### dev-v2 (not yet released) +* Core library: + * Support preferred video role flags in track selection + ((#9402)[https://github.com/google/ExoPlayer/issues/9402]). * DRM: * Remove `playbackLooper` from `DrmSessionManager.(pre)acquireSession`. When a `DrmSessionManager` is used by an app in a custom `MediaSource`, @@ -545,8 +548,8 @@ * The most used methods of `Player`'s audio, video, text and metadata components have been added directly to `Player`. * Add `Player.getAvailableCommands`, `Player.isCommandAvailable` and - `Listener.onAvailableCommandsChanged` to query which commands - that can be executed on the player. + `Listener.onAvailableCommandsChanged` to query which commands that can + be executed on the player. * Add a `Player.Listener` interface to receive all player events. Component listeners and `EventListener` have been deprecated. * Add `Player.getMediaMetadata`, which returns a combined and structured @@ -555,8 +558,8 @@ * `Player.setPlaybackParameters` no longer accepts null, use `PlaybackParameters.DEFAULT` instead. * Report information about the old and the new playback positions to - `Listener.onPositionDiscontinuity`. Add `DISCONTINUITY_REASON_SKIP` - and `DISCONTINUITY_REASON_REMOVE` as discontinuity reasons, and rename + `Listener.onPositionDiscontinuity`. Add `DISCONTINUITY_REASON_SKIP` and + `DISCONTINUITY_REASON_REMOVE` as discontinuity reasons, and rename `DISCONTINUITY_REASON_PERIOD_TRANSITION` to `DISCONTINUITY_REASON_AUTO_TRANSITION`. Remove `DISCONTINUITY_REASON_AD_INSERTION`, for which @@ -611,8 +614,8 @@ dispatched for each track in each period. * Include the session state in DRM session-acquired listener methods. * UI: - * Add `PlayerNotificationManager.Builder`, with the ability to - specify which group the notification should belong to. + * Add `PlayerNotificationManager.Builder`, with the ability to specify + which group the notification should belong to. * Remove `setUseSensorRotation` from `PlayerView` and `StyledPlayerView`. Instead, cast the view returned by `getVideoSurfaceView` to `SphericalGLSurfaceView`, and then call `setUseSensorRotation` on the @@ -684,7 +687,8 @@ ### 2.13.3 (2021-04-14) -* Published via the Google Maven repository (i.e., google()) rather than JCenter. +* Published via the Google Maven repository (i.e., google()) rather than + JCenter. * Core: * Reset playback speed when live playback speed control becomes unused ([#8664](https://github.com/google/ExoPlayer/issues/8664)). @@ -839,8 +843,8 @@ * Remove `Player.setVideoDecoderOutputBufferRenderer` from Player API. Use `setVideoSurfaceView` and `clearVideoSurfaceView` instead. * Default `SingleSampleMediaSource.treatLoadErrorsAsEndOfStream` to `true` - so that errors loading external subtitle files do not cause playback - to fail ([#8430](https://github.com/google/ExoPlayer/issues/8430)). A + so that errors loading external subtitle files do not cause playback to + fail ([#8430](https://github.com/google/ExoPlayer/issues/8430)). A warning will be logged by `SingleSampleMediaPeriod` whenever a load error is treated as though the end of the stream has been reached. * Time out on release to prevent ANRs if an underlying platform call is @@ -921,9 +925,8 @@ ([#7847](https://github.com/google/ExoPlayer/issues/7847)). * Drop key and provision responses if `DefaultDrmSession` is released while waiting for the response. This prevents harmless log messages of - the form: - `IllegalStateException: sending message to a Handler on a dead thread` - ([#8328](https://github.com/google/ExoPlayer/issues/8328)). + the form: `IllegalStateException: sending message to a Handler on a dead + thread` ([#8328](https://github.com/google/ExoPlayer/issues/8328)). * Allow apps to fully customize DRM behaviour for each `MediaItem` by passing a `DrmSessionManagerProvider` to `MediaSourceFactory` ([#8466](https://github.com/google/ExoPlayer/issues/8466)). @@ -938,8 +941,8 @@ existing decoder instance for the new format, and if not then the reasons why. * Video: - * Fall back to AVC/HEVC decoders for Dolby Vision streams with level 10 - to 13 ([#8530](https://github.com/google/ExoPlayer/issues/8530)). + * Fall back to AVC/HEVC decoders for Dolby Vision streams with level 10 to + 13 ([#8530](https://github.com/google/ExoPlayer/issues/8530)). * Fix VP9 format capability checks on API level 23 and earlier. The platform does not correctly report the VP9 level supported by the decoder in this case, so we estimate it based on the decoder's maximum @@ -1021,8 +1024,8 @@ * `ExtractorsMediaSource.Factory.setMinLoadableRetryCount(int)`. Use `ExtractorsMediaSource.Factory.setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy)` instead. - * `FixedTrackSelection.Factory`. If you need to disable adaptive - selection in `DefaultTrackSelector`, enable the + * `FixedTrackSelection.Factory`. If you need to disable adaptive selection + in `DefaultTrackSelector`, enable the `DefaultTrackSelector.Parameters.forceHighestSupportedBitrate` flag. * `HlsMediaSource.Factory.setMinLoadableRetryCount(int)`. Use `HlsMediaSource.Factory.setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy)` @@ -1035,8 +1038,8 @@ `MappedTrackInfo.getUnmappedTrackGroups()` instead. * `MappedTrackInfo.length`. Use `MappedTrackInfo.getRendererCount()` instead. - * `Player.DefaultEventListener.onTimelineChanged(Timeline, Object)`. - Use `Player.EventListener.onTimelineChanged(Timeline, int)` instead. + * `Player.DefaultEventListener.onTimelineChanged(Timeline, Object)`. Use + `Player.EventListener.onTimelineChanged(Timeline, int)` instead. * `Player.setAudioAttributes(AudioAttributes)`. Use `Player.AudioComponent.setAudioAttributes(AudioAttributes, boolean)` instead. @@ -1052,8 +1055,8 @@ `SimpleExoPlayer.removeVideoListener(VideoListener)` instead. * `SimpleExoPlayer.getAudioStreamType()`. Use `SimpleExoPlayer.getAudioAttributes()` instead. - * `SimpleExoPlayer.setAudioDebugListener(AudioRendererEventListener)`. - Use `SimpleExoPlayer.addAnalyticsListener(AnalyticsListener)` instead. + * `SimpleExoPlayer.setAudioDebugListener(AudioRendererEventListener)`. Use + `SimpleExoPlayer.addAnalyticsListener(AnalyticsListener)` instead. * `SimpleExoPlayer.setAudioStreamType(int)`. Use `SimpleExoPlayer.setAudioAttributes(AudioAttributes)` instead. * `SimpleExoPlayer.setMetadataOutput(MetadataOutput)`. Use @@ -1064,12 +1067,11 @@ * `SimpleExoPlayer.setPlaybackParams(PlaybackParams)`. Use `SimpleExoPlayer.setPlaybackParameters(PlaybackParameters)` instead. * `SimpleExoPlayer.setTextOutput(TextOutput)`. Use - `SimpleExoPlayer.addTextOutput(TextOutput)` instead. If your - application is calling `SimpleExoPlayer.setTextOutput(null)`, make sure - to replace this call with a call to - `SimpleExoPlayer.removeTextOutput(TextOutput)`. - * `SimpleExoPlayer.setVideoDebugListener(VideoRendererEventListener)`. - Use `SimpleExoPlayer.addAnalyticsListener(AnalyticsListener)` instead. + `SimpleExoPlayer.addTextOutput(TextOutput)` instead. If your application + is calling `SimpleExoPlayer.setTextOutput(null)`, make sure to replace + this call with a call to `SimpleExoPlayer.removeTextOutput(TextOutput)`. + * `SimpleExoPlayer.setVideoDebugListener(VideoRendererEventListener)`. Use + `SimpleExoPlayer.addAnalyticsListener(AnalyticsListener)` instead. * `SimpleExoPlayer.setVideoListener(VideoListener)`. Use `SimpleExoPlayer.addVideoListener(VideoListener)` instead. If your application is calling `SimpleExoPlayer.setVideoListener(null)`, make @@ -1093,7 +1095,7 @@ `SsMediaSource.Factory.setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy)` instead. -### 2.12.3 (2021-01-13) ### +### 2.12.3 (2021-01-13) * Core library: * Fix `MediaCodecRenderer` issue where empty streams would fail to play in @@ -1130,7 +1132,7 @@ fix a deadlock while creating PlaybackStateCompat internally. ([#8011](https://github.com/google/ExoPlayer/issues/8011)). -### 2.12.2 (2020-12-01) ### +### 2.12.2 (2020-12-01) * Core library: * Suppress exceptions from registering and unregistering the stream volume @@ -1191,7 +1193,7 @@ * Allow to remove all playlist items that makes the player reset ([#8047](https://github.com/google/ExoPlayer/issues/8047)). -### 2.12.1 (2020-10-23) ### +### 2.12.1 (2020-10-23) * Core library: * Fix issue where `Player.setMediaItems` would ignore its `resetPosition` @@ -1230,7 +1232,7 @@ ([#8058](https://github.com/google/ExoPlayer/issues/8058)). * Extractors: * MP4: - * Add support for `_mp2` boxes + * Add support for `_mp2` boxes ([#7967](https://github.com/google/ExoPlayer/issues/7967)). * Fix playback of files containing `pcm_alaw` or `pcm_mulaw` audio tracks, by enabling sample rechunking for such tracks. @@ -1266,11 +1268,11 @@ ([#7961](https://github.com/google/ExoPlayer/issues/7961)). * Fix incorrect truncation of large cue point positions ([#8067](https://github.com/google/ExoPlayer/issues/8067)). - * Upgrade IMA SDK dependency to 3.20.1. This brings in a fix for - companion ads rendering when targeting API 29 + * Upgrade IMA SDK dependency to 3.20.1. This brings in a fix for companion + ads rendering when targeting API 29 ([#6432](https://github.com/google/ExoPlayer/issues/6432)). -### 2.12.0 (2020-09-11) ### +### 2.12.0 (2020-09-11) To learn more about what's new in 2.12, read the corresponding [blog post](https://medium.com/google-exoplayer/exoplayer-2-12-whats-new-e43ef8ff72e7). @@ -1301,8 +1303,7 @@ To learn more about what's new in 2.12, read the corresponding * Remove `PlaybackParameters.skipSilence`, and replace it with `AudioComponent.setSkipSilenceEnabled`. This method is also available on `SimpleExoPlayer`. An - `AudioListener.onSkipSilenceEnabledChanged` callback is also - added. + `AudioListener.onSkipSilenceEnabledChanged` callback is also added. * Add `TextComponent.getCurrentCues` to get the current cues. This method is also available on `SimpleExoPlayer`. The current cues are no longer automatically forwarded to a `TextOutput` when it's added @@ -1630,20 +1631,19 @@ To learn more about what's new in 2.12, read the corresponding * Add support for downloading DRM-protected content using offline Widevine licenses. -### 2.11.8 (2020-08-25) ### +### 2.11.8 (2020-08-25) -* Fix distorted playback of floating point audio when samples exceed the - `[-1, 1]` nominal range. +* Fix distorted playback of floating point audio when samples exceed the `[-1, + 1]` nominal range. * MP4: * Add support for `piff` and `isml` brands ([#7584](https://github.com/google/ExoPlayer/issues/7584)). * Fix playback of very short MP4 files. * FMP4: - * Fix `saiz` and `senc` sample count checks, resolving a "length - mismatch" `ParserException` when playing certain protected FMP4 streams + * Fix `saiz` and `senc` sample count checks, resolving a "length mismatch" + `ParserException` when playing certain protected FMP4 streams ([#7592](https://github.com/google/ExoPlayer/issues/7592)). - * Fix handling of `traf` boxes containing multiple `sbgp` or `sgpd` - boxes. + * Fix handling of `traf` boxes containing multiple `sbgp` or `sgpd` boxes. * FLV: Ignore `SCRIPTDATA` segments with invalid name types, rather than failing playback ([#7675](https://github.com/google/ExoPlayer/issues/7675)). * Better infer the content type of `.ism` and `.isml` streaming URLs. @@ -1656,12 +1656,12 @@ To learn more about what's new in 2.12, read the corresponding * Demo app: Fix playback of ClearKey protected content on API level 26 and earlier ([#7735](https://github.com/google/ExoPlayer/issues/7735)). -### 2.11.7 (2020-06-29) ### +### 2.11.7 (2020-06-29) * IMA extension: Fix the way postroll "content complete" notifications are handled to avoid repeatedly refreshing the timeline after playback ends. -### 2.11.6 (2020-06-19) ### +### 2.11.6 (2020-06-19) * UI: Prevent `PlayerView` from temporarily hiding the video surface when seeking to an unprepared period within the current window. For example when @@ -1676,14 +1676,14 @@ To learn more about what's new in 2.12, read the corresponding ([#7508](https://github.com/google/ExoPlayer/issues/7508)). * Fix a bug where the number of ads in an ad group couldn't change ([#7477](https://github.com/google/ExoPlayer/issues/7477)). - * Work around unexpected `pauseAd`/`stopAd` for ads that have preloaded - on seeking to another position + * Work around unexpected `pauseAd`/`stopAd` for ads that have preloaded on + seeking to another position ([#7492](https://github.com/google/ExoPlayer/issues/7492)). * Fix incorrect rounding of ad cue points. * Fix handling of postrolls preloading ([#7518](https://github.com/google/ExoPlayer/issues/7518)). -### 2.11.5 (2020-06-05) ### +### 2.11.5 (2020-06-05) * Improve the smoothness of video playback immediately after starting, seeking or resuming a playback @@ -1691,8 +1691,8 @@ To learn more about what's new in 2.12, read the corresponding * Add `SilenceMediaSource.Factory` to support tags. * Enable the configuration of `SilenceSkippingAudioProcessor` ([#6705](https://github.com/google/ExoPlayer/issues/6705)). -* Fix bug where `PlayerMessages` throw an exception after `MediaSources` - are removed from the playlist +* Fix bug where `PlayerMessages` throw an exception after `MediaSources` are + removed from the playlist ([#7278](https://github.com/google/ExoPlayer/issues/7278)). * Fix "Not allowed to start service" `IllegalStateException` in `DownloadService` @@ -1724,13 +1724,11 @@ To learn more about what's new in 2.12, read the corresponding ([#7303](https://github.com/google/ExoPlayer/issues/7303)). * Add `showScrubber` and `hideScrubber` methods to `DefaultTimeBar`. * Text: - * Use anti-aliasing and bitmap filtering when displaying bitmap - subtitles. + * Use anti-aliasing and bitmap filtering when displaying bitmap subtitles. * Fix `SubtitlePainter` to render `EDGE_TYPE_OUTLINE` using the correct color. * IMA extension: - * Upgrade to IMA SDK version 3.19.0, and migrate to new - preloading APIs + * Upgrade to IMA SDK version 3.19.0, and migrate to new preloading APIs ([#6429](https://github.com/google/ExoPlayer/issues/6429)). This fixes several issues involving preloading and handling of ad loading error cases: ([#4140](https://github.com/google/ExoPlayer/issues/4140), diff --git a/library/common/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionParameters.java b/library/common/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionParameters.java index 8080451246..f114c81500 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionParameters.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionParameters.java @@ -80,6 +80,7 @@ public class TrackSelectionParameters implements Bundleable { private int viewportHeight; private boolean viewportOrientationMayChange; private ImmutableList preferredVideoMimeTypes; + private @C.RoleFlags int preferredVideoRoleFlags; // Audio private ImmutableList preferredAudioLanguages; private @C.RoleFlags int preferredAudioRoleFlags; @@ -111,6 +112,7 @@ public class TrackSelectionParameters implements Bundleable { viewportHeight = Integer.MAX_VALUE; viewportOrientationMayChange = true; preferredVideoMimeTypes = ImmutableList.of(); + preferredVideoRoleFlags = 0; // Audio preferredAudioLanguages = ImmutableList.of(); preferredAudioRoleFlags = 0; @@ -183,6 +185,10 @@ public class TrackSelectionParameters implements Bundleable { firstNonNull( bundle.getStringArray(keyForField(FIELD_PREFERRED_VIDEO_MIMETYPES)), new String[0])); + preferredVideoRoleFlags = + bundle.getInt( + keyForField(FIELD_PREFERRED_VIDEO_ROLE_FLAGS), + DEFAULT_WITHOUT_CONTEXT.preferredVideoRoleFlags); // Audio String[] preferredAudioLanguages1 = firstNonNull( @@ -261,6 +267,7 @@ public class TrackSelectionParameters implements Bundleable { viewportHeight = parameters.viewportHeight; viewportOrientationMayChange = parameters.viewportOrientationMayChange; preferredVideoMimeTypes = parameters.preferredVideoMimeTypes; + preferredVideoRoleFlags = parameters.preferredVideoRoleFlags; // Audio preferredAudioLanguages = parameters.preferredAudioLanguages; preferredAudioRoleFlags = parameters.preferredAudioRoleFlags; @@ -441,6 +448,17 @@ public class TrackSelectionParameters implements Bundleable { return this; } + /** + * Sets the preferred {@link C.RoleFlags} for video tracks. + * + * @param preferredVideoRoleFlags Preferred video role flags. + * @return This builder. + */ + public Builder setPreferredVideoRoleFlags(@C.RoleFlags int preferredVideoRoleFlags) { + this.preferredVideoRoleFlags = preferredVideoRoleFlags; + return this; + } + // Audio /** @@ -770,6 +788,11 @@ public class TrackSelectionParameters implements Bundleable { * no preference. The default is an empty list. */ public final ImmutableList preferredVideoMimeTypes; + /** + * The preferred {@link C.RoleFlags} for video tracks. {@code 0} selects the default track if + * there is one, or the first track if there's no default. The default value is {@code 0}. + */ + public final @C.RoleFlags int preferredVideoRoleFlags; // Audio /** * The preferred languages for audio and forced text tracks as IETF BCP 47 conformant tags in @@ -853,6 +876,7 @@ public class TrackSelectionParameters implements Bundleable { this.viewportHeight = builder.viewportHeight; this.viewportOrientationMayChange = builder.viewportOrientationMayChange; this.preferredVideoMimeTypes = builder.preferredVideoMimeTypes; + this.preferredVideoRoleFlags = builder.preferredVideoRoleFlags; // Audio this.preferredAudioLanguages = builder.preferredAudioLanguages; this.preferredAudioRoleFlags = builder.preferredAudioRoleFlags; @@ -898,6 +922,7 @@ public class TrackSelectionParameters implements Bundleable { && viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight && preferredVideoMimeTypes.equals(other.preferredVideoMimeTypes) + && preferredVideoRoleFlags == other.preferredVideoRoleFlags // Audio && preferredAudioLanguages.equals(other.preferredAudioLanguages) && preferredAudioRoleFlags == other.preferredAudioRoleFlags @@ -930,6 +955,7 @@ public class TrackSelectionParameters implements Bundleable { result = 31 * result + viewportWidth; result = 31 * result + viewportHeight; result = 31 * result + preferredVideoMimeTypes.hashCode(); + result = 31 * result + preferredVideoRoleFlags; // Audio result = 31 * result + preferredAudioLanguages.hashCode(); result = 31 * result + preferredAudioRoleFlags; @@ -978,6 +1004,7 @@ public class TrackSelectionParameters implements Bundleable { FIELD_SELECTION_OVERRIDE_KEYS, FIELD_SELECTION_OVERRIDE_VALUES, FIELD_DISABLED_TRACK_TYPE, + FIELD_PREFERRED_VIDEO_ROLE_FLAGS }) private @interface FieldNumber {} @@ -1006,6 +1033,7 @@ public class TrackSelectionParameters implements Bundleable { private static final int FIELD_SELECTION_OVERRIDE_KEYS = 23; private static final int FIELD_SELECTION_OVERRIDE_VALUES = 24; private static final int FIELD_DISABLED_TRACK_TYPE = 25; + private static final int FIELD_PREFERRED_VIDEO_ROLE_FLAGS = 26; @Override public Bundle toBundle() { @@ -1027,6 +1055,7 @@ public class TrackSelectionParameters implements Bundleable { bundle.putStringArray( keyForField(FIELD_PREFERRED_VIDEO_MIMETYPES), preferredVideoMimeTypes.toArray(new String[0])); + bundle.putInt(keyForField(FIELD_PREFERRED_VIDEO_ROLE_FLAGS), preferredVideoRoleFlags); // Audio bundle.putStringArray( keyForField(FIELD_PREFERRED_AUDIO_LANGUAGES), diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index d82ac79add..24ff480b5e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -27,6 +27,7 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.Bundleable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C.FormatSupport; +import com.google.android.exoplayer2.C.RoleFlags; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Renderer; @@ -368,6 +369,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { return this; } + @Override + public DefaultTrackSelector.ParametersBuilder setPreferredVideoRoleFlags( + @RoleFlags int preferredVideoRoleFlags) { + super.setPreferredVideoRoleFlags(preferredVideoRoleFlags); + return this; + } + // Audio @Override @@ -2468,6 +2476,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { } } + private static int getRoleFlagMatchScore(int trackRoleFlags, int preferredRoleFlags) { + if (trackRoleFlags != 0 && trackRoleFlags == preferredRoleFlags) { + // Prefer perfect match over partial matches. + return Integer.MAX_VALUE; + } + return Integer.bitCount(trackRoleFlags & preferredRoleFlags); + } + /** Represents how well a video track matches the selection {@link Parameters}. */ protected static final class VideoTrackScore implements Comparable { @@ -2483,6 +2499,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int bitrate; private final int pixelCount; private final int preferredMimeTypeMatchIndex; + private final int preferredRoleFlagsScore; + private final boolean hasMainOrNoRoleFlag; public VideoTrackScore( Format format, @@ -2510,6 +2528,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { isSupported(formatSupport, /* allowExceedsCapabilities= */ false); bitrate = format.bitrate; pixelCount = format.getPixelCount(); + preferredRoleFlagsScore = + getRoleFlagMatchScore(format.roleFlags, parameters.preferredVideoRoleFlags); + hasMainOrNoRoleFlag = format.roleFlags == 0 || (format.roleFlags & C.ROLE_FLAG_MAIN) != 0; int bestMimeTypeMatchIndex = Integer.MAX_VALUE; for (int i = 0; i < parameters.preferredVideoMimeTypes.size(); i++) { if (format.sampleMimeType != null @@ -2537,6 +2558,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { : FORMAT_VALUE_ORDERING.reverse(); return ComparisonChain.start() .compareFalseFirst(this.isWithinRendererCapabilities, other.isWithinRendererCapabilities) + .compare(this.preferredRoleFlagsScore, other.preferredRoleFlagsScore) + .compareFalseFirst(this.hasMainOrNoRoleFlag, other.hasMainOrNoRoleFlag) .compareFalseFirst(this.isWithinMaxConstraints, other.isWithinMaxConstraints) .compareFalseFirst(this.isWithinMinConstraints, other.isWithinMinConstraints) .compare( @@ -2568,6 +2591,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int preferredLanguageScore; private final int preferredLanguageIndex; private final int preferredRoleFlagsScore; + private final boolean hasMainOrNoRoleFlag; private final int localeLanguageMatchIndex; private final int localeLanguageScore; private final boolean isDefaultSelectionFlag; @@ -2598,7 +2622,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { preferredLanguageIndex = bestLanguageIndex; preferredLanguageScore = bestLanguageScore; preferredRoleFlagsScore = - Integer.bitCount(format.roleFlags & parameters.preferredAudioRoleFlags); + getRoleFlagMatchScore(format.roleFlags, parameters.preferredAudioRoleFlags); + hasMainOrNoRoleFlag = format.roleFlags == 0 || (format.roleFlags & C.ROLE_FLAG_MAIN) != 0; isDefaultSelectionFlag = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; channelCount = format.channelCount; sampleRate = format.sampleRate; @@ -2656,6 +2681,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { Ordering.natural().reverse()) .compare(this.preferredLanguageScore, other.preferredLanguageScore) .compare(this.preferredRoleFlagsScore, other.preferredRoleFlagsScore) + .compareFalseFirst(this.hasMainOrNoRoleFlag, other.hasMainOrNoRoleFlag) .compareFalseFirst(this.isWithinConstraints, other.isWithinConstraints) .compare( this.preferredMimeTypeMatchIndex, @@ -2732,7 +2758,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { preferredLanguageIndex = bestLanguageIndex; preferredLanguageScore = bestLanguageScore; preferredRoleFlagsScore = - Integer.bitCount(format.roleFlags & parameters.preferredTextRoleFlags); + getRoleFlagMatchScore(format.roleFlags, parameters.preferredTextRoleFlags); hasCaptionRoleFlags = (format.roleFlags & (C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND)) != 0; boolean selectedAudioLanguageUndetermined = diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java index 5209adb8dc..e7bf61f9ba 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java @@ -594,7 +594,7 @@ public final class DefaultTrackSelectorTest { } /** - * Tests that track selector will select audio track with the highest number of matching role + * Tests that track selector will select the audio track with the highest number of matching role * flags given by {@link Parameters}. */ @Test @@ -619,6 +619,17 @@ public final class DefaultTrackSelectorTest { periodId, TIMELINE); assertFixedSelection(result.selections[0], trackGroups, moreRoleFlags); + + // Also verify that exact match between parameters and tracks is preferred. + trackSelector.setParameters( + defaultParameters.buildUpon().setPreferredAudioRoleFlags(C.ROLE_FLAG_CAPTION)); + result = + trackSelector.selectTracks( + new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, + trackGroups, + periodId, + TIMELINE); + assertFixedSelection(result.selections[0], trackGroups, lessRoleFlags); } /** @@ -1279,6 +1290,45 @@ public final class DefaultTrackSelectorTest { assertFixedSelection(result.selections[1], trackGroups, german); } + /** + * Tests that track selector will select the text track with the highest number of matching role + * flags given by {@link Parameters}. + */ + @Test + public void selectTracks_withPreferredTextRoleFlags_selectPreferredTrack() throws Exception { + Format.Builder formatBuilder = TEXT_FORMAT.buildUpon(); + Format noRoleFlags = formatBuilder.build(); + Format lessRoleFlags = formatBuilder.setRoleFlags(C.ROLE_FLAG_CAPTION).build(); + Format moreRoleFlags = + formatBuilder + .setRoleFlags(C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_COMMENTARY | C.ROLE_FLAG_DUB) + .build(); + TrackGroupArray trackGroups = wrapFormats(noRoleFlags, moreRoleFlags, lessRoleFlags); + + trackSelector.setParameters( + defaultParameters + .buildUpon() + .setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_COMMENTARY)); + TrackSelectorResult result = + trackSelector.selectTracks( + new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, + trackGroups, + periodId, + TIMELINE); + assertFixedSelection(result.selections[0], trackGroups, moreRoleFlags); + + // Also verify that exact match between parameters and tracks is preferred. + trackSelector.setParameters( + defaultParameters.buildUpon().setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)); + result = + trackSelector.selectTracks( + new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, + trackGroups, + periodId, + TIMELINE); + assertFixedSelection(result.selections[0], trackGroups, lessRoleFlags); + } + /** * Tests that track selector will select the lowest bitrate supported audio track when {@link * Parameters#forceLowestBitrate} is set. @@ -1809,6 +1859,39 @@ public final class DefaultTrackSelectorTest { assertFixedSelection(result.selections[0], trackGroups, formatAv1); } + /** + * Tests that track selector will select the video track with the highest number of matching role + * flags given by {@link Parameters}. + */ + @Test + public void selectTracks_withPreferredVideoRoleFlags_selectPreferredTrack() throws Exception { + Format.Builder formatBuilder = VIDEO_FORMAT.buildUpon(); + Format noRoleFlags = formatBuilder.build(); + Format lessRoleFlags = formatBuilder.setRoleFlags(C.ROLE_FLAG_CAPTION).build(); + Format moreRoleFlags = + formatBuilder + .setRoleFlags(C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_COMMENTARY | C.ROLE_FLAG_DUB) + .build(); + TrackGroupArray trackGroups = wrapFormats(noRoleFlags, moreRoleFlags, lessRoleFlags); + + trackSelector.setParameters( + defaultParameters + .buildUpon() + .setPreferredVideoRoleFlags(C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_COMMENTARY)); + TrackSelectorResult result = + trackSelector.selectTracks( + new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroups, periodId, TIMELINE); + assertFixedSelection(result.selections[0], trackGroups, moreRoleFlags); + + // Also verify that exact match between parameters and tracks is preferred. + trackSelector.setParameters( + defaultParameters.buildUpon().setPreferredVideoRoleFlags(C.ROLE_FLAG_CAPTION)); + result = + trackSelector.selectTracks( + new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroups, periodId, TIMELINE); + assertFixedSelection(result.selections[0], trackGroups, lessRoleFlags); + } + @Test public void selectTracks_withPreferredAudioMimeTypes_selectsTrackWithPreferredMimeType() throws Exception {