@@ -319,63 +319,71 @@ const _seekOffset = (res) => {
319319} ;
320320async function _buildMp4SeekOptions ( url , seekTimeMs , proxy ) {
321321 const mp4Box = await getMP4Box ( ) ;
322- const mp4 = mp4Box . createFile ( ) ;
322+ const mp4 = mp4Box . createFile ( false ) ;
323323 const prefetch = [ ] ;
324324 let readyInfo = null ;
325325 let nextStart = 0 ;
326- await new Promise ( async ( resolve , reject ) => {
327- mp4 . onError = ( e ) => reject ( new Error ( `MP4Box init error: ${ e } ` ) ) ;
328- mp4 . onReady = ( info ) => {
329- readyInfo = info ;
330- resolve ( ) ;
331- } ;
332- const CHUNK = 512 * 1024 ;
333- const MAX_FETCHES = 40 ;
334- try {
335- for ( let i = 0 ; i < MAX_FETCHES && ! readyInfo ; i ++ ) {
336- const buf = await _fetchRange ( url , nextStart , nextStart + CHUNK - 1 , proxy ) ;
337- const ab = _toArrayBufferWithFileStart ( buf , nextStart ) ;
338- prefetch . push ( { fileStart : nextStart , data : ab } ) ;
339- const appended = mp4 . appendBuffer ( ab ) ;
340- if ( typeof appended === 'number' ) {
341- nextStart = appended ;
326+ try {
327+ await new Promise ( async ( resolve , reject ) => {
328+ mp4 . onError = ( e ) => reject ( new Error ( `MP4Box init error: ${ e } ` ) ) ;
329+ mp4 . onReady = ( info ) => {
330+ readyInfo = info ;
331+ resolve ( ) ;
332+ } ;
333+ const CHUNK = 512 * 1024 ;
334+ const MAX_FETCHES = 40 ;
335+ try {
336+ for ( let i = 0 ; i < MAX_FETCHES && ! readyInfo ; i ++ ) {
337+ const buf = await _fetchRange ( url , nextStart , nextStart + CHUNK - 1 , proxy ) ;
338+ const ab = _toArrayBufferWithFileStart ( buf , nextStart ) ;
339+ prefetch . push ( { fileStart : nextStart , data : ab } ) ;
340+ const appended = mp4 . appendBuffer ( ab ) ;
341+ if ( typeof appended === 'number' ) {
342+ nextStart = appended ;
343+ }
344+ else {
345+ nextStart += ab . byteLength ;
346+ }
347+ if ( ! Number . isFinite ( nextStart ) || nextStart < 0 )
348+ break ;
342349 }
343- else {
344- nextStart += ab . byteLength ;
350+ if ( ! readyInfo ) {
351+ reject ( new Error ( 'Could not parse MP4 metadata (moov not found quickly).' ) ) ;
345352 }
346- if ( ! Number . isFinite ( nextStart ) || nextStart < 0 )
347- break ;
348353 }
349- if ( ! readyInfo ) {
350- reject ( new Error ( 'Could not parse MP4 metadata (moov not found quickly).' ) ) ;
354+ catch ( e ) {
355+ reject ( e ) ;
351356 }
357+ } ) ;
358+ const info = readyInfo ;
359+ const audioTrack = info ?. tracks . find ( ( t ) => t . codec ?. startsWith ( 'mp4a' ) ) ;
360+ if ( ! audioTrack ) {
361+ throw new Error ( 'No AAC track found in MP4/M4A' ) ;
362+ }
363+ mp4 . setExtractionOptions ( audioTrack . id , null , { nbSamples : 1 } ) ;
364+ const seekTimeSec = seekTimeMs / 1000 ;
365+ const mp4boxFile = mp4 ;
366+ const seekRes = mp4boxFile . seek ( seekTimeSec , true ) ;
367+ const startOffset = _seekOffset ( seekRes ) ;
368+ if ( ! Number . isFinite ( startOffset ) || startOffset < 0 ) {
369+ throw new Error ( `MP4Box seek returned invalid offset: ${ JSON . stringify ( seekRes ) } ` ) ;
352370 }
353- catch ( e ) {
354- reject ( e ) ;
355- }
356- } ) ;
357- const info = readyInfo ;
358- const audioTrack = info ?. tracks . find ( ( t ) => t . codec ?. startsWith ( 'mp4a' ) ) ;
359- if ( ! audioTrack ) {
360- throw new Error ( 'No AAC track found in MP4/M4A' ) ;
361- }
362- mp4 . setExtractionOptions ( audioTrack . id , null , { nbSamples : 1 } ) ;
363- const seekTimeSec = seekTimeMs / 1000 ;
364- const mp4boxFile = mp4 ;
365- const seekRes = mp4boxFile . seek ( seekTimeSec , true ) ;
366- const startOffset = _seekOffset ( seekRes ) ;
367- try {
368- mp4 . stop ( ) ;
371+ return {
372+ prefetch,
373+ baseFileStart : startOffset ,
374+ seekTimeSec
375+ } ;
369376 }
370- catch { }
371- if ( ! Number . isFinite ( startOffset ) || startOffset < 0 ) {
372- throw new Error ( `MP4Box seek returned invalid offset: ${ JSON . stringify ( seekRes ) } ` ) ;
377+ finally {
378+ try {
379+ mp4 . stop ( ) ;
380+ mp4 . flush ( ) ;
381+ }
382+ catch { }
383+ mp4 . onReady = null ;
384+ mp4 . onSamples = null ;
385+ mp4 . onError = null ;
373386 }
374- return {
375- prefetch,
376- baseFileStart : startOffset ,
377- seekTimeSec
378- } ;
379387}
380388const _createSeekableProxyRequest = ( proxy ) => {
381389 if ( ! proxy )
@@ -502,8 +510,10 @@ class BaseAudioResource {
502510 }
503511 for ( let i = this . pipes . length - 1 ; i >= 0 ; i -- ) {
504512 const pipe = this . pipes [ i ] ;
513+ pipe . resume ?. ( ) ;
505514 pipe . abort ?. ( ) ;
506515 pipe . unpipe ?. ( ) ;
516+ pipe . cleanup ?. ( ) ;
507517 pipe . destroy ?. ( ) ;
508518 }
509519 this . pipes . length = 0 ;
@@ -694,6 +704,9 @@ class SymphoniaDecoderStream extends Transform {
694704 this . _aborted = true ;
695705 this . _cancelTimers ( ) ;
696706 }
707+ cleanup ( ) {
708+ this . _cleanup ( ) ;
709+ }
697710 _cancelTimers ( ) {
698711 if ( this . _timeoutId ) {
699712 clearTimeout ( this . _timeoutId ) ;
@@ -1044,15 +1057,28 @@ class AACDecoderStream extends Transform {
10441057 } )
10451058 . catch ( ( err ) => this . emit ( 'error' , err ) ) ;
10461059 }
1047- _destroy ( err , cb ) {
1060+ cleanup ( ) {
10481061 this . ringBuffer . dispose ( ) ;
10491062 this . pendingChunks . length = 0 ;
1050- if ( this . decoder )
1051- this . decoder . free ?. ( ) ;
1063+ if ( this . decoder ) {
1064+ try {
1065+ this . decoder . free ?. ( ) ;
1066+ this . decoder . destroy ?. ( ) ;
1067+ }
1068+ catch { }
1069+ this . decoder = null ;
1070+ }
10521071 if ( this . resampler ) {
1053- this . resampler . destroy ?. ( ) ;
1072+ try {
1073+ this . resampler . destroy ?. ( ) ;
1074+ }
1075+ catch { }
10541076 this . resampler = null ;
10551077 }
1078+ this . resamplerCreationPromise = null ;
1079+ }
1080+ _destroy ( err , cb ) {
1081+ this . cleanup ( ) ;
10561082 super . _destroy ( err , cb ) ;
10571083 }
10581084 _downmixToStereo ( interleavedPCM , channels , samplesPerChannel ) {
@@ -1213,12 +1239,25 @@ class AACDecoderStream extends Transform {
12131239 converterType : _getResamplerConverterType ( this . resamplingQuality , libSampleRate )
12141240 } ) )
12151241 . then ( ( resampler ) => {
1242+ if ( this . destroyed || this . closed ) {
1243+ try {
1244+ resampler . destroy ?. ( ) ;
1245+ }
1246+ catch { }
1247+ return null ;
1248+ }
12161249 this . resampler = resampler ;
12171250 this . resamplerCreationPromise = null ;
12181251 return resampler ;
1252+ } )
1253+ . catch ( ( err ) => {
1254+ this . resamplerCreationPromise = null ;
1255+ throw err ;
12191256 } ) ;
12201257 }
12211258 const resampler = await this . resamplerCreationPromise ;
1259+ if ( ! resampler || this . destroyed || this . closed )
1260+ return ;
12221261 const resampled = resampler . full ( pcm ) ;
12231262 const pcmInt16 = new Int16Array ( resampled . length ) ;
12241263 for ( let i = 0 ; i < resampled . length ; i ++ ) {
@@ -1268,12 +1307,7 @@ class AACDecoderStream extends Transform {
12681307 }
12691308 catch ( _err ) { }
12701309 }
1271- if ( this . resampler ) {
1272- this . resampler . destroy ?. ( ) ;
1273- this . resampler = null ;
1274- }
1275- if ( this . decoder )
1276- this . decoder . destroy ?. ( ) ;
1310+ this . cleanup ( ) ;
12771311 callback ( ) ;
12781312 }
12791313}
@@ -1375,7 +1409,7 @@ class MP4ToAACStream extends Transform {
13751409 }
13761410 this . _initPromise = ( async ( ) => {
13771411 const mp4Box = await getMP4Box ( ) ;
1378- this . mp4boxFile = mp4Box . createFile ( false ) ;
1412+ this . mp4boxFile = mp4Box . createFile ( true ) ;
13791413 this . _setupMP4BoxHandlers ( ) ;
13801414 } ) ( ) ;
13811415 await this . _initPromise ;
0 commit comments