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
@@ -14,6 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ import { ScoreConfidenceLevel } from "../utils/representation_score_calculator";
18
+
17
19
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
18
20
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
19
21
  /* eslint-disable @typescript-eslint/no-var-requires */
@@ -47,12 +49,25 @@ describe("BufferBasedChooser", () => {
47
49
  bufferGap: 0,
48
50
  speed: 1,
49
51
  currentBitrate: undefined,
50
- currentScore: 4,
52
+ currentScore: { score: 4, confidenceLevel: ScoreConfidenceLevel.LOW },
53
+ })).toEqual(1);
54
+ expect(new BufferBasedChooser([1, 2, 3]).getEstimate({
55
+ bufferGap: 0,
56
+ speed: 1,
57
+ currentBitrate: undefined,
58
+ currentScore: { score: 4, confidenceLevel: ScoreConfidenceLevel.HIGH },
59
+ })).toEqual(1);
60
+ expect(new BufferBasedChooser([1, 2, 3]).getEstimate({
61
+ bufferGap: 0,
62
+ speed: 1,
63
+ currentBitrate: undefined,
64
+ currentScore: { score: 1, confidenceLevel: ScoreConfidenceLevel.LOW },
51
65
  })).toEqual(1);
52
66
  expect(new BufferBasedChooser([1, 2, 3]).getEstimate({
53
67
  bufferGap: 0,
54
68
  speed: 1,
55
- currentScore: 1,
69
+ currentBitrate: undefined,
70
+ currentScore: { score: 1, confidenceLevel: ScoreConfidenceLevel.HIGH },
56
71
  })).toEqual(1);
57
72
  });
58
73
 
@@ -76,7 +91,7 @@ describe("BufferBasedChooser", () => {
76
91
  });
77
92
 
78
93
  /* eslint-disable max-len */
79
- it("should go to the next bitrate if the current one is maintainable and we have more buffer than the next level", () => {
94
+ it("should not go to the next bitrate if we don't have a high enough maintainability score", () => {
80
95
  /* eslint-enable max-len */
81
96
  const logger = { debug: jest.fn() };
82
97
  jest.mock("../../../log", () => ({ __esModule: true as const,
@@ -86,82 +101,82 @@ describe("BufferBasedChooser", () => {
86
101
  bufferGap: 16,
87
102
  speed: 1,
88
103
  currentBitrate: 10,
89
- currentScore: 1.01,
90
- })).toEqual(20);
104
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.LOW },
105
+ })).toEqual(10);
91
106
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
92
107
  bufferGap: 30,
93
108
  speed: 1,
94
109
  currentBitrate: 20,
95
- currentScore: 1.01,
96
- })).toEqual(40);
110
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.LOW },
111
+ })).toEqual(20);
97
112
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
98
113
  bufferGap: 30,
99
114
  speed: 1,
100
115
  currentBitrate: 20,
101
- currentScore: 100,
102
- })).toEqual(40);
116
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.LOW },
117
+ })).toEqual(20);
103
118
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
104
119
  bufferGap: 30,
105
120
  speed: 2,
106
121
  currentBitrate: 20,
107
- currentScore: 2.1,
108
- })).toEqual(40);
122
+ currentScore: { score: 2.30, confidenceLevel: ScoreConfidenceLevel.LOW },
123
+ })).toEqual(20);
109
124
  expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
110
125
  bufferGap: 30,
111
126
  speed: 2,
112
127
  currentBitrate: 20,
113
- currentScore: 2.1,
114
- })).toEqual(40);
128
+ currentScore: { score: 2.30, confidenceLevel: ScoreConfidenceLevel.LOW },
129
+ })).toEqual(20);
115
130
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
116
131
  bufferGap: 30,
117
132
  speed: 0, // 0 is a special case
118
133
  currentBitrate: 20,
119
- currentScore: 100,
120
- })).toEqual(40);
134
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.LOW },
135
+ })).toEqual(20);
121
136
  });
122
137
 
123
138
  /* eslint-disable max-len */
