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
13 changes: 13 additions & 0 deletions demos/main/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,19 @@
}
]
},
{
"name": "Audio description",
"samples": [
{
"name": "DASH unified streaming - Audio description (ENG)",
"uri": "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel-desc-aud.ism/.mpd"
},
{
"name": "HLS The Morning Show - Change: Season 2",
"uri": "https://play-edge.itunes.apple.com/WebObjects/MZPlayLocal.woa/hls/subscription/playlist.m3u8?cc=CH&svcId=tvs.vds.4021&a=1568297173&isExternal=true&brandId=tvs.sbd.4000&id=518034010&l=en-GB&aec=UHD"
}
]
},
{
"name": "Progressive",
"samples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public static class Builder {
private ImmutableList<String> preferredAudioLanguages;
private ImmutableList<String> preferredAudioLabels;
private @C.RoleFlags int preferredAudioRoleFlags;
private boolean usePreferredAudioRoleFlagsFromAccessibilityManager;
private int maxAudioChannelCount;
private int maxAudioBitrate;
private ImmutableList<String> preferredAudioMimeTypes;
Expand Down Expand Up @@ -134,6 +135,7 @@ public Builder() {
preferredAudioLanguages = ImmutableList.of();
preferredAudioLabels = ImmutableList.of();
preferredAudioRoleFlags = 0;
usePreferredAudioRoleFlagsFromAccessibilityManager = true;
maxAudioChannelCount = Integer.MAX_VALUE;
maxAudioBitrate = Integer.MAX_VALUE;
preferredAudioMimeTypes = ImmutableList.of();
Expand Down Expand Up @@ -213,6 +215,9 @@ protected Builder(Bundle bundle) {
firstNonNull(bundle.getStringArray(FIELD_PREFERRED_AUDIO_LABELS), new String[0]));
preferredAudioRoleFlags =
bundle.getInt(FIELD_PREFERRED_AUDIO_ROLE_FLAGS, DEFAULT.preferredAudioRoleFlags);
usePreferredAudioRoleFlagsFromAccessibilityManager = bundle.getBoolean(
FIELD_USE_PREFERRED_AUDI_ROLE_FLAGS_FROM_ACCESSIBILITY_MANAGER,
DEFAULT.usePreferredAudioRoleFlagsFromAccessibilityManager);
maxAudioChannelCount =
bundle.getInt(FIELD_MAX_AUDIO_CHANNEL_COUNT, DEFAULT.maxAudioChannelCount);
maxAudioBitrate = bundle.getInt(FIELD_MAX_AUDIO_BITRATE, DEFAULT.maxAudioBitrate);
Expand Down Expand Up @@ -329,6 +334,7 @@ private void init(@UnknownInitialization Builder this, TrackSelectionParameters
// Audio
preferredAudioLanguages = parameters.preferredAudioLanguages;
preferredAudioRoleFlags = parameters.preferredAudioRoleFlags;
usePreferredAudioRoleFlagsFromAccessibilityManager = parameters.usePreferredAudioRoleFlagsFromAccessibilityManager;
preferredAudioLabels = parameters.preferredAudioLabels;
maxAudioChannelCount = parameters.maxAudioChannelCount;
maxAudioBitrate = parameters.maxAudioBitrate;
Expand Down Expand Up @@ -635,6 +641,19 @@ public Builder setPreferredAudioLanguages(String... preferredAudioLanguages) {
@CanIgnoreReturnValue
public Builder setPreferredAudioRoleFlags(@C.RoleFlags int preferredAudioRoleFlags) {
this.preferredAudioRoleFlags = preferredAudioRoleFlags;
this.usePreferredAudioRoleFlagsFromAccessibilityManager = false;
return this;
}

/**
* Set preferred audio role flag from accessibility manager
*
* @return This builder.
*/
@CanIgnoreReturnValue
public Builder setPreferredAudioRoleFlagsFromAccessibilityManager() {
this.usePreferredAudioRoleFlagsFromAccessibilityManager = true;
this.preferredAudioRoleFlags = 0;
return this;
}

Expand Down Expand Up @@ -1299,6 +1318,8 @@ public static TrackSelectionParameters getDefaults(Context context) {
*/
public final @C.RoleFlags int preferredAudioRoleFlags;

public final boolean usePreferredAudioRoleFlagsFromAccessibilityManager;

/**
* Maximum allowed audio channel count. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
Expand Down Expand Up @@ -1427,6 +1448,7 @@ protected TrackSelectionParameters(Builder builder) {
// Audio
this.preferredAudioLanguages = builder.preferredAudioLanguages;
this.preferredAudioRoleFlags = builder.preferredAudioRoleFlags;
this.usePreferredAudioRoleFlagsFromAccessibilityManager = builder.usePreferredAudioRoleFlagsFromAccessibilityManager;
this.maxAudioChannelCount = builder.maxAudioChannelCount;
this.preferredAudioLabels = builder.preferredAudioLabels;
this.maxAudioBitrate = builder.maxAudioBitrate;
Expand Down Expand Up @@ -1485,6 +1507,8 @@ public boolean equals(@Nullable Object obj) {
&& preferredVideoRoleFlags == other.preferredVideoRoleFlags
// Audio
&& preferredAudioLanguages.equals(other.preferredAudioLanguages)
&& usePreferredAudioRoleFlagsFromAccessibilityManager
== other.usePreferredAudioRoleFlagsFromAccessibilityManager
&& preferredAudioRoleFlags == other.preferredAudioRoleFlags
&& maxAudioChannelCount == other.maxAudioChannelCount
&& preferredAudioLabels.equals(other.preferredAudioLabels)
Expand Down Expand Up @@ -1532,6 +1556,7 @@ public int hashCode() {
// Audio
result = 31 * result + preferredAudioLanguages.hashCode();
result = 31 * result + preferredAudioRoleFlags;
result = 31 * result + (usePreferredAudioRoleFlagsFromAccessibilityManager ? 1 : 0);
result = 31 * result + maxAudioChannelCount;
result = 31 * result + preferredAudioLabels.hashCode();
result = 31 * result + maxAudioBitrate;
Expand Down Expand Up @@ -1597,6 +1622,9 @@ public int hashCode() {
private static final String FIELD_PREFERRED_VIDEO_LABELS = Util.intToStringMaxRadix(36);
private static final String FIELD_PREFERRED_AUDIO_LABELS = Util.intToStringMaxRadix(37);
private static final String FIELD_PREFERRED_TEXT_LABELS = Util.intToStringMaxRadix(38);
private static final String
FIELD_USE_PREFERRED_AUDI_ROLE_FLAGS_FROM_ACCESSIBILITY_MANAGER =
Util.intToStringMaxRadix(39);

/**
* Defines a minimum field ID value for subclasses to use when implementing {@link #toBundle()}
Expand Down Expand Up @@ -1638,6 +1666,9 @@ public Bundle toBundle() {
bundle.putStringArray(
FIELD_PREFERRED_AUDIO_LANGUAGES, preferredAudioLanguages.toArray(new String[0]));
bundle.putInt(FIELD_PREFERRED_AUDIO_ROLE_FLAGS, preferredAudioRoleFlags);
bundle.putBoolean(
FIELD_USE_PREFERRED_AUDI_ROLE_FLAGS_FROM_ACCESSIBILITY_MANAGER,
usePreferredAudioRoleFlagsFromAccessibilityManager);
bundle.putInt(FIELD_MAX_AUDIO_CHANNEL_COUNT, maxAudioChannelCount);
bundle.putInt(FIELD_MAX_AUDIO_BITRATE, maxAudioBitrate);
bundle.putStringArray(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package androidx.media3.exoplayer.trackselection;

import static android.os.Build.VERSION.SDK_INT;
import static androidx.media3.common.C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND;
import static androidx.media3.common.C.ROLE_FLAG_DESCRIBES_VIDEO;
import static androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_DISABLED;
import static androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_REQUIRED;
import static androidx.media3.common.util.Assertions.checkNotNull;
Expand All @@ -34,13 +36,15 @@
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.Spatializer;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
Expand Down Expand Up @@ -410,6 +414,14 @@ public ParametersBuilder setPreferredAudioRoleFlags(@C.RoleFlags int preferredAu
return this;
}

@SuppressWarnings("deprecation") // Intentionally returning deprecated type
@CanIgnoreReturnValue
@Override
public ParametersBuilder setPreferredAudioRoleFlagsFromAccessibilityManager() {
delegate.setPreferredAudioRoleFlagsFromAccessibilityManager();
return this;
}

@SuppressWarnings("deprecation") // Intentionally returning deprecated type
@CanIgnoreReturnValue
@Override
Expand Down Expand Up @@ -1264,6 +1276,13 @@ public Builder setPreferredAudioRoleFlags(@C.RoleFlags int preferredAudioRoleFla
return this;
}

@CanIgnoreReturnValue
@Override
public Builder setPreferredAudioRoleFlagsFromAccessibilityManager() {
super.setPreferredAudioRoleFlagsFromAccessibilityManager();
return this;
}

@CanIgnoreReturnValue
@Override
public Builder setPreferredAudioLabels(String... preferredAudioLabels) {
Expand Down Expand Up @@ -2910,7 +2929,13 @@ protected Pair<ExoTrackSelection.Definition, Integer> selectAudioTrack(
break;
}
}

@RoleFlags int preferredRoleFlagFromAccessibilityManager = 0;
if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
preferredRoleFlagFromAccessibilityManager = getPreferredRoleFlagFromAccessibilityManager(context);
}
boolean hasVideoRendererWithMappedTracksFinal = hasVideoRendererWithMappedTracks;
@RoleFlags int preferredRoleFlagFromAccessibilityManagerFinal = preferredRoleFlagFromAccessibilityManager;
return selectTracksForType(
C.TRACK_TYPE_AUDIO,
mappedTrackInfo,
Expand All @@ -2923,7 +2948,8 @@ protected Pair<ExoTrackSelection.Definition, Integer> selectAudioTrack(
support,
hasVideoRendererWithMappedTracksFinal,
format -> isAudioFormatWithinAudioChannelCountConstraints(format, params),
rendererMixedMimeTypeAdaptationSupports[rendererIndex]),
rendererMixedMimeTypeAdaptationSupports[rendererIndex],
preferredRoleFlagFromAccessibilityManagerFinal),
AudioTrackInfo::compareSelections);
}

Expand Down Expand Up @@ -3601,6 +3627,21 @@ private static String getPreferredLanguageFromCaptioningManager(@Nullable Contex
return Util.getLocaleLanguageTag(preferredLocale);
}

@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
private static @RoleFlags int getPreferredRoleFlagFromAccessibilityManager(
@Nullable Context context) {
if (context == null) {
return 0;
}
AccessibilityManager accessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager != null) {
return accessibilityManager.isAudioDescriptionRequested() ? ROLE_FLAG_DESCRIBES_VIDEO
| ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND : 0;
}
return 0;
}

/** Base class for track selection information of a {@link Format}. */
private abstract static class TrackInfo<T extends TrackInfo<T>> {
/** Factory for {@link TrackInfo} implementations for a given {@link TrackGroup}. */
Expand Down Expand Up @@ -3910,7 +3951,8 @@ public static ImmutableList<AudioTrackInfo> createForTrackGroup(
@Capabilities int[] formatSupport,
boolean hasMappedVideoTracks,
Predicate<Format> withinAudioChannelCountConstraints,
@AdaptiveSupport int mixedMimeTypeAdaptationSupport) {
@AdaptiveSupport int mixedMimeTypeAdaptationSupport,
@RoleFlags int preferredRoleFlagFromAccessibilityManager) {
ImmutableList.Builder<AudioTrackInfo> listBuilder = ImmutableList.builder();
for (int i = 0; i < trackGroup.length; i++) {
listBuilder.add(
Expand All @@ -3922,7 +3964,8 @@ public static ImmutableList<AudioTrackInfo> createForTrackGroup(
formatSupport[i],
hasMappedVideoTracks,
withinAudioChannelCountConstraints,
mixedMimeTypeAdaptationSupport));
mixedMimeTypeAdaptationSupport,
preferredRoleFlagFromAccessibilityManager));
}
return listBuilder.build();
}
Expand Down Expand Up @@ -3957,7 +4000,8 @@ public AudioTrackInfo(
@Capabilities int formatSupport,
boolean hasMappedVideoTracks,
Predicate<Format> withinAudioChannelCountConstraints,
@AdaptiveSupport int mixedMimeTypeAdaptationSupport) {
@AdaptiveSupport int mixedMimeTypeAdaptationSupport,
@RoleFlags int preferredRoleFlagFromAccessibilityManager) {
super(rendererIndex, trackGroup, trackIndex);
this.parameters = parameters;
@SuppressLint("WrongConstant")
Expand Down Expand Up @@ -3988,8 +4032,10 @@ public AudioTrackInfo(
}
preferredLanguageIndex = bestLanguageIndex;
preferredLanguageScore = bestLanguageScore;
preferredRoleFlagsScore =
getRoleFlagMatchScore(format.roleFlags, parameters.preferredAudioRoleFlags);
@RoleFlags int preferredAudioRoleFlags =
preferredRoleFlagFromAccessibilityManager == 0 ? parameters.preferredAudioRoleFlags
: preferredRoleFlagFromAccessibilityManager;
preferredRoleFlagsScore = getRoleFlagMatchScore(format.roleFlags, preferredAudioRoleFlags);
preferredLabelMatchIndex = getBestLabelMatchIndex(format, parameters.preferredAudioLabels);
hasMainOrNoRoleFlag = format.roleFlags == 0 || (format.roleFlags & C.ROLE_FLAG_MAIN) != 0;
isDefaultSelectionFlag = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
Expand Down