hls.js 1.5.2-0.canary.9966 → 1.5.2-0.canary.9970

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.26.0"
132
132
  },
133
- "version": "1.5.2-0.canary.9966"
133
+ "version": "1.5.2-0.canary.9970"
134
134
  }
@@ -871,8 +871,9 @@ class AbrController extends Logger implements AbrComponentAPI {
871
871
  }
872
872
 
873
873
  public set nextAutoLevel(nextLevel: number) {
874
- const value = Math.max(this.hls.minAutoLevel, nextLevel);
875
- if (this._nextAutoLevel != value) {
874
+ const { maxAutoLevel, minAutoLevel } = this.hls;
875
+ const value = Math.min(Math.max(nextLevel, minAutoLevel), maxAutoLevel);
876
+ if (this._nextAutoLevel !== value) {
876
877
  this.nextAutoLevelKey = '';
877
878
  this._nextAutoLevel = value;
878
879
  }
@@ -278,12 +278,14 @@ class AudioStreamController
278
278
  const { hls, levels, media, trackId } = this;
279
279
  const config = hls.config;
280
280
 
281
- // 1. if video not attached AND
281
+ // 1. if buffering is suspended
282
+ // 2. if video not attached AND
282
283
  // start fragment already requested OR start frag prefetch not enabled
283
- // 2. if tracks or track not loaded and selected
284
+ // 3. if tracks or track not loaded and selected
284
285
  // then exit loop
285
286
  // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
286
287
  if (
288
+ !this.buffering ||
287
289
  (!media && (this.startFragRequested || !config.startFragPrefetch)) ||
288
290
  !levels?.[trackId]
289
291
  ) {
@@ -552,7 +554,7 @@ class AudioStreamController
552
554
 
553
555
  // compute start position if we are aligned with the main playlist
554
556
  if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) {
555
- this.setStartPosition(track.details, sliding);
557
+ this.setStartPosition(this.mainDetails || newDetails, sliding);
556
558
  }
557
559
  // only switch back to IDLE state if we were waiting for track to start downloading a new fragment
558
560
  if (
@@ -100,6 +100,7 @@ export default class BaseStreamController
100
100
  protected startFragRequested: boolean = false;
101
101
  protected decrypter: Decrypter;
102
102
  protected initPTS: RationalTimestamp[] = [];
103
+ protected buffering: boolean = true;
103
104
 
104
105
  constructor(
105
106
  hls: Hls,
@@ -161,6 +162,14 @@ export default class BaseStreamController
161
162
  this.state = State.STOPPED;
162
163
  }
163
164
 
165
+ public pauseBuffering() {
166
+ this.buffering = false;
167
+ }
168
+
169
+ public resumeBuffering() {
170
+ this.buffering = true;
171
+ }
172
+
164
173
  protected _streamEnded(
165
174
  bufferInfo: BufferInfo,
166
175
  levelDetails: LevelDetails,
@@ -295,6 +295,7 @@ export default class BufferController extends Logger implements ComponentAPI {
295
295
  this.resetBuffer(type);
296
296
  });
297
297
  this._initSourceBuffer();
298
+ this.hls.resumeBuffering();
298
299
  }
299
300
 
300
301
  private resetBuffer(type: SourceBufferName) {
@@ -134,7 +134,8 @@ export default class StreamController
134
134
  }
135
135
  // set new level to playlist loader : this will trigger start level load
136
136
  // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded
137
- this.level = hls.nextLoadLevel = startLevel;
137
+ hls.nextLoadLevel = startLevel;
138
+ this.level = hls.loadLevel;
138
139
  this.loadedmetadata = false;
139
140
  }
140
141
  // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime
@@ -231,7 +232,7 @@ export default class StreamController
231
232
  return;
232
233
  }
233
234
 
234
- if (!levels?.[level]) {
235
+ if (!this.buffering || !levels?.[level]) {
235
236
  return;
236
237
  }
237
238
 
@@ -318,7 +318,7 @@ export class SubtitleStreamController
318
318
  this.levelLastLoaded = track;
319
319
 
320
320
  if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) {
321
- this.setStartPosition(track.details, sliding);
321
+ this.setStartPosition(this.mainDetails || newDetails, sliding);
322
322
  }
323
323
 
324
324
  // trigger handler right now
package/src/hls.ts CHANGED
@@ -66,7 +66,6 @@ export default class Hls implements HlsEventEmitter {
66
66
 
67
67
  private coreComponents: ComponentAPI[];
68
68
  private networkControllers: NetworkComponentAPI[];
69
- private started: boolean = false;
70
69
  private _emitter: HlsEventEmitter = new EventEmitter();
71
70
  private _autoLevelCapping: number = -1;
72
71
  private _maxHdcpLevel: HdcpLevel = null;
@@ -441,7 +440,6 @@ export default class Hls implements HlsEventEmitter {
441
440
  */
442
441
  startLoad(startPosition: number = -1) {
443
442
  this.logger.log(`startLoad(${startPosition})`);
444
- this.started = true;
445
443
  this.networkControllers.forEach((controller) => {
446
444
  controller.startLoad(startPosition);
447
445
  });
@@ -452,33 +450,30 @@ export default class Hls implements HlsEventEmitter {
452
450
  */
453
451
  stopLoad() {
454
452
  this.logger.log('stopLoad');
455
- this.started = false;
456
453
  this.networkControllers.forEach((controller) => {
457
454
  controller.stopLoad();
458
455
  });
459
456
  }
460
457
 
461
458
  /**
462
- * Resumes stream controller segment loading if previously started.
459
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
463
460
  */
464
461
  resumeBuffering() {
465
- if (this.started) {
466
- this.networkControllers.forEach((controller) => {
467
- if ('fragmentLoader' in controller) {
468
- controller.startLoad(-1);
469
- }
470
- });
471
- }
462
+ this.networkControllers.forEach((controller) => {
463
+ if (controller.resumeBuffering) {
464
+ controller.resumeBuffering();
465
+ }
466
+ });
472
467
  }
473
468
 
474
469
  /**
475
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
470
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
476
471
  * This allows for media buffering to be paused without interupting playlist loading.
477
472
  */
478
473
  pauseBuffering() {
479
474
  this.networkControllers.forEach((controller) => {
480
- if ('fragmentLoader' in controller) {
481
- controller.stopLoad();
475
+ if (controller.pauseBuffering) {
476
+ controller.pauseBuffering();
482
477
  }
483
478
  });
484
479
  }
@@ -15,4 +15,6 @@ export interface AbrComponentAPI extends ComponentAPI {
15
15
  export interface NetworkComponentAPI extends ComponentAPI {
16
16
  startLoad(startPosition: number): void;
17
17
  stopLoad(): void;
18
+ pauseBuffering?(): void;
19
+ resumeBuffering?(): void;
18
20
  }
@@ -38,6 +38,13 @@ export function readUint32(buffer: Uint8Array, offset: number): number {
38
38
  return val < 0 ? 4294967296 + val : val;
39
39
  }
40
40
 
41
+ export function readUint64(buffer: Uint8Array, offset: number) {
42
+ let result = readUint32(buffer, offset);
43
+ result *= Math.pow(2, 32);
44
+ result += readUint32(buffer, offset + 4);
45
+ return result;
46
+ }
47
+
41
48
  export function readSint32(buffer: Uint8Array, offset: number): number {
42
49
  return (
43
50
  (buffer[offset] << 24) |
@@ -125,15 +132,15 @@ export function parseSegmentIndex(sidx: Uint8Array): SidxInfo | null {
125
132
  const timescale = readUint32(sidx, index);
126
133
  index += 4;
127
134
 
128
- // TODO: parse earliestPresentationTime and firstOffset
129
- // usually zero in our case
130
- const earliestPresentationTime = 0;
131
- const firstOffset = 0;
135
+ let earliestPresentationTime = 0;
136
+ let firstOffset = 0;
132
137
 
133
138
  if (version === 0) {
134
- index += 8;
139
+ earliestPresentationTime = readUint32(sidx, (index += 4));
140
+ firstOffset = readUint32(sidx, (index += 4));
135
141
  } else {
136
- index += 16;
142
+ earliestPresentationTime = readUint64(sidx, (index += 8));
143
+ firstOffset = readUint64(sidx, (index += 8));
137
144
  }
138
145
 
139
146
  // skip reserved
@@ -677,19 +684,31 @@ export function getDuration(data: Uint8Array, initData: InitData) {
677
684
  }
678
685
  if (videoDuration === 0 && audioDuration === 0) {
679
686
  // If duration samples are not available in the traf use sidx subsegment_duration
687
+ let sidxMinStart = Infinity;
688
+ let sidxMaxEnd = 0;
680
689
  let sidxDuration = 0;
681
690
  const sidxs = findBox(data, ['sidx']);
682
691
  for (let i = 0; i < sidxs.length; i++) {
683
692
  const sidx = parseSegmentIndex(sidxs[i]);
684
693
  if (sidx?.references) {
685
- sidxDuration += sidx.references.reduce(
694
+ sidxMinStart = Math.min(
695
+ sidxMinStart,
696
+ sidx.earliestPresentationTime / sidx.timescale,
697
+ );
698
+ const subSegmentDuration = sidx.references.reduce(
686
699
  (dur, ref) => dur + ref.info.duration || 0,
687
700
  0,
688
701
  );
702
+ sidxMaxEnd = Math.max(
703
+ sidxMaxEnd,
704
+ subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale,
705
+ );
706
+ sidxDuration = sidxMaxEnd - sidxMinStart;
689
707
  }
690
708
  }
691
-
692
- return sidxDuration;
709
+ if (sidxDuration && Number.isFinite(sidxDuration)) {
710
+ return sidxDuration;
711
+ }
693
712
  }
694
713
  if (videoDuration) {
695
714
  return videoDuration;