rx-player 4.0.0-beta.0 → 4.0.0-beta.2

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 (297) hide show
  1. package/.eslintrc.js +8 -0
  2. package/CHANGELOG.md +93 -0
  3. package/CONTRIBUTING.md +48 -168
  4. package/FILES.md +40 -92
  5. package/VERSION +1 -1
  6. package/dist/_esm5.processed/compat/browser_detection.d.ts +25 -12
  7. package/dist/_esm5.processed/compat/browser_detection.js +85 -38
  8. package/dist/_esm5.processed/compat/can_reuse_media_keys.js +2 -2
  9. package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
  10. package/dist/_esm5.processed/compat/eme/load_session.js +1 -1
  11. package/dist/_esm5.processed/compat/event_listeners.js +1 -1
  12. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.d.ts +21 -0
  13. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.js +26 -0
  14. package/dist/_esm5.processed/config.d.ts +4 -0
  15. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +9 -6
  16. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.d.ts +18 -1
  17. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.js +106 -25
  18. package/dist/_esm5.processed/core/adaptive/guess_based_chooser.js +6 -6
  19. package/dist/_esm5.processed/core/adaptive/network_analyzer.js +8 -5
  20. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.d.ts +19 -1
  21. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.js +1 -1
  22. package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
  23. package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
  24. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
  25. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +104 -0
  26. package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
  27. package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
  28. package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
  29. package/dist/_esm5.processed/core/api/debug/index.js +2 -0
  30. package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
  31. package/dist/_esm5.processed/core/api/debug/modules/general_info.js +180 -0
  32. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
  33. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
  34. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
  35. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
  36. package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
  37. package/dist/_esm5.processed/core/api/debug/render.js +32 -0
  38. package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
  39. package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
  40. package/dist/_esm5.processed/core/api/playback_observer.js +4 -2
  41. package/dist/_esm5.processed/core/api/public_api.d.ts +60 -3
  42. package/dist/_esm5.processed/core/api/public_api.js +280 -60
  43. package/dist/_esm5.processed/core/api/track_management/media_element_tracks_store.js +10 -1
  44. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.d.ts +13 -1
  45. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.js +30 -15
  46. package/dist/_esm5.processed/core/api/track_management/tracks_store.d.ts +3 -1
  47. package/dist/_esm5.processed/core/api/track_management/tracks_store.js +67 -152
  48. package/dist/_esm5.processed/core/api/utils.d.ts +10 -0
  49. package/dist/_esm5.processed/core/api/utils.js +23 -3
  50. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +27 -8
  51. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +28 -7
  52. package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
  53. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
  54. package/dist/_esm5.processed/core/decrypt/find_key_system.js +29 -6
  55. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +42 -32
  56. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
  57. package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +2 -0
  58. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +5 -1
  59. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
  60. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
  61. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
  62. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
  63. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
  64. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
  65. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +17 -7
  66. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +2 -2
  67. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +74 -41
  68. package/dist/_esm5.processed/core/init/types.d.ts +9 -1
  69. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.d.ts +28 -1
  70. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +24 -11
  71. package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
  72. package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
  73. package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
  74. package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
  75. package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
  76. package/dist/_esm5.processed/core/init/utils/media_source_duration_updater.d.ts +58 -0
  77. package/dist/_esm5.processed/core/init/utils/{media_duration_updater.js → media_source_duration_updater.js} +87 -86
  78. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.d.ts +36 -2
  79. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +83 -3
  80. package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
  81. package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
  82. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -7
  83. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +38 -50
  84. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +8 -0
  85. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +16 -2
  86. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +8 -0
  87. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +12 -0
  88. package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +11 -4
  89. package/dist/_esm5.processed/core/segment_buffers/index.d.ts +2 -2
  90. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
  91. package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +30 -16
  92. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.d.ts +47 -0
  93. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.js +70 -0
  94. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +41 -20
  95. package/dist/_esm5.processed/core/stream/period/period_stream.js +12 -11
  96. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +37 -28
  97. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.d.ts +4 -2
  98. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +3 -3
  99. package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
  100. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.d.ts +3 -2
  101. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.js +8 -8
  102. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.d.ts +2 -2
  103. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.js +2 -3
  104. package/dist/_esm5.processed/core/stream/utils/create_reload_request.js +1 -1
  105. package/dist/_esm5.processed/default_config.d.ts +41 -0
  106. package/dist/_esm5.processed/default_config.js +46 -2
  107. package/dist/_esm5.processed/errors/index.d.ts +2 -2
  108. package/dist/_esm5.processed/errors/media_error.d.ts +23 -1
  109. package/dist/_esm5.processed/errors/media_error.js +18 -5
  110. package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
  111. package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
  112. package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
  113. package/dist/_esm5.processed/experimental/features/index.js +1 -0
  114. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +1 -1
  115. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +8 -7
  116. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
  117. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +24 -12
  118. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/index.js +0 -2
  119. package/dist/_esm5.processed/features/features_object.js +1 -0
  120. package/dist/_esm5.processed/features/initialize_features.js +13 -10
  121. package/dist/_esm5.processed/features/types.d.ts +3 -0
  122. package/dist/_esm5.processed/manifest/adaptation.d.ts +21 -2
  123. package/dist/_esm5.processed/manifest/adaptation.js +80 -1
  124. package/dist/_esm5.processed/manifest/manifest.js +2 -0
  125. package/dist/_esm5.processed/manifest/period.js +2 -2
  126. package/dist/_esm5.processed/manifest/representation.d.ts +33 -2
  127. package/dist/_esm5.processed/manifest/representation.js +32 -4
  128. package/dist/_esm5.processed/manifest/utils.js +1 -3
  129. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
  130. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
  131. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.d.ts +1 -1
  132. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.js +1 -1
  133. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +1 -0
  134. package/dist/_esm5.processed/public_types.d.ts +13 -3
  135. package/dist/_esm5.processed/tools/TextTrackRenderer/text_track_renderer.js +1 -1
  136. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +15 -11
  137. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
  138. package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
  139. package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
  140. package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
  141. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.d.ts +4 -6
  142. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.js +4 -6
  143. package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
  144. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
  145. package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
  146. package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
  147. package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
  148. package/dist/_esm5.processed/utils/is_null_or_undefined.d.ts +1 -1
  149. package/dist/_esm5.processed/utils/is_null_or_undefined.js +1 -1
  150. package/dist/_esm5.processed/utils/reference.js +6 -0
  151. package/dist/_esm5.processed/utils/request/xhr.js +1 -1
  152. package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
  153. package/dist/_esm5.processed/utils/task_canceller.js +55 -22
  154. package/dist/mpd-parser.wasm +0 -0
  155. package/dist/rx-player.js +5424 -4712
  156. package/dist/rx-player.min.js +1 -1
  157. package/jest.config.js +1 -5
  158. package/package.json +44 -40
  159. package/scripts/build/constants.d.ts +1 -0
  160. package/scripts/build/generate_build.js +1 -1
  161. package/scripts/fast_demo_build.js +40 -40
  162. package/scripts/generate_full_demo.js +1 -1
  163. package/sonar-project.properties +1 -1
  164. package/src/compat/browser_detection.ts +105 -52
  165. package/src/compat/can_reuse_media_keys.ts +5 -2
  166. package/src/compat/eme/close_session.ts +2 -2
  167. package/src/compat/eme/load_session.ts +1 -1
  168. package/src/compat/event_listeners.ts +1 -1
  169. package/src/compat/has_issues_with_high_media_source_duration.ts +27 -0
  170. package/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +147 -48
  171. package/src/core/adaptive/adaptive_representation_selector.ts +11 -6
  172. package/src/core/adaptive/buffer_based_chooser.ts +144 -26
  173. package/src/core/adaptive/guess_based_chooser.ts +9 -8
  174. package/src/core/adaptive/network_analyzer.ts +9 -4
  175. package/src/core/adaptive/utils/representation_score_calculator.ts +22 -2
  176. package/src/core/api/debug/buffer_graph.ts +247 -0
  177. package/src/core/api/debug/buffer_size_graph.ts +130 -0
  178. package/src/core/api/debug/constants.ts +2 -0
  179. package/src/core/api/debug/index.ts +3 -0
  180. package/src/core/api/debug/modules/general_info.ts +184 -0
  181. package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
  182. package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
  183. package/src/core/api/debug/render.ts +40 -0
  184. package/src/core/api/debug/utils.ts +103 -0
  185. package/src/core/api/playback_observer.ts +5 -2
  186. package/src/core/api/public_api.ts +334 -73
  187. package/src/core/api/track_management/media_element_tracks_store.ts +17 -8
  188. package/src/core/api/track_management/track_dispatcher.ts +37 -14
  189. package/src/core/api/track_management/tracks_store.ts +77 -167
  190. package/src/core/api/utils.ts +29 -3
  191. package/src/core/decrypt/__tests__/__global__/utils.ts +61 -40
  192. package/src/core/decrypt/attach_media_keys.ts +1 -1
  193. package/src/core/decrypt/content_decryptor.ts +1 -1
  194. package/src/core/decrypt/find_key_system.ts +25 -11
  195. package/src/core/decrypt/session_events_listener.ts +45 -39
  196. package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
  197. package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +2 -1
  198. package/src/core/decrypt/utils/loaded_sessions_store.ts +8 -1
  199. package/src/core/fetchers/cdn_prioritizer.ts +18 -9
  200. package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
  201. package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
  202. package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
  203. package/src/core/fetchers/utils/schedule_request.ts +18 -7
  204. package/src/core/init/directfile_content_initializer.ts +2 -1
  205. package/src/core/init/media_source_content_initializer.ts +89 -50
  206. package/src/core/init/types.ts +9 -1
  207. package/src/core/init/utils/content_time_boundaries_observer.ts +48 -12
  208. package/src/core/init/utils/create_media_source.ts +4 -16
  209. package/src/core/init/utils/end_of_stream.ts +6 -3
  210. package/src/core/init/utils/get_loaded_reference.ts +2 -1
  211. package/src/core/init/utils/initial_seek_and_play.ts +9 -5
  212. package/src/core/init/utils/initialize_content_decryption.ts +2 -1
  213. package/src/core/init/utils/{media_duration_updater.ts → media_source_duration_updater.ts} +103 -110
  214. package/src/core/init/utils/rebuffering_controller.ts +115 -4
  215. package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
  216. package/src/core/init/utils/throw_on_media_error.ts +1 -1
  217. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +63 -66
  218. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +20 -2
  219. package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +16 -0
  220. package/src/core/segment_buffers/implementations/types.ts +16 -4
  221. package/src/core/segment_buffers/index.ts +2 -0
  222. package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
  223. package/src/core/stream/adaptation/adaptation_stream.ts +33 -19
  224. package/src/core/stream/adaptation/utils/create_representation_estimator.ts +114 -0
  225. package/src/core/stream/orchestrator/stream_orchestrator.ts +42 -20
  226. package/src/core/stream/period/period_stream.ts +13 -11
  227. package/src/core/stream/representation/representation_stream.ts +49 -37
  228. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +9 -4
  229. package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
  230. package/src/core/stream/representation/utils/push_init_segment.ts +11 -6
  231. package/src/core/stream/representation/utils/push_media_segment.ts +3 -3
  232. package/src/core/stream/utils/create_reload_request.ts +1 -1
  233. package/src/default_config.ts +59 -11
  234. package/src/errors/__tests__/media_error.test.ts +6 -6
  235. package/src/errors/index.ts +4 -1
  236. package/src/errors/media_error.ts +67 -1
  237. package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
  238. package/src/experimental/features/debug_element.ts +13 -0
  239. package/src/experimental/features/index.ts +1 -0
  240. package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +10 -7
  241. package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
  242. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +25 -10
  243. package/src/experimental/tools/mediaCapabilitiesProber/index.ts +0 -4
  244. package/src/features/__tests__/initialize_features.test.ts +11 -0
  245. package/src/features/features_object.ts +1 -0
  246. package/src/features/initialize_features.ts +15 -10
  247. package/src/features/types.ts +9 -0
  248. package/src/manifest/__tests__/manifest.test.ts +7 -7
  249. package/src/manifest/__tests__/period.test.ts +90 -45
  250. package/src/manifest/adaptation.ts +96 -1
  251. package/src/manifest/manifest.ts +4 -0
  252. package/src/manifest/period.ts +4 -2
  253. package/src/manifest/representation.ts +77 -5
  254. package/src/manifest/utils.ts +1 -3
  255. package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
  256. package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
  257. package/src/parsers/manifest/dash/js-parser/parse_from_document.ts +1 -1
  258. package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +1 -0
  259. package/src/parsers/texttracks/ttml/parse_ttml.ts +1 -1
  260. package/src/public_types.ts +16 -1
  261. package/src/tools/TextTrackRenderer/text_track_renderer.ts +1 -1
  262. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +31 -22
  263. package/src/transports/dash/low_latency_segment_loader.ts +2 -2
  264. package/src/transports/dash/manifest_parser.ts +1 -1
  265. package/src/transports/dash/segment_loader.ts +4 -4
  266. package/src/transports/local/segment_loader.ts +14 -30
  267. package/src/transports/smooth/isobmff/create_boxes.ts +4 -6
  268. package/src/transports/smooth/segment_loader.ts +4 -4
  269. package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
  270. package/src/typings/globals.d.ts +21 -19
  271. package/src/utils/cancellable_sleep.ts +5 -14
  272. package/src/utils/create_cancellable_promise.ts +69 -0
  273. package/src/utils/is_null_or_undefined.ts +1 -1
  274. package/src/utils/reference.ts +6 -0
  275. package/src/utils/request/xhr.ts +1 -1
  276. package/src/utils/task_canceller.ts +63 -34
  277. package/tsconfig.json +1 -1
  278. package/tsconfig.modules.json +1 -1
  279. package/dist/_esm5.processed/core/init/utils/media_duration_updater.d.ts +0 -56
  280. package/locked_reps.js +0 -46
  281. package/scripts/doc-generator/construct_table_of_contents.js +0 -76
  282. package/scripts/doc-generator/convert_MD_to_HMTL.js +0 -26
  283. package/scripts/doc-generator/create_documentation.js +0 -331
  284. package/scripts/doc-generator/create_documentation_page.js +0 -209
  285. package/scripts/doc-generator/create_page.js +0 -210
  286. package/scripts/doc-generator/generate_header_html.js +0 -147
  287. package/scripts/doc-generator/generate_page_html.js +0 -115
  288. package/scripts/doc-generator/generate_page_list_html.js +0 -92
  289. package/scripts/doc-generator/generate_sidebar_html.js +0 -85
  290. package/scripts/doc-generator/get_search_data_for_content.js +0 -137
  291. package/scripts/doc-generator/index.js +0 -34
  292. package/scripts/doc-generator/parse_doc_configs.js +0 -327
  293. package/scripts/doc-generator/scripts/lunr.js +0 -10
  294. package/scripts/doc-generator/scripts/script.js +0 -451
  295. package/scripts/doc-generator/styles/code.css +0 -99
  296. package/scripts/doc-generator/styles/style.css +0 -835
  297. package/scripts/doc-generator/utils.js +0 -74
