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

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 (251) hide show
  1. package/CHANGELOG.md +22 -5
  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/can_patch_out_pssh.d.ts +42 -0
  8. package/dist/commonjs/compat/can_patch_out_pssh.d.ts.map +1 -0
  9. package/dist/commonjs/compat/can_patch_out_pssh.js +53 -0
  10. package/dist/commonjs/compat/env_detector.d.ts +2 -0
  11. package/dist/commonjs/compat/env_detector.d.ts.map +1 -1
  12. package/dist/commonjs/compat/env_detector.js +5 -0
  13. package/dist/commonjs/core/adaptive/network_analyzer.d.ts +1 -2
  14. package/dist/commonjs/core/adaptive/network_analyzer.d.ts.map +1 -1
  15. package/dist/commonjs/core/adaptive/network_analyzer.js +3 -3
  16. package/dist/commonjs/core/adaptive/utils/representation_score_calculator.js +2 -2
  17. package/dist/commonjs/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
  18. package/dist/commonjs/core/cmcd/cmcd_data_builder.js +9 -3
  19. package/dist/commonjs/core/entry/content_preparer.d.ts.map +1 -1
  20. package/dist/commonjs/core/entry/content_preparer.js +5 -7
  21. package/dist/commonjs/core/entry/core_entry.d.ts.map +1 -1
  22. package/dist/commonjs/core/entry/core_entry.js +6 -2
  23. package/dist/commonjs/core/entry/core_text_displayer_interface.js +3 -3
  24. package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
  25. package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.js +12 -0
  26. package/dist/commonjs/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
  27. package/dist/commonjs/core/fetchers/utils/schedule_request.d.ts.map +1 -1
  28. package/dist/commonjs/core/fetchers/utils/schedule_request.js +4 -3
  29. package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts +0 -2
  30. package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts.map +1 -1
  31. package/dist/commonjs/core/segment_sinks/garbage_collector.js +0 -3
  32. package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
  33. package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
  34. package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
  35. package/dist/commonjs/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
  36. package/dist/commonjs/core/stream/adaptation/adaptation_stream.js +6 -6
  37. package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  38. package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.js +37 -40
  39. package/dist/commonjs/default_config.d.ts +5 -0
  40. package/dist/commonjs/default_config.d.ts.map +1 -1
  41. package/dist/commonjs/default_config.js +5 -0
  42. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  43. package/dist/commonjs/main_thread/api/public_api.js +23 -16
  44. package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  45. package/dist/commonjs/main_thread/decrypt/content_decryptor.js +4 -1
  46. package/dist/commonjs/main_thread/decrypt/session_events_listener.js +1 -1
  47. package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts +2 -0
  48. package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
  49. package/dist/commonjs/main_thread/decrypt/set_server_certificate.js +4 -0
  50. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +0 -8
  51. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  52. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +58 -50
  53. package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
  54. package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
  55. package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
  56. package/dist/commonjs/main_thread/render_thumbnail.d.ts.map +1 -1
  57. package/dist/commonjs/main_thread/render_thumbnail.js +4 -0
  58. package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
  59. package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.js +1 -0
  60. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +1 -1
  61. package/dist/commonjs/main_thread/tracks_store/tracks_store.js +1 -1
  62. package/dist/{es2017/parsers/containers/isobmff/take_pssh_out.d.ts → commonjs/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
  63. package/dist/commonjs/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
  64. package/dist/commonjs/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +22 -17
  65. package/dist/commonjs/parsers/containers/isobmff/index.d.ts +2 -2
  66. package/dist/commonjs/parsers/containers/isobmff/index.d.ts.map +1 -1
  67. package/dist/commonjs/parsers/containers/isobmff/index.js +4 -4
  68. package/dist/commonjs/playback_observer/core_playback_observer.d.ts +4 -4
  69. package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +1 -1
  70. package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts +1 -2
  71. package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts.map +1 -1
  72. package/dist/commonjs/transports/dash/segment_parser.js +1 -1
  73. package/dist/commonjs/transports/local/segment_parser.js +1 -1
  74. package/dist/commonjs/utils/test-utils.d.ts +30 -0
  75. package/dist/commonjs/utils/test-utils.d.ts.map +1 -0
  76. package/dist/commonjs/utils/test-utils.js +79 -0
  77. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  78. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  79. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  80. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  81. package/dist/es2017/compat/can_patch_out_pssh.d.ts +42 -0
  82. package/dist/es2017/compat/can_patch_out_pssh.d.ts.map +1 -0
  83. package/dist/es2017/compat/can_patch_out_pssh.js +50 -0
  84. package/dist/es2017/compat/env_detector.d.ts +2 -0
  85. package/dist/es2017/compat/env_detector.d.ts.map +1 -1
  86. package/dist/es2017/compat/env_detector.js +5 -0
  87. package/dist/es2017/core/adaptive/network_analyzer.d.ts +1 -2
  88. package/dist/es2017/core/adaptive/network_analyzer.d.ts.map +1 -1
  89. package/dist/es2017/core/adaptive/network_analyzer.js +3 -3
  90. package/dist/es2017/core/adaptive/utils/representation_score_calculator.js +2 -2
  91. package/dist/es2017/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
  92. package/dist/es2017/core/cmcd/cmcd_data_builder.js +9 -3
  93. package/dist/es2017/core/entry/content_preparer.d.ts.map +1 -1
  94. package/dist/es2017/core/entry/content_preparer.js +3 -5
  95. package/dist/es2017/core/entry/core_entry.d.ts.map +1 -1
  96. package/dist/es2017/core/entry/core_entry.js +6 -2
  97. package/dist/es2017/core/entry/core_text_displayer_interface.js +3 -3
  98. package/dist/es2017/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
  99. package/dist/es2017/core/fetchers/manifest/manifest_fetcher.js +12 -0
  100. package/dist/es2017/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
  101. package/dist/es2017/core/fetchers/utils/schedule_request.d.ts.map +1 -1
  102. package/dist/es2017/core/fetchers/utils/schedule_request.js +2 -3
  103. package/dist/es2017/core/segment_sinks/garbage_collector.d.ts +0 -2
  104. package/dist/es2017/core/segment_sinks/garbage_collector.d.ts.map +1 -1
  105. package/dist/es2017/core/segment_sinks/garbage_collector.js +0 -3
  106. package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
  107. package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
  108. package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
  109. package/dist/es2017/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
  110. package/dist/es2017/core/stream/adaptation/adaptation_stream.js +6 -6
  111. package/dist/es2017/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  112. package/dist/es2017/core/stream/orchestrator/stream_orchestrator.js +40 -39
  113. package/dist/es2017/default_config.d.ts +5 -0
  114. package/dist/es2017/default_config.d.ts.map +1 -1
  115. package/dist/es2017/default_config.js +5 -0
  116. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  117. package/dist/es2017/main_thread/api/public_api.js +20 -13
  118. package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  119. package/dist/es2017/main_thread/decrypt/content_decryptor.js +4 -1
  120. package/dist/es2017/main_thread/decrypt/session_events_listener.js +1 -1
  121. package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts +2 -0
  122. package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
  123. package/dist/es2017/main_thread/decrypt/set_server_certificate.js +4 -0
  124. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +0 -8
  125. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  126. package/dist/es2017/main_thread/init/media_source_content_initializer.js +58 -50
  127. package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
  128. package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
  129. package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
  130. package/dist/es2017/main_thread/render_thumbnail.d.ts.map +1 -1
  131. package/dist/es2017/main_thread/render_thumbnail.js +4 -0
  132. package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
  133. package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.js +1 -0
  134. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +1 -1
  135. package/dist/es2017/main_thread/tracks_store/tracks_store.js +1 -1
  136. package/dist/{commonjs/parsers/containers/isobmff/take_pssh_out.d.ts → es2017/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
  137. package/dist/es2017/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
  138. package/dist/es2017/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +21 -16
  139. package/dist/es2017/parsers/containers/isobmff/index.d.ts +2 -2
  140. package/dist/es2017/parsers/containers/isobmff/index.d.ts.map +1 -1
  141. package/dist/es2017/parsers/containers/isobmff/index.js +2 -2
  142. package/dist/es2017/playback_observer/core_playback_observer.d.ts +4 -4
  143. package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +1 -1
  144. package/dist/es2017/playback_observer/media_element_playback_observer.d.ts +1 -2
  145. package/dist/es2017/playback_observer/media_element_playback_observer.d.ts.map +1 -1
  146. package/dist/es2017/transports/dash/segment_parser.js +2 -2
  147. package/dist/es2017/transports/local/segment_parser.js +2 -2
  148. package/dist/es2017/utils/test-utils.d.ts +30 -0
  149. package/dist/es2017/utils/test-utils.d.ts.map +1 -0
  150. package/dist/es2017/utils/test-utils.js +36 -0
  151. package/dist/mpd-parser.wasm +0 -0
  152. package/dist/worker.js +6 -6
  153. package/package.json +4 -2
  154. package/src/README.md +7 -7
  155. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  156. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  157. package/src/compat/__tests__/can_patch_out_pssh.test.ts +40 -0
  158. package/src/compat/can_patch_out_pssh.ts +53 -0
  159. package/src/compat/env_detector.ts +4 -0
  160. package/src/core/README.md +1 -1
  161. package/src/core/adaptive/README.md +3 -3
  162. package/src/core/adaptive/__tests__/adaptive_representation_selector.test.ts +181 -110
  163. package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +229 -123
  164. package/src/core/adaptive/__tests__/mocks.ts +100 -0
  165. package/src/core/adaptive/__tests__/network_analyzer.test.ts +152 -59
  166. package/src/core/adaptive/network_analyzer.ts +4 -4
  167. package/src/core/adaptive/utils/__tests__/filter_by_bitrate.test.ts +11 -19
  168. package/src/core/adaptive/utils/__tests__/filter_by_resolution.test.ts +12 -12
  169. package/src/core/adaptive/utils/__tests__/last_estimate_storage.test.ts +25 -12
  170. package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +13 -9
  171. package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +11 -11
  172. package/src/core/adaptive/utils/__tests__/select_optimal_representation.test.ts +13 -23
  173. package/src/core/adaptive/utils/representation_score_calculator.ts +2 -2
  174. package/src/core/cmcd/__tests__/cmcd_data_builder.test.ts +60 -15
  175. package/src/core/cmcd/cmcd_data_builder.ts +9 -3
  176. package/src/core/entry/README.md +2 -2
  177. package/src/core/entry/__tests__/core_text_displayer_interface.test.ts +20 -0
  178. package/src/core/entry/content_preparer.ts +2 -5
  179. package/src/core/entry/core_entry.ts +6 -2
  180. package/src/core/entry/core_text_displayer_interface.ts +3 -3
  181. package/src/core/fetchers/manifest/__tests__/manifest_fetcher.test.ts +52 -3
  182. package/src/core/fetchers/manifest/manifest_fetcher.ts +12 -0
  183. package/src/core/fetchers/thumbnails/__tests__/thumbnail_fetcher.test.ts +70 -0
  184. package/src/core/fetchers/thumbnails/thumbnail_fetcher.ts +1 -1
  185. package/src/core/fetchers/utils/schedule_request.ts +5 -3
  186. package/src/core/segment_sinks/__tests__/garbage_collector.test.ts +434 -0
  187. package/src/core/segment_sinks/__tests__/mocks.ts +49 -0
  188. package/src/core/segment_sinks/garbage_collector.ts +0 -3
  189. package/src/core/segment_sinks/implementations/text/__tests__/text_segment_sink.test.ts +177 -0
  190. package/src/core/segment_sinks/implementations/text/text_segment_sink.ts +2 -2
  191. package/src/core/segment_sinks/inventory/__tests__/buffered_history.test.ts +215 -0
  192. package/src/core/segment_sinks/inventory/__tests__/segment_inventory.test.ts +448 -0
  193. package/src/core/stream/adaptation/__tests__/adaptation_stream.test.ts +973 -0
  194. package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +283 -374
  195. package/src/core/stream/adaptation/adaptation_stream.ts +6 -8
  196. package/src/core/stream/orchestrator/README.md +4 -4
  197. package/src/core/stream/orchestrator/__tests__/stream_orchestrator.test.ts +707 -0
  198. package/src/core/stream/orchestrator/stream_orchestrator.ts +41 -46
  199. package/src/core/stream/period/utils/__tests__/get_adaptation_switch_strategy.test.ts +290 -220
  200. package/src/core/stream/representation/__tests__/encryption_data_notifier.test.ts +93 -63
  201. package/src/core/stream/representation/utils/__tests__/append_segment_to_buffer.test.ts +106 -63
  202. package/src/core/stream/representation/utils/__tests__/check_for_discontinuity.test.ts +179 -204
  203. package/src/core/stream/representation/utils/__tests__/get_segment_priority.test.ts +7 -7
  204. package/src/core/stream/representation/utils/__tests__/push_init_segment.test.ts +103 -60
  205. package/src/core/stream/representation/utils/__tests__/push_media_segment.test.ts +231 -165
  206. package/src/default_config.ts +6 -0
  207. package/src/experimental/README.md +1 -1
  208. package/src/features/README.md +3 -3
  209. package/src/main_thread/api/README.md +6 -7
  210. package/src/main_thread/api/public_api.ts +16 -10
  211. package/src/main_thread/decrypt/README.md +4 -4
  212. package/src/main_thread/decrypt/__tests__/__global__/content_decryptor.test.ts +135 -0
  213. package/src/main_thread/decrypt/__tests__/__global__/get_license.test.ts +70 -0
  214. package/src/main_thread/decrypt/__tests__/__global__/server_certificate.test.ts +44 -0
  215. package/src/main_thread/decrypt/__tests__/__global__/utils.ts +2 -2
  216. package/src/main_thread/decrypt/content_decryptor.ts +6 -1
  217. package/src/main_thread/decrypt/session_events_listener.ts +1 -1
  218. package/src/main_thread/decrypt/set_server_certificate.ts +5 -0
  219. package/src/main_thread/init/media_source_content_initializer.ts +69 -55
  220. package/src/main_thread/init/utils/__tests__/stream_events_emitter.test.ts +175 -0
  221. package/src/main_thread/init/utils/stream_events_emitter/stream_events_emitter.ts +90 -26
  222. package/src/main_thread/render_thumbnail.ts +4 -0
  223. package/src/main_thread/tracks_store/README.md +12 -0
  224. package/src/main_thread/tracks_store/__tests__/media_element_tracks_store.test.ts +25 -18
  225. package/src/main_thread/tracks_store/media_element_tracks_store.ts +1 -0
  226. package/src/main_thread/tracks_store/tracks_store.ts +1 -1
  227. package/src/manifest/classes/__tests__/mocks.ts +202 -0
  228. package/src/parsers/containers/isobmff/__tests__/extract_pssh.test.ts +199 -0
  229. package/src/parsers/containers/isobmff/{take_pssh_out.ts → extract_pssh.ts} +21 -17
  230. package/src/parsers/containers/isobmff/index.ts +2 -2
  231. package/src/parsers/manifest/dash/wasm-parser/README.md +9 -9
  232. package/src/playback_observer/__tests__/mocks.ts +152 -0
  233. package/src/playback_observer/core_playback_observer.ts +4 -4
  234. package/src/playback_observer/media_element_playback_observer.ts +1 -1
  235. package/src/tools/README.md +2 -2
  236. package/src/transports/README.md +5 -5
  237. package/src/transports/dash/segment_parser.ts +2 -2
  238. package/src/transports/local/segment_parser.ts +2 -2
  239. package/src/transports/metaplaylist/README.md +4 -4
  240. package/src/utils/README.md +3 -3
  241. package/src/utils/test-utils.ts +50 -0
  242. package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
  243. package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
  244. package/dist/commonjs/core/entry/synchronize_sinks_on_observation.js +0 -20
  245. package/dist/commonjs/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
  246. package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
  247. package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
  248. package/dist/es2017/core/entry/synchronize_sinks_on_observation.js +0 -17
  249. package/dist/es2017/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
  250. package/src/core/adaptive/utils/__tests__/bandwith_estimator.test.ts +0 -117
  251. package/src/core/entry/synchronize_sinks_on_observation.ts +0 -22
@@ -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.2026033100";
440
+ this.version = /* PLAYER_VERSION */ "4.5.0-dev.2026041501";
441
441
  this.log = log;
442
442
  this.state = "STOPPED";
443
443
  this.videoElement = videoElement;
@@ -1244,31 +1244,31 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1244
1244
  ) {
1245
1245
  log.warn(
1246
1246
  "API",
1247
- "You only set a representationFilter function in a mulithreaded mode, ignoring it...",
1247
+ "You set a `representationFilter` function without a `workerId` while loading in multithread mode. That function cannot run in the worker, so it will be ignored.",
1248
1248
  );
1249
1249
  }
1250
1250
  }
