diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java b/library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java
index d2f925d718..c68cbe0d31 100644
--- a/library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java
+++ b/library/common/src/main/java/com/google/android/exoplayer2/util/FileTypes.java
@@ -35,13 +35,14 @@ public final class FileTypes {
/**
* File types. One of {@link #UNKNOWN}, {@link #AC3}, {@link #AC4}, {@link #ADTS}, {@link #AMR},
* {@link #FLAC}, {@link #FLV}, {@link #MATROSKA}, {@link #MP3}, {@link #MP4}, {@link #OGG},
- * {@link #PS}, {@link #TS}, {@link #WAV}, {@link #WEBVTT} and {@link #JPEG}.
+ * {@link #PS}, {@link #TS}, {@link #WAV}, {@link #WEBVTT}, {@link #JPEG} and {@link #MIDI}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({
- UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG
+ UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG,
+ MIDI
})
public @interface Type {}
/** Unknown file type. */
@@ -76,6 +77,8 @@ public final class FileTypes {
public static final int WEBVTT = 13;
/** File type for the JPEG format. */
public static final int JPEG = 14;
+ /** File type for the MIDI format. */
+ public static final int MIDI = 15;
@VisibleForTesting /* package */ static final String HEADER_CONTENT_TYPE = "Content-Type";
@@ -87,6 +90,9 @@ public final class FileTypes {
private static final String EXTENSION_AMR = ".amr";
private static final String EXTENSION_FLAC = ".flac";
private static final String EXTENSION_FLV = ".flv";
+ private static final String EXTENSION_MID = ".mid";
+ private static final String EXTENSION_MIDI = ".midi";
+ private static final String EXTENSION_SMF = ".smf";
private static final String EXTENSION_PREFIX_MK = ".mk";
private static final String EXTENSION_WEBM = ".webm";
private static final String EXTENSION_PREFIX_OG = ".og";
@@ -145,6 +151,8 @@ public final class FileTypes {
return FileTypes.FLAC;
case MimeTypes.VIDEO_FLV:
return FileTypes.FLV;
+ case MimeTypes.AUDIO_MIDI:
+ return FileTypes.MIDI;
case MimeTypes.VIDEO_MATROSKA:
case MimeTypes.AUDIO_MATROSKA:
case MimeTypes.VIDEO_WEBM:
@@ -191,6 +199,10 @@ public final class FileTypes {
return FileTypes.FLAC;
} else if (filename.endsWith(EXTENSION_FLV)) {
return FileTypes.FLV;
+ } else if (filename.endsWith(EXTENSION_MID)
+ || filename.endsWith(EXTENSION_MIDI)
+ || filename.endsWith(EXTENSION_SMF)) {
+ return FileTypes.MIDI;
} else if (filename.startsWith(
EXTENSION_PREFIX_MK,
/* toffset= */ filename.length() - (EXTENSION_PREFIX_MK.length() + 1))
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 1c2d3fa37a..e78bfb93e8 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
@@ -43,6 +43,7 @@ import com.google.android.exoplayer2.extractor.wav.WavExtractor;
import com.google.android.exoplayer2.util.FileTypes;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -75,6 +76,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
* the FLAC extension or the FFmpeg extension.
*
*
JPEG ({@link JpegExtractor})
+ * MIDI, if available, the MIDI extension's {@code
+ * com.google.android.exoplayer2.decoder.midi.MidiExtractor} is used.
*
*/
public final class DefaultExtractorsFactory implements ExtractorsFactory {
@@ -99,9 +102,13 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
FileTypes.AC4,
FileTypes.MP3,
FileTypes.JPEG,
+ FileTypes.MIDI,
};
- private static final FlacExtensionLoader FLAC_EXTENSION_LOADER = new FlacExtensionLoader();
+ private static final ExtensionLoader FLAC_EXTENSION_LOADER =
+ new ExtensionLoader(DefaultExtractorsFactory::getFlacExtractorConstructor);
+ private static final ExtensionLoader MIDI_EXTENSION_LOADER =
+ new ExtensionLoader(DefaultExtractorsFactory::getMidiExtractorConstructor);
private boolean constantBitrateSeekingEnabled;
private boolean constantBitrateSeekingAlwaysEnabled;
@@ -397,6 +404,12 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
case FileTypes.JPEG:
extractors.add(new JpegExtractor());
break;
+ case FileTypes.MIDI:
+ @Nullable Extractor midiExtractor = MIDI_EXTENSION_LOADER.getExtractor();
+ if (midiExtractor != null) {
+ extractors.add(midiExtractor);
+ }
+ break;
case FileTypes.WEBVTT:
case FileTypes.UNKNOWN:
default:
@@ -404,28 +417,63 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
}
- private static final class FlacExtensionLoader {
+ private static Constructor extends Extractor> getMidiExtractorConstructor()
+ throws ClassNotFoundException, NoSuchMethodException {
+ return Class.forName("com.google.android.exoplayer2.decoder.midi.MidiExtractor")
+ .asSubclass(Extractor.class)
+ .getConstructor();
+ }
+
+ @Nullable
+ private static Constructor extends Extractor> getFlacExtractorConstructor()
+ throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
+ IllegalAccessException {
+ @SuppressWarnings("nullness:argument")
+ boolean isFlacNativeLibraryAvailable =
+ Boolean.TRUE.equals(
+ Class.forName("com.google.android.exoplayer2.ext.flac.FlacLibrary")
+ .getMethod("isAvailable")
+ .invoke(/* obj= */ null));
+ if (isFlacNativeLibraryAvailable) {
+ return Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor")
+ .asSubclass(Extractor.class)
+ .getConstructor(int.class);
+ }
+ return null;
+ }
+
+ private static final class ExtensionLoader {
+
+ public interface ConstructorSupplier {
+ @Nullable
+ Constructor extends Extractor> getConstructor()
+ throws InvocationTargetException, IllegalAccessException, NoSuchMethodException,
+ ClassNotFoundException;
+ }
+
+ private final ConstructorSupplier constructorSupplier;
private final AtomicBoolean extensionLoaded;
@GuardedBy("extensionLoaded")
@Nullable
private Constructor extends Extractor> extractorConstructor;
- public FlacExtensionLoader() {
+ public ExtensionLoader(ConstructorSupplier constructorSupplier) {
+ this.constructorSupplier = constructorSupplier;
extensionLoaded = new AtomicBoolean(false);
}
@Nullable
- public Extractor getExtractor(int flags) {
+ public Extractor getExtractor(Object... constructorParams) {
@Nullable
Constructor extends Extractor> extractorConstructor = maybeLoadExtractorConstructor();
if (extractorConstructor == null) {
return null;
}
try {
- return extractorConstructor.newInstance(flags);
+ return extractorConstructor.newInstance(constructorParams);
} catch (Exception e) {
- throw new IllegalStateException("Unexpected error creating FLAC extractor", e);
+ throw new IllegalStateException("Unexpected error creating extractor", e);
}
}
@@ -436,23 +484,12 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
return extractorConstructor;
}
try {
- @SuppressWarnings("nullness:argument")
- boolean isFlacNativeLibraryAvailable =
- Boolean.TRUE.equals(
- Class.forName("com.google.android.exoplayer2.ext.flac.FlacLibrary")
- .getMethod("isAvailable")
- .invoke(/* obj= */ null));
- if (isFlacNativeLibraryAvailable) {
- extractorConstructor =
- Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor")
- .asSubclass(Extractor.class)
- .getConstructor(int.class);
- }
+ return constructorSupplier.getConstructor();
} catch (ClassNotFoundException e) {
- // Expected if the app was built without the FLAC extension.
+ // Expected if the app was built without the extension.
} catch (Exception e) {
- // The FLAC extension is present, but instantiation failed.
- throw new RuntimeException("Error instantiating FLAC extension", e);
+ // The extension is present, but instantiation failed.
+ throw new RuntimeException("Error instantiating extension", e);
}
extensionLoaded.set(true);
return extractorConstructor;