hls.js 1.5.10-0.canary.10320 → 1.5.10-0.canary.10326

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.
@@ -501,7 +501,7 @@ function enableLogs(debugConfig, context, id) {
501
501
  // Some browsers don't allow to use bind on console object anyway
502
502
  // fallback to default if needed
503
503
  try {
504
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.10-0.canary.10320"}`);
504
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.10-0.canary.10326"}`);
505
505
  } catch (e) {
506
506
  /* log fn threw an exception. All logger methods are no-ops. */
507
507
  return createLogger();
@@ -5262,7 +5262,7 @@ function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) {
5262
5262
  * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous
5263
5263
  * @returns a matching fragment or null
5264
5264
  */
5265
- function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0) {
5265
+ function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0, nextFragLookupTolerance = 0.005) {
5266
5266
  let fragNext = null;
5267
5267
  if (fragPrevious) {
5268
5268
  fragNext = fragments[fragPrevious.sn - fragments[0].sn + 1] || null;
@@ -5275,7 +5275,7 @@ function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUp
5275
5275
  fragNext = fragments[0];
5276
5276
  }
5277
5277
  // Prefer the next fragment if it's within tolerance
5278
- if (fragNext && (!fragPrevious || fragPrevious.level === fragNext.level) && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0) {
5278
+ if (fragNext && ((!fragPrevious || fragPrevious.level === fragNext.level) && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0 || fragmentWithinFastStartSwitch(fragNext, fragPrevious, Math.min(nextFragLookupTolerance, maxFragLookUpTolerance)))) {
5279
5279
  return fragNext;
5280
5280
  }
5281
5281
  // We might be seeking past the tolerance so find the best match
@@ -5286,6 +5286,18 @@ function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUp
5286
5286
  // If no match was found return the next fragment after fragPrevious, or null
5287
5287
  return fragNext;
5288
5288
  }
5289
+ function fragmentWithinFastStartSwitch(fragNext, fragPrevious, nextFragLookupTolerance) {
5290
+ if (fragPrevious && fragPrevious.start === 0 && fragPrevious.level < fragNext.level && (fragPrevious.endPTS || 0) > 0) {
5291
+ const firstDuration = fragPrevious.tagList.reduce((duration, tag) => {
5292
+ if (tag[0] === 'INF') {
5293
+ duration += parseFloat(tag[1]);
5294
+ }
5295
+ return duration;
5296
+ }, nextFragLookupTolerance);
5297
+ return fragNext.start <= firstDuration;
5298
+ }
5299
+ return false;
5300
+ }
5289
5301
 
5290
5302
  /**
5291
5303
  * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions.
@@ -9003,8 +9015,6 @@ class XhrLoader {
9003
9015
  this.config = null;
9004
9016
  this.context = null;
9005
9017
  this.xhrSetup = null;
9006
- // @ts-ignore
9007
- this.stats = null;
9008
9018
  }
9009
9019
  abortInternal() {
9010
9020
  const loader = this.loader;
@@ -9052,13 +9062,14 @@ class XhrLoader {
9052
9062
  const xhrSetup = this.xhrSetup;
9053
9063
  if (xhrSetup) {
9054
9064
  Promise.resolve().then(() => {
9055
- if (this.stats.aborted) return;
9065
+ if (this.loader !== xhr || this.stats.aborted) return;
9056
9066
  return xhrSetup(xhr, context.url);
9057
9067
  }).catch(error => {
9068
+ if (this.loader !== xhr || this.stats.aborted) return;
9058
9069
  xhr.open('GET', context.url, true);
9059
9070
  return xhrSetup(xhr, context.url);
9060
9071
  }).then(() => {
9061
- if (this.stats.aborted) return;
9072
+ if (this.loader !== xhr || this.stats.aborted) return;
9062
9073
  this.openAndSendXhr(xhr, context, config);
9063
9074
  }).catch(error => {
9064
9075
  // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
@@ -9178,8 +9189,8 @@ class XhrLoader {
9178
9189
  }
9179
9190
  }
9180
9191
  loadtimeout() {
9181
- var _this$config;
9182
- const retryConfig = (_this$config = this.config) == null ? void 0 : _this$config.loadPolicy.timeoutRetry;
9192
+ if (!this.config) return;
9193
+ const retryConfig = this.config.loadPolicy.timeoutRetry;
9183
9194
  const retryCount = this.stats.retry;
9184
9195
  if (shouldRetry(retryConfig, retryCount, true)) {
9185
9196
  this.retry(retryConfig);
@@ -12474,7 +12485,7 @@ class BaseStreamController extends TaskLoop {
12474
12485
  if (this.state === State.STOPPED || this.state === State.ERROR) {
12475
12486
  return;
12476
12487
  }
12477
- this.warn(reason);
12488
+ this.warn(`Frag error: ${(reason == null ? void 0 : reason.message) || reason}`);
12478
12489
  this.resetFragmentLoading(frag);
12479
12490
  });
12480
12491
  }
@@ -13171,7 +13182,9 @@ class BaseStreamController extends TaskLoop {
13171
13182
  const {
13172
13183
  fragmentHint
13173
13184
  } = levelDetails;
13174
- const tolerance = config.maxFragLookUpTolerance;
13185
+ const {
13186
+ maxFragLookUpTolerance
13187
+ } = config;
13175
13188
  const partList = levelDetails.partList;
13176
13189
  const loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
13177
13190
  if (loadingParts && fragmentHint && !this.bitrateTest) {
@@ -13181,7 +13194,7 @@ class BaseStreamController extends TaskLoop {
13181
13194
  }
13182
13195
  let frag;
13183
13196
  if (bufferEnd < end) {
13184
- const lookupTolerance = bufferEnd > end - tolerance ? 0 : tolerance;
13197
+ const lookupTolerance = bufferEnd > end - maxFragLookUpTolerance ? 0 : maxFragLookUpTolerance;
13185
13198
  // Remove the tolerance if it would put the bufferEnd past the actual end of stream
13186
13199
  // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE)
13187
13200
  frag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance);
@@ -15450,7 +15463,7 @@ class TSDemuxer {
15450
15463
  if (stt) {
15451
15464
  offset += data[offset] + 1;
15452
15465
  }
15453
- const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes);
15466
+ const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer);
15454
15467
 
15455
15468
  // only update track id if track PID found while parsing PMT
15456
15469
  // this is to avoid resetting the PID to -1 in case
@@ -15493,14 +15506,7 @@ class TSDemuxer {
15493
15506
  }
15494
15507
  }
15495
15508
  if (tsPacketErrors > 0) {
15496
- const error = new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`);
15497
- this.observer.emit(Events.ERROR, Events.ERROR, {
15498
- type: ErrorTypes.MEDIA_ERROR,
15499
- details: ErrorDetails.FRAG_PARSING_ERROR,
15500
- fatal: false,
15501
- error,
15502
- reason: error.message
15503
- });
15509
+ emitParsingError(this.observer, new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`));
15504
15510
  }
15505
15511
  videoTrack.pesData = videoData;
15506
15512
  audioTrack.pesData = audioData;
@@ -15658,16 +15664,7 @@ class TSDemuxer {
15658
15664
  } else {
15659
15665
  reason = 'No ADTS header found in AAC PES';
15660
15666
  }
15661
- const error = new Error(reason);
15662
- logger.warn(`parsing error: ${reason}`);
15663
- this.observer.emit(Events.ERROR, Events.ERROR, {
15664
- type: ErrorTypes.MEDIA_ERROR,
15665
- details: ErrorDetails.FRAG_PARSING_ERROR,
15666
- fatal: false,
15667
- levelRetry: recoverable,
15668
- error,
15669
- reason
15670
- });
15667
+ emitParsingError(this.observer, new Error(reason), recoverable);
15671
15668
  if (!recoverable) {
15672
15669
  return;
15673
15670
  }
@@ -15753,7 +15750,7 @@ function parsePAT(data, offset) {
15753
15750
  // skip the PSI header and parse the first PMT entry
15754
15751
  return (data[offset + 10] & 0x1f) << 8 | data[offset + 11];
15755
15752
  }
15756
- function parsePMT(data, offset, typeSupported, isSampleAes) {
15753
+ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
15757
15754
  const result = {
15758
15755
  audioPid: -1,
15759
15756
  videoPid: -1,
@@ -15861,11 +15858,13 @@ function parsePMT(data, offset, typeSupported, isSampleAes) {
15861
15858
  case 0xc2: // SAMPLE-AES EC3
15862
15859
  /* falls through */
15863
15860
  case 0x87:
15864
- throw new Error('Unsupported EC-3 in M2TS found');
15861
+ emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found'));
15862
+ return result;
15865
15863
  case 0x24:
15866
15864
  // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
15867
15865
  {
15868
- throw new Error('Unsupported HEVC in M2TS found');
15866
+ emitParsingError(observer, new Error('Unsupported HEVC in M2TS found'));
15867
+ return result;
15869
15868
  }
15870
15869
  }
15871
15870
  // move to the next table entry
@@ -15874,6 +15873,17 @@ function parsePMT(data, offset, typeSupported, isSampleAes) {
15874
15873
  }
15875
15874
  return result;
15876
15875
  }
