import registerDebug from 'debug' ; import { MessageType , IsomMessage } from '../message' ; import { BOX_HEADER_BYTES , boxType } from '../../utils/protocols/isom' ; import { Container } from '../mp4muxer/helpers/isom' ; const debug = registerDebug ( 'msl:mp4-parser' ) ; const ISO_BMFF_BOX_TYPES = new Set ( [ 'ftyp' , 'moov' , 'styp' , 'moof' , 'mdat' ] ) ; interface Mp4BoxInfo { type : string ; size : number ; } /**
 * Extract type and size information from the box header
 * (8-byte section at beginning of the box).
 */ const mp4BoxInfo = ( chunks ) : Mp4BoxInfo => { const header = Buffer . alloc ( BOX_HEADER_BYTES ) ; let i = 0 ; let bytesRead = 0 ; while ( bytesRead < header . length ) { const chunk = chunks [ i ++ ] ; const bytesToRead = Math . min ( chunk . length , header . length - bytesRead ) ; chunk . copy ( header , bytesRead , 0 , bytesToRead ) ; bytesRead += bytesToRead ; } const size = header . readUInt32BE ( 0 ) ; const type = boxType ( header ) ; return { type , size } ; } ; type { [ type ] } export class Parser { private _chunks : Buffer [ ] = [ ] ; private _length = 0 ; private _box ? : Mp4BoxInfo ; private _ftyp ? : Buffer ; /**
     * Create a new Parser object.
     */ constructor ( ) { this . _init ( ) ; } /**
     * Initialize the internal properties to their default starting
     * values.
     */ _init ( ) { this . _chunks = [ ] ; this . _length = 0 ; } _push ( chunk ) { this . _chunks . push ( chunk ) ; this . _length += chunk . length ; } return { Array } _parseBox ( ) { if ( this . _length < BOX_HEADER_BYTES ) { return null ; } if ( ! this . _box ) { this . _box = mp4BoxInfo ( this . _chunks ) ; } if ( this . _length < this . _box . size ) { return null ; } const buffer = this . _chunks . length === 1 ? this . _chunks [ 0 ] : Buffer . concat ( this . _chunks ) ; const box = buffer . slice ( 0 , this . _box . size ) ; const trailing = buffer . slice ( this . _box . size ) ; this . _init ( ) ; this . _push ( trailing ) ; if ( ! ISO_BMFF_BOX_TYPES . has ( this . _box . type ) ) { console . warn ( `ignored non-ISO BMFF Byte Stream box type: ${ this . _box . type } (${ this . _box . size } bytes)` ) ; return Buffer . alloc ( 0 ) ; } delete this . _box ; return box ; } param chunk return parse ( chunk ) { this . _push ( chunk ) ; const messages = [ ] ; let done = false ; while ( ! done ) { const data = this . _parseBox ( ) ; if ( data !== null ) { if ( boxType ( data ) === 'ftyp' ) { this . _ftyp = data ; } else if ( boxType ( data ) === 'moov' ) { const moov = new Container ( 'moov' ) ; const tracks = moov . parse ( data ) ; debug ( 'MP4 tracks: ' , tracks ) ; messages . push ( { type : MessageType . ISOM , data : Buffer . concat ( [ this . _ftyp ?? Buffer . alloc ( 0 ) , data ] ) , tracks , } ) ; } else { messages . push ( { type : MessageType . ISOM , data } ) ; } } else { done = true ; } } return messages ; } }