124
- it("should go to the next bitrate if the current one is maintainable and we have the buffer corresponding to the next level", () => {
139
+ it("should go to the next bitrate if the current one is maintainable and we have more buffer than the next level", () => {
125
140
  /* eslint-enable max-len */
126
141
  const logger = { debug: jest.fn() };
127
142
  jest.mock("../../../log", () => ({ __esModule: true as const,
128
143
  default: logger }));
129
144
  const BufferBasedChooser = jest.requireActual("../buffer_based_chooser").default;
130
145
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
131
- bufferGap: 15,
146
+ bufferGap: 16,
132
147
  speed: 1,
133
148
  currentBitrate: 10,
134
- currentScore: 1.01,
149
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.HIGH },
135
150
  })).toEqual(20);
136
151
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
137
- bufferGap: 20,
152
+ bufferGap: 30,
138
153
  speed: 1,
139
154
  currentBitrate: 20,
140
- currentScore: 1.01,
155
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.HIGH },
141
156
  })).toEqual(40);
142
157
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
143
- bufferGap: 20,
158
+ bufferGap: 30,
144
159
  speed: 1,
145
160
  currentBitrate: 20,
146
- currentScore: 100,
161
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
147
162
  })).toEqual(40);
148
163
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
149
- bufferGap: 20,
164
+ bufferGap: 30,
150
165
  speed: 2,
151
166
  currentBitrate: 20,
152
- currentScore: 2.1,
167
+ currentScore: { score: 2.30, confidenceLevel: ScoreConfidenceLevel.HIGH },
153
168
  })).toEqual(40);
154
169
  expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
155
- bufferGap: 20,
170
+ bufferGap: 30,
156
171
  speed: 2,
157
172
  currentBitrate: 20,
158
- currentScore: 2.1,
173
+ currentScore: { score: 2.30, confidenceLevel: ScoreConfidenceLevel.HIGH },
159
174
  })).toEqual(40);
160
175
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
161
- bufferGap: 20,
176
+ bufferGap: 30,
162
177
  speed: 0, // 0 is a special case
163
178
  currentBitrate: 20,
164
- currentScore: 100,
179
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
165
180
  })).toEqual(40);
166
181
  });
167
182
 
@@ -176,31 +191,31 @@ describe("BufferBasedChooser", () => {
176
191
  bufferGap: 6,
177
192
  speed: 1,
178
193
  currentBitrate: 10,
179
- currentScore: 1.01,
194
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.HIGH },
180
195
  })).toEqual(10);
181
196
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
182
- bufferGap: 10,
197
+ bufferGap: 13,
183
198
  speed: 1,
184
199
  currentBitrate: 20,
185
- currentScore: 1.01,
200
+ currentScore: { score: 1.15, confidenceLevel: ScoreConfidenceLevel.HIGH },
186
201
  })).toEqual(20);
187
202
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
188
- bufferGap: 10,
203
+ bufferGap: 13,
189
204
  speed: 1,
190
205
  currentBitrate: 20,
191
- currentScore: 100,
206
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
192
207
  })).toEqual(20);
193
208
  expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
194
- bufferGap: 10,
209
+ bufferGap: 13,
195
210
  speed: 1,
196
211
  currentBitrate: 20,
197
- currentScore: 100,
212
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
198
213
  })).toEqual(20);
199
214
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
200
- bufferGap: 10,
215
+ bufferGap: 13,
201
216
  speed: 2,
202
217
  currentBitrate: 20,
203
- currentScore: 2.1,
218
+ currentScore: { score: 2.30, confidenceLevel: ScoreConfidenceLevel.HIGH },
204
219
  })).toEqual(20);
205
220
  });
206
221
 
@@ -216,13 +231,13 @@ describe("BufferBasedChooser", () => {
216
231
  bufferGap: 100000000000,
217
232
  speed: 1,
218
233
  currentBitrate: 40,
219
- currentScore: 1000000,
234
+ currentScore: { score: 1000000, confidenceLevel: ScoreConfidenceLevel.HIGH },
220
235
  })).toEqual(40);
221
236
  expect(new BufferBasedChooser([10, 20, 40, 40]).getEstimate({
222
237
  bufferGap: 100000000000,
223
238
  speed: 1,
224
239
  currentBitrate: 40,
225
- currentScore: 1000000,
240
+ currentScore: { score: 1000000, confidenceLevel: ScoreConfidenceLevel.HIGH },
226
241
  })).toEqual(40);
227
242
  });
228
243
 
@@ -237,31 +252,115 @@ describe("BufferBasedChooser", () => {
237
252
  bufferGap: 15,
238
253
  speed: 2,
239
254
  currentBitrate: 10,
240
- currentScore: 1.01,
255
+ currentScore: { score: 2, confidenceLevel: ScoreConfidenceLevel.HIGH },
241
256
  })).toEqual(10);
