Commit 49858b82 by Oliver Woodman

Merge pull request #7184 from TiVo:p-subtitle-format-from-codecs

PiperOrigin-RevId: 305137114
parent cc29798d
......@@ -37,6 +37,8 @@
* Update the manifest URI to avoid repeated HTTP redirects
([#6907](https://github.com/google/ExoPlayer/issues/6907)).
* Parse period `AssetIdentifier` elements.
* HLS: Recognize IMSC subtitles
([#7185](https://github.com/google/ExoPlayer/issues/7185)).
* UI: Add an option to set whether to use the orientation sensor for rotation
in spherical playbacks
([#6761](https://github.com/google/ExoPlayer/issues/6761)).
......
......@@ -122,22 +122,22 @@ public final class MimeTypes {
customMimeTypes.add(customMimeType);
}
/** Returns whether the given string is an audio mime type. */
/** Returns whether the given string is an audio MIME type. */
public static boolean isAudio(@Nullable String mimeType) {
return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType));
}
/** Returns whether the given string is a video mime type. */
/** Returns whether the given string is a video MIME type. */
public static boolean isVideo(@Nullable String mimeType) {
return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType));
}
/** Returns whether the given string is a text mime type. */
/** Returns whether the given string is a text MIME type. */
public static boolean isText(@Nullable String mimeType) {
return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType));
}
/** Returns whether the given string is an application mime type. */
/** Returns whether the given string is an application MIME type. */
public static boolean isApplication(@Nullable String mimeType) {
return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType));
}
......@@ -173,13 +173,14 @@ public final class MimeTypes {
* @param codecs The codecs attribute.
* @return The derived video mimeType, or null if it could not be derived.
*/
public static @Nullable String getVideoMediaMimeType(@Nullable String codecs) {
@Nullable
public static String getVideoMediaMimeType(@Nullable String codecs) {
if (codecs == null) {
return null;
}
String[] codecList = Util.splitCodecs(codecs);
for (String codec : codecList) {
String mimeType = getMediaMimeType(codec);
@Nullable String mimeType = getMediaMimeType(codec);
if (mimeType != null && isVideo(mimeType)) {
return mimeType;
}
......@@ -193,13 +194,14 @@ public final class MimeTypes {
* @param codecs The codecs attribute.
* @return The derived audio mimeType, or null if it could not be derived.
*/
public static @Nullable String getAudioMediaMimeType(@Nullable String codecs) {
@Nullable
public static String getAudioMediaMimeType(@Nullable String codecs) {
if (codecs == null) {
return null;
}
String[] codecList = Util.splitCodecs(codecs);
for (String codec : codecList) {
String mimeType = getMediaMimeType(codec);
@Nullable String mimeType = getMediaMimeType(codec);
if (mimeType != null && isAudio(mimeType)) {
return mimeType;
}
......@@ -213,7 +215,8 @@ public final class MimeTypes {
* @param codec The codec identifier to derive.
* @return The mimeType, or null if it could not be derived.
*/
public static @Nullable String getMediaMimeType(@Nullable String codec) {
@Nullable
public static String getMediaMimeType(@Nullable String codec) {
if (codec == null) {
return null;
}
......@@ -234,7 +237,7 @@ public final class MimeTypes {
} else if (codec.startsWith("vp8") || codec.startsWith("vp08")) {
return MimeTypes.VIDEO_VP8;
} else if (codec.startsWith("mp4a")) {
String mimeType = null;
@Nullable String mimeType = null;
if (codec.startsWith("mp4a.")) {
String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix
if (objectTypeString.length() >= 2) {
......@@ -243,7 +246,7 @@ public final class MimeTypes {
int objectTypeInt = Integer.parseInt(objectTypeHexString, 16);
mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt);
} catch (NumberFormatException ignored) {
// ignored
// Ignored.
}
}
}
......@@ -266,6 +269,10 @@ public final class MimeTypes {
return MimeTypes.AUDIO_VORBIS;
} else if (codec.startsWith("flac")) {
return MimeTypes.AUDIO_FLAC;
} else if (codec.startsWith("stpp")) {
return MimeTypes.APPLICATION_TTML;
} else if (codec.startsWith("wvtt")) {
return MimeTypes.TEXT_VTT;
} else {
return getCustomMimeTypeForCodec(codec);
}
......@@ -405,7 +412,8 @@ public final class MimeTypes {
* Returns the top-level type of {@code mimeType}, or null if {@code mimeType} is null or does not
* contain a forward slash character ({@code '/'}).
*/
private static @Nullable String getTopLevelType(@Nullable String mimeType) {
@Nullable
private static String getTopLevelType(@Nullable String mimeType) {
if (mimeType == null) {
return null;
}
......@@ -416,7 +424,8 @@ public final class MimeTypes {
return mimeType.substring(0, indexOfSlash);
}
private static @Nullable String getCustomMimeTypeForCodec(String codec) {
@Nullable
private static String getCustomMimeTypeForCodec(String codec) {
int customMimeTypeCount = customMimeTypes.size();
for (int i = 0; i < customMimeTypeCount; i++) {
CustomMimeType customMimeType = customMimeTypes.get(i);
......
......@@ -73,6 +73,10 @@ public final class MimeTypesTest {
assertThat(MimeTypes.getMediaMimeType("mp4a.AA")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("mp4a.AB")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("mp4a.AD")).isEqualTo(MimeTypes.AUDIO_OPUS);
assertThat(MimeTypes.getMediaMimeType("wvtt")).isEqualTo(MimeTypes.TEXT_VTT);
assertThat(MimeTypes.getMediaMimeType("stpp.")).isEqualTo(MimeTypes.APPLICATION_TTML);
assertThat(MimeTypes.getMediaMimeType("stpp.ttml.im1t")).isEqualTo(MimeTypes.APPLICATION_TTML);
}
@Test
......
......@@ -480,19 +480,28 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
}
break;
case TYPE_SUBTITLES:
codecs = null;
sampleMimeType = null;
variant = getVariantWithSubtitleGroup(variants, groupId);
if (variant != null) {
codecs = Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_TEXT);
sampleMimeType = MimeTypes.getMediaMimeType(codecs);
}
if (sampleMimeType == null) {
sampleMimeType = MimeTypes.TEXT_VTT;
}
format =
Format.createTextContainerFormat(
/* id= */ formatId,
/* label= */ name,
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
/* sampleMimeType= */ MimeTypes.TEXT_VTT,
/* codecs= */ null,
/* bitrate= */ Format.NO_VALUE,
selectionFlags,
roleFlags,
language)
/* id= */ formatId,
/* label= */ name,
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
sampleMimeType,
codecs,
/* bitrate= */ Format.NO_VALUE,
selectionFlags,
roleFlags,
language)
.copyWithMetadata(metadata);
subtitles.add(new Rendition(uri, format, groupId, name));
break;
case TYPE_CLOSED_CAPTIONS:
String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID, variableDefinitions);
......@@ -569,6 +578,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return null;
}
@Nullable
private static Variant getVariantWithSubtitleGroup(ArrayList<Variant> variants, String groupId) {
for (int i = 0; i < variants.size(); i++) {
Variant variant = variants.get(i);
if (groupId.equals(variant.subtitleGroupId)) {
return variant;
}
}
return null;
}
private static HlsMediaPlaylist parseMediaPlaylist(
HlsMasterPlaylist masterPlaylist, LineIterator iterator, String baseUri) throws IOException {
@HlsMediaPlaylist.PlaylistType int playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_UNKNOWN;
......
......@@ -194,6 +194,19 @@ public class HlsMasterPlaylistParserTest {
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,"
+ "GROUP-ID=\"sub1\",NAME=\"English\",URI=\"s1/en/prog_index.m3u8\"\n";
private static final String PLAYLIST_WITH_TTML_SUBTITLE =
" #EXTM3U\n"
+ "\n"
+ "#EXT-X-VERSION:6\n"
+ "\n"
+ "#EXT-X-INDEPENDENT-SEGMENTS\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"stpp.ttml.im1t,mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,AUDIO=\"aud1\",SUBTITLES=\"sub1\"\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",NAME=\"English\",URI=\"a1/index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"sub1\",NAME=\"English\",AUTOSELECT=YES,DEFAULT=YES,URI=\"s1/en/prog_index.m3u8\"\n";
@Test
public void testParseMasterPlaylist() throws IOException {
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
......@@ -380,6 +393,18 @@ public class HlsMasterPlaylistParserTest {
.isEqualTo(createExtXMediaMetadata(/* groupId= */ "aud3", /* name= */ "English"));
}
@Test
public void parseMasterPlaylist_withTtmlSubtitle() throws IOException {
HlsMasterPlaylist playlistWithTtmlSubtitle =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_TTML_SUBTITLE);
HlsMasterPlaylist.Variant variant = playlistWithTtmlSubtitle.variants.get(0);
Format firstTextFormat = playlistWithTtmlSubtitle.subtitles.get(0).format;
assertThat(firstTextFormat.id).isEqualTo("sub1:English");
assertThat(firstTextFormat.containerMimeType).isEqualTo(MimeTypes.APPLICATION_M3U8);
assertThat(firstTextFormat.sampleMimeType).isEqualTo(MimeTypes.APPLICATION_TTML);
assertThat(variant.format.codecs).isEqualTo("stpp.ttml.im1t,mp4a.40.2,avc1.66.30");
}
private static Metadata createExtXStreamInfMetadata(HlsTrackMetadataEntry.VariantInfo... infos) {
return new Metadata(
new HlsTrackMetadataEntry(/* groupId= */ null, /* name= */ null, Arrays.asList(infos)));
......
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