Commit 339d99b8 by tonihei Committed by kim-vde

Add preferredVideoRoleFlags to TrackSelectionParameters.

And also tweak existing role flag logic to strictly prefer perfect
matches over partial matches.

Caveat: Video role flags only supported for fixed track selections
(same issue as Issue: google/ExoPlayer#9519).

Issue: google/ExoPlayer#9402
PiperOrigin-RevId: 412292835
parent e846e9f0
......@@ -80,6 +80,7 @@ public class TrackSelectionParameters implements Bundleable {
private int viewportHeight;
private boolean viewportOrientationMayChange;
private ImmutableList<String> preferredVideoMimeTypes;
private @C.RoleFlags int preferredVideoRoleFlags;
// Audio
private ImmutableList<String> 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<String> 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),
......
......@@ -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<VideoTrackScore> {
......@@ -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 =
......
......@@ -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);
}
/**
......@@ -1280,6 +1291,45 @@ public final class DefaultTrackSelectorTest {
}
/**
* 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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment