flv-inspector.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. 'use strict';
  2. var
  3. tagTypes = {
  4. 0x08: 'audio',
  5. 0x09: 'video',
  6. 0x12: 'metadata'
  7. },
  8. hex = function(val) {
  9. return '0x' + ('00' + val.toString(16)).slice(-2).toUpperCase();
  10. },
  11. hexStringList = function(data) {
  12. var arr = [], i;
  13. while (data.byteLength > 0) {
  14. i = 0;
  15. arr.push(hex(data[i++]));
  16. data = data.subarray(i);
  17. }
  18. return arr.join(' ');
  19. },
  20. parseAVCTag = function(tag, obj) {
  21. var
  22. avcPacketTypes = [
  23. 'AVC Sequence Header',
  24. 'AVC NALU',
  25. 'AVC End-of-Sequence'
  26. ],
  27. compositionTime = (tag[1] & parseInt('01111111', 2) << 16) | (tag[2] << 8) | tag[3];
  28. obj = obj || {};
  29. obj.avcPacketType = avcPacketTypes[tag[0]];
  30. obj.CompositionTime = (tag[1] & parseInt('10000000', 2)) ? -compositionTime : compositionTime;
  31. if (tag[0] === 1) {
  32. obj.nalUnitTypeRaw = hexStringList(tag.subarray(4, 100));
  33. } else {
  34. obj.data = hexStringList(tag.subarray(4));
  35. }
  36. return obj;
  37. },
  38. parseVideoTag = function(tag, obj) {
  39. var
  40. frameTypes = [
  41. 'Unknown',
  42. 'Keyframe (for AVC, a seekable frame)',
  43. 'Inter frame (for AVC, a nonseekable frame)',
  44. 'Disposable inter frame (H.263 only)',
  45. 'Generated keyframe (reserved for server use only)',
  46. 'Video info/command frame'
  47. ],
  48. codecID = tag[0] & parseInt('00001111', 2);
  49. obj = obj || {};
  50. obj.frameType = frameTypes[(tag[0] & parseInt('11110000', 2)) >>> 4];
  51. obj.codecID = codecID;
  52. if (codecID === 7) {
  53. return parseAVCTag(tag.subarray(1), obj);
  54. }
  55. return obj;
  56. },
  57. parseAACTag = function(tag, obj) {
  58. var packetTypes = [
  59. 'AAC Sequence Header',
  60. 'AAC Raw'
  61. ];
  62. obj = obj || {};
  63. obj.aacPacketType = packetTypes[tag[0]];
  64. obj.data = hexStringList(tag.subarray(1));
  65. return obj;
  66. },
  67. parseAudioTag = function(tag, obj) {
  68. var
  69. formatTable = [
  70. 'Linear PCM, platform endian',
  71. 'ADPCM',
  72. 'MP3',
  73. 'Linear PCM, little endian',
  74. 'Nellymoser 16-kHz mono',
  75. 'Nellymoser 8-kHz mono',
  76. 'Nellymoser',
  77. 'G.711 A-law logarithmic PCM',
  78. 'G.711 mu-law logarithmic PCM',
  79. 'reserved',
  80. 'AAC',
  81. 'Speex',
  82. 'MP3 8-Khz',
  83. 'Device-specific sound'
  84. ],
  85. samplingRateTable = [
  86. '5.5-kHz',
  87. '11-kHz',
  88. '22-kHz',
  89. '44-kHz'
  90. ],
  91. soundFormat = (tag[0] & parseInt('11110000', 2)) >>> 4;
  92. obj = obj || {};
  93. obj.soundFormat = formatTable[soundFormat];
  94. obj.soundRate = samplingRateTable[(tag[0] & parseInt('00001100', 2)) >>> 2];
  95. obj.soundSize = ((tag[0] & parseInt('00000010', 2)) >>> 1) ? '16-bit' : '8-bit';
  96. obj.soundType = (tag[0] & parseInt('00000001', 2)) ? 'Stereo' : 'Mono';
  97. if (soundFormat === 10) {
  98. return parseAACTag(tag.subarray(1), obj);
  99. }
  100. return obj;
  101. },
  102. parseGenericTag = function(tag) {
  103. return {
  104. tagType: tagTypes[tag[0]],
  105. dataSize: (tag[1] << 16) | (tag[2] << 8) | tag[3],
  106. timestamp: (tag[7] << 24) | (tag[4] << 16) | (tag[5] << 8) | tag[6],
  107. streamID: (tag[8] << 16) | (tag[9] << 8) | tag[10]
  108. };
  109. },
  110. inspectFlvTag = function(tag) {
  111. var header = parseGenericTag(tag);
  112. switch (tag[0]) {
  113. case 0x08:
  114. parseAudioTag(tag.subarray(11), header);
  115. break;
  116. case 0x09:
  117. parseVideoTag(tag.subarray(11), header);
  118. break;
  119. case 0x12:
  120. }
  121. return header;
  122. },
  123. inspectFlv = function(bytes) {
  124. var i = 9, // header
  125. dataSize,
  126. parsedResults = [],
  127. tag;
  128. // traverse the tags
  129. i += 4; // skip previous tag size
  130. while (i < bytes.byteLength) {
  131. dataSize = bytes[i + 1] << 16;
  132. dataSize |= bytes[i + 2] << 8;
  133. dataSize |= bytes[i + 3];
  134. dataSize += 11;
  135. tag = bytes.subarray(i, i + dataSize);
  136. parsedResults.push(inspectFlvTag(tag));
  137. i += dataSize + 4;
  138. }
  139. return parsedResults;
  140. },
  141. textifyFlv = function(flvTagArray) {
  142. return JSON.stringify(flvTagArray, null, 2);
  143. };
  144. module.exports = {
  145. inspectTag: inspectFlvTag,
  146. inspect: inspectFlv,
  147. textify: textifyFlv
  148. };