audio-frame-utils.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. var coneOfSilence = require('../data/silence');
  8. var clock = require('../utils/clock');
  9. /**
  10. * Sum the `byteLength` properties of the data in each AAC frame
  11. */
  12. var sumFrameByteLengths = function sumFrameByteLengths(array) {
  13. var i,
  14. currentObj,
  15. sum = 0; // sum the byteLength's all each nal unit in the frame
  16. for (i = 0; i < array.length; i++) {
  17. currentObj = array[i];
  18. sum += currentObj.data.byteLength;
  19. }
  20. return sum;
  21. }; // Possibly pad (prefix) the audio track with silence if appending this track
  22. // would lead to the introduction of a gap in the audio buffer
  23. var prefixWithSilence = function prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime) {
  24. var baseMediaDecodeTimeTs,
  25. frameDuration = 0,
  26. audioGapDuration = 0,
  27. audioFillFrameCount = 0,
  28. audioFillDuration = 0,
  29. silentFrame,
  30. i,
  31. firstFrame;
  32. if (!frames.length) {
  33. return;
  34. }
  35. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate); // determine frame clock duration based on sample rate, round up to avoid overfills
  36. frameDuration = Math.ceil(clock.ONE_SECOND_IN_TS / (track.samplerate / 1024));
  37. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  38. // insert the shortest possible amount (audio gap or audio to video gap)
  39. audioGapDuration = baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime); // number of full frames in the audio gap
  40. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  41. audioFillDuration = audioFillFrameCount * frameDuration;
  42. } // don't attempt to fill gaps smaller than a single frame or larger
  43. // than a half second
  44. if (audioFillFrameCount < 1 || audioFillDuration > clock.ONE_SECOND_IN_TS / 2) {
  45. return;
  46. }
  47. silentFrame = coneOfSilence()[track.samplerate];
  48. if (!silentFrame) {
  49. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  50. // from the content instead
  51. silentFrame = frames[0].data;
  52. }
  53. for (i = 0; i < audioFillFrameCount; i++) {
  54. firstFrame = frames[0];
  55. frames.splice(0, 0, {
  56. data: silentFrame,
  57. dts: firstFrame.dts - frameDuration,
  58. pts: firstFrame.pts - frameDuration
  59. });
  60. }
  61. track.baseMediaDecodeTime -= Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  62. return audioFillDuration;
  63. }; // If the audio segment extends before the earliest allowed dts
  64. // value, remove AAC frames until starts at or after the earliest
  65. // allowed DTS so that we don't end up with a negative baseMedia-
  66. // DecodeTime for the audio track
  67. var trimAdtsFramesByEarliestDts = function trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts) {
  68. if (track.minSegmentDts >= earliestAllowedDts) {
  69. return adtsFrames;
  70. } // We will need to recalculate the earliest segment Dts
  71. track.minSegmentDts = Infinity;
  72. return adtsFrames.filter(function (currentFrame) {
  73. // If this is an allowed frame, keep it and record it's Dts
  74. if (currentFrame.dts >= earliestAllowedDts) {
  75. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  76. track.minSegmentPts = track.minSegmentDts;
  77. return true;
  78. } // Otherwise, discard it
  79. return false;
  80. });
  81. }; // generate the track's raw mdat data from an array of frames
  82. var generateSampleTable = function generateSampleTable(frames) {
  83. var i,
  84. currentFrame,
  85. samples = [];
  86. for (i = 0; i < frames.length; i++) {
  87. currentFrame = frames[i];
  88. samples.push({
  89. size: currentFrame.data.byteLength,
  90. duration: 1024 // For AAC audio, all samples contain 1024 samples
  91. });
  92. }
  93. return samples;
  94. }; // generate the track's sample table from an array of frames
  95. var concatenateFrameData = function concatenateFrameData(frames) {
  96. var i,
  97. currentFrame,
  98. dataOffset = 0,
  99. data = new Uint8Array(sumFrameByteLengths(frames));
  100. for (i = 0; i < frames.length; i++) {
  101. currentFrame = frames[i];
  102. data.set(currentFrame.data, dataOffset);
  103. dataOffset += currentFrame.data.byteLength;
  104. }
  105. return data;
  106. };
  107. module.exports = {
  108. prefixWithSilence: prefixWithSilence,
  109. trimAdtsFramesByEarliestDts: trimAdtsFramesByEarliestDts,
  110. generateSampleTable: generateSampleTable,
  111. concatenateFrameData: concatenateFrameData
  112. };