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,285 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import PendingRequestsStore from "../pending_requests_store";
3
+ import type {
4
+ IPendingRequestStoreBegin,
5
+ IPendingRequestStoreProgress,
6
+ } from "../pending_requests_store";
7
+
8
+ /* eslint-disable @typescript-eslint/no-explicit-any */
9
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
10
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
11
+
12
+ const mocks = vi.hoisted(() => ({
13
+ warn: vi.fn(),
14
+ }));
15
+
16
+ // Mock dependencies
17
+ vi.mock("../../../../log", () => ({
18
+ default: {
19
+ warn: mocks.warn,
20
+ },
21
+ }));
22
+
23
+ describe("PendingRequestsStore", () => {
24
+ let store: PendingRequestsStore;
25
+
26
+ const createMockContent = (segmentTime: number = 0) => ({
27
+ manifest: {} as any,
28
+ period: {} as any,
29
+ adaptation: {} as any,
30
+ representation: {} as any,
31
+ segment: { time: segmentTime } as any,
32
+ });
33
+
34
+ const createMockRequest = (
35
+ id: string = "req-1",
36
+ requestTimestamp: number = 1000,
37
+ segmentTime: number = 0,
38
+ ): IPendingRequestStoreBegin => ({
39
+ id,
40
+ requestTimestamp,
41
+ content: createMockContent(segmentTime),
42
+ });
43
+
44
+ const createMockProgress = (
45
+ id: string = "req-1",
46
+ duration: number = 0.5,
47
+ size: number = 1024,
48
+ totalSize: number = 2048,
49
+ timestamp: number = 1500,
50
+ ): IPendingRequestStoreProgress => ({
51
+ id,
52
+ duration,
53
+ size,
54
+ totalSize,
55
+ timestamp,
56
+ });
57
+
58
+ beforeEach(() => {
59
+ store = new PendingRequestsStore();
60
+ vi.clearAllMocks();
61
+ });
62
+
63
+ describe("add", () => {
64
+ it("should add a new pending request", () => {
65
+ const request = createMockRequest();
66
+
67
+ store.add(request);
68
+
69
+ const requests = store.getRequests();
70
+ expect(requests).toHaveLength(1);
71
+ expect(requests[0]).toEqual({
72
+ requestTimestamp: request.requestTimestamp,
73
+ progress: [],
74
+ content: request.content,
75
+ });
76
+ });
77
+
78
+ it("should add multiple pending requests", () => {
79
+ const request1 = createMockRequest("req-1", 1000);
80
+ const request2 = createMockRequest("req-2", 2000);
81
+
82
+ store.add(request1);
83
+ store.add(request2);
84
+
85
+ const requests = store.getRequests();
86
+ expect(requests).toHaveLength(2);
87
+ });
88
+
89
+ it("should initialize progress as empty array", () => {
90
+ const request = createMockRequest();
91
+
92
+ store.add(request);
93
+
94
+ const requests = store.getRequests();
95
+ expect(requests[0].progress).toEqual([]);
96
+ });
97
+ });
98
+
99
+ describe("addProgress", () => {
100
+ it("should add progress to an existing request", () => {
101
+ const request = createMockRequest("req-1");
102
+ const progress = createMockProgress("req-1");
103
+
104
+ store.add(request);
105
+ store.addProgress(progress);
106
+
107
+ const requests = store.getRequests();
108
+ expect(requests[0].progress).toHaveLength(1);
109
+ expect(requests[0].progress[0]).toEqual(progress);
110
+ });
111
+
112
+ it("should add multiple progress updates to the same request", () => {
113
+ const request = createMockRequest("req-1");
114
+ const progress1 = createMockProgress("req-1", 0.5, 1024);
115
+ const progress2 = createMockProgress("req-1", 1.0, 2048);
116
+
117
+ store.add(request);
118
+ store.addProgress(progress1);
119
+ store.addProgress(progress2);
120
+
121
+ const requests = store.getRequests();
122
+ expect(requests[0].progress).toHaveLength(2);
123
+ expect(requests[0].progress[0]).toEqual(progress1);
124
+ expect(requests[0].progress[1]).toEqual(progress2);
125
+ });
126
+
127
+ it("should throw error when adding progress for non-existent request", () => {
128
+ const progress = createMockProgress("unknown-id");
129
+
130
+ expect(() => store.addProgress(progress)).toThrow(
131
+ "ABR: progress for a request not added",
132
+ );
133
+ });
134
+ });
135
+
136
+ describe("remove", () => {
137
+ it("should remove an existing request", () => {
138
+ const request = createMockRequest("req-1");
139
+
140
+ store.add(request);
141
+ expect(store.getRequests()).toHaveLength(1);
142
+
143
+ store.remove("req-1");
144
+ expect(store.getRequests()).toHaveLength(0);
145
+ });
146
+
147
+ it("should only remove the specified request", () => {
148
+ const request1 = createMockRequest("req-1");
149
+ const request2 = createMockRequest("req-2");
150
+
151
+ store.add(request1);
152
+ store.add(request2);
153
+
154
+ store.remove("req-1");
155
+
156
+ const requests = store.getRequests();
157
+ expect(requests).toHaveLength(1);
158
+ expect(requests[0].content).toBe(request2.content);
159
+ });
160
+
161
+ it("should throw error when removing non-existent request", () => {
162
+ expect(() => store.remove("unknown-id")).toThrow(
163
+ "ABR: can't remove unknown request",
164
+ );
165
+ });
166
+ });
167
+
168
+ describe("getRequests", () => {
169
+ it("should return empty array when no requests exist", () => {
170
+ const requests = store.getRequests();
171
+ expect(requests).toEqual([]);
172
+ });
173
+
174
+ it("should return all pending requests", () => {
175
+ const request1 = createMockRequest("req-1");
176
+ const request2 = createMockRequest("req-2");
177
+
178
+ store.add(request1);
179
+ store.add(request2);
180
+
181
+ const requests = store.getRequests();
182
+ expect(requests).toHaveLength(2);
183
+ });
184
+
185
+ it("should sort requests by segment time in chronological order", () => {
186
+ const request1 = createMockRequest("req-1", 1000, 300);
187
+ const request2 = createMockRequest("req-2", 2000, 100);
188
+ const request3 = createMockRequest("req-3", 3000, 200);
189
+
190
+ store.add(request1);
191
+ store.add(request2);
192
+ store.add(request3);
193
+
194
+ const requests = store.getRequests();
195
+ expect(requests[0].content.segment.time).toBe(100);
196
+ expect(requests[1].content.segment.time).toBe(200);
197
+ expect(requests[2].content.segment.time).toBe(300);
198
+ });
199
+
200
+ it("should return requests with their progress updates", () => {
201
+ const request = createMockRequest("req-1");
202
+ const progress1 = createMockProgress("req-1", 0.5, 1024);
203
+ const progress2 = createMockProgress("req-1", 1.0, 2048);
204
+
205
+ store.add(request);
206
+ store.addProgress(progress1);
207
+ store.addProgress(progress2);
208
+
209
+ const requests = store.getRequests();
210
+ expect(requests[0].progress).toEqual([progress1, progress2]);
211
+ });
212
+
213
+ it("should not include removed requests", () => {
214
+ const request1 = createMockRequest("req-1");
215
+ const request2 = createMockRequest("req-2");
216
+
217
+ store.add(request1);
218
+ store.add(request2);
219
+ store.remove("req-1");
220
+
221
+ const requests = store.getRequests();
222
+ expect(requests).toHaveLength(1);
223
+ expect(requests[0].content).toBe(request2.content);
224
+ });
225
+
226
+ it("should filter out null/undefined values", () => {
227
+ const request = createMockRequest("req-1");
228
+
229
+ store.add(request);
230
+ // Manually corrupt the internal state to test filtering
231
+ (store as any)._currentRequests.corrupted = null;
232
+
233
+ const requests = store.getRequests();
234
+ expect(requests).toHaveLength(1);
235
+ expect(requests[0].content).toBe(request.content);
236
+ });
237
+ });
238
+
239
+ describe("integration scenarios", () => {
240
+ it("should handle complete request lifecycle", () => {
241
+ const request = createMockRequest("req-1", 1000);
242
+ const progress1 = createMockProgress("req-1", 0.5, 1024, 2048, 1500);
243
+ const progress2 = createMockProgress("req-1", 1.0, 2048, 2048, 2000);
244
+
245
+ // Add request
246
+ store.add(request);
247
+ expect(store.getRequests()).toHaveLength(1);
248
+
249
+ // Add progress updates
250
+ store.addProgress(progress1);
251
+ store.addProgress(progress2);
252
+ expect(store.getRequests()[0].progress).toHaveLength(2);
253
+
254
+ // Remove request
255
+ store.remove("req-1");
256
+ expect(store.getRequests()).toHaveLength(0);
257
+ });
258
+
259
+ it("should handle multiple concurrent requests with different segment times", () => {
260
+ const request1 = createMockRequest("req-1", 1000, 500);
261
+ const request2 = createMockRequest("req-2", 1100, 200);
262
+ const request3 = createMockRequest("req-3", 1200, 350);
263
+
264
+ store.add(request1);
265
+ store.add(request2);
266
+ store.add(request3);
267
+
268
+ store.addProgress(createMockProgress("req-1", 0.5, 512));
269
+ store.addProgress(createMockProgress("req-2", 0.3, 300));
270
+ store.addProgress(createMockProgress("req-3", 0.7, 700));
271
+
272
+ const requests = store.getRequests();
273
+
274
+ // Should be sorted by segment time: 200, 350, 500
275
+ expect(requests[0].content.segment.time).toBe(200);
276
+ expect(requests[1].content.segment.time).toBe(350);
277
+ expect(requests[2].content.segment.time).toBe(500);
278
+
279
+ // Each should have their progress
280
+ expect(requests[0].progress).toHaveLength(1);
281
+ expect(requests[1].progress).toHaveLength(1);
282
+ expect(requests[2].progress).toHaveLength(1);
283
+ });
284
+ });
285
+ });
@@ -0,0 +1,247 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import type { IRepresentation } from "../../../../manifest";
3
+ import RepresentationScoreCalculator, {
4
+ ScoreConfidenceLevel,
5
+ } from "../representation_score_calculator";
6
+
7
+ vi.mock("../../../../log", () => ({
8
+ default: {
9
+ debug: vi.fn(),
10
+ },
11
+ }));
12
+
13
+ vi.mock("../ewma", () => {
14
+ class EWMA {
15
+ private _estimate: number;
16
+ private _samples: Array<{ duration: number; value: number }>;
17
+ constructor() {
18
+ this._estimate = 0;
19
+ this._samples = [];
20
+ }
21
+ public addSample = vi.fn((duration: number, value: number) => {
22
+ this._samples.push({ duration, value });
23
+ // Simple average for testing purposes
24
+ this._estimate =
25
+ this._samples.reduce((sum, s) => sum + s.value, 0) / this._samples.length;
26
+ });
27
+ public getEstimate = vi.fn(() => this._estimate);
28
+ }
29
+
30
+ return {
31
+ default: EWMA,
32
+ };
33
+ });
34
+
35
+ describe("RepresentationScoreCalculator", () => {
36
+ let calculator: RepresentationScoreCalculator;
37
+ let mockRepresentation1: IRepresentation;
38
+ let mockRepresentation2: IRepresentation;
39
+
40
+ beforeEach(() => {
41
+ vi.clearAllMocks();
42
+ calculator = new RepresentationScoreCalculator();
43
+
44
+ mockRepresentation1 = {
45
+ id: "rep1",
46
+ bitrate: 1000000,
47
+ } as IRepresentation;
48
+
49
+ mockRepresentation2 = {
50
+ id: "rep2",
51
+ bitrate: 2000000,
52
+ } as IRepresentation;
53
+ });
54
+
55
+ describe("addSample", () => {
56
+ it("should add a sample for a new representation", () => {
57
+ calculator.addSample(mockRepresentation1, 2, 4);
58
+
59
+ const estimate = calculator.getEstimate(mockRepresentation1);
60
+ expect(estimate).toBeDefined();
61
+ expect(estimate?.score).toBe(2); // 4 / 2 = 2
62
+ });
63
+
64
+ it("should calculate the correct ratio (segmentDuration / requestDuration)", () => {
65
+ // If we can download 10 seconds of content in 5 seconds, ratio should be 2
66
+ calculator.addSample(mockRepresentation1, 5, 10);
67
+
68
+ const estimate = calculator.getEstimate(mockRepresentation1);
69
+ expect(estimate?.score).toBe(2);
70
+ });
71
+
72
+ it("should accumulate samples for the same representation", () => {
73
+ calculator.addSample(mockRepresentation1, 2, 4); // ratio: 2
74
+ calculator.addSample(mockRepresentation1, 4, 8); // ratio: 2
75
+
76
+ const estimate = calculator.getEstimate(mockRepresentation1);
77
+ expect(estimate).toBeDefined();
78
+ expect(estimate?.score).toBe(2);
79
+ });
80
+
81
+ it("should reset data when switching to a different representation", () => {
82
+ calculator.addSample(mockRepresentation1, 2, 4);
83
+ calculator.addSample(mockRepresentation2, 1, 2);
84
+
85
+ const estimate1 = calculator.getEstimate(mockRepresentation1);
86
+ const estimate2 = calculator.getEstimate(mockRepresentation2);
87
+
88
+ expect(estimate1).toBeUndefined();
89
+ expect(estimate2).toBeDefined();
90
+ expect(estimate2?.score).toBe(2);
91
+ });
92
+
93
+ it("should track loaded segments count", () => {
94
+ calculator.addSample(mockRepresentation1, 2, 4);
95
+ calculator.addSample(mockRepresentation1, 2, 4);
96
+ calculator.addSample(mockRepresentation1, 2, 4);
97
+
98
+ const estimate = calculator.getEstimate(mockRepresentation1);
99
+ expect(estimate).toBeDefined();
100
+ });
101
+
102
+ it("should track total loaded duration", () => {
103
+ calculator.addSample(mockRepresentation1, 2, 4);
104
+ calculator.addSample(mockRepresentation1, 2, 6);
105
+ calculator.addSample(mockRepresentation1, 2, 5);
106
+
107
+ const estimate = calculator.getEstimate(mockRepresentation1);
108
+ expect(estimate).toBeDefined();
109
+ // Total duration should be 4 + 6 + 5 = 15
110
+ });
111
+ });
112
+
113
+ describe("getEstimate", () => {
114
+ it("should return undefined for a representation with no samples", () => {
115
+ const estimate = calculator.getEstimate(mockRepresentation1);
116
+ expect(estimate).toBeUndefined();
117
+ });
118
+
119
+ it("should return LOW confidence when less than 5 segments loaded", () => {
120
+ calculator.addSample(mockRepresentation1, 2, 4);
121
+ calculator.addSample(mockRepresentation1, 2, 4);
122
+
123
+ const estimate = calculator.getEstimate(mockRepresentation1);
124
+ expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.LOW);
125
+ });
126
+
127
+ it("should return LOW confidence when loaded duration is less than 10 seconds", () => {
128
+ // Add 5 segments but with short duration
129
+ for (let i = 0; i < 5; i++) {
130
+ calculator.addSample(mockRepresentation1, 1, 1);
131
+ }
132
+
133
+ const estimate = calculator.getEstimate(mockRepresentation1);
134
+ expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.LOW);
135
+ });
136
+
137
+ it("should return HIGH confidence when > 5 segments and >= 10 seconds loaded", () => {
138
+ // Add 5 segments with 2 seconds each (total 10 seconds)
139
+ for (let i = 0; i < 6; i++) {
140
+ calculator.addSample(mockRepresentation1, 1, 2);
141
+ }
142
+
143
+ const estimate = calculator.getEstimate(mockRepresentation1);
144
+ expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.HIGH);
145
+ });
146
+
147
+ it("should return HIGH confidence when > 5 segments and > 10 seconds loaded", () => {
148
+ // Add 5 segments with 3 seconds each (total 15 seconds)
149
+ for (let i = 0; i < 6; i++) {
150
+ calculator.addSample(mockRepresentation1, 1, 3);
151
+ }
152
+
153
+ const estimate = calculator.getEstimate(mockRepresentation1);
154
+ expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.HIGH);
155
+ });
156
+
157
+ it("should return undefined after switching representations", () => {
158
+ calculator.addSample(mockRepresentation1, 2, 4);
159
+ calculator.addSample(mockRepresentation2, 1, 2);
160
+
161
+ const estimate = calculator.getEstimate(mockRepresentation1);
162
+ expect(estimate).toBeUndefined();
163
+ });
164
+ });
165
+
166
+ describe("getLastStableRepresentation", () => {
167
+ it("should return null initially", () => {
168
+ expect(calculator.getLastStableRepresentation()).toBeNull();
169
+ });
170
+
171
+ it("should return the representation when score exceeds 1", () => {
172
+ // Add sample with ratio > 1 (can download faster than playback)
173
+ calculator.addSample(mockRepresentation1, 2, 5); // ratio: 2.5
174
+
175
+ expect(calculator.getLastStableRepresentation()).toBe(mockRepresentation1);
176
+ });
177
+
178
+ it("should not update when score is <= 1", () => {
179
+ // Add sample with ratio <= 1
180
+ calculator.addSample(mockRepresentation1, 5, 4); // ratio: 0.8
181
+
182
+ expect(calculator.getLastStableRepresentation()).toBeNull();
183
+ });
184
+
185
+ it("should update to new representation with good score", () => {
186
+ calculator.addSample(mockRepresentation1, 2, 5); // ratio: 2.5
187
+ expect(calculator.getLastStableRepresentation()).toBe(mockRepresentation1);
188
+
189
+ calculator.addSample(mockRepresentation2, 1, 3); // ratio: 3
190
+ expect(calculator.getLastStableRepresentation()).toBe(mockRepresentation2);
191
+ });
192
+
193
+ it("should not update when adding more samples to same representation", () => {
194
+ calculator.addSample(mockRepresentation1, 2, 5); // ratio: 2.5
195
+ const firstStable = calculator.getLastStableRepresentation();
196
+
197
+ calculator.addSample(mockRepresentation1, 2, 6); // ratio: 3
198
+ const secondStable = calculator.getLastStableRepresentation();
199
+
200
+ expect(firstStable).toBe(secondStable);
201
+ expect(secondStable).toBe(mockRepresentation1);
202
+ });
203
+
204
+ it("should persist last stable representation even after switching to worse representation", () => {
205
+ calculator.addSample(mockRepresentation1, 2, 5); // ratio: 2.5
206
+ expect(calculator.getLastStableRepresentation()).toBe(mockRepresentation1);
207
+
208
+ calculator.addSample(mockRepresentation2, 5, 2); // ratio: 0.4
209
+ expect(calculator.getLastStableRepresentation()).toBe(mockRepresentation1);
210
+ });
211
+ });
212
+
213
+ describe("score calculation scenarios", () => {
214
+ it("should handle score exactly equal to 1", () => {
215
+ // Can download exactly at playback speed
216
+ calculator.addSample(mockRepresentation1, 4, 4); // ratio: 1
217
+
218
+ const estimate = calculator.getEstimate(mockRepresentation1);
219
+ expect(estimate?.score).toBe(1);
220
+ });
221
+
222
+ it("should handle score less than 1 (slow download)", () => {
223
+ // Takes 10 seconds to download 5 seconds of content
224
+ calculator.addSample(mockRepresentation1, 10, 5); // ratio: 0.5
225
+
226
+ const estimate = calculator.getEstimate(mockRepresentation1);
227
+ expect(estimate?.score).toBe(0.5);
228
+ });
229
+
230
+ it("should handle score greater than 1 (fast download)", () => {
231
+ // Takes 2 seconds to download 10 seconds of content
232
+ calculator.addSample(mockRepresentation1, 2, 10); // ratio: 5
233
+
234
+ const estimate = calculator.getEstimate(mockRepresentation1);
235
+ expect(estimate?.score).toBe(5);
236
+ });
237
+
238
+ it("should average multiple samples correctly", () => {
239
+ calculator.addSample(mockRepresentation1, 2, 4); // ratio: 2
240
+ calculator.addSample(mockRepresentation1, 2, 6); // ratio: 3
241
+
242
+ const estimate = calculator.getEstimate(mockRepresentation1);
243
+ // Average of 2 and 3 is 2.5
244
+ expect(estimate?.score).toBe(2.5);
245
+ });
246
+ });
247
+ });
@@ -1,4 +1,4 @@
1
- import { config } from "../../experimental";
1
+ import config from "../../config";
2
2
  import log from "../../log";
3
3
  import type { IAdaptation, IPeriod, IRepresentation } from "../../manifest";
4
4
  import type {