@@ -290,14 +290,69 @@ const qualityLevels = (player, options) => {
290290 }
291291 } ;
292292
293+ // Helper to synchronise Video.js AudioTrackList with hls.js active track
294+ const updateVjsAudioTracksEnabled = activeIndex => {
295+ const list = player . audioTracks ( ) ;
296+ if ( ! list ) return ;
297+ for ( let i = 0 ; i < list . length ; i ++ ) {
298+ list [ i ] . enabled = i === activeIndex ;
299+ }
300+ // Sync complete – log for debug
301+ logAudioTrackInfo ( ) ;
302+ } ;
303+
304+ // Flush buffered audio by performing a tiny seek outside current range
305+ const flushBufferedAudio = ( ) => {
306+ try {
307+ const cur = player . currentTime ( ) ;
308+ const delta = 0.5 ; // 500 ms gap to escape current buffer
309+ const target = Math . min ( cur + delta , player . duration ( ) - 0.1 ) ;
310+ player . currentTime ( target ) ;
311+ player . currentTime ( cur ) ;
312+ } catch ( e ) {
313+ debugLog ( 'Error while flushing buffered audio' , e ) ;
314+ }
315+ } ;
316+
317+ // Force hls.js to switch track and immediately start loading fragments for it
318+ const selectAudioTrack = index => {
319+ var tech = player . tech ( { IWillNotUseThisInPlugins : true } ) ;
320+ if (
321+ typeof tech . sourceHandler_ != 'undefined' &&
322+ typeof tech . sourceHandler_ . hls != 'undefined' &&
323+ tech . sourceHandler_ . hls != null
324+ ) {
325+ const hls = tech . sourceHandler_ . hls ;
326+ if ( hls . audioTrack === index ) {
327+ return ;
328+ }
329+ hls . audioTrack = index ;
330+ // Quickly restart loading to fill the buffer of the new audio group
331+ if ( hls . media ) {
332+ try {
333+ hls . stopLoad ( ) ;
334+ hls . startLoad ( 0 ) ;
335+ } catch ( e ) {
336+ debugLog ( 'Error while reloading after audioTrack switch' , e ) ;
337+ }
338+ }
339+
340+ logAudioTrackInfo ( ) ;
341+ // will flush once switch is confirmed (AUDIO_TRACK_SWITCHED)
342+ hls . once ( Hls . Events . AUDIO_TRACK_SWITCHED , ( ) => {
343+ flushBufferedAudio ( ) ;
344+ } ) ;
345+ }
346+ } ;
347+
293348 // Audio track handling
294- const addAudioTrackVideojs = track => {
295- var vjsTrack = new videojs . AudioTrack ( {
349+ const addAudioTrackVideojs = ( track , hls ) => {
350+ const vjsTrack = new videojs . AudioTrack ( {
296351 id : `${ track . type } -id_${ track . id } -groupId_${ track . groupId } -${ track . name } ` ,
297352 kind : 'translation' ,
298353 label : track . name ,
299354 language : track . lang ,
300- enabled : track . enabled ,
355+ enabled : hls . audioTrack === hls . audioTracks . indexOf ( track ) ,
301356 default : track . default
302357 } ) ;
303358
@@ -315,7 +370,7 @@ const qualityLevels = (player, options) => {
315370 const hls = tech . sourceHandler_ . hls ;
316371 const len = hls . audioTracks . length ;
317372 for ( let i = 0 ; i < len ; i ++ ) {
318- addAudioTrackVideojs ( hls . audioTracks [ i ] ) ;
373+ addAudioTrackVideojs ( hls . audioTracks [ i ] , hls ) ;
319374 }
320375 }
321376
@@ -328,11 +383,10 @@ const qualityLevels = (player, options) => {
328383 typeof tech . sourceHandler_ . hls != 'undefined' &&
329384 tech . sourceHandler_ . hls != null
330385 ) {
331- const hls = tech . sourceHandler_ . hls ;
332386 for ( var i = 0 ; i < audioTrackList . length ; i ++ ) {
333387 var track = audioTrackList [ i ] ;
334388 if ( track . enabled ) {
335- hls . audioTrack = i ;
389+ selectAudioTrack ( i ) ;
336390 return ;
337391 }
338392 }
@@ -354,25 +408,35 @@ const qualityLevels = (player, options) => {
354408 tech . sourceHandler_ . hls != null
355409 ) {
356410 const hls = tech . sourceHandler_ . hls ;
357- hls . on ( Hls . Events . MANIFEST_LOADED , ( eventName , data ) => {
411+
412+ const manifestLoadedHandler = ( eventName , data ) => {
358413 debugLog ( `HLS event: ${ eventName } ` , data ) ;
359414 populateLevels ( hls . levels , 'hls' ) ;
360- } ) ;
415+ initAudioTrackInfo ( ) ;
416+ hls . off ( Hls . Events . MANIFEST_LOADED , manifestLoadedHandler ) ;
417+ } ;
418+
419+ // If manifest is already loaded, populate levels immediately.
420+ if ( hls . levels && hls . levels . length > 0 ) {
421+ manifestLoadedHandler ( 'MANUAL_MANIFEST_LOADED' , { } ) ;
422+ } else {
423+ hls . on ( Hls . Events . MANIFEST_LOADED , manifestLoadedHandler ) ;
424+ }
361425
362426 hls . on ( Hls . Events . LEVEL_SWITCHED , ( eventName , data ) => {
363427 debugLog ( `HLS event: ${ eventName } ` , data ) ;
364428 populateQualityLevelsChange ( data . level ) ;
365429 populateQualityChangedEvent ( data . level ) ;
366430 } ) ;
367431
368- hls . on ( Hls . Events . AUDIO_TRACKS_UPDATED , ( eventName , data ) => {
369- debugLog ( `HLS event: ${ eventName } ` , data ) ;
370- initAudioTrackInfo ( ) ;
371- } ) ;
372-
373432 hls . on ( Hls . Events . AUDIO_TRACK_SWITCHED , ( eventName , data ) => {
374433 debugLog ( `HLS event: ${ eventName } ` , data ) ;
375- logAudioTrackInfo ( ) ;
434+ // Make sure Video.js AudioTracks reflect the newly-selected HLS track
435+ if ( typeof data . id !== 'undefined' ) {
436+ updateVjsAudioTracksEnabled ( data . id ) ;
437+ }
438+ // Ensure buffer flushed when switch originates from hls.js (e.g. default track)
439+ flushBufferedAudio ( ) ;
376440 } ) ;
377441
378442 hls . on ( Hls . Events . ERROR , ( eventName , data ) => {
0 commit comments