mirror of
https://github.com/samsonjs/media.git
synced 2026-04-24 14:37:45 +00:00
Parse CHARACTERISTICS attribute for EXT-X-MEDIA tags
PiperOrigin-RevId: 239844963
This commit is contained in:
parent
cdd433acda
commit
77d597de5f
9 changed files with 65 additions and 39 deletions
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Reference in a new issue