@zenvor/hls.js 1.0.0
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/LICENSE +28 -0
- package/README.md +472 -0
- package/dist/hls-demo.js +26995 -0
- package/dist/hls-demo.js.map +1 -0
- package/dist/hls.d.mts +4204 -0
- package/dist/hls.d.ts +4204 -0
- package/dist/hls.js +40050 -0
- package/dist/hls.js.d.ts +4204 -0
- package/dist/hls.js.map +1 -0
- package/dist/hls.light.js +27145 -0
- package/dist/hls.light.js.map +1 -0
- package/dist/hls.light.min.js +2 -0
- package/dist/hls.light.min.js.map +1 -0
- package/dist/hls.light.mjs +26392 -0
- package/dist/hls.light.mjs.map +1 -0
- package/dist/hls.min.js +2 -0
- package/dist/hls.min.js.map +1 -0
- package/dist/hls.mjs +38956 -0
- package/dist/hls.mjs.map +1 -0
- package/dist/hls.worker.js +2 -0
- package/dist/hls.worker.js.map +1 -0
- package/package.json +143 -0
- package/src/config.ts +794 -0
- package/src/controller/abr-controller.ts +1019 -0
- package/src/controller/algo-data-controller.ts +794 -0
- package/src/controller/audio-stream-controller.ts +1099 -0
- package/src/controller/audio-track-controller.ts +454 -0
- package/src/controller/base-playlist-controller.ts +438 -0
- package/src/controller/base-stream-controller.ts +2526 -0
- package/src/controller/buffer-controller.ts +2015 -0
- package/src/controller/buffer-operation-queue.ts +159 -0
- package/src/controller/cap-level-controller.ts +367 -0
- package/src/controller/cmcd-controller.ts +422 -0
- package/src/controller/content-steering-controller.ts +622 -0
- package/src/controller/eme-controller.ts +1617 -0
- package/src/controller/error-controller.ts +627 -0
- package/src/controller/fps-controller.ts +146 -0
- package/src/controller/fragment-finders.ts +256 -0
- package/src/controller/fragment-tracker.ts +567 -0
- package/src/controller/gap-controller.ts +719 -0
- package/src/controller/id3-track-controller.ts +488 -0
- package/src/controller/interstitial-player.ts +302 -0
- package/src/controller/interstitials-controller.ts +2895 -0
- package/src/controller/interstitials-schedule.ts +698 -0
- package/src/controller/latency-controller.ts +294 -0
- package/src/controller/level-controller.ts +776 -0
- package/src/controller/stream-controller.ts +1597 -0
- package/src/controller/subtitle-stream-controller.ts +508 -0
- package/src/controller/subtitle-track-controller.ts +617 -0
- package/src/controller/timeline-controller.ts +677 -0
- package/src/crypt/aes-crypto.ts +36 -0
- package/src/crypt/aes-decryptor.ts +339 -0
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +225 -0
- package/src/crypt/fast-aes-key.ts +39 -0
- package/src/define-plugin.d.ts +17 -0
- package/src/demux/audio/aacdemuxer.ts +126 -0
- package/src/demux/audio/ac3-demuxer.ts +170 -0
- package/src/demux/audio/adts.ts +249 -0
- package/src/demux/audio/base-audio-demuxer.ts +205 -0
- package/src/demux/audio/dolby.ts +21 -0
- package/src/demux/audio/mp3demuxer.ts +85 -0
- package/src/demux/audio/mpegaudio.ts +177 -0
- package/src/demux/chunk-cache.ts +42 -0
- package/src/demux/dummy-demuxed-track.ts +13 -0
- package/src/demux/inject-worker.ts +75 -0
- package/src/demux/mp4demuxer.ts +234 -0
- package/src/demux/sample-aes.ts +198 -0
- package/src/demux/transmuxer-interface.ts +449 -0
- package/src/demux/transmuxer-worker.ts +221 -0
- package/src/demux/transmuxer.ts +560 -0
- package/src/demux/tsdemuxer.ts +1256 -0
- package/src/demux/video/avc-video-parser.ts +401 -0
- package/src/demux/video/base-video-parser.ts +198 -0
- package/src/demux/video/exp-golomb.ts +153 -0
- package/src/demux/video/hevc-video-parser.ts +736 -0
- package/src/empty-es.js +5 -0
- package/src/empty.js +3 -0
- package/src/errors.ts +107 -0
- package/src/events.ts +548 -0
- package/src/exports-default.ts +3 -0
- package/src/exports-named.ts +81 -0
- package/src/hls.ts +1613 -0
- package/src/is-supported.ts +54 -0
- package/src/loader/date-range.ts +207 -0
- package/src/loader/fragment-loader.ts +403 -0
- package/src/loader/fragment.ts +487 -0
- package/src/loader/interstitial-asset-list.ts +162 -0
- package/src/loader/interstitial-event.ts +337 -0
- package/src/loader/key-loader.ts +439 -0
- package/src/loader/level-details.ts +203 -0
- package/src/loader/level-key.ts +259 -0
- package/src/loader/load-stats.ts +17 -0
- package/src/loader/m3u8-parser.ts +1072 -0
- package/src/loader/playlist-loader.ts +839 -0
- package/src/polyfills/number.ts +15 -0
- package/src/remux/aac-helper.ts +81 -0
- package/src/remux/mp4-generator.ts +1380 -0
- package/src/remux/mp4-remuxer.ts +1261 -0
- package/src/remux/passthrough-remuxer.ts +434 -0
- package/src/task-loop.ts +130 -0
- package/src/types/algo.ts +44 -0
- package/src/types/buffer.ts +105 -0
- package/src/types/component-api.ts +20 -0
- package/src/types/demuxer.ts +208 -0
- package/src/types/events.ts +574 -0
- package/src/types/fragment-tracker.ts +23 -0
- package/src/types/level.ts +268 -0
- package/src/types/loader.ts +198 -0
- package/src/types/media-playlist.ts +92 -0
- package/src/types/network-details.ts +3 -0
- package/src/types/remuxer.ts +104 -0
- package/src/types/track.ts +12 -0
- package/src/types/transmuxer.ts +46 -0
- package/src/types/tuples.ts +6 -0
- package/src/types/vtt.ts +11 -0
- package/src/utils/arrays.ts +22 -0
- package/src/utils/attr-list.ts +192 -0
- package/src/utils/binary-search.ts +46 -0
- package/src/utils/buffer-helper.ts +173 -0
- package/src/utils/cea-608-parser.ts +1413 -0
- package/src/utils/chunker.ts +41 -0
- package/src/utils/codecs.ts +314 -0
- package/src/utils/cues.ts +96 -0
- package/src/utils/discontinuities.ts +174 -0
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/error-helper.ts +95 -0
- package/src/utils/event-listener-helper.ts +16 -0
- package/src/utils/ewma-bandwidth-estimator.ts +97 -0
- package/src/utils/ewma.ts +43 -0
- package/src/utils/fetch-loader.ts +331 -0
- package/src/utils/global.ts +2 -0
- package/src/utils/hash.ts +10 -0
- package/src/utils/hdr.ts +67 -0
- package/src/utils/hex.ts +32 -0
- package/src/utils/imsc1-ttml-parser.ts +261 -0
- package/src/utils/keysystem-util.ts +45 -0
- package/src/utils/level-helper.ts +629 -0
- package/src/utils/logger.ts +120 -0
- package/src/utils/media-option-attributes.ts +49 -0
- package/src/utils/mediacapabilities-helper.ts +301 -0
- package/src/utils/mediakeys-helper.ts +210 -0
- package/src/utils/mediasource-helper.ts +37 -0
- package/src/utils/mp4-tools.ts +1473 -0
- package/src/utils/number.ts +3 -0
- package/src/utils/numeric-encoding-utils.ts +26 -0
- package/src/utils/output-filter.ts +46 -0
- package/src/utils/rendition-helper.ts +505 -0
- package/src/utils/safe-json-stringify.ts +22 -0
- package/src/utils/texttrack-utils.ts +164 -0
- package/src/utils/time-ranges.ts +17 -0
- package/src/utils/timescale-conversion.ts +46 -0
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/variable-substitution.ts +105 -0
- package/src/utils/vttcue.ts +384 -0
- package/src/utils/vttparser.ts +497 -0
- package/src/utils/webvtt-parser.ts +166 -0
- package/src/utils/xhr-loader.ts +337 -0
- package/src/version.ts +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Events } from '../events';
|
|
2
|
+
import type StreamController from './stream-controller';
|
|
3
|
+
import type Hls from '../hls';
|
|
4
|
+
import type { ComponentAPI } from '../types/component-api';
|
|
5
|
+
import type { MediaAttachingData } from '../types/events';
|
|
6
|
+
|
|
7
|
+
class FPSController implements ComponentAPI {
|
|
8
|
+
private hls: Hls;
|
|
9
|
+
private isVideoPlaybackQualityAvailable: boolean = false;
|
|
10
|
+
private timer?: number;
|
|
11
|
+
private media: HTMLVideoElement | null = null;
|
|
12
|
+
private lastTime: any;
|
|
13
|
+
private lastDroppedFrames: number = 0;
|
|
14
|
+
private lastDecodedFrames: number = 0;
|
|
15
|
+
// stream controller must be provided as a dependency!
|
|
16
|
+
private streamController!: StreamController;
|
|
17
|
+
|
|
18
|
+
constructor(hls: Hls) {
|
|
19
|
+
this.hls = hls;
|
|
20
|
+
|
|
21
|
+
this.registerListeners();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public setStreamController(streamController: StreamController) {
|
|
25
|
+
this.streamController = streamController;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected registerListeners() {
|
|
29
|
+
this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
|
|
30
|
+
this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected unregisterListeners() {
|
|
34
|
+
this.hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
|
|
35
|
+
this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
destroy() {
|
|
39
|
+
if (this.timer) {
|
|
40
|
+
clearInterval(this.timer);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.unregisterListeners();
|
|
44
|
+
this.isVideoPlaybackQualityAvailable = false;
|
|
45
|
+
this.media = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected onMediaAttaching(
|
|
49
|
+
event: Events.MEDIA_ATTACHING,
|
|
50
|
+
data: MediaAttachingData,
|
|
51
|
+
) {
|
|
52
|
+
const config = this.hls.config;
|
|
53
|
+
if (config.capLevelOnFPSDrop) {
|
|
54
|
+
const media =
|
|
55
|
+
data.media instanceof self.HTMLVideoElement ? data.media : null;
|
|
56
|
+
this.media = media;
|
|
57
|
+
if (media && typeof media.getVideoPlaybackQuality === 'function') {
|
|
58
|
+
this.isVideoPlaybackQualityAvailable = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
self.clearInterval(this.timer);
|
|
62
|
+
this.timer = self.setInterval(
|
|
63
|
+
this.checkFPSInterval.bind(this),
|
|
64
|
+
config.fpsDroppedMonitoringPeriod,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private onMediaDetaching() {
|
|
70
|
+
this.media = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
checkFPS(
|
|
74
|
+
video: HTMLVideoElement,
|
|
75
|
+
decodedFrames: number,
|
|
76
|
+
droppedFrames: number,
|
|
77
|
+
) {
|
|
78
|
+
const currentTime = performance.now();
|
|
79
|
+
if (decodedFrames) {
|
|
80
|
+
if (this.lastTime) {
|
|
81
|
+
const currentPeriod = currentTime - this.lastTime;
|
|
82
|
+
const currentDropped = droppedFrames - this.lastDroppedFrames;
|
|
83
|
+
const currentDecoded = decodedFrames - this.lastDecodedFrames;
|
|
84
|
+
const droppedFPS = (1000 * currentDropped) / currentPeriod;
|
|
85
|
+
const hls = this.hls;
|
|
86
|
+
hls.trigger(Events.FPS_DROP, {
|
|
87
|
+
currentDropped: currentDropped,
|
|
88
|
+
currentDecoded: currentDecoded,
|
|
89
|
+
totalDroppedFrames: droppedFrames,
|
|
90
|
+
});
|
|
91
|
+
if (droppedFPS > 0) {
|
|
92
|
+
// hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
|
|
93
|
+
if (
|
|
94
|
+
currentDropped >
|
|
95
|
+
hls.config.fpsDroppedMonitoringThreshold * currentDecoded
|
|
96
|
+
) {
|
|
97
|
+
let currentLevel = hls.currentLevel;
|
|
98
|
+
hls.logger.warn(
|
|
99
|
+
'drop FPS ratio greater than max allowed value for currentLevel: ' +
|
|
100
|
+
currentLevel,
|
|
101
|
+
);
|
|
102
|
+
if (
|
|
103
|
+
currentLevel > 0 &&
|
|
104
|
+
(hls.autoLevelCapping === -1 ||
|
|
105
|
+
hls.autoLevelCapping >= currentLevel)
|
|
106
|
+
) {
|
|
107
|
+
currentLevel = currentLevel - 1;
|
|
108
|
+
hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
|
|
109
|
+
level: currentLevel,
|
|
110
|
+
droppedLevel: hls.currentLevel,
|
|
111
|
+
});
|
|
112
|
+
hls.autoLevelCapping = currentLevel;
|
|
113
|
+
this.streamController.nextLevelSwitch();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.lastTime = currentTime;
|
|
119
|
+
this.lastDroppedFrames = droppedFrames;
|
|
120
|
+
this.lastDecodedFrames = decodedFrames;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
checkFPSInterval() {
|
|
125
|
+
const video = this.media;
|
|
126
|
+
if (video) {
|
|
127
|
+
if (this.isVideoPlaybackQualityAvailable) {
|
|
128
|
+
const videoPlaybackQuality = video.getVideoPlaybackQuality();
|
|
129
|
+
this.checkFPS(
|
|
130
|
+
video,
|
|
131
|
+
videoPlaybackQuality.totalVideoFrames,
|
|
132
|
+
videoPlaybackQuality.droppedVideoFrames,
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
// HTMLVideoElement doesn't include the webkit types
|
|
136
|
+
this.checkFPS(
|
|
137
|
+
video,
|
|
138
|
+
(video as any).webkitDecodedFrameCount as number,
|
|
139
|
+
(video as any).webkitDroppedFrameCount as number,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default FPSController;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import BinarySearch from '../utils/binary-search';
|
|
2
|
+
import type { Fragment, MediaFragment } from '../loader/fragment';
|
|
3
|
+
import type { LevelDetails } from '../loader/level-details';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns first fragment whose endPdt value exceeds the given PDT, or null.
|
|
7
|
+
* @param fragments - The array of candidate fragments
|
|
8
|
+
* @param PDTValue - The PDT value which must be exceeded
|
|
9
|
+
* @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous
|
|
10
|
+
*/
|
|
11
|
+
export function findFragmentByPDT(
|
|
12
|
+
fragments: MediaFragment[],
|
|
13
|
+
PDTValue: number | null,
|
|
14
|
+
maxFragLookUpTolerance: number,
|
|
15
|
+
): MediaFragment | null {
|
|
16
|
+
if (
|
|
17
|
+
PDTValue === null ||
|
|
18
|
+
!Array.isArray(fragments) ||
|
|
19
|
+
!fragments.length ||
|
|
20
|
+
!Number.isFinite(PDTValue)
|
|
21
|
+
) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// if less than start
|
|
26
|
+
const startPDT = fragments[0].programDateTime;
|
|
27
|
+
if (PDTValue < (startPDT || 0)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const endPDT = fragments[fragments.length - 1].endProgramDateTime;
|
|
32
|
+
if (PDTValue >= (endPDT || 0)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let seg = 0; seg < fragments.length; ++seg) {
|
|
37
|
+
const frag = fragments[seg];
|
|
38
|
+
if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) {
|
|
39
|
+
return frag;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer.
|
|
48
|
+
* This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus
|
|
49
|
+
* breaking any traps which would cause the same fragment to be continuously selected within a small range.
|
|
50
|
+
* @param fragPrevious - The last frag successfully appended
|
|
51
|
+
* @param fragments - The array of candidate fragments
|
|
52
|
+
* @param bufferEnd - The end of the contiguous buffered range the playhead is currently within
|
|
53
|
+
* @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous
|
|
54
|
+
* @returns a matching fragment or null
|
|
55
|
+
*/
|
|
56
|
+
export function findFragmentByPTS(
|
|
57
|
+
fragPrevious: MediaFragment | null,
|
|
58
|
+
fragments: MediaFragment[],
|
|
59
|
+
bufferEnd: number = 0,
|
|
60
|
+
maxFragLookUpTolerance: number = 0,
|
|
61
|
+
nextFragLookupTolerance: number = 0.005,
|
|
62
|
+
): MediaFragment | null {
|
|
63
|
+
let fragNext: MediaFragment | null = null;
|
|
64
|
+
if (fragPrevious) {
|
|
65
|
+
fragNext = fragments[1 + fragPrevious.sn - fragments[0].sn] || null;
|
|
66
|
+
// check for buffer-end rounding error
|
|
67
|
+
const bufferEdgeError = (fragPrevious.endDTS as number) - bufferEnd;
|
|
68
|
+
if (bufferEdgeError > 0 && bufferEdgeError < 0.0000015) {
|
|
69
|
+
bufferEnd += 0.0000015;
|
|
70
|
+
}
|
|
71
|
+
if (
|
|
72
|
+
fragNext &&
|
|
73
|
+
fragPrevious.level !== fragNext.level &&
|
|
74
|
+
fragNext.end <= fragPrevious.end
|
|
75
|
+
) {
|
|
76
|
+
fragNext = fragments[2 + fragPrevious.sn - fragments[0].sn] || null;
|
|
77
|
+
}
|
|
78
|
+
} else if (bufferEnd === 0 && fragments[0].start === 0) {
|
|
79
|
+
fragNext = fragments[0];
|
|
80
|
+
}
|
|
81
|
+
// Prefer the next fragment if it's within tolerance
|
|
82
|
+
if (
|
|
83
|
+
fragNext &&
|
|
84
|
+
(((!fragPrevious || fragPrevious.level === fragNext.level) &&
|
|
85
|
+
fragmentWithinToleranceTest(
|
|
86
|
+
bufferEnd,
|
|
87
|
+
maxFragLookUpTolerance,
|
|
88
|
+
fragNext,
|
|
89
|
+
) === 0) ||
|
|
90
|
+
fragmentWithinFastStartSwitch(
|
|
91
|
+
fragNext,
|
|
92
|
+
fragPrevious,
|
|
93
|
+
Math.min(nextFragLookupTolerance, maxFragLookUpTolerance),
|
|
94
|
+
))
|
|
95
|
+
) {
|
|
96
|
+
return fragNext;
|
|
97
|
+
}
|
|
98
|
+
// We might be seeking past the tolerance so find the best match
|
|
99
|
+
const foundFragment = BinarySearch.search(
|
|
100
|
+
fragments,
|
|
101
|
+
fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance),
|
|
102
|
+
);
|
|
103
|
+
if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) {
|
|
104
|
+
return foundFragment;
|
|
105
|
+
}
|
|
106
|
+
// If no match was found return the next fragment after fragPrevious, or null
|
|
107
|
+
return fragNext;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function fragmentWithinFastStartSwitch(
|
|
111
|
+
fragNext: Fragment,
|
|
112
|
+
fragPrevious: Fragment | null,
|
|
113
|
+
nextFragLookupTolerance: number,
|
|
114
|
+
): boolean {
|
|
115
|
+
if (
|
|
116
|
+
fragPrevious?.start === 0 &&
|
|
117
|
+
fragPrevious.level < fragNext.level &&
|
|
118
|
+
(fragPrevious.endPTS || 0) > 0
|
|
119
|
+
) {
|
|
120
|
+
const firstDuration = fragPrevious.tagList.reduce((duration, tag) => {
|
|
121
|
+
if (tag[0] === 'INF') {
|
|
122
|
+
duration += parseFloat(tag[1]);
|
|
123
|
+
}
|
|
124
|
+
return duration;
|
|
125
|
+
}, nextFragLookupTolerance);
|
|
126
|
+
return fragNext.start <= firstDuration;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions.
|
|
133
|
+
* @param candidate - The fragment to test
|
|
134
|
+
* @param bufferEnd - The end of the current buffered range the playhead is currently within
|
|
135
|
+
* @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous
|
|
136
|
+
* @returns 0 if it matches, 1 if too low, -1 if too high
|
|
137
|
+
*/
|
|
138
|
+
export function fragmentWithinToleranceTest(
|
|
139
|
+
bufferEnd = 0,
|
|
140
|
+
maxFragLookUpTolerance = 0,
|
|
141
|
+
candidate: MediaFragment,
|
|
142
|
+
) {
|
|
143
|
+
// eagerly accept an accurate match (no tolerance)
|
|
144
|
+
if (
|
|
145
|
+
candidate.start <= bufferEnd &&
|
|
146
|
+
candidate.start + candidate.duration > bufferEnd
|
|
147
|
+
) {
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
// offset should be within fragment boundary - config.maxFragLookUpTolerance
|
|
151
|
+
// this is to cope with situations like
|
|
152
|
+
// bufferEnd = 9.991
|
|
153
|
+
// frag[Ø] : [0,10]
|
|
154
|
+
// frag[1] : [10,20]
|
|
155
|
+
// bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
|
|
156
|
+
// frag start frag start+duration
|
|
157
|
+
// |-----------------------------|
|
|
158
|
+
// <---> <--->
|
|
159
|
+
// ...--------><-----------------------------><---------....
|
|
160
|
+
// previous frag matching fragment next frag
|
|
161
|
+
// return -1 return 0 return 1
|
|
162
|
+
// logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
|
|
163
|
+
// Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
|
|
164
|
+
const candidateLookupTolerance = Math.min(
|
|
165
|
+
maxFragLookUpTolerance,
|
|
166
|
+
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0),
|
|
167
|
+
);
|
|
168
|
+
if (
|
|
169
|
+
candidate.start + candidate.duration - candidateLookupTolerance <=
|
|
170
|
+
bufferEnd
|
|
171
|
+
) {
|
|
172
|
+
return 1;
|
|
173
|
+
} else if (
|
|
174
|
+
candidate.start - candidateLookupTolerance > bufferEnd &&
|
|
175
|
+
candidate.start
|
|
176
|
+
) {
|
|
177
|
+
// if maxFragLookUpTolerance will have negative value then don't return -1 for first element
|
|
178
|
+
return -1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions.
|
|
186
|
+
* This function tests the candidate's program date time values, as represented in Unix time
|
|
187
|
+
* @param candidate - The fragment to test
|
|
188
|
+
* @param pdtBufferEnd - The Unix time representing the end of the current buffered range
|
|
189
|
+
* @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous
|
|
190
|
+
* @returns true if contiguous, false otherwise
|
|
191
|
+
*/
|
|
192
|
+
export function pdtWithinToleranceTest(
|
|
193
|
+
pdtBufferEnd: number,
|
|
194
|
+
maxFragLookUpTolerance: number,
|
|
195
|
+
candidate: MediaFragment,
|
|
196
|
+
): boolean {
|
|
197
|
+
const candidateLookupTolerance =
|
|
198
|
+
Math.min(
|
|
199
|
+
maxFragLookUpTolerance,
|
|
200
|
+
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0),
|
|
201
|
+
) * 1000;
|
|
202
|
+
|
|
203
|
+
// endProgramDateTime can be null, default to zero
|
|
204
|
+
const endProgramDateTime = candidate.endProgramDateTime || 0;
|
|
205
|
+
return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function findFragWithCC(
|
|
209
|
+
fragments: MediaFragment[],
|
|
210
|
+
cc: number,
|
|
211
|
+
): MediaFragment | null {
|
|
212
|
+
return BinarySearch.search(fragments, (candidate) => {
|
|
213
|
+
if (candidate.cc < cc) {
|
|
214
|
+
return 1;
|
|
215
|
+
} else if (candidate.cc > cc) {
|
|
216
|
+
return -1;
|
|
217
|
+
} else {
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function findNearestWithCC(
|
|
224
|
+
details: LevelDetails | undefined,
|
|
225
|
+
cc: number,
|
|
226
|
+
pos: number,
|
|
227
|
+
): MediaFragment | null {
|
|
228
|
+
if (details) {
|
|
229
|
+
if (details.startCC <= cc && details.endCC >= cc) {
|
|
230
|
+
let fragments = details.fragments;
|
|
231
|
+
const { fragmentHint } = details;
|
|
232
|
+
if (fragmentHint) {
|
|
233
|
+
fragments = fragments.concat(fragmentHint);
|
|
234
|
+
}
|
|
235
|
+
let closest: MediaFragment | undefined;
|
|
236
|
+
BinarySearch.search(fragments, (candidate) => {
|
|
237
|
+
if (candidate.cc < cc) {
|
|
238
|
+
return 1;
|
|
239
|
+
}
|
|
240
|
+
if (candidate.cc > cc) {
|
|
241
|
+
return -1;
|
|
242
|
+
}
|
|
243
|
+
closest = candidate;
|
|
244
|
+
if (candidate.end <= pos) {
|
|
245
|
+
return 1;
|
|
246
|
+
}
|
|
247
|
+
if (candidate.start > pos) {
|
|
248
|
+
return -1;
|
|
249
|
+
}
|
|
250
|
+
return 0;
|
|
251
|
+
});
|
|
252
|
+
return closest || null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|