rx-player 4.5.0-dev.2026012302 → 4.5.0-dev.2026031700
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 +21 -3
- 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/cmcd/cmcd_data_builder.js +2 -2
- package/dist/commonjs/core/entry/FreezeResolver.js +2 -2
- package/dist/commonjs/core/entry/content_preparer.d.ts +1 -6
- package/dist/commonjs/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/commonjs/core/entry/content_preparer.js +2 -125
- 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/entry/core_entry.d.ts +1 -1
- package/dist/commonjs/core/entry/core_entry.d.ts.map +1 -1
- package/dist/commonjs/core/entry/core_entry.js +2 -7
- package/dist/commonjs/core/entry/core_text_displayer_interface.d.ts.map +1 -1
- package/dist/commonjs/core/entry/core_text_displayer_interface.js +3 -1
- package/dist/commonjs/core/entry/index.d.ts +1 -1
- package/dist/commonjs/core/entry/index.d.ts.map +1 -1
- package/dist/commonjs/core/entry/utils.d.ts +64 -0
- package/dist/commonjs/core/entry/utils.d.ts.map +1 -1
- package/dist/commonjs/core/entry/utils.js +165 -0
- package/dist/commonjs/core/fetchers/segment/segment_queue.d.ts +5 -6
- package/dist/commonjs/core/fetchers/segment/segment_queue.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/thumbnails/thumbnail_fetcher.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/thumbnails/thumbnail_fetcher.js +34 -30
- 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/experimental/features/worker/index.d.ts +2 -1
- package/dist/commonjs/experimental/features/worker/index.d.ts.map +1 -1
- package/dist/commonjs/experimental/features/worker/index.js +4 -3
- package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +28 -4
- 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/parsers/texttracks/ttml/html/apply_line_height.d.ts.map +1 -1
- package/dist/commonjs/parsers/texttracks/ttml/html/apply_line_height.js +16 -1
- 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/cmcd/cmcd_data_builder.js +2 -2
- package/dist/es2017/core/entry/FreezeResolver.js +1 -1
- package/dist/es2017/core/entry/content_preparer.d.ts +1 -6
- package/dist/es2017/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/es2017/core/entry/content_preparer.js +1 -76
- 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/entry/core_entry.d.ts +1 -1
- package/dist/es2017/core/entry/core_entry.d.ts.map +1 -1
- package/dist/es2017/core/entry/core_entry.js +2 -7
- package/dist/es2017/core/entry/core_text_displayer_interface.d.ts.map +1 -1
- package/dist/es2017/core/entry/core_text_displayer_interface.js +3 -1
- package/dist/es2017/core/entry/index.d.ts +1 -1
- package/dist/es2017/core/entry/index.d.ts.map +1 -1
- package/dist/es2017/core/entry/utils.d.ts +64 -0
- package/dist/es2017/core/entry/utils.d.ts.map +1 -1
- package/dist/es2017/core/entry/utils.js +98 -0
- package/dist/es2017/core/fetchers/segment/segment_queue.d.ts +5 -6
- package/dist/es2017/core/fetchers/segment/segment_queue.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/thumbnails/thumbnail_fetcher.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/thumbnails/thumbnail_fetcher.js +21 -16
- 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/experimental/features/worker/index.d.ts +2 -1
- package/dist/es2017/experimental/features/worker/index.d.ts.map +1 -1
- package/dist/es2017/experimental/features/worker/index.js +2 -3
- package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +28 -4
- 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/parsers/texttracks/ttml/html/apply_line_height.d.ts.map +1 -1
- package/dist/es2017/parsers/texttracks/ttml/html/apply_line_height.js +16 -1
- 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 +8 -8
- package/package.json +26 -23
- 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__/adaptive_representation_selector.test.ts +734 -0
- package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +793 -0
- package/src/core/adaptive/__tests__/network_analyzer.test.ts +514 -0
- package/src/core/adaptive/utils/__tests__/bandwidth_estimator.test.ts +117 -0
- package/src/core/adaptive/utils/__tests__/last_estimate_storage.test.ts +76 -0
- package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +288 -0
- package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +249 -0
- package/src/core/cmcd/__tests__/cmcd_data_builder.test.ts +464 -0
- package/src/core/cmcd/cmcd_data_builder.ts +2 -2
- 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 +867 -0
- package/src/core/entry/__tests__/core_text_displayer_interface.test.ts +205 -0
- package/src/core/entry/__tests__/get_buffered_data_per_media_buffer.test.ts +95 -0
- package/src/core/entry/__tests__/track_choice_setter.test.ts +288 -0
- package/src/core/entry/__tests__/utils.test.ts +309 -0
- package/src/core/entry/content_preparer.ts +7 -118
- package/src/core/entry/content_time_boundaries_observer.ts +10 -0
- package/src/core/entry/core_entry.ts +7 -14
- package/src/core/entry/core_text_displayer_interface.ts +3 -1
- package/src/core/entry/index.ts +1 -1
- package/src/core/entry/utils.ts +151 -0
- package/src/core/fetchers/__tests__/cdn_prioritizer.test.ts +166 -0
- package/src/core/fetchers/manifest/__tests__/manifest_fetcher.test.ts +803 -0
- package/src/core/fetchers/segment/__tests__/segment_fetcher.test.ts +872 -0
- package/src/core/fetchers/segment/__tests__/segment_queue.test.ts +557 -0
- package/src/core/fetchers/segment/segment_queue.ts +5 -6
- package/src/core/fetchers/thumbnails/__tests__/thumbnail_fetcher.test.ts +435 -0
- package/src/core/fetchers/thumbnails/thumbnail_fetcher.ts +28 -20
- package/src/core/fetchers/utils/__tests__/schedule_request.test.ts +331 -0
- package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +593 -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/experimental/features/worker/index.ts +2 -4
- package/src/main_thread/api/public_api.ts +33 -4
- 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/mse/__tests__/main_media_source_interface.test.ts +846 -0
- package/src/parsers/texttracks/ttml/html/apply_line_height.ts +16 -1
- package/src/utils/__tests__/base64.test.ts +159 -191
- package/src/utils/base64.ts +30 -1
- package/src/utils/ranges.ts +1 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import pushInitSegment from "../push_init_segment";
|
|
3
|
+
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
8
|
+
|
|
9
|
+
const mockAppendSegmentToBuffer = vi.hoisted(() => vi.fn());
|
|
10
|
+
|
|
11
|
+
vi.mock("../append_segment_to_buffer", () => ({
|
|
12
|
+
default: mockAppendSegmentToBuffer,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
describe("pushInitSegment", () => {
|
|
16
|
+
let mockPlaybackObserver: any;
|
|
17
|
+
let mockSegmentSink: any;
|
|
18
|
+
let mockBufferGoal: any;
|
|
19
|
+
let mockCancelSignal: any;
|
|
20
|
+
let mockContent: any;
|
|
21
|
+
let mockSegment: any;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.resetAllMocks();
|
|
25
|
+
|
|
26
|
+
mockPlaybackObserver = {};
|
|
27
|
+
|
|
28
|
+
mockSegmentSink = {};
|
|
29
|
+
|
|
30
|
+
mockBufferGoal = {
|
|
31
|
+
getValue: vi.fn().mockReturnValue(30),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
mockCancelSignal = {
|
|
35
|
+
signal: { aborted: false },
|
|
36
|
+
cancellationError: null,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
mockContent = {
|
|
40
|
+
adaptation: { id: "adaptation-1" },
|
|
41
|
+
manifest: { id: "manifest-1" },
|
|
42
|
+
period: { id: "period-1" },
|
|
43
|
+
representation: {
|
|
44
|
+
id: "rep-1",
|
|
45
|
+
getMimeTypeString: vi.fn().mockReturnValue('video/mp4; codecs="avc1.42E01E"'),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
mockSegment = {
|
|
50
|
+
id: "init-segment",
|
|
51
|
+
isInit: true,
|
|
52
|
+
time: 0,
|
|
53
|
+
duration: 0,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should call appendSegmentToBuffer with correct data structure", async () => {
|
|
58
|
+
mockAppendSegmentToBuffer.mockResolvedValue({
|
|
59
|
+
start: 0,
|
|
60
|
+
end: 10,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const initSegmentUniqueId = "unique-init-123";
|
|
64
|
+
|
|
65
|
+
await pushInitSegment(
|
|
66
|
+
{
|
|
67
|
+
playbackObserver: mockPlaybackObserver,
|
|
68
|
+
content: mockContent,
|
|
69
|
+
initSegmentUniqueId,
|
|
70
|
+
segmentData: null,
|
|
71
|
+
segment: mockSegment,
|
|
72
|
+
segmentSink: mockSegmentSink,
|
|
73
|
+
bufferGoal: mockBufferGoal,
|
|
74
|
+
},
|
|
75
|
+
mockCancelSignal,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledTimes(1);
|
|
79
|
+
|
|
80
|
+
const callArgs = mockAppendSegmentToBuffer.mock.calls[0];
|
|
81
|
+
expect(callArgs[0]).toBe(mockPlaybackObserver);
|
|
82
|
+
expect(callArgs[1]).toBe(mockSegmentSink);
|
|
83
|
+
expect(callArgs[3]).toBe(mockBufferGoal);
|
|
84
|
+
expect(callArgs[4]).toBe(mockCancelSignal);
|
|
85
|
+
|
|
86
|
+
const { data, inventoryInfos } = callArgs[2];
|
|
87
|
+
expect(data).toEqual({
|
|
88
|
+
initSegmentUniqueId,
|
|
89
|
+
chunk: null,
|
|
90
|
+
timestampOffset: 0,
|
|
91
|
+
appendWindow: [undefined, undefined],
|
|
92
|
+
codec: 'video/mp4; codecs="avc1.42E01E"',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(inventoryInfos).toMatchObject({
|
|
96
|
+
segment: mockSegment,
|
|
97
|
+
chunkSize: undefined,
|
|
98
|
+
start: 0,
|
|
99
|
+
end: 0,
|
|
100
|
+
adaptation: mockContent.adaptation,
|
|
101
|
+
manifest: mockContent.manifest,
|
|
102
|
+
period: mockContent.period,
|
|
103
|
+
representation: mockContent.representation,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should return correct payload with buffered result", async () => {
|
|
108
|
+
const mockBuffered = { start: 0, end: 10 };
|
|
109
|
+
mockAppendSegmentToBuffer.mockResolvedValue(mockBuffered);
|
|
110
|
+
|
|
111
|
+
const result = await pushInitSegment(
|
|
112
|
+
{
|
|
113
|
+
playbackObserver: mockPlaybackObserver,
|
|
114
|
+
content: mockContent,
|
|
115
|
+
initSegmentUniqueId: "init-456",
|
|
116
|
+
segmentData: null,
|
|
117
|
+
segment: mockSegment,
|
|
118
|
+
segmentSink: mockSegmentSink,
|
|
119
|
+
bufferGoal: mockBufferGoal,
|
|
120
|
+
},
|
|
121
|
+
mockCancelSignal,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(result).toEqual({
|
|
125
|
+
content: mockContent,
|
|
126
|
+
segment: mockSegment,
|
|
127
|
+
buffered: mockBuffered,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should call getMimeTypeString on representation", async () => {
|
|
132
|
+
mockAppendSegmentToBuffer.mockResolvedValue({ start: 0, end: 0 });
|
|
133
|
+
|
|
134
|
+
await pushInitSegment(
|
|
135
|
+
{
|
|
136
|
+
playbackObserver: mockPlaybackObserver,
|
|
137
|
+
content: mockContent,
|
|
138
|
+
initSegmentUniqueId: "init-789",
|
|
139
|
+
segmentData: null,
|
|
140
|
+
segment: mockSegment,
|
|
141
|
+
segmentSink: mockSegmentSink,
|
|
142
|
+
bufferGoal: mockBufferGoal,
|
|
143
|
+
},
|
|
144
|
+
mockCancelSignal,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expect(mockContent.representation.getMimeTypeString).toHaveBeenCalledTimes(1);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should set chunk to null for init segments", async () => {
|
|
151
|
+
mockAppendSegmentToBuffer.mockResolvedValue({});
|
|
152
|
+
|
|
153
|
+
await pushInitSegment(
|
|
154
|
+
{
|
|
155
|
+
playbackObserver: mockPlaybackObserver,
|
|
156
|
+
content: mockContent,
|
|
157
|
+
initSegmentUniqueId: "init-abc",
|
|
158
|
+
segmentData: { someData: "value" },
|
|
159
|
+
segment: mockSegment,
|
|
160
|
+
segmentSink: mockSegmentSink,
|
|
161
|
+
bufferGoal: mockBufferGoal,
|
|
162
|
+
},
|
|
163
|
+
mockCancelSignal,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const { data } = mockAppendSegmentToBuffer.mock.calls[0][2];
|
|
167
|
+
expect(data.chunk).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should set timestampOffset to 0", async () => {
|
|
171
|
+
mockAppendSegmentToBuffer.mockResolvedValue({});
|
|
172
|
+
|
|
173
|
+
await pushInitSegment(
|
|
174
|
+
{
|
|
175
|
+
playbackObserver: mockPlaybackObserver,
|
|
176
|
+
content: mockContent,
|
|
177
|
+
initSegmentUniqueId: "init-def",
|
|
178
|
+
segmentData: null,
|
|
179
|
+
segment: mockSegment,
|
|
180
|
+
segmentSink: mockSegmentSink,
|
|
181
|
+
bufferGoal: mockBufferGoal,
|
|
182
|
+
},
|
|
183
|
+
mockCancelSignal,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const { data } = mockAppendSegmentToBuffer.mock.calls[0][2];
|
|
187
|
+
expect(data.timestampOffset).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should set appendWindow to [undefined, undefined]", async () => {
|
|
191
|
+
mockAppendSegmentToBuffer.mockResolvedValue({});
|
|
192
|
+
|
|
193
|
+
await pushInitSegment(
|
|
194
|
+
{
|
|
195
|
+
playbackObserver: mockPlaybackObserver,
|
|
196
|
+
content: mockContent,
|
|
197
|
+
initSegmentUniqueId: "init-ghi",
|
|
198
|
+
segmentData: null,
|
|
199
|
+
segment: mockSegment,
|
|
200
|
+
segmentSink: mockSegmentSink,
|
|
201
|
+
bufferGoal: mockBufferGoal,
|
|
202
|
+
},
|
|
203
|
+
mockCancelSignal,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const { data } = mockAppendSegmentToBuffer.mock.calls[0][2];
|
|
207
|
+
expect(data.appendWindow).toEqual([undefined, undefined]);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import type { CancellationSignal } from "../../../../../utils/task_canceller";
|
|
3
|
+
import pushMediaSegment from "../push_media_segment";
|
|
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
|
+
// Mock dependencies with vi.hoisted
|
|
10
|
+
const mockGetCurrent = vi.hoisted(() => vi.fn());
|
|
11
|
+
const mockObjectAssign = vi.hoisted(() => vi.fn());
|
|
12
|
+
const mockAppendSegmentToBuffer = vi.hoisted(() => vi.fn());
|
|
13
|
+
|
|
14
|
+
vi.mock("../../../../../config", () => ({
|
|
15
|
+
default: {
|
|
16
|
+
getCurrent: mockGetCurrent,
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("../../../../../utils/object_assign", () => ({
|
|
21
|
+
default: mockObjectAssign,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock("../append_segment_to_buffer.ts", () => ({
|
|
25
|
+
default: mockAppendSegmentToBuffer,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe("pushMediaSegment", () => {
|
|
29
|
+
const mockPlaybackObserver = {} as any;
|
|
30
|
+
const mockBufferGoal = { getValue: () => 30 } as any;
|
|
31
|
+
const mockCancelSignal = {} as CancellationSignal;
|
|
32
|
+
const mockSegmentSink = {} as any;
|
|
33
|
+
|
|
34
|
+
const mockContent = {
|
|
35
|
+
adaptation: {} as any,
|
|
36
|
+
manifest: {} as any,
|
|
37
|
+
period: {} as any,
|
|
38
|
+
representation: {
|
|
39
|
+
getMimeTypeString: vi.fn(() => 'video/mp4; codecs="avc1.42E01E"'),
|
|
40
|
+
} as any,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const mockSegment = {
|
|
44
|
+
time: 10,
|
|
45
|
+
duration: 5,
|
|
46
|
+
} as any;
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
vi.resetAllMocks();
|
|
50
|
+
mockGetCurrent.mockReturnValue({
|
|
51
|
+
APPEND_WINDOW_SECURITIES: {
|
|
52
|
+
START: 0.1,
|
|
53
|
+
END: 0.2,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
mockObjectAssign.mockImplementation((target, ...sources) =>
|
|
57
|
+
// eslint-disable-next-line no-restricted-properties
|
|
58
|
+
Object.assign(target, ...sources),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should return null when chunkData is null", async () => {
|
|
63
|
+
const parsedSegment: any = {
|
|
64
|
+
chunkData: null,
|
|
65
|
+
chunkInfos: null,
|
|
66
|
+
chunkOffset: 0,
|
|
67
|
+
chunkSize: 0,
|
|
68
|
+
appendWindow: [undefined, undefined] as [number | undefined, number | undefined],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const result = await pushMediaSegment(
|
|
72
|
+
{
|
|
73
|
+
playbackObserver: mockPlaybackObserver,
|
|
74
|
+
bufferGoal: mockBufferGoal,
|
|
75
|
+
content: mockContent,
|
|
76
|
+
initSegmentUniqueId: "init-123",
|
|
77
|
+
parsedSegment,
|
|
78
|
+
segment: mockSegment,
|
|
79
|
+
segmentSink: mockSegmentSink,
|
|
80
|
+
},
|
|
81
|
+
mockCancelSignal,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(result).toBeNull();
|
|
85
|
+
expect(mockAppendSegmentToBuffer).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should append segment with basic data when chunkData is present", async () => {
|
|
89
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
90
|
+
const parsedSegment: any = {
|
|
91
|
+
chunkData: mockChunkData,
|
|
92
|
+
chunkInfos: null,
|
|
93
|
+
chunkOffset: 2.5,
|
|
94
|
+
chunkSize: 1024,
|
|
95
|
+
appendWindow: [undefined, undefined] as [number | undefined, number | undefined],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const mockBuffered = [{ start: 10, end: 15 }];
|
|
99
|
+
mockAppendSegmentToBuffer.mockResolvedValue(mockBuffered);
|
|
100
|
+
|
|
101
|
+
const result = await pushMediaSegment(
|
|
102
|
+
{
|
|
103
|
+
playbackObserver: mockPlaybackObserver,
|
|
104
|
+
bufferGoal: mockBufferGoal,
|
|
105
|
+
content: mockContent,
|
|
106
|
+
initSegmentUniqueId: "init-123",
|
|
107
|
+
parsedSegment,
|
|
108
|
+
segment: mockSegment,
|
|
109
|
+
segmentSink: mockSegmentSink,
|
|
110
|
+
},
|
|
111
|
+
mockCancelSignal,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
115
|
+
mockPlaybackObserver,
|
|
116
|
+
mockSegmentSink,
|
|
117
|
+
{
|
|
118
|
+
data: {
|
|
119
|
+
initSegmentUniqueId: "init-123",
|
|
120
|
+
chunk: mockChunkData,
|
|
121
|
+
timestampOffset: 2.5,
|
|
122
|
+
appendWindow: [undefined, undefined],
|
|
123
|
+
codec: 'video/mp4; codecs="avc1.42E01E"',
|
|
124
|
+
},
|
|
125
|
+
inventoryInfos: expect.objectContaining({
|
|
126
|
+
segment: mockSegment,
|
|
127
|
+
chunkSize: 1024,
|
|
128
|
+
start: 10,
|
|
129
|
+
end: 15,
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
mockBufferGoal,
|
|
133
|
+
mockCancelSignal,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
expect(result).toEqual({
|
|
137
|
+
content: mockContent,
|
|
138
|
+
segment: mockSegment,
|
|
139
|
+
buffered: mockBuffered,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should use chunkInfos for time calculations when available", async () => {
|
|
144
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
145
|
+
const parsedSegment: any = {
|
|
146
|
+
chunkData: mockChunkData,
|
|
147
|
+
chunkInfos: { time: 20, duration: 8 },
|
|
148
|
+
chunkOffset: 0,
|
|
149
|
+
chunkSize: 2048,
|
|
150
|
+
appendWindow: [undefined, undefined] as [number | undefined, number | undefined],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
mockAppendSegmentToBuffer.mockResolvedValue([]);
|
|
154
|
+
|
|
155
|
+
await pushMediaSegment(
|
|
156
|
+
{
|
|
157
|
+
playbackObserver: mockPlaybackObserver,
|
|
158
|
+
bufferGoal: mockBufferGoal,
|
|
159
|
+
content: mockContent,
|
|
160
|
+
initSegmentUniqueId: null,
|
|
161
|
+
parsedSegment,
|
|
162
|
+
segment: mockSegment,
|
|
163
|
+
segmentSink: mockSegmentSink,
|
|
164
|
+
},
|
|
165
|
+
mockCancelSignal,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
169
|
+
expect.anything(),
|
|
170
|
+
expect.anything(),
|
|
171
|
+
expect.objectContaining({
|
|
172
|
+
inventoryInfos: expect.objectContaining({
|
|
173
|
+
start: 20,
|
|
174
|
+
end: 28,
|
|
175
|
+
}),
|
|
176
|
+
}),
|
|
177
|
+
expect.anything(),
|
|
178
|
+
expect.anything(),
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should apply append window securities to start window", async () => {
|
|
183
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
184
|
+
const parsedSegment: any = {
|
|
185
|
+
chunkData: mockChunkData,
|
|
186
|
+
chunkInfos: null,
|
|
187
|
+
chunkOffset: 0,
|
|
188
|
+
chunkSize: 1024,
|
|
189
|
+
appendWindow: [5, undefined] as [number | undefined, number | undefined],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
mockAppendSegmentToBuffer.mockResolvedValue([]);
|
|
193
|
+
|
|
194
|
+
await pushMediaSegment(
|
|
195
|
+
{
|
|
196
|
+
playbackObserver: mockPlaybackObserver,
|
|
197
|
+
bufferGoal: mockBufferGoal,
|
|
198
|
+
content: mockContent,
|
|
199
|
+
initSegmentUniqueId: "init-123",
|
|
200
|
+
parsedSegment,
|
|
201
|
+
segment: mockSegment,
|
|
202
|
+
segmentSink: mockSegmentSink,
|
|
203
|
+
},
|
|
204
|
+
mockCancelSignal,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
208
|
+
expect.anything(),
|
|
209
|
+
expect.anything(),
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
data: expect.objectContaining({
|
|
212
|
+
appendWindow: [4.9, undefined], // 5 - 0.1
|
|
213
|
+
}),
|
|
214
|
+
inventoryInfos: expect.objectContaining({
|
|
215
|
+
start: 10, // max(10, 4.9)
|
|
216
|
+
}),
|
|
217
|
+
}),
|
|
218
|
+
expect.anything(),
|
|
219
|
+
expect.anything(),
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should apply append window securities to end window", async () => {
|
|
224
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
225
|
+
const parsedSegment: any = {
|
|
226
|
+
chunkData: mockChunkData,
|
|
227
|
+
chunkInfos: null,
|
|
228
|
+
chunkOffset: 0,
|
|
229
|
+
chunkSize: 1024,
|
|
230
|
+
appendWindow: [undefined, 20] as [number | undefined, number | undefined],
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
mockAppendSegmentToBuffer.mockResolvedValue([]);
|
|
234
|
+
|
|
235
|
+
await pushMediaSegment(
|
|
236
|
+
{
|
|
237
|
+
playbackObserver: mockPlaybackObserver,
|
|
238
|
+
bufferGoal: mockBufferGoal,
|
|
239
|
+
content: mockContent,
|
|
240
|
+
initSegmentUniqueId: "init-123",
|
|
241
|
+
parsedSegment,
|
|
242
|
+
segment: mockSegment,
|
|
243
|
+
segmentSink: mockSegmentSink,
|
|
244
|
+
},
|
|
245
|
+
mockCancelSignal,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
249
|
+
expect.anything(),
|
|
250
|
+
expect.anything(),
|
|
251
|
+
expect.objectContaining({
|
|
252
|
+
data: expect.objectContaining({
|
|
253
|
+
appendWindow: [undefined, 20.2], // 20 + 0.2
|
|
254
|
+
}),
|
|
255
|
+
inventoryInfos: expect.objectContaining({
|
|
256
|
+
end: 15, // min(15, 20.2)
|
|
257
|
+
}),
|
|
258
|
+
}),
|
|
259
|
+
expect.anything(),
|
|
260
|
+
expect.anything(),
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("should apply both start and end append windows", async () => {
|
|
265
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
266
|
+
const parsedSegment: any = {
|
|
267
|
+
chunkData: mockChunkData,
|
|
268
|
+
chunkInfos: { time: 8, duration: 4 },
|
|
269
|
+
chunkOffset: 0,
|
|
270
|
+
chunkSize: 1024,
|
|
271
|
+
appendWindow: [9, 11] as [number | undefined, number | undefined],
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
mockAppendSegmentToBuffer.mockResolvedValue([]);
|
|
275
|
+
|
|
276
|
+
await pushMediaSegment(
|
|
277
|
+
{
|
|
278
|
+
playbackObserver: mockPlaybackObserver,
|
|
279
|
+
bufferGoal: mockBufferGoal,
|
|
280
|
+
content: mockContent,
|
|
281
|
+
initSegmentUniqueId: "init-123",
|
|
282
|
+
parsedSegment,
|
|
283
|
+
segment: mockSegment,
|
|
284
|
+
segmentSink: mockSegmentSink,
|
|
285
|
+
},
|
|
286
|
+
mockCancelSignal,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
290
|
+
expect.anything(),
|
|
291
|
+
expect.anything(),
|
|
292
|
+
expect.objectContaining({
|
|
293
|
+
data: expect.objectContaining({
|
|
294
|
+
appendWindow: [8.9, 11.2], // [9 - 0.1, 11 + 0.2]
|
|
295
|
+
}),
|
|
296
|
+
inventoryInfos: expect.objectContaining({
|
|
297
|
+
start: 8.9, // max(8, 8.9)
|
|
298
|
+
end: 11.2, // min(12, 11.2)
|
|
299
|
+
}),
|
|
300
|
+
}),
|
|
301
|
+
expect.anything(),
|
|
302
|
+
expect.anything(),
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("should not go below 0 for start append window", async () => {
|
|
307
|
+
const mockChunkData = new Uint8Array([1, 2, 3]);
|
|
308
|
+
const parsedSegment: any = {
|
|
309
|
+
chunkData: mockChunkData,
|
|
310
|
+
chunkInfos: null,
|
|
311
|
+
chunkOffset: 0,
|
|
312
|
+
chunkSize: 1024,
|
|
313
|
+
appendWindow: [0.05, undefined] as [number | undefined, number | undefined],
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
mockAppendSegmentToBuffer.mockResolvedValue([]);
|
|
317
|
+
|
|
318
|
+
await pushMediaSegment(
|
|
319
|
+
{
|
|
320
|
+
playbackObserver: mockPlaybackObserver,
|
|
321
|
+
bufferGoal: mockBufferGoal,
|
|
322
|
+
content: mockContent,
|
|
323
|
+
initSegmentUniqueId: "init-123",
|
|
324
|
+
parsedSegment,
|
|
325
|
+
segment: mockSegment,
|
|
326
|
+
segmentSink: mockSegmentSink,
|
|
327
|
+
},
|
|
328
|
+
mockCancelSignal,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(mockAppendSegmentToBuffer).toHaveBeenCalledWith(
|
|
332
|
+
expect.anything(),
|
|
333
|
+
expect.anything(),
|
|
334
|
+
expect.objectContaining({
|
|
335
|
+
data: expect.objectContaining({
|
|
336
|
+
appendWindow: [0, undefined], // max(0, 0.05 - 0.1)
|
|
337
|
+
}),
|
|
338
|
+
}),
|
|
339
|
+
expect.anything(),
|
|
340
|
+
expect.anything(),
|
|
341
|
+
);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
@@ -326,7 +326,7 @@ function getAvailableBufferSize(
|
|
|
326
326
|
/**
|
|
327
327
|
* From the given array of buffered chunks (`bufferedSegments`) returns the last
|
|
328
328
|
* buffered chunk contiguous with the one at the `startIndex` index given.
|
|
329
|
-
* @param {Array.<Object>}
|
|
329
|
+
* @param {Array.<Object>} bufferedSegments
|
|
330
330
|
* @param {number} startIndex
|
|
331
331
|
* @returns {Object}
|
|
332
332
|
*/
|
package/src/default_config.ts
CHANGED
|
@@ -1333,6 +1333,12 @@ const DEFAULT_CONFIG = {
|
|
|
1333
1333
|
* announcing the content as loaded.
|
|
1334
1334
|
*/
|
|
1335
1335
|
FORCE_WAIT_FOR_HAVE_ENOUGH_DATA: false,
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* If `true`, forces an initial seek to 0. Mainly needed for Safari HLS,
|
|
1339
|
+
* where the player may start at the live position instead of 0.
|
|
1340
|
+
*/
|
|
1341
|
+
FORCE_INITIAL_SEEK_TO_ZERO: false,
|
|
1336
1342
|
};
|
|
1337
1343
|
|
|
1338
1344
|
export type IDefaultConfig = typeof DEFAULT_CONFIG;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { DASH, DASH_WASM } from "../../../features/list";
|
|
1
|
+
export { DASH, DASH_WASM, SMOOTH } from "../../../features/list";
|
|
2
2
|
export { LOCAL_MANIFEST } from "../local";
|
|
3
|
-
|
|
4
|
-
// TODO: `SMOOTH` (has to be made worker-compatible)
|
|
5
|
-
// TODO: `METAPLAYLIST` (once every transport is ported)
|
|
3
|
+
export { METAPLAYLIST } from "../metaplaylist";
|
|
@@ -437,7 +437,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
437
437
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
|
|
438
438
|
videoElement.preload = "auto";
|
|
439
439
|
|
|
440
|
-
this.version = /* PLAYER_VERSION */ "4.5.0-dev.
|
|
440
|
+
this.version = /* PLAYER_VERSION */ "4.5.0-dev.2026031700";
|
|
441
441
|
this.log = log;
|
|
442
442
|
this.state = "STOPPED";
|
|
443
443
|
this.videoElement = videoElement;
|
|
@@ -2772,6 +2772,13 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2772
2772
|
}
|
|
2773
2773
|
|
|
2774
2774
|
if (this._priv_contentInfos.isDirectFile) {
|
|
2775
|
+
if (this.videoElement === null) {
|
|
2776
|
+
log.error("API", "getMinimumPosition() called on a disposed player");
|
|
2777
|
+
return 0;
|
|
2778
|
+
}
|
|
2779
|
+
if (this.videoElement.seekable.length > 0) {
|
|
2780
|
+
return this.videoElement.seekable.start(0);
|
|
2781
|
+
}
|
|
2775
2782
|
return 0;
|
|
2776
2783
|
}
|
|
2777
2784
|
|
|
@@ -2818,8 +2825,14 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2818
2825
|
|
|
2819
2826
|
if (isDirectFile) {
|
|
2820
2827
|
if (this.videoElement === null) {
|
|
2821
|
-
|
|
2828
|
+
log.error("API", "getMaximumPosition() called on a disposed player");
|
|
2829
|
+
return null;
|
|
2822
2830
|
}
|
|
2831
|
+
|
|
2832
|
+
if (this.videoElement.seekable.length > 0) {
|
|
2833
|
+
return this.videoElement.seekable.end(this.videoElement.seekable.length - 1);
|
|
2834
|
+
}
|
|
2835
|
+
// if for some reason seekable has no entry, fallback on duration
|
|
2823
2836
|
return this.videoElement.duration;
|
|
2824
2837
|
}
|
|
2825
2838
|
|
|
@@ -3526,6 +3539,22 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
3526
3539
|
if (startDate !== undefined) {
|
|
3527
3540
|
positionData.wallClockTime = startDate + observation.position.getPolled();
|
|
3528
3541
|
}
|
|
3542
|
+
|
|
3543
|
+
let directFileMaximumPosition;
|
|
3544
|
+
if (this.videoElement.seekable.length > 0) {
|
|
3545
|
+
directFileMaximumPosition = this.videoElement.seekable.end(
|
|
3546
|
+
this.videoElement.seekable.length - 1,
|
|
3547
|
+
);
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
if (directFileMaximumPosition !== undefined && !isNaN(directFileMaximumPosition)) {
|
|
3551
|
+
positionData.maximumPosition = directFileMaximumPosition;
|
|
3552
|
+
// infinity duration means the content is live
|
|
3553
|
+
if (this.videoElement.duration === Infinity) {
|
|
3554
|
+
positionData.liveGap =
|
|
3555
|
+
directFileMaximumPosition - this.videoElement.currentTime;
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3529
3558
|
}
|
|
3530
3559
|
this.trigger("positionUpdate", positionData);
|
|
3531
3560
|
}
|
|
@@ -3766,7 +3795,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
3766
3795
|
// TODO: Make it work with multithread?
|
|
3767
3796
|
return false;
|
|
3768
3797
|
}
|
|
3769
|
-
if (options.transport
|
|
3798
|
+
if (options.transport === "directfile") {
|
|
3770
3799
|
return false;
|
|
3771
3800
|
}
|
|
3772
3801
|
if (
|
|
@@ -3802,7 +3831,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
3802
3831
|
return true;
|
|
3803
3832
|
}
|
|
3804
3833
|
}
|
|
3805
|
-
Player.version = /* PLAYER_VERSION */ "4.5.0-dev.
|
|
3834
|
+
Player.version = /* PLAYER_VERSION */ "4.5.0-dev.2026031700";
|
|
3806
3835
|
|
|
3807
3836
|
/** Every events sent by the RxPlayer's public API. */
|
|
3808
3837
|
interface IPublicAPIEvent {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import type { IMediaElement } from "../../compat/browser_compatibility_types";
|
|
23
23
|
import clearElementSrc from "../../compat/clear_element_src";
|
|
24
|
+
import getStartDate from "../../compat/get_start_date";
|
|
24
25
|
import type { MediaError } from "../../errors";
|
|
25
26
|
import log from "../../log";
|
|
26
27
|
import type { IMediaElementPlaybackObserver } from "../../playback_observer";
|
|
@@ -307,7 +308,31 @@ function getDirectFileInitialTime(
|
|
|
307
308
|
if (!isNullOrUndefined(startAt.position)) {
|
|
308
309
|
return startAt.position;
|
|
309
310
|
} else if (!isNullOrUndefined(startAt.wallClockTime)) {
|
|
310
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Special case for Safari with HLS in directfile mode:
|
|
313
|
+
*
|
|
314
|
+
* Safari's native HLS implementation uses `#EXT-X-PROGRAM-DATE-TIME` from the manifest
|
|
315
|
+
* as the time reference. This means `mediaElement.currentTime` represents time relative
|
|
316
|
+
* to the playlist's start date.
|
|
317
|
+
*
|
|
318
|
+
* When seeking to a specific `wallClockTime`, we calculate the position by subtracting
|
|
319
|
+
* the playlist's start date from `startPosition.wallClockTime`:
|
|
320
|
+
* seekPosition = wallClockTime - startDate
|
|
321
|
+
*
|
|
322
|
+
* The startDate is retrieved via `mediaElement.getStartDate()`, which only returns
|
|
323
|
+
* a valid date once `mediaElement.readyState >= HAVE_CURRENT_DATA` (≥ 2).
|
|
324
|
+
* If unavailable, we return undefined.
|
|
325
|
+
*/
|
|
326
|
+
const startDate = getStartDate(mediaElement);
|
|
327
|
+
if (startDate === undefined) {
|
|
328
|
+
log.warn(
|
|
329
|
+
"Init",
|
|
330
|
+
"startAt.wallClockTime set but startDate is not known, either it's too soon to seek" +
|
|
331
|
+
" or the browser has no API to get the startDate",
|
|
332
|
+
);
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
return startAt.wallClockTime - startDate;
|
|
311
336
|
} else if (!isNullOrUndefined(startAt.fromFirstPosition)) {
|
|
312
337
|
return startAt.fromFirstPosition;
|
|
313
338
|
}
|