242
257
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
243
- bufferGap: 20,
258
+ bufferGap: 22,
244
259
  speed: 2,
245
260
  currentBitrate: 20,
246
- currentScore: 1.01,
261
+ currentScore: { score: 2, confidenceLevel: ScoreConfidenceLevel.HIGH },
247
262
  })).toEqual(20);
248
263
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
249
- bufferGap: 20,
264
+ bufferGap: 22,
250
265
  speed: 100,
251
266
  currentBitrate: 20,
252
- currentScore: 100,
267
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
253
268
  })).toEqual(20);
254
269
  expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
255
- bufferGap: 20,
270
+ bufferGap: 22,
256
271
  speed: 100,
257
272
  currentBitrate: 20,
258
- currentScore: 100,
273
+ currentScore: { score: 100, confidenceLevel: ScoreConfidenceLevel.HIGH },
259
274
  })).toEqual(20);
260
275
  expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
261
- bufferGap: 20,
276
+ bufferGap: 22,
277
+ speed: 3,
278
+ currentBitrate: 20,
279
+ currentScore: { score: 3, confidenceLevel: ScoreConfidenceLevel.HIGH },
280
+ })).toEqual(20);
281
+ });
282
+
283
+ /* eslint-disable max-len */
284
+ it("should lower bitrate if the current one is not maintainable due to the speed", () => {
285
+ /* eslint-enable max-len */
286
+ const logger = { debug: jest.fn() };
287
+ jest.mock("../../../log", () => ({ __esModule: true as const,
288
+ default: logger }));
289
+ const BufferBasedChooser = jest.requireActual("../buffer_based_chooser").default;
290
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
291
+ bufferGap: 15,
292
+ speed: 2,
293
+ currentBitrate: 10,
294
+ currentScore: { score: 1.9, confidenceLevel: ScoreConfidenceLevel.HIGH },
295
+ })).toEqual(10);
296
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
297
+ bufferGap: 22,
298
+ speed: 2,
299
+ currentBitrate: 20,
300
+ currentScore: { score: 1.9, confidenceLevel: ScoreConfidenceLevel.HIGH },
301
+ })).toEqual(10);
302
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
303
+ bufferGap: 22,
304
+ speed: 100,
305
+ currentBitrate: 20,
306
+ currentScore: { score: 99, confidenceLevel: ScoreConfidenceLevel.HIGH },
307
+ })).toEqual(10);
308
+ expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
309
+ bufferGap: 22,
310
+ speed: 100,
311
+ currentBitrate: 20,
312
+ currentScore: { score: 99, confidenceLevel: ScoreConfidenceLevel.HIGH },
313
+ })).toEqual(10);
314
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
315
+ bufferGap: 22,
316
+ speed: 3,
317
+ currentBitrate: 20,
318
+ currentScore: { score: 2.5, confidenceLevel: ScoreConfidenceLevel.HIGH },
319
+ })).toEqual(10);
320
+ });
321
+
322
+ /* eslint-disable max-len */
323
+ it("should not lower bitrate if the current one is not maintainable due to the speed but confidence on the score is low", () => {
324
+ /* eslint-enable max-len */
325
+ const logger = { debug: jest.fn() };
326
+ jest.mock("../../../log", () => ({ __esModule: true as const,
327
+ default: logger }));
328
+ const BufferBasedChooser = jest.requireActual("../buffer_based_chooser").default;
329
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
330
+ bufferGap: 15,
331
+ speed: 2,
332
+ currentBitrate: 10,
333
+ currentScore: { score: 1.9, confidenceLevel: ScoreConfidenceLevel.LOW },
334
+ })).toEqual(10);
335
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
336
+ bufferGap: 22,
337
+ speed: 2,
338
+ currentBitrate: 20,
339
+ currentScore: undefined,
340
+ })).toEqual(20);
341
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
342
+ bufferGap: 22,
343
+ speed: 2,
344
+ currentBitrate: 20,
345
+ currentScore: { score: 1.9, confidenceLevel: ScoreConfidenceLevel.LOW },
346
+ })).toEqual(20);
347
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
348
+ bufferGap: 22,
349
+ speed: 100,
350
+ currentBitrate: 20,
351
+ currentScore: { score: 99, confidenceLevel: ScoreConfidenceLevel.LOW },
352
+ })).toEqual(20);
353
+ expect(new BufferBasedChooser([10, 20, 20, 40]).getEstimate({
354
+ bufferGap: 22,
355
+ speed: 100,
356
+ currentBitrate: 20,
357
+ currentScore: { score: 99, confidenceLevel: ScoreConfidenceLevel.LOW },
358
+ })).toEqual(20);
359
+ expect(new BufferBasedChooser([10, 20, 40]).getEstimate({
360
+ bufferGap: 22,
262
361
  speed: 3,
263
362
  currentBitrate: 20,
264
- currentScore: 2.1,
363
+ currentScore: { score: 2.5, confidenceLevel: ScoreConfidenceLevel.LOW },
265
364
  })).toEqual(20);
