adts.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. 'use strict';
  2. var Stream = require('../utils/stream.js');
  3. var AdtsStream;
  4. var
  5. ADTS_SAMPLING_FREQUENCIES = [
  6. 96000,
  7. 88200,
  8. 64000,
  9. 48000,
  10. 44100,
  11. 32000,
  12. 24000,
  13. 22050,
  14. 16000,
  15. 12000,
  16. 11025,
  17. 8000,
  18. 7350
  19. ];
  20. /*
  21. * Accepts a ElementaryStream and emits data events with parsed
  22. * AAC Audio Frames of the individual packets. Input audio in ADTS
  23. * format is unpacked and re-emitted as AAC frames.
  24. *
  25. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  26. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  27. */
  28. AdtsStream = function() {
  29. var buffer;
  30. AdtsStream.prototype.init.call(this);
  31. this.push = function(packet) {
  32. var
  33. i = 0,
  34. frameNum = 0,
  35. frameLength,
  36. protectionSkipBytes,
  37. frameEnd,
  38. oldBuffer,
  39. sampleCount,
  40. adtsFrameDuration;
  41. if (packet.type !== 'audio') {
  42. // ignore non-audio data
  43. return;
  44. }
  45. // Prepend any data in the buffer to the input data so that we can parse
  46. // aac frames the cross a PES packet boundary
  47. if (buffer) {
  48. oldBuffer = buffer;
  49. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  50. buffer.set(oldBuffer);
  51. buffer.set(packet.data, oldBuffer.byteLength);
  52. } else {
  53. buffer = packet.data;
  54. }
  55. // unpack any ADTS frames which have been fully received
  56. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  57. while (i + 5 < buffer.length) {
  58. // Loook for the start of an ADTS header..
  59. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  60. // If a valid header was not found, jump one forward and attempt to
  61. // find a valid ADTS header starting at the next byte
  62. i++;
  63. continue;
  64. }
  65. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  66. // end of the ADTS header
  67. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  68. // Frame length is a 13 bit integer starting 16 bits from the
  69. // end of the sync sequence
  70. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  71. (buffer[i + 4] << 3) |
  72. ((buffer[i + 5] & 0xe0) >> 5);
  73. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  74. adtsFrameDuration = (sampleCount * 90000) /
  75. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  76. frameEnd = i + frameLength;
  77. // If we don't have enough data to actually finish this ADTS frame, return
  78. // and wait for more data
  79. if (buffer.byteLength < frameEnd) {
  80. return;
  81. }
  82. // Otherwise, deliver the complete AAC frame
  83. this.trigger('data', {
  84. pts: packet.pts + (frameNum * adtsFrameDuration),
  85. dts: packet.dts + (frameNum * adtsFrameDuration),
  86. sampleCount: sampleCount,
  87. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  88. channelcount: ((buffer[i + 2] & 1) << 2) |
  89. ((buffer[i + 3] & 0xc0) >>> 6),
  90. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  91. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  92. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  93. samplesize: 16,
  94. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  95. });
  96. // If the buffer is empty, clear it and return
  97. if (buffer.byteLength === frameEnd) {
  98. buffer = undefined;
  99. return;
  100. }
  101. frameNum++;
  102. // Remove the finished frame from the buffer and start the process again
  103. buffer = buffer.subarray(frameEnd);
  104. }
  105. };
  106. this.flush = function() {
  107. this.trigger('done');
  108. };
  109. };
  110. AdtsStream.prototype = new Stream();
  111. module.exports = AdtsStream;