Open luwes opened 1 year ago
In this case what would you expect to be a reasonable amount of time, and what length of time have you observed to be very late?
the Apple bip bop stream has the same issue https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8
see here https://recordit.co/o06l3VY7OH
a reasonable time for switching is like under 15s. it depends how far the buffer was loaded, it seems like it's not clearing the buffer of the previous chosen rendition at all. it could be some minutes.
is this expected behavior? thanks!
set nextLevel
calls streamController.nextLevelSwitch()
which depends on getBufferedFrag()
to determine what to flush from the fwd buffer. That is coming back null
here for some reason:
So the stream producing this issue has audio in the main TS and alt-audio. Switching to alt-audio, and back to the audio in the main playlist, requires the main fragments to be loaded again. For this reason we eject those fragments from the tracker on this kind of audio switch. Only using alt-audio (CMAF) would avoid this issue completely.
I'll leave this confirmed as a bug, but it only impacts TS main muxed with alt-audio. The tracker may remove segments whose video is still buffered after audio coming from that segment is removed, and it has to remove all for which audio is needed but may only have one track buffered (case in point above).
@luwes,
Let me know how critical this issue is for you. I can suggest workarounds for the recent releases if you need one.
A fix would be more involved and I don't have an easy one in mind for 1.5.
thanks for diagnosing this, I really appreciate it!
it's not very critical I think. yes, please let us know the workarounds. that would be great. we might help out with a fix soon. for now it's fine, I don't think many users will get in this behavior.
You can workaround the player not flushing by triggering a flush event in your app after setting nextLevel
.
hls.trigger(Hls.Events.BUFFER_FLUSHING, { startOffset: video.currentTime + 15, endOffset: Infinity, type: 'video' })
When the player does this flush itself, it triggers this event synchronously upon setting nextLevel
, so you could write something that listens for the event and flips a flag to tell if it was called or not. Because it may perform two flushes (back and forward buffer) you need to listen for all events while the setter is evoked:
function smoothSwitch(levelIndex) {
const currentTime = video.currentTime;
let flushedFwdBuffer = false;
const callback = (m, data) => {
// console.log(m, 'currentTime:', currentTime, data);
flushedFwdBuffer ||= !Number.isFinite(data.endOffset);
};
hls.on(Hls.Events.BUFFER_FLUSHING, callback);
hls.nextLevel = levelIndex;
hls.off(Hls.Events.BUFFER_FLUSHING, callback);
// console.log(flushedFwdBuffer ? 'flushed' : 'did not flush buffer');
if (!flushedFwdBuffer) {
hls.trigger(Hls.Events.BUFFER_FLUSHING, { startOffset: currentTime + 15, endOffset: Infinity, type: 'video' });
}
}
The startOffset
could also use some finesse here, but it doesn't have to be exactly on a segment boundary. hls.levels[hls.currentLevel].details.fragments
will give you the active playlist segment times, but not necessarily those which were appended at currentTime which we would from the fragmentTracker, but (because of the issue here) were cleared to allow reloading of audio in the main playlist.
It's not pretty, but it gets the job done.
For this reason we eject those fragments from the tracker on this kind of audio switch
Adding a note that a solution to this issue could be to modify fragment entities in the fragment tracker for the main playlist to denote that audio (but not video) was removed, instead of calling removeAllFragments
: https://github.com/video-dev/hls.js/blob/5f19df35ae9df5742dd998677f7613ee8b159f90/src/controller/stream-controller.ts#L789-L796
Then, find everywhere that we use the tracker to determine which fragment is buffered in which SourceBuffer(s) and whether they need to be reloaded for the missing/muxed audio.
Right now, if the tracked main playlist fragment entities were not all removed on muxed to alt audio switch, then they would never be reloaded on the switch back to muxed audio and playback would stall with an empty audio buffer and the player not knowing how to fill it. A simpler fix to this could be to only remove all the segments on the switch back to muxed audio. It's just little unclear whether we're removing everything on both changes or not.
This issue should be resolved by #6845 which will land in v1.6.0-beta.2.
TODO: verify the following:
Right now, if the tracked main playlist fragment entities were not all removed on muxed to alt audio switch, then they would never be reloaded on the switch back to muxed audio and playback would stall with an empty audio buffer and the player not knowing how to fill it. A simpler fix to this could be to only remove all the segments on the switch back to muxed audio. It's just little unclear whether we're removing everything on both changes or not.
From what I can tell, the issue was that main audio was still appended while switching and in some cases could append over alt audio. So I think you were hearing main audio that was queued to append before the selection was made, or that was re-loaded/re-appended after the selection but before the first alt-audio finalized the switch.
What version of Hls.js are you using?
1.4.10
What browser (including version) are you using?
Chrome Version 115.0.5790.170 (Official Build) (arm64)
What OS (including version) are you using?
MacOS
Test stream
https://hlsjs.video-dev.org/demo/?src=https%3A%2F%2Fstream.mux.com%2FSc89iWAyNkhJ3P1rQ02nrEdCFTnfT01CZ2KmaEcxXfB008.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsInN0b3BPblN0YWxsIjpmYWxzZSwiZHVtcGZNUDQiOmZhbHNlLCJsZXZlbENhcHBpbmciOi0xLCJsaW1pdE1ldHJpY3MiOi0xfQ==
Configuration
Additional player setup steps
No response
Checklist
Steps to reproduce
Expected behaviour
that the rendition / level switch happens in a reasonable time
What actually happened?
the rendition / level switch happens but very late, it's like the video buffer is not cleared to make room for the new chosen rendition when an alternative audio track is playing
Console output
Chrome media internals output
No response