15876
+ function emitParsingError(observer, error, levelRetry) {
15877
+ logger.warn(`parsing error: ${error.message}`);
15878
+ observer.emit(Events.ERROR, Events.ERROR, {
15879
+ type: ErrorTypes.MEDIA_ERROR,
15880
+ details: ErrorDetails.FRAG_PARSING_ERROR,
15881
+ fatal: false,
15882
+ levelRetry,
15883
+ error,
15884
+ reason: error.message
15885
+ });
15886
+ }
15877
15887
  function logEncryptedSamplesFoundInUnencryptedStream(type) {
15878
15888
  logger.log(`${type} with AES-128-CBC encryption found in unencrypted stream`);
15879
15889
  }
@@ -18611,10 +18621,6 @@ class TransmuxerInterface {
18611
18621
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
18612
18622
  this.observer.on(Events.ERROR, forwardMessage);
18613
18623
  const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
18614
-
18615
- // navigator.vendor is not always available in Web Worker
18616
- // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
18617
- const vendor = navigator.vendor;
18618
18624
  if (this.useWorker && typeof Worker !== 'undefined') {
18619
18625
  const canCreateWorker = config.workerPath || hasUMDWorker();
18620
18626
  if (canCreateWorker) {
@@ -18626,7 +18632,7 @@ class TransmuxerInterface {
18626
18632
  logger.log(`injecting Web Worker for "${id}"`);
18627
18633
  this.workerContext = injectWorker();
18628
18634
  }
18629
- this.onwmsg = ev => this.onWorkerMessage(ev);
18635
+ this.onwmsg = event => this.onWorkerMessage(event);
18630
18636
  const {
18631
18637
  worker
18632
18638
  } = this.workerContext;
@@ -18646,7 +18652,7 @@ class TransmuxerInterface {
18646
18652
  worker.postMessage({
18647
18653
  cmd: 'init',
18648
18654
  typeSupported: m2tsTypeSupported,
18649
- vendor: vendor,
18655
+ vendor: '',
18650
18656
  id: id,
18651
18657
  config: JSON.stringify(config)
18652
18658
  });
@@ -18654,12 +18660,12 @@ class TransmuxerInterface {
18654
18660
  logger.warn(`Error setting up "${id}" Web Worker, fallback to inline`, err);
18655
18661
  this.resetWorker();
18656
18662
  this.error = null;
18657
- this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, vendor, id);
18663
+ this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id);
18658
18664
  }
18659
18665
  return;
18660
18666
  }
18661
18667
  }
