Home Reference Source

src/demux/base-audio-demuxer.ts

  1. import * as ID3 from '../demux/id3';
  2. import type {
  3. DemuxerResult,
  4. Demuxer,
  5. DemuxedAudioTrack,
  6. AppendedAudioFrame,
  7. DemuxedMetadataTrack,
  8. DemuxedAvcTrack,
  9. DemuxedUserdataTrack,
  10. KeyData,
  11. } from '../types/demuxer';
  12. import { dummyTrack } from './dummy-demuxed-track';
  13. import { appendUint8Array } from '../utils/mp4-tools';
  14. import { sliceUint8 } from '../utils/typed-array';
  15.  
  16. class BaseAudioDemuxer implements Demuxer {
  17. protected _audioTrack!: DemuxedAudioTrack;
  18. protected _id3Track!: DemuxedMetadataTrack;
  19. protected frameIndex: number = 0;
  20. protected cachedData: Uint8Array | null = null;
  21. protected initPTS: number | null = null;
  22.  
  23. resetInitSegment(audioCodec: string, videoCodec: string, duration: number) {
  24. this._id3Track = {
  25. type: 'id3',
  26. id: 0,
  27. pid: -1,
  28. inputTimeScale: 90000,
  29. sequenceNumber: 0,
  30. samples: [],
  31. dropped: 0,
  32. };
  33. }
  34.  
  35. resetTimeStamp() {}
  36.  
  37. resetContiguity(): void {}
  38.  
  39. canParse(data: Uint8Array, offset: number): boolean {
  40. return false;
  41. }
  42.  
  43. appendFrame(
  44. track: DemuxedAudioTrack,
  45. data: Uint8Array,
  46. offset: number
  47. ): AppendedAudioFrame | void {}
  48.  
  49. // feed incoming data to the front of the parsing pipeline
  50. demux(data: Uint8Array, timeOffset: number): DemuxerResult {
  51. if (this.cachedData) {
  52. data = appendUint8Array(this.cachedData, data);
  53. this.cachedData = null;
  54. }
  55.  
  56. let id3Data: Uint8Array | undefined = ID3.getID3Data(data, 0);
  57. let offset = id3Data ? id3Data.length : 0;
  58. let lastDataIndex;
  59. let pts;
  60. const track = this._audioTrack;
  61. const id3Track = this._id3Track;
  62. const timestamp = id3Data ? ID3.getTimeStamp(id3Data) : undefined;
  63. const length = data.length;
  64.  
  65. if (this.frameIndex === 0 || this.initPTS === null) {
  66. this.initPTS = initPTSFn(timestamp, timeOffset);
  67. }
  68.  
  69. // more expressive than alternative: id3Data?.length
  70. if (id3Data && id3Data.length > 0) {
  71. id3Track.samples.push({
  72. pts: this.initPTS,
  73. dts: this.initPTS,
  74. data: id3Data,
  75. });
  76. }
  77.  
  78. pts = this.initPTS;
  79.  
  80. while (offset < length) {
  81. if (this.canParse(data, offset)) {
  82. const frame = this.appendFrame(track, data, offset);
  83. if (frame) {
  84. this.frameIndex++;
  85. pts = frame.sample.pts;
  86. offset += frame.length;
  87. lastDataIndex = offset;
  88. } else {
  89. offset = length;
  90. }
  91. } else if (ID3.canParse(data, offset)) {
  92. // after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data
  93. id3Data = ID3.getID3Data(data, offset)!;
  94. id3Track.samples.push({ pts: pts, dts: pts, data: id3Data });
  95. offset += id3Data.length;
  96. lastDataIndex = offset;
  97. } else {
  98. offset++;
  99. }
  100. if (offset === length && lastDataIndex !== length) {
  101. const partialData = sliceUint8(data, lastDataIndex);
  102. if (this.cachedData) {
  103. this.cachedData = appendUint8Array(this.cachedData, partialData);
  104. } else {
  105. this.cachedData = partialData;
  106. }
  107. }
  108. }
  109.  
  110. return {
  111. audioTrack: track,
  112. avcTrack: dummyTrack() as DemuxedAvcTrack,
  113. id3Track,
  114. textTrack: dummyTrack() as DemuxedUserdataTrack,
  115. };
  116. }
  117.  
  118. demuxSampleAes(
  119. data: Uint8Array,
  120. keyData: KeyData,
  121. timeOffset: number
  122. ): Promise<DemuxerResult> {
  123. return Promise.reject(
  124. new Error(`[${this}] This demuxer does not support Sample-AES decryption`)
  125. );
  126. }
  127.  
  128. flush(timeOffset: number): DemuxerResult {
  129. // Parse cache in case of remaining frames.
  130. const cachedData = this.cachedData;
  131. if (cachedData) {
  132. this.cachedData = null;
  133. this.demux(cachedData, 0);
  134. }
  135.  
  136. this.frameIndex = 0;
  137.  
  138. return {
  139. audioTrack: this._audioTrack,
  140. avcTrack: dummyTrack() as DemuxedAvcTrack,
  141. id3Track: this._id3Track,
  142. textTrack: dummyTrack() as DemuxedUserdataTrack,
  143. };
  144. }
  145.  
  146. destroy() {}
  147. }
  148.  
  149. /**
  150. * Initialize PTS
  151. * <p>
  152. * use timestamp unless it is undefined, NaN or Infinity
  153. * </p>
  154. */
  155. export const initPTSFn = (
  156. timestamp: number | undefined,
  157. timeOffset: number
  158. ): number => {
  159. return Number.isFinite(timestamp as number)
  160. ? timestamp! * 90
  161. : timeOffset * 90000;
  162. };
  163. export default BaseAudioDemuxer;