rx-player 4.5.0-dev.2026041501 → 4.5.0-dev.2026060400

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 (166) 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/eme/eme-api-implementation.js +1 -1
  8. package/dist/commonjs/compat/patch_webkit_source_buffer.d.ts.map +1 -1
  9. package/dist/commonjs/core/adaptive/utils/pending_requests_store.d.ts.map +1 -1
  10. package/dist/commonjs/core/adaptive/utils/pending_requests_store.js +2 -0
  11. package/dist/commonjs/core/entry/content_preparer.js +3 -1
  12. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts +0 -13
  13. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  14. package/dist/commonjs/core/entry/content_time_boundaries_observer.js +25 -4
  15. package/dist/commonjs/core/segment_sinks/implementations/audio_video/audio_video_segment_sink.js +3 -1
  16. package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
  17. package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.js +4 -1
  18. package/dist/commonjs/core/segment_sinks/implementations/utils/manual_time_ranges.d.ts.map +1 -1
  19. package/dist/commonjs/core/segment_sinks/implementations/utils/manual_time_ranges.js +2 -0
  20. package/dist/commonjs/core/stream/adaptation/types.d.ts.map +1 -1
  21. package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  22. package/dist/commonjs/errors/index.d.ts +2 -2
  23. package/dist/commonjs/errors/index.d.ts.map +1 -1
  24. package/dist/commonjs/errors/index.js +2 -1
  25. package/dist/commonjs/errors/media_error.d.ts +19 -4
  26. package/dist/commonjs/errors/media_error.d.ts.map +1 -1
  27. package/dist/commonjs/errors/media_error.js +29 -0
  28. package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/utils.d.ts.map +1 -1
  29. package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/utils.js +2 -0
  30. package/dist/commonjs/importable_worker.d.ts.map +1 -1
  31. package/dist/commonjs/importable_worker.js +1 -0
  32. package/dist/commonjs/main_thread/api/public_api.d.ts +3 -1
  33. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  34. package/dist/commonjs/main_thread/api/public_api.js +13 -6
  35. package/dist/commonjs/main_thread/decrypt/utils/loaded_sessions_store.d.ts.map +1 -1
  36. package/dist/commonjs/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
  37. package/dist/commonjs/main_thread/init/directfile_content_initializer.js +20 -11
  38. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +0 -1
  39. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  40. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +7 -14
  41. package/dist/commonjs/main_thread/text_displayer/html/html_text_displayer.js +1 -0
  42. package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.d.ts +2 -0
  43. package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
  44. package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.js +57 -24
  45. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +12 -1
  46. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  47. package/dist/commonjs/main_thread/tracks_store/tracks_store.js +15 -3
  48. package/dist/commonjs/main_thread/utils/media_capabilities.d.ts +5 -0
  49. package/dist/commonjs/main_thread/utils/media_capabilities.d.ts.map +1 -0
  50. package/dist/commonjs/main_thread/utils/media_capabilities.js +12 -0
  51. package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +1 -1
  52. package/dist/commonjs/transports/dash/manifest_parser.js +6 -2
  53. package/dist/commonjs/transports/smooth/segment_loader.d.ts.map +1 -1
  54. package/dist/commonjs/transports/smooth/segment_loader.js +3 -1
  55. package/dist/commonjs/utils/assert.d.ts.map +1 -1
  56. package/dist/commonjs/utils/assert.js +3 -1
  57. package/dist/commonjs/utils/create_uuid.js +2 -2
  58. package/dist/commonjs/utils/event_emitter.d.ts.map +1 -1
  59. package/dist/commonjs/utils/event_emitter.js +1 -0
  60. package/dist/commonjs/utils/reference.d.ts.map +1 -1
  61. package/dist/commonjs/utils/reference.js +1 -0
  62. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  63. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  64. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  65. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  66. package/dist/es2017/compat/eme/eme-api-implementation.js +1 -1
  67. package/dist/es2017/compat/patch_webkit_source_buffer.d.ts.map +1 -1
  68. package/dist/es2017/core/adaptive/utils/pending_requests_store.d.ts.map +1 -1
  69. package/dist/es2017/core/adaptive/utils/pending_requests_store.js +2 -0
  70. package/dist/es2017/core/entry/content_preparer.js +3 -1
  71. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts +0 -13
  72. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  73. package/dist/es2017/core/entry/content_time_boundaries_observer.js +25 -4
  74. package/dist/es2017/core/segment_sinks/implementations/audio_video/audio_video_segment_sink.js +3 -1
  75. package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
  76. package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.js +4 -1
  77. package/dist/es2017/core/segment_sinks/implementations/utils/manual_time_ranges.d.ts.map +1 -1
  78. package/dist/es2017/core/segment_sinks/implementations/utils/manual_time_ranges.js +2 -0
  79. package/dist/es2017/core/stream/adaptation/types.d.ts.map +1 -1
  80. package/dist/es2017/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  81. package/dist/es2017/errors/index.d.ts +2 -2
  82. package/dist/es2017/errors/index.d.ts.map +1 -1
  83. package/dist/es2017/errors/index.js +2 -2
  84. package/dist/es2017/errors/media_error.d.ts +19 -4
  85. package/dist/es2017/errors/media_error.d.ts.map +1 -1
  86. package/dist/es2017/errors/media_error.js +28 -0
  87. package/dist/es2017/experimental/tools/mediaCapabilitiesProber/utils.d.ts.map +1 -1
  88. package/dist/es2017/experimental/tools/mediaCapabilitiesProber/utils.js +2 -0
  89. package/dist/es2017/importable_worker.d.ts.map +1 -1
  90. package/dist/es2017/importable_worker.js +1 -0
  91. package/dist/es2017/main_thread/api/public_api.d.ts +3 -1
  92. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  93. package/dist/es2017/main_thread/api/public_api.js +13 -6
  94. package/dist/es2017/main_thread/decrypt/utils/loaded_sessions_store.d.ts.map +1 -1
  95. package/dist/es2017/main_thread/init/directfile_content_initializer.d.ts.map +1 -1
  96. package/dist/es2017/main_thread/init/directfile_content_initializer.js +20 -11
  97. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +0 -1
  98. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  99. package/dist/es2017/main_thread/init/media_source_content_initializer.js +8 -15
  100. package/dist/es2017/main_thread/text_displayer/html/html_text_displayer.js +1 -0
  101. package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.d.ts +2 -0
  102. package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
  103. package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.js +57 -24
  104. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +12 -1
  105. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  106. package/dist/es2017/main_thread/tracks_store/tracks_store.js +15 -3
  107. package/dist/es2017/main_thread/utils/media_capabilities.d.ts +5 -0
  108. package/dist/es2017/main_thread/utils/media_capabilities.d.ts.map +1 -0
  109. package/dist/es2017/main_thread/utils/media_capabilities.js +8 -0
  110. package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +1 -1
  111. package/dist/es2017/transports/dash/manifest_parser.js +6 -2
  112. package/dist/es2017/transports/smooth/segment_loader.d.ts.map +1 -1
  113. package/dist/es2017/transports/smooth/segment_loader.js +3 -1
  114. package/dist/es2017/utils/assert.d.ts.map +1 -1
  115. package/dist/es2017/utils/assert.js +3 -1
  116. package/dist/es2017/utils/create_uuid.js +2 -2
  117. package/dist/es2017/utils/event_emitter.d.ts.map +1 -1
  118. package/dist/es2017/utils/event_emitter.js +1 -0
  119. package/dist/es2017/utils/reference.d.ts.map +1 -1
  120. package/dist/es2017/utils/reference.js +1 -0
  121. package/dist/mpd-parser.wasm +0 -0
  122. package/dist/worker.js +6 -6
  123. package/eslint.config.mjs +7 -2
  124. package/package.json +15 -15
  125. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  126. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  127. package/src/compat/__tests__/is_codec_supported.test.ts +8 -2
  128. package/src/compat/eme/eme-api-implementation.ts +1 -1
  129. package/src/compat/patch_webkit_source_buffer.ts +6 -7
  130. package/src/core/adaptive/utils/pending_requests_store.ts +2 -0
  131. package/src/core/entry/__tests__/content_time_boundaries_observer.test.ts +19 -0
  132. package/src/core/entry/content_preparer.ts +1 -0
  133. package/src/core/entry/content_time_boundaries_observer.ts +28 -4
  134. package/src/core/segment_sinks/implementations/audio_video/audio_video_segment_sink.ts +1 -0
  135. package/src/core/segment_sinks/implementations/text/text_segment_sink.ts +2 -0
  136. package/src/core/segment_sinks/implementations/utils/manual_time_ranges.ts +2 -0
  137. package/src/core/stream/adaptation/types.ts +5 -4
  138. package/src/core/stream/orchestrator/stream_orchestrator.ts +4 -2
  139. package/src/errors/__tests__/media_error.test.ts +106 -3
  140. package/src/errors/index.ts +2 -1
  141. package/src/errors/media_error.ts +63 -7
  142. package/src/experimental/tools/mediaCapabilitiesProber/probers/__tests__/HDCPPolicy.test.ts +8 -2
  143. package/src/experimental/tools/mediaCapabilitiesProber/probers/__tests__/defaultCodecsFinder.test.ts +6 -2
  144. package/src/experimental/tools/mediaCapabilitiesProber/probers/__tests__/mediaContentType.test.ts +6 -2
  145. package/src/experimental/tools/mediaCapabilitiesProber/utils.ts +2 -0
  146. package/src/importable_worker.ts +1 -0
  147. package/src/main_thread/api/public_api.ts +14 -4
  148. package/src/main_thread/decrypt/utils/loaded_sessions_store.ts +1 -1
  149. package/src/main_thread/init/directfile_content_initializer.ts +25 -15
  150. package/src/main_thread/init/media_source_content_initializer.ts +9 -18
  151. package/src/main_thread/init/utils/__tests__/stream_events_emitter.test.ts +1 -1
  152. package/src/main_thread/text_displayer/html/html_text_displayer.ts +1 -0
  153. package/src/main_thread/tracks_store/__tests__/media_element_tracks_store.test.ts +63 -43
  154. package/src/main_thread/tracks_store/__tests__/tracks_store.test.ts +91 -0
  155. package/src/main_thread/tracks_store/media_element_tracks_store.ts +66 -27
  156. package/src/main_thread/tracks_store/tracks_store.ts +25 -3
  157. package/src/main_thread/utils/media_capabilities.ts +14 -0
  158. package/src/manifest/classes/__tests__/update_periods.test.ts +1 -0
  159. package/src/parsers/manifest/dash/common/__tests__/get_periods_time_infos.test.ts +1 -0
  160. package/src/playback_observer/core_playback_observer.ts +1 -3
  161. package/src/transports/dash/manifest_parser.ts +2 -0
  162. package/src/transports/smooth/segment_loader.ts +1 -0
  163. package/src/utils/assert.ts +1 -0
  164. package/src/utils/create_uuid.ts +2 -2
  165. package/src/utils/event_emitter.ts +1 -0
  166. package/src/utils/reference.ts +1 -0
