hls.js 1.6.0-beta.1.0.canary.10750 → 1.6.0-beta.1.0.canary.10752
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 +5 -1
- package/dist/hls.d.ts +5 -1
- package/dist/hls.js +74 -24
- package/dist/hls.js.d.ts +5 -1
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +74 -24
- 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 +69 -23
- 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 +69 -23
- 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/base-stream-controller.ts +29 -21
- package/src/controller/stream-controller.ts +5 -4
- package/src/demux/transmuxer-interface.ts +4 -2
- package/src/demux/transmuxer.ts +1 -1
- package/src/loader/fragment-loader.ts +1 -1
- package/src/loader/fragment.ts +33 -0
- package/src/loader/m3u8-parser.ts +10 -0
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,
|
@@ -477,18 +477,19 @@ export default class BaseStreamController
|
|
477
477
|
}
|
478
478
|
|
479
479
|
private _loadFragForPlayback(
|
480
|
-
|
480
|
+
fragment: Fragment,
|
481
481
|
level: Level,
|
482
482
|
targetBufferTime: number,
|
483
483
|
) {
|
484
484
|
const progressCallback: FragmentLoadProgressCallback = (
|
485
485
|
data: FragLoadedData,
|
486
486
|
) => {
|
487
|
+
const frag = data.frag;
|
487
488
|
if (this.fragContextChanged(frag)) {
|
488
489
|
this.warn(
|
489
|
-
|
490
|
-
data.part ? '
|
491
|
-
} of
|
490
|
+
`${frag.type} sn: ${frag.sn}${
|
491
|
+
data.part ? ' part: ' + data.part.index : ''
|
492
|
+
} of ${this.fragInfo(frag, false, data.part)}) was dropped during download.`,
|
492
493
|
);
|
493
494
|
this.fragmentTracker.removeFragment(frag);
|
494
495
|
return;
|
@@ -497,13 +498,14 @@ export default class BaseStreamController
|
|
497
498
|
this._handleFragmentLoadProgress(data);
|
498
499
|
};
|
499
500
|
|
500
|
-
this._doFragLoad(
|
501
|
+
this._doFragLoad(fragment, level, targetBufferTime, progressCallback)
|
501
502
|
.then((data) => {
|
502
503
|
if (!data) {
|
503
504
|
// if we're here we probably needed to backtrack or are waiting for more parts
|
504
505
|
return;
|
505
506
|
}
|
506
507
|
const state = this.state;
|
508
|
+
const frag = data.frag;
|
507
509
|
if (this.fragContextChanged(frag)) {
|
508
510
|
if (
|
509
511
|
state === State.FRAG_LOADING ||
|
@@ -530,7 +532,7 @@ export default class BaseStreamController
|
|
530
532
|
return;
|
531
533
|
}
|
532
534
|
this.warn(`Frag error: ${reason?.message || reason}`);
|
533
|
-
this.resetFragmentLoading(
|
535
|
+
this.resetFragmentLoading(fragment);
|
534
536
|
});
|
535
537
|
}
|
536
538
|
|
@@ -608,10 +610,11 @@ export default class BaseStreamController
|
|
608
610
|
this.hls.trigger(Events.BUFFER_FLUSHING, flushScope);
|
609
611
|
}
|
610
612
|
|
611
|
-
protected _loadInitSegment(
|
612
|
-
this._doFragLoad(
|
613
|
+
protected _loadInitSegment(fragment: Fragment, level: Level) {
|
614
|
+
this._doFragLoad(fragment, level)
|
613
615
|
.then((data) => {
|
614
|
-
|
616
|
+
const frag = data?.frag;
|
617
|
+
if (!frag || this.fragContextChanged(frag) || !this.levels) {
|
615
618
|
throw new Error('init load aborted');
|
616
619
|
}
|
617
620
|
|
@@ -619,7 +622,7 @@ export default class BaseStreamController
|
|
619
622
|
})
|
620
623
|
.then((data: FragLoadedData) => {
|
621
624
|
const { hls } = this;
|
622
|
-
const { payload } = data;
|
625
|
+
const { frag, payload } = data;
|
623
626
|
const decryptData = frag.decryptdata;
|
624
627
|
|
625
628
|
// check to see if the payload needs to be decrypted
|
@@ -671,7 +674,7 @@ export default class BaseStreamController
|
|
671
674
|
return;
|
672
675
|
}
|
673
676
|
this.warn(reason);
|
674
|
-
this.resetFragmentLoading(
|
677
|
+
this.resetFragmentLoading(fragment);
|
675
678
|
});
|
676
679
|
}
|
677
680
|
|
@@ -705,7 +708,7 @@ export default class BaseStreamController
|
|
705
708
|
this.log(
|
706
709
|
`Buffered ${frag.type} sn: ${frag.sn}${
|
707
710
|
part ? ' part: ' + part.index : ''
|
708
|
-
} of ${this.fragInfo(frag)} > buffer:${
|
711
|
+
} of ${this.fragInfo(frag, false, part)} > buffer:${
|
709
712
|
media
|
710
713
|
? TimeRanges.toString(BufferHelper.getBuffered(media))
|
711
714
|
: '(detached)'
|
@@ -823,12 +826,11 @@ export default class BaseStreamController
|
|
823
826
|
const partIndex = this.getNextPart(partList, frag, targetBufferTime);
|
824
827
|
if (partIndex > -1) {
|
825
828
|
const part = partList[partIndex];
|
829
|
+
frag = this.fragCurrent = part.fragment;
|
826
830
|
this.log(
|
827
|
-
`Loading
|
831
|
+
`Loading ${frag.type} sn: ${frag.sn} part: ${part.index} (${partIndex}/${partList.length - 1}) of ${this.fragInfo(frag, false, part)}) cc: ${
|
828
832
|
frag.cc
|
829
|
-
}
|
830
|
-
details.endSN
|
831
|
-
}] parts [0-${partIndex}-${partList.length - 1}] ${this.playlistLabel()}: ${frag.level}, target: ${parseFloat(
|
833
|
+
} [${details.startSN}-${details.endSN}], target: ${parseFloat(
|
832
834
|
targetBufferTime.toFixed(3),
|
833
835
|
)}`,
|
834
836
|
);
|
@@ -1947,7 +1949,7 @@ export default class BaseStreamController
|
|
1947
1949
|
this.log(
|
1948
1950
|
`Parsed ${frag.type} sn: ${frag.sn}${
|
1949
1951
|
part ? ' part: ' + part.index : ''
|
1950
|
-
} of ${this.fragInfo(frag)})`,
|
1952
|
+
} of ${this.fragInfo(frag, false, part)})`,
|
1951
1953
|
);
|
1952
1954
|
this.hls.trigger(Events.FRAG_PARSED, { frag, part });
|
1953
1955
|
}
|
@@ -1956,10 +1958,16 @@ export default class BaseStreamController
|
|
1956
1958
|
return this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track';
|
1957
1959
|
}
|
1958
1960
|
|
1959
|
-
private fragInfo(
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1961
|
+
private fragInfo(
|
1962
|
+
frag: Fragment,
|
1963
|
+
pts: boolean = true,
|
1964
|
+
part?: Part | null,
|
1965
|
+
): string {
|
1966
|
+
return `${this.playlistLabel()} ${frag.level} (${part ? 'part' : 'frag'}:[${((pts && !part ? frag.startPTS : (part || frag).start) ?? NaN).toFixed(3)}-${(
|
1967
|
+
(pts && !part ? frag.endPTS : (part || frag).end) ?? NaN
|
1968
|
+
).toFixed(
|
1969
|
+
3,
|
1970
|
+
)}]${part && frag.type === 'main' ? 'INDEPENDENT=' + (part.independent ? 'YES' : 'NO') : ''}`;
|
1963
1971
|
}
|
1964
1972
|
|
1965
1973
|
protected resetTransmuxer() {
|
@@ -1151,11 +1151,12 @@ export default class StreamController
|
|
1151
1151
|
return audioCodec;
|
1152
1152
|
}
|
1153
1153
|
|
1154
|
-
private _loadBitrateTestFrag(
|
1155
|
-
|
1156
|
-
this._doFragLoad(
|
1154
|
+
private _loadBitrateTestFrag(fragment: Fragment, level: Level) {
|
1155
|
+
fragment.bitrateTest = true;
|
1156
|
+
this._doFragLoad(fragment, level).then((data) => {
|
1157
1157
|
const { hls } = this;
|
1158
|
-
|
1158
|
+
const frag = data?.frag;
|
1159
|
+
if (!frag || this.fragContextChanged(frag)) {
|
1159
1160
|
return;
|
1160
1161
|
}
|
1161
1162
|
level.fragmentError = 0;
|
@@ -12,13 +12,13 @@ import Transmuxer, {
|
|
12
12
|
} from '../demux/transmuxer';
|
13
13
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
14
14
|
import { Events } from '../events';
|
15
|
+
import { PlaylistLevelType } from '../types/loader';
|
15
16
|
import { getM2TSSupportedAudioTypes } from '../utils/codecs';
|
16
17
|
import type { WorkerContext } from './inject-worker';
|
17
18
|
import type { HlsEventEmitter, HlsListeners } from '../events';
|
18
19
|
import type Hls from '../hls';
|
19
20
|
import type { MediaFragment, Part } from '../loader/fragment';
|
20
21
|
import type { ErrorData, FragDecryptedData } from '../types/events';
|
21
|
-
import type { PlaylistLevelType } from '../types/loader';
|
22
22
|
import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
|
23
23
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
24
24
|
|
@@ -234,7 +234,9 @@ export default class TransmuxerInterface {
|
|
234
234
|
);
|
235
235
|
if (!contiguous || discontinuity || initSegmentChange) {
|
236
236
|
this.hls.logger
|
237
|
-
.log(`[transmuxer-interface
|
237
|
+
.log(`[transmuxer-interface]: Starting new transmux session for ${frag.type} sn: ${chunkMeta.sn}${
|
238
|
+
chunkMeta.part > -1 ? ' part: ' + chunkMeta.part : ''
|
239
|
+
} ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${chunkMeta.level} id: ${chunkMeta.id}
|
238
240
|
discontinuity: ${discontinuity}
|
239
241
|
trackSwitch: ${trackSwitch}
|
240
242
|
contiguous: ${contiguous}
|
package/src/demux/transmuxer.ts
CHANGED
@@ -290,7 +290,7 @@ export default class Transmuxer {
|
|
290
290
|
const { accurateTimeOffset, timeOffset } = this.currentTransmuxState;
|
291
291
|
this.logger.log(
|
292
292
|
`[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${
|
293
|
-
chunkMeta.part > -1 ? '
|
293
|
+
chunkMeta.part > -1 ? ' part: ' + chunkMeta.part : ''
|
294
294
|
} of ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'} ${chunkMeta.level}`,
|
295
295
|
);
|
296
296
|
const remuxResult = this.remuxer!.remux(
|
@@ -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);
|