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 @@
* 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
......
......@@ -980,11 +980,12 @@ public final class 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 @@ public final class 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 @@ public final class 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
......
......@@ -523,26 +523,6 @@ public final class Format implements Parcelable {
// 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 final class Format implements Parcelable {
@Nullable String codecs,
int bitrate,
@C.SelectionFlags int selectionFlags,
@C.RoleFlags int roleFlags,
@Nullable String language) {
return createTextContainerFormat(
id,
......@@ -560,7 +541,7 @@ public final class Format implements Parcelable {
codecs,
bitrate,
selectionFlags,
/* roleFlags= */ 0,
roleFlags,
language,
/* accessibilityChannel= */ NO_VALUE);
}
......
......@@ -1603,6 +1603,7 @@ public final class DefaultTrackSelectorTest {
/* codecs= */ null,
/* bitrate= */ Format.NO_VALUE,
selectionFlags,
/* roleFlags= */ 0,
language);
}
......
......@@ -1128,9 +1128,9 @@ public class DashManifestParser extends DefaultHandler
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 @@ public class DashManifestParser extends DefaultHandler
}
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.
......
......@@ -703,7 +703,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
variantFormat.frameRate,
/* initializationData= */ null,
variantFormat.selectionFlags,
/* roleFlags= */ 0);
variantFormat.roleFlags);
}
private static Format deriveAudioFormat(
......@@ -711,12 +711,14 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
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 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
if (isPrimaryTrackInVariant) {
channelCount = variantFormat.channelCount;
selectionFlags = variantFormat.selectionFlags;
roleFlags = mediaTagFormat.roleFlags;
language = variantFormat.language;
label = variantFormat.label;
}
......@@ -741,7 +744,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
/* sampleRate= */ Format.NO_VALUE,
/* initializationData= */ null,
selectionFlags,
/* roleFlags= */ 0,
roleFlags,
language);
}
......
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist;
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<HlsPlayli
private static final Pattern REGEX_LANGUAGE = Pattern.compile("LANGUAGE=\"(.+?)\"");
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_CHARACTERISTICS = Pattern.compile("CHARACTERISTICS=\"(.+?)\"");
private static final Pattern REGEX_INSTREAM_ID =
Pattern.compile("INSTREAM-ID=\"((?:CC|SERVICE)\\d+)\"");
private static final Pattern REGEX_AUTOSELECT = compileBooleanAttrPattern("AUTOSELECT");
......@@ -339,6 +341,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
for (int i = 0; i < mediaTags.size(); i++) {
line = mediaTags.get(i);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
......@@ -362,7 +365,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* sampleRate= */ Format.NO_VALUE,
/* initializationData= */ null,
selectionFlags,
/* roleFlags= */ 0,
roleFlags,
language);
if (isMediaTagMuxed(variants, uri)) {
muxedAudioFormat = format;
......@@ -380,6 +383,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* codecs= */ null,
/* bitrate= */ Format.NO_VALUE,
selectionFlags,
roleFlags,
language);
subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format, name));
break;
......@@ -406,7 +410,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* codecs= */ null,
/* bitrate= */ Format.NO_VALUE,
selectionFlags,
/* roleFlags= */ 0,
roleFlags,
language,
accessibilityChannel));
break;
......@@ -678,6 +682,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
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) {
String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
return channelsString != null
......
......@@ -184,6 +184,7 @@ public final class HlsMediaPeriodTest {
/* codecs= */ null,
/* bitrate= */ Format.NO_VALUE,
/* selectionFlags= */ 0,
/* roleFlags= */ 0,
language);
}
}
......@@ -719,6 +719,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
/* codecs= */ null,
bitrate,
/* selectionFlags= */ 0,
/* roleFlags= */ 0,
language);
} else {
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