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
This commit is contained in:
parent
a520e7f559
commit
a7d7c7b73a
@ -79,6 +79,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;
|
||||
@ -185,6 +187,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(
|
||||
@ -263,6 +269,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;
|
||||
@ -444,6 +451,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
|
||||
|
||||
/**
|
||||
@ -775,6 +793,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
|
||||
@ -859,6 +882,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;
|
||||
@ -904,6 +928,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
|
||||
@ -936,6 +961,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;
|
||||
@ -984,6 +1010,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 {}
|
||||
|
||||
@ -1012,6 +1039,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;
|
||||
|
||||
@UnstableApi
|
||||
@Override
|
||||
@ -1034,6 +1062,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 androidx.media3.common.Bundleable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.C.FormatSupport;
|
||||
import androidx.media3.common.C.RoleFlags;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.TrackGroup;
|
||||
@ -373,6 +374,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultTrackSelector.ParametersBuilder setPreferredVideoRoleFlags(
|
||||
@RoleFlags int preferredVideoRoleFlags) {
|
||||
super.setPreferredVideoRoleFlags(preferredVideoRoleFlags);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
||||
@Override
|
||||
@ -2476,6 +2484,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> {
|
||||
|
||||
@ -2491,6 +2507,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,
|
||||
@ -2518,6 +2536,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
|
||||
@ -2545,6 +2566,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(
|
||||
@ -2576,6 +2599,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;
|
||||
@ -2606,7 +2630,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;
|
||||
@ -2664,6 +2689,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,
|
||||
@ -2740,7 +2766,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 =
|
||||
|
@ -596,7 +596,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
|
||||
@ -621,6 +621,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1281,6 +1292,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.
|
||||
@ -1811,6 +1861,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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user