From 703fb777c4fc5fca35ff4428f694eb0cd42d882f Mon Sep 17 00:00:00 2001 From: kimvde Date: Wed, 8 Apr 2020 10:20:36 +0100 Subject: [PATCH] Optimize extractors sniffing order Issue: #6410 PiperOrigin-RevId: 305436352 --- RELEASENOTES.md | 3 + .../extractor/DefaultExtractorsFactory.java | 62 ++++++++-------- .../hls/DefaultHlsExtractorFactory.java | 70 +++++++++++-------- 3 files changed, 77 insertions(+), 58 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4eb7bbd466..09597ece58 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -109,6 +109,9 @@ * Cast extension: Implement playlist API and deprecate the old queue manipulation API. * Demo app: Retain previous position in list of samples. +* Change the order of extractors for sniffing to reduce start-up latency in + `DefaultExtractorsFactory` and `DefaultHlsExtractorsFactory` + ([#6410](https://github.com/google/ExoPlayer/issues/6410)). ### 2.11.4 (2020-04-08) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java index b7a5da10ab..9306a146d5 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java @@ -241,44 +241,46 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { @Override public synchronized Extractor[] createExtractors() { Extractor[] extractors = new Extractor[14]; - extractors[0] = new MatroskaExtractor(matroskaFlags); - extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags); - extractors[2] = new Mp4Extractor(mp4Flags); - extractors[3] = - new Mp3Extractor( - mp3Flags - | (constantBitrateSeekingEnabled - ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0)); - extractors[4] = - new AdtsExtractor( - adtsFlags - | (constantBitrateSeekingEnabled - ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0)); - extractors[5] = new Ac3Extractor(); - extractors[6] = new TsExtractor(tsMode, tsFlags); - extractors[7] = new FlvExtractor(); - extractors[8] = new OggExtractor(); - extractors[9] = new PsExtractor(); - extractors[10] = new WavExtractor(); - extractors[11] = - new AmrExtractor( - amrFlags - | (constantBitrateSeekingEnabled - ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0)); - extractors[12] = new Ac4Extractor(); + // Extractors order is optimized according to + // https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ. + extractors[0] = new FlvExtractor(); if (FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR != null) { try { - extractors[13] = FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR.newInstance(); + extractors[1] = FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR.newInstance(); } catch (Exception e) { // Should never happen. throw new IllegalStateException("Unexpected error creating FLAC extractor", e); } } else { - extractors[13] = new FlacExtractor(coreFlacFlags); + extractors[1] = new FlacExtractor(coreFlacFlags); } + extractors[2] = new WavExtractor(); + extractors[3] = new FragmentedMp4Extractor(fragmentedMp4Flags); + extractors[4] = new Mp4Extractor(mp4Flags); + extractors[5] = + new AmrExtractor( + amrFlags + | (constantBitrateSeekingEnabled + ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING + : 0)); + extractors[6] = new PsExtractor(); + extractors[7] = new OggExtractor(); + extractors[8] = new TsExtractor(tsMode, tsFlags); + extractors[9] = new MatroskaExtractor(matroskaFlags); + extractors[10] = + new AdtsExtractor( + adtsFlags + | (constantBitrateSeekingEnabled + ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING + : 0)); + extractors[11] = new Ac3Extractor(); + extractors[12] = new Ac4Extractor(); + extractors[13] = + new Mp3Extractor( + mp3Flags + | (constantBitrateSeekingEnabled + ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING + : 0)); return extractors; } 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 3f4e037b4c..2ba2cd83af 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 @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; 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.MimeTypes; import com.google.android.exoplayer2.util.TimestampAdjuster; import java.io.EOFException; @@ -51,6 +52,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { 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"; @@ -94,7 +97,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { throws IOException { if (previousExtractor != null) { - // A extractor has already been successfully used. Return one of the same type. + // An extractor has already been successfully used. Return one of the same type. if (isReusable(previousExtractor)) { return buildResult(previousExtractor); } else { @@ -108,15 +111,29 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } // Try selecting the extractor by the file extension. + @Nullable Extractor extractorByFileExtension = createExtractorByFileExtension(uri, format, muxedCaptionFormats, timestampAdjuster); extractorInput.resetPeekPosition(); - if (sniffQuietly(extractorByFileExtension, extractorInput)) { + if (extractorByFileExtension != null + && sniffQuietly(extractorByFileExtension, extractorInput)) { return buildResult(extractorByFileExtension); } // We need to manually sniff each known type, without retrying the one selected by file - // extension. + // extension. Extractors order is optimized according to + // https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ. + + // Extractor to be used if the type is not recognized. + @Nullable Extractor fallBackExtractor = extractorByFileExtension; + + if (!(extractorByFileExtension instanceof FragmentedMp4Extractor)) { + FragmentedMp4Extractor fragmentedMp4Extractor = + createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats); + if (sniffQuietly(fragmentedMp4Extractor, extractorInput)) { + return buildResult(fragmentedMp4Extractor); + } + } if (!(extractorByFileExtension instanceof WebvttExtractor)) { WebvttExtractor webvttExtractor = new WebvttExtractor(format.language, timestampAdjuster); @@ -125,6 +142,22 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } } + if (!(extractorByFileExtension instanceof TsExtractor)) { + TsExtractor tsExtractor = + createTsExtractor( + payloadReaderFactoryFlags, + exposeCea608WhenMissingDeclarations, + format, + muxedCaptionFormats, + timestampAdjuster); + if (sniffQuietly(tsExtractor, extractorInput)) { + return buildResult(tsExtractor); + } + if (fallBackExtractor == null) { + fallBackExtractor = tsExtractor; + } + } + if (!(extractorByFileExtension instanceof AdtsExtractor)) { AdtsExtractor adtsExtractor = new AdtsExtractor(); if (sniffQuietly(adtsExtractor, extractorInput)) { @@ -154,31 +187,10 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } } - if (!(extractorByFileExtension instanceof FragmentedMp4Extractor)) { - FragmentedMp4Extractor fragmentedMp4Extractor = - createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats); - if (sniffQuietly(fragmentedMp4Extractor, extractorInput)) { - return buildResult(fragmentedMp4Extractor); - } - } - - if (!(extractorByFileExtension instanceof TsExtractor)) { - TsExtractor tsExtractor = - createTsExtractor( - payloadReaderFactoryFlags, - exposeCea608WhenMissingDeclarations, - format, - muxedCaptionFormats, - timestampAdjuster); - if (sniffQuietly(tsExtractor, extractorInput)) { - return buildResult(tsExtractor); - } - } - - // Fall back on the extractor created by file extension. - return buildResult(extractorByFileExtension); + return buildResult(Assertions.checkNotNull(fallBackExtractor)); } + @Nullable private Extractor createExtractorByFileExtension( Uri uri, Format format, @@ -206,14 +218,16 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { || lastPathSegment.startsWith(MP4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5) || lastPathSegment.startsWith(CMF_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 5)) { return createFragmentedMp4Extractor(timestampAdjuster, format, muxedCaptionFormats); - } else { - // For any other file extension, we assume TS format. + } else if (lastPathSegment.endsWith(TS_FILE_EXTENSION) + || lastPathSegment.startsWith(TS_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 4)) { return createTsExtractor( payloadReaderFactoryFlags, exposeCea608WhenMissingDeclarations, format, muxedCaptionFormats, timestampAdjuster); + } else { + return null; } }