diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index c9dd91cd65..98ba5839fc 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -283,8 +283,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, case Util.TYPE_OTHER: Allocator allocator = new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE); DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter()); - return new ExtractorSampleSource(uri, dataSource, allocator, - C.DEFAULT_MUXED_BUFFER_SIZE, player.getMainHandler(), player, 0); + return new ExtractorSampleSource(uri, dataSource, allocator, C.DEFAULT_MUXED_BUFFER_SIZE, + player.getMainHandler(), player, 0, ExtractorSampleSource.newDefaultExtractors()); default: throw new IllegalStateException("Unsupported type: " + type); } diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java index be30bcd41d..0768a4f143 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.TrackRenderer; +import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.upstream.DefaultAllocator; @@ -83,7 +84,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase { new DefaultDataSource(context, null, Util.getUserAgent(context, "ExoPlayerExtFlacTest"), false), new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT, - new MatroskaExtractor()); + new Extractor[] {new MatroskaExtractor()}); player.setSource(sampleSource); player.setPlayWhenReady(true); Looper.loop(); diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java index d64a91b1fa..32b9b55c8b 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.TrackRenderer; +import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.upstream.DefaultAllocator; @@ -83,7 +84,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase { new DefaultDataSource(context, null, Util.getUserAgent(context, "ExoPlayerExtOpusTest"), false), new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT, - new MatroskaExtractor()); + new Extractor[] {new MatroskaExtractor()}); player.setSource(sampleSource); player.setPlayWhenReady(true); Looper.loop(); diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java index 59314da3c0..d395887eda 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.TrackRenderer; +import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.upstream.DefaultAllocator; @@ -99,7 +100,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase { new DefaultDataSource(context, null, Util.getUserAgent(context, "ExoPlayerExtVP9Test"), false), new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT, - new MatroskaExtractor()); + new Extractor[] {new MatroskaExtractor()}); player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, new VpxVideoSurfaceView(context)); player.setSource(sampleSource); diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java index 7380a8f33c..1a21d8b661 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java @@ -108,94 +108,8 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu private static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1; - /** - * Default extractor classes in priority order. They are referred to indirectly so that it is - * possible to remove unused extractors. - */ - private static final List> DEFAULT_EXTRACTOR_CLASSES; - static { - DEFAULT_EXTRACTOR_CLASSES = new ArrayList<>(); - // Load extractors using reflection so that they can be deleted cleanly. - // Class.forName() appears for each extractor so that automated tools like proguard - // can detect the use of reflection (see http://proguard.sourceforge.net/FAQ.html#forname). - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.mkv.MatroskaExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.mp4.Mp4Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.mp3.Mp3Extractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.ts.AdtsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.ts.TsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.flv.FlvExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.ogg.OggExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.ts.PsExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.extractor.wav.WavExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - try { - DEFAULT_EXTRACTOR_CLASSES.add( - Class.forName("com.google.android.exoplayer.ext.flac.FlacExtractor") - .asSubclass(Extractor.class)); - } catch (ClassNotFoundException e) { - // Extractor not found. - } - } + // Lazily initialized default extractor classes in priority order. + private static List> defaultExtractorClasses; private final Loader loader; private final ExtractorHolder extractorHolder; @@ -235,11 +149,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu * @param allocator An {@link Allocator} from which to obtain memory allocations. * @param requestedBufferSize The requested total buffer size for storing sample data, in bytes. * The actual allocated size may exceed the value passed in if the implementation requires it. - * @param extractors {@link Extractor}s to extract the media stream, in order of decreasing - * priority. If omitted, the default extractors will be used. + * @param extractors {@link Extractor}s to process the media stream. Where the possible formats + * are known, instantiate and inject only instances of the corresponding {@link Extractor}s. + * Where this is not possible, {@link #newDefaultExtractors()} can be used to construct an + * array of default extractors. */ public ExtractorSampleSource(Uri uri, DataSource dataSource, Allocator allocator, - int requestedBufferSize, Extractor... extractors) { + int requestedBufferSize, Extractor[] extractors) { this(uri, dataSource, allocator, requestedBufferSize, MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA, extractors); } @@ -254,12 +170,14 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventSourceId An identifier that gets passed to {@code eventListener} methods. - * @param extractors {@link Extractor}s to extract the media stream, in order of decreasing - * priority. If omitted, the default extractors will be used. + * @param extractors {@link Extractor}s to process the media stream. Where the possible formats + * are known, instantiate and inject only instances of the corresponding {@link Extractor}s. + * Where this is not possible, {@link #newDefaultExtractors()} can be used to construct an + * array of default extractors. */ public ExtractorSampleSource(Uri uri, DataSource dataSource, Allocator allocator, int requestedBufferSize, Handler eventHandler, EventListener eventListener, - int eventSourceId, Extractor... extractors) { + int eventSourceId, Extractor[] extractors) { this(uri, dataSource, allocator, requestedBufferSize, MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA, eventHandler, eventListener, eventSourceId, extractors); } @@ -272,11 +190,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu * The actual allocated size may exceed the value passed in if the implementation requires it. * @param minLoadableRetryCount The minimum number of times that the sample source will retry * if a loading error occurs. - * @param extractors {@link Extractor}s to extract the media stream, in order of decreasing - * priority. If omitted, the default extractors will be used. + * @param extractors {@link Extractor}s to process the media stream. Where the possible formats + * are known, instantiate and inject only instances of the corresponding {@link Extractor}s. + * Where this is not possible, {@link #newDefaultExtractors()} can be used to construct an + * array of default extractors. */ public ExtractorSampleSource(Uri uri, DataSource dataSource, Allocator allocator, - int requestedBufferSize, int minLoadableRetryCount, Extractor... extractors) { + int requestedBufferSize, int minLoadableRetryCount, Extractor[] extractors) { this(uri, dataSource, allocator, requestedBufferSize, minLoadableRetryCount, null, null, 0, extractors); } @@ -293,12 +213,15 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventSourceId An identifier that gets passed to {@code eventListener} methods. - * @param extractors {@link Extractor}s to extract the media stream, in order of decreasing - * priority. If omitted, the default extractors will be used. + * @param extractors {@link Extractor}s to process the media stream. Where the possible formats + * are known, instantiate and inject only instances of the corresponding {@link Extractor}s. + * Where this is not possible {@link #newDefaultExtractors()} can be used to construct an + * array of default extractors. */ public ExtractorSampleSource(Uri uri, DataSource dataSource, Allocator allocator, int requestedBufferSize, int minLoadableRetryCount, Handler eventHandler, - EventListener eventListener, int eventSourceId, Extractor... extractors) { + EventListener eventListener, int eventSourceId, Extractor[] extractors) { + Assertions.checkState(extractors != null && extractors.length > 0); this.uri = uri; this.dataSource = dataSource; this.eventListener = eventListener; @@ -311,21 +234,117 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu int initialMinRetryCount = minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA ? DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND : minLoadableRetryCount; loader = new Loader("Loader:ExtractorSampleSource", initialMinRetryCount); - if (extractors == null || extractors.length == 0) { - extractors = new Extractor[DEFAULT_EXTRACTOR_CLASSES.size()]; - for (int i = 0; i < extractors.length; i++) { - try { - extractors[i] = DEFAULT_EXTRACTOR_CLASSES.get(i).newInstance(); - } catch (Exception e) { - throw new IllegalStateException("Unexpected error creating default extractor", e); - } - } - } extractorHolder = new ExtractorHolder(extractors, this); pendingResetPositionUs = C.UNSET_TIME_US; sampleQueues = new DefaultTrackOutput[0]; } + /** + * Builds default extractors that can be passed to an {@link ExtractorSampleSource} constructor. + * + * @return An array of default extractors. + */ + public static Extractor[] newDefaultExtractors() { + synchronized (ExtractorSampleSource.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.exoplayer.extractor.mkv.MatroskaExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.mp4.Mp4Extractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.mp3.Mp3Extractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.ts.AdtsExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.ts.TsExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.flv.FlvExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.ogg.OggExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.ts.PsExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.extractor.wav.WavExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + try { + extractorClasses.add( + Class.forName("com.google.android.exoplayer.ext.flac.FlacExtractor") + .asSubclass(Extractor.class)); + } catch (ClassNotFoundException e) { + // Extractor not found. + } + defaultExtractorClasses = extractorClasses; + } + } + + Extractor[] extractors = new Extractor[defaultExtractorClasses.size()]; + for (int i = 0; i < extractors.length; i++) { + try { + extractors[i] = defaultExtractorClasses.get(i).newInstance(); + } catch (Exception e) { + // Should never happen. + throw new IllegalStateException("Unexpected error creating default extractor", e); + } + } + return extractors; + } + // SampleSource implementation. @Override