Home Reference Source

src/demux/mp4demuxer.ts

  1. /**
  2. * MP4 demuxer
  3. */
  4. import {
  5. Demuxer,
  6. DemuxerResult,
  7. PassthroughVideoTrack,
  8. DemuxedAudioTrack,
  9. DemuxedUserdataTrack,
  10. DemuxedMetadataTrack,
  11. KeyData,
  12. } from '../types/demuxer';
  13. import {
  14. findBox,
  15. segmentValidRange,
  16. appendUint8Array,
  17. } from '../utils/mp4-tools';
  18. import { dummyTrack } from './dummy-demuxed-track';
  19. import type { HlsEventEmitter } from '../events';
  20. import type { HlsConfig } from '../config';
  21.  
  22. class MP4Demuxer implements Demuxer {
  23. static readonly minProbeByteLength = 1024;
  24. private remainderData: Uint8Array | null = null;
  25. private config: HlsConfig;
  26.  
  27. constructor(observer: HlsEventEmitter, config: HlsConfig) {
  28. this.config = config;
  29. }
  30.  
  31. resetTimeStamp() {}
  32.  
  33. resetInitSegment() {}
  34.  
  35. resetContiguity(): void {}
  36.  
  37. static probe(data) {
  38. // ensure we find a moof box in the first 16 kB
  39. return (
  40. findBox({ data: data, start: 0, end: Math.min(data.length, 16384) }, [
  41. 'moof',
  42. ]).length > 0
  43. );
  44. }
  45.  
  46. demux(data): DemuxerResult {
  47. // Load all data into the avc track. The CMAF remuxer will look for the data in the samples object; the rest of the fields do not matter
  48. let avcSamples = data;
  49. const avcTrack = dummyTrack() as PassthroughVideoTrack;
  50. if (this.config.progressive) {
  51. // Split the bytestream into two ranges: one encompassing all data up until the start of the last moof, and everything else.
  52. // This is done to guarantee that we're sending valid data to MSE - when demuxing progressively, we have no guarantee
  53. // that the fetch loader gives us flush moof+mdat pairs. If we push jagged data to MSE, it will throw an exception.
  54. if (this.remainderData) {
  55. avcSamples = appendUint8Array(this.remainderData, data);
  56. }
  57. const segmentedData = segmentValidRange(avcSamples);
  58. this.remainderData = segmentedData.remainder;
  59. avcTrack.samples = segmentedData.valid || new Uint8Array();
  60. } else {
  61. avcTrack.samples = avcSamples;
  62. }
  63.  
  64. return {
  65. audioTrack: dummyTrack() as DemuxedAudioTrack,
  66. avcTrack,
  67. id3Track: dummyTrack() as DemuxedMetadataTrack,
  68. textTrack: dummyTrack() as DemuxedUserdataTrack,
  69. };
  70. }
  71.  
  72. flush() {
  73. const avcTrack = dummyTrack() as PassthroughVideoTrack;
  74. avcTrack.samples = this.remainderData || new Uint8Array();
  75. this.remainderData = null;
  76.  
  77. return {
  78. audioTrack: dummyTrack() as DemuxedAudioTrack,
  79. avcTrack,
  80. id3Track: dummyTrack() as DemuxedMetadataTrack,
  81. textTrack: dummyTrack() as DemuxedUserdataTrack,
  82. };
  83. }
  84.  
  85. demuxSampleAes(
  86. data: Uint8Array,
  87. keyData: KeyData,
  88. timeOffset: number
  89. ): Promise<DemuxerResult> {
  90. return Promise.reject(
  91. new Error('The MP4 demuxer does not support SAMPLE-AES decryption')
  92. );
  93. }
  94.  
  95. destroy() {}
  96. }
  97.  
  98. export default MP4Demuxer;