flv-inspector.js 4.2 KB

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