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 com.google.android.exoplayer2.util; 17 18 import android.text.TextUtils; 19 import androidx.annotation.Nullable; 20 import 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 public static final String APPLICATION_AIT = BASE_TYPE_APPLICATION + "/vnd.dvb.ait"; 101 102 private static final ArrayList<CustomMimeType> customMimeTypes = new ArrayList<>(); 103 104 /** 105 * Registers a custom MIME type. Most applications do not need to call this method, as handling of 106 * standard MIME types is built in. These built-in MIME types take precedence over any registered 107 * via this method. If this method is used, it must be called before creating any player(s). 108 * 109 * @param mimeType The custom MIME type to register. 110 * @param codecPrefix The RFC 6381-style codec string prefix associated with the MIME type. 111 * @param trackType The {@link C}{@code .TRACK_TYPE_*} constant associated with the MIME type. 112 * This value is ignored if the top-level type of {@code mimeType} is audio, video or text. 113 */ registerCustomMimeType(String mimeType, String codecPrefix, int trackType)114 public static void registerCustomMimeType(String mimeType, String codecPrefix, int trackType) { 115 CustomMimeType customMimeType = new CustomMimeType(mimeType, codecPrefix, trackType); 116 int customMimeTypeCount = customMimeTypes.size(); 117 for (int i = 0; i < customMimeTypeCount; i++) { 118 if (mimeType.equals(customMimeTypes.get(i).mimeType)) { 119 customMimeTypes.remove(i); 120 break; 121 } 122 } 123 customMimeTypes.add(customMimeType); 124 } 125 126 /** Returns whether the given string is an audio MIME type. */ isAudio(@ullable String mimeType)127 public static boolean isAudio(@Nullable String mimeType) { 128 return BASE_TYPE_AUDIO.equals(getTopLevelType(mimeType)); 129 } 130 131 /** Returns whether the given string is a video MIME type. */ isVideo(@ullable String mimeType)132 public static boolean isVideo(@Nullable String mimeType) { 133 return BASE_TYPE_VIDEO.equals(getTopLevelType(mimeType)); 134 } 135 136 /** 137 * Returns whether the given string is a text MIME type, including known text types that use 138 * "application" as their base type. 139 */ isText(@ullable String mimeType)140 public static boolean isText(@Nullable String mimeType) { 141 return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType)) 142 || APPLICATION_CEA608.equals(mimeType) 143 || APPLICATION_CEA708.equals(mimeType) 144 || APPLICATION_MP4CEA608.equals(mimeType) 145 || APPLICATION_SUBRIP.equals(mimeType) 146 || APPLICATION_TTML.equals(mimeType) 147 || APPLICATION_TX3G.equals(mimeType) 148 || APPLICATION_MP4VTT.equals(mimeType) 149 || APPLICATION_RAWCC.equals(mimeType) 150 || APPLICATION_VOBSUB.equals(mimeType) 151 || APPLICATION_PGS.equals(mimeType) 152 || APPLICATION_DVBSUBS.equals(mimeType); 153 } 154 155 /** 156 * Returns true if it is known that all samples in a stream of the given sample MIME type are 157 * guaranteed to be sync samples (i.e., {@link C#BUFFER_FLAG_KEY_FRAME} is guaranteed to be set on 158 * every sample). 159 * 160 * @param mimeType The sample MIME type. 161 * @return True if it is known that all samples in a stream of the given sample MIME type are 162 * guaranteed to be sync samples. False otherwise, including if {@code null} is passed. 163 */ allSamplesAreSyncSamples(@ullable String mimeType)164 public static boolean allSamplesAreSyncSamples(@Nullable String mimeType) { 165 if (mimeType == null) { 166 return false; 167 } 168 // TODO: Consider adding additional audio MIME types here. 169 switch (mimeType) { 170 case AUDIO_AAC: 171 case AUDIO_MPEG: 172 case AUDIO_MPEG_L1: 173 case AUDIO_MPEG_L2: 174 return true; 175 default: 176 return false; 177 } 178 } 179 180 /** 181 * Derives a video sample mimeType from a codecs attribute. 182 * 183 * @param codecs The codecs attribute. 184 * @return The derived video mimeType, or null if it could not be derived. 185 */ 186 @Nullable getVideoMediaMimeType(@ullable String codecs)187 public static String getVideoMediaMimeType(@Nullable String codecs) { 188 if (codecs == null) { 189 return null; 190 } 191 String[] codecList = Util.splitCodecs(codecs); 192 for (String codec : codecList) { 193 @Nullable String mimeType = getMediaMimeType(codec); 194 if (mimeType != null && isVideo(mimeType)) { 195 return mimeType; 196 } 197 } 198 return null; 199 } 200 201 /** 202 * Derives a audio sample mimeType from a codecs attribute. 203 * 204 * @param codecs The codecs attribute. 205 * @return The derived audio mimeType, or null if it could not be derived. 206 */ 207 @Nullable getAudioMediaMimeType(@ullable String codecs)208 public static String getAudioMediaMimeType(@Nullable String codecs) { 209 if (codecs == null) { 210 return null; 211 } 212 String[] codecList = Util.splitCodecs(codecs); 213 for (String codec : codecList) { 214 @Nullable String mimeType = getMediaMimeType(codec); 215 if (mimeType != null && isAudio(mimeType)) { 216 return mimeType; 217 } 218 } 219 return null; 220 } 221 222 /** 223 * Derives a text sample mimeType from a codecs attribute. 224 * 225 * @param codecs The codecs attribute. 226 * @return The derived text mimeType, or null if it could not be derived. 227 */ 228 @Nullable getTextMediaMimeType(@ullable String codecs)229 public static String getTextMediaMimeType(@Nullable String codecs) { 230 if (codecs == null) { 231 return null; 232 } 233 String[] codecList = Util.splitCodecs(codecs); 234 for (String codec : codecList) { 235 @Nullable String mimeType = getMediaMimeType(codec); 236 if (mimeType != null && isText(mimeType)) { 237 return mimeType; 238 } 239 } 240 return null; 241 } 242 243 /** 244 * Derives a mimeType from a codec identifier, as defined in RFC 6381. 245 * 246 * @param codec The codec identifier to derive. 247 * @return The mimeType, or null if it could not be derived. 248 */ 249 @Nullable getMediaMimeType(@ullable String codec)250 public static String getMediaMimeType(@Nullable String codec) { 251 if (codec == null) { 252 return null; 253 } 254 codec = Util.toLowerInvariant(codec.trim()); 255 if (codec.startsWith("avc1") || codec.startsWith("avc3")) { 256 return MimeTypes.VIDEO_H264; 257 } else if (codec.startsWith("hev1") || codec.startsWith("hvc1")) { 258 return MimeTypes.VIDEO_H265; 259 } else if (codec.startsWith("dvav") 260 || codec.startsWith("dva1") 261 || codec.startsWith("dvhe") 262 || codec.startsWith("dvh1")) { 263 return MimeTypes.VIDEO_DOLBY_VISION; 264 } else if (codec.startsWith("av01")) { 265 return MimeTypes.VIDEO_AV1; 266 } else if (codec.startsWith("vp9") || codec.startsWith("vp09")) { 267 return MimeTypes.VIDEO_VP9; 268 } else if (codec.startsWith("vp8") || codec.startsWith("vp08")) { 269 return MimeTypes.VIDEO_VP8; 270 } else if (codec.startsWith("mp4a")) { 271 @Nullable String mimeType = null; 272 if (codec.startsWith("mp4a.")) { 273 String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix 274 if (objectTypeString.length() >= 2) { 275 try { 276 String objectTypeHexString = Util.toUpperInvariant(objectTypeString.substring(0, 2)); 277 int objectTypeInt = Integer.parseInt(objectTypeHexString, 16); 278 mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt); 279 } catch (NumberFormatException ignored) { 280 // Ignored. 281 } 282 } 283 } 284 return mimeType == null ? MimeTypes.AUDIO_AAC : mimeType; 285 } else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) { 286 return MimeTypes.AUDIO_AC3; 287 } else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) { 288 return MimeTypes.AUDIO_E_AC3; 289 } else if (codec.startsWith("ec+3")) { 290 return MimeTypes.AUDIO_E_AC3_JOC; 291 } else if (codec.startsWith("ac-4") || codec.startsWith("dac4")) { 292 return MimeTypes.AUDIO_AC4; 293 } else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) { 294 return MimeTypes.AUDIO_DTS; 295 } else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) { 296 return MimeTypes.AUDIO_DTS_HD; 297 } else if (codec.startsWith("opus")) { 298 return MimeTypes.AUDIO_OPUS; 299 } else if (codec.startsWith("vorbis")) { 300 return MimeTypes.AUDIO_VORBIS; 301 } else if (codec.startsWith("flac")) { 302 return MimeTypes.AUDIO_FLAC; 303 } else if (codec.startsWith("stpp")) { 304 return MimeTypes.APPLICATION_TTML; 305 } else if (codec.startsWith("wvtt")) { 306 return MimeTypes.TEXT_VTT; 307 } else if (codec.contains("cea708")) { 308 return MimeTypes.APPLICATION_CEA708; 309 } else if (codec.contains("eia608") || codec.contains("cea608")) { 310 return MimeTypes.APPLICATION_CEA608; 311 } else { 312 return getCustomMimeTypeForCodec(codec); 313 } 314 } 315 316 /** 317 * Derives a mimeType from MP4 object type identifier, as defined in RFC 6381 and 318 * https://mp4ra.org/#/object_types. 319 * 320 * @param objectType The objectType identifier to derive. 321 * @return The mimeType, or null if it could not be derived. 322 */ 323 @Nullable getMimeTypeFromMp4ObjectType(int objectType)324 public static String getMimeTypeFromMp4ObjectType(int objectType) { 325 switch (objectType) { 326 case 0x20: 327 return MimeTypes.VIDEO_MP4V; 328 case 0x21: 329 return MimeTypes.VIDEO_H264; 330 case 0x23: 331 return MimeTypes.VIDEO_H265; 332 case 0x60: 333 case 0x61: 334 case 0x62: 335 case 0x63: 336 case 0x64: 337 case 0x65: 338 return MimeTypes.VIDEO_MPEG2; 339 case 0x6A: 340 return MimeTypes.VIDEO_MPEG; 341 case 0x69: 342 case 0x6B: 343 return MimeTypes.AUDIO_MPEG; 344 case 0xA3: 345 return MimeTypes.VIDEO_VC1; 346 case 0xB1: 347 return MimeTypes.VIDEO_VP9; 348 case 0x40: 349 case 0x66: 350 case 0x67: 351 case 0x68: 352 return MimeTypes.AUDIO_AAC; 353 case 0xA5: 354 return MimeTypes.AUDIO_AC3; 355 case 0xA6: 356 return MimeTypes.AUDIO_E_AC3; 357 case 0xA9: 358 case 0xAC: 359 return MimeTypes.AUDIO_DTS; 360 case 0xAA: 361 case 0xAB: 362 return MimeTypes.AUDIO_DTS_HD; 363 case 0xAD: 364 return MimeTypes.AUDIO_OPUS; 365 case 0xAE: 366 return MimeTypes.AUDIO_AC4; 367 default: 368 return null; 369 } 370 } 371 372 /** 373 * Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type. 374 * {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be 375 * established. 376 * 377 * @param mimeType The MIME type. 378 * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type. 379 */ getTrackType(@ullable String mimeType)380 public static int getTrackType(@Nullable String mimeType) { 381 if (TextUtils.isEmpty(mimeType)) { 382 return C.TRACK_TYPE_UNKNOWN; 383 } else if (isAudio(mimeType)) { 384 return C.TRACK_TYPE_AUDIO; 385 } else if (isVideo(mimeType)) { 386 return C.TRACK_TYPE_VIDEO; 387 } else if (isText(mimeType)) { 388 return C.TRACK_TYPE_TEXT; 389 } else if (APPLICATION_ID3.equals(mimeType) 390 || APPLICATION_EMSG.equals(mimeType) 391 || APPLICATION_SCTE35.equals(mimeType)) { 392 return C.TRACK_TYPE_METADATA; 393 } else if (APPLICATION_CAMERA_MOTION.equals(mimeType)) { 394 return C.TRACK_TYPE_CAMERA_MOTION; 395 } else { 396 return getTrackTypeForCustomMimeType(mimeType); 397 } 398 } 399 400 /** 401 * Returns the {@link C}{@code .ENCODING_*} constant that corresponds to specified MIME type, if 402 * it is an encoded (non-PCM) audio format, or {@link C#ENCODING_INVALID} otherwise. 403 * 404 * @param mimeType The MIME type. 405 * @return The {@link C}{@code .ENCODING_*} constant that corresponds to a specified MIME type, or 406 * {@link C#ENCODING_INVALID}. 407 */ getEncoding(String mimeType)408 public static @C.Encoding int getEncoding(String mimeType) { 409 switch (mimeType) { 410 case MimeTypes.AUDIO_MPEG: 411 return C.ENCODING_MP3; 412 case MimeTypes.AUDIO_AC3: 413 return C.ENCODING_AC3; 414 case MimeTypes.AUDIO_E_AC3: 415 return C.ENCODING_E_AC3; 416 case MimeTypes.AUDIO_E_AC3_JOC: 417 return C.ENCODING_E_AC3_JOC; 418 case MimeTypes.AUDIO_AC4: 419 return C.ENCODING_AC4; 420 case MimeTypes.AUDIO_DTS: 421 return C.ENCODING_DTS; 422 case MimeTypes.AUDIO_DTS_HD: 423 return C.ENCODING_DTS_HD; 424 case MimeTypes.AUDIO_TRUEHD: 425 return C.ENCODING_DOLBY_TRUEHD; 426 default: 427 return C.ENCODING_INVALID; 428 } 429 } 430 431 /** 432 * Equivalent to {@code getTrackType(getMediaMimeType(codec))}. 433 * 434 * @param codec The codec. 435 * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified codec. 436 */ getTrackTypeOfCodec(String codec)437 public static int getTrackTypeOfCodec(String codec) { 438 return getTrackType(getMediaMimeType(codec)); 439 } 440 441 /** 442 * Returns the top-level type of {@code mimeType}, or null if {@code mimeType} is null or does not 443 * contain a forward slash character ({@code '/'}). 444 */ 445 @Nullable getTopLevelType(@ullable String mimeType)446 private static String getTopLevelType(@Nullable String mimeType) { 447 if (mimeType == null) { 448 return null; 449 } 450 int indexOfSlash = mimeType.indexOf('/'); 451 if (indexOfSlash == -1) { 452 return null; 453 } 454 return mimeType.substring(0, indexOfSlash); 455 } 456 457 @Nullable getCustomMimeTypeForCodec(String codec)458 private static String getCustomMimeTypeForCodec(String codec) { 459 int customMimeTypeCount = customMimeTypes.size(); 460 for (int i = 0; i < customMimeTypeCount; i++) { 461 CustomMimeType customMimeType = customMimeTypes.get(i); 462 if (codec.startsWith(customMimeType.codecPrefix)) { 463 return customMimeType.mimeType; 464 } 465 } 466 return null; 467 } 468 getTrackTypeForCustomMimeType(String mimeType)469 private static int getTrackTypeForCustomMimeType(String mimeType) { 470 int customMimeTypeCount = customMimeTypes.size(); 471 for (int i = 0; i < customMimeTypeCount; i++) { 472 CustomMimeType customMimeType = customMimeTypes.get(i); 473 if (mimeType.equals(customMimeType.mimeType)) { 474 return customMimeType.trackType; 475 } 476 } 477 return C.TRACK_TYPE_UNKNOWN; 478 } 479 MimeTypes()480 private MimeTypes() { 481 // Prevent instantiation. 482 } 483 484 private static final class CustomMimeType { 485 public final String mimeType; 486 public final String codecPrefix; 487 public final int trackType; 488 CustomMimeType(String mimeType, String codecPrefix, int trackType)489 public CustomMimeType(String mimeType, String codecPrefix, int trackType) { 490 this.mimeType = mimeType; 491 this.codecPrefix = codecPrefix; 492 this.trackType = trackType; 493 } 494 } 495 } 496