diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/FilenameUtil.java b/library/common/src/main/java/com/google/android/exoplayer2/util/FilenameUtil.java new file mode 100644 index 0000000000..234d855c4e --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/FilenameUtil.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.util; + +import androidx.annotation.IntDef; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Filename related utility methods. */ +public final class FilenameUtil { + + /** + * File formats. One of {@link #FILE_FORMAT_UNKNOWN}, {@link #FILE_FORMAT_AC3}, {@link + * #FILE_FORMAT_AC4}, {@link #FILE_FORMAT_ADTS}, {@link #FILE_FORMAT_AMR}, {@link + * #FILE_FORMAT_FLAC}, {@link #FILE_FORMAT_FLV}, {@link #FILE_FORMAT_MATROSKA}, {@link + * #FILE_FORMAT_MP3}, {@link #FILE_FORMAT_MP4}, {@link #FILE_FORMAT_OGG}, {@link #FILE_FORMAT_PS}, + * {@link #FILE_FORMAT_TS}, {@link #FILE_FORMAT_WAV} and {@link #FILE_FORMAT_WEBVTT}. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + FILE_FORMAT_UNKNOWN, + FILE_FORMAT_AC3, + FILE_FORMAT_AC4, + FILE_FORMAT_ADTS, + FILE_FORMAT_AMR, + FILE_FORMAT_FLAC, + FILE_FORMAT_FLV, + FILE_FORMAT_MATROSKA, + FILE_FORMAT_MP3, + FILE_FORMAT_MP4, + FILE_FORMAT_OGG, + FILE_FORMAT_PS, + FILE_FORMAT_TS, + FILE_FORMAT_WAV, + FILE_FORMAT_WEBVTT + }) + public @interface FileFormat {} + /** Unknown file format. */ + public static final int FILE_FORMAT_UNKNOWN = -1; + /** File format for AC-3 and E-AC-3. */ + public static final int FILE_FORMAT_AC3 = 0; + /** File format for AC-4. */ + public static final int FILE_FORMAT_AC4 = 1; + /** File format for ADTS. */ + public static final int FILE_FORMAT_ADTS = 2; + /** File format for AMR. */ + public static final int FILE_FORMAT_AMR = 3; + /** File format for FLAC. */ + public static final int FILE_FORMAT_FLAC = 4; + /** File format for FLV. */ + public static final int FILE_FORMAT_FLV = 5; + /** File format for Matroska and WebM. */ + public static final int FILE_FORMAT_MATROSKA = 6; + /** File format for MP3. */ + public static final int FILE_FORMAT_MP3 = 7; + /** File format for MP4. */ + public static final int FILE_FORMAT_MP4 = 8; + /** File format for Ogg. */ + public static final int FILE_FORMAT_OGG = 9; + /** File format for MPEG-PS. */ + public static final int FILE_FORMAT_PS = 10; + /** File format for MPEG-TS. */ + public static final int FILE_FORMAT_TS = 11; + /** File format for WAV. */ + public static final int FILE_FORMAT_WAV = 12; + /** File format for WebVTT. */ + public static final int FILE_FORMAT_WEBVTT = 13; + + private static final String FILE_EXTENSION_AC3 = ".ac3"; + private static final String FILE_EXTENSION_EC3 = ".ec3"; + private static final String FILE_EXTENSION_AC4 = ".ac4"; + private static final String FILE_EXTENSION_ADTS = ".adts"; + private static final String FILE_EXTENSION_AAC = ".aac"; + private static final String FILE_EXTENSION_AMR = ".amr"; + private static final String FILE_EXTENSION_FLAC = ".flac"; + private static final String FILE_EXTENSION_FLV = ".flv"; + private static final String FILE_EXTENSION_PREFIX_MK = ".mk"; + private static final String FILE_EXTENSION_WEBM = ".webm"; + private static final String FILE_EXTENSION_PREFIX_OG = ".og"; + private static final String FILE_EXTENSION_OPUS = ".opus"; + private static final String FILE_EXTENSION_MP3 = ".mp3"; + private static final String FILE_EXTENSION_MP4 = ".mp4"; + private static final String FILE_EXTENSION_PREFIX_M4 = ".m4"; + private static final String FILE_EXTENSION_PREFIX_MP4 = ".mp4"; + private static final String FILE_EXTENSION_PREFIX_CMF = ".cmf"; + private static final String FILE_EXTENSION_PS = ".ps"; + private static final String FILE_EXTENSION_MPEG = ".mpeg"; + private static final String FILE_EXTENSION_MPG = ".mpg"; + private static final String FILE_EXTENSION_M2P = ".m2p"; + private static final String FILE_EXTENSION_TS = ".ts"; + private static final String FILE_EXTENSION_PREFIX_TS = ".ts"; + private static final String FILE_EXTENSION_WAV = ".wav"; + private static final String FILE_EXTENSION_WAVE = ".wave"; + private static final String FILE_EXTENSION_VTT = ".vtt"; + private static final String FILE_EXTENSION_WEBVTT = ".webvtt"; + + private FilenameUtil() {} + + /** + * Returns the {@link FileFormat} corresponding to the extension of the provided {@code filename}. + */ + @FileFormat + public static int getFormatFromExtension(String filename) { + if (filename.endsWith(FILE_EXTENSION_AC3) || filename.endsWith(FILE_EXTENSION_EC3)) { + return FILE_FORMAT_AC3; + } else if (filename.endsWith(FILE_EXTENSION_AC4)) { + return FILE_FORMAT_AC4; + } else if (filename.endsWith(FILE_EXTENSION_ADTS) || filename.endsWith(FILE_EXTENSION_AAC)) { + return FILE_FORMAT_ADTS; + } else if (filename.endsWith(FILE_EXTENSION_AMR)) { + return FILE_FORMAT_AMR; + } else if (filename.endsWith(FILE_EXTENSION_FLAC)) { + return FILE_FORMAT_FLAC; + } else if (filename.endsWith(FILE_EXTENSION_FLV)) { + return FILE_FORMAT_FLV; + } else if (filename.startsWith( + FILE_EXTENSION_PREFIX_MK, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_MK.length() + 1)) + || filename.endsWith(FILE_EXTENSION_WEBM)) { + return FILE_FORMAT_MATROSKA; + } else if (filename.endsWith(FILE_EXTENSION_MP3)) { + return FILE_FORMAT_MP3; + } else if (filename.endsWith(FILE_EXTENSION_MP4) + || filename.startsWith( + FILE_EXTENSION_PREFIX_M4, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_M4.length() + 1)) + || filename.startsWith( + FILE_EXTENSION_PREFIX_MP4, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_MP4.length() + 1)) + || filename.startsWith( + FILE_EXTENSION_PREFIX_CMF, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_CMF.length() + 1))) { + return FILE_FORMAT_MP4; + } else if (filename.startsWith( + FILE_EXTENSION_PREFIX_OG, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_OG.length() + 1)) + || filename.endsWith(FILE_EXTENSION_OPUS)) { + return FILE_FORMAT_OGG; + } else if (filename.endsWith(FILE_EXTENSION_PS) + || filename.endsWith(FILE_EXTENSION_MPEG) + || filename.endsWith(FILE_EXTENSION_MPG) + || filename.endsWith(FILE_EXTENSION_M2P)) { + return FILE_FORMAT_PS; + } else if (filename.endsWith(FILE_EXTENSION_TS) + || filename.startsWith( + FILE_EXTENSION_PREFIX_TS, + /* toffset= */ filename.length() - (FILE_EXTENSION_PREFIX_TS.length() + 1))) { + return FILE_FORMAT_TS; + } else if (filename.endsWith(FILE_EXTENSION_WAV) || filename.endsWith(FILE_EXTENSION_WAVE)) { + return FILE_FORMAT_WAV; + } else if (filename.endsWith(FILE_EXTENSION_VTT) || filename.endsWith(FILE_EXTENSION_WEBVTT)) { + return FILE_FORMAT_WEBVTT; + } else { + return FILE_FORMAT_UNKNOWN; + } + } +} diff --git a/library/common/src/test/java/com/google/android/exoplayer2/util/FilenameUtilTest.java b/library/common/src/test/java/com/google/android/exoplayer2/util/FilenameUtilTest.java new file mode 100644 index 0000000000..89270e0f33 --- /dev/null +++ b/library/common/src/test/java/com/google/android/exoplayer2/util/FilenameUtilTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.util; + +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_MATROSKA; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_MP3; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_UNKNOWN; +import static com.google.android.exoplayer2.util.FilenameUtil.getFormatFromExtension; +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link FilenameUtilTest}. */ +@RunWith(AndroidJUnit4.class) +public class FilenameUtilTest { + + @Test + public void getFormatFromExtension_withExtension_returnsExpectedFormat() { + assertThat(getFormatFromExtension("filename.mp3")).isEqualTo(FILE_FORMAT_MP3); + } + + @Test + public void getFormatFromExtension_withExtensionPrefix_returnsExpectedFormat() { + assertThat(getFormatFromExtension("filename.mka")).isEqualTo(FILE_FORMAT_MATROSKA); + } + + @Test + public void getFormatFromExtension_unknownExtension_returnsUnknownFormat() { + assertThat(getFormatFromExtension("filename.unknown")).isEqualTo(FILE_FORMAT_UNKNOWN); + } +} diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java index 2ba2cd83af..5df0f88692 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java @@ -15,6 +15,15 @@ */ package com.google.android.exoplayer2.source.hls; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_AC3; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_AC4; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_ADTS; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_MP3; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_MP4; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_TS; +import static com.google.android.exoplayer2.util.FilenameUtil.FILE_FORMAT_WEBVTT; +import static com.google.android.exoplayer2.util.FilenameUtil.getFormatFromExtension; + import android.net.Uri; import android.text.TextUtils; import androidx.annotation.Nullable; @@ -30,6 +39,7 @@ import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; import com.google.android.exoplayer2.extractor.ts.TsExtractor; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.FilenameUtil; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.TimestampAdjuster; import java.io.EOFException; @@ -43,20 +53,6 @@ import java.util.Map; */ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { - public static final String AAC_FILE_EXTENSION = ".aac"; - public static final String AC3_FILE_EXTENSION = ".ac3"; - public static final String EC3_FILE_EXTENSION = ".ec3"; - public static final String AC4_FILE_EXTENSION = ".ac4"; - public static final String MP3_FILE_EXTENSION = ".mp3"; - public static final String MP4_FILE_EXTENSION = ".mp4"; - public static final String M4_FILE_EXTENSION_PREFIX = ".m4"; - public static final String MP4_FILE_EXTENSION_PREFIX = ".mp4"; - public static final String CMF_FILE_EXTENSION_PREFIX = ".cmf"; - public static final String TS_FILE_EXTENSION = ".ts"; - public static final String TS_FILE_EXTENSION_PREFIX = ".ts"; - public static final String VTT_FILE_EXTENSION = ".vtt"; - public static final String WEBVTT_FILE_EXTENSION = ".webvtt"; - @DefaultTsPayloadReaderFactory.Flags private final int payloadReaderFactoryFlags; private final boolean exposeCea608WhenMissingDeclarations; @@ -196,39 +192,37 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { Format format, @Nullable List muxedCaptionFormats, TimestampAdjuster timestampAdjuster) { - String lastPathSegment = uri.getLastPathSegment(); - if (lastPathSegment == null) { - lastPathSegment = ""; - } - if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType) - || lastPathSegment.endsWith(WEBVTT_FILE_EXTENSION) - || lastPathSegment.endsWith(VTT_FILE_EXTENSION)) { + if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType)) { return new WebvttExtractor(format.language, timestampAdjuster); - } else if (lastPathSegment.endsWith(AAC_FILE_EXTENSION)) { - return new AdtsExtractor(); - } else if (lastPathSegment.endsWith(AC3_FILE_EXTENSION) - || lastPathSegment.endsWith(EC3_FILE_EXTENSION)) { - return new Ac3Extractor(); - } else if (lastPathSegment.endsWith(AC4_FILE_EXTENSION)) { - return new Ac4Extractor(); - } else if (lastPathSegment.endsWith(MP3_FILE_EXTENSION)) { - return new Mp3Extractor(/* flags= */ 0, /* forcedFirstSampleTimestampUs= */ 0); - } else if (lastPathSegment.endsWith(MP4_FILE_EXTENSION) - || lastPathSegment.startsWith(M4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 4) - || lastPathSegment.startsWith(MP4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5) - || lastPathSegment.startsWith(CMF_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5)) { - return createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats); - } else if (lastPathSegment.endsWith(TS_FILE_EXTENSION) - || lastPathSegment.startsWith(TS_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 4)) { - return createTsExtractor( - payloadReaderFactoryFlags, - exposeCea608WhenMissingDeclarations, - format, - muxedCaptionFormats, - timestampAdjuster); - } else { + } + String filename = uri.getLastPathSegment(); + if (filename == null) { return null; } + @FilenameUtil.FileFormat int fileFormat = getFormatFromExtension(filename); + switch (fileFormat) { + case FILE_FORMAT_WEBVTT: + return new WebvttExtractor(format.language, timestampAdjuster); + case FILE_FORMAT_ADTS: + return new AdtsExtractor(); + case FILE_FORMAT_AC3: + return new Ac3Extractor(); + case FILE_FORMAT_AC4: + return new Ac4Extractor(); + case FILE_FORMAT_MP3: + return new Mp3Extractor(/* flags= */ 0, /* forcedFirstSampleTimestampUs= */ 0); + case FILE_FORMAT_MP4: + return createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats); + case FILE_FORMAT_TS: + return createTsExtractor( + payloadReaderFactoryFlags, + exposeCea608WhenMissingDeclarations, + format, + muxedCaptionFormats, + timestampAdjuster); + default: + return null; + } } private static TsExtractor createTsExtractor(