@@ -57,6 +57,7 @@ import {
57
57
  ILockedAudioRepresentationsSettings,
58
58
  ILockedVideoRepresentationsSettings,
59
59
  ITrackUpdateEventPayload,
60
+ IRepresentationListUpdateContext,
60
61
  IPeriod,
61
62
  IPeriodChangeEvent,
62
63
  IPlayerError,
@@ -70,7 +71,10 @@ import {
70
71
  IVideoTrack,
71
72
  IVideoTrackSetting,
72
73
  IVideoTrackSwitchingMode,
74
+ ITrackType,
73
75
  } from "../../public_types";
76
+ import arrayFind from "../../utils/array_find";
77
+ import arrayIncludes from "../../utils/array_includes";
74
78
  import assert from "../../utils/assert";
75
79
  import assertUnreachable from "../../utils/assert_unreachable";
76
80
  import EventEmitter, {
@@ -123,9 +127,9 @@ import TracksStore, {
123
127
  } from "./track_management/tracks_store";
124
128
  import {
125
129
  constructPlayerStateReference,
130
+ emitPlayPauseEvents,
126
131
  emitSeekEvents,
127
132
  isLoadedState,
128
- // emitSeekEvents,
129
133
  PLAYER_STATES,
130
134
  } from "./utils";
131
135
 
@@ -280,6 +284,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
280
284
  reloadPosition?: number;
281
285
  };
282
286
 
287
+ /**
288
+ * Store last value of autoPlay, from the last load or reload.
289
+ */
290
+ private _priv_lastAutoPlay: boolean;
291
+
283
292
  /** All possible Error types emitted by the RxPlayer. */
284
293
  static get ErrorTypes() : Record<IErrorType, IErrorType> {
285
294
  return ErrorTypes;
@@ -327,7 +336,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
327
336
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
328
337
  videoElement.preload = "auto";
329
338
 
330
- this.version = /* PLAYER_VERSION */"4.0.0-beta.0";
339
+ this.version = /* PLAYER_VERSION */"4.0.0-beta.2";
331
340
  this.log = log;
332
341
  this.state = "STOPPED";
333
342
  this.videoElement = videoElement;
@@ -372,6 +381,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
372
381
  this._priv_setPlayerState(PLAYER_STATES.STOPPED);
373
382
 
374
383
  this._priv_reloadingMetadata = {};
384
+
385
+ this._priv_lastAutoPlay = false;
375
386
  }
376
387
 
377
388
  /**
@@ -442,6 +453,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
442
453
  log.info("API: Calling loadvideo", options.url, options.transport);
443
454
  this._priv_reloadingMetadata = { options };
444
455
  this._priv_initializeContentPlayback(options);
456
+ this._priv_lastAutoPlay = options.autoPlay;
445
457
  }
446
458
 
447
459
  /**
@@ -493,6 +505,21 @@ class Player extends EventEmitter<IPublicAPIEvent> {
493
505
  this._priv_initializeContentPlayback(newOptions);
494
506
  }
495
507
 
508
+ public createDebugElement(element : HTMLElement) : {
509
+ dispose() : void;
510
+ } {
511
+ if (features.createDebugElement === null) {
512
+ throw new Error("Feature `DEBUG_ELEMENT` not added to the RxPlayer");
513
+ }
514
+ const canceller = new TaskCanceller() ;
515
+ features.createDebugElement(element, this, canceller.signal);
516
+ return {
517
+ dispose() {
518
+ canceller.cancel();
519
+ },
520
+ };
521
+ }
522
+
496
523
  /**
497
524
  * From given options, initialize content playback.
498
525
  * @param {Object} options
@@ -639,7 +666,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
639
666
  }
640
667
  mediaElementTracksStore =
641
668
  this._priv_initializeMediaElementTracksStore(currentContentCanceller.signal);
642
- if (currentContentCanceller.isUsed) {
669
+ if (currentContentCanceller.isUsed()) {
643
670
  return;
644
671
  }
645
672
  initializer = new features.directfile.initDirectFile({ autoPlay,
@@ -668,26 +695,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
668
695
 
669
696
  // Bind events
670
697
  initializer.addEventListener("error", (error) => {
671
- const formattedError = formatError(error, {
672
- defaultCode: "NONE",
673
- defaultReason: "An unknown error stopped content playback.",
674
- });
675
- formattedError.fatal = true;
676
-
677
- contentInfos.currentContentCanceller.cancel();
678
- this._priv_cleanUpCurrentContentState();
679
- this._priv_currentError = formattedError;
680
- log.error("API: The player stopped because of an error",
681
- error instanceof Error ? error : "");
682
- this._priv_setPlayerState(PLAYER_STATES.STOPPED);
683
-
684
- // TODO This condition is here because the eventual callback called when the
685
- // player state is updated can launch a new content, thus the error will not
686
- // be here anymore, in which case triggering the "error" event is unwanted.
687
- // This is very ugly though, and we should probable have a better solution
688
- if (this._priv_currentError === formattedError) {
689
- this.trigger("error", formattedError);
690
- }
698
+ this._priv_onFatalError(error, contentInfos);
691
699
  });
692
700
  initializer.addEventListener("warning", (error) => {
693
701
  const formattedError = formatError(error, {
@@ -697,11 +705,12 @@ class Player extends EventEmitter<IPublicAPIEvent> {
697
705
  log.warn("API: Sending warning:", formattedError);
698
706
  this.trigger("warning", formattedError);
699
707
  });
700
- initializer.addEventListener("reloadingMediaSource", () => {
708
+ initializer.addEventListener("reloadingMediaSource", (payload) => {
701
709
  contentInfos.segmentBuffersStore = null;
702
710
  if (contentInfos.tracksStore !== null) {
703
711
  contentInfos.tracksStore.resetPeriodObjects();
704
712
  }
713
+ this._priv_lastAutoPlay = payload.autoPlay;
705
714
  });
706
715
  initializer.addEventListener("inbandEvents", (inbandEvents) =>
707
716
  this.trigger("inbandEvents", inbandEvents));
@@ -785,6 +794,53 @@ class Player extends EventEmitter<IPublicAPIEvent> {
785
794
  }
786
795
  };
787
796
 
797
+ /**
798
+ * `TaskCanceller` allowing to stop emitting `"play"` and `"pause"`
799
+ * events.
800
+ * `null` when such events are not emitted currently.
801
+ */
802
+ let playPauseEventsCanceller : TaskCanceller | null = null;
803
+
804
+ /**
805
+ * Callback emitting `"play"` and `"pause`" events once the content is
806
+ * loaded, starting from the state indicated in argument.
807
+ * @param {boolean} willAutoPlay - If `false`, we're currently paused.
808
+ */
809
+ const triggerPlayPauseEventsWhenReady = (willAutoPlay: boolean) => {
810
+ if (playPauseEventsCanceller !== null) {
811
+ playPauseEventsCanceller.cancel(); // cancel previous logic
812
+ playPauseEventsCanceller = null;
813
+ }
814
+ playerStateRef.onUpdate((val, stopListeningToStateUpdates) => {
815
+ if (!isLoadedState(val)) {
816
+ return; // content not loaded yet: no event
817
+ }
818
+ stopListeningToStateUpdates();
819
+ if (playPauseEventsCanceller !== null) {
820
+ playPauseEventsCanceller.cancel();
821
+ }
822
+ playPauseEventsCanceller = new TaskCanceller();
823
+ playPauseEventsCanceller.linkToSignal(currentContentCanceller.signal);
824
+ if (willAutoPlay !== !videoElement.paused) {
825
+ // paused status is not at the expected value on load: emit event
826
+ if (videoElement.paused) {
827
+ this.trigger("pause", null);
828
+ } else {
829
+ this.trigger("play", null);
830
+ }
831
+ }
832
+ emitPlayPauseEvents(videoElement,
833
+ () => this.trigger("play", null),
834
+ () => this.trigger("pause", null),
835
+ currentContentCanceller.signal);
836
+ }, { emitCurrentValue: false, clearSignal: currentContentCanceller.signal });
837
+ };
838
+
839
+ triggerPlayPauseEventsWhenReady(autoPlay);
840
+ initializer.addEventListener("reloadingMediaSource", (payload) => {
841
+ triggerPlayPauseEventsWhenReady(payload.autoPlay);
842
+ });
843
+
788
844
  /**
789
845
  * `TaskCanceller` allowing to stop emitting `"seeking"` and `"seeked"`
790
846
  * events.
@@ -797,7 +853,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
797
853
  updateReloadingMetadata(newState);
798
854
  this._priv_setPlayerState(newState);
799
855
 
800
- if (currentContentCanceller.isUsed) {
856
+ if (currentContentCanceller.isUsed()) {
801
857
  return;
802
858
  }
803
859
 
@@ -807,9 +863,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
807
863
  seekEventsCanceller = null;
808
864
  }
809
865
  } else if (isLoadedState(this.state)) {
810
- seekEventsCanceller = new TaskCanceller({
811
- cancelOn: currentContentCanceller.signal,
812
- });
866
+ seekEventsCanceller = new TaskCanceller();
867
+ seekEventsCanceller.linkToSignal(currentContentCanceller.signal);
813
868
  emitSeekEvents(videoElement,
814
869
  playbackObserver,
815
870
  () => this.trigger("seeking", null),
@@ -870,6 +925,42 @@ class Player extends EventEmitter<IPublicAPIEvent> {
870
925
  return this.state;
871
926
  }
872
927
 
928
+ /**
929
+ * Returns true if a content is loaded.
930
+ * @returns {Boolean} - `true` if a content is loaded, `false` otherwise.
931
+ */
932
+ isContentLoaded() : boolean {
933
+ return !arrayIncludes(["LOADING", "RELOADING", "STOPPED"], this.state);
934
+ }
935
+
936
+ /**
937
+ * Returns true if the player is buffering.
938
+ * @returns {Boolean} - `true` if the player is buffering, `false` otherwise.
939
+ */
940
+ isBuffering() : boolean {
941
+ return arrayIncludes(["BUFFERING", "SEEKING", "LOADING", "RELOADING"], this.state);
942
+ }
943
+
944
+ /**
945
+ * Returns the play/pause status of the player :
946
+ * - when `LOADING` or `RELOADING`, returns the scheduled play/pause condition
947
+ * for when loading is over,
948
+ * - in other states, returns the `<video>` element .paused value,
949
+ * - if the player is disposed, returns `true`.
950
+ * @returns {Boolean} - `true` if the player is paused or will be after loading,
951
+ * `false` otherwise.
952
+ */
953
+ isPaused() : boolean {
954
+ if (this.videoElement) {
955
+ if (arrayIncludes(["LOADING", "RELOADING"], this.state)) {
956
+ return !this._priv_lastAutoPlay;
957
+ } else {
958
+ return this.videoElement.paused;
959
+ }
960
+ }
961
+ return true;
962
+ }
963
+
873
964
  /**
874
965
  * Returns true if both:
875
966
  * - a content is loaded
@@ -1022,6 +1113,15 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1022
1113
  return this.videoElement.currentTime;
1023
1114
  }
1024
1115
 
1116
+ /**
1117
+ * Returns the last stored content position, in seconds.
1118
+ *
1119
+ * @returns {number|undefined}
1120
+ */
1121
+ getLastStoredContentPosition() : number|undefined {
1122
+ return this._priv_reloadingMetadata.reloadPosition;
1123
+ }
1124
+
1025
1125
  /**
1026
1126
  * Returns the current playback rate at which the video plays.
1027
1127
  * @returns {Number}
@@ -1119,7 +1219,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1119
1219
  * @returns {Object|null|undefined}
1120
1220
  */
1121
1221
  getVideoRepresentation() : IVideoRepresentation | null | undefined {
1122
- const representations = this._priv_getCurrentRepresentations();
1222
+ const representations = this.__priv_getCurrentRepresentations();
1123
1223
  if (representations === null) {
1124
1224
  return undefined;
1125
1225
  }
@@ -1136,7 +1236,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1136
1236
  * @returns {Object|null|undefined}
1137
1237
  */
1138
1238
  getAudioRepresentation() : IAudioRepresentation | null | undefined {
1139
- const representations = this._priv_getCurrentRepresentations();
1239
+ const representations = this.__priv_getCurrentRepresentations();
1140
1240
  if (representations === null) {
1141
1241
  return undefined;
1142
1242
  }
@@ -1237,6 +1337,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1237
1337
  if (positionWanted === undefined) {
1238
1338
  throw new Error("invalid time given");
1239
1339
  }
1340
+ log.info("API: API Seek to", positionWanted);
1240
1341
  this.videoElement.currentTime = positionWanted;
1241
1342
  return positionWanted;
1242
1343
  }
@@ -1891,6 +1992,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1891
1992
  * null if the player is STOPPED.
1892
1993
  * @returns {Manifest|null} - The current Manifest (`null` when not known).
1893
1994
  */
1995
+ // TODO remove the need for that public method
1894
1996
  __priv_getManifest() : Manifest|null {
1895
1997
  if (this._priv_contentInfos === null) {
1896
1998
  return null;
@@ -1898,6 +2000,38 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1898
2000
  return this._priv_contentInfos.manifest;
1899
2001
  }
1900
2002
 
2003
+ // TODO remove the need for that public method
2004
+ __priv_getCurrentAdaptation(
2005
+ ) : Partial<Record<IBufferType, Adaptation|null>> | null {
2006
+ if (this._priv_contentInfos === null) {
2007
+ return null;
2008
+ }
2009
+ const { currentPeriod, activeAdaptations } = this._priv_contentInfos;
2010
+ if (currentPeriod === null ||
2011
+ activeAdaptations === null ||
2012
+ isNullOrUndefined(activeAdaptations[currentPeriod.id]))
2013
+ {
2014
+ return null;
2015
+ }
2016
+ return activeAdaptations[currentPeriod.id];
2017
+ }
2018
+
2019
+ // TODO remove the need for that public method
2020
+ __priv_getCurrentRepresentations(
2021
+ ) : Partial<Record<IBufferType, Representation|null>> | null {
2022
+ if (this._priv_contentInfos === null) {
2023
+ return null;
2024
+ }
2025
+ const { currentPeriod, activeRepresentations } = this._priv_contentInfos;
2026
+ if (currentPeriod === null ||
2027
+ activeRepresentations === null ||
2028
+ isNullOrUndefined(activeRepresentations[currentPeriod.id]))
2029
+ {
2030
+ return null;
2031
+ }
2032
+ return activeRepresentations[currentPeriod.id];
2033
+ }
2034
+
1901
2035
  // ---- Private methods ----
1902
2036
 
1903
2037
  /**
@@ -1954,21 +2088,34 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1954
2088
  return; // Event for another content
1955
2089
  }
1956
2090
  contentInfos.manifest = manifest;
1957
- const cancelSignal = contentInfos.currentContentCanceller.signal;
1958
2091
  this._priv_reloadingMetadata.manifest = manifest;
1959
2092
 
1960
- contentInfos.tracksStore = new TracksStore({
2093
+ const tracksStore = new TracksStore({
1961
2094
  preferTrickModeTracks: this._priv_preferTrickModeTracks,
1962
2095
  defaultAudioTrackSwitchingMode: contentInfos.defaultAudioTrackSwitchingMode,
1963
2096
  });
1964
- contentInfos.tracksStore.addEventListener("newAvailablePeriods", (p) => {
2097
+ contentInfos.tracksStore = tracksStore;
2098
+ tracksStore.addEventListener("newAvailablePeriods", (p) => {
1965
2099
  this.trigger("newAvailablePeriods", p);
1966
2100
  });
1967
- contentInfos.tracksStore.addEventListener("brokenRepresentationsLock", (e) => {
2101
+ tracksStore.addEventListener("brokenRepresentationsLock", (e) => {
1968
2102
  this.trigger("brokenRepresentationsLock", e);
1969
2103
  });
1970
- contentInfos.tracksStore.addEventListener("trackUpdate", (e) => {
2104
+ tracksStore.addEventListener("trackUpdate", (e) => {
1971
2105
  this.trigger("trackUpdate", e);
2106
+
2107
+ const currentPeriod = this._priv_contentInfos?.currentPeriod ?? undefined;
2108
+ if (e.reason === "no-playable-representation" &&
2109
+ e.period.id === currentPeriod?.id)
2110
+ {
2111
+ this._priv_onAvailableTracksMayHaveChanged(e.trackType);
2112
+ }
2113
+ });
2114
+ contentInfos.tracksStore.addEventListener("warning", (err) => {
2115
+ this.trigger("warning", err);
2116
+ });
2117
+ contentInfos.tracksStore.addEventListener("error", (err) => {
2118
+ this._priv_onFatalError(err, contentInfos);
1972
2119
  });
1973
2120
 
1974
2121
  contentInfos.tracksStore.updatePeriodList(manifest);
@@ -1979,8 +2126,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1979
2126
  contentInfos.tracksStore.updatePeriodList(manifest);
1980
2127
  }
1981
2128
  const currentPeriod = this._priv_contentInfos?.currentPeriod ?? undefined;
1982
- const tracksStore = this._priv_contentInfos?.tracksStore;
1983
- if (currentPeriod === undefined || isNullOrUndefined(tracksStore)) {
2129
+ const currTracksStore = this._priv_contentInfos?.tracksStore;
2130
+ if (currentPeriod === undefined || isNullOrUndefined(currTracksStore)) {
1984
2131
  return;
1985
2132
  }
1986
2133
  for (const update of updates.updatedPeriods) {
@@ -1989,27 +2136,72 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1989
2136
  update.result.removedAdaptations.length > 0)
1990
2137
  {
1991
2138
  // We might have new (or less) tracks, send events just to be sure
1992
- const periodRef = tracksStore.getPeriodObjectFromPeriod(currentPeriod);
2139
+ const periodRef = currTracksStore.getPeriodObjectFromPeriod(currentPeriod);
1993
2140
  if (periodRef === undefined) {
1994
2141
  return;
1995
2142
  }
1996
- const audioTracks = tracksStore.getAvailableAudioTracks(periodRef);
1997
- this._priv_triggerEventIfNotStopped("availableAudioTracksChange",
1998
- audioTracks ?? [],
1999
- cancelSignal);
2000
- const textTracks = tracksStore.getAvailableTextTracks(periodRef);
2001
- this._priv_triggerEventIfNotStopped("availableTextTracksChange",
2002
- textTracks ?? [],
2003
- cancelSignal);
2004
- const videoTracks = tracksStore.getAvailableVideoTracks(periodRef);
2005
- this._priv_triggerEventIfNotStopped("availableVideoTracksChange",
2006
- videoTracks ?? [],
2007
- cancelSignal);
2143
+ this._priv_onAvailableTracksMayHaveChanged("audio");
2144
+ this._priv_onAvailableTracksMayHaveChanged("text");
2145
+ this._priv_onAvailableTracksMayHaveChanged("video");
2008
2146
  }
2009
2147
  }
2010
2148
  return;
2011
2149
  }
2012
2150
  }, contentInfos.currentContentCanceller.signal);
2151
+
2152
+ manifest.addEventListener("decipherabilityUpdate", (elts) => {
2153
+ /**
2154
+ * Array of tuples only including once the Period/Track combination, and
2155
+ * only when it concerns the currently-selected track.
2156
+ */
2157
+ const periodsAndTrackTypes = elts.reduce(
2158
+ (acc: Array<[Period, ITrackType]>, elt) => {
2159
+ const isFound = arrayFind(
2160
+ acc,
2161
+ (x) => x[0].id === elt.period.id &&
2162
+ x[1] === elt.adaptation.type
2163
+ ) === undefined;
2164
+
2165
+ if (!isFound) {
2166
+ // Only consider the currently-selected tracks.
2167
+ // NOTE: Maybe there's room for optimizations? Unclear.
2168
+ const tStore = contentInfos.tracksStore;
2169
+ if (tStore === null) {
2170
+ return acc;
2171
+ }
2172
+ let isCurrent = false;
2173
+ const periodRef = tStore.getPeriodObjectFromPeriod(elt.period);
2174
+ if (periodRef === undefined) {
2175
+ return acc;
2176
+ }
2177
+ switch (elt.adaptation.type) {
2178
+ case "audio":
2179
+ isCurrent = tStore
2180
+ .getChosenAudioTrack(periodRef)?.id === elt.adaptation.id;
2181
+ break;
2182
+ case "video":
2183
+ isCurrent = tStore
2184
+ .getChosenVideoTrack(periodRef)?.id === elt.adaptation.id;
2185
+ break;
2186
+ case "text":
2187
+ isCurrent = tStore
2188
+ .getChosenTextTrack(periodRef)?.id === elt.adaptation.id;
2189
+ break;
2190
+ }
2191
+ if (isCurrent) {
2192
+ acc.push([elt.period, elt.adaptation.type]);
2193
+ }
2194
+ }
2195
+ return acc;
2196
+ }, []);
2197
+ for (const [period, trackType] of periodsAndTrackTypes) {
2198
+ this._priv_triggerEventIfNotStopped(
2199
+ "representationListUpdate",
2200
+ { period, trackType, reason: "decipherability-update" },
2201
+ contentInfos.currentContentCanceller.signal
2202
+ );
2203
+ }
2204
+ }, contentInfos.currentContentCanceller.signal);
2013
2205
  }
