Skip to content

BUFFER_STALLED_ERROR executes many times very quickly and is making the video freeze at random points. #7433

@brianmontero

Description

@brianmontero

What version of Hls.js are you using?

1.5.20

What browser (including version) are you using?

Chrome 138.0.7204.169 (Official build) (64 bits)

What OS (including version) are you using?

Windows 10

Test stream

No response

Configuration

{
	capLevelToPlayerSize: true,
	autoStartLoad: true,
	startLevel: 0,
	maxBufferLength: 30,
	maxBufferSize: 30 * 1000 * 1000,
	maxBufferHole: 0.5,
	lowLatencyMode: false,
	maxFragLookUpTolerance: 0.25,
	abrEwmaFastLive: 3.0,
	abrEwmaSlowLive: 9.0,
	abrEwmaFastVoD: 3.0,
	abrEwmaSlowVoD: 9.0,
	abrBandWidthFactor: 0.7,
	abrBandWidthUpFactor: 0.25,
	maxLiveSyncPlaybackRate: 1.0,
	liveSyncDurationCount: 3,
	enableWorker: true,
	backBufferLength: 30,
}

Additional player setup steps

ngAfterViewInit(): void {
this.videoEndEmitted = false;
const video = this.videoRef.nativeElement;
this.repositionSubtitles();

	if (Hls.isSupported()) {
		this.hls = new Hls({
			capLevelToPlayerSize: true,
			autoStartLoad: true,
			startLevel: 0,
			maxBufferLength: 30,
			maxBufferSize: 30 * 1000 * 1000,
			maxBufferHole: 0.5,
			lowLatencyMode: false,
			maxFragLookUpTolerance: 0.25,
			abrEwmaFastLive: 3.0,
			abrEwmaSlowLive: 9.0,
			abrEwmaFastVoD: 3.0,
			abrEwmaSlowVoD: 9.0,
			abrBandWidthFactor: 0.7,
			abrBandWidthUpFactor: 0.25,
			maxLiveSyncPlaybackRate: 1.0,
			liveSyncDurationCount: 3,
			enableWorker: true,
			backBufferLength: 30,
		});

		if ((navigator as any)?.connection && (navigator as any).connection?.effectiveType === '4g') {
			this.hls.config.maxBufferLength = 60;
			this.hls.config.maxBufferSize = 50 * 1000 * 1000;
		}

		this.hls.loadSource(this.videoUrl);
		this.hls.attachMedia(video);

		this.hls.on(Hls.Events.MANIFEST_PARSED, async () => {
			await video.play();
		});

		this.hls.on(Hls.Events.ERROR, (event, data) => {
			if (data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
				console.warn('HLS.js stalled, trying to recover...');

				try {
					this.hls.recoverMediaError();
				} catch (err) {
					console.error('Failed to recover media error:', err);
				}

				setTimeout(() => {
					this.hls.startLoad();
				}, 1000);
			}
		});

		this.hls.on(Hls.Events.ERROR, (event, data) => {
			if (data && data?.reason.includes('buffer holes')) {
				return;
			}

			console.error('hls player error: ', data);
		});
	} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
		video.src = this.videoUrl;
		video.addEventListener('loadedmetadata', async () => {
			await video.play();
		}, { once: true });
	}
}

Checklist

Steps to reproduce

  1. Open the video player
  2. Play the video
  3. Wait until it stalls

Expected behaviour

Video shouldn't stall that much

What actually happened?

I have a fairly big platform where i'm using ngx-videogular and hls.js to reproduce transcoded videos. Most users play the videos just fine but there are many people complaining that the videos stall when they play them, they report that the stall can occur at any point in the video, randomly.

The issue does not seems to be device-specific, we have been able to reproduce it in PCs and phones (both iOS and Android). Since most users are able to play videos fine, i assume the .m3u8 and .ts files are correctly formed, otherwise it would fail for everyone.

The error seems to happen when the BUFFER_STALLED_ERROR event fires. It's my understanding that this can happen and that the library itself will try to recover the stream by default. From what our QA team was able to reproduce, the bug happens when the BUFFER_STALLED_ERROR event fires a lot in a short period of time. This will cause the "freeze" that many users are complaining about.

This is what i'm doing in my code

`this.hls.on(Hls.Events.ERROR, (event, data) => {
if (data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
console.warn('HLS.js stalled, trying to recover...');

				try {
					this.hls.recoverMediaError();
				} catch (err) {
					console.error('Failed to recover media error:', err);
				}

				setTimeout(() => {
					this.hls.startLoad();
				}, 1000);
			}

});`

the console.error('Failed to recover media error:', err); never logs, so i'm guessing the this.hls.recoverMediaError(); is not throwing.

QA team also discards a NETWORK_ERROR event, they have a bandwidth tracker when playing the videos and the levels are steady all the time (no connection errors).

Additional information: we use Cloudfront to serve the videos. We do the Hls instance ourselves (we're not using ngx-videogular directive), we properly dispose the hls instance on destroy of the video player component.

We barely have any information on the causes of this bug because it happens to a relatively small percentage of users, but our platform has more than 35k users registered and the concurrent amonut of users tends to be something around ~200 users at any given time.

I've already changed the hls configuration many times in the past five days, i've reduced the maxBufferLength a lot, thinking the freeze had something to do with too many chunks being downloaded too fast and overwhelming some devices, i have the appropiate hls.destroy() invokation in my ngOnDestory and i don't think it is a memory issue (the devices have the videos freezed but the user can still browse the app just fine).

Please, any guidance would be more than helpful, the client is very angry about it and we're kind of desperate here.

Console output

None, we don't have the `debug` property logging in production, and when the QA team reproduced the issues in dev we didn't have the `debug` property on.

Chrome media internals output

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions