bufferAppendError
What version of Hls.js are you using?
1.5.2
What browser (including version) are you using?
Chrome 120.0.6099.234 (Official Build) (arm64)
What OS (including version) are you using?
macOS (Apple Silicon M1 if that makes a difference)
Test stream
No response
Configuration
{
maxLiveSyncPlaybackRate: 1.5,
debug: true,
}
Additional player setup steps
I'm streaming live video from a MediaMTX endpoint with this HLS player setup:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<style>
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#video {
width: 100%;
height: 100%;
background: black;
}
</style>
</head>
<body>
<script src="hls.min.js"></script>
<script>
const create = (video) => {
// always prefer hls.js over native HLS.
// this is because some Android versions support native HLS
// but don't support fMP4s.
if (Hls.isSupported()) {
const hls = new Hls({
maxLiveSyncPlaybackRate: 1.5,
debug: true,
});
hls.on(Hls.Events.ERROR, (evt, data) => {
if (data.fatal) {
if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
console.log('Recovering media error', data);
hls.recoverMediaError();
} else {
console.log('Fatal data');
hls.destroy();
setTimeout(() => create(video), 2000);
}
}
});
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
hls.loadSource(
'[MediaMTX stream endpoint]' +
'index.m3u8' +
window.location.search
);
});
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play();
});
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// since it's not possible to detect timeout errors in iOS,
// wait for the playlist to be available before starting the stream
fetch('index.m3u8').then(() => {
video.src = 'index.m3u8';
video.play();
});
}
};
/**
* Parses the query string from a URL into an object representing the query parameters.
* If no URL is provided, it uses the query string from the current page's URL.
*
* @param {string} [url=window.location.search] - The URL to parse the query string from.
* @returns {Object} An object representing the query parameters with keys as parameter names and values as parameter values.
*/
const parseQueryString = (url) => {
const queryString = (url || window.location.search).split('?')[1];
if (!queryString) return {};
const paramsArray = queryString.split('&');
const result = {};
for (let i = 0; i < paramsArray.length; i++) {
const param = paramsArray[i].split('=');
const key = decodeURIComponent(param[0]);
const value = decodeURIComponent(param[1] || '');
if (key) {
if (result[key]) {
if (Array.isArray(result[key])) {
result[key].push(value);
} else {
result[key] = [result[key], value];
}
} else {
result[key] = value;
}
}
}
return result;
};
/**
* Parses a string with boolean-like values and returns a boolean.
* @param {string} str The string to parse
* @param {boolean} defaultVal The default value
* @returns {boolean}
*/
const parseBoolString = (str, defaultVal) => {
const trueValues = ['1', 'yes', 'true'];
const falseValues = ['0', 'no', 'false'];
str = (str || '').toString();
if (trueValues.includes(str.toLowerCase())) {
return true;
} else if (falseValues.includes(str.toLowerCase())) {
return false;
} else {
return defaultVal;
}
};
/**
* Sets video attributes based on query string parameters or default values.
*
* @param {HTMLVideoElement} video - The video element on which to set the attributes.
*/
const setVideoAttributes = (video) => {
let qs = parseQueryString();
// video.controls = parseBoolString(qs['controls'], true);
video.muted = parseBoolString(qs['muted'], true);
video.autoplay = parseBoolString(qs['autoplay'], true);
video.playsInline = parseBoolString(qs['playsinline'], true);
};
/**
*
* @param {(video: HTMLVideoElement) => void} callback
* @param {HTMLElement} container
* @returns
*/
const initVideoElement = (callback, container) => {
return () => {
const video = document.createElement('video');
video.id = 'video';
setVideoAttributes(video);
container.append(video);
callback(video);
};
};
window.addEventListener(
'DOMContentLoaded',
initVideoElement(create, document.body)
);
</script>
</body>
</html>
Checklist
- [X] The issue observed is not already reported by searching on Github under https://github.com/video-dev/hls.js/issues
- [X] The issue occurs in the stable client (latest release) on https://hlsjs.video-dev.org/demo and not just on my page
- [x] The issue occurs in the latest client (main branch) on https://hlsjs-dev.video-dev.org/demo and not just on my page
- [X] The stream has correct Access-Control-Allow-Origin headers (CORS)
- [X] There are no network errors such as 404s in the browser console when trying to play the stream
Steps to reproduce
This bug is intermittent but it happens about once every minute on average.
Expected behaviour
No bug.
What actually happened?
I get bufferAppendError every minute. What are some potential causes for this and possible configs that can help workaround?
Console output
hls.min.js:1 [log] > [stream-controller]: Level 0 loaded [11314,11320][part-11321-1], cc [0, 0] duration:12.125
hls.min.js:1 [log] > [stream-controller]: Loading part sn: 11321 p: 1 cc: 0 of playlist [11314-11320] parts [0-14-14] level: 0, target: 587.875
hls.min.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.min.js:1 [log] > [buffer-controller] Updating Media Source duration to 588.125
hls.min.js:1 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.min.js:1 [log] > [transmuxer.ts]: Flushed fragment 11321 p: 1 of level 0
hls.min.js:1 [log] > [stream-controller]: PARSING->PARSED
hls.min.js:1 [log] > [stream-controller]: Buffered main sn: 11321 part: 1 of level 0 (frag:[NaN-NaN] > buffer:[6.125-588.125])
hls.min.js:1 [log] > [stream-controller]: PARSED->IDLE
hls.min.js:1 [log] > [level-controller]: live playlist 0 REFRESHED 11321-2
hls.min.js:1 [log] > [level-controller]: Loading level index 0 at sn 11321 part 3 with [stream name]/stream.m3u8?_HLS_msn=11321&_HLS_part=3&_HLS_skip=YES
hls.min.js:1 [log] > [stream-controller]: Level 0 loaded [11314,11320][part-11321-2], cc [0, 0] duration:12.375
hls.min.js:1 [log] > [stream-controller]: Loading part sn: 11321 p: 2 cc: 0 of playlist [11314-11320] parts [0-15-15] level: 0, target: 588.125
hls.min.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.min.js:1 [log] > [buffer-controller] Updating Media Source duration to 588.375
hls.min.js:1 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.min.js:1 [log] > [stream-controller]: Reset loading state
hls.min.js:1 [log] > [stream-controller]: PARSING->IDLE
hls.min.js:1 [log] > stopLoad
hls.min.js:1 [log] > [stream-controller]: IDLE->STOPPED
hls.min.js:1 [log] > [subtitle-stream-controller]: IDLE->STOPPED
hlsplayer.html:38 Issue
hlsplayer.html:40 Recovering media error {type: 'mediaError', parent: 'main', details: 'bufferAppendError', sourceBufferName: 'video', frag: e, …}chunkMeta: {level: 0, sn: 11321, part: 2, id: 3, size: 5905, …}details: "bufferAppendError"err: DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.
at e.appendExecutor (file:///Users/user/Downloads/hls.min.js:1:266629)
at Object.execute (file:///Users/user/Downloads/hls.min.js:1:258747)
at e.executeNext (file:///Users/user/Downloads/hls.min.js:1:250534)
at e.append (file:///Users/user/Downloads/hls.min.js:1:250191)
at e.onBufferAppending (file:///Users/user/Downloads/hls.min.js:1:259669)
at o.emit (file:///Users/user/Downloads/hls.min.js:1:206056)
at e.emit (file:///Users/user/Downloads/hls.min.js:1:404023)
at e.trigger (file:///Users/user/Downloads/hls.min.js:1:404093)
at r.bufferFragmentData (file:///Users/user/Downloads/hls.min.js:1:134949)
at r._handleTransmuxComplete (file:///Users/user/Downloads/hls.min.js:1:396384)error: DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.
at e.appendExecutor (file:///Users/user/Downloads/hls.min.js:1:266629)
at Object.execute (file:///Users/user/Downloads/hls.min.js:1:258747)
at e.executeNext (file:///Users/user/Downloads/hls.min.js:1:250534)
at e.append (file:///Users/user/Downloads/hls.min.js:1:250191)
at e.onBufferAppending (file:///Users/user/Downloads/hls.min.js:1:259669)
at o.emit (file:///Users/user/Downloads/hls.min.js:1:206056)
at e.emit (file:///Users/user/Downloads/hls.min.js:1:404023)
at e.trigger (file:///Users/user/Downloads/hls.min.js:1:404093)
at r.bufferFragmentData (file:///Users/user/Downloads/hls.min.js:1:134949)
at r._handleTransmuxComplete (file:///Users/user/Downloads/hls.min.js:1:396384)errorAction: {action: 2, flags: 1}fatal: truefrag: e {_byteRange: null, _url: null, baseurl: '[stream name]?_HLS_msn=11321&_HLS_part=2&_HLS_skip=YES', relurl: undefined, elementaryStreams: {…}, …}parent: "main"part: e {_byteRange: null, _url: '[stream name]/eb0b50908d80_part56098.mp4', baseurl: '[stream name]?_HLS_msn=11321&_HLS_part=2&_HLS_skip=YES', relurl: 'eb0b50908d80_part56098.mp4', elementaryStreams: {…}, …}sourceBufferName: "video"type: "mediaError"[[Prototype]]: Object
hls.min.js:1 [log] > recoverMediaError
hls.min.js:1 [warn] > [buffer-operation-queue]: Exception executing "video" SourceBuffer operation: InvalidStateError: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.
hls.min.js:1 [warn] > [buffer-controller] Failed 1/3 times to append segment in "video" sourceBuffer
hls.min.js:1 [warn] > Could not resolve bufferAppendError ("Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.") with content-steering for Pathway: . levels: 1 priorities: ["."] penalized: {".":325687.7000000477}
I also noticed there's a correlation between this error being thrown and some requests getting canceled. Here it is as shown in Chrome's inspector:
Chrome media internals output
No response
Hi. I haven't seen this issue on Low-Latency HLS assets. Do the parts have the correct INDEPENDENT attribute markers?
Take a look at the media dev tools panel for more information about the what is putting the video element in an error state. It could be a decoding error caused by the part content delivered. HLS.js should be able to append any of the parts delivered without an issue unless they are marked as not independent and appended out of sequence. From the logs you've shared everything appears to be in order. That leads me to think you have mp4 parts that cannot be appended independently and either they are not marked as such or they genuinely cause decode errors all on their own.
@robwalch Hmm here's some metadata that might help debug futher:
index.m3u8:
#EXTM3U
#EXT-X-VERSION:9
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=209120,AVERAGE-BANDWIDTH=202088,CODECS="avc1.640016",RESOLUTION=512x512,FRAME-RATE=8.000
stream.m3u8
stream.m3u8 (one of them)
#EXTM3U
#EXT-X-VERSION:9
#EXT-X-TARGETDURATION:1
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=0.62500,CAN-SKIP-UNTIL=6.00000
#EXT-X-PART-INF:PART-TARGET=0.25000
#EXT-X-MEDIA-SEQUENCE:732
#EXT-X-SKIP:SKIPPED-SEGMENTS=2
#EXTINF:1.37500,
206122b75bc6_seg734.mp4
#EXTINF:1.00000,
206122b75bc6_seg735.mp4
#EXTINF:1.00000,
206122b75bc6_seg736.mp4
#EXT-X-PROGRAM-DATE-TIME:2024-01-29T18:16:01.295Z
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3019.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3020.mp4"
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3021.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3022.mp4",INDEPENDENT=YES
#EXTINF:1.00000,
206122b75bc6_seg737.mp4
#EXT-X-PROGRAM-DATE-TIME:2024-01-29T18:16:02.329Z
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3023.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3024.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3025.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3026.mp4",INDEPENDENT=YES
#EXTINF:1.00000,
206122b75bc6_seg738.mp4
#EXT-X-PART:DURATION=0.25000,URI="206122b75bc6_part3027.mp4",INDEPENDENT=YES
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="206122b75bc6_part3028.mp4"
I see that for most #EXT-X-PART lines the INDEPENDENT attribute marker is present but does this look right?
I don't see any explicit errors in the media dev tools messages tab:
ChunkDemuxer
Cannot select DecryptingVideoDecoder for video decoding
Effective playback rate changed from 0 to 1
Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [512,512], visible rect: [0,0,512,512], natural size: [512,512], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}
Cannot select DecryptingVideoDecoder for video decoding
Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [512,512], visible rect: [0,0,512,512], natural size: [512,512], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}
^ these all appear to be info level messages. If there's somewhere I can find more error states I'd be happy to look.
Unable to debug further without a sample stream. Please provide one that reproduces the issue.
Unfortunately I'm unable to provide a sample stream currently as it's a customer stream. However, we did find something of a workaround that may help posterity and possibly help point to the problem.
We switched our edge device gstreamer streaming protocol from RTSP over UDP to RTSP over TCP and the problem essentially disappeared. This works for our streaming network topology but may not work for those who want to multicast from their edge devices. I can imagine that with the RTSP link running over TCP, HLS segments are more reliably delivered to the client so we're not seeing the bufferAppendError anymore. However, I expect HLS.js to gracefully wait for the next available segment instead of throwing a fatal error.
Not sure if this is helpful in debugging or making improvments to the library but it's the best I can do right now.
I expect HLS.js to gracefully wait for the next available segment instead of throwing a fatal error.
DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.
This error is caused by an earlier corruption of playback in the HTMLMediaElement (most likely a decode error from an earlier append). It can't be handled gracefully after the fact. Calling recoverMediaError is the only recourse (or restart the live stream) but that may only result in the same error and it is visible to the user.
If we could reproduce the issue there may be something we could do like detect and drop the parts or segments that result in a decode error.
Unable to debug further without a sample stream. Please provide one that reproduces the issue.
I also encountered the problem of bufferAppendError environment:
- hls.js: v1.5.7
- chrome: 123.0.6312.107(official version) (arm64)
- macOS Sonoma 14.4.1 (23E224) test.zip
I can't provide online streaming, but I downloaded it to a local file,You can start the service locally and test it in https://e655f194.hls-js-dev.pages.dev/demo/ ,There is an audio problem with the first 2 ts of the stream. It freezes during playback and cannot continue to play.
v1.5.7 debug信息: 1.5.7-hlsjs.video-dev.org-1712642726128.log
I installed the hls playback plug-in on chrome, and I found that m3u8 can be played using v0.14.16, debug information 0.14.16-hls-debug.log
@wangyong991,
Please file a new issue. Your sample and the error it produces (initial segments are missing audio) does not match the description filed by @0xAl3xH.
@0xAl3xH,
I get bufferAppendError every minute. What are some potential causes for this and possible configs that can help workaround? We switched our edge device gstreamer streaming protocol from RTSP over UDP to RTSP over TCP and the problem essentially disappeared. This works for our streaming network topology but may not work for those who want to multicast from their edge devices. I can imagine that with the RTSP link running over TCP, HLS segments are more reliably delivered to the client so we're not seeing the bufferAppendError anymore. However, I expect HLS.js to gracefully wait for the next available segment instead of throwing a fatal error.
HLS.js waits for media to be published in the Playlist. Any missing or incomplete media should never be published in a Playilst update. There is nothing for HLS.js to wait for in this case. The only recourse to publishing a segment HLS.js should not load is to append a GAP tag above it. Otherwise, HLS.js is following the published content and content was published that corrupts the buffer.
I also noticed there's a correlation between this error being thrown and some requests getting canceled.
If you provided multiple variants, then on error HLS.js will attempt to switch levels which would cancel in-flight requests. We would need complete logs to confirm this.
Closing as stream-issue as the comment about RTSP over UDP indicates the possibility of an encoder issue delivering incomplete or potentially corrupt segments.