From 74a9d8f680995f2096c59fde6cd1ef6e85bb4d55 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 7 Apr 2020 16:02:54 +0100 Subject: [PATCH] Clean up manifest MIME type and codec parsing PiperOrigin-RevId: 305258836 --- .../android/exoplayer2/util/MimeTypes.java | 55 +++++++++++++---- .../exoplayer2/util/MimeTypesTest.java | 26 ++++++++ .../source/dash/DefaultDashChunkSource.java | 17 +++--- .../dash/manifest/DashManifestParser.java | 59 ++++--------------- 4 files changed, 87 insertions(+), 70 deletions(-) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index 50930c6992..e9e74d4ba8 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -133,14 +133,23 @@ public final class MimeTypes { 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, including known text types that use + * "application" as their base 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. */ - public static boolean isApplication(@Nullable String mimeType) { - return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType)); + return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType)) + || APPLICATION_CEA608.equals(mimeType) + || APPLICATION_CEA708.equals(mimeType) + || APPLICATION_MP4CEA608.equals(mimeType) + || APPLICATION_SUBRIP.equals(mimeType) + || APPLICATION_TTML.equals(mimeType) + || APPLICATION_TX3G.equals(mimeType) + || APPLICATION_MP4VTT.equals(mimeType) + || APPLICATION_RAWCC.equals(mimeType) + || APPLICATION_VOBSUB.equals(mimeType) + || APPLICATION_PGS.equals(mimeType) + || APPLICATION_DVBSUBS.equals(mimeType); } /** @@ -210,6 +219,27 @@ public final class MimeTypes { return null; } + /** + * Derives a text sample mimeType from a codecs attribute. + * + * @param codecs The codecs attribute. + * @return The derived text mimeType, or null if it could not be derived. + */ + @Nullable + public static String getTextMediaMimeType(@Nullable String codecs) { + if (codecs == null) { + return null; + } + String[] codecList = Util.splitCodecs(codecs); + for (String codec : codecList) { + @Nullable String mimeType = getMediaMimeType(codec); + if (mimeType != null && isText(mimeType)) { + return mimeType; + } + } + return null; + } + /** * Derives a mimeType from a codec identifier, as defined in RFC 6381. * @@ -274,6 +304,10 @@ public final class MimeTypes { return MimeTypes.APPLICATION_TTML; } else if (codec.startsWith("wvtt")) { return MimeTypes.TEXT_VTT; + } else if (codec.contains("cea708")) { + return MimeTypes.APPLICATION_CEA708; + } else if (codec.contains("eia608") || codec.contains("cea608")) { + return MimeTypes.APPLICATION_CEA608; } else { return getCustomMimeTypeForCodec(codec); } @@ -350,12 +384,7 @@ public final class MimeTypes { return C.TRACK_TYPE_AUDIO; } else if (isVideo(mimeType)) { return C.TRACK_TYPE_VIDEO; - } else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType) - || APPLICATION_CEA708.equals(mimeType) || APPLICATION_MP4CEA608.equals(mimeType) - || APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType) - || APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) - || APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType) - || APPLICATION_PGS.equals(mimeType) || APPLICATION_DVBSUBS.equals(mimeType)) { + } else if (isText(mimeType)) { return C.TRACK_TYPE_TEXT; } else if (APPLICATION_ID3.equals(mimeType) || APPLICATION_EMSG.equals(mimeType) diff --git a/library/common/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java b/library/common/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java index 6224b7b60e..e88385bbca 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java @@ -25,6 +25,29 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class MimeTypesTest { + @Test + public void isText_returnsCorrectResult() { + assertThat(MimeTypes.isText(MimeTypes.TEXT_VTT)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.TEXT_SSA)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_CEA608)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_CEA708)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_MP4CEA608)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_SUBRIP)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_TTML)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_TX3G)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_MP4VTT)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_VOBSUB)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_PGS)).isTrue(); + assertThat(MimeTypes.isText(MimeTypes.APPLICATION_DVBSUBS)).isTrue(); + assertThat(MimeTypes.isText("text/custom")).isTrue(); + + assertThat(MimeTypes.isText(MimeTypes.VIDEO_MP4)).isFalse(); + assertThat(MimeTypes.isText(MimeTypes.VIDEO_H264)).isFalse(); + assertThat(MimeTypes.isText(MimeTypes.AUDIO_MP4)).isFalse(); + assertThat(MimeTypes.isText(MimeTypes.AUDIO_AAC)).isFalse(); + assertThat(MimeTypes.isText("application/custom")).isFalse(); + } + @Test public void getMediaMimeType_fromValidCodecs_returnsCorrectMimeType() { assertThat(MimeTypes.getMediaMimeType("avc1")).isEqualTo(MimeTypes.VIDEO_H264); @@ -77,6 +100,9 @@ public final class MimeTypesTest { 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); + assertThat(MimeTypes.getMediaMimeType("eia608.")).isEqualTo(MimeTypes.APPLICATION_CEA608); + assertThat(MimeTypes.getMediaMimeType("cea608")).isEqualTo(MimeTypes.APPLICATION_CEA608); + assertThat(MimeTypes.getMediaMimeType("cea708")).isEqualTo(MimeTypes.APPLICATION_CEA708); } @Test diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index a5ed7b4437..e03ade2d48 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -772,10 +772,6 @@ public class DefaultDashChunkSource implements DashChunkSource { || mimeType.startsWith(MimeTypes.APPLICATION_WEBM); } - private static boolean mimeTypeIsRawText(String mimeType) { - return MimeTypes.isText(mimeType) || MimeTypes.APPLICATION_TTML.equals(mimeType); - } - private static @Nullable ChunkExtractorWrapper createExtractorWrapper( int trackType, Representation representation, @@ -783,12 +779,15 @@ public class DefaultDashChunkSource implements DashChunkSource { List closedCaptionFormats, @Nullable TrackOutput playerEmsgTrackOutput) { String containerMimeType = representation.format.containerMimeType; - if (mimeTypeIsRawText(containerMimeType)) { - return null; - } Extractor extractor; - if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { - extractor = new RawCcExtractor(representation.format); + if (MimeTypes.isText(containerMimeType)) { + if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { + // RawCC is special because it's a text specific container format. + extractor = new RawCcExtractor(representation.format); + } else { + // All other text types are raw formats that do not need an extractor. + return null; + } } else if (mimeTypeIsWebm(containerMimeType)) { extractor = new MatroskaExtractor(MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES); } else { 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 4420a0f985..1ceeb31c83 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 @@ -337,8 +337,9 @@ public class DashManifestParser extends DefaultHandler supplementalProperties, segmentBase, periodDurationMs); - contentType = checkContentTypeConsistency(contentType, - getContentType(representationInfo.format)); + contentType = + checkContentTypeConsistency( + contentType, MimeTypes.getTrackType(representationInfo.format.sampleMimeType)); representationInfos.add(representationInfo); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase); @@ -389,20 +390,6 @@ public class DashManifestParser extends DefaultHandler : C.TRACK_TYPE_UNKNOWN; } - protected int getContentType(Format format) { - String sampleMimeType = format.sampleMimeType; - if (TextUtils.isEmpty(sampleMimeType)) { - return C.TRACK_TYPE_UNKNOWN; - } else if (MimeTypes.isVideo(sampleMimeType)) { - return C.TRACK_TYPE_VIDEO; - } else if (MimeTypes.isAudio(sampleMimeType)) { - return C.TRACK_TYPE_AUDIO; - } else if (mimeTypeIsRawText(sampleMimeType)) { - return C.TRACK_TYPE_TEXT; - } - return C.TRACK_TYPE_UNKNOWN; - } - /** * Parses a ContentProtection element. * @@ -620,7 +607,7 @@ public class DashManifestParser extends DefaultHandler formatBuilder.setWidth(width).setHeight(height).setFrameRate(frameRate); } else if (MimeTypes.isAudio(sampleMimeType)) { formatBuilder.setChannelCount(audioChannels).setSampleRate(audioSamplingRate); - } else if (mimeTypeIsRawText(sampleMimeType)) { + } else if (MimeTypes.isText(sampleMimeType)) { int accessibilityChannel = Format.NO_VALUE; if (MimeTypes.APPLICATION_CEA608.equals(sampleMimeType)) { accessibilityChannel = parseCea608AccessibilityChannel(accessibilityDescriptors); @@ -1310,43 +1297,19 @@ public class DashManifestParser extends DefaultHandler return MimeTypes.getAudioMediaMimeType(codecs); } else if (MimeTypes.isVideo(containerMimeType)) { return MimeTypes.getVideoMediaMimeType(codecs); - } else if (mimeTypeIsRawText(containerMimeType)) { + } else if (MimeTypes.isText(containerMimeType)) { + if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { + // RawCC is special because it's a text specific container format. + return MimeTypes.getTextMediaMimeType(codecs); + } + // All other text types are raw formats. return containerMimeType; } else if (MimeTypes.APPLICATION_MP4.equals(containerMimeType)) { - if (codecs != null) { - if (codecs.startsWith("stpp")) { - return MimeTypes.APPLICATION_TTML; - } else if (codecs.startsWith("wvtt")) { - return MimeTypes.APPLICATION_MP4VTT; - } - } - } else if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { - if (codecs != null) { - if (codecs.contains("cea708")) { - return MimeTypes.APPLICATION_CEA708; - } else if (codecs.contains("eia608") || codecs.contains("cea608")) { - return MimeTypes.APPLICATION_CEA608; - } - } - return null; + return MimeTypes.getMediaMimeType(codecs); } return null; } - /** - * Returns whether a mimeType is a text sample mimeType. - * - * @param mimeType The mimeType. - * @return Whether the mimeType is a text sample mimeType. - */ - private static boolean mimeTypeIsRawText(@Nullable String mimeType) { - return MimeTypes.isText(mimeType) - || MimeTypes.APPLICATION_TTML.equals(mimeType) - || MimeTypes.APPLICATION_MP4VTT.equals(mimeType) - || MimeTypes.APPLICATION_CEA708.equals(mimeType) - || MimeTypes.APPLICATION_CEA608.equals(mimeType); - } - /** * Checks two languages for consistency, returning the consistent language, or throwing an {@link * IllegalStateException} if the languages are inconsistent.