2014
2206
 
2015
2207
  /**
@@ -2073,15 +2265,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2073
2265
  this._priv_triggerEventIfNotStopped("videoTrackChange", null, cancelSignal);
2074
2266
  }
2075
2267
 
2076
- const audioRepresentation = this._priv_getCurrentRepresentations()?.audio ?? null;
2268
+ const audioRepresentation = this.__priv_getCurrentRepresentations()?.audio ?? null;
2077
2269
  this._priv_triggerEventIfNotStopped("audioRepresentationChange",
2078
2270
  audioRepresentation,
2079
2271
  cancelSignal);
2080
- if (contentInfos.currentContentCanceller.isUsed) {
2081
- return;
2082
- }
2083
-
2084
- const videoRepresentation = this._priv_getCurrentRepresentations()?.video ?? null;
2272
+ const videoRepresentation = this.__priv_getCurrentRepresentations()?.video ?? null;
2085
2273
  this._priv_triggerEventIfNotStopped("videoRepresentationChange",
2086
2274
  videoRepresentation,
2087
2275
  cancelSignal);
@@ -2377,21 +2565,6 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2377
2565
  this.trigger("positionUpdate", positionData);
2378
2566
  }
2379
2567
 
2380
- private _priv_getCurrentRepresentations(
2381
- ) : Partial<Record<IBufferType, Representation|null>> | null {
2382
- if (this._priv_contentInfos === null) {
2383
- return null;
2384
- }
2385
- const { currentPeriod, activeRepresentations } = this._priv_contentInfos;
2386
- if (currentPeriod === null ||
2387
- activeRepresentations === null ||
2388
- isNullOrUndefined(activeRepresentations[currentPeriod.id]))
2389
- {
2390
- return null;
2391
- }
2392
- return activeRepresentations[currentPeriod.id];
2393
- }
2394
-
2395
2568
  /**
2396
2569
  * @param {string} evt
2397
2570
  * @param {*} arg
@@ -2402,7 +2575,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2402
2575
  arg : IEventPayload<IPublicAPIEvent, TEventName>,
2403
2576
  currentContentCancelSignal : CancellationSignal
2404
2577
  ) {
2405
- if (!currentContentCancelSignal.isCancelled) {
2578
+ if (!currentContentCancelSignal.isCancelled()) {
2406
2579
  this.trigger(evt, arg);
2407
2580
  }
2408
2581
  }
@@ -2497,8 +2670,93 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2497
2670
  }
2498
2671
  return cb(tracksStore, periodRef);
2499
2672
  }
2673
+
2674
+ /**
2675
+ * Method to call when some event lead to a high for possibility that the
2676
+ * available tracks for the given type have changed.
2677
+ * Send the corresponding `available*Tracks` change event with the last
2678
+ * available tracks.
2679
+ *
2680
+ * @param {string} trackType
2681
+ * @param {Object|undefined} [oPeriodRef] - optional period object used by the
2682
+ * `tracksStore` API, allows to optimize the method by bypassing this step.
2683
+ */
2684
+ private _priv_onAvailableTracksMayHaveChanged(
2685
+ trackType : IBufferType,
2686
+ oPeriodRef? : ITMPeriodObject
2687
+ ): void {
2688
+ const contentInfos = this._priv_contentInfos;
2689
+ if (contentInfos === null) {
2690
+ return;
2691
+ }
2692
+ const { currentPeriod,
2693
+ tracksStore,
2694
+ currentContentCanceller } = contentInfos;
2695
+ const cancelSignal = currentContentCanceller.signal;
2696
+ if (isNullOrUndefined(currentPeriod) || tracksStore === null) {
2697
+ return;
2698
+ }
2699
+ const periodRef = oPeriodRef ?? tracksStore.getPeriodObjectFromPeriod(currentPeriod);
2700
+ if (periodRef === undefined) {
2701
+ return;
2702
+ }
2703
+ switch (trackType) {
2704
+ case "video":
2705
+ const videoTracks = tracksStore.getAvailableVideoTracks(periodRef);
2706
+ this._priv_triggerEventIfNotStopped("availableVideoTracksChange",
2707
+ videoTracks ?? [],
2708
+ cancelSignal);
2709
+ break;
2710
+ case "audio":
2711
+ const audioTracks = tracksStore.getAvailableAudioTracks(periodRef);
2712
+ this._priv_triggerEventIfNotStopped("availableAudioTracksChange",
2713
+ audioTracks ?? [],
2714
+ cancelSignal);
2715
+ break;
2716
+ case "text":
2717
+ const textTracks = tracksStore.getAvailableTextTracks(periodRef);
2718
+ this._priv_triggerEventIfNotStopped("availableTextTracksChange",
2719
+ textTracks ?? [],
2720
+ cancelSignal);
2721
+ break;
2722
+ default:
2723
+ assertUnreachable(trackType);
2724
+ }
2725
+ }
2726
+
2727
+ /**
2728
+ * Method to call when a fatal error lead to the stopping of the current
2729
+ * content.
2730
+ *
2731
+ * @param {*} err - The error encountered.
2732
+ * @param {Object} contentInfos - The `IPublicApiContentInfos` object linked
2733
+ * to the content for which the error was received.
2734
+ */
2735
+ private _priv_onFatalError(
2736
+ err : unknown,
2737
+ contentInfos : IPublicApiContentInfos
2738
+ ): void {
2739
+ const formattedError = formatError(err, {
2740
+ defaultCode: "NONE",
2741
+ defaultReason: "An unknown error stopped content playback.",
2742
+ });
2743
+ formattedError.fatal = true;
2744
+ contentInfos.currentContentCanceller.cancel();
2745
+ this._priv_cleanUpCurrentContentState();
2746
+ this._priv_currentError = formattedError;
2747
+ log.error("API: The player stopped because of an error", formattedError);
2748
+ this._priv_setPlayerState(PLAYER_STATES.STOPPED);
2749
+
2750
+ // TODO This condition is here because the eventual callback called when the
2751
+ // player state is updated can launch a new content, thus the error will not
2752
+ // be here anymore, in which case triggering the "error" event is unwanted.
2753
+ // This is very ugly though, and we should probable have a better solution
2754
+ if (this._priv_currentError === formattedError) {
2755
+ this.trigger("error", formattedError);
2756
+ }
2757
+ }
2500
2758
  }
