Commit 77d597de by olly Committed by Oliver Woodman

Parse CHARACTERISTICS attribute for EXT-X-MEDIA tags

PiperOrigin-RevId: 239844963
parent cdd433ac
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* Form an adaptive track group out of audio renditions with matching name. * Form an adaptive track group out of audio renditions with matching name.
* Support encrypted initialization segments * Support encrypted initialization segments
([#5441](https://github.com/google/ExoPlayer/issues/5441)). ([#5441](https://github.com/google/ExoPlayer/issues/5441)).
* Parse `EXT-X-MEDIA` `CHARACTERISTICS` attribute into `Format.roleFlags`.
* `ExtractorMediaSource` renamed to `ProgressiveMediaSource`. * `ExtractorMediaSource` renamed to `ProgressiveMediaSource`.
* Support for playing spherical videos on Daydream. * Support for playing spherical videos on Daydream.
* Improve decoder re-use between playbacks. TODO: Write and link a blog post * Improve decoder re-use between playbacks. TODO: Write and link a blog post
......
...@@ -980,11 +980,12 @@ public final class C { ...@@ -980,11 +980,12 @@ public final class C {
public static final int NETWORK_TYPE_OTHER = 8; public static final int NETWORK_TYPE_OTHER = 8;
/** /**
* Track role flags. Possible values are {@link #ROLE_FLAG_MAIN}, {@link #ROLE_FLAG_ALTERNATE}, * Track role flags. Possible flag values are {@link #ROLE_FLAG_MAIN}, {@link
* {@link #ROLE_FLAG_SUPPLEMENTARY}, {@link #ROLE_FLAG_COMMENTARY}, {@link #ROLE_FLAG_DUB}, {@link * #ROLE_FLAG_ALTERNATE}, {@link #ROLE_FLAG_SUPPLEMENTARY}, {@link #ROLE_FLAG_COMMENTARY}, {@link
* #ROLE_FLAG_EMERGENCY}, {@link #ROLE_FLAG_CAPTION}, {@link #ROLE_FLAG_SUBTITLE}, {@link * #ROLE_FLAG_DUB}, {@link #ROLE_FLAG_EMERGENCY}, {@link #ROLE_FLAG_CAPTION}, {@link
* #ROLE_FLAG_SIGN}, {@link #ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY}, {@link * #ROLE_FLAG_SUBTITLE}, {@link #ROLE_FLAG_SIGN}, {@link #ROLE_FLAG_DESCRIBES_VIDEO}, {@link
* #ROLE_FLAG_DESCRIPTION}. * #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 @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
...@@ -1000,8 +1001,11 @@ public final class C { ...@@ -1000,8 +1001,11 @@ public final class C {
ROLE_FLAG_CAPTION, ROLE_FLAG_CAPTION,
ROLE_FLAG_SUBTITLE, ROLE_FLAG_SUBTITLE,
ROLE_FLAG_SIGN, ROLE_FLAG_SIGN,
ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY, ROLE_FLAG_DESCRIBES_VIDEO,
ROLE_FLAG_DESCRIPTION ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND,
ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY,
ROLE_FLAG_TRANSCRIBES_DIALOG,
ROLE_FLAG_EASY_TO_READ
}) })
public @interface RoleFlags {} public @interface RoleFlags {}
/** Indicates a main track. */ /** Indicates a main track. */
...@@ -1037,10 +1041,16 @@ public final class C { ...@@ -1037,10 +1041,16 @@ public final class C {
public static final int ROLE_FLAG_SUBTITLE = 1 << 7; public static final int ROLE_FLAG_SUBTITLE = 1 << 7;
/** Indicates the track contains a visual sign-language interpretation of an audio track. */ /** Indicates the track contains a visual sign-language interpretation of an audio track. */
public static final int ROLE_FLAG_SIGN = 1 << 8; 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. */ /** 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 * Converts a time in microseconds to the corresponding time in milliseconds, preserving
......
...@@ -523,26 +523,6 @@ public final class Format implements Parcelable { ...@@ -523,26 +523,6 @@ public final class Format implements Parcelable {
// Text. // 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( public static Format createTextContainerFormat(
@Nullable String id, @Nullable String id,
@Nullable String label, @Nullable String label,
...@@ -551,6 +531,7 @@ public final class Format implements Parcelable { ...@@ -551,6 +531,7 @@ public final class Format implements Parcelable {
@Nullable String codecs, @Nullable String codecs,
int bitrate, int bitrate,
@C.SelectionFlags int selectionFlags, @C.SelectionFlags int selectionFlags,
@C.RoleFlags int roleFlags,
@Nullable String language) { @Nullable String language) {
return createTextContainerFormat( return createTextContainerFormat(
id, id,
...@@ -560,7 +541,7 @@ public final class Format implements Parcelable { ...@@ -560,7 +541,7 @@ public final class Format implements Parcelable {
codecs, codecs,
bitrate, bitrate,
selectionFlags, selectionFlags,
/* roleFlags= */ 0, roleFlags,
language, language,
/* accessibilityChannel= */ NO_VALUE); /* accessibilityChannel= */ NO_VALUE);
} }
......
...@@ -1603,6 +1603,7 @@ public final class DefaultTrackSelectorTest { ...@@ -1603,6 +1603,7 @@ public final class DefaultTrackSelectorTest {
/* codecs= */ null, /* codecs= */ null,
/* bitrate= */ Format.NO_VALUE, /* bitrate= */ Format.NO_VALUE,
selectionFlags, selectionFlags,
/* roleFlags= */ 0,
language); language);
} }
......
...@@ -1128,9 +1128,9 @@ public class DashManifestParser extends DefaultHandler ...@@ -1128,9 +1128,9 @@ public class DashManifestParser extends DefaultHandler
case "sign": case "sign":
return C.ROLE_FLAG_SIGN; return C.ROLE_FLAG_SIGN;
case "description": case "description":
return C.ROLE_FLAG_DESCRIPTION; return C.ROLE_FLAG_DESCRIBES_VIDEO;
case "enhanced-audio-intelligibility": case "enhanced-audio-intelligibility":
return C.ROLE_FLAG_ENHANCED_AUDIO_INTELLIGIBILITY; return C.ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY;
default: default:
return 0; return 0;
} }
...@@ -1143,9 +1143,9 @@ public class DashManifestParser extends DefaultHandler ...@@ -1143,9 +1143,9 @@ public class DashManifestParser extends DefaultHandler
} }
switch (value) { switch (value) {
case "1": // Audio description for the visually impaired. 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. 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. case "3": // Supplemental commentary.
return C.ROLE_FLAG_SUPPLEMENTARY; return C.ROLE_FLAG_SUPPLEMENTARY;
case "4": // Director's commentary. case "4": // Director's commentary.
......
...@@ -703,7 +703,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -703,7 +703,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
variantFormat.frameRate, variantFormat.frameRate,
/* initializationData= */ null, /* initializationData= */ null,
variantFormat.selectionFlags, variantFormat.selectionFlags,
/* roleFlags= */ 0); variantFormat.roleFlags);
} }
private static Format deriveAudioFormat( private static Format deriveAudioFormat(
...@@ -711,12 +711,14 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -711,12 +711,14 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
String codecs; String codecs;
int channelCount = Format.NO_VALUE; int channelCount = Format.NO_VALUE;
int selectionFlags = 0; int selectionFlags = 0;
int roleFlags = 0;
String language = null; String language = null;
String label = null; String label = null;
if (mediaTagFormat != null) { if (mediaTagFormat != null) {
codecs = mediaTagFormat.codecs; codecs = mediaTagFormat.codecs;
channelCount = mediaTagFormat.channelCount; channelCount = mediaTagFormat.channelCount;
selectionFlags = mediaTagFormat.selectionFlags; selectionFlags = mediaTagFormat.selectionFlags;
roleFlags = mediaTagFormat.roleFlags;
language = mediaTagFormat.language; language = mediaTagFormat.language;
label = mediaTagFormat.label; label = mediaTagFormat.label;
} else { } else {
...@@ -724,6 +726,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -724,6 +726,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
if (isPrimaryTrackInVariant) { if (isPrimaryTrackInVariant) {
channelCount = variantFormat.channelCount; channelCount = variantFormat.channelCount;
selectionFlags = variantFormat.selectionFlags; selectionFlags = variantFormat.selectionFlags;
roleFlags = mediaTagFormat.roleFlags;
language = variantFormat.language; language = variantFormat.language;
label = variantFormat.label; label = variantFormat.label;
} }
...@@ -741,7 +744,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -741,7 +744,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
/* sampleRate= */ Format.NO_VALUE, /* sampleRate= */ Format.NO_VALUE,
/* initializationData= */ null, /* initializationData= */ null,
selectionFlags, selectionFlags,
/* roleFlags= */ 0, roleFlags,
language); language);
} }
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -145,6 +146,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -145,6 +146,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_LANGUAGE = Pattern.compile("LANGUAGE=\"(.+?)\""); private static final Pattern REGEX_LANGUAGE = Pattern.compile("LANGUAGE=\"(.+?)\"");
private static final Pattern REGEX_NAME = Pattern.compile("NAME=\"(.+?)\""); private static final Pattern REGEX_NAME = Pattern.compile("NAME=\"(.+?)\"");
private static final Pattern REGEX_GROUP_ID = Pattern.compile("GROUP-ID=\"(.+?)\""); private static final Pattern REGEX_GROUP_ID = Pattern.compile("GROUP-ID=\"(.+?)\"");
private static final Pattern REGEX_CHARACTERISTICS = Pattern.compile("CHARACTERISTICS=\"(.+?)\"");
private static final Pattern REGEX_INSTREAM_ID = private static final Pattern REGEX_INSTREAM_ID =
Pattern.compile("INSTREAM-ID=\"((?:CC|SERVICE)\\d+)\""); Pattern.compile("INSTREAM-ID=\"((?:CC|SERVICE)\\d+)\"");
private static final Pattern REGEX_AUTOSELECT = compileBooleanAttrPattern("AUTOSELECT"); private static final Pattern REGEX_AUTOSELECT = compileBooleanAttrPattern("AUTOSELECT");
...@@ -339,6 +341,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -339,6 +341,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
for (int i = 0; i < mediaTags.size(); i++) { for (int i = 0; i < mediaTags.size(); i++) {
line = mediaTags.get(i); line = mediaTags.get(i);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line); @C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions); String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions); String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions); String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
...@@ -362,7 +365,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -362,7 +365,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* sampleRate= */ Format.NO_VALUE, /* sampleRate= */ Format.NO_VALUE,
/* initializationData= */ null, /* initializationData= */ null,
selectionFlags, selectionFlags,
/* roleFlags= */ 0, roleFlags,
language); language);
if (isMediaTagMuxed(variants, uri)) { if (isMediaTagMuxed(variants, uri)) {
muxedAudioFormat = format; muxedAudioFormat = format;
...@@ -380,6 +383,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -380,6 +383,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* codecs= */ null, /* codecs= */ null,
/* bitrate= */ Format.NO_VALUE, /* bitrate= */ Format.NO_VALUE,
selectionFlags, selectionFlags,
roleFlags,
language); language);
subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format, name)); subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format, name));
break; break;
...@@ -406,7 +410,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -406,7 +410,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* codecs= */ null, /* codecs= */ null,
/* bitrate= */ Format.NO_VALUE, /* bitrate= */ Format.NO_VALUE,
selectionFlags, selectionFlags,
/* roleFlags= */ 0, roleFlags,
language, language,
accessibilityChannel)); accessibilityChannel));
break; break;
...@@ -678,6 +682,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -678,6 +682,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return flags; return flags;
} }
@C.RoleFlags
private static int parseRoleFlags(String line, Map<String, String> 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<String, String> variableDefinitions) { private static int parseChannelsAttribute(String line, Map<String, String> variableDefinitions) {
String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions); String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
return channelsString != null return channelsString != null
......
...@@ -184,6 +184,7 @@ public final class HlsMediaPeriodTest { ...@@ -184,6 +184,7 @@ public final class HlsMediaPeriodTest {
/* codecs= */ null, /* codecs= */ null,
/* bitrate= */ Format.NO_VALUE, /* bitrate= */ Format.NO_VALUE,
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0,
language); language);
} }
} }
...@@ -719,6 +719,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> { ...@@ -719,6 +719,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
/* codecs= */ null, /* codecs= */ null,
bitrate, bitrate,
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0,
language); language);
} else { } else {
format = format =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment