hls.js 1.5.17 → 1.5.18
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/dist/hls.js +94 -67
- package/dist/hls.js.d.ts +7 -2
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +90 -64
- 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 +90 -64
- 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 +94 -67
- 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/audio-stream-controller.ts +4 -2
- package/src/controller/base-stream-controller.ts +9 -0
- package/src/controller/buffer-controller.ts +1 -0
- package/src/controller/stream-controller.ts +4 -1
- package/src/hls.ts +24 -17
- package/src/remux/mp4-remuxer.ts +11 -7
- package/src/types/component-api.ts +2 -0
- package/src/utils/xhr-loader.ts +52 -49
package/package.json
CHANGED
@@ -281,12 +281,14 @@ class AudioStreamController
|
|
281
281
|
const { hls, levels, media, trackId } = this;
|
282
282
|
const config = hls.config;
|
283
283
|
|
284
|
-
// 1. if
|
284
|
+
// 1. if buffering is suspended
|
285
|
+
// 2. if video not attached AND
|
285
286
|
// start fragment already requested OR start frag prefetch not enabled
|
286
|
-
//
|
287
|
+
// 3. if tracks or track not loaded and selected
|
287
288
|
// then exit loop
|
288
289
|
// => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
|
289
290
|
if (
|
291
|
+
!this.buffering ||
|
290
292
|
(!media && (this.startFragRequested || !config.startFragPrefetch)) ||
|
291
293
|
!levels?.[trackId]
|
292
294
|
) {
|
@@ -97,6 +97,7 @@ export default class BaseStreamController
|
|
97
97
|
protected startFragRequested: boolean = false;
|
98
98
|
protected decrypter: Decrypter;
|
99
99
|
protected initPTS: RationalTimestamp[] = [];
|
100
|
+
protected buffering: boolean = true;
|
100
101
|
protected onvseeking: EventListener | null = null;
|
101
102
|
protected onvended: EventListener | null = null;
|
102
103
|
|
@@ -150,6 +151,14 @@ export default class BaseStreamController
|
|
150
151
|
this.state = State.STOPPED;
|
151
152
|
}
|
152
153
|
|
154
|
+
public pauseBuffering() {
|
155
|
+
this.buffering = false;
|
156
|
+
}
|
157
|
+
|
158
|
+
public resumeBuffering() {
|
159
|
+
this.buffering = true;
|
160
|
+
}
|
161
|
+
|
153
162
|
protected _streamEnded(
|
154
163
|
bufferInfo: BufferInfo,
|
155
164
|
levelDetails: LevelDetails,
|
@@ -237,7 +237,7 @@ export default class StreamController
|
|
237
237
|
return;
|
238
238
|
}
|
239
239
|
|
240
|
-
const level = hls.nextLoadLevel;
|
240
|
+
const level = this.buffering ? hls.nextLoadLevel : hls.loadLevel;
|
241
241
|
if (!levels?.[level]) {
|
242
242
|
return;
|
243
243
|
}
|
@@ -262,6 +262,9 @@ export default class StreamController
|
|
262
262
|
this.state = State.ENDED;
|
263
263
|
return;
|
264
264
|
}
|
265
|
+
if (!this.buffering) {
|
266
|
+
return;
|
267
|
+
}
|
265
268
|
|
266
269
|
// set next load level : this will trigger a playlist load if needed
|
267
270
|
if (hls.loadLevel !== level && hls.manualLevel === -1) {
|
package/src/hls.ts
CHANGED
@@ -430,9 +430,13 @@ export default class Hls implements HlsEventEmitter {
|
|
430
430
|
startLoad(startPosition: number = -1) {
|
431
431
|
logger.log(`startLoad(${startPosition})`);
|
432
432
|
this.started = true;
|
433
|
-
this.
|
434
|
-
|
435
|
-
|
433
|
+
this.resumeBuffering();
|
434
|
+
for (let i = 0; i < this.networkControllers.length; i++) {
|
435
|
+
this.networkControllers[i].startLoad(startPosition);
|
436
|
+
if (!this.started || !this.networkControllers) {
|
437
|
+
break;
|
438
|
+
}
|
439
|
+
}
|
436
440
|
}
|
437
441
|
|
438
442
|
/**
|
@@ -441,32 +445,35 @@ export default class Hls implements HlsEventEmitter {
|
|
441
445
|
stopLoad() {
|
442
446
|
logger.log('stopLoad');
|
443
447
|
this.started = false;
|
444
|
-
this.networkControllers.
|
445
|
-
|
446
|
-
|
448
|
+
for (let i = 0; i < this.networkControllers.length; i++) {
|
449
|
+
this.networkControllers[i].stopLoad();
|
450
|
+
if (this.started || !this.networkControllers) {
|
451
|
+
break;
|
452
|
+
}
|
453
|
+
}
|
447
454
|
}
|
448
455
|
|
449
456
|
/**
|
450
|
-
* Resumes stream controller segment loading
|
457
|
+
* Resumes stream controller segment loading after `pauseBuffering` has been called.
|
451
458
|
*/
|
452
459
|
resumeBuffering() {
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
}
|
460
|
+
logger.log(`resume buffering`);
|
461
|
+
this.networkControllers.forEach((controller) => {
|
462
|
+
if (controller.resumeBuffering) {
|
463
|
+
controller.resumeBuffering();
|
464
|
+
}
|
465
|
+
});
|
460
466
|
}
|
461
467
|
|
462
468
|
/**
|
463
|
-
*
|
469
|
+
* Prevents stream controller from loading new segments until `resumeBuffering` is called.
|
464
470
|
* This allows for media buffering to be paused without interupting playlist loading.
|
465
471
|
*/
|
466
472
|
pauseBuffering() {
|
473
|
+
logger.log(`pause buffering`);
|
467
474
|
this.networkControllers.forEach((controller) => {
|
468
|
-
if (
|
469
|
-
controller.
|
475
|
+
if (controller.pauseBuffering) {
|
476
|
+
controller.pauseBuffering();
|
470
477
|
}
|
471
478
|
});
|
472
479
|
}
|
package/src/remux/mp4-remuxer.ts
CHANGED
@@ -100,20 +100,24 @@ export default class MP4Remuxer implements Remuxer {
|
|
100
100
|
this.videoTrackConfig = undefined;
|
101
101
|
}
|
102
102
|
|
103
|
-
getVideoStartPts(videoSamples) {
|
103
|
+
getVideoStartPts(videoSamples: VideoSample[]) {
|
104
|
+
// Get the minimum PTS value relative to the first sample's PTS, normalized for 33-bit wrapping
|
104
105
|
let rolloverDetected = false;
|
106
|
+
const firstPts = videoSamples[0].pts;
|
105
107
|
const startPTS = videoSamples.reduce((minPTS, sample) => {
|
106
|
-
|
108
|
+
let pts = sample.pts;
|
109
|
+
let delta = pts - minPTS;
|
107
110
|
if (delta < -4294967296) {
|
108
111
|
// 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation
|
109
112
|
rolloverDetected = true;
|
110
|
-
|
111
|
-
|
113
|
+
pts = normalizePts(pts, firstPts);
|
114
|
+
delta = pts - minPTS;
|
115
|
+
}
|
116
|
+
if (delta > 0) {
|
112
117
|
return minPTS;
|
113
|
-
} else {
|
114
|
-
return sample.pts;
|
115
118
|
}
|
116
|
-
|
119
|
+
return pts;
|
120
|
+
}, firstPts);
|
117
121
|
if (rolloverDetected) {
|
118
122
|
logger.debug('PTS rollover detected');
|
119
123
|
}
|
package/src/utils/xhr-loader.ts
CHANGED
@@ -203,59 +203,62 @@ class XhrLoader implements Loader<LoaderContext> {
|
|
203
203
|
xhr.onprogress = null;
|
204
204
|
const status = xhr.status;
|
205
205
|
// http status between 200 to 299 are all successful
|
206
|
-
const
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
(
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
206
|
+
const useResponseText =
|
207
|
+
xhr.responseType === 'text' ? xhr.responseText : null;
|
208
|
+
if (status >= 200 && status < 300) {
|
209
|
+
const data = useResponseText ?? xhr.response;
|
210
|
+
if (data != null) {
|
211
|
+
stats.loading.end = Math.max(
|
212
|
+
self.performance.now(),
|
213
|
+
stats.loading.first,
|
214
|
+
);
|
215
|
+
const len =
|
216
|
+
xhr.responseType === 'arraybuffer'
|
217
|
+
? data.byteLength
|
218
|
+
: data.length;
|
219
|
+
stats.loaded = stats.total = len;
|
220
|
+
stats.bwEstimate =
|
221
|
+
(stats.total * 8000) / (stats.loading.end - stats.loading.first);
|
222
|
+
if (!this.callbacks) {
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
const onProgress = this.callbacks.onProgress;
|
226
|
+
if (onProgress) {
|
227
|
+
onProgress(stats, context, data, xhr);
|
228
|
+
}
|
229
|
+
if (!this.callbacks) {
|
230
|
+
return;
|
231
|
+
}
|
232
|
+
const response: LoaderResponse = {
|
233
|
+
url: xhr.responseURL,
|
234
|
+
data: data,
|
235
|
+
code: status,
|
236
|
+
};
|
237
|
+
|
238
|
+
this.callbacks.onSuccess(response, stats, context, xhr);
|
230
239
|
return;
|
231
240
|
}
|
232
|
-
|
233
|
-
url: xhr.responseURL,
|
234
|
-
data: data,
|
235
|
-
code: status,
|
236
|
-
};
|
241
|
+
}
|
237
242
|
|
238
|
-
|
243
|
+
// Handle bad status or nullish response
|
244
|
+
const retryConfig = config.loadPolicy.errorRetry;
|
245
|
+
const retryCount = stats.retry;
|
246
|
+
// if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
|
247
|
+
const response: LoaderResponse = {
|
248
|
+
url: context.url,
|
249
|
+
data: undefined,
|
250
|
+
code: status,
|
251
|
+
};
|
252
|
+
if (shouldRetry(retryConfig, retryCount, false, response)) {
|
253
|
+
this.retry(retryConfig);
|
239
254
|
} else {
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
};
|
248
|
-
if (shouldRetry(retryConfig, retryCount, false, response)) {
|
249
|
-
this.retry(retryConfig);
|
250
|
-
} else {
|
251
|
-
logger.error(`${status} while loading ${context.url}`);
|
252
|
-
this.callbacks!.onError(
|
253
|
-
{ code: status, text: xhr.statusText },
|
254
|
-
context,
|
255
|
-
xhr,
|
256
|
-
stats,
|
257
|
-
);
|
258
|
-
}
|
255
|
+
logger.error(`${status} while loading ${context.url}`);
|
256
|
+
this.callbacks!.onError(
|
257
|
+
{ code: status, text: xhr.statusText },
|
258
|
+
context,
|
259
|
+
xhr,
|
260
|
+
stats,
|
261
|
+
);
|
259
262
|
}
|
260
263
|
}
|
261
264
|
}
|