@@ -61,7 +61,9 @@ class AACDecoder {
6161 this . _ptr = 0
6262 this . _cap = 0
6363 this . _left = null
64- this . _m4a = null // { sizes: number[], idx: number } — M4A streaming state
64+ this . _fileOff = 0 // absolute file offset of _left[0] (M4A streaming)
65+ this . _skip = 0 // bytes to discard from incoming data (M4A streaming, when next frame is past _left)
66+ this . _m4a = null // M4A streaming iterator: { sizes, stco, stsc, idx, ci, sInC, spc, nextOff }
6567 this . _accum = null // Uint8Array[] — M4A header accumulator
6668 this . _accumLen = 0
6769 }
@@ -72,36 +74,18 @@ class AACDecoder {
7274
7375 let buf = data instanceof Uint8Array ? data : new Uint8Array ( data )
7476
75- // M4A streming phase 2: extract frames by known sizes
7677 if ( this . _m4a ) return this . _feedM4AData ( buf )
77-
78- // M4A accumulating: waiting for moov + mdat header
79- if ( this . _accum ) {
80- this . _accum . push ( buf )
81- this . _accumLen += buf . length
82- return this . _tryM4AInit ( )
83- }
84-
85- // ADTS mode (already initialized)
8678 if ( this . h ) return this . _decodeADTS ( buf )
87-
88- // First call: detect format
89- if ( buf . length > 8 && buf [ 4 ] === 0x66 && buf [ 5 ] === 0x74 && buf [ 6 ] === 0x79 && buf [ 7 ] === 0x70 ) {
90- this . _accum = [ buf ]
91- this . _accumLen = buf . length
79+ // M4A: accumulating moov+mdat header, or first chunk starts with ftyp
80+ if ( this . _accum || ( buf . length > 8 && buf [ 4 ] === 0x66 && buf [ 5 ] === 0x74 && buf [ 6 ] === 0x79 && buf [ 7 ] === 0x70 ) ) {
81+ ( this . _accum ??= [ ] ) . push ( buf )
82+ this . _accumLen += buf . length
9283 return this . _tryM4AInit ( )
9384 }
94-
9585 return this . _decodeADTS ( buf )
9686 }
9787
9888 flush ( ) {
99- if ( this . _accum ?. length ) {
100- // M4A moov-last: one-shot decode on accumulated data
101- let buf = this . _catAccum ( )
102- this . _accum = null ; this . _accumLen = 0
103- return this . _decodeM4A ( buf )
104- }
10589 this . _left = null
10690 return EMPTY
10791 }
@@ -121,6 +105,7 @@ class AACDecoder {
121105 }
122106 this . _accum = null ; this . _accumLen = 0
123107 this . _m4a = null ; this . _left = null
108+ this . _fileOff = 0 ; this . _skip = 0
124109 }
125110
126111 _catAccum ( ) {
@@ -133,18 +118,16 @@ class AACDecoder {
133118 _tryM4AInit ( ) {
134119 let buf = this . _catAccum ( )
135120 let asc = null , stsz = null , stco = null , stsc = null
136- let mdatOff = 0 , mdatLen = 0
137121
138- parseBoxes ( buf , 0 , buf . length , ( type , data , off ) => {
122+ parseBoxes ( buf , 0 , buf . length , ( type , data ) => {
139123 if ( type === 'esds' ) asc = parseEsds ( data )
140124 else if ( type === 'stsz' ) stsz = parseStsz ( data )
141125 else if ( type === 'stco' ) stco = parseStco ( data )
142126 else if ( type === 'co64' ) stco = parseCo64 ( data )
143127 else if ( type === 'stsc' ) stsc = parseStsc ( data )
144- else if ( type === 'mdat' ) { mdatOff = off ; mdatLen = data . length }
145128 } )
146129
147- if ( ! asc ) return EMPTY // moov not found yet
130+ if ( ! asc || ! stsz || ! stco ?. length ) return EMPTY // moov/tables not ready
148131
149132 // Init WASM decoder with ASC
150133 let m = this . m , h = m . _aac_create ( )
@@ -158,47 +141,52 @@ class AACDecoder {
158141 if ( ! this . ch ) { m . _aac_close ( h ) ; throw Error ( 'M4A init: no channels in ASC' ) }
159142 this . h = h
160143
161- // Extract available frames using stco (correct absolute offsets)
162- let frames = ( stsz && stco )
163- ? extractFrames ( buf , stsz , stco , stsc )
164- : mdatLen ? scanMdat ( buf , mdatOff , mdatLen ) : [ ]
165-
166- let decodedIdx = frames . length
167- this . _m4a = { sizes : stsz || [ ] , idx : decodedIdx }
144+ // Streaming: walk sample tables by absolute file offset so chunk boundaries are irrelevant.
168145 this . _accum = null ; this . _accumLen = 0
169-
170- // For streaming: compute leftover mdat data after extracted frames
171- if ( stsz && decodedIdx < stsz . length && decodedIdx > 0 && stco ?. length ) {
172- let consumed = 0
173- for ( let i = 0 ; i < decodedIdx ; i ++ ) consumed += stsz [ i ]
174- let dataStart = stco [ 0 ] + consumed
175- if ( dataStart < buf . length ) this . _left = buf . subarray ( dataStart ) . slice ( )
176- }
177-
178- if ( ! frames . length ) return EMPTY
179- return this . _feedFrames ( frames )
146+ this . _m4a = { sizes : stsz , stco, stsc, idx : 0 , ci : 0 , sInC : 0 , spc : spcAt ( 0 , stsc ) , nextOff : stco [ 0 ] }
147+ this . _left = buf
148+ this . _fileOff = 0
149+ this . _skip = 0
150+ return this . _extractM4A ( )
180151 }
181152
182153 _feedM4AData ( buf ) {
183- let st = this . _m4a
184- if ( this . _left ) {
185- let merged = new Uint8Array ( this . _left . length + buf . length )
186- merged . set ( this . _left ) ; merged . set ( buf , this . _left . length )
187- buf = merged ; this . _left = null
154+ if ( this . _skip > 0 ) {
155+ let n = Math . min ( this . _skip , buf . length )
156+ this . _skip -= n
157+ this . _fileOff += n
158+ buf = buf . subarray ( n )
159+ if ( ! buf . length ) return EMPTY
188160 }
161+ this . _left = append ( this . _left , buf )
162+ return this . _extractM4A ( )
163+ }
189164
190- let frames = [ ] , pos = 0
165+ _extractM4A ( ) {
166+ let st = this . _m4a , frames = [ ]
191167 while ( st . idx < st . sizes . length ) {
192- let sz = st . sizes [ st . idx ]
193- if ( pos + sz > buf . length ) break
194- frames . push ( buf . subarray ( pos , pos + sz ) )
195- pos += sz
196- st . idx ++
168+ let off = st . nextOff , sz = st . sizes [ st . idx ]
169+ let bufOff = off - this . _fileOff
170+ if ( bufOff + sz > this . _left . length ) break
171+ if ( bufOff >= 0 ) frames . push ( this . _left . subarray ( bufOff , bufOff + sz ) )
172+ advanceM4A ( st )
197173 }
198174
199- if ( pos < buf . length ) this . _left = buf . subarray ( pos ) . slice ( )
200- if ( ! frames . length ) return EMPTY
201- return this . _feedFrames ( frames )
175+ if ( st . idx < st . sizes . length ) {
176+ let nextOff = st . nextOff , end = this . _fileOff + this . _left . length
177+ if ( nextOff >= end ) {
178+ this . _skip = nextOff - end
179+ this . _fileOff = end
180+ this . _left = null
181+ } else if ( nextOff > this . _fileOff ) {
182+ this . _left = this . _left . subarray ( nextOff - this . _fileOff ) . slice ( )
183+ this . _fileOff = nextOff
184+ }
185+ } else {
186+ this . _left = null
187+ }
188+
189+ return frames . length ? this . _feedFrames ( frames ) : EMPTY
202190 }
203191
204192 _alloc ( len ) {
@@ -213,14 +201,7 @@ class AACDecoder {
213201 _decodeADTS ( buf ) {
214202 let m = this . m
215203
216- // prepend leftover from previous call
217- if ( this . _left ) {
218- let merged = new Uint8Array ( this . _left . length + buf . length )
219- merged . set ( this . _left )
220- merged . set ( buf , this . _left . length )
221- buf = merged
222- this . _left = null
223- }
204+ if ( this . _left ) { buf = append ( this . _left , buf ) ; this . _left = null }
224205
225206 if ( ! this . h ) {
226207 if ( buf . length < 7 ) { this . _left = buf . slice ( ) ; return EMPTY }
@@ -261,27 +242,6 @@ class AACDecoder {
261242 return this . _feedFrames ( frames )
262243 }
263244
264- _decodeM4A ( buf ) {
265- let { asc, frames } = demuxM4A ( buf )
266- if ( ! asc || ! frames . length ) return EMPTY
267-
268- let m = this . m
269- let h = m . _aac_create ( )
270-
271- let srP = m . _aac_sr_ptr ( ) , chP = m . _aac_ch_ptr ( )
272- let ptr = this . _alloc ( asc . length )
273- m . HEAPU8 . set ( asc , ptr )
274- let err = m . _aac_init2 ( h , ptr , asc . length , srP , chP )
275- if ( err < 0 ) { m . _aac_close ( h ) ; throw Error ( 'M4A init failed (code ' + err + ')' ) }
276-
277- this . sr = m . getValue ( srP , 'i32' )
278- this . ch = m . getValue ( chP , 'i8' )
279- if ( ! this . ch ) { m . _aac_close ( h ) ; throw Error ( 'M4A init: no channels in ASC' ) }
280- this . h = h
281-
282- return this . _feedFrames ( frames )
283- }
284-
285245 _feedFrames ( frames ) {
286246 let m = this . m , h = this . h
287247 let chunks = [ ] , totalPerCh = 0 , channels = this . ch , errors = 0
@@ -322,26 +282,11 @@ class AACDecoder {
322282
323283// ===== M4A demuxer =====
324284
325- function demuxM4A ( buf ) {
326- let asc = null , stsz = null , stco = null , stsc = null
327- let mdatOff = 0 , mdatLen = 0
328-
329- parseBoxes ( buf , 0 , buf . length , ( type , data , off ) => {
330- if ( type === 'esds' ) asc = parseEsds ( data )
331- else if ( type === 'stsz' ) stsz = parseStsz ( data )
332- else if ( type === 'stco' ) stco = parseStco ( data )
333- else if ( type === 'co64' ) stco = parseCo64 ( data )
334- else if ( type === 'stsc' ) stsc = parseStsc ( data )
335- else if ( type === 'mdat' ) { mdatOff = off ; mdatLen = data . length }
336- } )
337-
338- if ( ! asc ) return { asc : null , frames : [ ] }
339-
340- let frames = ( stsz && stco )
341- ? extractFrames ( buf , stsz , stco , stsc )
342- : mdatLen ? scanMdat ( buf , mdatOff , mdatLen ) : [ ]
343-
344- return { asc, frames }
285+ function append ( left , buf ) {
286+ if ( ! left ?. length ) return buf . slice ( )
287+ let merged = new Uint8Array ( left . length + buf . length )
288+ merged . set ( left ) ; merged . set ( buf , left . length )
289+ return merged
345290}
346291
347292const CONTAINERS = new Set ( [ 'moov' , 'trak' , 'mdia' , 'minf' , 'stbl' , 'udta' , 'meta' , 'edts' , 'sinf' ] )
@@ -352,28 +297,21 @@ function parseBoxes(buf, start, end, cb) {
352297 let size = r32 ( buf , off )
353298 let type = String . fromCharCode ( buf [ off + 4 ] , buf [ off + 5 ] , buf [ off + 6 ] , buf [ off + 7 ] )
354299
355- if ( size === 0 ) {
356- size = end - off
357- } else if ( size === 1 && off + 16 <= end ) {
300+ if ( size === 0 ) size = end - off
301+ else if ( size === 1 && off + 16 <= end ) {
358302 size = r32 ( buf , off + 12 )
359303 if ( size < 16 ) break
360- } else if ( size < 8 ) {
361- break
362- }
304+ } else if ( size < 8 ) break
363305
364- let bodyOff = off + 8
365- let truncated = off + size > end
306+ // skip mdat fast — the raw frames don't interest us here
307+ if ( type === 'mdat' ) { off += size ; continue }
308+ // truncated non-mdat box: tables would be garbage — wait for more data
309+ if ( off + size > end ) break
366310
367- // mdat carries raw frames — partial is fine; callers honour availability
368- if ( type === 'mdat' ) {
369- cb ( type , buf . subarray ( bodyOff , truncated ? end : off + size ) , bodyOff )
370- if ( truncated ) break
371- }
372- // any other box must be fully present before parsing — partial tables yield garbage
373- else if ( truncated ) break
374- else if ( type === 'stsd' ) parseSampleDesc ( buf , bodyOff , size - 8 , cb )
311+ let bodyOff = off + 8
312+ if ( type === 'stsd' ) parseSampleDesc ( buf , bodyOff , size - 8 , cb )
375313 else if ( CONTAINERS . has ( type ) ) parseBoxes ( buf , bodyOff + ( type === 'meta' ? 4 : 0 ) , off + size , cb )
376- else cb ( type , buf . subarray ( bodyOff , off + size ) , bodyOff )
314+ else cb ( type , buf . subarray ( bodyOff , off + size ) )
377315
378316 off += size
379317 }
@@ -428,41 +366,24 @@ function parseStsc(data) {
428366 return e
429367}
430368
431- function extractFrames ( buf , stsz , stco , stsc ) {
432- let frames = [ ] , si = 0
433- for ( let ci = 0 ; ci < stco . length ; ci ++ ) {
434- let spc = 1
435- if ( stsc ?. length ) {
436- let cn = ci + 1
437- for ( let j = stsc . length - 1 ; j >= 0 ; j -- )
438- if ( cn >= stsc [ j ] . first ) { spc = stsc [ j ] . spc ; break }
439- }
440- let off = stco [ ci ]
441- for ( let s = 0 ; s < spc && si < stsz . length ; s ++ ) {
442- let sz = stsz [ si ]
443- if ( off + sz > buf . length ) return frames // sequential: first missing → rest missing
444- frames . push ( buf . subarray ( off , off + sz ) )
445- si ++
446- off += sz
447- }
448- }
449- return frames
369+ function spcAt ( ci , stsc ) {
370+ if ( ! stsc ?. length ) return 1
371+ let spc = 1 , cn = ci + 1
372+ for ( let j = stsc . length - 1 ; j >= 0 ; j -- )
373+ if ( cn >= stsc [ j ] . first ) { spc = stsc [ j ] . spc ; break }
374+ return spc
450375}
451376
452- function scanMdat ( buf , off , len ) {
453- let frames = [ ] , end = off + len , pos = off
454- while ( pos < end - 7 ) {
455- if ( buf [ pos ] === 0xFF && ( buf [ pos + 1 ] & 0xF6 ) === 0xF0 ) {
456- let flen = ( ( buf [ pos + 3 ] & 0x03 ) << 11 ) | ( buf [ pos + 4 ] << 3 ) | ( buf [ pos + 5 ] >> 5 )
457- if ( flen > 0 && pos + flen <= end ) {
458- frames . push ( buf . subarray ( pos , pos + flen ) )
459- pos += flen
460- continue
461- }
462- }
463- pos ++
377+ function advanceM4A ( st ) {
378+ st . nextOff += st . sizes [ st . idx ]
379+ st . idx ++
380+ st . sInC ++
381+ if ( st . sInC >= st . spc && st . ci + 1 < st . stco . length ) {
382+ st . ci ++
383+ st . sInC = 0
384+ st . spc = spcAt ( st . ci , st . stsc )
385+ st . nextOff = st . stco [ st . ci ]
464386 }
465- return frames
466387}
467388
468389function r32 ( buf , off ) {
0 commit comments