hls.js 1.6.0-rc.2 → 1.6.0-rc.2.0.canary.11083
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.d.mts +1 -0
- package/dist/hls.d.ts +1 -0
- package/dist/hls.js +85 -42
- package/dist/hls.js.d.ts +1 -0
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +32 -25
- 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 +34 -27
- 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 +86 -43
- 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 +10 -5
- package/src/controller/base-stream-controller.ts +8 -1
- package/src/controller/interstitials-controller.ts +61 -17
- package/src/controller/interstitials-schedule.ts +1 -1
- package/src/exports-named.ts +2 -0
- package/src/utils/mp4-tools.ts +13 -15
package/package.json
CHANGED
@@ -46,6 +46,7 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
46
46
|
private fragCurrent: Fragment | null = null;
|
47
47
|
private partCurrent: Part | null = null;
|
48
48
|
private bitrateTestDelay: number = 0;
|
49
|
+
private rebufferNotice: number = -1;
|
49
50
|
|
50
51
|
public bwEstimator: EwmaBandWidthEstimator;
|
51
52
|
|
@@ -647,6 +648,7 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
647
648
|
bwUpFactor,
|
648
649
|
);
|
649
650
|
if (bestLevel >= 0) {
|
651
|
+
this.rebufferNotice = -1;
|
650
652
|
return bestLevel;
|
651
653
|
}
|
652
654
|
}
|
@@ -688,11 +690,14 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
688
690
|
bwFactor,
|
689
691
|
bwUpFactor,
|
690
692
|
);
|
691
|
-
this.
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
693
|
+
if (this.rebufferNotice !== bestLevel) {
|
694
|
+
this.rebufferNotice = bestLevel;
|
695
|
+
this.info(
|
696
|
+
`${
|
697
|
+
bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'
|
698
|
+
}, optimal quality level ${bestLevel}`,
|
699
|
+
);
|
700
|
+
}
|
696
701
|
if (bestLevel > -1) {
|
697
702
|
return bestLevel;
|
698
703
|
}
|
@@ -1310,7 +1310,14 @@ export default class BaseStreamController
|
|
1310
1310
|
: levelDetails.fragmentEnd;
|
1311
1311
|
frag = this.getFragmentAtPosition(pos, end, levelDetails);
|
1312
1312
|
}
|
1313
|
-
|
1313
|
+
let programFrag = this.filterReplacedPrimary(frag, levelDetails);
|
1314
|
+
if (!programFrag && frag) {
|
1315
|
+
const curSNIdx = frag.sn - levelDetails.startSN;
|
1316
|
+
programFrag = this.filterReplacedPrimary(
|
1317
|
+
fragments[curSNIdx + 1] || null,
|
1318
|
+
levelDetails,
|
1319
|
+
);
|
1320
|
+
}
|
1314
1321
|
return this.mapToInitFragWhenRequired(programFrag);
|
1315
1322
|
}
|
1316
1323
|
|
@@ -1715,7 +1715,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
1715
1715
|
}
|
1716
1716
|
|
1717
1717
|
// Schedule buffer control
|
1718
|
-
private checkBuffer() {
|
1718
|
+
private checkBuffer(starved?: boolean) {
|
1719
1719
|
const items = this.schedule.items;
|
1720
1720
|
if (!items) {
|
1721
1721
|
return;
|
@@ -1726,8 +1726,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
1726
1726
|
this.timelinePos,
|
1727
1727
|
0,
|
1728
1728
|
);
|
1729
|
-
|
1730
|
-
|
1729
|
+
if (starved) {
|
1730
|
+
this.bufferedPos = this.timelinePos;
|
1731
|
+
}
|
1732
|
+
starved ||= bufferInfo.len < 1;
|
1733
|
+
this.updateBufferedPos(bufferInfo.end, items, starved);
|
1731
1734
|
}
|
1732
1735
|
|
1733
1736
|
private updateBufferedPos(
|
@@ -1788,10 +1791,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
1788
1791
|
} else if (
|
1789
1792
|
bufferIsEmpty &&
|
1790
1793
|
playingItem &&
|
1791
|
-
!this.itemsMatch(playingItem, bufferingItem)
|
1792
|
-
bufferEndIndex === playingIndex
|
1794
|
+
!this.itemsMatch(playingItem, bufferingItem)
|
1793
1795
|
) {
|
1794
|
-
|
1796
|
+
if (bufferEndIndex === playingIndex) {
|
1797
|
+
this.bufferedToItem(playingItem);
|
1798
|
+
} else if (bufferEndIndex === playingIndex + 1) {
|
1799
|
+
this.bufferedToItem(items[bufferEndIndex]);
|
1800
|
+
}
|
1795
1801
|
}
|
1796
1802
|
}
|
1797
1803
|
|
@@ -1930,7 +1936,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
1930
1936
|
if (neverLoaded) {
|
1931
1937
|
const timelineStart = interstitial.timelineStart;
|
1932
1938
|
if (interstitial.appendInPlace) {
|
1933
|
-
|
1939
|
+
const playingItem = this.playingItem;
|
1940
|
+
if (
|
1941
|
+
!this.isInterstitial(playingItem) &&
|
1942
|
+
playingItem?.nextEvent?.identifier === interstitial.identifier
|
1943
|
+
) {
|
1944
|
+
this.flushFrontBuffer(timelineStart + 0.25);
|
1945
|
+
}
|
1934
1946
|
}
|
1935
1947
|
let hlsStartOffset;
|
1936
1948
|
let liveStartPosition = 0;
|
@@ -1998,6 +2010,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
1998
2010
|
if (!requiredTracks) {
|
1999
2011
|
return;
|
2000
2012
|
}
|
2013
|
+
this.log(`Removing front buffer starting at ${startOffset}`);
|
2001
2014
|
const sourceBufferNames = Object.keys(requiredTracks);
|
2002
2015
|
sourceBufferNames.forEach((type: SourceBufferName) => {
|
2003
2016
|
this.hls.trigger(Events.BUFFER_FLUSHING, {
|
@@ -2087,12 +2100,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2087
2100
|
}
|
2088
2101
|
}
|
2089
2102
|
}
|
2103
|
+
const assetId = assetItem.identifier;
|
2090
2104
|
const playerConfig: Partial<HlsConfig> = {
|
2091
2105
|
...userConfig,
|
2092
2106
|
autoStartLoad: true,
|
2093
2107
|
startFragPrefetch: true,
|
2094
2108
|
primarySessionId: primary.sessionId,
|
2095
|
-
assetPlayerId:
|
2109
|
+
assetPlayerId: assetId,
|
2096
2110
|
abrEwmaDefaultEstimate: primary.bandwidthEstimate,
|
2097
2111
|
interstitialsController: undefined,
|
2098
2112
|
startPosition,
|
@@ -2114,6 +2128,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2114
2128
|
contentId: hash(assetItem.uri),
|
2115
2129
|
});
|
2116
2130
|
}
|
2131
|
+
if (this.getAssetPlayer(assetId)) {
|
2132
|
+
this.warn(
|
2133
|
+
`Duplicate date range identifier ${interstitial} and asset ${assetId}`,
|
2134
|
+
);
|
2135
|
+
}
|
2117
2136
|
const player = new HlsAssetPlayer(
|
2118
2137
|
this.HlsPlayerClass,
|
2119
2138
|
playerConfig,
|
@@ -2122,7 +2141,6 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2122
2141
|
);
|
2123
2142
|
this.playerQueue.push(player);
|
2124
2143
|
interstitial.assetList[assetListIndex] = assetItem;
|
2125
|
-
const assetId = assetItem.identifier;
|
2126
2144
|
// Listen for LevelDetails and PTS change to update duration
|
2127
2145
|
const updateAssetPlayerDetails = (details: LevelDetails) => {
|
2128
2146
|
if (details.live) {
|
@@ -2181,14 +2199,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2181
2199
|
}
|
2182
2200
|
};
|
2183
2201
|
player.on(Events.BUFFER_CODECS, onBufferCodecs);
|
2184
|
-
const bufferedToEnd = (
|
2202
|
+
const bufferedToEnd = () => {
|
2185
2203
|
const inQueuPlayer = this.getAssetPlayer(assetId);
|
2186
2204
|
this.log(`buffered to end of asset ${inQueuPlayer}`);
|
2187
2205
|
if (!inQueuPlayer) {
|
2188
2206
|
return;
|
2189
2207
|
}
|
2190
|
-
inQueuPlayer.off(Events.BUFFERED_TO_END, bufferedToEnd);
|
2191
|
-
|
2192
2208
|
// Preload at end of asset
|
2193
2209
|
const scheduleIndex = this.schedule.findEventIndex(
|
2194
2210
|
interstitial.identifier,
|
@@ -2202,7 +2218,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2202
2218
|
!interstitial.isAssetPastPlayoutLimit(nextAssetIndex) &&
|
2203
2219
|
!interstitial.assetList[nextAssetIndex].error
|
2204
2220
|
) {
|
2205
|
-
this.bufferedToItem(item,
|
2221
|
+
this.bufferedToItem(item, nextAssetIndex);
|
2206
2222
|
} else {
|
2207
2223
|
const nextItem = this.schedule.items?.[scheduleIndex + 1];
|
2208
2224
|
if (nextItem) {
|
@@ -2228,6 +2244,30 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2228
2244
|
player.once(Events.MEDIA_ENDED, endedWithAssetIndex(assetListIndex));
|
2229
2245
|
player.once(Events.PLAYOUT_LIMIT_REACHED, endedWithAssetIndex(Infinity));
|
2230
2246
|
player.on(Events.ERROR, (event: Events.ERROR, data: ErrorData) => {
|
2247
|
+
const inQueuPlayer = this.getAssetPlayer(assetId);
|
2248
|
+
if (data.details === ErrorDetails.BUFFER_STALLED_ERROR) {
|
2249
|
+
if (inQueuPlayer?.media) {
|
2250
|
+
const assetCurrentTime = inQueuPlayer.currentTime;
|
2251
|
+
const distanceFromEnd = inQueuPlayer.duration - assetCurrentTime;
|
2252
|
+
if (
|
2253
|
+
assetCurrentTime &&
|
2254
|
+
interstitial.appendInPlace &&
|
2255
|
+
distanceFromEnd / inQueuPlayer.media.playbackRate < 0.5
|
2256
|
+
) {
|
2257
|
+
this.log(
|
2258
|
+
`Advancing buffer past end of asset ${assetId} ${interstitial} at ${inQueuPlayer.media.currentTime}`,
|
2259
|
+
);
|
2260
|
+
bufferedToEnd();
|
2261
|
+
} else {
|
2262
|
+
this.warn(
|
2263
|
+
`Stalled at ${assetCurrentTime} of ${assetCurrentTime + distanceFromEnd} in asset ${assetId} ${interstitial}`,
|
2264
|
+
);
|
2265
|
+
this.onTimeupdate();
|
2266
|
+
this.checkBuffer(true);
|
2267
|
+
}
|
2268
|
+
}
|
2269
|
+
return;
|
2270
|
+
}
|
2231
2271
|
this.handleAssetItemError(
|
2232
2272
|
data,
|
2233
2273
|
interstitial,
|
@@ -2336,10 +2376,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2336
2376
|
});
|
2337
2377
|
}
|
2338
2378
|
|
2339
|
-
|
2340
|
-
|
2341
|
-
this.bufferAssetPlayer(player, media);
|
2342
|
-
}
|
2379
|
+
// detach media and attach to interstitial player if it does not have another element attached
|
2380
|
+
this.bufferAssetPlayer(player, media);
|
2343
2381
|
}
|
2344
2382
|
|
2345
2383
|
private bufferAssetPlayer(player: HlsAssetPlayer, media: HTMLMediaElement) {
|
@@ -2569,6 +2607,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
|
|
2569
2607
|
if (interstitial) {
|
2570
2608
|
this.primaryFallback(interstitial);
|
2571
2609
|
}
|
2610
|
+
break;
|
2611
|
+
}
|
2612
|
+
case ErrorDetails.BUFFER_STALLED_ERROR: {
|
2613
|
+
this.onTimeupdate();
|
2614
|
+
this.checkBuffer(true);
|
2615
|
+
break;
|
2572
2616
|
}
|
2573
2617
|
}
|
2574
2618
|
}
|
@@ -628,7 +628,7 @@ export class InterstitialsSchedule extends Logger {
|
|
628
628
|
return !playlists.some((playlistType) => {
|
629
629
|
const details = mediaSelection[playlistType].details;
|
630
630
|
const playlistEnd = details.edge;
|
631
|
-
if (resumeTime
|
631
|
+
if (resumeTime >= playlistEnd) {
|
632
632
|
// Live playback - resumption segments are not yet available
|
633
633
|
this.log(
|
634
634
|
`"${interstitial.identifier}" resumption ${resumeTime} past ${playlistType} playlist end ${playlistEnd}`,
|
package/src/exports-named.ts
CHANGED
@@ -12,6 +12,7 @@ import ErrorController from './controller/error-controller';
|
|
12
12
|
import FPSController from './controller/fps-controller';
|
13
13
|
import SubtitleTrackController from './controller/subtitle-track-controller';
|
14
14
|
import Hls from './hls';
|
15
|
+
import M3U8Parser from './loader/m3u8-parser';
|
15
16
|
import Cues from './utils/cues';
|
16
17
|
import FetchLoader from './utils/fetch-loader';
|
17
18
|
import XhrLoader from './utils/xhr-loader';
|
@@ -36,6 +37,7 @@ export {
|
|
36
37
|
XhrLoader,
|
37
38
|
FetchLoader,
|
38
39
|
Cues,
|
40
|
+
M3U8Parser,
|
39
41
|
};
|
40
42
|
|
41
43
|
export { Events } from './events';
|
package/src/utils/mp4-tools.ts
CHANGED
@@ -275,22 +275,20 @@ export function parseInitSegment(initSegment: Uint8Array): InitData {
|
|
275
275
|
soun: ElementaryStreamTypes.AUDIO as const,
|
276
276
|
vide: ElementaryStreamTypes.VIDEO as const,
|
277
277
|
}[hdlrType];
|
278
|
+
// Parse codec details
|
279
|
+
const stsdBox = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];
|
280
|
+
const stsd = parseStsd(stsdBox);
|
278
281
|
if (type) {
|
279
|
-
//
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
timescale,
|
290
|
-
type: hdlrType as HdlrType,
|
291
|
-
stsd,
|
292
|
-
};
|
293
|
-
}
|
282
|
+
// Add 'audio', 'video', and 'audiovideo' track records that will map to SourceBuffers
|
283
|
+
result[trackId] = { timescale, type, stsd };
|
284
|
+
result[type] = { timescale, id: trackId, ...stsd };
|
285
|
+
} else {
|
286
|
+
// Add 'meta' and other track records required by `offsetStartDTS`
|
287
|
+
result[trackId] = {
|
288
|
+
timescale,
|
289
|
+
type: hdlrType as HdlrType,
|
290
|
+
stsd,
|
291
|
+
};
|
294
292
|
}
|
295
293
|
}
|
296
294
|
}
|