1251
1251
  if (
1252
1252
  transportOptions.manifestLoader !== undefined &&
1253
- !isNullOrUndefined(transportOptions.manifestLoader.workerId)
1253
+ !isNullOrUndefined(transportOptions.manifestLoader.fn)
1254
1254
  ) {
1255
1255
  transportOptions.manifestLoader.fn = undefined;
1256
- if (isNullOrUndefined(transportOptions.manifestLoader.fn)) {
1256
+ if (isNullOrUndefined(transportOptions.manifestLoader.workerId)) {
1257
1257
  log.warn(
1258
1258
  "API",
1259
- "You only set a manifestLoader function in a mulithreaded mode, ignoring it...",
1259
+ "You set a `manifestLoader` function without a `workerId` while loading in multithread mode. That function cannot run in the worker, so it will be ignored.",
1260
1260
  );
1261
1261
  }
1262
1262
  }
1263
1263
  if (
1264
1264
  transportOptions.segmentLoader !== undefined &&
1265
- !isNullOrUndefined(transportOptions.segmentLoader.workerId)
1265
+ !isNullOrUndefined(transportOptions.segmentLoader.fn)
1266
1266
  ) {
1267
1267
  transportOptions.segmentLoader.fn = undefined;
1268
- if (isNullOrUndefined(transportOptions.segmentLoader.fn)) {
1268
+ if (isNullOrUndefined(transportOptions.segmentLoader.workerId)) {
1269
1269
  log.warn(
1270
1270
  "API",
1271
- "You only set a segmentLoader function in a mulithreaded mode, ignoring it...",
1271
+ "You set a `segmentLoader` function without a `workerId` while loading in multithread mode. That function cannot run in the worker, so it will be ignored.",
1272
1272
  );
1273
1273
  }
1274
1274
  }
@@ -3029,6 +3029,9 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3029
3029
  contentInfos: IPublicApiContentInfos,
3030
3030
  updates: IPeriodsUpdateResult,
3031
3031
  ): void {
3032
+ if (contentInfos.contentId !== this._priv_contentInfos?.contentId) {
3033
+ return; // Event for another content
3034
+ }
3032
3035
  if (this._priv_contentInfos === null || this._priv_contentInfos.manifest === null) {
3033
3036
  return;
3034
3037
  }
@@ -3210,7 +3213,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3210
3213
  "audioRepresentationChange",
3211
3214
  isNullOrUndefined(audioRepresentation)
3212
3215
  ? audioRepresentation
3213
- : toVideoRepresentation(audioRepresentation),
3216
+ : toAudioRepresentation(audioRepresentation),
3214
3217
  cancelSignal,
3215
3218
  );
3216
3219
  const videoRepresentation = this.__priv_getCurrentRepresentations()?.video ?? null;
@@ -3747,6 +3750,9 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3747
3750
  * to the content for which the error was received.
3748
3751
  */
3749
3752
  private _priv_onFatalError(err: unknown, contentInfos: IPublicApiContentInfos): void {
3753
+ if (contentInfos.contentId !== this._priv_contentInfos?.contentId) {
3754
+ return; // Event for another content
3755
+ }
3750
3756
  const formattedError = formatError(err, {
3751
3757
  defaultCode: "NONE",
3752
3758
  defaultReason: "An unknown error stopped content playback.",
@@ -3831,7 +3837,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3831
3837
  return true;
3832
3838
  }
3833
3839
  }
3834
- Player.version = /* PLAYER_VERSION */ "4.5.0-dev.2026033100";
3840
+ Player.version = /* PLAYER_VERSION */ "4.5.0-dev.2026041501";
3835
3841
 
3836
3842
  /** Every events sent by the RxPlayer's public API. */
3837
3843
  interface IPublicAPIEvent {
@@ -12,11 +12,11 @@ imported by outside code).
12
12
  ## Overview
13
13
 
14
14
  This directory exports the `ContentDecryptor`, which allows to easily interface with the
15
- browser APIs for decrypting an encrypted content, it follows the
16
- [Encrypted Media Extensions recommandations](https://www.w3.org/TR/encrypted-media/).
15
+ browser APIs for decrypting encrypted content. It follows the
16
+ [Encrypted Media Extensions recommendations](https://www.w3.org/TR/encrypted-media/).
17
17
 
18
- The `ContentDecryptor` is a module isolated from the rest of the player taking a form of a
19
- class. It starts initializing decryption capabilities as soon as it is instanciated and
18
+ The `ContentDecryptor` is a module isolated from the rest of the player taking the form of
19
+ a class. It starts initializing decryption capabilities as soon as it is instantiated and
20
20
  emits various events to communicate with external code.
21
21
 
22
22
  A central concept of the `ContentDecryptor` is its states. As decryption initialization
@@ -1,10 +1,52 @@
1
1
  import { describe, beforeEach, it, expect, vi } from "vitest";
2
+ import getEmeApiImplementation from "../../../../compat/eme";
3
+ import { EncryptedMediaError } from "../../../../errors";
4
+ import type { IKeySystemOption } from "../../../../public_types";
5
+ import assert from "../../../../utils/assert";
2
6
  import { getMissingKeyIds } from "../../content_decryptor";
7
+ import { ContentDecryptorState } from "../../types";
8
+ import { defaultKSConfig, mockCompat } from "./utils";
9
+
10
+ const mocks = vi.hoisted(() => {
11
+ return {
12
+ shouldRenewMediaKeySystemAccess: vi.fn(() => false),
13
+ canReuseMediaKeys: vi.fn(() => true),
14
+ onEncrypted: vi.fn(),
15
+ requestMediaKeySystemAccess: vi.fn(),
16
+ setMediaKeys: vi.fn(),
17
+ getInitData: vi.fn(),
18
+ generateKeyRequest: vi.fn(),
19
+ };
20
+ });
21
+ vi.mock("../../../../compat/should_renew_media_key_system_access", () => ({
22
+ default: mocks.shouldRenewMediaKeySystemAccess,
23
+ }));
24
+ vi.mock("../../../../compat/can_reuse_media_keys", () => ({
25
+ default: mocks.canReuseMediaKeys,
26
+ }));
27
+ vi.mock("../../../../compat/eme", () => ({
28
+ default: () => ({
29
+ onEncrypted: mocks.onEncrypted,
30
+ requestMediaKeySystemAccess: mocks.requestMediaKeySystemAccess,
31
+ setMediaKeys: mocks.setMediaKeys,
32
+ }),
33
+ getInitData: mocks.getInitData,
34
+ generateKeyRequest: mocks.generateKeyRequest,
35
+ closeSession: vi.fn(),
36
+ loadSession: vi.fn(),
37
+ }));
3
38
 
4
39
  describe("content_decryptor - blacklist missing key Ids", () => {
5
40
  beforeEach(() => {
6
41
  vi.resetModules();
7
42
  vi.restoreAllMocks();
43
+ mocks.shouldRenewMediaKeySystemAccess.mockReset();
44
+ mocks.canReuseMediaKeys.mockReset();
45
+ mocks.onEncrypted.mockReset();
46
+ mocks.requestMediaKeySystemAccess.mockReset();
47
+ mocks.setMediaKeys.mockReset();
48
+ mocks.getInitData.mockReset();
49
+ mocks.generateKeyRequest.mockReset();
8
50
  });
9
51
 
10
52
  it("should return an empty array if actualKeyIds contains all expectedKeyIds", () => {
@@ -43,3 +85,96 @@ describe("content_decryptor - blacklist missing key Ids", () => {
43
85
  expect(result).toEqual([new Uint8Array([2]), new Uint8Array([4])]);
44
86
  });
45
87
  });
88
+
89
+ describe("content_decryptor - session decommissioning", () => {
90
+ beforeEach(() => {
91
+ vi.resetModules();
92
+ vi.restoreAllMocks();
93
+ mocks.shouldRenewMediaKeySystemAccess.mockReset();
94
+ mocks.canReuseMediaKeys.mockReset();
95
+ mocks.onEncrypted.mockReset();
96
+ mocks.requestMediaKeySystemAccess.mockReset();
97
+ mocks.setMediaKeys.mockReset();
98
+ mocks.getInitData.mockReset();
99
+ mocks.generateKeyRequest.mockReset();
100
+ });
101
+
102
+ it("should only remove the decommissioned session from tracked sessions", async () => {
103
+ vi.resetModules();
104
+ const videoElt = document.createElement("video");
105
+ mockCompat(mocks);
106
+ const sessionCallbacks: Array<{ onError: (err: unknown) => void }> = [];
107
+ vi.doMock("../../session_events_listener", () => ({
108
+ __esModule: true,
109
+ default: vi.fn(
110
+ (_session: unknown, _options: unknown, _access: unknown, callbacks: unknown) => {
111
+ sessionCallbacks.push(callbacks as { onError: (err: unknown) => void });
112
+ },
113
+ ),
114
+ BlacklistedSessionError: class BlacklistedSessionError extends Error {},
115
+ }));
116
+ const { DecommissionedSessionError } = await import("../../utils/check_key_statuses");
117
+ const { default: ContentDecryptor } = await import("../../content_decryptor");
118
+
119
+ const mockGetLicense = vi.fn(() => Promise.resolve(new Uint8Array([1, 2, 3, 4])));
120
+ const ksConfig: IKeySystemOption[] = [
121
+ {
122
+ type: "com.widevine.alpha",
123
+ getLicense: mockGetLicense,
124
+ onKeyInternalError: "close-session",
125
+ },
126
+ ];
127
+
128
+ const eme = getEmeApiImplementation("auto");
129
+ assert(eme !== null, "Expected to have an EME implementation");
130
+ const contentDecryptor = new ContentDecryptor(eme, videoElt, ksConfig);
131
+
132
+ await new Promise<void>((res, rej) => {
133
+ contentDecryptor.addEventListener("stateChange", (state) => {
134
+ if (state !== ContentDecryptorState.WaitingForAttachment) {
135
+ rej(new Error(`Unexpected state: ${state}`));
136
+ return;
137
+ }
138
+ contentDecryptor.removeEventListener("stateChange");
139
+ contentDecryptor.attach();
140
+ res();
141
+ });
142
+ });
143
+
144
+ contentDecryptor.onInitializationData({
145
+ type: "cenc",
146
+ values: [{ systemId: "15", data: new Uint8Array([1, 1, 1]) }],
147
+ });
148
+ contentDecryptor.onInitializationData({
149
+ type: "cenc",
150
+ values: [{ systemId: "15", data: new Uint8Array([2, 2, 2]) }],
151
+ });
152
+ contentDecryptor.onInitializationData({
153
+ type: "cenc",
154
+ values: [{ systemId: "15", data: new Uint8Array([3, 3, 3]) }],
155
+ });
156
+
157
+ await new Promise((res) => setTimeout(res, 120));
158
+
159
+ expect(
160
+ (contentDecryptor as unknown as { _currentSessions: unknown[] })._currentSessions,
161
+ ).toHaveLength(3);
162
+
163
+ sessionCallbacks[1].onError(
164
+ new DecommissionedSessionError(
165
+ new EncryptedMediaError("KEY_LOAD_ERROR", "forced decommissioning", {
166
+ keyStatuses: undefined,
167
+ keySystemConfiguration: defaultKSConfig[0],
168
+ keySystem: "com.widevine.alpha",
169
+ }),
170
+ ),
171
+ );
172
+ await new Promise((res) => setTimeout(res, 30));
173
+
174
+ expect(
175
+ (contentDecryptor as unknown as { _currentSessions: unknown[] })._currentSessions,
176
+ ).toHaveLength(2);
177
+
178
+ contentDecryptor.dispose(undefined);
179
+ });
180
+ });
@@ -284,6 +284,76 @@ describe("decrypt - global tests - getLicense", () => {
284
284
  ignoreLicenseRequests: false,
285
285
  });
286
286
  }, 15000);
287
+
288
+ it("should emit KEY_LOAD_TIMEOUT when getLicense times out", async () => {
289
+ const initData = new Uint8Array([54, 55, 75]);
290
+ const initDataEvent = {
291
+ type: "cenc",
292
+ values: [{ systemId: "15", data: initData }],
293
+ };
294
+ const challenge = formatFakeChallengeFromInitData(initData, "cenc");
295
+ const mediaKeySession = new MediaKeySessionImpl();
296
+ vi.spyOn(MediaKeysImpl.prototype, "createSession").mockReturnValue(mediaKeySession);
297
+ const mockGetLicense = vi.fn(() => {
298
+ return new Promise<BufferSource>((resolve) => {
299
+ setTimeout(() => {
300
+ resolve(challenge);
301
+ }, 30);
302
+ });
303
+ });
304
+
305
+ const videoElt = document.createElement("video");
306
+ mockCompat(mocks);
307
+
308
+ await new Promise<void>((res, rej) => {
309
+ const ksConfig: IKeySystemOption[] = [
310
+ {
311
+ type: "com.widevine.alpha",
312
+ getLicense: mockGetLicense,
313
+ getLicenseConfig: {
314
+ retry: 0,
315
+ timeout: 5,
316
+ },
317
+ },
318
+ ];
319
+ const eme = getEmeApiImplementation("auto");
320
+ assert(eme !== null);
321
+ const contentDecryptor = new ContentDecryptor(eme, videoElt, ksConfig);
322
+
323
+ contentDecryptor.addEventListener("stateChange", (newState: number) => {
324
+ if (newState !== ContentDecryptorState.WaitingForAttachment) {
325
+ rej(new Error(`Unexpected state: ${newState}`));
326
+ return;
327
+ }
328
+ contentDecryptor.removeEventListener("stateChange");
329
+ contentDecryptor.attach();
330
+ });
331
+
332
+ contentDecryptor.addEventListener("warning", (warning: Error) => {
333
+ rej(new Error(`Unexpected warning: ${warning.toString()}`));
334
+ });
335
+
336
+ contentDecryptor.addEventListener("error", (error: Error) => {
337
+ try {
338
+ expect(error).toBeInstanceOf(Error);
339
+ expect((error as IPlayerError).name).toEqual("EncryptedMediaError");
340
+ expect((error as IPlayerError).type).toEqual("ENCRYPTED_MEDIA_ERROR");
341
+ expect((error as IPlayerError).code).toEqual("KEY_LOAD_TIMEOUT");
342
+ expect((error as IPlayerError).message).toEqual(
343
+ "KEY_LOAD_TIMEOUT: The license server took too much time to respond.",
344
+ );
345
+ expect(mockGetLicense).toHaveBeenCalledTimes(1);
346
+ expect(mockGetLicense).toHaveBeenNthCalledWith(1, challenge, "license-request");
347
+ contentDecryptor.dispose(undefined);
348
+ res();
349
+ } catch (e) {
350
+ rej(e);
351
+ }
352
+ });
353
+
354
+ contentDecryptor.onInitializationData(initDataEvent);
355
+ });
356
+ });
287
357
  });
288
358
 
289
359
  /**
@@ -291,4 +291,48 @@ describe("decrypt - global tests - server certificate", () => {
291
291
  }, 10);
292
292
  });
293
293
  });
294
+
295
+ it("should continue if setServerCertificate resolves to false and retry later", () => {
296
+ const videoElt = document.createElement("video");
297
+ mockCompat(mocks);
298
+ const mockSetServerCertificate = vi
299
+ .spyOn(MediaKeysImpl.prototype, "setServerCertificate")
300
+ .mockImplementationOnce((_serverCertificate: BufferSource): Promise<false> => {
301
+ return Promise.resolve(false);
302
+ })
303
+ .mockImplementationOnce((_serverCertificate: BufferSource): Promise<true> => {
304
+ return Promise.resolve(true);
305
+ });
306
+
307
+ const eme = getEmeApiImplementation("auto");
308
+ assert(eme !== null, "Expected to have an EME implementation");
309
+ const firstDecryptor = new ContentDecryptor(eme, videoElt, ksConfigCert);
310
+
311
+ return new Promise<void>((res) => {
312
+ firstDecryptor.addEventListener("stateChange", (state) => {
313
+ if (state === ContentDecryptorState.WaitingForAttachment) {
314
+ firstDecryptor.removeEventListener("stateChange");
315
+ firstDecryptor.attach();
316
+ }
317
+ });
318
+
319
+ setTimeout(() => {
320
+ firstDecryptor.dispose(undefined);
321
+
322
+ const secondDecryptor = new ContentDecryptor(eme, videoElt, ksConfigCert);
323
+ secondDecryptor.addEventListener("stateChange", (state) => {
324
+ if (state === ContentDecryptorState.WaitingForAttachment) {
325
+ secondDecryptor.removeEventListener("stateChange");
326
+ secondDecryptor.attach();
327
+ }
328
+ });
329
+
330
+ setTimeout(() => {
331
+ secondDecryptor.dispose(undefined);
332
+ expect(mockSetServerCertificate).toHaveBeenCalledTimes(2);
333
+ res();
334
+ }, 10);
335
+ }, 10);
336
+ });
337
+ });
294
338
  });
@@ -121,7 +121,7 @@ export class MediaKeyStatusMapImpl {
121
121
  ) => void,
122
122
  thisArg?: unknown,
123
123
  ): void {
124
- this._map.forEach((value, key) => callbackfn.bind(thisArg, value, key, this));
124
+ this._map.forEach((value, key) => callbackfn.call(thisArg, value, key, this));
125
125
  }
126
126
 
127
127
  public _setKeyStatus(keyId: BufferSource, value: MediaKeyStatus | undefined): void {
@@ -222,7 +222,7 @@ export class MediaKeysImpl {
222
222
  return new MediaKeySessionImpl();
223
223
  }
224
224
 
225
- setServerCertificate(_serverCertificate: BufferSource): Promise<true> {
225
+ setServerCertificate(_serverCertificate: BufferSource): Promise<boolean> {
226
226
  return Promise.resolve(true);
227
227
  }
228
228
  }
@@ -309,6 +309,11 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
309
309
  );
310
310
  if (resSsc.type === "error") {
311
311
  this.trigger("warning", resSsc.value);
312
+ } else if (resSsc.type === "not-supported") {
313
+ log.warn(
314
+ "DRM",
315
+ "Server certificate is not supported by the current MediaKeys.",
316
+ );
312
317
  }
313
318
  }
314
319
 
@@ -654,7 +659,7 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
654
659
  this._lockInitDataQueue();
655
660
  const indexOf = this._currentSessions.indexOf(sessionInfo);
656
661
  if (indexOf >= 0) {
657
- this._currentSessions.splice(indexOf);
662
+ this._currentSessions.splice(indexOf, 1);
658
663
  }
659
664
  if (initializationData.content !== undefined) {
660
665
  this.trigger("keyIdsCompatibilityUpdate", {
@@ -400,7 +400,7 @@ export class GetLicenseTimeoutError extends Error {
400
400
  constructor(message: string) {
401
401
  super(message);
402
402
  // @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class
403
- Object.setPrototypeOf(this, BlacklistedSessionError.prototype);
403
+ Object.setPrototypeOf(this, GetLicenseTimeoutError.prototype);
404
404
  this.message = message;
405
405
  }
406
406
  }
@@ -84,6 +84,7 @@ export default async function trySettingServerCertificate(
84
84
  ): Promise<
85
85
  | { type: "success"; value: unknown }
86
86
  | { type: "already-has-one" }
87
+ | { type: "not-supported" }
87
88
  | { type: "method-not-implemented" }
88
89
  | { type: "error"; value: IPlayerError }
89
90
  > {
@@ -112,6 +113,10 @@ export default async function trySettingServerCertificate(
112
113
  serverCertificate,
113
114
  mediaKeySystemAccess,
114
115
  );
116
+ if (result === false) {
117
+ log.warn("DRM", "MediaKeys ignored the server certificate. Continuing without it.");
118
+ return { type: "not-supported" };
119
+ }
115
120
  ServerCertificateStore.set(mediaKeys, serverCertificate);
116
121
  return { type: "success", value: result };
117
122
  } catch (error) {