1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.mozilla.thirdparty.com.google.android.exoplayer2.extractor;
17 
18 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.amr.AmrExtractor;
19 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.flac.FlacExtractor;
20 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.flv.FlvExtractor;
21 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
22 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
23 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
24 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
25 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ogg.OggExtractor;
26 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
27 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
28 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
29 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
30 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.PsExtractor;
31 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.TsExtractor;
32 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.ts.TsPayloadReader;
33 import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.wav.WavExtractor;
34 import org.mozilla.thirdparty.com.google.android.exoplayer2.util.TimestampAdjuster;
35 import java.lang.reflect.Constructor;
36 
37 /**
38  * An {@link ExtractorsFactory} that provides an array of extractors for the following formats:
39  *
40  * <ul>
41  *   <li>MP4, including M4A ({@link Mp4Extractor})
42  *   <li>fMP4 ({@link FragmentedMp4Extractor})
43  *   <li>Matroska and WebM ({@link MatroskaExtractor})
44  *   <li>Ogg Vorbis/FLAC ({@link OggExtractor}
45  *   <li>MP3 ({@link Mp3Extractor})
46  *   <li>AAC ({@link AdtsExtractor})
47  *   <li>MPEG TS ({@link TsExtractor})
48  *   <li>MPEG PS ({@link PsExtractor})
49  *   <li>FLV ({@link FlvExtractor})
50  *   <li>WAV ({@link WavExtractor})
51  *   <li>AC3 ({@link Ac3Extractor})
52  *   <li>AC4 ({@link Ac4Extractor})
53  *   <li>AMR ({@link AmrExtractor})
54  *   <li>FLAC
55  *       <ul>
56  *         <li>If available, the FLAC extension extractor is used.
57  *         <li>Otherwise, the core {@link FlacExtractor} is used. Note that Android devices do not
58  *             generally include a FLAC decoder before API 27. This can be worked around by using
59  *             the FLAC extension or the FFmpeg extension.
60  *       </ul>
61  * </ul>
62  */
63 public final class DefaultExtractorsFactory implements ExtractorsFactory {
64 
65   private static final Constructor<? extends Extractor> FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR;
66 
67   static {
68     Constructor<? extends Extractor> flacExtensionExtractorConstructor = null;
69     try {
70       // LINT.IfChange
71       @SuppressWarnings("nullness:argument.type.incompatible")
72       boolean isFlacNativeLibraryAvailable =
73           Boolean.TRUE.equals(
74               Class.forName("com.google.android.exoplayer2.ext.flac.FlacLibrary")
75                   .getMethod("isAvailable")
76                   .invoke(/* obj= */ null));
77       if (isFlacNativeLibraryAvailable) {
78         flacExtensionExtractorConstructor =
79             Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor")
80                 .asSubclass(Extractor.class)
81                 .getConstructor();
82       }
83       // LINT.ThenChange(../../../../../../../../proguard-rules.txt)
84     } catch (ClassNotFoundException e) {
85       // Expected if the app was built without the FLAC extension.
86     } catch (Exception e) {
87       // The FLAC extension is present, but instantiation failed.
88       throw new RuntimeException("Error instantiating FLAC extension", e);
89     }
90     FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR = flacExtensionExtractorConstructor;
91   }
92 
93   private boolean constantBitrateSeekingEnabled;
94   private @AdtsExtractor.Flags int adtsFlags;
95   private @AmrExtractor.Flags int amrFlags;
96   private @MatroskaExtractor.Flags int matroskaFlags;
97   private @Mp4Extractor.Flags int mp4Flags;
98   private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags;
99   private @Mp3Extractor.Flags int mp3Flags;
100   private @TsExtractor.Mode int tsMode;
101   private @DefaultTsPayloadReaderFactory.Flags int tsFlags;
102 
DefaultExtractorsFactory()103   public DefaultExtractorsFactory() {
104     tsMode = TsExtractor.MODE_SINGLE_PMT;
105   }
106 
107   /**
108    * Convenience method to set whether approximate seeking using constant bitrate assumptions should
109    * be enabled for all extractors that support it. If set to true, the flags required to enable
110    * this functionality will be OR'd with those passed to the setters when creating extractor
111    * instances. If set to false then the flags passed to the setters will be used without
112    * modification.
113    *
114    * @param constantBitrateSeekingEnabled Whether approximate seeking using a constant bitrate
115    *     assumption should be enabled for all extractors that support it.
116    * @return The factory, for convenience.
117    */
setConstantBitrateSeekingEnabled( boolean constantBitrateSeekingEnabled)118   public synchronized DefaultExtractorsFactory setConstantBitrateSeekingEnabled(
119       boolean constantBitrateSeekingEnabled) {
120     this.constantBitrateSeekingEnabled = constantBitrateSeekingEnabled;
121     return this;
122   }
123 
124   /**
125    * Sets flags for {@link AdtsExtractor} instances created by the factory.
126    *
127    * @see AdtsExtractor#AdtsExtractor(int)
128    * @param flags The flags to use.
129    * @return The factory, for convenience.
130    */
setAdtsExtractorFlags( @dtsExtractor.Flags int flags)131   public synchronized DefaultExtractorsFactory setAdtsExtractorFlags(
132       @AdtsExtractor.Flags int flags) {
133     this.adtsFlags = flags;
134     return this;
135   }
136 
137   /**
138    * Sets flags for {@link AmrExtractor} instances created by the factory.
139    *
140    * @see AmrExtractor#AmrExtractor(int)
141    * @param flags The flags to use.
142    * @return The factory, for convenience.
143    */
setAmrExtractorFlags(@mrExtractor.Flags int flags)144   public synchronized DefaultExtractorsFactory setAmrExtractorFlags(@AmrExtractor.Flags int flags) {
145     this.amrFlags = flags;
146     return this;
147   }
148 
149   /**
150    * Sets flags for {@link MatroskaExtractor} instances created by the factory.
151    *
152    * @see MatroskaExtractor#MatroskaExtractor(int)
153    * @param flags The flags to use.
154    * @return The factory, for convenience.
155    */
setMatroskaExtractorFlags( @atroskaExtractor.Flags int flags)156   public synchronized DefaultExtractorsFactory setMatroskaExtractorFlags(
157       @MatroskaExtractor.Flags int flags) {
158     this.matroskaFlags = flags;
159     return this;
160   }
161 
162   /**
163    * Sets flags for {@link Mp4Extractor} instances created by the factory.
164    *
165    * @see Mp4Extractor#Mp4Extractor(int)
166    * @param flags The flags to use.
167    * @return The factory, for convenience.
168    */
setMp4ExtractorFlags(@p4Extractor.Flags int flags)169   public synchronized DefaultExtractorsFactory setMp4ExtractorFlags(@Mp4Extractor.Flags int flags) {
170     this.mp4Flags = flags;
171     return this;
172   }
173 
174   /**
175    * Sets flags for {@link FragmentedMp4Extractor} instances created by the factory.
176    *
177    * @see FragmentedMp4Extractor#FragmentedMp4Extractor(int)
178    * @param flags The flags to use.
179    * @return The factory, for convenience.
180    */
setFragmentedMp4ExtractorFlags( @ragmentedMp4Extractor.Flags int flags)181   public synchronized DefaultExtractorsFactory setFragmentedMp4ExtractorFlags(
182       @FragmentedMp4Extractor.Flags int flags) {
183     this.fragmentedMp4Flags = flags;
184     return this;
185   }
186 
187   /**
188    * Sets flags for {@link Mp3Extractor} instances created by the factory.
189    *
190    * @see Mp3Extractor#Mp3Extractor(int)
191    * @param flags The flags to use.
192    * @return The factory, for convenience.
193    */
setMp3ExtractorFlags(@p3Extractor.Flags int flags)194   public synchronized DefaultExtractorsFactory setMp3ExtractorFlags(@Mp3Extractor.Flags int flags) {
195     mp3Flags = flags;
196     return this;
197   }
198 
199   /**
200    * Sets the mode for {@link TsExtractor} instances created by the factory.
201    *
202    * @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory)
203    * @param mode The mode to use.
204    * @return The factory, for convenience.
205    */
setTsExtractorMode(@sExtractor.Mode int mode)206   public synchronized DefaultExtractorsFactory setTsExtractorMode(@TsExtractor.Mode int mode) {
207     tsMode = mode;
208     return this;
209   }
210 
211   /**
212    * Sets flags for {@link DefaultTsPayloadReaderFactory}s used by {@link TsExtractor} instances
213    * created by the factory.
214    *
215    * @see TsExtractor#TsExtractor(int)
216    * @param flags The flags to use.
217    * @return The factory, for convenience.
218    */
setTsExtractorFlags( @efaultTsPayloadReaderFactory.Flags int flags)219   public synchronized DefaultExtractorsFactory setTsExtractorFlags(
220       @DefaultTsPayloadReaderFactory.Flags int flags) {
221     tsFlags = flags;
222     return this;
223   }
224 
225   @Override
createExtractors()226   public synchronized Extractor[] createExtractors() {
227     Extractor[] extractors = new Extractor[14];
228     extractors[0] = new MatroskaExtractor(matroskaFlags);
229     extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags);
230     extractors[2] = new Mp4Extractor(mp4Flags);
231     extractors[3] =
232         new Mp3Extractor(
233             mp3Flags
234                 | (constantBitrateSeekingEnabled
235                     ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
236                     : 0));
237     extractors[4] =
238         new AdtsExtractor(
239             adtsFlags
240                 | (constantBitrateSeekingEnabled
241                     ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
242                     : 0));
243     extractors[5] = new Ac3Extractor();
244     extractors[6] = new TsExtractor(tsMode, tsFlags);
245     extractors[7] = new FlvExtractor();
246     extractors[8] = new OggExtractor();
247     extractors[9] = new PsExtractor();
248     extractors[10] = new WavExtractor();
249     extractors[11] =
250         new AmrExtractor(
251             amrFlags
252                 | (constantBitrateSeekingEnabled
253                     ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
254                     : 0));
255     extractors[12] = new Ac4Extractor();
256     if (FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR != null) {
257       try {
258         extractors[13] = FLAC_EXTENSION_EXTRACTOR_CONSTRUCTOR.newInstance();
259       } catch (Exception e) {
260         // Should never happen.
261         throw new IllegalStateException("Unexpected error creating FLAC extractor", e);
262       }
263     } else {
264       extractors[13] = new FlacExtractor();
265     }
266     return extractors;
267   }
268 
269 }
270