@@ -1,10 +1,16 @@
1
1
  import { describe, beforeEach, it, expect, vi, afterEach } from "vitest";
2
2
  import isCodecSupported from "../is_codec_supported";
3
3
 
4
- const mocks = vi.hoisted(() => {
4
+ type IMockedMediaSource =
5
+ | {
6
+ isTypeSupported?: ((codec: string) => boolean) | undefined;
7
+ }
8
+ | undefined;
9
+
10
+ const mocks: { default: { MediaSource_: IMockedMediaSource } } = vi.hoisted(() => {
5
11
  return {
6
12
  default: {
7
- MediaSource_: {} as unknown,
13
+ MediaSource_: {},
8
14
  },
9
15
  };
10
16
  });
@@ -315,7 +315,7 @@ function defaultSetMediaKeys(
315
315
  try {
316
316
  let ret: unknown;
317
317
  if (typeof elt.setMediaKeys === "function") {
318
- // eslint-disable-next-line @typescript-eslint/no-restricted-types
318
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-restricted-types
319
319
  ret = elt.setMediaKeys(mediaKeys as MediaKeys);
320
320
  }
321
321
 
@@ -25,13 +25,12 @@ interface IWebKitSourceBufferConstructor {
25
25
  prototype: IWebKitSourceBuffer;
26
26
  }
27
27
 
28
- interface IWebKitSourceBuffer
29
- extends EventEmitter<{
30
- updatestart: Event;
31
- update: Event;
32
- updateend: Event;
33
- error: Event | Error;
34
- }> {
28
+ interface IWebKitSourceBuffer extends EventEmitter<{
29
+ updatestart: Event;
30
+ update: Event;
31
+ updateend: Event;
32
+ error: Event | Error;
33
+ }> {
35
34
  _emitUpdate?: (eventName: "error" | "update", val: unknown) => void;
36
35
  appendBuffer?: (data: BufferSource) => void;
37
36
  updating: boolean;
@@ -54,6 +54,7 @@ export default class PendingRequestsStore {
54
54
  public addProgress(progress: IPendingRequestStoreProgress): void {
55
55
  const request = this._currentRequests[progress.id];
56
56
  if (isNullOrUndefined(request)) {
57
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
57
58
  if ((__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.DEV as number)) {
58
59
  throw new Error("ABR: progress for a request not added");
59
60
  }
@@ -69,6 +70,7 @@ export default class PendingRequestsStore {
69
70
  */
70
71
  public remove(id: string): void {
71
72
  if (isNullOrUndefined(this._currentRequests[id])) {
73
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
72
74
  if ((__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.DEV as number)) {
73
75
  throw new Error("ABR: can't remove unknown request");
74
76
  }
@@ -143,6 +143,25 @@ describe("ContentTimeBoundariesObserver", () => {
143
143
  expect(mockTrigger).toHaveBeenCalledWith("warning", expect.any(Object));
144
144
  const warning = mockTrigger.mock.calls[0][1];
145
145
  expect(warning.code).toBe("MEDIA_TIME_BEFORE_MANIFEST");
146
+ expect(warning.timeInfo).toEqual({
147
+ position: 5,
148
+ minPosition: 10,
149
+ maxPosition: 100,
150
+ });
151
+ });
152
+
153
+ it("should not trigger warning when position is within epsilon of manifest minimum", async () => {
154
+ mockGetMinimumSafePosition.mockReturnValue(10);
155
+ const manifest = createMockManifest();
156
+ const playbackObserver = createMockPlaybackObserver();
157
+
158
+ new ContentTimeBoundariesObserver(manifest, playbackObserver, ["audio"]);
159
+ await sleep(0);
160
+
161
+ const positionCallback = mockPlaybackObserverListen.mock.calls[0][0];
162
+ positionCallback({ position: { getWanted: () => 9.9995 } });
163
+
164
+ expect(mockTrigger).not.toHaveBeenCalled();
146
165
  });
147
166
 
148
167
  it("should trigger warning when position is after manifest maximum", async () => {
@@ -500,6 +500,7 @@ function createMediaSourceInterfaceAndSegmentSinksStore(
500
500
  value: sentMediaSourceLink,
501
501
  mediaSourceId: mediaSourceInterface.id,
502
502
  },
503
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
503
504
  [handle.value as unknown as Transferable],
504
505
  );
505
506
  } else {
@@ -47,6 +47,12 @@ import TaskCanceller from "../../utils/task_canceller";
47
47
  * Manifest range.
48
48
  * @class ContentTimeBoundariesObserver
49
49
  */
50
+
51
+ // `HTMLMediaElement.currentTime` can be off by a very small amount from the
52
+ // Manifest-derived boundaries. We keep an epsilon so those rounding
53
+ // differences do not trigger spurious boundary warnings.
54
+ const EPSILON = 1e-3;
55
+
50
56
  export default class ContentTimeBoundariesObserver extends EventEmitter<IContentTimeBoundariesObserverEvent> {
51
57
  /** Allows to interrupt everything the `ContentTimeBoundariesObserver` is doing. */
52
58
  private _canceller: TaskCanceller;
@@ -113,20 +119,38 @@ export default class ContentTimeBoundariesObserver extends EventEmitter<IContent
113
119
  playbackObserver.listen(
114
120
  ({ position }) => {
115
121
  const wantedPosition = position.getWanted();
116
- if (wantedPosition < manifest.getMinimumSafePosition()) {
122
+ const minimumPosition = manifest.getMinimumSafePosition();
123
+ const maximumPosition = maximumPositionCalculator.getMaximumAvailablePosition();
124
+
125
+ // Only report "before manifest" when the position is meaningfully
126
+ // behind the minimum, not when `<video>.currentTime` and the
127
+ // Manifest disagree by a tiny floating-point delta.
128
+ if (wantedPosition < minimumPosition - EPSILON) {
117
129
  const warning = new MediaError(
118
130
  "MEDIA_TIME_BEFORE_MANIFEST",
119
131
  "The current position is behind the " +
120
132
  "earliest time announced in the Manifest.",
133
+ {
134
+ timeInfo: {
135
+ position: wantedPosition,
136
+ minPosition: minimumPosition,
137
+ maxPosition: maximumPosition,
138
+ },
139
+ },
121
140
  );
122
141
  this.trigger("warning", warning);
123
- } else if (
124
- wantedPosition > maximumPositionCalculator.getMaximumAvailablePosition()
125
- ) {
142
+ } else if (wantedPosition > maximumPosition + EPSILON) {
126
143
  const warning = new MediaError(
127
144
  "MEDIA_TIME_AFTER_MANIFEST",
128
145
  "The current position is after the latest " +
129
146
  "time announced in the Manifest.",
147
+ {
148
+ timeInfo: {
149
+ position: wantedPosition,
150
+ minPosition: minimumPosition,
151
+ maxPosition: maximumPosition,
152
+ },
153
+ },
130
154
  );
131
155
  this.trigger("warning", warning);
132
156
  }
@@ -349,6 +349,7 @@ export default class AudioVideoSegmentSink extends SegmentSink {
349
349
  */
350
350
  function assertDataIsBufferSource(data: unknown): asserts data is BufferSource {
351
351
  if (
352
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
352
353
  (__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.PRODUCTION as number)
353
354
  ) {
354
355
  return;
@@ -191,6 +191,7 @@ function assertChunkIsTextTrackSegmentData(
191
191
  chunk: unknown,
192
192
  ): asserts chunk is ITextTracksBufferSegmentData {
193
193
  if (
194
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
194
195
  (__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.PRODUCTION as number)
195
196
  ) {
196
197
  return;
@@ -285,6 +286,7 @@ export interface ITextDisplayerInterface {
285
286
  * It doesn't correspond at all to real code that will be called. This is just
286
287
  * a hack to tell TypeScript to perform that check.
287
288
  */
289
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
288
290
  if ((__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.DEV as number)) {
289
291
  // @ts-expect-error: unused function for type checking
290
292
  function _checkType(input: ITextTrackSegmentData): void {
@@ -36,6 +36,7 @@ export default class ManualTimeRanges implements TimeRanges {
36
36
  }
37
37
 
38
38
  insert(start: number, end: number): void {
39
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
39
40
  if ((__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.DEV as number)) {
40
41
  assert(start >= 0, "invalid start time");
41
42
  assert(end - start > 0, "invalid end time");
@@ -45,6 +46,7 @@ export default class ManualTimeRanges implements TimeRanges {
45
46
  }
46
47
 
47
48
  remove(start: number, end: number): void {
49
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
48
50
  if ((__ENVIRONMENT__.CURRENT_ENV as number) === (__ENVIRONMENT__.DEV as number)) {
49
51
  assert(start >= 0, "invalid start time");
50
52
  assert(end - start > 0, "invalid end time");
@@ -15,8 +15,10 @@ import type {
15
15
  } from "../representation";
16
16
 
17
17
  /** Callbacks called by the `AdaptationStream` on various events. */
18
- export interface IAdaptationStreamCallbacks
19
- extends Omit<IRepresentationStreamCallbacks, "terminating" | "addedSegment"> {
18
+ export interface IAdaptationStreamCallbacks extends Omit<
19
+ IRepresentationStreamCallbacks,
20
+ "terminating" | "addedSegment"
21
+ > {
20
22
  /** Called as new bitrate estimates are done. */
21
23
  bitrateEstimateChange(payload: IBitrateEstimateChangePayload): void;
22
24
  /**
@@ -111,8 +113,7 @@ export interface INeedsBufferFlushPayload {
111
113
  }
112
114
 
113
115
  /** Regular playback information needed by the AdaptationStream. */
114
- export interface IAdaptationStreamPlaybackObservation
115
- extends IRepresentationStreamPlaybackObservation {
116
+ export interface IAdaptationStreamPlaybackObservation extends IRepresentationStreamPlaybackObservation {
116
117
  /**
117
118
  * For the current SegmentSink, difference in seconds between the next position
118
119
  * where no segment data is available and the current position.
@@ -710,8 +710,10 @@ export type IStreamOrchestratorOptions = IPeriodStreamOptions & {
710
710
  };
711
711
 
712
712
  /** Callbacks called by the `StreamOrchestrator` on various events. */
713
- export interface IStreamOrchestratorCallbacks
714
- extends Omit<IPeriodStreamCallbacks, "waitingMediaSourceReload"> {
713
+ export interface IStreamOrchestratorCallbacks extends Omit<
714
+ IPeriodStreamCallbacks,
715
+ "waitingMediaSourceReload"
716
+ > {
715
717
  /**
716
718
  * Called when a `PeriodStream` has been removed.
717
719
  * This event can be used for clean-up purposes. For example, you are free to
@@ -1,10 +1,13 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import MediaError from "../media_error";
2
+ import type { ISerializedMediaError } from "../media_error";
3
+ import MediaError, { deserializeMediaError } from "../media_error";
3
4
 
4
5
  describe("errors - MediaError", () => {
5
6
  it("should format a MediaError", () => {
6
7
  const reason = "test";
7
- const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", reason);
8
+ const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", reason, {
9
+ timeInfo: { position: 5, minPosition: 10, maxPosition: 100 },
10
+ });
8
11
  expect(mediaError).toBeInstanceOf(Error);
9
12
  expect(mediaError.name).toBe("MediaError");
10
13
  expect(mediaError.type).toBe("MEDIA_ERROR");
@@ -15,7 +18,9 @@ describe("errors - MediaError", () => {
15
18
 
16
19
  it("should be able to set it as fatal", () => {
17
20
  const reason = "test";
18
- const mediaError = new MediaError("MEDIA_TIME_AFTER_MANIFEST", reason);
21
+ const mediaError = new MediaError("MEDIA_TIME_AFTER_MANIFEST", reason, {
22
+ timeInfo: { position: 5, minPosition: 10, maxPosition: 100 },
23
+ });
19
24
  mediaError.fatal = true;
20
25
  expect(mediaError).toBeInstanceOf(Error);
21
26
  expect(mediaError.name).toBe("MediaError");
@@ -36,4 +41,102 @@ describe("errors - MediaError", () => {
36
41
  expect(mediaError.fatal).toBe(true);
37
42
  expect(mediaError.message).toBe("MEDIA_ERR_NETWORK: test");
38
43
  });
44
+
45
+ it("should expose and serialize custom position metadata", () => {
46
+ const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", "test", {
47
+ timeInfo: {
48
+ position: 3,
49
+ minPosition: 10,
50
+ maxPosition: 20,
51
+ },
52
+ });
53
+
54
+ expect(mediaError.timeInfo).toEqual({
55
+ position: 3,
56
+ minPosition: 10,
57
+ maxPosition: 20,
58
+ });
59
+ expect(mediaError.serialize()).toEqual({
60
+ isSerializedError: true,
61
+ name: "MediaError",
62
+ code: "MEDIA_TIME_BEFORE_MANIFEST",
63
+ reason: "test",
64
+ tracks: undefined,
65
+ timeInfo: {
66
+ position: 3,
67
+ minPosition: 10,
68
+ maxPosition: 20,
69
+ },
70
+ });
71
+ });
72
+
73
+ it("should deserialize a serialized MediaError with timeInfo", () => {
74
+ const serializedError: ISerializedMediaError = {
75
+ isSerializedError: true,
76
+ name: "MediaError",
77
+ code: "MEDIA_TIME_BEFORE_MANIFEST",
78
+ reason: "test",
79
+ tracks: undefined,
80
+ timeInfo: {
81
+ position: 3,
82
+ minPosition: 10,
83
+ maxPosition: 20,
84
+ },
85
+ };
86
+
87
+ const mediaError = deserializeMediaError(serializedError);
88
+
89
+ expect(mediaError).toBeInstanceOf(MediaError);
90
+ expect(mediaError.code).toBe("MEDIA_TIME_BEFORE_MANIFEST");
91
+ expect(mediaError.message).toBe("MEDIA_TIME_BEFORE_MANIFEST: test");
92
+ expect(mediaError.timeInfo).toEqual(serializedError.timeInfo);
93
+ });
94
+
95
+ it("should deserialize a serialized MediaError with tracks", () => {
96
+ const serializedError: ISerializedMediaError = {
97
+ isSerializedError: true,
98
+ name: "MediaError",
99
+ code: "BUFFER_APPEND_ERROR",
100
+ reason: "test",
101
+ tracks: [
102
+ {
103
+ type: "audio",
104
+ track: {
105
+ id: "fra1",
106
+ audioDescription: false,
107
+ language: "fra",
108
+ normalized: "fra",
109
+ representations: [],
110
+ },
111
+ },
112
+ ],
113
+ timeInfo: undefined,
114
+ };
115
+
116
+ const mediaError = deserializeMediaError(serializedError);
117
+
118
+ expect(mediaError).toBeInstanceOf(MediaError);
119
+ expect(mediaError.code).toBe("BUFFER_APPEND_ERROR");
120
+ expect(mediaError.message).toBe("BUFFER_APPEND_ERROR: test");
121
+ expect(mediaError.tracksInfo).toEqual(serializedError.tracks);
122
+ });
123
+
124
+ it("should deserialize a serialized MediaError without metadata", () => {
125
+ const serializedError = {
126
+ isSerializedError: true as const,
127
+ name: "MediaError" as const,
128
+ code: "MEDIA_ERR_NETWORK" as const,
129
+ reason: "test",
130
+ tracks: undefined,
131
+ timeInfo: undefined,
132
+ };
133
+
134
+ const mediaError = deserializeMediaError(serializedError);
135
+
136
+ expect(mediaError).toBeInstanceOf(MediaError);
137
+ expect(mediaError.code).toBe("MEDIA_ERR_NETWORK");
138
+ expect(mediaError.message).toBe("MEDIA_ERR_NETWORK: test");
139
+ expect(mediaError.timeInfo).toBeUndefined();
140
+ expect(mediaError.tracksInfo).toBeUndefined();
141
+ });
39
142
  });
@@ -22,7 +22,7 @@ import { ErrorCodes, ErrorTypes, NetworkErrorTypes } from "./error_codes";
22
22
  import formatError from "./format_error";
23
23
  import isKnownError from "./is_known_error";
24
24
  import type { ISerializedMediaError } from "./media_error";
25
- import MediaError from "./media_error";
25
+ import MediaError, { deserializeMediaError } from "./media_error";
26
26
  import type { ISerializedNetworkError } from "./network_error";
27
27
  import NetworkError from "./network_error";
28
28
  import type { ISerializedOtherError } from "./other_error";
@@ -46,6 +46,7 @@ export {
46
46
  ErrorTypes,
47
47
  formatError,
48
48
  MediaError,
49
+ deserializeMediaError,
49
50
  NetworkError,
50
51
  OtherError,
51
52
  NetworkErrorTypes,
@@ -15,16 +15,27 @@
15
15
  */
16
16
 
17
17
  import type { ITaggedTrack } from "../manifest";
18
+ import assert from "../utils/assert";
18
19
  import type { IMediaErrorCode } from "./error_codes";
19
20
  import { ErrorTypes } from "./error_codes";
20
21
  import errorMessage from "./error_message";
21
22
 
22
- type ICodeWithAdaptationType =
23
+ export type ICodeWithAdaptationType =
23
24
  | "BUFFER_APPEND_ERROR"
24
25
  | "BUFFER_FULL_ERROR"
25
26
  | "NO_PLAYABLE_REPRESENTATION"
26
27
  | "MANIFEST_INCOMPATIBLE_CODECS_ERROR";
27
28
 
29
+ export type ICodeWithManifestPositionError =
30
+ | "MEDIA_TIME_BEFORE_MANIFEST"
31
+ | "MEDIA_TIME_AFTER_MANIFEST";
32
+
33
+ interface ITimeInfo {
34
+ position: number;
35
+ minPosition: number;
36
+ maxPosition: number;
37
+ }
38
+
28
39
  /**
29
40
  * Error linked to the media Playback.
30
41
  *
@@ -36,6 +47,7 @@ export default class MediaError extends Error {
36
47
  public readonly type: "MEDIA_ERROR";
37
48
  public readonly code: IMediaErrorCode;
38
49
  public readonly tracksInfo: ITaggedTrack[] | undefined;
50
+ public readonly timeInfo: ITimeInfo | undefined;
39
51
  public fatal: boolean;
40
52
  private _originalMessage: string;
41
53
 
@@ -52,13 +64,18 @@ export default class MediaError extends Error {
52
64
  },
53
65
  );
54
66
  constructor(
55
- code: Exclude<IMediaErrorCode, ICodeWithAdaptationType>,
67
+ code: ICodeWithManifestPositionError,
68
+ reason: string,
69
+ context: {
70
+ timeInfo: ITimeInfo;
71
+ },
72
+ );
73
+ constructor(
74
+ code: Exclude<
75
+ IMediaErrorCode,
76
+ ICodeWithAdaptationType | ICodeWithManifestPositionError
77
+ >,
56
78
  reason: string,
57
- context?:
58
- | {
59
- tracks?: undefined;
60
- }
61
- | undefined,
62
79
  );
63
80
  constructor(
64
81
  code: IMediaErrorCode,
@@ -66,6 +83,7 @@ export default class MediaError extends Error {
66
83
  context?:
67
84
  | {
68
85
  tracks?: ITaggedTrack[] | undefined;
86
+ timeInfo?: ITimeInfo;
69
87
  }
70
88
  | undefined,
71
89
  ) {
@@ -82,6 +100,9 @@ export default class MediaError extends Error {
82
100
  if (context?.tracks !== undefined && context?.tracks.length > 0) {
83
101
  this.tracksInfo = context.tracks;
84
102
  }
103
+ if (context?.timeInfo !== undefined) {
104
+ this.timeInfo = context.timeInfo;
105
+ }
85
106
  }
86
107
 
87
108
  /**
@@ -97,10 +118,44 @@ export default class MediaError extends Error {
97
118
  code: this.code,
98
119
  reason: this._originalMessage,
99
120
  tracks: this.tracksInfo,
121
+ timeInfo: this.timeInfo,
100
122
  };
101
123
  }
102
124
  }
103
125
 
126
+ /**
127
+ * Re-create a `MediaError` from a serialized representation.
128
+ * @param {Object} serializedMediaError
129
+ * @returns {MediaError}
130
+ */
131
+ export function deserializeMediaError(
132
+ serializedMediaError: ISerializedMediaError,
133
+ ): MediaError {
134
+ if (
135
+ serializedMediaError.code === "MEDIA_TIME_BEFORE_MANIFEST" ||
136
+ serializedMediaError.code === "MEDIA_TIME_AFTER_MANIFEST"
137
+ ) {
138
+ assert(
139
+ serializedMediaError.timeInfo !== undefined,
140
+ `The MediaError with code ${serializedMediaError.code} does not provide "timeinfo"`,
141
+ );
142
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason, {
143
+ timeInfo: serializedMediaError.timeInfo,
144
+ });
145
+ }
146
+ if (
147
+ serializedMediaError.code === "BUFFER_APPEND_ERROR" ||
148
+ serializedMediaError.code === "BUFFER_FULL_ERROR" ||
149
+ serializedMediaError.code === "NO_PLAYABLE_REPRESENTATION" ||
150
+ serializedMediaError.code === "MANIFEST_INCOMPATIBLE_CODECS_ERROR"
151
+ ) {
152
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason, {
153
+ tracks: serializedMediaError.tracks,
154
+ });
155
+ }
156
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason);
157
+ }
158
+
104
159
  /** Serializable object which allows to create a `MediaError` later. */
105
160
  export interface ISerializedMediaError {
106
161
  isSerializedError: true;
@@ -108,4 +163,5 @@ export interface ISerializedMediaError {
108
163
  code: IMediaErrorCode;
109
164
  reason: string;
110
165
  tracks: ITaggedTrack[] | undefined;
166
+ timeInfo?: ITimeInfo | undefined;
111
167
  }
@@ -1,10 +1,16 @@
1
1
  import { describe, afterEach, it, expect, vi, beforeEach } from "vitest";
2
2
  import probeHDCPPolicy from "../../probers/HDCPPolicy";
3
3
 
4
- const compatEmeMock = vi.hoisted(() => {
4
+ type IRequestMediaKeySystemAccess =
5
+ | ((...args: unknown[]) => Promise<unknown>)
6
+ | undefined;
7
+
8
+ const compatEmeMock: {
9
+ default: { requestMediaKeySystemAccess: IRequestMediaKeySystemAccess };
10
+ } = vi.hoisted(() => {
5
11
  return {
6
12
  default: {
7
- requestMediaKeySystemAccess: undefined as unknown,
13
+ requestMediaKeySystemAccess: undefined,
8
14
  },
9
15
  };
10
16
  });
@@ -4,10 +4,14 @@ import {
4
4
  findDefaultAudioCodec,
5
5
  } from "../../probers/defaultCodecsFinder";
6
6
 
7
- const mocks = vi.hoisted(() => {
7
+ type IMockedMediaSource = {
8
+ isTypeSupported?: ((codec: string) => boolean) | undefined;
9
+ } | null;
10
+
11
+ const mocks: { default: { MediaSource_: IMockedMediaSource } } = vi.hoisted(() => {
8
12
  return {
9
13
  default: {
10
- MediaSource_: {} as unknown,
14
+ MediaSource_: {},
11
15
  },
12
16
  };
13
17
  });
@@ -2,10 +2,14 @@ import { describe, beforeEach, it, expect, vi, afterEach } from "vitest";
2
2
  import probeMediaContentType from "../../probers/mediaContentType";
3
3
  import type { IMediaConfiguration } from "../../types";
4
4
 
5
- const mocks = vi.hoisted(() => {
5
+ type IMockedMediaSource = {
6
+ isTypeSupported?: ((codec: string) => boolean) | boolean | undefined;
7
+ } | null;
8
+
9
+ const mocks: { default: { MediaSource_: IMockedMediaSource } } = vi.hoisted(() => {
6
10
  return {
7
11
  default: {
8
- MediaSource_: {} as unknown,
12
+ MediaSource_: {},
9
13
  },
10
14
  };
11
15
  });
@@ -83,6 +83,7 @@ export function filterConfigurationWithCapabilities(
83
83
  }
84
84
  )[capability] !== undefined
85
85
  ) {
86
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
86
87
  (probedConfig as { [id: string]: string | IMediaConfiguration })[capability] = (
87
88
  configuration as { [id: string]: string | IMediaConfiguration }
88
89
  )[capability];
@@ -105,6 +106,7 @@ export function filterConfigurationWithCapabilities(
105
106
  Object.keys((configuration as { [id: string]: IMediaConfiguration })[key])
106
107
  .length === 0)
107
108
  ) {
109
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
108
110
  (probedConfig as { [id: string]: IMediaConfiguration })[key] = subProbedConfig;
109
111
  }
110
112
  }
@@ -63,6 +63,7 @@ export default class RxPlayerWorker<
63
63
  globalScope.addEventListener("message", (evt: MessageEvent<IMainThreadMessage>) => {
64
64
  if (evt.data.type === MainThreadMessageType.AppDefined) {
65
65
  const { name, payload } = evt.data.value;
66
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
66
67
  const listeners = this._messageListeners.get(name as keyof TReceivingEventRecord);
67
68
  if (listeners === undefined) {
68
69
  return;