266
365
  });
267
366
 
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ import config from "../../config";
17
18
  import log from "../../log";
18
19
  import Manifest, {
19
20
  Adaptation,
@@ -189,7 +190,8 @@ function getEstimateReference(
189
190
  * This TaskCanceller is used both for restarting estimates with a new
190
191
  * configuration and to cancel them altogether.
191
192
  */
192
- let currentEstimatesCanceller = new TaskCanceller({ cancelOn: stopAllEstimates });
193
+ let currentEstimatesCanceller = new TaskCanceller();
194
+ currentEstimatesCanceller.linkToSignal(stopAllEstimates);
193
195
 
194
196
  // Create `ISharedReference` on which estimates will be emitted.
195
197
  const estimateRef = createEstimateReference(representationsRef.getValue(),
@@ -259,8 +261,7 @@ function getEstimateReference(
259
261
  const timeRanges = val.buffered;
260
262
  const bufferGap = getLeftSizeOfRange(timeRanges, position.last);
261
263
  const { representation } = val.content;
262
- const scoreData = scoreCalculator.getEstimate(representation);
263
- const currentScore = scoreData?.[0];
264
+ const currentScore = scoreCalculator.getEstimate(representation);
264
265
  const currentBitrate = representation.bitrate;
265
266
  const observation = { bufferGap, currentBitrate, currentScore, speed };
266
267
  currentBufferBasedEstimate = bufferBasedChooser.getEstimate(observation);
@@ -306,11 +307,14 @@ function getEstimateReference(
306
307
  lastPlaybackObservation.speed :
307
308
  1);
308
309
 
309
- if (allowBufferBasedEstimates && bufferGap <= 5) {
310
+ const { ABR_ENTER_BUFFER_BASED_ALGO,
311
+ ABR_EXIT_BUFFER_BASED_ALGO } = config.getCurrent();
312
+
313
+ if (allowBufferBasedEstimates && bufferGap <= ABR_EXIT_BUFFER_BASED_ALGO) {
310
314
  allowBufferBasedEstimates = false;
311
315
  } else if (!allowBufferBasedEstimates &&
312
316
  isFinite(bufferGap) &&
313
- bufferGap > 10)
317
+ bufferGap >= ABR_ENTER_BUFFER_BASED_ALGO)
314
318
  {
315
319
  allowBufferBasedEstimates = true;
316
320
  }
@@ -428,7 +432,8 @@ function getEstimateReference(
428
432
  function restartEstimatesProductionFromCurrentConditions() : void {
429
433
  const representations = representationsRef.getValue();
430
434
  currentEstimatesCanceller.cancel();
431
- currentEstimatesCanceller = new TaskCanceller({ cancelOn: stopAllEstimates });
435
+ currentEstimatesCanceller = new TaskCanceller();
436
+ currentEstimatesCanceller.linkToSignal(stopAllEstimates);
432
437
  const newRef = createEstimateReference(representations,
433
438
  currentEstimatesCanceller.signal);
434
439
 
@@ -17,6 +17,54 @@
17
17
  import log from "../../log";
18
18
  import arrayFindIndex from "../../utils/array_find_index";
19
19
  import getBufferLevels from "./utils/get_buffer_levels";
20
+ import {
21
+ IRepresentationMaintainabilityScore,
22
+ ScoreConfidenceLevel,
23
+ } from "./utils/representation_score_calculator";
24
+
25
+ /**
26
+ * Minimum amount of time, in milliseconds, during which we are blocked from
27
+ * raising in quality after it had been considered as too high.
28
+ */
29
+ const MINIMUM_BLOCK_RAISE_DELAY = 6000;
30
+
31
+ /**
32
+ * Maximum amount of time, in milliseconds, during which we are blocked from
33
+ * raising in quality after it had been considered as too high.
34
+ */
35
+ const MAXIMUM_BLOCK_RAISE_DELAY = 15000;
36
+
37
+ /**
38
+ * Amount of time, in milliseconds, with which the blocking time in raising
39
+ * the quality will be incremented if the current quality estimate is seen
40
+ * as too unstable.
41
+ */
42
+ const RAISE_BLOCKING_DELAY_INCREMENT = 3000;
43
+
44
+ /**
45
+ * Amount of time, in milliseconds, with which the blocking time in raising
46
+ * the quality will be dcremented if the current quality estimate is seen
47
+ * as relatively stable, until `MINIMUM_BLOCK_RAISE_DELAY` is reached.
48
+ */
49
+ const RAISE_BLOCKING_DELAY_DECREMENT = 1000;
50
+
51
+ /**
52
+ * Amount of time, in milliseconds, after the "raise blocking delay" currently
53
+ * in place (during which it is forbidden to raise up in quality), during which
54
+ * we might want to raise the "raise blocking delay" if the last chosen quality
55
+ * seems unsuitable.
56
+ *
57
+ * For example, let's consider that the current raise blocking delay is at
58
+ * `4000`, or 4 seconds, and that this `STABILITY_CHECK_DELAY` is at `5000`, or
59
+ * 5 seconds.
60
+ * Here it means that if the estimated quality is found to be unsuitable less
61
+ * than 4+5 = 9 seconds after it last was, we will increment the raise blocking
62
+ * delay by `RAISE_BLOCKING_DELAY_INCREMENT` (unless `MAXIMUM_BLOCK_RAISE_DELAY`
63
+ * is reached).
64
+ * Else, if takes more than 9 seconds, the raise blocking delay might be
65
+ * decremented.
66
+ */
67
+ const STABILITY_CHECK_DELAY = 9000;
20
68
 
21
69
  /**
22
70
  * Choose a bitrate based on the currently available buffer.
@@ -29,18 +77,41 @@ import getBufferLevels from "./utils/get_buffer_levels";
29
77
  * "maintanable" or not.
30
78
  * If so, we may switch to a better quality, or conversely to a worse quality.
31
79
  *
80
+ * It also rely on mechanisms to avoid fluctuating too much between qualities.
81
+ *
32
82
  * @class BufferBasedChooser
33
83
  */
34
84
  export default class BufferBasedChooser {
35
85
  private _levelsMap : number[];
36
86
  private _bitrates : number[];
37
87
 
88
+ /**
89
+ * Laast timestamp, in terms of `performance.now`, at which the current
90
+ * quality was seen as too high by this algorithm.
91
+ * Begins at `undefined`.
92
+ */
93
+ private _lastUnsuitableQualityTimestamp: number | undefined;
94
+
95
+ /**
96
+ * After lowering in quality, we forbid raising during a set amount of time.
97
+ * This amount is adaptive may continue to raise if it seems that quality
98
+ * is switching too much between low and high qualities.
99
+ *
100
+ * `_blockRaiseDelay` represents this time in milliseconds.
101
+ */
102
+ private _blockRaiseDelay: number;
103
+
38
104
  /**
39
105
  * @param {Array.<number>} bitrates
40
106
  */
41
107
  constructor(bitrates : number[]) {
42
- this._levelsMap = getBufferLevels(bitrates);
108
+ this._levelsMap = getBufferLevels(bitrates).map(bl => {
109
+ return bl + 4; // Add some buffer security as it will be used conjointly with
110
+ // other algorithms anyway
111
+ });
43
112
  this._bitrates = bitrates;
113
+ this._lastUnsuitableQualityTimestamp = undefined;
114
+ this._blockRaiseDelay = MINIMUM_BLOCK_RAISE_DELAY;
44
115
  log.debug("ABR: Steps for buffer based chooser.",
45
116
  this._levelsMap.map((l, i) => `bufferLevel: ${l}, bitrate: ${bitrates[i]}`)
46
117
  .join(" ,"));
@@ -59,43 +130,90 @@ export default class BufferBasedChooser {
59
130
  if (currentBitrate == null) {
60
131
  return bitrates[0];
61
132
  }
62
- const currentBitrateIndex = arrayFindIndex(bitrates, b => b === currentBitrate);
133
+
134
+ let currentBitrateIndex = -1;
135
+ for (let i = 0; i < bitrates.length; i++) {
136
+ // There could be bitrate duplicates. Only take the last one to simplify
137
+ const bitrate = bitrates[i];
138
+ if (bitrate === currentBitrate) {
139
+ currentBitrateIndex = i;
140
+ } else if (bitrate > currentBitrate) {
141
+ break;
142
+ }
143
+ }
144
+
63
145
  if (currentBitrateIndex < 0 || bitrates.length !== bufferLevels.length) {
64
146
  log.error("ABR: Current Bitrate not found in the calculated levels");
65
147
  return bitrates[0];
66
148
  }
67
149
 
68
150
  let scaledScore : number|undefined;
69
- if (currentScore != null) {
70
- scaledScore = speed === 0 ? currentScore : (currentScore / speed);
151
+ if (currentScore !== undefined) {
152
+ scaledScore = speed === 0 ? currentScore.score : (currentScore.score / speed);
71
153
  }
72
154
 
73
- if (scaledScore != null && scaledScore > 1) {
74
- const currentBufferLevel = bufferLevels[currentBitrateIndex];
75
- const nextIndex = (() => {
76
- for (let i = currentBitrateIndex + 1; i < bufferLevels.length; i++) {
77
- if (bufferLevels[i] > currentBufferLevel) {
78
- return i;
79
- }
80
- }
81
- })();
82
- if (nextIndex != null) {
83
- const nextBufferLevel = bufferLevels[nextIndex];
84
- if (bufferGap >= nextBufferLevel) {
85
- return bitrates[nextIndex];
155
+ const actualBufferGap = isFinite(bufferGap) ?
156
+ bufferGap :
157
+ 0;
158
+
159
+ const now = performance.now();
160
+
161
+ if (
162
+ actualBufferGap < bufferLevels[currentBitrateIndex] ||
163
+ (
164
+ scaledScore !== undefined && scaledScore < 1 &&
165
+ currentScore?.confidenceLevel === ScoreConfidenceLevel.HIGH
166
+ )
167
+ ) {
168
+ const timeSincePrev = this._lastUnsuitableQualityTimestamp === undefined ?
169
+ -1 :
170
+ now - this._lastUnsuitableQualityTimestamp;
171
+ if (timeSincePrev < this._blockRaiseDelay + STABILITY_CHECK_DELAY) {
172
+ const newDelay = this._blockRaiseDelay + RAISE_BLOCKING_DELAY_INCREMENT;
173
+ this._blockRaiseDelay = Math.min(newDelay, MAXIMUM_BLOCK_RAISE_DELAY);
174
+ log.debug("ABR: Incrementing blocking raise in BufferBasedChooser due " +
175
+ "to unstable quality",
176
+ this._blockRaiseDelay);
177
+ } else {
178
+ const newDelay = this._blockRaiseDelay - RAISE_BLOCKING_DELAY_DECREMENT;
179
+ this._blockRaiseDelay = Math.max(MINIMUM_BLOCK_RAISE_DELAY, newDelay);
180
+ log.debug("ABR: Lowering quality in BufferBasedChooser", this._blockRaiseDelay);
181
+ }
182
+ this._lastUnsuitableQualityTimestamp = now;
183
+ // Security if multiple bitrates are equal, we now take the first one
184
+ const baseIndex = arrayFindIndex(bitrates, (b) => b === currentBitrate);
185
+ for (let i = baseIndex - 1; i >= 0; i--) {
186
+ if (actualBufferGap >= bufferLevels[i]) {
187
+ return bitrates[i];
86
188
  }
87
189
  }
190
+ return bitrates[0];
191
+ }
192
+
193
+ if (
194
+ (
195
+ this._lastUnsuitableQualityTimestamp !== undefined &&
196
+ now - this._lastUnsuitableQualityTimestamp < this._blockRaiseDelay
197
+ ) ||
198
+ scaledScore === undefined || scaledScore < 1.15 ||
199
+ currentScore?.confidenceLevel !== ScoreConfidenceLevel.HIGH
200
+ ) {
201
+ return currentBitrate;
88
202
  }
89
203
 
90
- if (scaledScore == null || scaledScore < 1.15) {
91
- const currentBufferLevel = bufferLevels[currentBitrateIndex];
92
- if (bufferGap < currentBufferLevel) {
93
- for (let i = currentBitrateIndex - 1; i >= 0; i--) {
94
- if (bitrates[i] < currentBitrate) {
95
- return bitrates[i];
96
- }
204
+ const currentBufferLevel = bufferLevels[currentBitrateIndex];
205
+ const nextIndex = (() => {
206
+ for (let i = currentBitrateIndex + 1; i < bufferLevels.length; i++) {
207
+ if (bufferLevels[i] > currentBufferLevel) {
208
+ return i;
97
209
  }
98
- return currentBitrate;
210
+ }
211
+ })();
212
+ if (nextIndex !== undefined) {
213
+ const nextBufferLevel = bufferLevels[nextIndex];
214
+ if (bufferGap >= nextBufferLevel) {
215
+ log.debug("ABR: Raising quality in BufferBasedChooser", bitrates[nextIndex]);
216
+ return bitrates[nextIndex];
99
217
  }
100
218
  }
101
219
  return currentBitrate;
@@ -113,7 +231,7 @@ export interface IBufferBasedChooserPlaybackObservation {
113
231
  /** The bitrate of the currently downloaded segments, in bps. */
114
232
  currentBitrate? : number | undefined;
115
233
  /** The "maintainability score" of the currently downloaded segments. */
116
- currentScore? : number | undefined;
234
+ currentScore? : IRepresentationMaintainabilityScore | undefined;
117
235
  /** Playback rate wanted (e.g. `1` is regular playback, `2` is double speed etc.). */
118
236
  speed : number;
119
237
  }
@@ -23,6 +23,7 @@ import LastEstimateStorage, {
23
23
  } from "./utils/last_estimate_storage";
24
24
  import { IRequestInfo } from "./utils/pending_requests_store";
25
25
  import RepresentationScoreCalculator, {
26
+ IRepresentationMaintainabilityScore,
26
27
  ScoreConfidenceLevel,
27
28
  } from "./utils/representation_score_calculator";
28
29
 
@@ -178,11 +179,11 @@ export default class GuessBasedChooser {
178
179
  private _canGuessHigher(
179
180
  bufferGap : number,
180
181
  speed : number,
181
- [score, scoreConfidenceLevel] : [number, ScoreConfidenceLevel]
182
+ { score, confidenceLevel } : IRepresentationMaintainabilityScore
182
183
  ) : boolean {
183
184
  return isFinite(bufferGap) && bufferGap >= 2.5 &&
184
185
  performance.now() > this._blockGuessesUntil &&
185
- scoreConfidenceLevel === ScoreConfidenceLevel.HIGH &&
186
+ confidenceLevel === ScoreConfidenceLevel.HIGH &&
186
187
  score / speed > 1.01;
187
188
  }
188
189
 
@@ -197,13 +198,13 @@ export default class GuessBasedChooser {
197
198
  */
198
199
  private _shouldStopGuess(
199
200
  lastGuess : Representation,
200
- scoreData : [number, ScoreConfidenceLevel] | undefined,
201
+ scoreData : IRepresentationMaintainabilityScore | undefined,
201
202
  bufferGap : number,
202
203
  requests : IRequestInfo[]
203
204
  ) : boolean {
204
- if (scoreData !== undefined && scoreData[0] < 1.01) {
205
+ if (scoreData !== undefined && scoreData.score < 1.01) {
205
206
  return true;
206
- } else if ((scoreData === undefined || scoreData[0] < 1.2) && bufferGap < 0.6) {
207
+ } else if ((scoreData === undefined || scoreData.score < 1.2) && bufferGap < 0.6) {
207
208
  return true;
208
209
  }
209
210
 
@@ -233,11 +234,11 @@ export default class GuessBasedChooser {
233
234
  private _isLastGuessValidated(
234
235
  lastGuess : Representation,
235
236
  incomingBestBitrate : number,
236
- scoreData : [number, ScoreConfidenceLevel] | undefined
237
+ scoreData : IRepresentationMaintainabilityScore | undefined
237
238
  ) : boolean {
238
239
  if (scoreData !== undefined &&
239
- scoreData[1] === ScoreConfidenceLevel.HIGH &&
240
- scoreData[0] > 1.5)
240
+ scoreData.confidenceLevel === ScoreConfidenceLevel.HIGH &&
241
+ scoreData.score > 1.5)
241
242
  {
242
243
  return true;
243
244
  }