diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 563d0a275bf..63edfff07c5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -12,6 +12,7 @@ * Form an adaptive track group out of audio renditions with matching name. * Support encrypted initialization segments ([#5441](https://github.com/google/ExoPlayer/issues/5441)). + * Parse `EXT-X-MEDIA` `CHARACTERISTICS` attribute into `Format.roleFlags`. * `ExtractorMediaSource` renamed to `ProgressiveMediaSource`. * Support for playing spherical videos on Daydream. * Improve decoder re-use between playbacks. TODO: Write and link a blog post diff --git a/library/core/src/main/java/com/google/android/exoplayer2/C.java b/library/core/src/main/java/com/google/android/exoplayer2/C.java index 601cdd5e09f..d0676e946da 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/C.java @@ -980,11 +980,12 @@ private C() {} public static final int NETWORK_TYPE_OTHER = 8; /** - * Track role flags. Possible values are {@link #ROLE_FLAG_MAIN}, {@link #ROLE_FLAG_ALTERNATE}, - * {@link #ROLE_FLAG_SUPPLEMENTARY}, {@link #ROLE_FLAG_COMMENTARY}, {@link #ROLE_FLAG_DUB}, {@link - * #ROLE_FLAG_EMERGENCY}, {@link #ROLE_FLAG_CAPTION}, {@link #ROLE_FLAG_SUBTITLE}, {@link - * #ROLE_FLAG_SIGN}, {@link #ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY}, {@link - * #ROLE_FLAG_DESCRIPTION}. + * Track role flags. Possible flag values are {@link #ROLE_FLAG_MAIN}, {@link + * #ROLE_FLAG_ALTERNATE}, {@link #ROLE_FLAG_SUPPLEMENTARY}, {@link #ROLE_FLAG_COMMENTARY}, {@link + * #ROLE_FLAG_DUB}, {@link #ROLE_FLAG_EMERGENCY}, {@link #ROLE_FLAG_CAPTION}, {@link + * #ROLE_FLAG_SUBTITLE}, {@link #ROLE_FLAG_SIGN}, {@link #ROLE_FLAG_DESCRIBES_VIDEO}, {@link + * #ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND}, {@link #ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY}, + * {@link #ROLE_FLAG_TRANSCRIBES_DIALOG} and {@link #ROLE_FLAG_EASY_TO_READ}. */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -1000,8 +1001,11 @@ private C() {} ROLE_FLAG_CAPTION, ROLE_FLAG_SUBTITLE, ROLE_FLAG_SIGN, - ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY, - ROLE_FLAG_DESCRIPTION + ROLE_FLAG_DESCRIBES_VIDEO, + ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND, + ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY, + ROLE_FLAG_TRANSCRIBES_DIALOG, + ROLE_FLAG_EASY_TO_READ }) public @interface RoleFlags {} /** Indicates a main track. */ @@ -1037,10 +1041,16 @@ private C() {} public static final int ROLE_FLAG_SUBTITLE = 1 << 7; /** Indicates the track contains a visual sign-language interpretation of an audio track. */ public static final int ROLE_FLAG_SIGN = 1 << 8; - /** Indicates the track is designed for improved intelligibility of dialogue. */ - public static final int ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY = 1 << 9; /** Indicates the track contains an audio or textual description of a video track. */ - public static final int ROLE_FLAG_DESCRIPTION = 1 << 10; + public static final int ROLE_FLAG_DESCRIBES_VIDEO = 1 << 9; + /** Indicates the track contains a textual description of music and sound. */ + public static final int ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND = 1 << 10; + /** Indicates the track is designed for improved intelligibility of dialogue. */ + public static final int ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY = 1 << 11; + /** Indicates the track contains a transcription of spoken dialog. */ + public static final int ROLE_FLAG_TRANSCRIBES_DIALOG = 1 << 12; + /** Indicates the track contains a text that has been edited for ease of reading. */ + public static final int ROLE_FLAG_EASY_TO_READ = 1 << 13; /** * Converts a time in microseconds to the corresponding time in milliseconds, preserving diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Format.java b/library/core/src/main/java/com/google/android/exoplayer2/Format.java index e170faf50b1..8628ead4d66 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Format.java @@ -523,26 +523,6 @@ public static Format createAudioSampleFormat( // Text. - @Deprecated - public static Format createTextContainerFormat( - @Nullable String id, - @Nullable String containerMimeType, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - @C.SelectionFlags int selectionFlags, - @Nullable String language) { - return createTextContainerFormat( - id, - /* label= */ null, - containerMimeType, - sampleMimeType, - codecs, - bitrate, - selectionFlags, - language); - } - public static Format createTextContainerFormat( @Nullable String id, @Nullable String label, @@ -551,6 +531,7 @@ public static Format createTextContainerFormat( @Nullable String codecs, int bitrate, @C.SelectionFlags int selectionFlags, + @C.RoleFlags int roleFlags, @Nullable String language) { return createTextContainerFormat( id, @@ -560,7 +541,7 @@ public static Format createTextContainerFormat( codecs, bitrate, selectionFlags, - /* roleFlags= */ 0, + roleFlags, language, /* accessibilityChannel= */ NO_VALUE); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java index d2aad12f247..aaeb6b7c993 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java @@ -1603,6 +1603,7 @@ private static Format buildTextFormat(String id, String language, int selectionF /* codecs= */ null, /* bitrate= */ Format.NO_VALUE, selectionFlags, + /* roleFlags= */ 0, language); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index 8cdc54bec8d..c4f61a73cd6 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -1128,9 +1128,9 @@ protected int parseDashRoleSchemeValue(String value) { case "sign": return C.ROLE_FLAG_SIGN; case "description": - return C.ROLE_FLAG_DESCRIPTION; + return C.ROLE_FLAG_DESCRIBES_VIDEO; case "enhanced-audio-intelligibility": - return C.ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY; + return C.ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY; default: return 0; } @@ -1143,9 +1143,9 @@ protected int parseTvaAudioPurposeCsValue(String value) { } switch (value) { case "1": // Audio description for the visually impaired. - return C.ROLE_FLAG_DESCRIPTION; + return C.ROLE_FLAG_DESCRIBES_VIDEO; case "2": // Audio description for the hard of hearing. - return C.ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY; + return C.ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY; case "3": // Supplemental commentary. return C.ROLE_FLAG_SUPPLEMENTARY; case "4": // Director's commentary. diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index e4e0245605d..c6df726c2c6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -703,7 +703,7 @@ private static Format deriveVideoFormat(Format variantFormat) { variantFormat.frameRate, /* initializationData= */ null, variantFormat.selectionFlags, - /* roleFlags= */ 0); + variantFormat.roleFlags); } private static Format deriveAudioFormat( @@ -711,12 +711,14 @@ private static Format deriveAudioFormat( String codecs; int channelCount = Format.NO_VALUE; int selectionFlags = 0; + int roleFlags = 0; String language = null; String label = null; if (mediaTagFormat != null) { codecs = mediaTagFormat.codecs; channelCount = mediaTagFormat.channelCount; selectionFlags = mediaTagFormat.selectionFlags; + roleFlags = mediaTagFormat.roleFlags; language = mediaTagFormat.language; label = mediaTagFormat.label; } else { @@ -724,6 +726,7 @@ private static Format deriveAudioFormat( if (isPrimaryTrackInVariant) { channelCount = variantFormat.channelCount; selectionFlags = variantFormat.selectionFlags; + roleFlags = mediaTagFormat.roleFlags; language = variantFormat.language; label = variantFormat.label; } @@ -741,7 +744,7 @@ private static Format deriveAudioFormat( /* sampleRate= */ Format.NO_VALUE, /* initializationData= */ null, selectionFlags, - /* roleFlags= */ 0, + roleFlags, language); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 177c9aaeffb..fd49e10bea5 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -17,6 +17,7 @@ import android.net.Uri; import androidx.annotation.Nullable; +import android.text.TextUtils; import android.util.Base64; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -145,6 +146,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variableDefinitions) { + String concatenatedCharacteristics = + parseOptionalStringAttr(line, REGEX_CHARACTERISTICS, variableDefinitions); + if (TextUtils.isEmpty(concatenatedCharacteristics)) { + return 0; + } + String[] characteristics = Util.split(concatenatedCharacteristics, ","); + @C.RoleFlags int roleFlags = 0; + if (Util.contains(characteristics, "public.accessibility.describes-video")) { + roleFlags |= C.ROLE_FLAG_DESCRIBES_VIDEO; + } + if (Util.contains(characteristics, "public.accessibility.transcribes-spoken-dialog")) { + roleFlags |= C.ROLE_FLAG_TRANSCRIBES_DIALOG; + } + if (Util.contains(characteristics, "public.accessibility.describes-music-and-sound")) { + roleFlags |= C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND; + } + if (Util.contains(characteristics, "public.easy-to-read")) { + roleFlags |= C.ROLE_FLAG_EASY_TO_READ; + } + return roleFlags; + } + private static int parseChannelsAttribute(String line, Map variableDefinitions) { String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions); return channelsString != null diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java index add244db158..a3e2af53b64 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java @@ -184,6 +184,7 @@ private static Format createSubtitleFormat(String language) { /* codecs= */ null, /* bitrate= */ Format.NO_VALUE, /* selectionFlags= */ 0, + /* roleFlags= */ 0, language); } } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java index 2047ef1c627..66731660f57 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java @@ -719,6 +719,7 @@ public void parseStartTag(XmlPullParser parser) throws ParserException { /* codecs= */ null, bitrate, /* selectionFlags= */ 0, + /* roleFlags= */ 0, language); } else { format =