containers.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import { toUint8, bytesMatch } from './byte-helpers.js';
  2. import { findBox } from './mp4-helpers.js';
  3. import { findEbml, EBML_TAGS } from './ebml-helpers.js';
  4. import { getId3Offset } from './id3-helpers.js';
  5. import { findH264Nal, findH265Nal } from './nal-helpers.js';
  6. var CONSTANTS = {
  7. // "webm" string literal in hex
  8. 'webm': toUint8([0x77, 0x65, 0x62, 0x6d]),
  9. // "matroska" string literal in hex
  10. 'matroska': toUint8([0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61]),
  11. // "fLaC" string literal in hex
  12. 'flac': toUint8([0x66, 0x4c, 0x61, 0x43]),
  13. // "OggS" string literal in hex
  14. 'ogg': toUint8([0x4f, 0x67, 0x67, 0x53]),
  15. // ac-3 sync byte, also works for ec-3 as that is simply a codec
  16. // of ac-3
  17. 'ac3': toUint8([0x0b, 0x77]),
  18. // "RIFF" string literal in hex used for wav and avi
  19. 'riff': toUint8([0x52, 0x49, 0x46, 0x46]),
  20. // "AVI" string literal in hex
  21. 'avi': toUint8([0x41, 0x56, 0x49]),
  22. // "WAVE" string literal in hex
  23. 'wav': toUint8([0x57, 0x41, 0x56, 0x45]),
  24. // "ftyp3g" string literal in hex
  25. '3gp': toUint8([0x66, 0x74, 0x79, 0x70, 0x33, 0x67]),
  26. // "ftyp" string literal in hex
  27. 'mp4': toUint8([0x66, 0x74, 0x79, 0x70]),
  28. // "styp" string literal in hex
  29. 'fmp4': toUint8([0x73, 0x74, 0x79, 0x70]),
  30. // "ftypqt" string literal in hex
  31. 'mov': toUint8([0x66, 0x74, 0x79, 0x70, 0x71, 0x74]),
  32. // moov string literal in hex
  33. 'moov': toUint8([0x6D, 0x6F, 0x6F, 0x76]),
  34. // moof string literal in hex
  35. 'moof': toUint8([0x6D, 0x6F, 0x6F, 0x66])
  36. };
  37. var _isLikely = {
  38. aac: function aac(bytes) {
  39. var offset = getId3Offset(bytes);
  40. return bytesMatch(bytes, [0xFF, 0x10], {
  41. offset: offset,
  42. mask: [0xFF, 0x16]
  43. });
  44. },
  45. mp3: function mp3(bytes) {
  46. var offset = getId3Offset(bytes);
  47. return bytesMatch(bytes, [0xFF, 0x02], {
  48. offset: offset,
  49. mask: [0xFF, 0x06]
  50. });
  51. },
  52. webm: function webm(bytes) {
  53. var docType = findEbml(bytes, [EBML_TAGS.EBML, EBML_TAGS.DocType])[0]; // check if DocType EBML tag is webm
  54. return bytesMatch(docType, CONSTANTS.webm);
  55. },
  56. mkv: function mkv(bytes) {
  57. var docType = findEbml(bytes, [EBML_TAGS.EBML, EBML_TAGS.DocType])[0]; // check if DocType EBML tag is matroska
  58. return bytesMatch(docType, CONSTANTS.matroska);
  59. },
  60. mp4: function mp4(bytes) {
  61. // if this file is another base media file format, it is not mp4
  62. if (_isLikely['3gp'](bytes) || _isLikely.mov(bytes)) {
  63. return false;
  64. } // if this file starts with a ftyp or styp box its mp4
  65. if (bytesMatch(bytes, CONSTANTS.mp4, {
  66. offset: 4
  67. }) || bytesMatch(bytes, CONSTANTS.fmp4, {
  68. offset: 4
  69. })) {
  70. return true;
  71. } // if this file starts with a moof/moov box its mp4
  72. if (bytesMatch(bytes, CONSTANTS.moof, {
  73. offset: 4
  74. }) || bytesMatch(bytes, CONSTANTS.moov, {
  75. offset: 4
  76. })) {
  77. return true;
  78. }
  79. },
  80. mov: function mov(bytes) {
  81. return bytesMatch(bytes, CONSTANTS.mov, {
  82. offset: 4
  83. });
  84. },
  85. '3gp': function gp(bytes) {
  86. return bytesMatch(bytes, CONSTANTS['3gp'], {
  87. offset: 4
  88. });
  89. },
  90. ac3: function ac3(bytes) {
  91. var offset = getId3Offset(bytes);
  92. return bytesMatch(bytes, CONSTANTS.ac3, {
  93. offset: offset
  94. });
  95. },
  96. ts: function ts(bytes) {
  97. if (bytes.length < 189 && bytes.length >= 1) {
  98. return bytes[0] === 0x47;
  99. }
  100. var i = 0; // check the first 376 bytes for two matching sync bytes
  101. while (i + 188 < bytes.length && i < 188) {
  102. if (bytes[i] === 0x47 && bytes[i + 188] === 0x47) {
  103. return true;
  104. }
  105. i += 1;
  106. }
  107. return false;
  108. },
  109. flac: function flac(bytes) {
  110. var offset = getId3Offset(bytes);
  111. return bytesMatch(bytes, CONSTANTS.flac, {
  112. offset: offset
  113. });
  114. },
  115. ogg: function ogg(bytes) {
  116. return bytesMatch(bytes, CONSTANTS.ogg);
  117. },
  118. avi: function avi(bytes) {
  119. return bytesMatch(bytes, CONSTANTS.riff) && bytesMatch(bytes, CONSTANTS.avi, {
  120. offset: 8
  121. });
  122. },
  123. wav: function wav(bytes) {
  124. return bytesMatch(bytes, CONSTANTS.riff) && bytesMatch(bytes, CONSTANTS.wav, {
  125. offset: 8
  126. });
  127. },
  128. 'h264': function h264(bytes) {
  129. // find seq_parameter_set_rbsp
  130. return findH264Nal(bytes, 7, 3).length;
  131. },
  132. 'h265': function h265(bytes) {
  133. // find video_parameter_set_rbsp or seq_parameter_set_rbsp
  134. return findH265Nal(bytes, [32, 33], 3).length;
  135. }
  136. }; // get all the isLikely functions
  137. // but make sure 'ts' is above h264 and h265
  138. // but below everything else as it is the least specific
  139. var isLikelyTypes = Object.keys(_isLikely) // remove ts, h264, h265
  140. .filter(function (t) {
  141. return t !== 'ts' && t !== 'h264' && t !== 'h265';
  142. }) // add it back to the bottom
  143. .concat(['ts', 'h264', 'h265']); // make sure we are dealing with uint8 data.
  144. isLikelyTypes.forEach(function (type) {
  145. var isLikelyFn = _isLikely[type];
  146. _isLikely[type] = function (bytes) {
  147. return isLikelyFn(toUint8(bytes));
  148. };
  149. }); // export after wrapping
  150. export var isLikely = _isLikely; // A useful list of file signatures can be found here
  151. // https://en.wikipedia.org/wiki/List_of_file_signatures
  152. export var detectContainerForBytes = function detectContainerForBytes(bytes) {
  153. bytes = toUint8(bytes);
  154. for (var i = 0; i < isLikelyTypes.length; i++) {
  155. var type = isLikelyTypes[i];
  156. if (isLikely[type](bytes)) {
  157. return type;
  158. }
  159. }
  160. return '';
  161. }; // fmp4 is not a container
  162. export var isLikelyFmp4MediaSegment = function isLikelyFmp4MediaSegment(bytes) {
  163. return findBox(bytes, ['moof']).length > 0;
  164. };