-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
What version of Hls.js are you using?
1.5.15
What browser (including version) are you using?
Edge
What OS (including version) are you using?
win11
Test stream
No response
Configuration
if (Hls.isSupported() && videoElementRef.value) {
//请求m3u8
const { $customLoader } = useNuxtApp();
const { path, videoID } = await getPath(id);
hls.value = new Hls(
{
loader: $customLoader as any, // 使用自定义加载器
autoStartLoad: false,
lowLatencyMode: false, // 禁用低延迟模式
maxBufferLength: 30, // 缓冲区中最多保留30秒的视频数据
maxMaxBufferLength: 60, // 缓冲区中的最大保留时长
backBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, // 缓冲区最大数据量,单位为字节
maxBufferHole: 0.5, // 在缓冲的片段之间允许的最大时间间隙,单位为秒
startFragPrefetch: true, // 启用片段预加载
keyLoadPolicy: 'none',
// xhrSetup: function (xhr, url) {
// xhr.withCredentials = true; // 允许发送凭据(如 Cookie)
// },
debug: true // 启用调试日志
} as any,
);
await hls.value?.loadSource(`${useNuxtApp().$apiBase}/video/${path}/${videoID}.m3u8`);
hls.value.attachMedia(videoElementRef.value);
hls.value?.on(Hls.Events.MANIFEST_PARSED, () => {
console.log('Manifest parsed, video can start playing.');
hls.value?.startLoad(); // 手动开始加载 TS 片段
});
hls.value?.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS.js error:', event, 'data:', data);
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
console.error('Fatal network error encountered, trying to recover...');
hls.value?.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.error('Fatal media error encountered, trying to recover...');
hls.value?.recoverMediaError();
break;
default:
console.error('Unrecoverable error');
hls.value?.destroy();
break;
}
}
});
console.log('This browser support HLS.');
}else if (videoElementRef.value.canPlayType('application/vnd.apple.mpegurl')) {
const { path, videoID } = await getPath(id);
videoElementRef.value.src = `${useNuxtApp().$apiBase}/video/${path}/${videoID}.m3u8`;
videoElementRef.value.addEventListener('loadedmetadata', () => {
videoElementRef.value.play();
});
console.log('This browser supports native HLS.');
} else {
console.error('HLS is not supported by this browser.');
}Additional player setup steps
No response
Checklist
- The issue observed is not already reported by searching on Github under https://github.com/video-dev/hls.js/issues
- The issue occurs in the stable client (latest release) on https://hlsjs.video-dev.org/demo and not just on my page
- The issue occurs in the latest client (main branch) on https://hlsjs-dev.video-dev.org/demo and not just on my page
- The stream has correct Access-Control-Allow-Origin headers (CORS)
- There are no network errors such as 404s in the browser console when trying to play the stream
Steps to reproduce
-
hls.value = new Hls(
{
loader: $customLoader as any,
autoStartLoad: false,
lowLatencyMode: false,
maxBufferLength: 30,
maxMaxBufferLength: 60,
backBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.5,
startFragPrefetch: true,// xhrSetup: function (xhr, url) { // xhr.withCredentials = true; // }, debug: true } as any, );
Expected behaviour
1.Each context has its own type
2.I can't find a way to load a custom key
3.The fragment stream is loaded with the key, and the fragment is loaded when the key is not returned
4.My m3u8 file :
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="keys/encryption.key",IV=0xe2640f2cbb490f4fa29103ba2cd4afff
#EXTINF:7.200000,
1080P/000.ts
#EXTINF:4.800000,
1080P/001.ts
#EXTINF:4.800000,
1080P/002.ts
#EXTINF:4.800000,
1080P/003.ts
#EXTINF:3.600000,
1080P/004.ts....
What actually happened?
In order to be more specific, I give part of the code, because the content.type has not been correct, so I forced to mediaSegment, part of the comment is Chinese, please ignore:
```
this.xhr.onload = async () => {
if (this.xhr.status >= 200 && this.xhr.status < 300) {
stats.tload = performance.now();
stats.loaded = this.xhr.response.byteLength || this.xhr.responseText.length;
stats.loading.end = performance.now();
if (context.type === 'manifest') {
stats.parsing.start = performance.now();
const manifestData = this.xhr.responseText;
stats.parsing.end = performance.now();
console.log('mainfestData', manifestData);
// console.log('context', context);
console.log('callbacks', callbacks);
console.log('stats', stats);
const response = {
url: this.xhr.responseURL,
data: manifestData
};
callbacks.onSuccess(response, stats, context, null);
} else if (context.type === 'mediaSegment') {
const pathSegments = url.split('/').filter(Boolean); // 去除空字符串部分
const secondLastSegment = pathSegments[pathSegments.length - 2];
if (secondLastSegment === 'keys') {
console.log('the latest path:', secondLastSegment);
console.log('this.xhr.response', this.xhr.response);
const response = {
url: this.xhr.responseURL,
data: this.xhr.response
};
CustomLoader.soulkey = this.xhr.response;
console.log(' CustomLoader.soulkey:', CustomLoader.soulkey); // 确认密钥加载成功
callbacks.onSuccess(response, stats, context, null);
} else
{
// 处理 TS 片段
const segmentData = new Uint8Array(this.xhr.response);
let finalData;
// console.log('this.config.soulkey2', this.config.soulkey);
if (!CustomLoader.soulkey || !CustomLoader.ivHex) {
console.warn('No key available, skipping decryption.');
finalData = segmentData;
} else {
const mm = performance.now();
finalData = await this.decrypt(segmentData, CustomLoader.soulkey, CustomLoader.ivHex);
const duration = performance.now() - mm;
// console.log('duration',duration);
}
// console.log('segmentData', finalData)
const response = {
url: this.xhr.responseURL,
data: finalData
};
callbacks.onSuccess(response, stats, context, null);
}
} else if (context.type === 'level') {
stats.parsing.start = performance.now();
const levelData = this.xhr.responseText;
stats.parsing.end = performance.now();
console.log('levelData', levelData);
// 检查 manifestData 中是否包含 #EXT-X-KEY
const keyUriMatch = levelData.match(/#EXT-X-KEY:METHOD=AES-128,URI="(.*?)"(?:,IV=(0x[0-9a-fA-F]+))?/);
const baseUrl = url.substring(0, url.lastIndexOf('/'));
if (keyUriMatch) {
let keyUri = keyUriMatch[1];
keyUri = `${baseUrl}/${keyUri}`;
const key = await this.loadKey(keyUri);
const ivHex = keyUriMatch[2] ? keyUriMatch[2].slice(2) : null;
CustomLoader.ivHex = ivHex;
CustomLoader.soulkey=key;
console.log(' CustomLoader.ivHex:', CustomLoader.ivHex); // 确认密钥加载成功
}
const response = {
url: this.xhr.responseURL,
data: levelData
};
// console.log('Before onSuccess:', JSON.stringify(stats));
callbacks.onSuccess(response, stats, context, null);
// console.log('After onSuccess:', JSON.stringify(stats));
}
} else {
callbacks.onError({ code: this.xhr.status, text: this.xhr.statusText }, context);
}
};
### Console output
```shell
url http://localhost:3001/api/video/2024/08/2024-08-22/0bffc702-ab68-4fe9-ab28-8d777c87bdfb/1080P/000.ts
vconsole.min.js:10 [log] > [stream-controller]: FRAG_LOADING->ERROR
vconsole.min.js:10 [log] > [audio-stream-controller]: STOPPED->ERROR
vconsole.min.js:10 [log] > stopLoad
Chrome media internals output
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Status