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/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  "url-toolkit": "2.2.5",
131
131
  "wrangler": "3.85.0"
132
132
  },
133
- "version": "1.6.0-beta.1.0.canary.10751"
133
+ "version": "1.6.0-beta.1.0.canary.10754"
134
134
  }
@@ -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.total ||
290
- Math.max(stats.loaded, Math.round((duration * level.averageBitrate) / 8));
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
- ? levels[i].averageBitrate
884
- : levels[i].maxBitrate;
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 (this.shouldLoadPlaylist(audioTrack) && audioTrack) {
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,
@@ -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);
@@ -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 useResponse = xhr.responseType !== 'text';
208
- if (
209
- status >= 200 &&
210
- status < 300 &&
211
- ((useResponse && xhr.response) || xhr.responseText !== null)
212
- ) {
213
- stats.loading.end = Math.max(
214
- self.performance.now(),
215
- stats.loading.first,
216
- );
217
- const data = useResponse ? xhr.response : xhr.responseText;
218
- const len =
219
- xhr.responseType === 'arraybuffer' ? data.byteLength : 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) {
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
- const response: LoaderResponse = {
234
- url: xhr.responseURL,
235
- data: data,
236
- code: status,
237
- };
242
+ }
238
243
 
239
- this.callbacks.onSuccess(response, stats, context, xhr);
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
- const retryConfig = config.loadPolicy.errorRetry;
242
- const retryCount = stats.retry;
243
- // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
244
- const response: LoaderResponse = {
245
- url: context.url,
246
- data: undefined,
247
- code: status,
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
  }