rx-player 4.5.0-dev.2026012302 → 4.5.0-dev.2026021900
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/CHANGELOG.md +9 -1
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/commonjs/compat/should_seek_at_zero.d.ts +12 -0
- package/dist/commonjs/compat/should_seek_at_zero.d.ts.map +1 -0
- package/dist/commonjs/compat/should_seek_at_zero.js +23 -0
- package/dist/commonjs/core/entry/FreezeResolver.js +2 -2
- package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
- package/dist/commonjs/core/entry/content_time_boundaries_observer.js +8 -0
- package/dist/commonjs/core/stream/representation/encryption_data_notifier.d.ts +70 -0
- package/dist/commonjs/core/stream/representation/encryption_data_notifier.d.ts.map +1 -0
- package/dist/commonjs/core/stream/representation/encryption_data_notifier.js +107 -0
- package/dist/commonjs/core/stream/representation/representation_stream.d.ts.map +1 -1
- package/dist/commonjs/core/stream/representation/representation_stream.js +12 -60
- package/dist/commonjs/core/stream/representation/utils/get_needed_segments.js +1 -1
- package/dist/commonjs/default_config.d.ts +5 -0
- package/dist/commonjs/default_config.d.ts.map +1 -1
- package/dist/commonjs/default_config.js +5 -0
- package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +27 -3
- package/dist/commonjs/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/directfile_content_initializer.js +23 -1
- package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.js +3 -1
- package/dist/commonjs/manifest/classes/index.d.ts +2 -1
- package/dist/commonjs/manifest/classes/index.d.ts.map +1 -1
- package/dist/commonjs/manifest/classes/representation.d.ts +1 -1
- package/dist/commonjs/manifest/classes/representation.d.ts.map +1 -1
- package/dist/commonjs/manifest/classes/representation.js +2 -2
- package/dist/commonjs/manifest/classes/representation_index/types.d.ts +2 -2
- package/dist/commonjs/utils/base64.d.ts +15 -1
- package/dist/commonjs/utils/base64.d.ts.map +1 -1
- package/dist/commonjs/utils/base64.js +6 -0
- package/dist/commonjs/utils/ranges.d.ts +1 -1
- package/dist/commonjs/utils/ranges.js +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/es2017/compat/should_seek_at_zero.d.ts +12 -0
- package/dist/es2017/compat/should_seek_at_zero.d.ts.map +1 -0
- package/dist/es2017/compat/should_seek_at_zero.js +20 -0
- package/dist/es2017/core/entry/FreezeResolver.js +1 -1
- package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
- package/dist/es2017/core/entry/content_time_boundaries_observer.js +8 -0
- package/dist/es2017/core/stream/representation/encryption_data_notifier.d.ts +70 -0
- package/dist/es2017/core/stream/representation/encryption_data_notifier.d.ts.map +1 -0
- package/dist/es2017/core/stream/representation/encryption_data_notifier.js +80 -0
- package/dist/es2017/core/stream/representation/representation_stream.d.ts.map +1 -1
- package/dist/es2017/core/stream/representation/representation_stream.js +10 -38
- package/dist/es2017/core/stream/representation/utils/get_needed_segments.js +1 -1
- package/dist/es2017/default_config.d.ts +5 -0
- package/dist/es2017/default_config.d.ts.map +1 -1
- package/dist/es2017/default_config.js +5 -0
- package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +27 -3
- package/dist/es2017/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/directfile_content_initializer.js +23 -1
- package/dist/es2017/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/utils/initial_seek_and_play.js +3 -1
- package/dist/es2017/manifest/classes/index.d.ts +2 -1
- package/dist/es2017/manifest/classes/index.d.ts.map +1 -1
- package/dist/es2017/manifest/classes/representation.d.ts +1 -1
- package/dist/es2017/manifest/classes/representation.d.ts.map +1 -1
- package/dist/es2017/manifest/classes/representation.js +1 -1
- package/dist/es2017/manifest/classes/representation_index/types.d.ts +2 -2
- package/dist/es2017/utils/base64.d.ts +15 -1
- package/dist/es2017/utils/base64.d.ts.map +1 -1
- package/dist/es2017/utils/base64.js +6 -0
- package/dist/es2017/utils/ranges.d.ts +1 -1
- package/dist/es2017/utils/ranges.js +1 -1
- package/dist/mpd-parser.wasm +0 -0
- package/dist/worker.js +6 -6
- package/package.json +9 -3
- package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
- package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
- package/src/compat/should_seek_at_zero.ts +23 -0
- package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +790 -0
- package/src/core/adaptive/__tests__/network_analyzer.test.ts +514 -0
- package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +285 -0
- package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +247 -0
- package/src/core/entry/FreezeResolver.ts +1 -1
- package/src/core/entry/__tests__/FreezeResolver.test.ts +542 -0
- package/src/core/entry/__tests__/content_time_boundaries_observer.test.ts +868 -0
- package/src/core/entry/content_time_boundaries_observer.ts +10 -0
- package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +592 -0
- package/src/core/stream/period/utils/__tests__/get_adaptation_switch_strategy.test.ts +646 -0
- package/src/core/stream/representation/__tests__/encryption_data_notifier.test.ts +531 -0
- package/src/core/stream/representation/encryption_data_notifier.ts +122 -0
- package/src/core/stream/representation/representation_stream.ts +11 -49
- package/src/core/stream/representation/utils/__tests__/append_segment_to_buffer.test.ts +321 -0
- package/src/core/stream/representation/utils/__tests__/check_for_discontinuity.test.ts +513 -0
- package/src/core/stream/representation/utils/__tests__/get_segment_priority.test.ts +132 -0
- package/src/core/stream/representation/utils/__tests__/push_init_segment.test.ts +209 -0
- package/src/core/stream/representation/utils/__tests__/push_media_segment.test.ts +343 -0
- package/src/core/stream/representation/utils/get_needed_segments.ts +1 -1
- package/src/default_config.ts +6 -0
- package/src/main_thread/api/public_api.ts +32 -3
- package/src/main_thread/init/directfile_content_initializer.ts +26 -1
- package/src/main_thread/init/utils/initial_seek_and_play.ts +5 -1
- package/src/manifest/classes/index.ts +2 -0
- package/src/manifest/classes/representation.ts +2 -1
- package/src/manifest/classes/representation_index/types.ts +2 -2
- package/src/utils/base64.ts +30 -1
- package/src/utils/ranges.ts +1 -1
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import NetworkAnalyzer, { estimateRequestBandwidth } from "../network_analyzer";
|
|
3
|
+
import type { IRequestInfo } from "../utils/pending_requests_store";
|
|
4
|
+
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
8
|
+
|
|
9
|
+
vi.mock("../../../config", () => ({
|
|
10
|
+
default: {
|
|
11
|
+
getCurrent: vi.fn(() => ({
|
|
12
|
+
ABR_STARVATION_GAP: {
|
|
13
|
+
DEFAULT: 2,
|
|
14
|
+
LOW_LATENCY: 1,
|
|
15
|
+
},
|
|
16
|
+
OUT_OF_STARVATION_GAP: {
|
|
17
|
+
DEFAULT: 3,
|
|
18
|
+
LOW_LATENCY: 1.5,
|
|
19
|
+
},
|
|
20
|
+
ABR_STARVATION_FACTOR: {
|
|
21
|
+
DEFAULT: 0.72,
|
|
22
|
+
LOW_LATENCY: 0.8,
|
|
23
|
+
},
|
|
24
|
+
ABR_REGULAR_FACTOR: {
|
|
25
|
+
DEFAULT: 0.8,
|
|
26
|
+
LOW_LATENCY: 0.85,
|
|
27
|
+
},
|
|
28
|
+
ABR_STARVATION_DURATION_DELTA: 5,
|
|
29
|
+
})),
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
vi.mock("../../../log", () => ({
|
|
34
|
+
default: {
|
|
35
|
+
info: vi.fn(),
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
vi.mock("../../../utils/monotonic_timestamp", () => ({
|
|
40
|
+
// eslint-disable-next-line no-restricted-properties
|
|
41
|
+
default: vi.fn(() => performance.now()),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
function createMockRequest(
|
|
45
|
+
segmentTime: number,
|
|
46
|
+
segmentDuration: number,
|
|
47
|
+
requestTimestamp: number,
|
|
48
|
+
progress: Array<{ size: number; timestamp: number; totalSize: number }> = [],
|
|
49
|
+
complete = true,
|
|
50
|
+
): IRequestInfo {
|
|
51
|
+
return {
|
|
52
|
+
content: {
|
|
53
|
+
segment: {
|
|
54
|
+
time: segmentTime,
|
|
55
|
+
duration: segmentDuration,
|
|
56
|
+
complete,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
requestTimestamp,
|
|
60
|
+
progress,
|
|
61
|
+
} as IRequestInfo;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function createMockPlaybackInfo(
|
|
65
|
+
bufferGap: number,
|
|
66
|
+
position: number,
|
|
67
|
+
speed = 1,
|
|
68
|
+
duration = 100,
|
|
69
|
+
): any {
|
|
70
|
+
return {
|
|
71
|
+
bufferGap,
|
|
72
|
+
position: { getWanted: () => position },
|
|
73
|
+
speed,
|
|
74
|
+
duration,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function createMockBandwidthEstimator(estimate?: number): any {
|
|
79
|
+
return {
|
|
80
|
+
getEstimate: vi.fn(() => estimate),
|
|
81
|
+
reset: vi.fn(),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("estimateRequestBandwidth", () => {
|
|
86
|
+
it("should return undefined if progress events are less than 5", () => {
|
|
87
|
+
// eslint-disable-next-line no-restricted-properties
|
|
88
|
+
const request = createMockRequest(0, 4, performance.now(), [
|
|
89
|
+
{ size: 1000, timestamp: 1000, totalSize: 10000 },
|
|
90
|
+
{ size: 2000, timestamp: 2000, totalSize: 10000 },
|
|
91
|
+
]);
|
|
92
|
+
expect(estimateRequestBandwidth(request)).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should calculate bandwidth estimate from progress events", () => {
|
|
96
|
+
const baseTime = 1000;
|
|
97
|
+
const request = createMockRequest(0, 4, baseTime, [
|
|
98
|
+
{ size: 0, timestamp: baseTime, totalSize: 10000 },
|
|
99
|
+
{ size: 1000, timestamp: baseTime + 100, totalSize: 10000 },
|
|
100
|
+
{ size: 2000, timestamp: baseTime + 200, totalSize: 10000 },
|
|
101
|
+
{ size: 3000, timestamp: baseTime + 300, totalSize: 10000 },
|
|
102
|
+
{ size: 4000, timestamp: baseTime + 400, totalSize: 10000 },
|
|
103
|
+
{ size: 5000, timestamp: baseTime + 500, totalSize: 10000 },
|
|
104
|
+
]);
|
|
105
|
+
const estimate = estimateRequestBandwidth(request);
|
|
106
|
+
expect(estimate).toBeDefined();
|
|
107
|
+
expect(typeof estimate).toBe("number");
|
|
108
|
+
expect(estimate).toBeGreaterThan(50000);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should handle varying download speeds", () => {
|
|
112
|
+
const baseTime = 1000;
|
|
113
|
+
const request = createMockRequest(0, 4, baseTime, [
|
|
114
|
+
{ size: 0, timestamp: baseTime, totalSize: 20000 },
|
|
115
|
+
{ size: 2000, timestamp: baseTime + 100, totalSize: 20000 },
|
|
116
|
+
{ size: 4000, timestamp: baseTime + 200, totalSize: 20000 },
|
|
117
|
+
{ size: 5000, timestamp: baseTime + 400, totalSize: 20000 }, // Slower
|
|
118
|
+
{ size: 6000, timestamp: baseTime + 600, totalSize: 20000 }, // Slower
|
|
119
|
+
{ size: 7000, timestamp: baseTime + 800, totalSize: 20000 },
|
|
120
|
+
]);
|
|
121
|
+
const estimate = estimateRequestBandwidth(request);
|
|
122
|
+
expect(estimate).toBeDefined();
|
|
123
|
+
expect(estimate).toBeGreaterThan(50000);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("NetworkAnalyzer", () => {
|
|
128
|
+
describe("constructor", () => {
|
|
129
|
+
it("should initialize with default mode configuration", () => {
|
|
130
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
131
|
+
expect(analyzer).toBeDefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should initialize with low latency mode configuration", () => {
|
|
135
|
+
const analyzer = new NetworkAnalyzer(1000000, true);
|
|
136
|
+
expect(analyzer).toBeDefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("getBandwidthEstimate", () => {
|
|
141
|
+
it("should return initial bitrate when no bandwidth estimate is available", () => {
|
|
142
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
143
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
144
|
+
const bandwidthEstimator = createMockBandwidthEstimator(undefined);
|
|
145
|
+
|
|
146
|
+
const result = analyzer.getBandwidthEstimate(
|
|
147
|
+
playbackInfo,
|
|
148
|
+
bandwidthEstimator,
|
|
149
|
+
null,
|
|
150
|
+
[],
|
|
151
|
+
undefined,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
expect(result.bitrateChosen).toBe(1000000);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("should apply regular factor to bandwidth estimate in normal mode", () => {
|
|
158
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
159
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
160
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
161
|
+
|
|
162
|
+
const result = analyzer.getBandwidthEstimate(
|
|
163
|
+
playbackInfo,
|
|
164
|
+
bandwidthEstimator,
|
|
165
|
+
null,
|
|
166
|
+
[],
|
|
167
|
+
undefined,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Should apply ABR_REGULAR_FACTOR.DEFAULT (0.8)
|
|
171
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
172
|
+
expect(result.bandwidthEstimate).toBe(2000000);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should enter starvation mode when buffer gap is low", () => {
|
|
176
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
177
|
+
const playbackInfo = createMockPlaybackInfo(1, 10); // Low buffer gap
|
|
178
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
179
|
+
|
|
180
|
+
const result = analyzer.getBandwidthEstimate(
|
|
181
|
+
playbackInfo,
|
|
182
|
+
bandwidthEstimator,
|
|
183
|
+
null,
|
|
184
|
+
[],
|
|
185
|
+
undefined,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Should apply ABR_STARVATION_FACTOR.DEFAULT (0.72)
|
|
189
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.72);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should exit starvation mode when buffer gap increases", () => {
|
|
193
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
194
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
195
|
+
|
|
196
|
+
// Enter starvation mode
|
|
197
|
+
analyzer.getBandwidthEstimate(
|
|
198
|
+
createMockPlaybackInfo(1, 10),
|
|
199
|
+
bandwidthEstimator,
|
|
200
|
+
null,
|
|
201
|
+
[],
|
|
202
|
+
undefined,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Exit starvation mode
|
|
206
|
+
const result = analyzer.getBandwidthEstimate(
|
|
207
|
+
createMockPlaybackInfo(5, 10),
|
|
208
|
+
bandwidthEstimator,
|
|
209
|
+
null,
|
|
210
|
+
[],
|
|
211
|
+
undefined,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Should apply regular factor again (0.8)
|
|
215
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("should adjust bitrate based on playback speed", () => {
|
|
219
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
220
|
+
const playbackInfo = createMockPlaybackInfo(5, 0, 2); // 2x speed
|
|
221
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
222
|
+
|
|
223
|
+
const result = analyzer.getBandwidthEstimate(
|
|
224
|
+
playbackInfo,
|
|
225
|
+
bandwidthEstimator,
|
|
226
|
+
null,
|
|
227
|
+
[],
|
|
228
|
+
undefined,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Should divide by speed: (2000000 * 0.8) / 2
|
|
232
|
+
expect(result.bitrateChosen).toBe((2000000 * 0.8) / 2);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("should use last estimated bitrate when no new estimate available", () => {
|
|
236
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
237
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
238
|
+
const bandwidthEstimator = createMockBandwidthEstimator(undefined);
|
|
239
|
+
|
|
240
|
+
const result = analyzer.getBandwidthEstimate(
|
|
241
|
+
playbackInfo,
|
|
242
|
+
bandwidthEstimator,
|
|
243
|
+
null,
|
|
244
|
+
[],
|
|
245
|
+
1500000, // Last estimated bitrate
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(result.bitrateChosen).toBe(1500000 * 0.8);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should exit starvation mode near end of content", () => {
|
|
252
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
253
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
254
|
+
|
|
255
|
+
// Enter starvation mode
|
|
256
|
+
analyzer.getBandwidthEstimate(
|
|
257
|
+
createMockPlaybackInfo(1, 10, 1, 100),
|
|
258
|
+
bandwidthEstimator,
|
|
259
|
+
null,
|
|
260
|
+
[],
|
|
261
|
+
undefined,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// Near end of content (position + buffer gap close to duration)
|
|
265
|
+
const result = analyzer.getBandwidthEstimate(
|
|
266
|
+
createMockPlaybackInfo(1, 96, 1, 100), // 96 + 1 = 97, close to 100
|
|
267
|
+
bandwidthEstimator,
|
|
268
|
+
null,
|
|
269
|
+
[],
|
|
270
|
+
undefined,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Should exit starvation mode and use regular factor
|
|
274
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("should handle infinite buffer gap", () => {
|
|
278
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
279
|
+
const playbackInfo = createMockPlaybackInfo(Infinity, 0);
|
|
280
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
281
|
+
|
|
282
|
+
const result = analyzer.getBandwidthEstimate(
|
|
283
|
+
playbackInfo,
|
|
284
|
+
bandwidthEstimator,
|
|
285
|
+
null,
|
|
286
|
+
[],
|
|
287
|
+
undefined,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Should treat infinite as 0 and apply starvation factor
|
|
291
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.72);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should use different thresholds in low latency mode", () => {
|
|
295
|
+
const analyzer = new NetworkAnalyzer(1000000, true);
|
|
296
|
+
const playbackInfo = createMockPlaybackInfo(0.5, 10); // Would enter starvation in low latency
|
|
297
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
298
|
+
|
|
299
|
+
const result = analyzer.getBandwidthEstimate(
|
|
300
|
+
playbackInfo,
|
|
301
|
+
bandwidthEstimator,
|
|
302
|
+
null,
|
|
303
|
+
[],
|
|
304
|
+
undefined,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// Should use low latency starvation factor (0.8)
|
|
308
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("isUrgent", () => {
|
|
313
|
+
it("should return true when no current representation", () => {
|
|
314
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
315
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
316
|
+
|
|
317
|
+
const result = analyzer.isUrgent(500000, null, [], playbackInfo);
|
|
318
|
+
|
|
319
|
+
expect(result).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should return false when bitrate is higher than current representation", () => {
|
|
323
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
324
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
325
|
+
const currentRepresentation = { bitrate: 1000000 } as any;
|
|
326
|
+
|
|
327
|
+
const result = analyzer.isUrgent(2000000, currentRepresentation, [], playbackInfo);
|
|
328
|
+
|
|
329
|
+
expect(result).toBe(false);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("should return true in low latency mode when switching to lower bitrate", () => {
|
|
333
|
+
const analyzer = new NetworkAnalyzer(1000000, true);
|
|
334
|
+
const playbackInfo = createMockPlaybackInfo(5, 0);
|
|
335
|
+
const currentRepresentation = { bitrate: 2000000 } as any;
|
|
336
|
+
|
|
337
|
+
const result = analyzer.isUrgent(1000000, currentRepresentation, [], playbackInfo);
|
|
338
|
+
|
|
339
|
+
expect(result).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should return true when no pending requests for lower bitrate", () => {
|
|
343
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
344
|
+
const playbackInfo = createMockPlaybackInfo(5, 10);
|
|
345
|
+
const currentRepresentation = { bitrate: 2000000 } as any;
|
|
346
|
+
|
|
347
|
+
const result = analyzer.isUrgent(1000000, currentRepresentation, [], playbackInfo);
|
|
348
|
+
|
|
349
|
+
expect(result).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("should evaluate urgency based on pending request progress", () => {
|
|
353
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
354
|
+
// eslint-disable-next-line no-restricted-properties
|
|
355
|
+
const now = performance.now();
|
|
356
|
+
const playbackInfo = createMockPlaybackInfo(2, 10);
|
|
357
|
+
const currentRepresentation = { bitrate: 2000000 } as any;
|
|
358
|
+
|
|
359
|
+
const request = createMockRequest(10, 4, now - 1000, [
|
|
360
|
+
{ size: 0, timestamp: now - 1000, totalSize: 10000 },
|
|
361
|
+
{ size: 2000, timestamp: now - 800, totalSize: 10000 },
|
|
362
|
+
{ size: 4000, timestamp: now - 600, totalSize: 10000 },
|
|
363
|
+
{ size: 6000, timestamp: now - 400, totalSize: 10000 },
|
|
364
|
+
{ size: 8000, timestamp: now - 200, totalSize: 10000 },
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
const result = analyzer.isUrgent(
|
|
368
|
+
1000000,
|
|
369
|
+
currentRepresentation,
|
|
370
|
+
[request],
|
|
371
|
+
playbackInfo,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(typeof result).toBe("boolean");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("should handle requests with zero duration segments", () => {
|
|
378
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
379
|
+
const playbackInfo = createMockPlaybackInfo(5, 10);
|
|
380
|
+
const currentRepresentation = { bitrate: 2000000 } as any;
|
|
381
|
+
|
|
382
|
+
// eslint-disable-next-line no-restricted-properties
|
|
383
|
+
const request = createMockRequest(10, 0, performance.now()); // Zero duration
|
|
384
|
+
|
|
385
|
+
const result = analyzer.isUrgent(
|
|
386
|
+
1000000,
|
|
387
|
+
currentRepresentation,
|
|
388
|
+
[request],
|
|
389
|
+
playbackInfo,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(result).toBe(true);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should handle infinite buffer gap in urgency check", () => {
|
|
396
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
397
|
+
const playbackInfo = createMockPlaybackInfo(Infinity, 10);
|
|
398
|
+
const currentRepresentation = { bitrate: 2000000 } as any;
|
|
399
|
+
|
|
400
|
+
const result = analyzer.isUrgent(1000000, currentRepresentation, [], playbackInfo);
|
|
401
|
+
|
|
402
|
+
expect(typeof result).toBe("boolean");
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe("integration scenarios", () => {
|
|
407
|
+
it("should handle typical playback progression", () => {
|
|
408
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
409
|
+
const bandwidthEstimator = createMockBandwidthEstimator(2000000);
|
|
410
|
+
|
|
411
|
+
// Initial state - good buffer
|
|
412
|
+
let result = analyzer.getBandwidthEstimate(
|
|
413
|
+
createMockPlaybackInfo(10, 0),
|
|
414
|
+
bandwidthEstimator,
|
|
415
|
+
null,
|
|
416
|
+
[],
|
|
417
|
+
undefined,
|
|
418
|
+
);
|
|
419
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
420
|
+
|
|
421
|
+
// Buffer depleting - entering starvation
|
|
422
|
+
result = analyzer.getBandwidthEstimate(
|
|
423
|
+
createMockPlaybackInfo(1.5, 20),
|
|
424
|
+
bandwidthEstimator,
|
|
425
|
+
{ bitrate: 1600000 } as any,
|
|
426
|
+
[],
|
|
427
|
+
result.bitrateChosen,
|
|
428
|
+
);
|
|
429
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.72);
|
|
430
|
+
|
|
431
|
+
// Buffer recovering - exiting starvation
|
|
432
|
+
result = analyzer.getBandwidthEstimate(
|
|
433
|
+
createMockPlaybackInfo(4, 30),
|
|
434
|
+
bandwidthEstimator,
|
|
435
|
+
{ bitrate: 1440000 } as any,
|
|
436
|
+
[],
|
|
437
|
+
result.bitrateChosen,
|
|
438
|
+
);
|
|
439
|
+
expect(result.bitrateChosen).toBe(2000000 * 0.8);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("should handle bandwidth fluctuations", () => {
|
|
443
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
444
|
+
|
|
445
|
+
// Start with good bandwidth
|
|
446
|
+
let estimator = createMockBandwidthEstimator(3000000);
|
|
447
|
+
let result = analyzer.getBandwidthEstimate(
|
|
448
|
+
createMockPlaybackInfo(5, 0),
|
|
449
|
+
estimator,
|
|
450
|
+
null,
|
|
451
|
+
[],
|
|
452
|
+
undefined,
|
|
453
|
+
);
|
|
454
|
+
expect(result.bitrateChosen).toBe(3000000 * 0.8);
|
|
455
|
+
|
|
456
|
+
// Bandwidth drops
|
|
457
|
+
estimator = createMockBandwidthEstimator(1000000);
|
|
458
|
+
result = analyzer.getBandwidthEstimate(
|
|
459
|
+
createMockPlaybackInfo(5, 10),
|
|
460
|
+
estimator,
|
|
461
|
+
{ bitrate: 2400000 } as any,
|
|
462
|
+
[],
|
|
463
|
+
result.bitrateChosen,
|
|
464
|
+
);
|
|
465
|
+
expect(result.bitrateChosen).toBe(1000000 * 0.8);
|
|
466
|
+
|
|
467
|
+
// Bandwidth recovers
|
|
468
|
+
estimator = createMockBandwidthEstimator(2500000);
|
|
469
|
+
result = analyzer.getBandwidthEstimate(
|
|
470
|
+
createMockPlaybackInfo(5, 20),
|
|
471
|
+
estimator,
|
|
472
|
+
{ bitrate: 800000 } as any,
|
|
473
|
+
[],
|
|
474
|
+
result.bitrateChosen,
|
|
475
|
+
);
|
|
476
|
+
expect(result.bitrateChosen).toBe(2500000 * 0.8);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("should handle speed changes during playback", () => {
|
|
480
|
+
const analyzer = new NetworkAnalyzer(1000000, false);
|
|
481
|
+
const estimator = createMockBandwidthEstimator(2000000);
|
|
482
|
+
|
|
483
|
+
// Normal speed
|
|
484
|
+
let result = analyzer.getBandwidthEstimate(
|
|
485
|
+
createMockPlaybackInfo(5, 0, 1),
|
|
486
|
+
estimator,
|
|
487
|
+
null,
|
|
488
|
+
[],
|
|
489
|
+
undefined,
|
|
490
|
+
);
|
|
491
|
+
const normalBitrate = result.bitrateChosen;
|
|
492
|
+
|
|
493
|
+
// 2x speed - should halve the bitrate
|
|
494
|
+
result = analyzer.getBandwidthEstimate(
|
|
495
|
+
createMockPlaybackInfo(5, 10, 2),
|
|
496
|
+
estimator,
|
|
497
|
+
{ bitrate: normalBitrate } as any,
|
|
498
|
+
[],
|
|
499
|
+
result.bitrateChosen,
|
|
500
|
+
);
|
|
501
|
+
expect(result.bitrateChosen).toBe(normalBitrate / 2);
|
|
502
|
+
|
|
503
|
+
// 0.5x speed - keep regular bitrate
|
|
504
|
+
result = analyzer.getBandwidthEstimate(
|
|
505
|
+
createMockPlaybackInfo(5, 20, 0.5),
|
|
506
|
+
estimator,
|
|
507
|
+
{ bitrate: normalBitrate } as any,
|
|
508
|
+
[],
|
|
509
|
+
result.bitrateChosen,
|
|
510
|
+
);
|
|
511
|
+
expect(result.bitrateChosen).toBe(normalBitrate);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
});
|