@@ -216,8 +216,47 @@ export default class AudioBase {
216216 return manifest . source
217217 }
218218
219+ handleDemuxerError = async ( event ) => {
220+ if ( ! event || ! event . error ) {
221+ return null
222+ }
223+
224+ if (
225+ event . error . message &&
226+ event . error . message . includes ( "QuotaExceededError" )
227+ ) {
228+ this . console . warn (
229+ "QuotaExceededError detected, attempting buffer cleanup and recovery" ,
230+ )
231+
232+ const currentTime = this . audio . currentTime
233+
234+ // try to clear buffers and recover
235+ if ( this . demuxer ) {
236+ // reset and reinitialize the demuxer
237+ this . demuxer . reset ( )
238+
239+ // reattach the source
240+ const manifest = this . player . queue . currentItem
241+
242+ if ( manifest && manifest . dash_manifest ) {
243+ await this . demuxer . attachSource ( manifest . dash_manifest , currentTime )
244+
245+ // resume playback from current position
246+ this . audio . currentTime = currentTime
247+
248+ if ( this . player . state . playback_status === "playing" ) {
249+ await this . demuxer . play ( )
250+ }
251+
252+ this . console . log ( "Successfully recovered from QuotaExceededError" )
253+ }
254+ }
255+ }
256+ }
257+
219258 async createDemuxer ( ) {
220- // Destroy previous instance if exists
259+ // destroy previous instance if exists
221260 if ( this . demuxer ) {
222261 try {
223262 this . demuxer . reset ( )
@@ -238,59 +277,75 @@ export default class AudioBase {
238277
239278 this . demuxer . updateSettings ( {
240279 streaming : {
241- //cacheInitSegments: true,
280+ //cacheInitSegments: true
242281 buffer : {
243- bufferTimeAtTopQuality : 30 ,
244- bufferTimeAtTopQualityLongForm : 60 ,
245- bufferTimeDefault : 20 ,
246- initialBufferLevel : 5 ,
247- bufferToKeep : 10 ,
282+ fastSwitchEnabled : true ,
283+ flushBufferAtTrackSwitch : true ,
284+ resetSourceBuffersForTrackSwitch : true ,
285+ bufferToKeep : 2 ,
286+ bufferTimeDefault : 15 ,
287+ bufferTimeAtTopQuality : 20 ,
288+ bufferTimeAtTopQualityLongForm : 25 ,
248289 longFormContentDurationThreshold : 300 ,
249- stallThreshold : 0.5 ,
250- bufferPruningInterval : 30 ,
251- } ,
252- abr : {
253- initialBitrate : {
254- audio : 128 ,
255- } ,
256- rules : {
257- insufficientBufferRule : {
258- active : true ,
259- parameters : {
260- bufferLevel : 0.3 ,
261- } ,
262- } ,
263- switchHistoryRule : {
264- active : true ,
265- parameters : {
266- sampleSize : 8 ,
267- } ,
268- } ,
269- } ,
270- throughput : {
271- averageCalculationMode : "slidingWindow" ,
272- slidingWindowSize : 20 ,
273- ewmaHalfLife : 4 ,
274- } ,
275- } ,
276- retrySettings : {
277- maxRetries : 5 ,
278- retryDelayMs : 1000 ,
279- retryBackoffFactor : 2 ,
280- } ,
281- requests : {
282- requestTimeout : 30000 ,
283- xhrWithCredentials : false ,
290+ stallThreshold : 1 ,
291+ bufferPruningInterval : 3 ,
284292 } ,
285293 } ,
286294 } )
287295
288- // Event listeners
296+ // event listeners
289297 this . demuxer . on ( dashjs . MediaPlayer . events . ERROR , ( event ) => {
290298 console . error ( "Demuxer error" , event )
299+ this . handleDemuxerError ( event )
291300 } )
292301 }
293302
303+ preventiveBufferCleanup = async ( ) => {
304+ if ( ! this . demuxer ) {
305+ return null
306+ }
307+
308+ try {
309+ const currentTime = this . audio . currentTime
310+ const duration = this . audio . duration
311+ const bufferCleanupThreshold = 30
312+ const buffered = this . audio . buffered
313+
314+ // clean buffers that are more than 30 seconds behind current position
315+ if ( currentTime && duration && duration !== Infinity ) {
316+ if ( currentTime > bufferCleanupThreshold ) {
317+ // clean old ones
318+ for ( let i = 0 ; i < buffered . length ; i ++ ) {
319+ const start = buffered . start ( i )
320+ const end = buffered . end ( i )
321+
322+ // if this buffer range is too far behind, its a candidate for cleanup
323+ if ( end < currentTime - bufferCleanupThreshold ) {
324+ try {
325+ // force demuxer to prune old buffers
326+ this . demuxer . updateSettings ( {
327+ streaming : {
328+ buffer : {
329+ ...this . demuxer . getSettings ( ) . streaming . buffer ,
330+ bufferToKeep : 2 ,
331+ bufferPruningInterval : 1 ,
332+ } ,
333+ } ,
334+ } )
335+
336+ this . console . log ( `Cleaned buffer range: ${ start } -${ end } ` )
337+ } catch ( cleanupError ) {
338+ this . console . warn ( "Buffer cleanup failed:" , cleanupError )
339+ }
340+ }
341+ }
342+ }
343+ }
344+ } catch ( error ) {
345+ this . console . warn ( "Preventive buffer cleanup failed:" , error )
346+ }
347+ }
348+
294349 timeTick = async ( ) => {
295350 if (
296351 ! this . audio ||
@@ -314,11 +369,22 @@ export default class AudioBase {
314369 )
315370 }
316371 }
372+
373+ // run preventive buffer cleanup every 10 seconds
374+ if ( Math . floor ( this . audio . currentTime ) % 10 === 0 ) {
375+ await this . preventiveBufferCleanup ( )
376+ }
317377 }
318378
319379 async flush ( ) {
320380 this . audio . pause ( )
321381 this . audio . currentTime = 0
382+
383+ // clear intervals
384+ if ( typeof this . _timeTickInterval !== "undefined" ) {
385+ clearInterval ( this . _timeTickInterval )
386+ }
387+
322388 await this . createDemuxer ( )
323389 }
324390
0 commit comments