format-parser.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.parseFormatForBytes = void 0;
  6. var _byteHelpers = require("./byte-helpers.js");
  7. var _ebmlHelpers = require("./ebml-helpers.js");
  8. var _mp4Helpers = require("./mp4-helpers.js");
  9. var _riffHelpers = require("./riff-helpers.js");
  10. var _oggHelpers = require("./ogg-helpers.js");
  11. var _containers = require("./containers.js");
  12. var _nalHelpers = require("./nal-helpers.js");
  13. var _m2tsHelpers = require("./m2ts-helpers.js");
  14. var _codecHelpers = require("./codec-helpers.js");
  15. var _id3Helpers = require("./id3-helpers.js");
  16. // https://docs.microsoft.com/en-us/windows/win32/medfound/audio-subtype-guids
  17. // https://tools.ietf.org/html/rfc2361
  18. var wFormatTagCodec = function wFormatTagCodec(wFormatTag) {
  19. wFormatTag = (0, _byteHelpers.toUint8)(wFormatTag);
  20. if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x00, 0x55])) {
  21. return 'mp3';
  22. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x16, 0x00]) || (0, _byteHelpers.bytesMatch)(wFormatTag, [0x00, 0xFF])) {
  23. return 'aac';
  24. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x70, 0x4f])) {
  25. return 'opus';
  26. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x6C, 0x61])) {
  27. return 'alac';
  28. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0xF1, 0xAC])) {
  29. return 'flac';
  30. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x20, 0x00])) {
  31. return 'ac-3';
  32. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0xFF, 0xFE])) {
  33. return 'ec-3';
  34. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x00, 0x50])) {
  35. return 'mp2';
  36. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0x56, 0x6f])) {
  37. return 'vorbis';
  38. } else if ((0, _byteHelpers.bytesMatch)(wFormatTag, [0xA1, 0x09])) {
  39. return 'speex';
  40. }
  41. return '';
  42. };
  43. var formatMimetype = function formatMimetype(name, codecs) {
  44. var codecString = ['video', 'audio'].reduce(function (acc, type) {
  45. if (codecs[type]) {
  46. acc += (acc.length ? ',' : '') + codecs[type];
  47. }
  48. return acc;
  49. }, '');
  50. return (codecs.video ? 'video' : 'audio') + "/" + name + (codecString ? ";codecs=\"" + codecString + "\"" : '');
  51. };
  52. var parseCodecFrom = {
  53. mov: function mov(bytes) {
  54. // mov and mp4 both use a nearly identical box structure.
  55. var retval = parseCodecFrom.mp4(bytes);
  56. if (retval.mimetype) {
  57. retval.mimetype = retval.mimetype.replace('mp4', 'quicktime');
  58. }
  59. return retval;
  60. },
  61. mp4: function mp4(bytes) {
  62. bytes = (0, _byteHelpers.toUint8)(bytes);
  63. var codecs = {};
  64. var tracks = (0, _mp4Helpers.parseTracks)(bytes);
  65. for (var i = 0; i < tracks.length; i++) {
  66. var track = tracks[i];
  67. if (track.type === 'audio' && !codecs.audio) {
  68. codecs.audio = track.codec;
  69. }
  70. if (track.type === 'video' && !codecs.video) {
  71. codecs.video = track.codec;
  72. }
  73. }
  74. return {
  75. codecs: codecs,
  76. mimetype: formatMimetype('mp4', codecs)
  77. };
  78. },
  79. '3gp': function gp(bytes) {
  80. return {
  81. codecs: {},
  82. mimetype: 'video/3gpp'
  83. };
  84. },
  85. ogg: function ogg(bytes) {
  86. var pages = (0, _oggHelpers.getPages)(bytes, 0, 4);
  87. var codecs = {};
  88. pages.forEach(function (page) {
  89. if ((0, _byteHelpers.bytesMatch)(page, [0x4F, 0x70, 0x75, 0x73], {
  90. offset: 28
  91. })) {
  92. codecs.audio = 'opus';
  93. } else if ((0, _byteHelpers.bytesMatch)(page, [0x56, 0x50, 0x38, 0x30], {
  94. offset: 29
  95. })) {
  96. codecs.video = 'vp8';
  97. } else if ((0, _byteHelpers.bytesMatch)(page, [0x74, 0x68, 0x65, 0x6F, 0x72, 0x61], {
  98. offset: 29
  99. })) {
  100. codecs.video = 'theora';
  101. } else if ((0, _byteHelpers.bytesMatch)(page, [0x46, 0x4C, 0x41, 0x43], {
  102. offset: 29
  103. })) {
  104. codecs.audio = 'flac';
  105. } else if ((0, _byteHelpers.bytesMatch)(page, [0x53, 0x70, 0x65, 0x65, 0x78], {
  106. offset: 28
  107. })) {
  108. codecs.audio = 'speex';
  109. } else if ((0, _byteHelpers.bytesMatch)(page, [0x76, 0x6F, 0x72, 0x62, 0x69, 0x73], {
  110. offset: 29
  111. })) {
  112. codecs.audio = 'vorbis';
  113. }
  114. });
  115. return {
  116. codecs: codecs,
  117. mimetype: formatMimetype('ogg', codecs)
  118. };
  119. },
  120. wav: function wav(bytes) {
  121. var format = (0, _riffHelpers.findFourCC)(bytes, ['WAVE', 'fmt'])[0];
  122. var wFormatTag = Array.prototype.slice.call(format, 0, 2).reverse();
  123. var mimetype = 'audio/vnd.wave';
  124. var codecs = {
  125. audio: wFormatTagCodec(wFormatTag)
  126. };
  127. var codecString = wFormatTag.reduce(function (acc, v) {
  128. if (v) {
  129. acc += (0, _byteHelpers.toHexString)(v);
  130. }
  131. return acc;
  132. }, '');
  133. if (codecString) {
  134. mimetype += ";codec=" + codecString;
  135. }
  136. if (codecString && !codecs.audio) {
  137. codecs.audio = codecString;
  138. }
  139. return {
  140. codecs: codecs,
  141. mimetype: mimetype
  142. };
  143. },
  144. avi: function avi(bytes) {
  145. var movi = (0, _riffHelpers.findFourCC)(bytes, ['AVI', 'movi'])[0];
  146. var strls = (0, _riffHelpers.findFourCC)(bytes, ['AVI', 'hdrl', 'strl']);
  147. var codecs = {};
  148. strls.forEach(function (strl) {
  149. var strh = (0, _riffHelpers.findFourCC)(strl, ['strh'])[0];
  150. var strf = (0, _riffHelpers.findFourCC)(strl, ['strf'])[0]; // now parse AVIStreamHeader to get codec and type:
  151. // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/avifmt/ns-avifmt-avistreamheader
  152. var type = (0, _byteHelpers.bytesToString)(strh.subarray(0, 4));
  153. var codec;
  154. var codecType;
  155. if (type === 'vids') {
  156. // https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
  157. var handler = (0, _byteHelpers.bytesToString)(strh.subarray(4, 8));
  158. var compression = (0, _byteHelpers.bytesToString)(strf.subarray(16, 20)); // look for 00dc (compressed video fourcc code) or 00db (uncompressed video fourcc code)
  159. var videoData = (0, _riffHelpers.findFourCC)(movi, ['00dc'])[0] || (0, _riffHelpers.findFourCC)(movi, ['00db'][0]);
  160. if (handler === 'H264' || compression === 'H264') {
  161. if (videoData && videoData.length) {
  162. codec = parseCodecFrom.h264(videoData).codecs.video;
  163. } else {
  164. codec = 'avc1';
  165. }
  166. } else if (handler === 'HEVC' || compression === 'HEVC') {
  167. if (videoData && videoData.length) {
  168. codec = parseCodecFrom.h265(videoData).codecs.video;
  169. } else {
  170. codec = 'hev1';
  171. }
  172. } else if (handler === 'FMP4' || compression === 'FMP4') {
  173. if (movi.length) {
  174. codec = 'mp4v.20.' + movi[12].toString();
  175. } else {
  176. codec = 'mp4v.20';
  177. }
  178. } else if (handler === 'VP80' || compression === 'VP80') {
  179. codec = 'vp8';
  180. } else if (handler === 'VP90' || compression === 'VP90') {
  181. codec = 'vp9';
  182. } else if (handler === 'AV01' || compression === 'AV01') {
  183. codec = 'av01';
  184. } else if (handler === 'theo' || compression === 'theora') {
  185. codec = 'theora';
  186. } else {
  187. if (videoData && videoData.length) {
  188. var result = (0, _containers.detectContainerForBytes)(videoData);
  189. if (result === 'h264') {
  190. codec = parseCodecFrom.h264(movi).codecs.video;
  191. }
  192. if (result === 'h265') {
  193. codec = parseCodecFrom.h265(movi).codecs.video;
  194. }
  195. }
  196. if (!codec) {
  197. codec = handler || compression;
  198. }
  199. }
  200. codecType = 'video';
  201. } else if (type === 'auds') {
  202. codecType = 'audio'; // look for 00wb (audio data fourcc)
  203. // const audioData = findFourCC(movi, ['01wb']);
  204. var wFormatTag = Array.prototype.slice.call(strf, 0, 2).reverse();
  205. codecs.audio = wFormatTagCodec(wFormatTag);
  206. } else {
  207. return;
  208. }
  209. if (codec) {
  210. codecs[codecType] = codec;
  211. }
  212. });
  213. return {
  214. codecs: codecs,
  215. mimetype: formatMimetype('avi', codecs)
  216. };
  217. },
  218. ts: function ts(bytes) {
  219. var result = (0, _m2tsHelpers.parseTs)(bytes);
  220. var codecs = {};
  221. Object.keys(result.streams).forEach(function (esPid) {
  222. var stream = result.streams[esPid];
  223. if (stream.codec === 'avc1' && stream.packets.length) {
  224. stream.codec = parseCodecFrom.h264(stream.packets[0]).codecs.video;
  225. } else if (stream.codec === 'hev1' && stream.packets.length) {
  226. stream.codec = parseCodecFrom.h265(stream.packets[0]).codecs.video;
  227. }
  228. codecs[stream.type] = stream.codec;
  229. });
  230. return {
  231. codecs: codecs,
  232. mimetype: formatMimetype('mp2t', codecs)
  233. };
  234. },
  235. webm: function webm(bytes) {
  236. // mkv and webm both use ebml to store code info
  237. var retval = parseCodecFrom.mkv(bytes);
  238. if (retval.mimetype) {
  239. retval.mimetype = retval.mimetype.replace('x-matroska', 'webm');
  240. }
  241. return retval;
  242. },
  243. mkv: function mkv(bytes) {
  244. var codecs = {};
  245. var tracks = (0, _ebmlHelpers.parseTracks)(bytes);
  246. for (var i = 0; i < tracks.length; i++) {
  247. var track = tracks[i];
  248. if (track.type === 'audio' && !codecs.audio) {
  249. codecs.audio = track.codec;
  250. }
  251. if (track.type === 'video' && !codecs.video) {
  252. codecs.video = track.codec;
  253. }
  254. }
  255. return {
  256. codecs: codecs,
  257. mimetype: formatMimetype('x-matroska', codecs)
  258. };
  259. },
  260. aac: function aac(bytes) {
  261. return {
  262. codecs: {
  263. audio: 'aac'
  264. },
  265. mimetype: 'audio/aac'
  266. };
  267. },
  268. ac3: function ac3(bytes) {
  269. // past id3 and syncword
  270. var offset = (0, _id3Helpers.getId3Offset)(bytes) + 2; // default to ac-3
  271. var codec = 'ac-3';
  272. if ((0, _byteHelpers.bytesMatch)(bytes, [0xB8, 0xE0], {
  273. offset: offset
  274. })) {
  275. codec = 'ac-3'; // 0x01, 0x7F
  276. } else if ((0, _byteHelpers.bytesMatch)(bytes, [0x01, 0x7f], {
  277. offset: offset
  278. })) {
  279. codec = 'ec-3';
  280. }
  281. return {
  282. codecs: {
  283. audio: codec
  284. },
  285. mimetype: 'audio/vnd.dolby.dd-raw'
  286. };
  287. },
  288. mp3: function mp3(bytes) {
  289. return {
  290. codecs: {
  291. audio: 'mp3'
  292. },
  293. mimetype: 'audio/mpeg'
  294. };
  295. },
  296. flac: function flac(bytes) {
  297. return {
  298. codecs: {
  299. audio: 'flac'
  300. },
  301. mimetype: 'audio/flac'
  302. };
  303. },
  304. 'h264': function h264(bytes) {
  305. // find seq_parameter_set_rbsp to get encoding settings for codec
  306. var nal = (0, _nalHelpers.findH264Nal)(bytes, 7, 3);
  307. var retval = {
  308. codecs: {
  309. video: 'avc1'
  310. },
  311. mimetype: 'video/h264'
  312. };
  313. if (nal.length) {
  314. retval.codecs.video += "." + (0, _codecHelpers.getAvcCodec)(nal);
  315. }
  316. return retval;
  317. },
  318. 'h265': function h265(bytes) {
  319. var retval = {
  320. codecs: {
  321. video: 'hev1'
  322. },
  323. mimetype: 'video/h265'
  324. }; // find video_parameter_set_rbsp or seq_parameter_set_rbsp
  325. // to get encoding settings for codec
  326. var nal = (0, _nalHelpers.findH265Nal)(bytes, [32, 33], 3);
  327. if (nal.length) {
  328. var type = nal[0] >> 1 & 0x3F; // profile_tier_level starts at byte 5 for video_parameter_set_rbsp
  329. // byte 2 for seq_parameter_set_rbsp
  330. retval.codecs.video += "." + (0, _codecHelpers.getHvcCodec)(nal.subarray(type === 32 ? 5 : 2));
  331. }
  332. return retval;
  333. }
  334. };
  335. var parseFormatForBytes = function parseFormatForBytes(bytes) {
  336. bytes = (0, _byteHelpers.toUint8)(bytes);
  337. var result = {
  338. codecs: {},
  339. container: (0, _containers.detectContainerForBytes)(bytes),
  340. mimetype: ''
  341. };
  342. var parseCodecFn = parseCodecFrom[result.container];
  343. if (parseCodecFn) {
  344. var parsed = parseCodecFn ? parseCodecFn(bytes) : {};
  345. result.codecs = parsed.codecs || {};
  346. result.mimetype = parsed.mimetype || '';
  347. }
  348. return result;
  349. };
  350. exports.parseFormatForBytes = parseFormatForBytes;