hls.js 1.6.0-beta.1.0.canary.10751 → 1.6.0-beta.1.0.canary.10754
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -11
- package/dist/hls.d.mts +4 -0
- package/dist/hls.d.ts +4 -0
- package/dist/hls.js +96 -44
- package/dist/hls.js.d.ts +4 -0
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +90 -42
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +86 -42
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +92 -44
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +1 -1
- package/src/controller/abr-controller.ts +6 -5
- package/src/controller/audio-track-controller.ts +8 -2
- package/src/loader/fragment-loader.ts +1 -1
- package/src/loader/fragment.ts +33 -0
- package/src/loader/m3u8-parser.ts +10 -0
- package/src/utils/xhr-loader.ts +52 -49
package/package.json
CHANGED
@@ -285,9 +285,10 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
285
285
|
const bwEstimate: number = this.getBwEstimate();
|
286
286
|
const levels = hls.levels;
|
287
287
|
const level = levels[frag.level];
|
288
|
-
const expectedLen =
|
289
|
-
stats.
|
290
|
-
Math.
|
288
|
+
const expectedLen = Math.max(
|
289
|
+
stats.loaded,
|
290
|
+
Math.round((duration * (frag.bitrate || level.averageBitrate)) / 8),
|
291
|
+
);
|
291
292
|
let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading;
|
292
293
|
if (timeStreaming < 1 && loadedFirstByte) {
|
293
294
|
timeStreaming = Math.min(timeLoading, (stats.loaded * 8) / bwEstimate);
|
@@ -880,8 +881,8 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
880
881
|
currentFragDuration &&
|
881
882
|
bufferStarvationDelay >= currentFragDuration * 2 &&
|
882
883
|
maxStarvationDelay === 0
|
883
|
-
?
|
884
|
-
:
|
884
|
+
? levelInfo.averageBitrate
|
885
|
+
: levelInfo.maxBitrate;
|
885
886
|
const fetchDuration: number = this.getTimeToLoadFrag(
|
886
887
|
ttfbEstimateSec,
|
887
888
|
adjustedbw,
|
@@ -404,11 +404,17 @@ class AudioTrackController extends BasePlaylistController {
|
|
404
404
|
|
405
405
|
protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void {
|
406
406
|
const audioTrack = this.currentTrack;
|
407
|
-
if (
|
407
|
+
if (!audioTrack) {
|
408
|
+
return;
|
409
|
+
}
|
410
|
+
let url = audioTrack.url;
|
411
|
+
if (
|
412
|
+
this.shouldLoadPlaylist(audioTrack) &&
|
413
|
+
url !== this.hls.levels[this.hls.loadLevel]?.uri
|
414
|
+
) {
|
408
415
|
super.loadPlaylist();
|
409
416
|
const id = audioTrack.id;
|
410
417
|
const groupId = audioTrack.groupId as string;
|
411
|
-
let url = audioTrack.url;
|
412
418
|
if (hlsUrlParameters) {
|
413
419
|
try {
|
414
420
|
url = hlsUrlParameters.addDirectives(url);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
2
2
|
import { getLoaderConfigWithoutReties } from '../utils/error-helper';
|
3
|
-
import type { HlsConfig } from '../config';
|
4
3
|
import type { BaseSegment, Fragment, Part } from './fragment';
|
4
|
+
import type { HlsConfig } from '../config';
|
5
5
|
import type {
|
6
6
|
ErrorData,
|
7
7
|
FragLoadedData,
|
package/src/loader/fragment.ts
CHANGED
@@ -159,6 +159,8 @@ export class Fragment extends BaseSegment {
|
|
159
159
|
private _decryptdata: LevelKey | null = null;
|
160
160
|
private _programDateTime: number | null = null;
|
161
161
|
private _ref: MediaFragmentRef | null = null;
|
162
|
+
// Approximate bit rate of the fragment expressed in bits per second (bps) as indicated by the last EXT-X-BITRATE (kbps) tag
|
163
|
+
private _bitrate?: number;
|
162
164
|
|
163
165
|
public rawProgramDateTime: string | null = null;
|
164
166
|
public tagList: Array<string[]> = [];
|
@@ -219,6 +221,37 @@ export class Fragment extends BaseSegment {
|
|
219
221
|
this.type = type;
|
220
222
|
}
|
221
223
|
|
224
|
+
get byteLength(): number | null {
|
225
|
+
if (this.hasStats) {
|
226
|
+
const total = this.stats.total;
|
227
|
+
if (total) {
|
228
|
+
return total;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
if (this.byteRange) {
|
232
|
+
const start = this.byteRange[0];
|
233
|
+
const end = this.byteRange[1];
|
234
|
+
if (Number.isFinite(start) && Number.isFinite(end)) {
|
235
|
+
return (end as number) - (start as number);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
return null;
|
239
|
+
}
|
240
|
+
|
241
|
+
get bitrate(): number | null {
|
242
|
+
if (this.byteLength) {
|
243
|
+
return (this.byteLength * 8) / this.duration;
|
244
|
+
}
|
245
|
+
if (this._bitrate) {
|
246
|
+
return this._bitrate;
|
247
|
+
}
|
248
|
+
return null;
|
249
|
+
}
|
250
|
+
|
251
|
+
set bitrate(value: number) {
|
252
|
+
this._bitrate = value;
|
253
|
+
}
|
254
|
+
|
222
255
|
get decryptdata(): LevelKey | null {
|
223
256
|
const { levelkeys } = this;
|
224
257
|
if (!levelkeys && !this._decryptdata) {
|
@@ -314,6 +314,7 @@ export default class M3U8Parser {
|
|
314
314
|
let currentPart = 0;
|
315
315
|
let totalduration = 0;
|
316
316
|
let discontinuityCounter = 0;
|
317
|
+
let currentBitrate = 0;
|
317
318
|
let prevFrag: Fragment | null = null;
|
318
319
|
let frag: Fragment = new Fragment(type, base);
|
319
320
|
let result: RegExpExecArray | RegExpMatchArray | null;
|
@@ -338,6 +339,9 @@ export default class M3U8Parser {
|
|
338
339
|
frag.start = totalduration;
|
339
340
|
frag.sn = currentSN;
|
340
341
|
frag.cc = discontinuityCounter;
|
342
|
+
if (currentBitrate) {
|
343
|
+
frag.bitrate = currentBitrate;
|
344
|
+
}
|
341
345
|
frag.level = id;
|
342
346
|
if (currentInitSegment) {
|
343
347
|
frag.initSegment = currentInitSegment;
|
@@ -481,6 +485,12 @@ export default class M3U8Parser {
|
|
481
485
|
break;
|
482
486
|
case 'BITRATE':
|
483
487
|
frag.tagList.push([tag, value1]);
|
488
|
+
currentBitrate = parseInt(value1) * 1000;
|
489
|
+
if (Number.isFinite(currentBitrate)) {
|
490
|
+
frag.bitrate = currentBitrate;
|
491
|
+
} else {
|
492
|
+
currentBitrate = 0;
|
493
|
+
}
|
484
494
|
break;
|
485
495
|
case 'DATERANGE': {
|
486
496
|
const dateRangeAttr = new AttrList(value1, level);
|
package/src/utils/xhr-loader.ts
CHANGED
@@ -204,59 +204,62 @@ class XhrLoader implements Loader<LoaderContext> {
|
|
204
204
|
xhr.onprogress = null;
|
205
205
|
const status = xhr.status;
|
206
206
|
// http status between 200 to 299 are all successful
|
207
|
-
const
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
(
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
207
|
+
const useResponseText =
|
208
|
+
xhr.responseType === 'text' ? xhr.responseText : null;
|
209
|
+
if (status >= 200 && status < 300) {
|
210
|
+
const data = useResponseText ?? xhr.response;
|
211
|
+
if (data != null) {
|
212
|
+
stats.loading.end = Math.max(
|
213
|
+
self.performance.now(),
|
214
|
+
stats.loading.first,
|
215
|
+
);
|
216
|
+
const len =
|
217
|
+
xhr.responseType === 'arraybuffer'
|
218
|
+
? data.byteLength
|
219
|
+
: data.length;
|
220
|
+
stats.loaded = stats.total = len;
|
221
|
+
stats.bwEstimate =
|
222
|
+
(stats.total * 8000) / (stats.loading.end - stats.loading.first);
|
223
|
+
if (!this.callbacks) {
|
224
|
+
return;
|
225
|
+
}
|
226
|
+
const onProgress = this.callbacks.onProgress;
|
227
|
+
if (onProgress) {
|
228
|
+
onProgress(stats, context, data, xhr);
|
229
|
+
}
|
230
|
+
if (!this.callbacks) {
|
231
|
+
return;
|
232
|
+
}
|
233
|
+
const response: LoaderResponse = {
|
234
|
+
url: xhr.responseURL,
|
235
|
+
data: data,
|
236
|
+
code: status,
|
237
|
+
};
|
238
|
+
|
239
|
+
this.callbacks.onSuccess(response, stats, context, xhr);
|
231
240
|
return;
|
232
241
|
}
|
233
|
-
|
234
|
-
url: xhr.responseURL,
|
235
|
-
data: data,
|
236
|
-
code: status,
|
237
|
-
};
|
242
|
+
}
|
238
243
|
|
239
|
-
|
244
|
+
// Handle bad status or nullish response
|
245
|
+
const retryConfig = config.loadPolicy.errorRetry;
|
246
|
+
const retryCount = stats.retry;
|
247
|
+
// if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
|
248
|
+
const response: LoaderResponse = {
|
249
|
+
url: context.url,
|
250
|
+
data: undefined,
|
251
|
+
code: status,
|
252
|
+
};
|
253
|
+
if (shouldRetry(retryConfig, retryCount, false, response)) {
|
254
|
+
this.retry(retryConfig);
|
240
255
|
} else {
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
};
|
249
|
-
if (shouldRetry(retryConfig, retryCount, false, response)) {
|
250
|
-
this.retry(retryConfig);
|
251
|
-
} else {
|
252
|
-
logger.error(`${status} while loading ${context.url}`);
|
253
|
-
this.callbacks!.onError(
|
254
|
-
{ code: status, text: xhr.statusText },
|
255
|
-
context,
|
256
|
-
xhr,
|
257
|
-
stats,
|
258
|
-
);
|
259
|
-
}
|
256
|
+
logger.error(`${status} while loading ${context.url}`);
|
257
|
+
this.callbacks!.onError(
|
258
|
+
{ code: status, text: xhr.statusText },
|
259
|
+
context,
|
260
|
+
xhr,
|
261
|
+
stats,
|
262
|
+
);
|
260
263
|
}
|
261
264
|
}
|
262
265
|
}
|