hls.js 1.6.0-beta.2.0.canary.10923 → 1.6.0-beta.2.0.canary.10925
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 +61 -29
- package/dist/hls.d.ts +61 -29
- package/dist/hls.js +653 -491
- package/dist/hls.js.d.ts +61 -29
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +3855 -3696
- 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 +1650 -1490
- 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 +656 -493
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/package.json +1 -1
- package/src/config.ts +15 -9
- package/src/controller/base-stream-controller.ts +12 -8
- package/src/controller/buffer-controller.ts +19 -22
- package/src/controller/eme-controller.ts +6 -1
- package/src/controller/gap-controller.ts +257 -35
- package/src/controller/interstitials-controller.ts +13 -10
- package/src/controller/stream-controller.ts +26 -73
- package/src/hls.ts +47 -3
- package/src/utils/buffer-helper.ts +35 -13
- package/src/utils/event-listener-helper.ts +16 -0
@@ -22,6 +22,10 @@ import {
|
|
22
22
|
TimelineOccupancy,
|
23
23
|
} from '../loader/interstitial-event';
|
24
24
|
import { BufferHelper } from '../utils/buffer-helper';
|
25
|
+
import {
|
26
|
+
addEventListener,
|
27
|
+
removeEventListener,
|
28
|
+
} from '../utils/event-listener-helper';
|
25
29
|
import { hash } from '../utils/hash';
|
26
30
|
import { Logger } from '../utils/logger';
|
27
31
|
import { isCompatibleTrackChange } from '../utils/mediasource-helper';
|
@@ -226,17 +230,17 @@ export default class InterstitialsController
|
|
226
230
|
}
|
227
231
|
|
228
232
|
private onDestroying() {
|
229
|
-
const media = this.primaryMedia;
|
233
|
+
const media = this.primaryMedia || this.media;
|
230
234
|
if (media) {
|
231
235
|
this.removeMediaListeners(media);
|
232
236
|
}
|
233
237
|
}
|
234
238
|
|
235
239
|
private removeMediaListeners(media: HTMLMediaElement) {
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
+
removeEventListener(media, 'play', this.onPlay);
|
241
|
+
removeEventListener(media, 'pause', this.onPause);
|
242
|
+
removeEventListener(media, 'seeking', this.onSeeking);
|
243
|
+
removeEventListener(media, 'timeupdate', this.onTimeupdate);
|
240
244
|
}
|
241
245
|
|
242
246
|
private onMediaAttaching(
|
@@ -244,11 +248,10 @@ export default class InterstitialsController
|
|
244
248
|
data: MediaAttachingData,
|
245
249
|
) {
|
246
250
|
const media = (this.media = data.media);
|
247
|
-
this.
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
media.addEventListener('pause', this.onPause);
|
251
|
+
addEventListener(media, 'seeking', this.onSeeking);
|
252
|
+
addEventListener(media, 'timeupdate', this.onTimeupdate);
|
253
|
+
addEventListener(media, 'play', this.onPlay);
|
254
|
+
addEventListener(media, 'pause', this.onPause);
|
252
255
|
}
|
253
256
|
|
254
257
|
private onMediaAttached(
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import BaseStreamController, { State } from './base-stream-controller';
|
2
2
|
import { findFragmentByPTS } from './fragment-finders';
|
3
3
|
import { FragmentState } from './fragment-tracker';
|
4
|
-
import
|
4
|
+
import { MAX_START_GAP_JUMP } from './gap-controller';
|
5
5
|
import TransmuxerInterface from '../demux/transmuxer-interface';
|
6
6
|
import { ErrorDetails } from '../errors';
|
7
7
|
import { Events } from '../events';
|
@@ -11,13 +11,21 @@ import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
|
11
11
|
import { ChunkMetadata } from '../types/transmuxer';
|
12
12
|
import { BufferHelper } from '../utils/buffer-helper';
|
13
13
|
import { pickMostCompleteCodecName } from '../utils/codecs';
|
14
|
+
import {
|
15
|
+
addEventListener,
|
16
|
+
removeEventListener,
|
17
|
+
} from '../utils/event-listener-helper';
|
14
18
|
import { useAlternateAudio } from '../utils/rendition-helper';
|
15
19
|
import type { FragmentTracker } from './fragment-tracker';
|
16
20
|
import type Hls from '../hls';
|
17
21
|
import type { Fragment, MediaFragment } from '../loader/fragment';
|
18
22
|
import type KeyLoader from '../loader/key-loader';
|
19
23
|
import type { LevelDetails } from '../loader/level-details';
|
20
|
-
import type {
|
24
|
+
import type {
|
25
|
+
BufferCreatedTrack,
|
26
|
+
ExtendedSourceBuffer,
|
27
|
+
SourceBufferName,
|
28
|
+
} from '../types/buffer';
|
21
29
|
import type { NetworkComponentAPI } from '../types/component-api';
|
22
30
|
import type {
|
23
31
|
AudioTrackSwitchedData,
|
@@ -56,7 +64,6 @@ export default class StreamController
|
|
56
64
|
implements NetworkComponentAPI
|
57
65
|
{
|
58
66
|
private audioCodecSwap: boolean = false;
|
59
|
-
private gapController: GapController | null = null;
|
60
67
|
private level: number = -1;
|
61
68
|
private _forceStartLoad: boolean = false;
|
62
69
|
private _hasEnoughToStart: boolean = false;
|
@@ -67,7 +74,7 @@ export default class StreamController
|
|
67
74
|
private couldBacktrack: boolean = false;
|
68
75
|
private backtrackFragment: Fragment | null = null;
|
69
76
|
private audioCodecSwitch: boolean = false;
|
70
|
-
private videoBuffer:
|
77
|
+
private videoBuffer: ExtendedSourceBuffer | null = null;
|
71
78
|
|
72
79
|
constructor(
|
73
80
|
hls: Hls,
|
@@ -123,7 +130,7 @@ export default class StreamController
|
|
123
130
|
|
124
131
|
protected onHandlerDestroying() {
|
125
132
|
// @ts-ignore
|
126
|
-
this.onMediaPlaying = this.onMediaSeeked =
|
133
|
+
this.onMediaPlaying = this.onMediaSeeked = null;
|
127
134
|
this.unregisterListeners();
|
128
135
|
super.onHandlerDestroying();
|
129
136
|
}
|
@@ -230,7 +237,9 @@ export default class StreamController
|
|
230
237
|
|
231
238
|
protected onTickEnd() {
|
232
239
|
super.onTickEnd();
|
233
|
-
this.
|
240
|
+
if (this.media?.readyState) {
|
241
|
+
this.lastCurrentTime = this.media.currentTime;
|
242
|
+
}
|
234
243
|
this.checkFragmentChanged();
|
235
244
|
}
|
236
245
|
|
@@ -533,17 +542,8 @@ export default class StreamController
|
|
533
542
|
) {
|
534
543
|
super.onMediaAttached(event, data);
|
535
544
|
const media = data.media;
|
536
|
-
media
|
537
|
-
media
|
538
|
-
media.removeEventListener('waiting', this.onMediaWaiting);
|
539
|
-
media.addEventListener('playing', this.onMediaPlaying);
|
540
|
-
media.addEventListener('seeked', this.onMediaSeeked);
|
541
|
-
media.addEventListener('waiting', this.onMediaWaiting);
|
542
|
-
this.gapController = new GapController(
|
543
|
-
media,
|
544
|
-
this.fragmentTracker,
|
545
|
-
this.hls,
|
546
|
-
);
|
545
|
+
addEventListener(media, 'playing', this.onMediaPlaying);
|
546
|
+
addEventListener(media, 'seeked', this.onMediaSeeked);
|
547
547
|
}
|
548
548
|
|
549
549
|
protected onMediaDetaching(
|
@@ -552,16 +552,11 @@ export default class StreamController
|
|
552
552
|
) {
|
553
553
|
const { media } = this;
|
554
554
|
if (media) {
|
555
|
-
|
556
|
-
|
557
|
-
media.removeEventListener('waiting', this.onMediaWaiting);
|
555
|
+
removeEventListener(media, 'playing', this.onMediaPlaying);
|
556
|
+
removeEventListener(media, 'seeked', this.onMediaSeeked);
|
558
557
|
}
|
559
558
|
this.videoBuffer = null;
|
560
559
|
this.fragPlaying = null;
|
561
|
-
if (this.gapController) {
|
562
|
-
this.gapController.destroy();
|
563
|
-
this.gapController = null;
|
564
|
-
}
|
565
560
|
super.onMediaDetaching(event, data);
|
566
561
|
const transferringMedia = !!data.transferMedia;
|
567
562
|
if (transferringMedia) {
|
@@ -570,20 +565,8 @@ export default class StreamController
|
|
570
565
|
this._hasEnoughToStart = false;
|
571
566
|
}
|
572
567
|
|
573
|
-
private onMediaWaiting = () => {
|
574
|
-
const gapController = this.gapController;
|
575
|
-
if (gapController) {
|
576
|
-
gapController.waiting = self.performance.now();
|
577
|
-
}
|
578
|
-
};
|
579
|
-
|
580
568
|
private onMediaPlaying = () => {
|
581
569
|
// tick to speed up FRAG_CHANGED triggering
|
582
|
-
const gapController = this.gapController;
|
583
|
-
if (gapController) {
|
584
|
-
gapController.ended = 0;
|
585
|
-
gapController.waiting = 0;
|
586
|
-
}
|
587
570
|
this.tick();
|
588
571
|
};
|
589
572
|
|
@@ -609,19 +592,6 @@ export default class StreamController
|
|
609
592
|
this.tick();
|
610
593
|
};
|
611
594
|
|
612
|
-
protected triggerEnded() {
|
613
|
-
const gapController = this.gapController;
|
614
|
-
if (gapController) {
|
615
|
-
if (gapController.ended) {
|
616
|
-
return;
|
617
|
-
}
|
618
|
-
gapController.ended = this.media?.currentTime || 1;
|
619
|
-
}
|
620
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
621
|
-
stalled: false,
|
622
|
-
});
|
623
|
-
}
|
624
|
-
|
625
595
|
protected onManifestLoading() {
|
626
596
|
super.onManifestLoading();
|
627
597
|
// reset buffer on manifest loading
|
@@ -935,11 +905,11 @@ export default class StreamController
|
|
935
905
|
data: BufferCreatedData,
|
936
906
|
) {
|
937
907
|
const tracks = data.tracks;
|
938
|
-
let mediaTrack;
|
939
|
-
let name;
|
908
|
+
let mediaTrack: BufferCreatedTrack | undefined;
|
909
|
+
let name: string | undefined;
|
940
910
|
let alternate = false;
|
941
911
|
for (const type in tracks) {
|
942
|
-
const track = tracks[type];
|
912
|
+
const track: BufferCreatedTrack = tracks[type];
|
943
913
|
if (track.id === 'main') {
|
944
914
|
name = type;
|
945
915
|
mediaTrack = track;
|
@@ -1056,25 +1026,6 @@ export default class StreamController
|
|
1056
1026
|
}
|
1057
1027
|
}
|
1058
1028
|
|
1059
|
-
// Checks the health of the buffer and attempts to resolve playback stalls.
|
1060
|
-
private checkBuffer() {
|
1061
|
-
const { media, gapController } = this;
|
1062
|
-
if (!media || !gapController || !media.readyState) {
|
1063
|
-
// Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0)
|
1064
|
-
return;
|
1065
|
-
}
|
1066
|
-
|
1067
|
-
if (this._hasEnoughToStart || !BufferHelper.getBuffered(media).length) {
|
1068
|
-
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
1069
|
-
const state = this.state;
|
1070
|
-
const activeFrag = state !== State.IDLE ? this.fragCurrent : null;
|
1071
|
-
const levelDetails = this.getLevelDetails();
|
1072
|
-
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
1073
|
-
}
|
1074
|
-
|
1075
|
-
this.lastCurrentTime = media.currentTime;
|
1076
|
-
}
|
1077
|
-
|
1078
1029
|
private onFragLoadEmergencyAborted() {
|
1079
1030
|
this.state = State.IDLE;
|
1080
1031
|
// if loadedmetadata is not set, it means that we are emergency switch down on first frag
|
@@ -1095,8 +1046,10 @@ export default class StreamController
|
|
1095
1046
|
(type === ElementaryStreamTypes.VIDEO
|
1096
1047
|
? this.videoBuffer
|
1097
1048
|
: this.mediaBuffer) || this.media;
|
1098
|
-
|
1099
|
-
|
1049
|
+
if (mediaBuffer) {
|
1050
|
+
this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
|
1051
|
+
this.tick();
|
1052
|
+
}
|
1100
1053
|
}
|
1101
1054
|
}
|
1102
1055
|
|
package/src/hls.ts
CHANGED
@@ -3,6 +3,7 @@ import { EventEmitter } from 'eventemitter3';
|
|
3
3
|
import { buildAbsoluteURL } from 'url-toolkit';
|
4
4
|
import { enableStreamingMode, hlsDefaultConfig, mergeConfig } from './config';
|
5
5
|
import { FragmentTracker } from './controller/fragment-tracker';
|
6
|
+
import GapController from './controller/gap-controller';
|
6
7
|
import ID3TrackController from './controller/id3-track-controller';
|
7
8
|
import LatencyController from './controller/latency-controller';
|
8
9
|
import LevelController from './controller/level-controller';
|
@@ -14,6 +15,7 @@ import KeyLoader from './loader/key-loader';
|
|
14
15
|
import PlaylistLoader from './loader/playlist-loader';
|
15
16
|
import { MetadataSchema } from './types/demuxer';
|
16
17
|
import { type HdcpLevel, isHdcpLevel, type Level } from './types/level';
|
18
|
+
import { PlaylistLevelType } from './types/loader';
|
17
19
|
import { enableLogs, type ILogger } from './utils/logger';
|
18
20
|
import { getMediaDecodingInfoPromise } from './utils/mediacapabilities-helper';
|
19
21
|
import { getMediaSource } from './utils/mediasource-helper';
|
@@ -24,6 +26,7 @@ import type AbrController from './controller/abr-controller';
|
|
24
26
|
import type AudioStreamController from './controller/audio-stream-controller';
|
25
27
|
import type AudioTrackController from './controller/audio-track-controller';
|
26
28
|
import type BasePlaylistController from './controller/base-playlist-controller';
|
29
|
+
import type { InFlightData, State } from './controller/base-stream-controller';
|
27
30
|
import type BaseStreamController from './controller/base-stream-controller';
|
28
31
|
import type BufferController from './controller/buffer-controller';
|
29
32
|
import type CapLevelController from './controller/cap-level-controller';
|
@@ -34,6 +37,7 @@ import type ErrorController from './controller/error-controller';
|
|
34
37
|
import type FPSController from './controller/fps-controller';
|
35
38
|
import type InterstitialsController from './controller/interstitials-controller';
|
36
39
|
import type { InterstitialsManager } from './controller/interstitials-controller';
|
40
|
+
import type { SubtitleStreamController } from './controller/subtitle-stream-controller';
|
37
41
|
import type SubtitleTrackController from './controller/subtitle-track-controller';
|
38
42
|
import type Decrypter from './crypt/decrypter';
|
39
43
|
import type TransmuxerInterface from './demux/transmuxer-interface';
|
@@ -91,9 +95,12 @@ export default class Hls implements HlsEventEmitter {
|
|
91
95
|
private latencyController: LatencyController;
|
92
96
|
private levelController: LevelController;
|
93
97
|
private streamController: StreamController;
|
98
|
+
private audioStreamController?: AudioStreamController;
|
99
|
+
private subtititleStreamController?: SubtitleStreamController;
|
94
100
|
private audioTrackController?: AudioTrackController;
|
95
101
|
private subtitleTrackController?: SubtitleTrackController;
|
96
102
|
private interstitialsController?: InterstitialsController;
|
103
|
+
private gapController: GapController;
|
97
104
|
private emeController?: EMEController;
|
98
105
|
private cmcdController?: CMCDController;
|
99
106
|
private _media: HTMLMediaElement | null = null;
|
@@ -229,6 +236,11 @@ export default class Hls implements HlsEventEmitter {
|
|
229
236
|
keyLoader,
|
230
237
|
));
|
231
238
|
|
239
|
+
const gapController = (this.gapController = new GapController(
|
240
|
+
this,
|
241
|
+
fragmentTracker,
|
242
|
+
));
|
243
|
+
|
232
244
|
// Cap level controller uses streamController to flush the buffer
|
233
245
|
capLevelController.setStreamController(streamController);
|
234
246
|
// fpsController uses streamController to switch when frames are being dropped
|
@@ -250,6 +262,7 @@ export default class Hls implements HlsEventEmitter {
|
|
250
262
|
const coreComponents: ComponentAPI[] = [
|
251
263
|
abrController,
|
252
264
|
bufferController,
|
265
|
+
gapController,
|
253
266
|
capLevelController,
|
254
267
|
fpsController,
|
255
268
|
id3TrackController,
|
@@ -263,7 +276,11 @@ export default class Hls implements HlsEventEmitter {
|
|
263
276
|
const AudioStreamControllerClass = config.audioStreamController;
|
264
277
|
if (AudioStreamControllerClass) {
|
265
278
|
networkControllers.push(
|
266
|
-
new AudioStreamControllerClass(
|
279
|
+
(this.audioStreamController = new AudioStreamControllerClass(
|
280
|
+
this,
|
281
|
+
fragmentTracker,
|
282
|
+
keyLoader,
|
283
|
+
)),
|
267
284
|
);
|
268
285
|
}
|
269
286
|
// Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
|
@@ -274,7 +291,11 @@ export default class Hls implements HlsEventEmitter {
|
|
274
291
|
const SubtitleStreamControllerClass = config.subtitleStreamController;
|
275
292
|
if (SubtitleStreamControllerClass) {
|
276
293
|
networkControllers.push(
|
277
|
-
new SubtitleStreamControllerClass(
|
294
|
+
(this.subtititleStreamController = new SubtitleStreamControllerClass(
|
295
|
+
this,
|
296
|
+
fragmentTracker,
|
297
|
+
keyLoader,
|
298
|
+
)),
|
278
299
|
);
|
279
300
|
}
|
280
301
|
this.createController(config.timelineController, coreComponents);
|
@@ -604,6 +625,21 @@ export default class Hls implements HlsEventEmitter {
|
|
604
625
|
}
|
605
626
|
}
|
606
627
|
|
628
|
+
get inFlightFragments(): InFlightFragments {
|
629
|
+
const inFlightData = {
|
630
|
+
[PlaylistLevelType.MAIN]: this.streamController.inFlightFrag,
|
631
|
+
};
|
632
|
+
if (this.audioStreamController) {
|
633
|
+
inFlightData[PlaylistLevelType.AUDIO] =
|
634
|
+
this.audioStreamController.inFlightFrag;
|
635
|
+
}
|
636
|
+
if (this.subtititleStreamController) {
|
637
|
+
inFlightData[PlaylistLevelType.SUBTITLE] =
|
638
|
+
this.subtititleStreamController.inFlightFrag;
|
639
|
+
}
|
640
|
+
return inFlightData;
|
641
|
+
}
|
642
|
+
|
607
643
|
/**
|
608
644
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
609
645
|
*/
|
@@ -1181,6 +1217,11 @@ export default class Hls implements HlsEventEmitter {
|
|
1181
1217
|
}
|
1182
1218
|
}
|
1183
1219
|
|
1220
|
+
export type InFlightFragments = {
|
1221
|
+
[PlaylistLevelType.MAIN]: InFlightData;
|
1222
|
+
[PlaylistLevelType.AUDIO]?: InFlightData;
|
1223
|
+
[PlaylistLevelType.SUBTITLE]?: InFlightData;
|
1224
|
+
};
|
1184
1225
|
export type {
|
1185
1226
|
AudioSelectionOption,
|
1186
1227
|
SubtitleSelectionOption,
|
@@ -1211,6 +1252,7 @@ export type {
|
|
1211
1252
|
FPSController,
|
1212
1253
|
InterstitialsController,
|
1213
1254
|
StreamController,
|
1255
|
+
SubtitleStreamController,
|
1214
1256
|
SubtitleTrackController,
|
1215
1257
|
EwmaBandWidthEstimator,
|
1216
1258
|
InterstitialsManager,
|
@@ -1219,6 +1261,8 @@ export type {
|
|
1219
1261
|
KeyLoader,
|
1220
1262
|
TaskLoop,
|
1221
1263
|
TransmuxerInterface,
|
1264
|
+
InFlightData,
|
1265
|
+
State,
|
1222
1266
|
};
|
1223
1267
|
export type {
|
1224
1268
|
ABRControllerConfig,
|
@@ -1232,6 +1276,7 @@ export type {
|
|
1232
1276
|
FPSControllerConfig,
|
1233
1277
|
FragmentLoaderConfig,
|
1234
1278
|
FragmentLoaderConstructor,
|
1279
|
+
GapControllerConfig,
|
1235
1280
|
HlsLoadPolicies,
|
1236
1281
|
LevelControllerConfig,
|
1237
1282
|
LoaderConfig,
|
@@ -1270,7 +1315,6 @@ export type {
|
|
1270
1315
|
InterstitialScheduleItem,
|
1271
1316
|
InterstitialSchedulePrimaryItem,
|
1272
1317
|
} from './controller/interstitials-schedule';
|
1273
|
-
export type { SubtitleStreamController } from './controller/subtitle-stream-controller';
|
1274
1318
|
export type { TimelineController } from './controller/timeline-controller';
|
1275
1319
|
export type { DecrypterAesMode } from './crypt/decrypter-aes-mode';
|
1276
1320
|
export type { DateRange, DateRangeCue } from './loader/date-range';
|
@@ -23,6 +23,7 @@ export type BufferInfo = {
|
|
23
23
|
end: number;
|
24
24
|
nextStart?: number;
|
25
25
|
buffered?: BufferTimeRange[];
|
26
|
+
bufferedIndex: number;
|
26
27
|
};
|
27
28
|
|
28
29
|
const noopBuffered: TimeRanges = {
|
@@ -47,22 +48,34 @@ export class BufferHelper {
|
|
47
48
|
return false;
|
48
49
|
}
|
49
50
|
|
51
|
+
static bufferedRanges(media: Bufferable | null): BufferTimeRange[] {
|
52
|
+
if (media) {
|
53
|
+
const timeRanges = BufferHelper.getBuffered(media);
|
54
|
+
return BufferHelper.timeRangesToArray(timeRanges);
|
55
|
+
}
|
56
|
+
return [];
|
57
|
+
}
|
58
|
+
|
59
|
+
static timeRangesToArray(timeRanges: TimeRanges): BufferTimeRange[] {
|
60
|
+
const buffered: BufferTimeRange[] = [];
|
61
|
+
for (let i = 0; i < timeRanges.length; i++) {
|
62
|
+
buffered.push({ start: timeRanges.start(i), end: timeRanges.end(i) });
|
63
|
+
}
|
64
|
+
return buffered;
|
65
|
+
}
|
66
|
+
|
50
67
|
static bufferInfo(
|
51
68
|
media: Bufferable | null,
|
52
69
|
pos: number,
|
53
70
|
maxHoleDuration: number,
|
54
71
|
): BufferInfo {
|
55
72
|
if (media) {
|
56
|
-
const
|
57
|
-
if (
|
58
|
-
const buffered: BufferTimeRange[] = [];
|
59
|
-
for (let i = 0; i < vbuffered.length; i++) {
|
60
|
-
buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) });
|
61
|
-
}
|
73
|
+
const buffered = BufferHelper.bufferedRanges(media);
|
74
|
+
if (buffered.length) {
|
62
75
|
return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
|
63
76
|
}
|
64
77
|
}
|
65
|
-
return { len: 0, start: pos, end: pos };
|
78
|
+
return { len: 0, start: pos, end: pos, bufferedIndex: -1 };
|
66
79
|
}
|
67
80
|
|
68
81
|
static bufferedInfo(
|
@@ -72,14 +85,20 @@ export class BufferHelper {
|
|
72
85
|
): BufferInfo {
|
73
86
|
pos = Math.max(0, pos);
|
74
87
|
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
|
75
|
-
buffered.
|
88
|
+
if (buffered.length > 1) {
|
89
|
+
buffered.sort((a, b) => a.start - b.start || b.end - a.end);
|
90
|
+
}
|
76
91
|
|
92
|
+
let bufferedIndex: number = -1;
|
77
93
|
let buffered2: BufferTimeRange[] = [];
|
78
94
|
if (maxHoleDuration) {
|
79
95
|
// there might be some small holes between buffer time range
|
80
96
|
// consider that holes smaller than maxHoleDuration are irrelevant and build another
|
81
97
|
// buffer time range representations that discards those holes
|
82
98
|
for (let i = 0; i < buffered.length; i++) {
|
99
|
+
if (pos >= buffered[i].start && pos <= buffered[i].end) {
|
100
|
+
bufferedIndex = i;
|
101
|
+
}
|
83
102
|
const buf2len = buffered2.length;
|
84
103
|
if (buf2len) {
|
85
104
|
const buf2end = buffered2[buf2len - 1].end;
|
@@ -107,23 +126,25 @@ export class BufferHelper {
|
|
107
126
|
|
108
127
|
let bufferLen = 0;
|
109
128
|
|
110
|
-
|
111
|
-
let bufferStartNext: number | undefined;
|
129
|
+
let nextStart: number | undefined;
|
112
130
|
|
113
|
-
// bufferStart and bufferEnd are buffer boundaries around current
|
131
|
+
// bufferStart and bufferEnd are buffer boundaries around current playback position (pos)
|
114
132
|
let bufferStart: number = pos;
|
115
133
|
let bufferEnd: number = pos;
|
116
134
|
for (let i = 0; i < buffered2.length; i++) {
|
117
135
|
const start = buffered2[i].start;
|
118
136
|
const end = buffered2[i].end;
|
119
137
|
// logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
|
138
|
+
if (bufferedIndex === -1 && pos >= start && pos <= end) {
|
139
|
+
bufferedIndex = i;
|
140
|
+
}
|
120
141
|
if (pos + maxHoleDuration >= start && pos < end) {
|
121
142
|
// play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
|
122
143
|
bufferStart = start;
|
123
144
|
bufferEnd = end;
|
124
145
|
bufferLen = bufferEnd - pos;
|
125
146
|
} else if (pos + maxHoleDuration < start) {
|
126
|
-
|
147
|
+
nextStart = start;
|
127
148
|
break;
|
128
149
|
}
|
129
150
|
}
|
@@ -131,8 +152,9 @@ export class BufferHelper {
|
|
131
152
|
len: bufferLen,
|
132
153
|
start: bufferStart || 0,
|
133
154
|
end: bufferEnd || 0,
|
134
|
-
nextStart
|
155
|
+
nextStart,
|
135
156
|
buffered,
|
157
|
+
bufferedIndex,
|
136
158
|
};
|
137
159
|
}
|
138
160
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
export function addEventListener(
|
2
|
+
el: HTMLElement,
|
3
|
+
type: string,
|
4
|
+
listener: EventListenerOrEventListenerObject,
|
5
|
+
) {
|
6
|
+
removeEventListener(el, type, listener);
|
7
|
+
el.addEventListener(type, listener);
|
8
|
+
}
|
9
|
+
|
10
|
+
export function removeEventListener(
|
11
|
+
el: HTMLElement,
|
12
|
+
type: string,
|
13
|
+
listener: EventListenerOrEventListenerObject,
|
14
|
+
) {
|
15
|
+
el.removeEventListener(type, listener);
|
16
|
+
}
|