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.
Files changed (108) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/VERSION +1 -1
  3. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  5. package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  6. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  7. package/dist/commonjs/compat/should_seek_at_zero.d.ts +12 -0
  8. package/dist/commonjs/compat/should_seek_at_zero.d.ts.map +1 -0
  9. package/dist/commonjs/compat/should_seek_at_zero.js +23 -0
  10. package/dist/commonjs/core/entry/FreezeResolver.js +2 -2
  11. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  12. package/dist/commonjs/core/entry/content_time_boundaries_observer.js +8 -0
  13. package/dist/commonjs/core/stream/representation/encryption_data_notifier.d.ts +70 -0
  14. package/dist/commonjs/core/stream/representation/encryption_data_notifier.d.ts.map +1 -0
  15. package/dist/commonjs/core/stream/representation/encryption_data_notifier.js +107 -0
  16. package/dist/commonjs/core/stream/representation/representation_stream.d.ts.map +1 -1
  17. package/dist/commonjs/core/stream/representation/representation_stream.js +12 -60
  18. package/dist/commonjs/core/stream/representation/utils/get_needed_segments.js +1 -1
  19. package/dist/commonjs/default_config.d.ts +5 -0
  20. package/dist/commonjs/default_config.d.ts.map +1 -1
  21. package/dist/commonjs/default_config.js +5 -0
  22. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  23. package/dist/commonjs/main_thread/api/public_api.js +27 -3
  24. package/dist/commonjs/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
  25. package/dist/commonjs/main_thread/init/directfile_content_initializer.js +23 -1
  26. package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
  27. package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.js +3 -1
  28. package/dist/commonjs/manifest/classes/index.d.ts +2 -1
  29. package/dist/commonjs/manifest/classes/index.d.ts.map +1 -1
  30. package/dist/commonjs/manifest/classes/representation.d.ts +1 -1
  31. package/dist/commonjs/manifest/classes/representation.d.ts.map +1 -1
  32. package/dist/commonjs/manifest/classes/representation.js +2 -2
  33. package/dist/commonjs/manifest/classes/representation_index/types.d.ts +2 -2
  34. package/dist/commonjs/utils/base64.d.ts +15 -1
  35. package/dist/commonjs/utils/base64.d.ts.map +1 -1
  36. package/dist/commonjs/utils/base64.js +6 -0
  37. package/dist/commonjs/utils/ranges.d.ts +1 -1
  38. package/dist/commonjs/utils/ranges.js +1 -1
  39. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  40. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  41. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  42. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  43. package/dist/es2017/compat/should_seek_at_zero.d.ts +12 -0
  44. package/dist/es2017/compat/should_seek_at_zero.d.ts.map +1 -0
  45. package/dist/es2017/compat/should_seek_at_zero.js +20 -0
  46. package/dist/es2017/core/entry/FreezeResolver.js +1 -1
  47. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  48. package/dist/es2017/core/entry/content_time_boundaries_observer.js +8 -0
  49. package/dist/es2017/core/stream/representation/encryption_data_notifier.d.ts +70 -0
  50. package/dist/es2017/core/stream/representation/encryption_data_notifier.d.ts.map +1 -0
  51. package/dist/es2017/core/stream/representation/encryption_data_notifier.js +80 -0
  52. package/dist/es2017/core/stream/representation/representation_stream.d.ts.map +1 -1
  53. package/dist/es2017/core/stream/representation/representation_stream.js +10 -38
  54. package/dist/es2017/core/stream/representation/utils/get_needed_segments.js +1 -1
  55. package/dist/es2017/default_config.d.ts +5 -0
  56. package/dist/es2017/default_config.d.ts.map +1 -1
  57. package/dist/es2017/default_config.js +5 -0
  58. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  59. package/dist/es2017/main_thread/api/public_api.js +27 -3
  60. package/dist/es2017/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
  61. package/dist/es2017/main_thread/init/directfile_content_initializer.js +23 -1
  62. package/dist/es2017/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
  63. package/dist/es2017/main_thread/init/utils/initial_seek_and_play.js +3 -1
  64. package/dist/es2017/manifest/classes/index.d.ts +2 -1
  65. package/dist/es2017/manifest/classes/index.d.ts.map +1 -1
  66. package/dist/es2017/manifest/classes/representation.d.ts +1 -1
  67. package/dist/es2017/manifest/classes/representation.d.ts.map +1 -1
  68. package/dist/es2017/manifest/classes/representation.js +1 -1
  69. package/dist/es2017/manifest/classes/representation_index/types.d.ts +2 -2
  70. package/dist/es2017/utils/base64.d.ts +15 -1
  71. package/dist/es2017/utils/base64.d.ts.map +1 -1
  72. package/dist/es2017/utils/base64.js +6 -0
  73. package/dist/es2017/utils/ranges.d.ts +1 -1
  74. package/dist/es2017/utils/ranges.js +1 -1
  75. package/dist/mpd-parser.wasm +0 -0
  76. package/dist/worker.js +6 -6
  77. package/package.json +9 -3
  78. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  79. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  80. package/src/compat/should_seek_at_zero.ts +23 -0
  81. package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +790 -0
  82. package/src/core/adaptive/__tests__/network_analyzer.test.ts +514 -0
  83. package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +285 -0
  84. package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +247 -0
  85. package/src/core/entry/FreezeResolver.ts +1 -1
  86. package/src/core/entry/__tests__/FreezeResolver.test.ts +542 -0
  87. package/src/core/entry/__tests__/content_time_boundaries_observer.test.ts +868 -0
  88. package/src/core/entry/content_time_boundaries_observer.ts +10 -0
  89. package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +592 -0
  90. package/src/core/stream/period/utils/__tests__/get_adaptation_switch_strategy.test.ts +646 -0
  91. package/src/core/stream/representation/__tests__/encryption_data_notifier.test.ts +531 -0
  92. package/src/core/stream/representation/encryption_data_notifier.ts +122 -0
  93. package/src/core/stream/representation/representation_stream.ts +11 -49
  94. package/src/core/stream/representation/utils/__tests__/append_segment_to_buffer.test.ts +321 -0
  95. package/src/core/stream/representation/utils/__tests__/check_for_discontinuity.test.ts +513 -0
  96. package/src/core/stream/representation/utils/__tests__/get_segment_priority.test.ts +132 -0
  97. package/src/core/stream/representation/utils/__tests__/push_init_segment.test.ts +209 -0
  98. package/src/core/stream/representation/utils/__tests__/push_media_segment.test.ts +343 -0
  99. package/src/core/stream/representation/utils/get_needed_segments.ts +1 -1
  100. package/src/default_config.ts +6 -0
  101. package/src/main_thread/api/public_api.ts +32 -3
  102. package/src/main_thread/init/directfile_content_initializer.ts +26 -1
  103. package/src/main_thread/init/utils/initial_seek_and_play.ts +5 -1
  104. package/src/manifest/classes/index.ts +2 -0
  105. package/src/manifest/classes/representation.ts +2 -1
  106. package/src/manifest/classes/representation_index/types.ts +2 -2
  107. package/src/utils/base64.ts +30 -1
  108. 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
+ });