Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions libraries/common/src/main/java/androidx/media3/common/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
* <li>{@link #language}
* <li>{@link #selectionFlags}
* <li>{@link #roleFlags}
* <li>{@link #selectionPriority}
* <li>{@link #averageBitrate}
* <li>{@link #peakBitrate}
* <li>{@link #codecs}
Expand Down Expand Up @@ -155,6 +156,7 @@ public static final class Builder {
@Nullable private String language;
private @C.SelectionFlags int selectionFlags;
private @C.RoleFlags int roleFlags;
private float selectionPriority;
private @C.AuxiliaryTrackType int auxiliaryTrackType;
private int averageBitrate;
private int peakBitrate;
Expand Down Expand Up @@ -219,6 +221,7 @@ public static final class Builder {
/** Creates a new instance with default values. */
public Builder() {
labels = ImmutableList.of();
selectionPriority = NO_VALUE;
averageBitrate = NO_VALUE;
peakBitrate = NO_VALUE;
// Sample specific.
Expand Down Expand Up @@ -263,6 +266,7 @@ private Builder(Format format) {
this.language = format.language;
this.selectionFlags = format.selectionFlags;
this.roleFlags = format.roleFlags;
this.selectionPriority = format.selectionPriority;
this.averageBitrate = format.averageBitrate;
this.peakBitrate = format.peakBitrate;
this.codecs = format.codecs;
Expand Down Expand Up @@ -404,6 +408,18 @@ public Builder setRoleFlags(@C.RoleFlags int roleFlags) {
return this;
}

/**
* Sets {@link Format#selectionPriority}. The default value is {@link Format#NO_VALUE}.
*
* @param selectionPriority The {@link Format#selectionPriority}.
* @return The builder.
*/
@CanIgnoreReturnValue
public Builder setSelectionPriority(float selectionPriority) {
this.selectionPriority = selectionPriority;
return this;
}

/**
* Sets {@link Format#auxiliaryTrackType}. The default value is {@link
* C#AUXILIARY_TRACK_TYPE_UNDEFINED}.
Expand Down Expand Up @@ -988,6 +1004,16 @@ public Format build() {
/** Track role flags. */
public final @C.RoleFlags int roleFlags;

/**
* A relative preference among formats in the same {@link TrackGroup}. A higher value indicates a
* more preferred format, or {@link #NO_VALUE} if unset.
*
* <p>Populated from the HLS {@code SCORE} attribute on {@code EXT-X-STREAM-INF} and {@code
* EXT-X-I-FRAME-STREAM-INF}, and from the DASH {@code @selectionPriority} attribute on {@code
* Representation} and {@code AdaptationSet}.
*/
@UnstableApi public final float selectionPriority;

/** The auxiliary track type. */
@UnstableApi public final @C.AuxiliaryTrackType int auxiliaryTrackType;

Expand Down Expand Up @@ -1291,6 +1317,7 @@ private Format(Builder builder) {
"Auxiliary track type must only be set to a value other than AUXILIARY_TRACK_TYPE_UNDEFINED"
+ " only when ROLE_FLAG_AUXILIARY is set");
roleFlags = builder.roleFlags;
selectionPriority = builder.selectionPriority;
auxiliaryTrackType = builder.auxiliaryTrackType;
averageBitrate = builder.averageBitrate;
peakBitrate = builder.peakBitrate;
Expand Down Expand Up @@ -1372,6 +1399,7 @@ public Format withManifestFormatInfo(Format manifestFormat) {

// Use manifest value only.
@Nullable String id = manifestFormat.id;
float selectionPriority = manifestFormat.selectionPriority;
int tileCountHorizontal = manifestFormat.tileCountHorizontal;
int tileCountVertical = manifestFormat.tileCountVertical;

Expand Down Expand Up @@ -1428,6 +1456,7 @@ public Format withManifestFormatInfo(Format manifestFormat) {
.setLanguage(language)
.setSelectionFlags(selectionFlags)
.setRoleFlags(roleFlags)
.setSelectionPriority(selectionPriority)
.setAverageBitrate(averageBitrate)
.setPeakBitrate(peakBitrate)
.setCodecs(codecs)
Expand Down Expand Up @@ -1500,6 +1529,7 @@ public int hashCode() {
result = 31 * result + (language == null ? 0 : language.hashCode());
result = 31 * result + selectionFlags;
result = 31 * result + roleFlags;
result = 31 * result + Float.floatToIntBits(selectionPriority);
result = 31 * result + auxiliaryTrackType;
result = 31 * result + averageBitrate;
result = 31 * result + peakBitrate;
Expand Down Expand Up @@ -1588,6 +1618,7 @@ public boolean equals(@Nullable Object obj) {
&& tileCountVertical == other.tileCountVertical
&& cryptoType == other.cryptoType
&& Float.compare(frameRate, other.frameRate) == 0
&& Float.compare(selectionPriority, other.selectionPriority) == 0
&& Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0
&& Objects.equals(id, other.id)
&& Objects.equals(label, other.label)
Expand Down Expand Up @@ -1646,6 +1677,9 @@ public static String toLogString(@Nullable Format format) {
if (format.bitrate != NO_VALUE) {
builder.append(", bitrate=").append(format.bitrate);
}
if (format.selectionPriority != NO_VALUE) {
builder.append(", selectionPriority=").append(format.selectionPriority);
}
if (format.codecs != null) {
builder.append(", codecs=").append(format.codecs);
}
Expand Down Expand Up @@ -1783,6 +1817,7 @@ public static String toLogString(@Nullable Format format) {
private static final String FIELD_CHANNEL_MASK = Util.intToStringMaxRadix(38);
private static final String FIELD_MIRROR_HORIZONTAL = Util.intToStringMaxRadix(39);
private static final String FIELD_PIXEL_FORMAT = Util.intToStringMaxRadix(40);
private static final String FIELD_SELECTION_PRIORITY = Util.intToStringMaxRadix(41);

/** Returns a {@link Bundle} representing the information stored in this object. */
@UnstableApi
Expand All @@ -1795,6 +1830,7 @@ public Bundle toBundle() {
bundle.putString(FIELD_LANGUAGE, language);
bundle.putInt(FIELD_SELECTION_FLAGS, selectionFlags);
bundle.putInt(FIELD_ROLE_FLAGS, roleFlags);
bundle.putFloat(FIELD_SELECTION_PRIORITY, selectionPriority);
if (auxiliaryTrackType != DEFAULT.auxiliaryTrackType) {
bundle.putInt(FIELD_AUXILIARY_TRACK_TYPE, auxiliaryTrackType);
}
Expand Down Expand Up @@ -1868,6 +1904,8 @@ public static Format fromBundle(Bundle bundle) {
.setLanguage(defaultIfNull(bundle.getString(FIELD_LANGUAGE), DEFAULT.language))
.setSelectionFlags(bundle.getInt(FIELD_SELECTION_FLAGS, DEFAULT.selectionFlags))
.setRoleFlags(bundle.getInt(FIELD_ROLE_FLAGS, DEFAULT.roleFlags))
.setSelectionPriority(
bundle.getFloat(FIELD_SELECTION_PRIORITY, DEFAULT.selectionPriority))
.setAuxiliaryTrackType(
bundle.getInt(FIELD_AUXILIARY_TRACK_TYPE, DEFAULT.auxiliaryTrackType))
.setAverageBitrate(bundle.getInt(FIELD_AVERAGE_BITRATE, DEFAULT.averageBitrate))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ public void formatBuild_withChannelCountAndChannelMaskSetButNoMatch_throwsExcept
assertThrows(IllegalStateException.class, () -> builder.build());
}

@Test
public void withManifestFormatInfo_preservesManifestSelectionPriority() {
Format manifestFormat = new Format.Builder().setSelectionPriority(3f).build();
Format sampleFormat = new Format.Builder().setSelectionPriority(Format.NO_VALUE).build();

Format format = sampleFormat.withManifestFormatInfo(manifestFormat);

assertThat(format.selectionPriority).isEqualTo(3f);
}

private static Format createTestFormat() {
byte[] initData1 = new byte[] {1, 2, 3};
byte[] initData2 = new byte[] {4, 5, 6};
Expand Down Expand Up @@ -151,6 +161,7 @@ private static Format createTestFormat() {
.setLanguage("language")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.setRoleFlags(C.ROLE_FLAG_MAIN)
.setSelectionPriority(2f)
.setAverageBitrate(1024)
.setPeakBitrate(2048)
.setCodecs("codec")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ public class HlsMultivariantPlaylistParserTest {
+ "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n";

private static final String PLAYLIST_WITH_SCORE =
" #EXTM3U \n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"avc1.66.30\","
+ "RESOLUTION=304x128,SCORE=1.5\n"
+ "http://example.com/low.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8940000,CODECS=\"avc1.66.30\","
+ "RESOLUTION=1920x1080,SCORE=2.0\n"
+ "http://example.com/high.m3u8\n";

private static final String PLAYLIST_WITH_DOLBY_VISION =
" #EXTM3U \n"
+ "\n"
Expand Down Expand Up @@ -381,6 +390,15 @@ public class HlsMultivariantPlaylistParserTest {
+ "8940000/index.m3u8\n"
+ "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1313400,RESOLUTION=1920x1080,CODECS=\"avc1.640028\",URI=\"iframe_1313400/index.m3u8\"\n";

private static final String PLAYLIST_WITH_IFRAME_VARIANT_SCORE =
"#EXTM3U\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8940000,RESOLUTION=1920x1080,"
+ "CODECS=\"avc1.640028\"\n"
+ "8940000/index.m3u8\n"
+ "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1313400,SCORE=4.5,"
+ "RESOLUTION=1920x1080,CODECS=\"avc1.640028\","
+ "URI=\"iframe_1313400/index.m3u8\"\n";

@Test
public void parseMultivariantPlaylist_withSimple_success() throws IOException {
HlsMultivariantPlaylist multivariantPlaylist =
Expand Down Expand Up @@ -435,6 +453,17 @@ public void parseMultivariantPlaylist_withSimple_success() throws IOException {
assertThat(multivariantPlaylist.contentSteeringInfo).isNull();
}

@Test
public void parseMultivariantPlaylist_withoutScore_setsSelectionPriorityToNoValue()
throws IOException {
HlsMultivariantPlaylist multivariantPlaylist =
parseMultivariantPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);

for (HlsMultivariantPlaylist.Variant variant : multivariantPlaylist.variants) {
assertThat(variant.format.selectionPriority).isEqualTo((float) Format.NO_VALUE);
}
}

@Test
public void parseMultivariantPlaylist_withAverageBandwidth_success() throws IOException {
HlsMultivariantPlaylist multivariantPlaylist =
Expand All @@ -446,6 +475,16 @@ public void parseMultivariantPlaylist_withAverageBandwidth_success() throws IOEx
assertThat(variants.get(1).format.bitrate).isEqualTo(1280000);
}

@Test
public void parseMultivariantPlaylist_withScore_success() throws IOException {
HlsMultivariantPlaylist multivariantPlaylist =
parseMultivariantPlaylist(PLAYLIST_URI, PLAYLIST_WITH_SCORE);

List<HlsMultivariantPlaylist.Variant> variants = multivariantPlaylist.variants;
assertThat(variants.get(0).format.selectionPriority).isEqualTo(1.5f);
assertThat(variants.get(1).format.selectionPriority).isEqualTo(2.0f);
}

@Test
public void parseMultivariantPlaylist_withDolbyVisionProfile10_success() throws IOException {
HlsMultivariantPlaylist multivariantPlaylist =
Expand Down Expand Up @@ -892,6 +931,17 @@ public void testIFrameVariant() throws IOException {
.isEqualTo(C.ROLE_FLAG_TRICK_PLAY);
}

@Test
public void parseMultivariantPlaylist_withIFrameStreamInfScore_success() throws IOException {
HlsMultivariantPlaylist playlist =
parseMultivariantPlaylist(PLAYLIST_URI, PLAYLIST_WITH_IFRAME_VARIANT_SCORE);

assertThat(playlist.variants).hasSize(2);
assertThat(playlist.variants.get(0).format.selectionPriority)
.isEqualTo((float) Format.NO_VALUE);
assertThat(playlist.variants.get(1).format.selectionPriority).isEqualTo(4.5f);
}

private static Metadata createExtXStreamInfMetadata(HlsTrackMetadataEntry.VariantInfo... infos) {
return new Metadata(
new HlsTrackMetadataEntry(/* groupId= */ null, /* name= */ null, Arrays.asList(infos)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public static final class DeltaUpdateException extends IOException {}
Pattern.compile("SUPPLEMENTAL-CODECS=" + ATTR_QUOTED_STRING_VALUE_PATTERN);
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
private static final Pattern REGEX_FRAME_RATE = Pattern.compile("FRAME-RATE=([\\d\\.]+)\\b");
private static final Pattern REGEX_SCORE = Pattern.compile("SCORE=([\\d\\.]+)\\b");
private static final Pattern REGEX_SERVER_URI =
Pattern.compile("SERVER-URI=" + ATTR_QUOTED_STRING_VALUE_PATTERN);
private static final Pattern REGEX_PATHWAY_ID =
Expand Down Expand Up @@ -571,6 +572,12 @@ private static HlsMultivariantPlaylist parseMultivariantPlaylist(
if (frameRateString != null) {
frameRate = Float.parseFloat(frameRateString);
}
float selectionPriority = Format.NO_VALUE;
String selectionPriorityString =
parseOptionalStringAttr(line, REGEX_SCORE, variableDefinitions, matcherCache);
if (selectionPriorityString != null) {
selectionPriority = Float.parseFloat(selectionPriorityString);
}
@Nullable
String pathwayId =
parseOptionalStringAttr(line, REGEX_PATHWAY_ID, variableDefinitions, matcherCache);
Expand Down Expand Up @@ -610,6 +617,7 @@ private static HlsMultivariantPlaylist parseMultivariantPlaylist(
.setWidth(width)
.setHeight(height)
.setFrameRate(frameRate)
.setSelectionPriority(selectionPriority)
.setRoleFlags(roleFlags)
.setColorInfo(colorInfo)
.build();
Expand Down