Skip to content

Commit 7b5522f

Browse files
copybara-githubSheenaChhabra
authored andcommitted
Merge pull request #1054 from jekopena:main
PiperOrigin-RevId: 619573181 (cherry picked from commit 8fe7033)
1 parent 4caed3c commit 7b5522f

File tree

33 files changed

+352
-16
lines changed

33 files changed

+352
-16
lines changed

RELEASENOTES.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Unreleased changes
44

5+
* Common Library:
6+
* Add `Format.labels` to allow localized or other alternative labels.
57
* ExoPlayer:
68
* Fix issue where `PreloadMediaPeriod` cannot retain the streams when it
79
is preloaded again.
@@ -28,7 +30,7 @@
2830
from WAV files ([#1117](https://github.com/androidx/media/pull/1117)).
2931
* MP3: Populate `Format.averageBitrate` from metadata frames such as
3032
`XING` and `VBRI`.
31-
* Audio:
33+
* Audio:
3234
* Allow renderer recovery by disabling offload if audio track fails to
3335
initialize in offload mode.
3436
* Video:
@@ -60,10 +62,13 @@
6062
* Fix issue where `MediaMetadata` with just non-null `extras` is not
6163
transmitted between media controllers and sessions
6264
([#1176](https://github.com/androidx/media/issues/1176)).
63-
* UI:
65+
* UI:
6466
* Fallback to include audio track language name if `Locale` cannot
6567
identify a display name
6668
([#988](https://github.com/androidx/media/issues/988)).
69+
* DASH Extension:
70+
* Populate all `Label` elements from the manifest into `Format.labels`
71+
([#1054](https://github.com/androidx/media/pull/1054)).
6772
* RTSP Extension:
6873
* Skip empty session information values (i-tags) in SDP parsing
6974
([#1087](https://github.com/androidx/media/issues/1087)).

libraries/common/src/main/java/androidx/media3/common/Format.java

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
*/
1616
package androidx.media3.common;
1717

18+
import static androidx.media3.common.util.Assertions.checkState;
1819
import static java.lang.annotation.ElementType.TYPE_USE;
1920

2021
import android.os.Bundle;
22+
import android.text.TextUtils;
2123
import androidx.annotation.IntDef;
2224
import androidx.annotation.Nullable;
2325
import androidx.media3.common.util.BundleCollectionUtil;
2426
import androidx.media3.common.util.UnstableApi;
2527
import androidx.media3.common.util.Util;
2628
import com.google.common.base.Joiner;
29+
import com.google.common.collect.ImmutableList;
2730
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2831
import java.lang.annotation.Documented;
2932
import java.lang.annotation.Retention;
@@ -50,6 +53,7 @@
5053
*
    5154
    *
  • {@link #id}
  • 5255
    *
  • {@link #label}
  • 56+
    *
  • {@link #labels}
  • 5357
    *
  • {@link #language}
  • 5458
    *
  • {@link #selectionFlags}
  • 5559
    *
  • {@link #roleFlags}
  • @@ -137,6 +141,7 @@ public static final class Builder {
    137141

    138142
    @Nullable private String id;
    139143
    @Nullable private String label;
    144+
    private List<Label> labels;
    140145
    @Nullable private String language;
    141146
    private @C.SelectionFlags int selectionFlags;
    142147
    private @C.RoleFlags int roleFlags;
    @@ -192,6 +197,7 @@ public static final class Builder {
    192197

    193198
    /** Creates a new instance with default values. */
    194199
    public Builder() {
    200+
    labels = ImmutableList.of();
    195201
    averageBitrate = NO_VALUE;
    196202
    peakBitrate = NO_VALUE;
    197203
    // Sample specific.
    @@ -225,6 +231,7 @@ public Builder() {
    225231
    private Builder(Format format) {
    226232
    this.id = format.id;
    227233
    this.label = format.label;
    234+
    this.labels = format.labels;
    228235
    this.language = format.language;
    229236
    this.selectionFlags = format.selectionFlags;
    230237
    this.roleFlags = format.roleFlags;
    @@ -293,6 +300,9 @@ public Builder setId(int id) {
    293300
    /**
    294301
    * Sets {@link Format#label}. The default value is {@code null}.
    295302
    *
    303+
    *

    If both this default label and a list of {@link #setLabels labels} are set, this default

    304+
    * label must be part of label list.
    305+
    *
    296306
    * @param label The {@link Format#label}.
    297307
    * @return The builder.
    298308
    */
    @@ -302,6 +312,21 @@ public Builder setLabel(@Nullable String label) {
    302312
    return this;
    303313
    }
    304314

    315+
    /**
    316+
    * Sets {@link Format#labels}. The default value is an empty list.
    317+
    *
    318+
    *

    If both the default {@linkplain #setLabel label} and this list are set, the default label

    319+
    * must be part of this list of labels.
    320+
    *
    321+
    * @param labels The {@link Format#labels}.
    322+
    * @return The builder.
    323+
    */
    324+
    @CanIgnoreReturnValue
    325+
    public Builder setLabels(List<Label> labels) {
    326+
    this.labels = ImmutableList.copyOf(labels);
    327+
    return this;
    328+
    }
    329+
    305330
    /**
    306331
    * Sets {@link Format#language}. The default value is {@code null}.
    307332
    *
    @@ -740,9 +765,22 @@ public Format build() {
    740765
    /** An identifier for the format, or null if unknown or not applicable. */
    741766
    @Nullable public final String id;
    742767

    743-
    /** The human readable label, or null if unknown or not applicable. */
    768+
    /**
    769+
    * The default human readable label, or null if unknown or not applicable.
    770+
    *
    771+
    *

    If non-null, the same label will be part of {@link #labels} too. If null, {@link #labels}

    772+
    * will be empty.
    773+
    */
    744774
    @Nullable public final String label;
    745775

    776+
    /**
    777+
    * The human readable list of labels, or an empty list if unknown or not applicable.
    778+
    *
    779+
    *

    If non-empty, the default {@link #label} will be part of this list. If empty, the default

    780+
    * {@link #label} will be null.
    781+
    */
    782+
    @UnstableApi public final List<Label> labels;
    783+
    746784
    /** The language as an IETF BCP 47 conformant tag, or null if unknown or not applicable. */
    747785
    @Nullable public final String language;
    748786

    @@ -931,8 +969,20 @@ public Format build() {
    931969

    932970
    private Format(Builder builder) {
    933971
    id = builder.id;
    934-
    label = builder.label;
    935972
    language = Util.normalizeLanguageCode(builder.language);
    973+
    if (builder.labels.isEmpty() && builder.label != null) {
    974+
    labels = ImmutableList.of(new Label(language, builder.label));
    975+
    label = builder.label;
    976+
    } else if (!builder.labels.isEmpty() && builder.label == null) {
    977+
    labels = builder.labels;
    978+
    label = getDefaultLabel(builder.labels, language);
    979+
    } else {
    980+
    checkState(
    981+
    (builder.labels.isEmpty() && builder.label == null)
    982+
    || (builder.labels.stream().anyMatch(l -> l.value.equals(builder.label))));
    983+
    labels = builder.labels;
    984+
    label = builder.label;
    985+
    }
    936986
    selectionFlags = builder.selectionFlags;
    937987
    roleFlags = builder.roleFlags;
    938988
    averageBitrate = builder.averageBitrate;
    @@ -1003,6 +1053,7 @@ public Format withManifestFormatInfo(Format manifestFormat) {
    10031053

    10041054
    // Prefer manifest values, but fill in from sample format if missing.
    10051055
    @Nullable String label = manifestFormat.label != null ? manifestFormat.label : this.label;
    1056+
    List<Label> labels = !manifestFormat.labels.isEmpty() ? manifestFormat.labels : this.labels;
    10061057
    @Nullable String language = this.language;
    10071058
    if ((trackType == C.TRACK_TYPE_TEXT || trackType == C.TRACK_TYPE_AUDIO)
    10081059
    && manifestFormat.language != null) {
    @@ -1044,6 +1095,7 @@ public Format withManifestFormatInfo(Format manifestFormat) {
    10441095
    return buildUpon()
    10451096
    .setId(id)
    10461097
    .setLabel(label)
    1098+
    .setLabels(labels)
    10471099
    .setLanguage(language)
    10481100
    .setSelectionFlags(selectionFlags)
    10491101
    .setRoleFlags(roleFlags)
    @@ -1111,7 +1163,8 @@ public int hashCode() {
    11111163
    // Some fields for which hashing is expensive are deliberately omitted.
    11121164
    int result = 17;
    11131165
    result = 31 * result + (id == null ? 0 : id.hashCode());
    1114-
    result = 31 * result + (label != null ? label.hashCode() : 0);
    1166+
    result = 31 * result + (label == null ? 0 : label.hashCode());
    1167+
    result = 31 * result + labels.hashCode();
    11151168
    result = 31 * result + (language == null ? 0 : language.hashCode());
    11161169
    result = 31 * result + selectionFlags;
    11171170
    result = 31 * result + roleFlags;
    @@ -1190,6 +1243,7 @@ public boolean equals(@Nullable Object obj) {
    11901243
    && Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0
    11911244
    && Util.areEqual(id, other.id)
    11921245
    && Util.areEqual(label, other.label)
    1246+
    && labels.equals(other.labels)
    11931247
    && Util.areEqual(codecs, other.codecs)
    11941248
    && Util.areEqual(containerMimeType, other.containerMimeType)
    11951249
    && Util.areEqual(sampleMimeType, other.sampleMimeType)
    @@ -1281,8 +1335,10 @@ public static String toLogString(@Nullable Format format) {
    12811335
    if (format.language != null) {
    12821336
    builder.append(", language=").append(format.language);
    12831337
    }
    1284-
    if (format.label != null) {
    1285-
    builder.append(", label=").append(format.label);
    1338+
    if (!format.labels.isEmpty()) {
    1339+
    builder.append(", labels=[");
    1340+
    Joiner.on(',').appendTo(builder, format.labels);
    1341+
    builder.append("]");
    12861342
    }
    12871343
    if (format.selectionFlags != 0) {
    12881344
    builder.append(", selectionFlags=[");
    @@ -1331,6 +1387,7 @@ public static String toLogString(@Nullable Format format) {
    13311387
    private static final String FIELD_CRYPTO_TYPE = Util.intToStringMaxRadix(29);
    13321388
    private static final String FIELD_TILE_COUNT_HORIZONTAL = Util.intToStringMaxRadix(30);
    13331389
    private static final String FIELD_TILE_COUNT_VERTICAL = Util.intToStringMaxRadix(31);
    1390+
    private static final String FIELD_LABELS = Util.intToStringMaxRadix(32);
    13341391

    13351392
    @UnstableApi
    13361393
    @Override
    @@ -1347,6 +1404,8 @@ public Bundle toBundle(boolean excludeMetadata) {
    13471404
    Bundle bundle = new Bundle();
    13481405
    bundle.putString(FIELD_ID, id);
    13491406
    bundle.putString(FIELD_LABEL, label);
    1407+
    bundle.putParcelableArrayList(
    1408+
    FIELD_LABELS, BundleCollectionUtil.toBundleArrayList(labels, Label::toBundle));
    13501409
    bundle.putString(FIELD_LANGUAGE, language);
    13511410
    bundle.putInt(FIELD_SELECTION_FLAGS, selectionFlags);
    13521411
    bundle.putInt(FIELD_ROLE_FLAGS, roleFlags);
    @@ -1413,7 +1472,14 @@ public static Format fromBundle(Bundle bundle) {
    14131472
    BundleCollectionUtil.ensureClassLoader(bundle);
    14141473
    builder
    14151474
    .setId(defaultIfNull(bundle.getString(FIELD_ID), DEFAULT.id))
    1416-
    .setLabel(defaultIfNull(bundle.getString(FIELD_LABEL), DEFAULT.label))
    1475+
    .setLabel(defaultIfNull(bundle.getString(FIELD_LABEL), DEFAULT.label));
    1476+
    @Nullable List<Bundle> labelsBundles = bundle.getParcelableArrayList(FIELD_LABELS);
    1477+
    List<Label> labels =
    1478+
    labelsBundles == null
    1479+
    ? ImmutableList.of()
    1480+
    : BundleCollectionUtil.fromBundleList(Label::fromBundle, labelsBundles);
    1481+
    builder
    1482+
    .setLabels(labels)
    14171483
    .setLanguage(defaultIfNull(bundle.getString(FIELD_LANGUAGE), DEFAULT.language))
    14181484
    .setSelectionFlags(bundle.getInt(FIELD_SELECTION_FLAGS, DEFAULT.selectionFlags))
    14191485
    .setRoleFlags(bundle.getInt(FIELD_ROLE_FLAGS, DEFAULT.roleFlags))
    @@ -1492,4 +1558,13 @@ private static String keyForInitializationData(int initialisationDataIndex) {
    14921558
    private static <T> T defaultIfNull(@Nullable T value, @Nullable T defaultValue) {
    14931559
    return value != null ? value : defaultValue;
    14941560
    }
    1561+
    1562+
    private static String getDefaultLabel(List<Label> labels, @Nullable String language) {
    1563+
    for (Label l : labels) {
    1564+
    if (TextUtils.equals(l.language, language)) {
    1565+
    return l.value;
    1566+
    }
    1567+
    }
    1568+
    return labels.get(0).value;
    1569+
    }
    14951570
    }
    Lines changed: 86 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,86 @@
    1+
    /*
    2+
    * Copyright 2024 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 androidx.media3.common;
    17+
    18+
    import static androidx.media3.common.util.Assertions.checkNotNull;
    19+
    20+
    import android.os.Bundle;
    21+
    import androidx.annotation.Nullable;
    22+
    import androidx.media3.common.util.UnstableApi;
    23+
    import androidx.media3.common.util.Util;
    24+
    25+
    /** A label for a {@link Format}. */
    26+
    @UnstableApi
    27+
    public class Label {
    28+
    /**
    29+
    * The language of this label, as an IETF BCP 47 conformant tag, or null if unknown or not
    30+
    * applicable.
    31+
    */
    32+
    @Nullable public final String language;
    33+
    34+
    /** The value for this label. */
    35+
    public final String value;
    36+
    37+
    /**
    38+
    * Creates a label.
    39+
    *
    40+
    * @param language The language of this label, as an IETF BCP 47 conformant tag, or null if
    41+
    * unknown or not applicable.
    42+
    * @param value The label value.
    43+
    */
    44+
    public Label(@Nullable String language, String value) {
    45+
    this.language = Util.normalizeLanguageCode(language);
    46+
    this.value = value;
    47+
    }
    48+
    49+
    @Override
    50+
    public boolean equals(@Nullable Object o) {
    51+
    if (this == o) {
    52+
    return true;
    53+
    }
    54+
    if (o == null || getClass() != o.getClass()) {
    55+
    return false;
    56+
    }
    57+
    Label label = (Label) o;
    58+
    return Util.areEqual(language, label.language) && Util.areEqual(value, label.value);
    59+
    }
    60+
    61+
    @Override
    62+
    public int hashCode() {
    63+
    int result = value.hashCode();
    64+
    result = 31 * result + (language != null ? language.hashCode() : 0);
    65+
    return result;
    66+
    }
    67+
    68+
    private static final String FIELD_LANGUAGE_INDEX = Util.intToStringMaxRadix(0);
    69+
    private static final String FIELD_VALUE_INDEX = Util.intToStringMaxRadix(1);
    70+
    71+
    /** Serializes this instance to a {@link Bundle}. */
    72+
    public Bundle toBundle() {
    73+
    Bundle bundle = new Bundle();
    74+
    if (language != null) {
    75+
    bundle.putString(FIELD_LANGUAGE_INDEX, language);
    76+
    }
    77+
    bundle.putString(FIELD_VALUE_INDEX, value);
    78+
    return bundle;
    79+
    }
    80+
    81+
    /** Deserializes an instance from a {@link Bundle} produced by {@link #toBundle()}. */
    82+
    public static Label fromBundle(Bundle bundle) {
    83+
    return new Label(
    84+
    bundle.getString(FIELD_LANGUAGE_INDEX), checkNotNull(bundle.getString(FIELD_VALUE_INDEX)));
    85+
    }
    86+
    }

    0 commit comments

    Comments
     (0)