2501
- Player.version = /* PLAYER_VERSION */"4.0.0-beta.0";
2759
+ Player.version = /* PLAYER_VERSION */"4.0.0-beta.2";
2502
2760
 
2503
2761
  /** Every events sent by the RxPlayer's public API. */
2504
2762
  interface IPublicAPIEvent {
@@ -2516,9 +2774,12 @@ interface IPublicAPIEvent {
2516
2774
  availableAudioTracksChange : IAvailableAudioTrack[];
2517
2775
  availableTextTracksChange : IAvailableTextTrack[];
2518
2776
  availableVideoTracksChange : IAvailableVideoTrack[];
2777
+ play: null;
2778
+ pause: null;
2519
2779
  newAvailablePeriods : IPeriod[];
2520
2780
  brokenRepresentationsLock : IBrokenRepresentationsLockContext;
2521
2781
  trackUpdate : ITrackUpdateEventPayload;
2782
+ representationListUpdate : IRepresentationListUpdateContext;
2522
2783
  seeking : null;
2523
2784
  seeked : null;
2524
2785
  streamEvent : IStreamEvent;
@@ -118,10 +118,7 @@ function createAudioTracks(
118
118
  */
119
119
  function createTextTracks(
120
120
  textTracks: ICompatTextTrackList
121
- ): Array<{ track: { id: string;
122
- normalized: string;
123
- language: string;
124
- closedCaption: boolean; };
121
+ ): Array<{ track: ITextTrack;
125
122
  nativeTrack: TextTrack; }> {
126
123
  const newTextTracks = [];
127
124
  const languagesOccurences: Partial<Record<string, number>> = {};
@@ -135,10 +132,20 @@ function createTextTracks(
135
132
  "_" +
136
133
  occurences.toString();
137
134
  languagesOccurences[language] = occurences + 1;
138
- const track = { language: textTrack.language,
139
- id,
140
- normalized: normalizeLanguage(textTrack.language),
141
- closedCaption: textTrack.kind === "captions" };
135
+
136
+ // Safari seems to be indicating that the subtitles track is a forced
137
+ // subtitles track by setting the `kind` attribute to `"forced"`.
138
+ // As of now (2023-04-04), this is not standard.
139
+ // @see https://github.com/whatwg/html/issues/4472
140
+ const forced = (textTrack.kind as string) === "forced" ?
141
+ true :
142
+ undefined;
143
+ const track = { language: textTrack.language,
144
+ forced,
145
+ label: textTrack.label,
146
+ id,
147
+ normalized: normalizeLanguage(textTrack.language),
148
+ closedCaption: textTrack.kind === "captions" };
142
149
  newTextTracks.push({ track,
143
150
  nativeTrack: textTrack });
144
151
  }
@@ -393,6 +400,8 @@ export default class MediaElementTracksStore
393
400
  public getAvailableTextTracks(): IAvailableTextTrack[] {
394
401
  return this._textTracks.map(({ track, nativeTrack }) => {
395
402
  return { id: track.id,
403
+ label: track.label,
404
+ forced: track.forced,
396
405
  language: track.language,
397
406
  normalized: track.normalized,
398
407
  closedCaption: track.closedCaption,