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.util;
17 
18 import android.text.TextUtils;
19 import androidx.annotation.Nullable;
20 import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
21 import java.util.ArrayList;
22 
23 /**
24  * Defines common MIME types and helper methods.
25  */
26 public final class MimeTypes {
27 
28   public static final String BASE_TYPE_VIDEO = "video";
29   public static final String BASE_TYPE_AUDIO = "audio";
30   public static final String BASE_TYPE_TEXT = "text";
31   public static final String BASE_TYPE_APPLICATION = "application";
32 
33   public static final String VIDEO_MP4 = BASE_TYPE_VIDEO + "/mp4";
34   public static final String VIDEO_WEBM = BASE_TYPE_VIDEO + "/webm";
35   public static final String VIDEO_H263 = BASE_TYPE_VIDEO + "/3gpp";
36   public static final String VIDEO_H264 = BASE_TYPE_VIDEO + "/avc";
37   public static final String VIDEO_H265 = BASE_TYPE_VIDEO + "/hevc";
38   public static final String VIDEO_VP8 = BASE_TYPE_VIDEO + "/x-vnd.on2.vp8";
39   public static final String VIDEO_VP9 = BASE_TYPE_VIDEO + "/x-vnd.on2.vp9";
40   public static final String VIDEO_AV1 = BASE_TYPE_VIDEO + "/av01";
41   public static final String VIDEO_MP4V = BASE_TYPE_VIDEO + "/mp4v-es";
42   public static final String VIDEO_MPEG = BASE_TYPE_VIDEO + "/mpeg";
43   public static final String VIDEO_MPEG2 = BASE_TYPE_VIDEO + "/mpeg2";
44   public static final String VIDEO_VC1 = BASE_TYPE_VIDEO + "/wvc1";
45   public static final String VIDEO_DIVX = BASE_TYPE_VIDEO + "/divx";
46   public static final String VIDEO_DOLBY_VISION = BASE_TYPE_VIDEO + "/dolby-vision";
47   public static final String VIDEO_UNKNOWN = BASE_TYPE_VIDEO + "/x-unknown";
48 
49   public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4";
50   public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
51   public static final String AUDIO_WEBM = BASE_TYPE_AUDIO + "/webm";
52   public static final String AUDIO_MPEG = BASE_TYPE_AUDIO + "/mpeg";
53   public static final String AUDIO_MPEG_L1 = BASE_TYPE_AUDIO + "/mpeg-L1";
54   public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2";
55   public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw";
56   public static final String AUDIO_ALAW = BASE_TYPE_AUDIO + "/g711-alaw";
57   public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw";
58   public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
59   public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3";
60   public static final String AUDIO_E_AC3_JOC = BASE_TYPE_AUDIO + "/eac3-joc";
61   public static final String AUDIO_AC4 = BASE_TYPE_AUDIO + "/ac4";
62   public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
63   public static final String AUDIO_DTS = BASE_TYPE_AUDIO + "/vnd.dts";
64   public static final String AUDIO_DTS_HD = BASE_TYPE_AUDIO + "/vnd.dts.hd";
65   public static final String AUDIO_DTS_EXPRESS = BASE_TYPE_AUDIO + "/vnd.dts.hd;profile=lbr";
66   public static final String AUDIO_VORBIS = BASE_TYPE_AUDIO + "/vorbis";
67   public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
68   public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp";
69   public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb";
70   public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/flac";
71   public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac";
72   public static final String AUDIO_MSGSM = BASE_TYPE_AUDIO + "/gsm";
73   public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown";
74 
75   public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";
76   public static final String TEXT_SSA = BASE_TYPE_TEXT + "/x-ssa";
77 
78   public static final String APPLICATION_MP4 = BASE_TYPE_APPLICATION + "/mp4";
79   public static final String APPLICATION_WEBM = BASE_TYPE_APPLICATION + "/webm";
80   public static final String APPLICATION_MPD = BASE_TYPE_APPLICATION + "/dash+xml";
81   public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL";
82   public static final String APPLICATION_SS = BASE_TYPE_APPLICATION + "/vnd.ms-sstr+xml";
83   public static final String APPLICATION_ID3 = BASE_TYPE_APPLICATION + "/id3";
84   public static final String APPLICATION_CEA608 = BASE_TYPE_APPLICATION + "/cea-608";
85   public static final String APPLICATION_CEA708 = BASE_TYPE_APPLICATION + "/cea-708";
86   public static final String APPLICATION_SUBRIP = BASE_TYPE_APPLICATION + "/x-subrip";
87   public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml";
88   public static final String APPLICATION_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
89   public static final String APPLICATION_MP4VTT = BASE_TYPE_APPLICATION + "/x-mp4-vtt";
90   public static final String APPLICATION_MP4CEA608 = BASE_TYPE_APPLICATION + "/x-mp4-cea-608";
91   public static final String APPLICATION_RAWCC = BASE_TYPE_APPLICATION + "/x-rawcc";
92   public static final String APPLICATION_VOBSUB = BASE_TYPE_APPLICATION + "/vobsub";
93   public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";
94   public static final String APPLICATION_SCTE35 = BASE_TYPE_APPLICATION + "/x-scte35";
95   public static final String APPLICATION_CAMERA_MOTION = BASE_TYPE_APPLICATION + "/x-camera-motion";
96   public static final String APPLICATION_EMSG = BASE_TYPE_APPLICATION + "/x-emsg";
97   public static final String APPLICATION_DVBSUBS = BASE_TYPE_APPLICATION + "/dvbsubs";
98   public static final String APPLICATION_EXIF = BASE_TYPE_APPLICATION + "/x-exif";
99   public static final String APPLICATION_ICY = BASE_TYPE_APPLICATION + "/x-icy";
100 
101   private static final ArrayList<CustomMimeType> customMimeTypes = new ArrayList<>();
102 
103   /**
104    * Registers a custom MIME type. Most applications do not need to call this method, as handling of
105    * standard MIME types is built in. These built-in MIME types take precedence over any registered
106    * via this method. If this method is used, it must be called before creating any player(s).
107    *
108    * @param mimeType The custom MIME type to register.
109    * @param codecPrefix The RFC 6381-style codec string prefix associated with the MIME type.
110    * @param trackType The {@link C}{@code .TRACK_TYPE_*} constant associated with the MIME type.
111    *     This value is ignored if the top-level type of {@code mimeType} is audio, video or text.
112    */
registerCustomMimeType(String mimeType, String codecPrefix, int trackType)113   public static void registerCustomMimeType(String mimeType, String codecPrefix, int trackType) {
114     CustomMimeType customMimeType = new CustomMimeType(mimeType, codecPrefix, trackType);
115     int customMimeTypeCount = customMimeTypes.size();
116     for (int i = 0; i < customMimeTypeCount; i++) {
117       if (mimeType.equals(customMimeTypes.get(i).mimeType)) {
118         customMimeTypes.remove(i);
119         break;
120       }
121     }
122     customMimeTypes.add(customMimeType);
123   }
124 
125   /** Returns whether the given string is an audio MIME type. */
isAudio(@ullable String mimeType)126   public static boolean isAudio(@Nullable String mimeType) {
127     return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType));
128   }
129 
130   /** Returns whether the given string is a video MIME type. */
isVideo(@ullable String mimeType)131   public static boolean isVideo(@Nullable String mimeType) {
132     return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType));
133   }
134 
135   /** Returns whether the given string is a text MIME type. */
isText(@ullable String mimeType)136   public static boolean isText(@Nullable String mimeType) {
137     return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType));
138   }
139 
140   /** Returns whether the given string is an application MIME type. */
isApplication(@ullable String mimeType)141   public static boolean isApplication(@Nullable String mimeType) {
142     return BASE_TYPE_APPLICATION.equals(getTopLevelType(mimeType));
143   }
144 
145   /**
146    * Returns true if it is known that all samples in a stream of the given sample MIME type are
147    * guaranteed to be sync samples (i.e., {@link C#BUFFER_FLAG_KEY_FRAME} is guaranteed to be set on
148    * every sample).
149    *
150    * @param mimeType The sample MIME type.
151    * @return True if it is known that all samples in a stream of the given sample MIME type are
152    *     guaranteed to be sync samples. False otherwise, including if {@code null} is passed.
153    */
allSamplesAreSyncSamples(@ullable String mimeType)154   public static boolean allSamplesAreSyncSamples(@Nullable String mimeType) {
155     if (mimeType == null) {
156       return false;
157     }
158     // TODO: Consider adding additional audio MIME types here.
159     switch (mimeType) {
160       case AUDIO_AAC:
161       case AUDIO_MPEG:
162       case AUDIO_MPEG_L1:
163       case AUDIO_MPEG_L2:
164         return true;
165       default:
166         return false;
167     }
168   }
169 
170   /**
171    * Derives a video sample mimeType from a codecs attribute.
172    *
173    * @param codecs The codecs attribute.
174    * @return The derived video mimeType, or null if it could not be derived.
175    */
176   @Nullable
getVideoMediaMimeType(@ullable String codecs)177   public static String getVideoMediaMimeType(@Nullable String codecs) {
178     if (codecs == null) {
179       return null;
180     }
181     String[] codecList = Util.splitCodecs(codecs);
182     for (String codec : codecList) {
183       @Nullable String mimeType = getMediaMimeType(codec);
184       if (mimeType != null && isVideo(mimeType)) {
185         return mimeType;
186       }
187     }
188     return null;
189   }
190 
191   /**
192    * Derives a audio sample mimeType from a codecs attribute.
193    *
194    * @param codecs The codecs attribute.
195    * @return The derived audio mimeType, or null if it could not be derived.
196    */
197   @Nullable
getAudioMediaMimeType(@ullable String codecs)198   public static String getAudioMediaMimeType(@Nullable String codecs) {
199     if (codecs == null) {
200       return null;
201     }
202     String[] codecList = Util.splitCodecs(codecs);
203     for (String codec : codecList) {
204       @Nullable String mimeType = getMediaMimeType(codec);
205       if (mimeType != null && isAudio(mimeType)) {
206         return mimeType;
207       }
208     }
209     return null;
210   }
211 
212   /**
213    * Derives a mimeType from a codec identifier, as defined in RFC 6381.
214    *
215    * @param codec The codec identifier to derive.
216    * @return The mimeType, or null if it could not be derived.
217    */
218   @Nullable
getMediaMimeType(@ullable String codec)219   public static String getMediaMimeType(@Nullable String codec) {
220     if (codec == null) {
221       return null;
222     }
223     codec = Util.toLowerInvariant(codec.trim());
224     if (codec.startsWith("avc1") || codec.startsWith("avc3")) {
225       return MimeTypes.VIDEO_H264;
226     } else if (codec.startsWith("hev1") || codec.startsWith("hvc1")) {
227       return MimeTypes.VIDEO_H265;
228     } else if (codec.startsWith("dvav")
229         || codec.startsWith("dva1")
230         || codec.startsWith("dvhe")
231         || codec.startsWith("dvh1")) {
232       return MimeTypes.VIDEO_DOLBY_VISION;
233     } else if (codec.startsWith("av01")) {
234       return MimeTypes.VIDEO_AV1;
235     } else if (codec.startsWith("vp9") || codec.startsWith("vp09")) {
236       return MimeTypes.VIDEO_VP9;
237     } else if (codec.startsWith("vp8") || codec.startsWith("vp08")) {
238       return MimeTypes.VIDEO_VP8;
239     } else if (codec.startsWith("mp4a")) {
240       @Nullable String mimeType = null;
241       if (codec.startsWith("mp4a.")) {
242         String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix
243         if (objectTypeString.length() >= 2) {
244           try {
245             String objectTypeHexString = Util.toUpperInvariant(objectTypeString.substring(0, 2));
246             int objectTypeInt = Integer.parseInt(objectTypeHexString, 16);
247             mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt);
248           } catch (NumberFormatException ignored) {
249             // Ignored.
250           }
251         }
252       }
253       return mimeType == null ? MimeTypes.AUDIO_AAC : mimeType;
254     } else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) {
255       return MimeTypes.AUDIO_AC3;
256     } else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
257       return MimeTypes.AUDIO_E_AC3;
258     } else if (codec.startsWith("ec+3")) {
259       return MimeTypes.AUDIO_E_AC3_JOC;
260     } else if (codec.startsWith("ac-4") || codec.startsWith("dac4")) {
261       return MimeTypes.AUDIO_AC4;
262     } else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) {
263       return MimeTypes.AUDIO_DTS;
264     } else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
265       return MimeTypes.AUDIO_DTS_HD;
266     } else if (codec.startsWith("opus")) {
267       return MimeTypes.AUDIO_OPUS;
268     } else if (codec.startsWith("vorbis")) {
269       return MimeTypes.AUDIO_VORBIS;
270     } else if (codec.startsWith("flac")) {
271       return MimeTypes.AUDIO_FLAC;
272     } else if (codec.startsWith("stpp")) {
273       return MimeTypes.APPLICATION_TTML;
274     } else if (codec.startsWith("wvtt")) {
275       return MimeTypes.TEXT_VTT;
276     } else {
277       return getCustomMimeTypeForCodec(codec);
278     }
279   }
280 
281   /**
282    * Derives a mimeType from MP4 object type identifier, as defined in RFC 6381 and
283    * https://mp4ra.org/#/object_types.
284    *
285    * @param objectType The objectType identifier to derive.
286    * @return The mimeType, or null if it could not be derived.
287    */
288   @Nullable
getMimeTypeFromMp4ObjectType(int objectType)289   public static String getMimeTypeFromMp4ObjectType(int objectType) {
290     switch (objectType) {
291       case 0x20:
292         return MimeTypes.VIDEO_MP4V;
293       case 0x21:
294         return MimeTypes.VIDEO_H264;
295       case 0x23:
296         return MimeTypes.VIDEO_H265;
297       case 0x60:
298       case 0x61:
299       case 0x62:
300       case 0x63:
301       case 0x64:
302       case 0x65:
303         return MimeTypes.VIDEO_MPEG2;
304       case 0x6A:
305         return MimeTypes.VIDEO_MPEG;
306       case 0x69:
307       case 0x6B:
308         return MimeTypes.AUDIO_MPEG;
309       case 0xA3:
310         return MimeTypes.VIDEO_VC1;
311       case 0xB1:
312         return MimeTypes.VIDEO_VP9;
313       case 0x40:
314       case 0x66:
315       case 0x67:
316       case 0x68:
317         return MimeTypes.AUDIO_AAC;
318       case 0xA5:
319         return MimeTypes.AUDIO_AC3;
320       case 0xA6:
321         return MimeTypes.AUDIO_E_AC3;
322       case 0xA9:
323       case 0xAC:
324         return MimeTypes.AUDIO_DTS;
325       case 0xAA:
326       case 0xAB:
327         return MimeTypes.AUDIO_DTS_HD;
328       case 0xAD:
329         return MimeTypes.AUDIO_OPUS;
330       case 0xAE:
331         return MimeTypes.AUDIO_AC4;
332       default:
333         return null;
334     }
335   }
336 
337   /**
338    * Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type.
339    * {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be
340    * established.
341    *
342    * @param mimeType The MIME type.
343    * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type.
344    */
getTrackType(@ullable String mimeType)345   public static int getTrackType(@Nullable String mimeType) {
346     if (TextUtils.isEmpty(mimeType)) {
347       return C.TRACK_TYPE_UNKNOWN;
348     } else if (isAudio(mimeType)) {
349       return C.TRACK_TYPE_AUDIO;
350     } else if (isVideo(mimeType)) {
351       return C.TRACK_TYPE_VIDEO;
352     } else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType)
353         || APPLICATION_CEA708.equals(mimeType) || APPLICATION_MP4CEA608.equals(mimeType)
354         || APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType)
355         || APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType)
356         || APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType)
357         || APPLICATION_PGS.equals(mimeType) || APPLICATION_DVBSUBS.equals(mimeType)) {
358       return C.TRACK_TYPE_TEXT;
359     } else if (APPLICATION_ID3.equals(mimeType)
360         || APPLICATION_EMSG.equals(mimeType)
361         || APPLICATION_SCTE35.equals(mimeType)) {
362       return C.TRACK_TYPE_METADATA;
363     } else if (APPLICATION_CAMERA_MOTION.equals(mimeType)) {
364       return C.TRACK_TYPE_CAMERA_MOTION;
365     } else {
366       return getTrackTypeForCustomMimeType(mimeType);
367     }
368   }
369 
370   /**
371    * Returns the {@link C}{@code .ENCODING_*} constant that corresponds to specified MIME type, if
372    * it is an encoded (non-PCM) audio format, or {@link C#ENCODING_INVALID} otherwise.
373    *
374    * @param mimeType The MIME type.
375    * @return The {@link C}{@code .ENCODING_*} constant that corresponds to a specified MIME type, or
376    *     {@link C#ENCODING_INVALID}.
377    */
getEncoding(String mimeType)378   public static @C.Encoding int getEncoding(String mimeType) {
379     switch (mimeType) {
380       case MimeTypes.AUDIO_MPEG:
381         return C.ENCODING_MP3;
382       case MimeTypes.AUDIO_AC3:
383         return C.ENCODING_AC3;
384       case MimeTypes.AUDIO_E_AC3:
385         return C.ENCODING_E_AC3;
386       case MimeTypes.AUDIO_E_AC3_JOC:
387         return C.ENCODING_E_AC3_JOC;
388       case MimeTypes.AUDIO_AC4:
389         return C.ENCODING_AC4;
390       case MimeTypes.AUDIO_DTS:
391         return C.ENCODING_DTS;
392       case MimeTypes.AUDIO_DTS_HD:
393         return C.ENCODING_DTS_HD;
394       case MimeTypes.AUDIO_TRUEHD:
395         return C.ENCODING_DOLBY_TRUEHD;
396       default:
397         return C.ENCODING_INVALID;
398     }
399   }
400 
401   /**
402    * Equivalent to {@code getTrackType(getMediaMimeType(codec))}.
403    *
404    * @param codec The codec.
405    * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified codec.
406    */
getTrackTypeOfCodec(String codec)407   public static int getTrackTypeOfCodec(String codec) {
408     return getTrackType(getMediaMimeType(codec));
409   }
410 
411   /**
412    * Returns the top-level type of {@code mimeType}, or null if {@code mimeType} is null or does not
413    * contain a forward slash character ({@code '/'}).
414    */
415   @Nullable
getTopLevelType(@ullable String mimeType)416   private static String getTopLevelType(@Nullable String mimeType) {
417     if (mimeType == null) {
418       return null;
419     }
420     int indexOfSlash = mimeType.indexOf('/');
421     if (indexOfSlash == -1) {
422       return null;
423     }
424     return mimeType.substring(0, indexOfSlash);
425   }
426 
427   @Nullable
getCustomMimeTypeForCodec(String codec)428   private static String getCustomMimeTypeForCodec(String codec) {
429     int customMimeTypeCount = customMimeTypes.size();
430     for (int i = 0; i < customMimeTypeCount; i++) {
431       CustomMimeType customMimeType = customMimeTypes.get(i);
432       if (codec.startsWith(customMimeType.codecPrefix)) {
433         return customMimeType.mimeType;
434       }
435     }
436     return null;
437   }
438 
getTrackTypeForCustomMimeType(String mimeType)439   private static int getTrackTypeForCustomMimeType(String mimeType) {
440     int customMimeTypeCount = customMimeTypes.size();
441     for (int i = 0; i < customMimeTypeCount; i++) {
442       CustomMimeType customMimeType = customMimeTypes.get(i);
443       if (mimeType.equals(customMimeType.mimeType)) {
444         return customMimeType.trackType;
445       }
446     }
447     return C.TRACK_TYPE_UNKNOWN;
448   }
449 
MimeTypes()450   private MimeTypes() {
451     // Prevent instantiation.
452   }
453 
454   private static final class CustomMimeType {
455     public final String mimeType;
456     public final String codecPrefix;
457     public final int trackType;
458 
CustomMimeType(String mimeType, String codecPrefix, int trackType)459     public CustomMimeType(String mimeType, String codecPrefix, int trackType) {
460       this.mimeType = mimeType;
461       this.codecPrefix = codecPrefix;
462       this.trackType = trackType;
463     }
464   }
465 }
466