From 85adecf948a7c30c149e2cf0de8267c30c73f8d4 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 11 Apr 2017 07:36:03 -0700 Subject: [PATCH] Remove reflection + make it easy to set extractor flags The idea of using reflection was so that a developer could delete a package they didn't want and have everything else still compile. However, a developer doing this is likely building from source, in which case editing the factories too is pretty trivial. Removing the reflection makes specifying extractor flags via the default factory easy, and removes the need for special proguard config. Issue: #2657 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152810423 --- library/core/build.gradle | 1 - library/core/proguard-rules.txt | 10 - .../extractor/DefaultExtractorsFactory.java | 209 ++++++++---------- .../ts/DefaultTsPayloadReaderFactory.java | 15 +- .../exoplayer2/extractor/ts/TsExtractor.java | 12 +- .../metadata/MetadataDecoderFactory.java | 40 +--- .../text/SubtitleDecoderFactory.java | 83 +++---- 7 files changed, 158 insertions(+), 212 deletions(-) delete mode 100644 library/core/proguard-rules.txt diff --git a/library/core/build.gradle b/library/core/build.gradle index 49ed791a78..8ab63af26f 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -20,7 +20,6 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion - consumerProguardFiles 'proguard-rules.txt' } sourceSets { diff --git a/library/core/proguard-rules.txt b/library/core/proguard-rules.txt deleted file mode 100644 index c5d752f4c6..0000000000 --- a/library/core/proguard-rules.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Accessed via reflection in SubtitleDecoderFactory.DEFAULT --keepclassmembers class com.google.android.exoplayer2.text.cea.Cea608Decoder { - public (java.lang.String, int); -} --keepclassmembers class com.google.android.exoplayer2.text.cea.Cea708Decoder { - public (int); -} --keepclassmembers class com.google.android.exoplayer2.text.dvb.DvbDecoder { - public (java.util.List); -} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java index 29fad1fbde..4ea8452956 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java @@ -15,141 +15,118 @@ */ package com.google.android.exoplayer2.extractor; -import java.util.ArrayList; -import java.util.List; +import com.google.android.exoplayer2.extractor.flv.FlvExtractor; +import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; +import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; +import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; +import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; +import com.google.android.exoplayer2.extractor.ogg.OggExtractor; +import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; +import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; +import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; +import com.google.android.exoplayer2.extractor.ts.PsExtractor; +import com.google.android.exoplayer2.extractor.ts.TsExtractor; +import com.google.android.exoplayer2.extractor.wav.WavExtractor; +import java.lang.reflect.Constructor; /** * An {@link ExtractorsFactory} that provides an array of extractors for the following formats: * *
    - *
  • MP4, including M4A ({@link com.google.android.exoplayer2.extractor.mp4.Mp4Extractor})
  • - *
  • fMP4 ({@link com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor})
  • - *
  • Matroska and WebM ({@link com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor}) - *
  • - *
  • Ogg Vorbis/FLAC ({@link com.google.android.exoplayer2.extractor.ogg.OggExtractor}
  • - *
  • MP3 ({@link com.google.android.exoplayer2.extractor.mp3.Mp3Extractor})
  • - *
  • AAC ({@link com.google.android.exoplayer2.extractor.ts.AdtsExtractor})
  • - *
  • MPEG TS ({@link com.google.android.exoplayer2.extractor.ts.TsExtractor})
  • - *
  • MPEG PS ({@link com.google.android.exoplayer2.extractor.ts.PsExtractor})
  • - *
  • FLV ({@link com.google.android.exoplayer2.extractor.flv.FlvExtractor})
  • - *
  • WAV ({@link com.google.android.exoplayer2.extractor.wav.WavExtractor})
  • + *
  • MP4, including M4A ({@link Mp4Extractor})
  • + *
  • fMP4 ({@link FragmentedMp4Extractor})
  • + *
  • Matroska and WebM ({@link MatroskaExtractor})
  • + *
  • Ogg Vorbis/FLAC ({@link OggExtractor}
  • + *
  • MP3 ({@link Mp3Extractor})
  • + *
  • AAC ({@link AdtsExtractor})
  • + *
  • MPEG TS ({@link TsExtractor})
  • + *
  • MPEG PS ({@link PsExtractor})
  • + *
  • FLV ({@link FlvExtractor})
  • + *
  • WAV ({@link WavExtractor})
  • + *
  • AC3 ({@link Ac3Extractor})
  • *
  • FLAC (only available if the FLAC extension is built and included)
  • *
*/ public final class DefaultExtractorsFactory implements ExtractorsFactory { - // Lazily initialized default extractor classes in priority order. - private static List> defaultExtractorClasses; + private static final Constructor FLAC_EXTRACTOR_CONSTRUCTOR; + static { + Constructor flacExtractorConstructor = null; + try { + flacExtractorConstructor = + Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor") + .asSubclass(Extractor.class).getConstructor(); + } catch (ClassNotFoundException e) { + // Extractor not found. + } catch (NoSuchMethodException e) { + // Constructor not found. + } + FLAC_EXTRACTOR_CONSTRUCTOR = flacExtractorConstructor; + } + + private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; + private @Mp3Extractor.Flags int mp3Flags; + private @DefaultTsPayloadReaderFactory.Flags int tsFlags; /** - * Creates a new factory for the default extractors. + * Sets flags for {@link FragmentedMp4Extractor} instances created by the factory. + * + * @see FragmentedMp4Extractor#FragmentedMp4Extractor(int) + * @param flags The flags to use. + * @return The factory, for convenience. */ - public DefaultExtractorsFactory() { - synchronized (DefaultExtractorsFactory.class) { - if (defaultExtractorClasses == null) { - // Lazily initialize defaultExtractorClasses. - List> extractorClasses = new ArrayList<>(); - // We reference extractors using reflection so that they can be deleted cleanly. - // Class.forName is used so that automated tools like proguard can detect the use of - // reflection (see http://proguard.sourceforge.net/FAQ.html#forname). - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.mp4.Mp4Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.mp3.Mp3Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.ts.AdtsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.ts.Ac3Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.ts.TsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.flv.FlvExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.ogg.OggExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.ts.PsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.extractor.wav.WavExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - extractorClasses.add( - Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - defaultExtractorClasses = extractorClasses; - } - } + public synchronized DefaultExtractorsFactory setFragmentedMp4ExtractorFlags( + @FragmentedMp4Extractor.Flags int flags) { + this.fragmentedMp4Flags = flags; + return this; + } + + /** + * Sets flags for {@link Mp3Extractor} instances created by the factory. + * + * @see Mp3Extractor#Mp3Extractor(int) + * @param flags The flags to use. + * @return The factory, for convenience. + */ + public synchronized DefaultExtractorsFactory setMp3ExtractorFlags(@Mp3Extractor.Flags int flags) { + mp3Flags = flags; + return this; + } + + /** + * Sets flags for {@link DefaultTsPayloadReaderFactory}s used by {@link TsExtractor} instances + * created by the factory. + * + * @see TsExtractor#TsExtractor(int) + * @param flags The flags to use. + * @return The factory, for convenience. + */ + public synchronized DefaultExtractorsFactory setTsExtractorFlags( + @DefaultTsPayloadReaderFactory.Flags int flags) { + tsFlags = flags; + return this; } @Override - public Extractor[] createExtractors() { - Extractor[] extractors = new Extractor[defaultExtractorClasses.size()]; - for (int i = 0; i < extractors.length; i++) { + public synchronized Extractor[] createExtractors() { + Extractor[] extractors = new Extractor[FLAC_EXTRACTOR_CONSTRUCTOR == null ? 11 : 12]; + extractors[0] = new MatroskaExtractor(); + extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags); + extractors[2] = new Mp4Extractor(); + extractors[3] = new Mp3Extractor(mp3Flags); + extractors[4] = new AdtsExtractor(); + extractors[5] = new Ac3Extractor(); + extractors[6] = new TsExtractor(tsFlags); + extractors[7] = new FlvExtractor(); + extractors[8] = new OggExtractor(); + extractors[9] = new PsExtractor(); + extractors[10] = new WavExtractor(); + if (FLAC_EXTRACTOR_CONSTRUCTOR != null) { try { - extractors[i] = defaultExtractorClasses.get(i).getConstructor().newInstance(); + extractors[11] = FLAC_EXTRACTOR_CONSTRUCTOR.newInstance(); } catch (Exception e) { // Should never happen. - throw new IllegalStateException("Unexpected error creating default extractor", e); + throw new IllegalStateException("Unexpected error creating FLAC extractor", e); } } return extractors; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java index 1e391c3eca..3808d18b9a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -39,8 +39,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact @IntDef(flag = true, value = {FLAG_ALLOW_NON_IDR_KEYFRAMES, FLAG_IGNORE_AAC_STREAM, FLAG_IGNORE_H264_STREAM, FLAG_DETECT_ACCESS_UNITS, FLAG_IGNORE_SPLICE_INFO_STREAM, FLAG_OVERRIDE_CAPTION_DESCRIPTORS}) - public @interface Flags { - } + public @interface Flags {} public static final int FLAG_ALLOW_NON_IDR_KEYFRAMES = 1; public static final int FLAG_IGNORE_AAC_STREAM = 1 << 1; public static final int FLAG_IGNORE_H264_STREAM = 1 << 2; @@ -54,11 +53,19 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact private final List closedCaptionFormats; public DefaultTsPayloadReaderFactory() { - this(0, Collections.emptyList()); + this(0); } /** - * @param flags A combination of {@code FLAG_*} values, which control the behavior of the created + * @param flags A combination of {@code FLAG_*} values that control the behavior of the created + * readers. + */ + public DefaultTsPayloadReaderFactory(@Flags int flags) { + this(flags, Collections.emptyList()); + } + + /** + * @param flags A combination of {@code FLAG_*} values that control the behavior of the created * readers. * @param closedCaptionFormats {@link Format}s to be exposed by payload readers for streams with * embedded closed captions when no caption service descriptors are provided. If diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index e242414ff2..b1c1220c45 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -27,6 +27,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory.Flags; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.util.Assertions; @@ -122,7 +123,16 @@ public final class TsExtractor implements Extractor { private TsPayloadReader id3Reader; public TsExtractor() { - this(MODE_NORMAL, new TimestampAdjuster(0), new DefaultTsPayloadReaderFactory()); + this(0); + } + + /** + * @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory} + * {@code FLAG_*} values that control the behavior of the payload readers. + */ + public TsExtractor(@Flags int defaultTsPayloadReaderFlags) { + this(MODE_NORMAL, new TimestampAdjuster(0), + new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags)); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoderFactory.java index 414a8269d7..028a8eb893 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoderFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoderFactory.java @@ -58,39 +58,23 @@ public interface MetadataDecoderFactory { @Override public boolean supportsFormat(Format format) { - return getDecoderClass(format.sampleMimeType) != null; + String mimeType = format.sampleMimeType; + return MimeTypes.APPLICATION_ID3.equals(mimeType) + || MimeTypes.APPLICATION_EMSG.equals(mimeType) + || MimeTypes.APPLICATION_SCTE35.equals(mimeType); } @Override public MetadataDecoder createDecoder(Format format) { - try { - Class clazz = getDecoderClass(format.sampleMimeType); - if (clazz == null) { + switch (format.sampleMimeType) { + case MimeTypes.APPLICATION_ID3: + return new Id3Decoder(); + case MimeTypes.APPLICATION_EMSG: + return new EventMessageDecoder(); + case MimeTypes.APPLICATION_SCTE35: + return new SpliceInfoDecoder(); + default: throw new IllegalArgumentException("Attempted to create decoder for unsupported format"); - } - return clazz.asSubclass(MetadataDecoder.class).getConstructor().newInstance(); - } catch (Exception e) { - throw new IllegalStateException("Unexpected error instantiating decoder", e); - } - } - - private Class getDecoderClass(String mimeType) { - if (mimeType == null) { - return null; - } - try { - switch (mimeType) { - case MimeTypes.APPLICATION_ID3: - return Class.forName("com.google.android.exoplayer2.metadata.id3.Id3Decoder"); - case MimeTypes.APPLICATION_EMSG: - return Class.forName("com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder"); - case MimeTypes.APPLICATION_SCTE35: - return Class.forName("com.google.android.exoplayer2.metadata.scte35.SpliceInfoDecoder"); - default: - return null; - } - } catch (ClassNotFoundException e) { - return null; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java index 5f318916b5..f65d5a6e55 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java @@ -18,13 +18,13 @@ package com.google.android.exoplayer2.text; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.text.cea.Cea608Decoder; import com.google.android.exoplayer2.text.cea.Cea708Decoder; +import com.google.android.exoplayer2.text.dvb.DvbDecoder; import com.google.android.exoplayer2.text.subrip.SubripDecoder; import com.google.android.exoplayer2.text.ttml.TtmlDecoder; import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder; import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder; import com.google.android.exoplayer2.text.webvtt.WebvttDecoder; import com.google.android.exoplayer2.util.MimeTypes; -import java.util.List; /** * A factory for {@link SubtitleDecoder} instances. @@ -61,68 +61,47 @@ public interface SubtitleDecoderFactory { *
  • TX3G ({@link Tx3gDecoder})
  • *
  • Cea608 ({@link Cea608Decoder})
  • *
  • Cea708 ({@link Cea708Decoder})
  • + *
  • DVB ({@link DvbDecoder})
  • * */ SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() { @Override public boolean supportsFormat(Format format) { - return getDecoderClass(format.sampleMimeType) != null; + String mimeType = format.sampleMimeType; + return MimeTypes.TEXT_VTT.equals(mimeType) + || MimeTypes.APPLICATION_TTML.equals(mimeType) + || MimeTypes.APPLICATION_MP4VTT.equals(mimeType) + || MimeTypes.APPLICATION_SUBRIP.equals(mimeType) + || MimeTypes.APPLICATION_TX3G.equals(mimeType) + || MimeTypes.APPLICATION_CEA608.equals(mimeType) + || MimeTypes.APPLICATION_MP4CEA608.equals(mimeType) + || MimeTypes.APPLICATION_CEA708.equals(mimeType) + || MimeTypes.APPLICATION_DVBSUBS.equals(mimeType); } @Override public SubtitleDecoder createDecoder(Format format) { - try { - Class clazz = getDecoderClass(format.sampleMimeType); - if (clazz == null) { + switch (format.sampleMimeType) { + case MimeTypes.TEXT_VTT: + return new WebvttDecoder(); + case MimeTypes.APPLICATION_MP4VTT: + return new Mp4WebvttDecoder(); + case MimeTypes.APPLICATION_TTML: + return new TtmlDecoder(); + case MimeTypes.APPLICATION_SUBRIP: + return new SubripDecoder(); + case MimeTypes.APPLICATION_TX3G: + return new Tx3gDecoder(); + case MimeTypes.APPLICATION_CEA608: + case MimeTypes.APPLICATION_MP4CEA608: + return new Cea608Decoder(format.sampleMimeType, format.accessibilityChannel); + case MimeTypes.APPLICATION_CEA708: + return new Cea708Decoder(format.accessibilityChannel); + case MimeTypes.APPLICATION_DVBSUBS: + return new DvbDecoder(format.initializationData); + default: throw new IllegalArgumentException("Attempted to create decoder for unsupported format"); - } - if (format.sampleMimeType.equals(MimeTypes.APPLICATION_CEA608) - || format.sampleMimeType.equals(MimeTypes.APPLICATION_MP4CEA608)) { - return clazz.asSubclass(SubtitleDecoder.class).getConstructor(String.class, Integer.TYPE) - .newInstance(format.sampleMimeType, format.accessibilityChannel); - } else if (format.sampleMimeType.equals(MimeTypes.APPLICATION_CEA708)) { - return clazz.asSubclass(SubtitleDecoder.class).getConstructor(Integer.TYPE) - .newInstance(format.accessibilityChannel); - } else if (format.sampleMimeType.equals(MimeTypes.APPLICATION_DVBSUBS)) { - return clazz.asSubclass(SubtitleDecoder.class).getConstructor(List.class) - .newInstance(format.initializationData); - } else { - return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance(); - } - } catch (Exception e) { - throw new IllegalStateException("Unexpected error instantiating decoder", e); - } - } - - private Class getDecoderClass(String mimeType) { - if (mimeType == null) { - return null; - } - try { - switch (mimeType) { - case MimeTypes.TEXT_VTT: - return Class.forName("com.google.android.exoplayer2.text.webvtt.WebvttDecoder"); - case MimeTypes.APPLICATION_TTML: - return Class.forName("com.google.android.exoplayer2.text.ttml.TtmlDecoder"); - case MimeTypes.APPLICATION_MP4VTT: - return Class.forName("com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder"); - case MimeTypes.APPLICATION_SUBRIP: - return Class.forName("com.google.android.exoplayer2.text.subrip.SubripDecoder"); - case MimeTypes.APPLICATION_TX3G: - return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder"); - case MimeTypes.APPLICATION_CEA608: - case MimeTypes.APPLICATION_MP4CEA608: - return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder"); - case MimeTypes.APPLICATION_CEA708: - return Class.forName("com.google.android.exoplayer2.text.cea.Cea708Decoder"); - case MimeTypes.APPLICATION_DVBSUBS: - return Class.forName("com.google.android.exoplayer2.text.dvb.DvbDecoder"); - default: - return null; - } - } catch (ClassNotFoundException e) { - return null; } }