18662
- this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, vendor, id);
18668
+ this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id);
18663
18669
  }
18664
18670
  resetWorker() {
18665
18671
  if (this.workerContext) {
@@ -18811,9 +18817,16 @@ class TransmuxerInterface {
18811
18817
  });
18812
18818
  this.onFlush(chunkMeta);
18813
18819
  }
18814
- onWorkerMessage(ev) {
18815
- const data = ev.data;
18820
+ onWorkerMessage(event) {
18821
+ const data = event.data;
18822
+ if (!(data != null && data.event)) {
18823
+ logger.warn(`worker message received with no ${data ? 'event name' : 'data'}`);
18824
+ return;
18825
+ }
18816
18826
  const hls = this.hls;
18827
+ if (!this.hls) {
18828
+ return;
18829
+ }
18817
18830
  switch (data.event) {
18818
18831
  case 'init':
18819
18832
  {
@@ -20209,7 +20222,8 @@ class StreamController extends BaseStreamController {
20209
20222
  // In the case that AAC and HE-AAC audio codecs are signalled in manifest,
20210
20223
  // force HE-AAC, as it seems that most browsers prefers it.
20211
20224
  // don't force HE-AAC if mono stream, or in Firefox
20212
- if (audio.metadata.channelCount !== 1 && ua.indexOf('firefox') === -1) {
20225
+ const audioMetadata = audio.metadata;
20226
+ if (audioMetadata && 'channelCount' in audioMetadata && (audioMetadata.channelCount || 1) !== 1 && ua.indexOf('firefox') === -1) {
20213
20227
  audioCodec = 'mp4a.40.5';
20214
20228
  }
20215
20229
  }
@@ -20372,7 +20386,7 @@ class Hls {
20372
20386
  * Get the video-dev/hls.js package version.
20373
20387
  */
20374
20388
  static get version() {
20375
- return "1.5.10-0.canary.10320";
20389
+ return "1.5.10-0.canary.10326";
20376
20390
  }
20377
20391
 
20378
20392
  /**