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