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
@@ -1,5 +1,6 @@
1
1
  import log from "../../../log";
2
2
  import arrayFindIndex from "../../../utils/array_find_index";
3
+ import createCancellablePromise from "../../../utils/create_cancellable_promise";
3
4
  import TaskCanceller, {
4
5
  CancellationError,
5
6
  CancellationSignal,
@@ -64,19 +65,34 @@ export default class TaskPrioritizer<T> {
64
65
  cancelSignal: CancellationSignal
65
66
  ): Promise<T> {
66
67
  let newTask: IPrioritizerTask<T>;
67
-
68
- return new Promise<T>((resolve, reject) => {
68
+ return createCancellablePromise<T>(cancelSignal, (resolve, reject) => {
69
69
  /** Function allowing to start the underlying Promise. */
70
70
  const trigger = () => {
71
71
  if (newTask.hasEnded) {
72
- unregisterCancelSignal();
73
72
  return;
74
73
  }
75
- const interrupter = new TaskCanceller({ cancelOn: cancelSignal });
74
+ const finishTask = () => {
75
+ unlinkInterrupter();
76
+ this._endTask(newTask);
77
+ };
78
+
79
+ const onResolve = (value: T) => {
80
+ callbacks.beforeEnded();
81
+ finishTask();
82
+ resolve(value);
83
+ };
84
+
85
+ const onReject = (err: unknown) => {
86
+ finishTask();
87
+ reject(err);
88
+ };
89
+
90
+ const interrupter = new TaskCanceller();
91
+ const unlinkInterrupter = interrupter.linkToSignal(cancelSignal);
76
92
  newTask.interrupter = interrupter;
77
93
  interrupter.signal.register(() => {
78
94
  newTask.interrupter = null;
79
- if (!cancelSignal.isCancelled) {
95
+ if (!cancelSignal.isCancelled()) {
80
96
  callbacks.beforeInterrupted();
81
97
  }
82
98
  });
@@ -89,8 +105,8 @@ export default class TaskPrioritizer<T> {
89
105
  newTask.taskFn(interrupter.signal)
90
106
  .then(onResolve)
91
107
  .catch((err) => {
92
- if (!cancelSignal.isCancelled &&
93
- interrupter.isUsed &&
108
+ if (!cancelSignal.isCancelled() &&
109
+ interrupter.isUsed() &&
94
110
  err instanceof CancellationError)
95
111
  {
96
112
  return;
@@ -99,29 +115,6 @@ export default class TaskPrioritizer<T> {
99
115
  });
100
116
  };
101
117
 
102
- const unregisterCancelSignal = cancelSignal.register(
103
- (cancellationError: CancellationError) => {
104
- this._endTask(newTask);
105
- reject(cancellationError);
106
- }
107
- );
108
-
109
- const finishTask = () => {
110
- unregisterCancelSignal();
111
- this._endTask(newTask);
112
- };
113
-
114
- const onResolve = (value: T) => {
115
- callbacks.beforeEnded();
116
- finishTask();
117
- resolve(value);
118
- };
119
-
120
- const onReject = (err: unknown) => {
121
- finishTask();
122
- reject(err);
123
- };
124
-
125
118
  newTask = {
126
119
  hasEnded: false,
127
120
  priority,
@@ -146,6 +139,8 @@ export default class TaskPrioritizer<T> {
146
139
  this._interruptCancellableTasks();
147
140
  }
148
141
  }
142
+
143
+ return () => this._endTask(newTask);
149
144
  });
150
145
  }
151
146
 
@@ -266,7 +266,7 @@ export async function scheduleRequestWithCdns<T>(
266
266
  async function retryWithNextCdn(prevRequestError : unknown) : Promise<T> {
267
267
  const nextCdn = getCdnToRequest();
268
268
 
269
- if (cancellationSignal.isCancelled) {
269
+ if (cancellationSignal.isCancelled()) {
270
270
  throw cancellationSignal.cancellationError;
271
271
  }
272
272
 
@@ -275,7 +275,7 @@ export async function scheduleRequestWithCdns<T>(
275
275
  }
276
276
 
277
277
  onRetry(prevRequestError);
278
- if (cancellationSignal.isCancelled) {
278
+ if (cancellationSignal.isCancelled()) {
279
279
  throw cancellationSignal.cancellationError;
280
280
  }
281
281
 
@@ -309,26 +309,37 @@ export async function scheduleRequestWithCdns<T>(
309
309
  return requestCdn(nextWantedCdn);
310
310
  }
311
311
 
312
- const canceller = new TaskCanceller({ cancelOn: cancellationSignal });
312
+ const canceller = new TaskCanceller();
313
+ const unlinkCanceller = canceller.linkToSignal(cancellationSignal);
313
314
  return new Promise<T>((res, rej) => {
314
315
  /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
315
316
  cdnPrioritizer?.addEventListener("priorityChange", () => {
316
317
  const updatedPrioritaryCdn = getCdnToRequest();
317
- if (cancellationSignal.isCancelled) {
318
+ if (cancellationSignal.isCancelled()) {
318
319
  throw cancellationSignal.cancellationError;
319
320
  }
320
321
  if (updatedPrioritaryCdn === undefined) {
321
- return rej(prevRequestError);
322
+ return cleanAndReject(prevRequestError);
322
323
  }
323
324
  if (updatedPrioritaryCdn !== nextWantedCdn) {
324
325
  canceller.cancel();
325
326
  waitPotentialBackoffAndRequest(updatedPrioritaryCdn, prevRequestError)
326
- .then(res, rej);
327
+ .then(cleanAndResolve, cleanAndReject);
327
328
  }
328
329
  }, canceller.signal);
329
330
 
330
331
  cancellableSleep(blockedFor, canceller.signal)
331
- .then(() => requestCdn(nextWantedCdn).then(res, rej), noop);
332
+ .then(() => requestCdn(nextWantedCdn)
333
+ .then(cleanAndResolve, cleanAndReject), noop);
334
+
335
+ function cleanAndResolve(response : T) {
336
+ unlinkCanceller();
337
+ res(response);
338
+ }
339
+ function cleanAndReject(err : unknown) {
340
+ unlinkCanceller();
341
+ rej(err);
342
+ }
332
343
  });
333
344
  }
334
345
 
@@ -84,6 +84,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
84
84
  * events when it cannot, as well as "unstalled" events when it get out of one.
85
85
  */
86
86
  const rebufferingController = new RebufferingController(playbackObserver,
87
+ null,
87
88
  null,
88
89
  speed);
89
90
  rebufferingController.addEventListener("stalled", (evt) =>
@@ -167,7 +168,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
167
168
  }
168
169
  }, { emitCurrentValue: true, clearSignal: cancelSignal }))
169
170
  .catch((err) => {
170
- if (!cancelSignal.isCancelled) {
171
+ if (!cancelSignal.isCancelled()) {
171
172
  this._onFatalError(err);
172
173
  }
173
174
  });
@@ -25,13 +25,13 @@ import {
25
25
  } from "../../public_types";
26
26
  import { ITransportPipelines } from "../../transports";
27
27
  import assert from "../../utils/assert";
28
+ import createCancellablePromise from "../../utils/create_cancellable_promise";
28
29
  import objectAssign from "../../utils/object_assign";
29
30
  import createSharedReference, {
30
31
  IReadOnlySharedReference,
31
32
  ISharedReference,
32
33
  } from "../../utils/reference";
33
34
  import TaskCanceller, {
34
- CancellationError,
35
35
  CancellationSignal,
36
36
  } from "../../utils/task_canceller";
37
37
  import AdaptiveRepresentationSelector, {
@@ -67,7 +67,7 @@ import getInitialTime, {
67
67
  import getLoadedReference from "./utils/get_loaded_reference";
68
68
  import performInitialSeekAndPlay from "./utils/initial_seek_and_play";
69
69
  import initializeContentDecryption from "./utils/initialize_content_decryption";
70
- import MediaDurationUpdater from "./utils/media_duration_updater";
70
+ import MediaSourceDurationUpdater from "./utils/media_source_duration_updater";
71
71
  import RebufferingController from "./utils/rebuffering_controller";
72
72
  import streamEventsEmitter from "./utils/stream_events_emitter";
73
73
  import listenToMediaError from "./utils/throw_on_media_error";
@@ -124,21 +124,22 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
124
124
  if (this._initialManifestProm !== null) {
125
125
  return;
126
126
  }
127
- this._initialManifestProm = new Promise((res, rej) => {
128
- this._initCanceller.signal.register((err : CancellationError) => {
129
- this._manifestFetcher.dispose();
130
- rej(err);
131
- });
132
- this._manifestFetcher.addEventListener("warning", (err : IPlayerError) =>
133
- this.trigger("warning", err));
134
- this._manifestFetcher.addEventListener("error", (err : unknown) => {
135
- this.trigger("error", err);
136
- rej(err);
137
- });
138
- this._manifestFetcher.addEventListener("manifestReady", (manifest) => {
139
- res(manifest);
127
+ this._initialManifestProm = createCancellablePromise(
128
+ this._initCanceller.signal,
129
+ (res, rej) => {
130
+ this._manifestFetcher.addEventListener("warning", (err : IPlayerError) =>
131
+ this.trigger("warning", err));
132
+ this._manifestFetcher.addEventListener("error", (err : unknown) => {
133
+ this.trigger("error", err);
134
+ rej(err);
135
+ });
136
+ this._manifestFetcher.addEventListener("manifestReady", (manifest) => {
137
+ res(manifest);
138
+ });
140
139
  });
141
- this._manifestFetcher.start();
140
+ this._manifestFetcher.start();
141
+ this._initCanceller.signal.register(() => {
142
+ this._manifestFetcher.dispose();
142
143
  });
143
144
  }
144
145
 
@@ -191,7 +192,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
191
192
  }
192
193
 
193
194
  private _onFatalError(err : unknown) {
194
- if (this._initCanceller.isUsed) {
195
+ if (this._initCanceller.isUsed()) {
195
196
  return;
196
197
  }
197
198
  this._initCanceller.cancel();
@@ -205,13 +206,9 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
205
206
  drmSystemId : string | undefined;
206
207
  unlinkMediaSource : TaskCanceller; }>
207
208
  {
208
- return new Promise((resolve, reject) => {
209
+ const initCanceller = this._initCanceller;
210
+ return createCancellablePromise(initCanceller.signal, (resolve) => {
209
211
  const { keySystems } = this._settings;
210
- const initCanceller = this._initCanceller;
211
-
212
- const unregisterReject = initCanceller.signal.register((err) => {
213
- reject(err);
214
- });
215
212
 
216
213
  /** Initialize decryption capabilities. */
217
214
  const drmInitRef =
@@ -226,9 +223,8 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
226
223
  }
227
224
  stopListeningToDrmUpdates();
228
225
 
229
- const mediaSourceCanceller = new TaskCanceller({
230
- cancelOn: initCanceller.signal,
231
- });
226
+ const mediaSourceCanceller = new TaskCanceller();
227
+ mediaSourceCanceller.linkToSignal(initCanceller.signal);
232
228
  openMediaSource(mediaElement, mediaSourceCanceller.signal)
233
229
  .then((mediaSource) => {
234
230
  const lastDrmStatus = drmInitRef.getValue();
@@ -237,7 +233,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
237
233
  drmInitRef.onUpdate((newDrmStatus, stopListeningToDrmUpdatesAgain) => {
238
234
  if (newDrmStatus.initializationState.type === "initialized") {
239
235
  stopListeningToDrmUpdatesAgain();
240
- unregisterReject();
241
236
  resolve({ mediaSource,
242
237
  drmSystemId: newDrmStatus.drmSystemId,
243
238
  unlinkMediaSource: mediaSourceCanceller });
@@ -245,7 +240,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
245
240
  }
246
241
  }, { emitCurrentValue: true, clearSignal: initCanceller.signal });
247
242
  } else if (drmStatus.initializationState.type === "initialized") {
248
- unregisterReject();
249
243
  resolve({ mediaSource,
250
244
  drmSystemId: drmStatus.drmSystemId,
251
245
  unlinkMediaSource: mediaSourceCanceller });
@@ -253,7 +247,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
253
247
  }
254
248
  })
255
249
  .catch((err) => {
256
- if (mediaSourceCanceller.isUsed) {
250
+ if (mediaSourceCanceller.isUsed()) {
257
251
  return;
258
252
  }
259
253
  this._onFatalError(err);
@@ -307,7 +301,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
307
301
  initCanceller.signal);
308
302
 
309
303
  this.trigger("manifestReady", manifest);
310
- if (initCanceller.isUsed) {
304
+ if (initCanceller.isUsed()) {
311
305
  return ;
312
306
  }
313
307
 
@@ -355,15 +349,16 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
355
349
  autoPlay : boolean; }
356
350
  ) : void {
357
351
  currentCanceller.cancel();
358
- if (initCanceller.isUsed) {
352
+ if (initCanceller.isUsed()) {
359
353
  return;
360
354
  }
361
- triggerEvent("reloadingMediaSource", null);
362
- if (initCanceller.isUsed) {
355
+ triggerEvent("reloadingMediaSource", reloadOrder);
356
+ if (initCanceller.isUsed()) {
363
357
  return;
364
358
  }
365
359
 
366
- const newCanceller = new TaskCanceller({ cancelOn: initCanceller.signal });
360
+ const newCanceller = new TaskCanceller();
361
+ newCanceller.linkToSignal(initCanceller.signal);
367
362
  openMediaSource(mediaElement, newCanceller.signal)
368
363
  .then(newMediaSource => {
369
364
  recursivelyLoadOnMediaSource(newMediaSource,
@@ -372,7 +367,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
372
367
  newCanceller);
373
368
  })
374
369
  .catch((err) => {
375
- if (newCanceller.isUsed) {
370
+ if (newCanceller.isUsed()) {
376
371
  return;
377
372
  }
378
373
  onFatalError(err);
@@ -427,7 +422,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
427
422
  (err) => this.trigger("warning", err),
428
423
  cancelSignal);
429
424
 
430
- if (cancelSignal.isCancelled) {
425
+ if (cancelSignal.isCancelled()) {
431
426
  return;
432
427
  }
433
428
 
@@ -453,9 +448,20 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
453
448
 
454
449
  const rebufferingController = this._createRebufferingController(playbackObserver,
455
450
  manifest,
451
+ segmentBuffersStore,
456
452
  speed,
457
453
  cancelSignal);
458
-
454
+ rebufferingController.addEventListener("needsReload", () => {
455
+ // NOTE couldn't both be always calculated at event destination?
456
+ // Maybe there are exceptions?
457
+ const position = initialSeekPerformed.getValue() ?
458
+ playbackObserver.getCurrentTime() :
459
+ initialTime;
460
+ const autoplay = initialPlayPerformed.getValue() ?
461
+ !playbackObserver.getIsPaused() :
462
+ autoPlay;
463
+ onReloadOrder({ position, autoPlay: autoplay });
464
+ }, cancelSignal);
459
465
  const contentTimeBoundariesObserver = this
460
466
  ._createContentTimeBoundariesObserver(manifest,
461
467
  mediaSource,
@@ -479,7 +485,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
479
485
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
480
486
  })
481
487
  .catch((err) => {
482
- if (cancelSignal.isCancelled) {
488
+ if (cancelSignal.isCancelled()) {
483
489
  return; // Current loading cancelled, no need to trigger the error
484
490
  }
485
491
  this._onFatalError(err);
@@ -503,8 +509,36 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
503
509
  */
504
510
  function handleStreamOrchestratorCallbacks() : IStreamOrchestratorCallbacks {
505
511
  return {
506
- needsBufferFlush: () =>
507
- playbackObserver.setCurrentTime(mediaElement.currentTime + 0.001),
512
+ needsBufferFlush: () => {
513
+ const seekedTime = mediaElement.currentTime + 0.001;
514
+ playbackObserver.setCurrentTime(seekedTime);
515
+
516
+ // Seek again once data begins to be buffered.
517
+ // This is sadly necessary on some browsers to avoid decoding
518
+ // issues after a flush.
519
+ //
520
+ // NOTE: there's in theory a potential race condition in the following
521
+ // logic as the callback could be called when media data is still
522
+ // being removed by the browser - which is an asynchronous process.
523
+ // The following condition checking for buffered data could thus lead
524
+ // to a false positive where we're actually checking previous data.
525
+ // For now, such scenario is avoided by setting the
526
+ // `includeLastObservation` option to `false` and calling
527
+ // `needsBufferFlush` once MSE media removal operations have been
528
+ // explicitely validated by the browser, but that's a complex and easy
529
+ // to break system.
530
+ playbackObserver.listen((obs, stopListening) => {
531
+ if (
532
+ // Data is buffered around the current position
533
+ obs.currentRange !== null ||
534
+ // Or, for whatever reason, playback is already advancing
535
+ obs.position > seekedTime + 0.1
536
+ ) {
537
+ stopListening();
538
+ playbackObserver.setCurrentTime(obs.position + 0.001);
539
+ }
540
+ }, { includeLastObservation: false, clearSignal: cancelSignal });
541
+ },
508
542
 
509
543
  streamStatusUpdate(value) {
510
544
  // Announce discontinuities if found
@@ -515,7 +549,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
515
549
  discontinuity: imminentDiscontinuity,
516
550
  position,
517
551
  });
518
- if (cancelSignal.isCancelled) {
552
+ if (cancelSignal.isCancelled()) {
519
553
  return; // Previous call has stopped streams due to a side-effect
520
554
  }
521
555
 
@@ -556,7 +590,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
556
590
 
557
591
  adaptationChange: (value) => {
558
592
  self.trigger("adaptationChange", value);
559
- if (cancelSignal.isCancelled) {
593
+ if (cancelSignal.isCancelled()) {
560
594
  return; // Previous call has stopped streams due to a side-effect
561
595
  }
562
596
  contentTimeBoundariesObserver.onAdaptationChange(value.type,
@@ -566,7 +600,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
566
600
 
567
601
  representationChange: (value) => {
568
602
  self.trigger("representationChange", value);
569
- if (cancelSignal.isCancelled) {
603
+ if (cancelSignal.isCancelled()) {
570
604
  return; // Previous call has stopped streams due to a side-effect
571
605
  }
572
606
  contentTimeBoundariesObserver.onRepresentationChange(value.type, value.period);
@@ -580,7 +614,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
580
614
 
581
615
  periodStreamCleared: (value) => {
582
616
  contentTimeBoundariesObserver.onPeriodCleared(value.type, value.period);
583
- if (cancelSignal.isCancelled) {
617
+ if (cancelSignal.isCancelled()) {
584
618
  return; // Previous call has stopped streams due to a side-effect
585
619
  }
586
620
  self.trigger("periodStreamCleared", value);
@@ -611,7 +645,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
611
645
  encryptionDataEncountered: (value) => {
612
646
  for (const protectionData of value) {
613
647
  protectionRef.setValue(protectionData);
614
- if (cancelSignal.isCancelled) {
648
+ if (cancelSignal.isCancelled()) {
615
649
  return; // Previous call has stopped streams due to a side-effect
616
650
  }
617
651
  }
@@ -645,9 +679,9 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
645
679
  cancelSignal : CancellationSignal
646
680
  ) : ContentTimeBoundariesObserver {
647
681
  /** Maintains the MediaSource's duration up-to-date with the Manifest */
648
- const mediaDurationUpdater = new MediaDurationUpdater(manifest, mediaSource);
682
+ const mediaSourceDurationUpdater = new MediaSourceDurationUpdater(mediaSource);
649
683
  cancelSignal.register(() => {
650
- mediaDurationUpdater.stop();
684
+ mediaSourceDurationUpdater.stopUpdating();
651
685
  });
652
686
  /** Allows to cancel a pending `end-of-stream` operation. */
653
687
  let endOfStreamCanceller : TaskCanceller | null = null;
@@ -665,12 +699,12 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
665
699
  this.trigger("activePeriodChanged", { period });
666
700
  });
667
701
  contentTimeBoundariesObserver.addEventListener("durationUpdate", (newDuration) => {
668
- log.debug("Init: Duration has to be updated.", newDuration);
669
- mediaDurationUpdater.updateKnownDuration(newDuration);
702
+ mediaSourceDurationUpdater.updateDuration(newDuration.duration, newDuration.isEnd);
670
703
  });
671
704
  contentTimeBoundariesObserver.addEventListener("endOfStream", () => {
672
705
  if (endOfStreamCanceller === null) {
673
- endOfStreamCanceller = new TaskCanceller({ cancelOn: cancelSignal });
706
+ endOfStreamCanceller = new TaskCanceller();
707
+ endOfStreamCanceller.linkToSignal(cancelSignal);
674
708
  log.debug("Init: end-of-stream order received.");
675
709
  maintainEndOfStream(mediaSource, endOfStreamCanceller.signal);
676
710
  }
@@ -682,6 +716,9 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
682
716
  endOfStreamCanceller = null;
683
717
  }
684
718
  });
719
+ const currentDuration = contentTimeBoundariesObserver.getCurrentDuration();
720
+ mediaSourceDurationUpdater.updateDuration(currentDuration.duration,
721
+ currentDuration.isEnd);
685
722
  return contentTimeBoundariesObserver;
686
723
  }
687
724
 
@@ -706,11 +743,13 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
706
743
  private _createRebufferingController(
707
744
  playbackObserver : PlaybackObserver,
708
745
  manifest : Manifest,
746
+ segmentBuffersStore : SegmentBuffersStore,
709
747
  speed : IReadOnlySharedReference<number>,
710
748
  cancelSignal : CancellationSignal
711
749
  ) : RebufferingController {
712
750
  const rebufferingController = new RebufferingController(playbackObserver,
713
751
  manifest,
752
+ segmentBuffersStore,
714
753
  speed);
715
754
  // Bubble-up events
716
755
  rebufferingController.addEventListener("stalled",
@@ -120,7 +120,15 @@ export interface IContentInitializerEvents {
120
120
  * Event sent when we're starting attach a new MediaSource to the media element
121
121
  * (after removing the previous one).
122
122
  */
123
- reloadingMediaSource: null;
123
+ reloadingMediaSource: {
124
+ /** The position we're reloading at, in seconds. */
125
+ position: number;
126
+ /**
127
+ * If `true`, we'll play directly after finishing the reloading operation.
128
+ * If `false`, we'll be paused after it.
129
+ */
130
+ autoPlay: boolean;
131
+ };
124
132
  /** Event sent after the player stalled. */
125
133
  stalled : IStallingSituation;
126
134
  /** Event sent when the player goes out of a stalling situation. */
@@ -111,18 +111,20 @@ export default class ContentTimeBoundariesObserver
111
111
  }, { includeLastObservation: true, clearSignal: cancelSignal });
112
112
 
113
113
  manifest.addEventListener("manifestUpdate", () => {
114
- this.trigger("durationUpdate", getManifestDuration());
115
- if (cancelSignal.isCancelled) {
114
+ this.trigger("durationUpdate", this._getManifestDuration());
115
+ if (cancelSignal.isCancelled()) {
116
116
  return;
117
117
  }
118
118
  this._checkEndOfStream();
119
119
  }, cancelSignal);
120
+ }
120
121
 
121
- function getManifestDuration() : number | undefined {
122
- return manifest.isDynamic ?
123
- maximumPositionCalculator.getMaximumAvailablePosition() :
124
- maximumPositionCalculator.getEndingPosition();
125
- }
122
+ /**
123
+ * Returns an estimate of the current duration of the content.
124
+ * @returns {Object}
125
+ */
126
+ public getCurrentDuration() : IDurationItem {
127
+ return this._getManifestDuration();
126
128
  }
127
129
 
128
130
  /**
@@ -154,14 +156,17 @@ export default class ContentTimeBoundariesObserver
154
156
  this._maximumPositionCalculator
155
157
  .updateLastVideoAdaptation(adaptation);
156
158
  }
157
- const newDuration = this._manifest.isDynamic ?
158
- this._maximumPositionCalculator.getMaximumAvailablePosition() :
159
- this._maximumPositionCalculator.getEndingPosition();
159
+ const endingPosition = this._maximumPositionCalculator.getEndingPosition();
160
+ const newDuration = endingPosition !== undefined ?
161
+ { isEnd: true,
162
+ duration: endingPosition } :
163
+ { isEnd: false,
164
+ duration: this._maximumPositionCalculator.getMaximumAvailablePosition() };
160
165
  this.trigger("durationUpdate", newDuration);
161
166
  }
162
167
  }
163
168
  }
164
- if (this._canceller.isUsed) {
169
+ if (this._canceller.isUsed()) {
165
170
  return;
166
171
  }
167
172
  if (adaptation === null) {
@@ -306,6 +311,15 @@ export default class ContentTimeBoundariesObserver
306
311
  }
307
312
  }
308
313
 
314
+ private _getManifestDuration() : IDurationItem {
315
+ const endingPosition = this._maximumPositionCalculator.getEndingPosition();
316
+ return endingPosition !== undefined ?
317
+ { isEnd: true,
318
+ duration: endingPosition } :
319
+ { isEnd: false,
320
+ duration: this._maximumPositionCalculator.getMaximumAvailablePosition() };
321
+ }
322
+
309
323
  private _lazilyCreateActiveStreamInfo(bufferType : IBufferType) : IActiveStreamsInfo {
310
324
  let streamInfo = this._activeStreams.get(bufferType);
311
325
  if (streamInfo === undefined) {
@@ -334,6 +348,28 @@ export default class ContentTimeBoundariesObserver
334
348
  }
335
349
  }
336
350
 
351
+ export interface IDurationItem {
352
+ /**
353
+ * The new maximum known position (note that this is the ending position
354
+ * currently known of the current content, it might be superior to the last
355
+ * position at which segments are available and it might also evolve over
356
+ * time), in seconds.
357
+ */
358
+ duration : number;
359
+ /**
360
+ * If `true`, the communicated `duration` is the actual end of the content.
361
+ * It may still be updated due to a track change or to add precision, but it
362
+ * is still a (rough) estimate of the maximum position that content should
363
+ * have.
364
+ *
365
+ * If `false`, this is the currently known maximum position associated to
366
+ * the content, but the content is still evolving (typically, new media
367
+ * segments are still being generated) and as such it can still have a
368
+ * longer duration in the future.
369
+ */
370
+ isEnd : boolean;
371
+ }
372
+
337
373
  /**
338
374
  * Events triggered by a `ContentTimeBoundariesObserver` where the keys are the
339
375
  * event names and the value is the payload of those events.
@@ -347,7 +383,7 @@ export interface IContentTimeBoundariesObserverEvent {
347
383
  * Triggered when the duration of the currently-playing content became known
348
384
  * or changed.
349
385
  */
350
- durationUpdate : number | undefined;
386
+ durationUpdate : IDurationItem;
351
387
  /**
352
388
  * Triggered when the last possible chronological segment for all types of
353
389
  * buffers has either been pushed or is being pushed to the corresponding
@@ -21,10 +21,9 @@ import {
21
21
  } from "../../../compat";
22
22
  import { MediaError } from "../../../errors";
23
23
  import log from "../../../log";
24
+ import createCancellablePromise from "../../../utils/create_cancellable_promise";
24
25
  import isNonEmptyString from "../../../utils/is_non_empty_string";
25
- import TaskCanceller, {
26
- CancellationSignal,
27
- } from "../../../utils/task_canceller";
26
+ import { CancellationSignal } from "../../../utils/task_canceller";
28
27
 
29
28
  /**
30
29
  * Dispose of ressources taken by the MediaSource:
@@ -128,21 +127,10 @@ export default function openMediaSource(
128
127
  mediaElement : HTMLMediaElement,
129
128
  unlinkSignal : CancellationSignal
130
129
  ) : Promise<MediaSource> {
131
- return new Promise((resolve, reject) => {
132
- let hasResolved = false;
130
+ return createCancellablePromise(unlinkSignal, (resolve) => {
133
131
  const mediaSource = createMediaSource(mediaElement, unlinkSignal);
134
- const eventListenerCanceller = new TaskCanceller({ cancelOn: unlinkSignal });
135
-
136
132
  events.onSourceOpen(mediaSource, () => {
137
- eventListenerCanceller.cancel();
138
- hasResolved = true;
139
133
  resolve(mediaSource);
140
- }, eventListenerCanceller.signal);
141
-
142
- unlinkSignal.register((error) => {
143
- if (!hasResolved) {
144
- reject(error);
145
- }
146
- });
134
+ }, unlinkSignal);
147
135
  });
148
136
  }