Skip to content

Commit

Permalink
Remove reflection + make it easy to set extractor flags
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ojw28 committed Apr 18, 2017
1 parent 1dc8bb5 commit 85adecf
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 212 deletions.
1 change: 0 additions & 1 deletion library/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ android {
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
consumerProguardFiles 'proguard-rules.txt'
}

sourceSets {
Expand Down
10 changes: 0 additions & 10 deletions library/core/proguard-rules.txt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
* <ul>
* <li>MP4, including M4A ({@link com.google.android.exoplayer2.extractor.mp4.Mp4Extractor})</li>
* <li>fMP4 ({@link com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor})</li>
* <li>Matroska and WebM ({@link com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor})
* </li>
* <li>Ogg Vorbis/FLAC ({@link com.google.android.exoplayer2.extractor.ogg.OggExtractor}</li>
* <li>MP3 ({@link com.google.android.exoplayer2.extractor.mp3.Mp3Extractor})</li>
* <li>AAC ({@link com.google.android.exoplayer2.extractor.ts.AdtsExtractor})</li>
* <li>MPEG TS ({@link com.google.android.exoplayer2.extractor.ts.TsExtractor})</li>
* <li>MPEG PS ({@link com.google.android.exoplayer2.extractor.ts.PsExtractor})</li>
* <li>FLV ({@link com.google.android.exoplayer2.extractor.flv.FlvExtractor})</li>
* <li>WAV ({@link com.google.android.exoplayer2.extractor.wav.WavExtractor})</li>
* <li>MP4, including M4A ({@link Mp4Extractor})</li>
* <li>fMP4 ({@link FragmentedMp4Extractor})</li>
* <li>Matroska and WebM ({@link MatroskaExtractor})</li>
* <li>Ogg Vorbis/FLAC ({@link OggExtractor}</li>
* <li>MP3 ({@link Mp3Extractor})</li>
* <li>AAC ({@link AdtsExtractor})</li>
* <li>MPEG TS ({@link TsExtractor})</li>
* <li>MPEG PS ({@link PsExtractor})</li>
* <li>FLV ({@link FlvExtractor})</li>
* <li>WAV ({@link WavExtractor})</li>
* <li>AC3 ({@link Ac3Extractor})</li>
* <li>FLAC (only available if the FLAC extension is built and included)</li>
* </ul>
*/
public final class DefaultExtractorsFactory implements ExtractorsFactory {

// Lazily initialized default extractor classes in priority order.
private static List<Class<? extends Extractor>> defaultExtractorClasses;
private static final Constructor<? extends Extractor> FLAC_EXTRACTOR_CONSTRUCTOR;
static {
Constructor<? extends Extractor> 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<Class<? extends Extractor>> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -54,11 +53,19 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
private final List<Format> closedCaptionFormats;

public DefaultTsPayloadReaderFactory() {
this(0, Collections.<Format>emptyList());
this(0);
}

/**
* @param flags A combination of {@code FLAG_*} values that control the behavior of the created
* readers.
*/
public DefaultTsPayloadReaderFactory(@Flags int flags) {
this(flags, Collections.<Format>emptyList());
}

/**
* @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.
* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;
Expand Down Expand Up @@ -122,7 +123,16 @@ public Extractor[] createExtractors() {
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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
Loading

0 comments on commit 85adecf

Please # to comment.