audio-frame-utils.js 4.3 KB

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