codecs.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.DEFAULT_VIDEO_CODEC = exports.DEFAULT_AUDIO_CODEC = exports.muxerSupportsCodec = exports.browserSupportsCodec = exports.getMimeForCodec = exports.isTextCodec = exports.isAudioCodec = exports.isVideoCodec = exports.codecsFromDefault = exports.parseCodecs = exports.mapLegacyAvcCodecs = exports.translateLegacyCodecs = exports.translateLegacyCodec = void 0;
  7. var _window = _interopRequireDefault(require("global/window"));
  8. var regexs = {
  9. // to determine mime types
  10. mp4: /^(av0?1|avc0?[1234]|vp0?9|flac|opus|mp3|mp4a|mp4v|stpp.ttml.im1t)/,
  11. webm: /^(vp0?[89]|av0?1|opus|vorbis)/,
  12. ogg: /^(vp0?[89]|theora|flac|opus|vorbis)/,
  13. // to determine if a codec is audio or video
  14. video: /^(av0?1|avc0?[1234]|vp0?[89]|hvc1|hev1|theora|mp4v)/,
  15. audio: /^(mp4a|flac|vorbis|opus|ac-[34]|ec-3|alac|mp3|speex|aac)/,
  16. text: /^(stpp.ttml.im1t)/,
  17. // mux.js support regex
  18. muxerVideo: /^(avc0?1)/,
  19. muxerAudio: /^(mp4a)/,
  20. // match nothing as muxer does not support text right now.
  21. // there cannot never be a character before the start of a string
  22. // so this matches nothing.
  23. muxerText: /a^/
  24. };
  25. var mediaTypes = ['video', 'audio', 'text'];
  26. var upperMediaTypes = ['Video', 'Audio', 'Text'];
  27. /**
  28. * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
  29. * `avc1.<hhhhhh>`
  30. *
  31. * @param {string} codec
  32. * Codec string to translate
  33. * @return {string}
  34. * The translated codec string
  35. */
  36. var translateLegacyCodec = function translateLegacyCodec(codec) {
  37. if (!codec) {
  38. return codec;
  39. }
  40. return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
  41. var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
  42. var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
  43. return 'avc1.' + profileHex + '00' + avcLevelHex;
  44. });
  45. };
  46. /**
  47. * Replace the old apple-style `avc1.<dd>.<dd>` codec strings with the standard
  48. * `avc1.<hhhhhh>`
  49. *
  50. * @param {string[]} codecs
  51. * An array of codec strings to translate
  52. * @return {string[]}
  53. * The translated array of codec strings
  54. */
  55. exports.translateLegacyCodec = translateLegacyCodec;
  56. var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  57. return codecs.map(translateLegacyCodec);
  58. };
  59. /**
  60. * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
  61. * standard `avc1.<hhhhhh>`.
  62. *
  63. * @param {string} codecString
  64. * The codec string
  65. * @return {string}
  66. * The codec string with old apple-style codecs replaced
  67. *
  68. * @private
  69. */
  70. exports.translateLegacyCodecs = translateLegacyCodecs;
  71. var mapLegacyAvcCodecs = function mapLegacyAvcCodecs(codecString) {
  72. return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
  73. return translateLegacyCodecs([match])[0];
  74. });
  75. };
  76. /**
  77. * @typedef {Object} ParsedCodecInfo
  78. * @property {number} codecCount
  79. * Number of codecs parsed
  80. * @property {string} [videoCodec]
  81. * Parsed video codec (if found)
  82. * @property {string} [videoObjectTypeIndicator]
  83. * Video object type indicator (if found)
  84. * @property {string|null} audioProfile
  85. * Audio profile
  86. */
  87. /**
  88. * Parses a codec string to retrieve the number of codecs specified, the video codec and
  89. * object type indicator, and the audio profile.
  90. *
  91. * @param {string} [codecString]
  92. * The codec string to parse
  93. * @return {ParsedCodecInfo}
  94. * Parsed codec info
  95. */
  96. exports.mapLegacyAvcCodecs = mapLegacyAvcCodecs;
  97. var parseCodecs = function parseCodecs(codecString) {
  98. if (codecString === void 0) {
  99. codecString = '';
  100. }
  101. var codecs = codecString.split(',');
  102. var result = [];
  103. codecs.forEach(function (codec) {
  104. codec = codec.trim();
  105. var codecType;
  106. mediaTypes.forEach(function (name) {
  107. var match = regexs[name].exec(codec.toLowerCase());
  108. if (!match || match.length <= 1) {
  109. return;
  110. }
  111. codecType = name; // maintain codec case
  112. var type = codec.substring(0, match[1].length);
  113. var details = codec.replace(type, '');
  114. result.push({
  115. type: type,
  116. details: details,
  117. mediaType: name
  118. });
  119. });
  120. if (!codecType) {
  121. result.push({
  122. type: codec,
  123. details: '',
  124. mediaType: 'unknown'
  125. });
  126. }
  127. });
  128. return result;
  129. };
  130. /**
  131. * Returns a ParsedCodecInfo object for the default alternate audio playlist if there is
  132. * a default alternate audio playlist for the provided audio group.
  133. *
  134. * @param {Object} master
  135. * The master playlist
  136. * @param {string} audioGroupId
  137. * ID of the audio group for which to find the default codec info
  138. * @return {ParsedCodecInfo}
  139. * Parsed codec info
  140. */
  141. exports.parseCodecs = parseCodecs;
  142. var codecsFromDefault = function codecsFromDefault(master, audioGroupId) {
  143. if (!master.mediaGroups.AUDIO || !audioGroupId) {
  144. return null;
  145. }
  146. var audioGroup = master.mediaGroups.AUDIO[audioGroupId];
  147. if (!audioGroup) {
  148. return null;
  149. }
  150. for (var name in audioGroup) {
  151. var audioType = audioGroup[name];
  152. if (audioType.default && audioType.playlists) {
  153. // codec should be the same for all playlists within the audio type
  154. return parseCodecs(audioType.playlists[0].attributes.CODECS);
  155. }
  156. }
  157. return null;
  158. };
  159. exports.codecsFromDefault = codecsFromDefault;
  160. var isVideoCodec = function isVideoCodec(codec) {
  161. if (codec === void 0) {
  162. codec = '';
  163. }
  164. return regexs.video.test(codec.trim().toLowerCase());
  165. };
  166. exports.isVideoCodec = isVideoCodec;
  167. var isAudioCodec = function isAudioCodec(codec) {
  168. if (codec === void 0) {
  169. codec = '';
  170. }
  171. return regexs.audio.test(codec.trim().toLowerCase());
  172. };
  173. exports.isAudioCodec = isAudioCodec;
  174. var isTextCodec = function isTextCodec(codec) {
  175. if (codec === void 0) {
  176. codec = '';
  177. }
  178. return regexs.text.test(codec.trim().toLowerCase());
  179. };
  180. exports.isTextCodec = isTextCodec;
  181. var getMimeForCodec = function getMimeForCodec(codecString) {
  182. if (!codecString || typeof codecString !== 'string') {
  183. return;
  184. }
  185. var codecs = codecString.toLowerCase().split(',').map(function (c) {
  186. return translateLegacyCodec(c.trim());
  187. }); // default to video type
  188. var type = 'video'; // only change to audio type if the only codec we have is
  189. // audio
  190. if (codecs.length === 1 && isAudioCodec(codecs[0])) {
  191. type = 'audio';
  192. } else if (codecs.length === 1 && isTextCodec(codecs[0])) {
  193. // text uses application/<container> for now
  194. type = 'application';
  195. } // default the container to mp4
  196. var container = 'mp4'; // every codec must be able to go into the container
  197. // for that container to be the correct one
  198. if (codecs.every(function (c) {
  199. return regexs.mp4.test(c);
  200. })) {
  201. container = 'mp4';
  202. } else if (codecs.every(function (c) {
  203. return regexs.webm.test(c);
  204. })) {
  205. container = 'webm';
  206. } else if (codecs.every(function (c) {
  207. return regexs.ogg.test(c);
  208. })) {
  209. container = 'ogg';
  210. }
  211. return type + "/" + container + ";codecs=\"" + codecString + "\"";
  212. };
  213. exports.getMimeForCodec = getMimeForCodec;
  214. var browserSupportsCodec = function browserSupportsCodec(codecString) {
  215. if (codecString === void 0) {
  216. codecString = '';
  217. }
  218. return _window.default.MediaSource && _window.default.MediaSource.isTypeSupported && _window.default.MediaSource.isTypeSupported(getMimeForCodec(codecString)) || false;
  219. };
  220. exports.browserSupportsCodec = browserSupportsCodec;
  221. var muxerSupportsCodec = function muxerSupportsCodec(codecString) {
  222. if (codecString === void 0) {
  223. codecString = '';
  224. }
  225. return codecString.toLowerCase().split(',').every(function (codec) {
  226. codec = codec.trim(); // any match is supported.
  227. for (var i = 0; i < upperMediaTypes.length; i++) {
  228. var type = upperMediaTypes[i];
  229. if (regexs["muxer" + type].test(codec)) {
  230. return true;
  231. }
  232. }
  233. return false;
  234. });
  235. };
  236. exports.muxerSupportsCodec = muxerSupportsCodec;
  237. var DEFAULT_AUDIO_CODEC = 'mp4a.40.2';
  238. exports.DEFAULT_AUDIO_CODEC = DEFAULT_AUDIO_CODEC;
  239. var DEFAULT_VIDEO_CODEC = 'avc1.4d400d';
  240. exports.DEFAULT_VIDEO_CODEC = DEFAULT_VIDEO_CODEC;