probe.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /**
  2. * mux.js
  3. *
  4. * Copyright (c) 2016 Brightcove
  5. * All rights reserved.
  6. *
  7. * Utilities to detect basic properties and metadata about Aac data.
  8. */
  9. 'use strict';
  10. var ADTS_SAMPLING_FREQUENCIES = [
  11. 96000,
  12. 88200,
  13. 64000,
  14. 48000,
  15. 44100,
  16. 32000,
  17. 24000,
  18. 22050,
  19. 16000,
  20. 12000,
  21. 11025,
  22. 8000,
  23. 7350
  24. ];
  25. var parseSyncSafeInteger = function(data) {
  26. return (data[0] << 21) |
  27. (data[1] << 14) |
  28. (data[2] << 7) |
  29. (data[3]);
  30. };
  31. // return a percent-encoded representation of the specified byte range
  32. // @see http://en.wikipedia.org/wiki/Percent-encoding
  33. var percentEncode = function(bytes, start, end) {
  34. var i, result = '';
  35. for (i = start; i < end; i++) {
  36. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  37. }
  38. return result;
  39. };
  40. // return the string representation of the specified byte range,
  41. // interpreted as ISO-8859-1.
  42. var parseIso88591 = function(bytes, start, end) {
  43. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  44. };
  45. var parseId3TagSize = function(header, byteIndex) {
  46. var
  47. returnSize = (header[byteIndex + 6] << 21) |
  48. (header[byteIndex + 7] << 14) |
  49. (header[byteIndex + 8] << 7) |
  50. (header[byteIndex + 9]),
  51. flags = header[byteIndex + 5],
  52. footerPresent = (flags & 16) >> 4;
  53. if (footerPresent) {
  54. return returnSize + 20;
  55. }
  56. return returnSize + 10;
  57. };
  58. var parseAdtsSize = function(header, byteIndex) {
  59. var
  60. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  61. middle = header[byteIndex + 4] << 3,
  62. highTwo = header[byteIndex + 3] & 0x3 << 11;
  63. return (highTwo | middle) | lowThree;
  64. };
  65. var parseType = function(header, byteIndex) {
  66. if ((header[byteIndex] === 'I'.charCodeAt(0)) &&
  67. (header[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  68. (header[byteIndex + 2] === '3'.charCodeAt(0))) {
  69. return 'timed-metadata';
  70. } else if ((header[byteIndex] & 0xff === 0xff) &&
  71. ((header[byteIndex + 1] & 0xf0) === 0xf0)) {
  72. return 'audio';
  73. }
  74. return null;
  75. };
  76. var parseSampleRate = function(packet) {
  77. var i = 0;
  78. while (i + 5 < packet.length) {
  79. if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
  80. // If a valid header was not found, jump one forward and attempt to
  81. // find a valid ADTS header starting at the next byte
  82. i++;
  83. continue;
  84. }
  85. return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
  86. }
  87. return null;
  88. };
  89. var parseAacTimestamp = function(packet) {
  90. var frameStart, frameSize, frame, frameHeader;
  91. // find the start of the first frame and the end of the tag
  92. frameStart = 10;
  93. if (packet[5] & 0x40) {
  94. // advance the frame start past the extended header
  95. frameStart += 4; // header size field
  96. frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
  97. }
  98. // parse one or more ID3 frames
  99. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  100. do {
  101. // determine the number of bytes in this frame
  102. frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
  103. if (frameSize < 1) {
  104. return null;
  105. }
  106. frameHeader = String.fromCharCode(packet[frameStart],
  107. packet[frameStart + 1],
  108. packet[frameStart + 2],
  109. packet[frameStart + 3]);
  110. if (frameHeader === 'PRIV') {
  111. frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
  112. for (var i = 0; i < frame.byteLength; i++) {
  113. if (frame[i] === 0) {
  114. var owner = parseIso88591(frame, 0, i);
  115. if (owner === 'com.apple.streaming.transportStreamTimestamp') {
  116. var d = frame.subarray(i + 1);
  117. var size = ((d[3] & 0x01) << 30) |
  118. (d[4] << 22) |
  119. (d[5] << 14) |
  120. (d[6] << 6) |
  121. (d[7] >>> 2);
  122. size *= 4;
  123. size += d[7] & 0x03;
  124. return size;
  125. }
  126. break;
  127. }
  128. }
  129. }
  130. frameStart += 10; // advance past the frame header
  131. frameStart += frameSize; // advance past the frame body
  132. } while (frameStart < packet.byteLength);
  133. return null;
  134. };
  135. module.exports = {
  136. parseId3TagSize: parseId3TagSize,
  137. parseAdtsSize: parseAdtsSize,
  138. parseType: parseType,
  139. parseSampleRate: parseSampleRate,
  140. parseAacTimestamp: parseAacTimestamp
  141. };