rx-player 4.4.1-dev.2025101500 → 4.4.1

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 (350) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +111 -50
  3. package/VERSION +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  5. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  6. package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  7. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  8. package/dist/commonjs/core/{entry → main/common}/FreezeResolver.d.ts +3 -3
  9. package/dist/commonjs/core/main/common/FreezeResolver.d.ts.map +1 -0
  10. package/dist/commonjs/core/{entry → main/common}/FreezeResolver.js +3 -3
  11. package/dist/commonjs/core/{entry → main/common}/content_time_boundaries_observer.d.ts +5 -5
  12. package/dist/commonjs/core/main/common/content_time_boundaries_observer.d.ts.map +1 -0
  13. package/dist/commonjs/core/{entry → main/common}/content_time_boundaries_observer.js +6 -6
  14. package/dist/{es2017/core/entry → commonjs/core/main/common}/create_content_time_boundaries_observer.d.ts +6 -6
  15. package/dist/commonjs/core/main/common/create_content_time_boundaries_observer.d.ts.map +1 -0
  16. package/dist/commonjs/core/{entry → main/common}/create_content_time_boundaries_observer.js +1 -1
  17. package/dist/{es2017/core/entry → commonjs/core/main/common}/get_buffered_data_per_media_buffer.d.ts +4 -4
  18. package/dist/commonjs/core/main/common/get_buffered_data_per_media_buffer.d.ts.map +1 -0
  19. package/dist/commonjs/core/{entry → main/common}/get_buffered_data_per_media_buffer.js +1 -1
  20. package/dist/{es2017/core/entry → commonjs/core/main/common}/get_thumbnail_data.d.ts +3 -3
  21. package/dist/commonjs/core/main/common/get_thumbnail_data.d.ts.map +1 -0
  22. package/dist/commonjs/core/{entry → main/common}/get_thumbnail_data.js +2 -2
  23. package/dist/{es2017/core/entry → commonjs/core/main/common}/synchronize_sinks_on_observation.d.ts +2 -2
  24. package/dist/commonjs/core/main/common/synchronize_sinks_on_observation.d.ts.map +1 -0
  25. package/dist/{es2017/core/entry → commonjs/core/main/worker}/content_preparer.d.ts +15 -22
  26. package/dist/commonjs/core/main/worker/content_preparer.d.ts.map +1 -0
  27. package/dist/commonjs/core/{entry → main/worker}/content_preparer.js +64 -62
  28. package/dist/commonjs/core/main/worker/globals.d.ts +14 -0
  29. package/dist/commonjs/core/main/worker/globals.d.ts.map +1 -0
  30. package/dist/commonjs/core/main/worker/globals.js +26 -0
  31. package/dist/commonjs/core/main/worker/index.d.ts +3 -0
  32. package/dist/commonjs/core/main/worker/index.d.ts.map +1 -0
  33. package/dist/commonjs/core/main/worker/index.js +4 -0
  34. package/dist/commonjs/core/main/worker/send_message.d.ts +4 -0
  35. package/dist/commonjs/core/main/worker/send_message.d.ts.map +1 -0
  36. package/dist/commonjs/core/main/worker/send_message.js +23 -0
  37. package/dist/{es2017/core/entry → commonjs/core/main/worker}/track_choice_setter.d.ts +4 -4
  38. package/dist/commonjs/core/main/worker/track_choice_setter.d.ts.map +1 -0
  39. package/dist/commonjs/core/{entry → main/worker}/track_choice_setter.js +4 -4
  40. package/dist/commonjs/core/main/worker/worker_main.d.ts +2 -0
  41. package/dist/commonjs/core/main/worker/worker_main.d.ts.map +1 -0
  42. package/dist/commonjs/core/{entry/core_entry.js → main/worker/worker_main.js} +153 -192
  43. package/dist/commonjs/core/{entry/core_text_displayer_interface.d.ts → main/worker/worker_text_displayer_interface.d.ts} +11 -11
  44. package/dist/commonjs/core/main/worker/worker_text_displayer_interface.d.ts.map +1 -0
  45. package/dist/commonjs/core/{entry/core_text_displayer_interface.js → main/worker/worker_text_displayer_interface.js} +22 -22
  46. package/dist/commonjs/core/types.d.ts +1 -519
  47. package/dist/commonjs/core/types.d.ts.map +1 -1
  48. package/dist/commonjs/core/types.js +0 -1
  49. package/dist/commonjs/experimental/features/local.d.ts.map +1 -1
  50. package/dist/commonjs/experimental/features/local.js +1 -7
  51. package/dist/commonjs/experimental/features/metaplaylist.d.ts.map +1 -1
  52. package/dist/commonjs/experimental/features/metaplaylist.js +1 -7
  53. package/dist/commonjs/experimental/features/multi_thread.d.ts.map +1 -1
  54. package/dist/commonjs/experimental/features/multi_thread.js +2 -6
  55. package/dist/commonjs/features/features_object.js +1 -1
  56. package/dist/commonjs/features/list/dash.d.ts.map +1 -1
  57. package/dist/commonjs/features/list/dash.js +1 -7
  58. package/dist/commonjs/features/list/dash_wasm.d.ts.map +1 -1
  59. package/dist/commonjs/features/list/dash_wasm.js +1 -7
  60. package/dist/commonjs/features/list/media_source_main.d.ts.map +1 -1
  61. package/dist/commonjs/features/list/media_source_main.js +1 -7
  62. package/dist/commonjs/features/list/smooth.d.ts.map +1 -1
  63. package/dist/commonjs/features/list/smooth.js +1 -7
  64. package/dist/commonjs/features/types.d.ts +4 -20
  65. package/dist/commonjs/features/types.d.ts.map +1 -1
  66. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  67. package/dist/commonjs/main_thread/api/public_api.js +44 -40
  68. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +108 -166
  69. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  70. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +918 -1491
  71. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts +308 -0
  72. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -0
  73. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.js +1713 -0
  74. package/dist/commonjs/main_thread/init/send_message.d.ts +3 -0
  75. package/dist/commonjs/main_thread/init/send_message.d.ts.map +1 -0
  76. package/dist/commonjs/main_thread/init/send_message.js +13 -0
  77. package/dist/commonjs/main_thread/init/utils/create_core_playback_observer.d.ts.map +1 -1
  78. package/dist/commonjs/main_thread/init/utils/create_core_playback_observer.js +1 -2
  79. package/dist/commonjs/main_thread/init/utils/update_manifest_codec_support.d.ts +1 -1
  80. package/dist/commonjs/main_thread/init/utils/update_manifest_codec_support.d.ts.map +1 -1
  81. package/dist/commonjs/main_thread/types.d.ts +0 -537
  82. package/dist/commonjs/main_thread/types.d.ts.map +1 -1
  83. package/dist/commonjs/manifest/utils.d.ts.map +1 -1
  84. package/dist/commonjs/manifest/utils.js +4 -18
  85. package/dist/commonjs/mse/worker_media_source_interface.d.ts +2 -2
  86. package/dist/commonjs/mse/worker_media_source_interface.d.ts.map +1 -1
  87. package/dist/commonjs/mse/worker_media_source_interface.js +12 -12
  88. package/dist/commonjs/multithread_types.d.ts +915 -0
  89. package/dist/commonjs/multithread_types.d.ts.map +1 -0
  90. package/dist/commonjs/multithread_types.js +7 -0
  91. package/dist/commonjs/parsers/manifest/smooth/create_parser.d.ts +1 -1
  92. package/dist/commonjs/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  93. package/dist/commonjs/parsers/manifest/smooth/create_parser.js +27 -31
  94. package/dist/commonjs/parsers/manifest/smooth/parse_C_nodes.d.ts +2 -3
  95. package/dist/commonjs/parsers/manifest/smooth/parse_C_nodes.d.ts.map +1 -1
  96. package/dist/commonjs/parsers/manifest/smooth/parse_C_nodes.js +7 -16
  97. package/dist/commonjs/parsers/manifest/smooth/parse_protection_node.d.ts +2 -3
  98. package/dist/commonjs/parsers/manifest/smooth/parse_protection_node.d.ts.map +1 -1
  99. package/dist/commonjs/parsers/manifest/smooth/parse_protection_node.js +6 -37
  100. package/dist/commonjs/parsers/manifest/smooth/utils/parseBoolean.d.ts +1 -1
  101. package/dist/commonjs/parsers/manifest/smooth/utils/parseBoolean.d.ts.map +1 -1
  102. package/dist/commonjs/parsers/manifest/smooth/utils/reduceChildren.d.ts +2 -3
  103. package/dist/commonjs/parsers/manifest/smooth/utils/reduceChildren.d.ts.map +1 -1
  104. package/dist/commonjs/parsers/manifest/smooth/utils/reduceChildren.js +5 -28
  105. package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts +28 -8
  106. package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts.map +1 -1
  107. package/dist/commonjs/playback_observer/media_element_playback_observer.js +146 -64
  108. package/dist/{es2017/playback_observer/core_playback_observer.d.ts → commonjs/playback_observer/worker_playback_observer.d.ts} +8 -8
  109. package/dist/commonjs/playback_observer/worker_playback_observer.d.ts.map +1 -0
  110. package/dist/commonjs/playback_observer/{core_playback_observer.js → worker_playback_observer.js} +13 -13
  111. package/dist/commonjs/transports/smooth/pipelines.d.ts.map +1 -1
  112. package/dist/commonjs/transports/smooth/pipelines.js +3 -25
  113. package/dist/commonjs/worker_entry_point.js +2 -62
  114. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  115. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  116. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  117. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  118. package/dist/es2017/core/{entry → main/common}/FreezeResolver.d.ts +3 -3
  119. package/dist/es2017/core/main/common/FreezeResolver.d.ts.map +1 -0
  120. package/dist/es2017/core/{entry → main/common}/FreezeResolver.js +3 -3
  121. package/dist/es2017/core/{entry → main/common}/content_time_boundaries_observer.d.ts +5 -5
  122. package/dist/es2017/core/main/common/content_time_boundaries_observer.d.ts.map +1 -0
  123. package/dist/es2017/core/{entry → main/common}/content_time_boundaries_observer.js +6 -6
  124. package/dist/{commonjs/core/entry → es2017/core/main/common}/create_content_time_boundaries_observer.d.ts +6 -6
  125. package/dist/es2017/core/main/common/create_content_time_boundaries_observer.d.ts.map +1 -0
  126. package/dist/es2017/core/{entry → main/common}/create_content_time_boundaries_observer.js +1 -1
  127. package/dist/{commonjs/core/entry → es2017/core/main/common}/get_buffered_data_per_media_buffer.d.ts +4 -4
  128. package/dist/es2017/core/main/common/get_buffered_data_per_media_buffer.d.ts.map +1 -0
  129. package/dist/es2017/core/{entry → main/common}/get_buffered_data_per_media_buffer.js +1 -1
  130. package/dist/{commonjs/core/entry → es2017/core/main/common}/get_thumbnail_data.d.ts +3 -3
  131. package/dist/es2017/core/main/common/get_thumbnail_data.d.ts.map +1 -0
  132. package/dist/es2017/core/{entry → main/common}/get_thumbnail_data.js +2 -2
  133. package/dist/{commonjs/core/entry → es2017/core/main/common}/synchronize_sinks_on_observation.d.ts +2 -2
  134. package/dist/es2017/core/main/common/synchronize_sinks_on_observation.d.ts.map +1 -0
  135. package/dist/{commonjs/core/entry → es2017/core/main/worker}/content_preparer.d.ts +15 -22
  136. package/dist/es2017/core/main/worker/content_preparer.d.ts.map +1 -0
  137. package/dist/es2017/core/{entry → main/worker}/content_preparer.js +55 -53
  138. package/dist/es2017/core/main/worker/globals.d.ts +14 -0
  139. package/dist/es2017/core/main/worker/globals.d.ts.map +1 -0
  140. package/dist/es2017/core/main/worker/globals.js +18 -0
  141. package/dist/es2017/core/main/worker/index.d.ts +3 -0
  142. package/dist/es2017/core/main/worker/index.d.ts.map +1 -0
  143. package/dist/es2017/core/main/worker/index.js +2 -0
  144. package/dist/es2017/core/main/worker/send_message.d.ts +4 -0
  145. package/dist/es2017/core/main/worker/send_message.d.ts.map +1 -0
  146. package/dist/es2017/core/main/worker/send_message.js +19 -0
  147. package/dist/{commonjs/core/entry → es2017/core/main/worker}/track_choice_setter.d.ts +4 -4
  148. package/dist/es2017/core/main/worker/track_choice_setter.d.ts.map +1 -0
  149. package/dist/es2017/core/{entry → main/worker}/track_choice_setter.js +4 -4
  150. package/dist/es2017/core/main/worker/worker_main.d.ts +2 -0
  151. package/dist/es2017/core/main/worker/worker_main.d.ts.map +1 -0
  152. package/dist/es2017/core/{entry/core_entry.js → main/worker/worker_main.js} +114 -153
  153. package/dist/es2017/core/{entry/core_text_displayer_interface.d.ts → main/worker/worker_text_displayer_interface.d.ts} +11 -11
  154. package/dist/es2017/core/main/worker/worker_text_displayer_interface.d.ts.map +1 -0
  155. package/dist/es2017/core/{entry/core_text_displayer_interface.js → main/worker/worker_text_displayer_interface.js} +10 -10
  156. package/dist/es2017/core/types.d.ts +1 -519
  157. package/dist/es2017/core/types.d.ts.map +1 -1
  158. package/dist/es2017/core/types.js +0 -1
  159. package/dist/es2017/experimental/features/local.d.ts.map +1 -1
  160. package/dist/es2017/experimental/features/local.js +1 -7
  161. package/dist/es2017/experimental/features/metaplaylist.d.ts.map +1 -1
  162. package/dist/es2017/experimental/features/metaplaylist.js +1 -7
  163. package/dist/es2017/experimental/features/multi_thread.d.ts.map +1 -1
  164. package/dist/es2017/experimental/features/multi_thread.js +2 -6
  165. package/dist/es2017/features/features_object.js +1 -1
  166. package/dist/es2017/features/list/dash.d.ts.map +1 -1
  167. package/dist/es2017/features/list/dash.js +1 -7
  168. package/dist/es2017/features/list/dash_wasm.d.ts.map +1 -1
  169. package/dist/es2017/features/list/dash_wasm.js +1 -7
  170. package/dist/es2017/features/list/media_source_main.d.ts.map +1 -1
  171. package/dist/es2017/features/list/media_source_main.js +1 -7
  172. package/dist/es2017/features/list/smooth.d.ts.map +1 -1
  173. package/dist/es2017/features/list/smooth.js +1 -7
  174. package/dist/es2017/features/types.d.ts +4 -20
  175. package/dist/es2017/features/types.d.ts.map +1 -1
  176. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  177. package/dist/es2017/main_thread/api/public_api.js +45 -41
  178. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +108 -166
  179. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  180. package/dist/es2017/main_thread/init/media_source_content_initializer.js +760 -1405
  181. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts +308 -0
  182. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -0
  183. package/dist/es2017/main_thread/init/multi_thread_content_initializer.js +1559 -0
  184. package/dist/es2017/main_thread/init/send_message.d.ts +3 -0
  185. package/dist/es2017/main_thread/init/send_message.d.ts.map +1 -0
  186. package/dist/es2017/main_thread/init/send_message.js +10 -0
  187. package/dist/es2017/main_thread/init/utils/create_core_playback_observer.d.ts.map +1 -1
  188. package/dist/es2017/main_thread/init/utils/create_core_playback_observer.js +1 -2
  189. package/dist/es2017/main_thread/init/utils/update_manifest_codec_support.d.ts +1 -1
  190. package/dist/es2017/main_thread/init/utils/update_manifest_codec_support.d.ts.map +1 -1
  191. package/dist/es2017/main_thread/types.d.ts +0 -537
  192. package/dist/es2017/main_thread/types.d.ts.map +1 -1
  193. package/dist/es2017/manifest/utils.d.ts.map +1 -1
  194. package/dist/es2017/manifest/utils.js +4 -16
  195. package/dist/es2017/mse/worker_media_source_interface.d.ts +2 -2
  196. package/dist/es2017/mse/worker_media_source_interface.d.ts.map +1 -1
  197. package/dist/es2017/mse/worker_media_source_interface.js +12 -12
  198. package/dist/es2017/multithread_types.d.ts +915 -0
  199. package/dist/es2017/multithread_types.d.ts.map +1 -0
  200. package/dist/es2017/multithread_types.js +6 -0
  201. package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts +1 -1
  202. package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  203. package/dist/es2017/parsers/manifest/smooth/create_parser.js +27 -31
  204. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.d.ts +2 -3
  205. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.d.ts.map +1 -1
  206. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.js +7 -16
  207. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.d.ts +2 -3
  208. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.d.ts.map +1 -1
  209. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.js +6 -15
  210. package/dist/es2017/parsers/manifest/smooth/utils/parseBoolean.d.ts +1 -1
  211. package/dist/es2017/parsers/manifest/smooth/utils/parseBoolean.d.ts.map +1 -1
  212. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.d.ts +2 -3
  213. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.d.ts.map +1 -1
  214. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.js +5 -6
  215. package/dist/es2017/playback_observer/media_element_playback_observer.d.ts +28 -8
  216. package/dist/es2017/playback_observer/media_element_playback_observer.d.ts.map +1 -1
  217. package/dist/es2017/playback_observer/media_element_playback_observer.js +144 -64
  218. package/dist/{commonjs/playback_observer/core_playback_observer.d.ts → es2017/playback_observer/worker_playback_observer.d.ts} +8 -8
  219. package/dist/es2017/playback_observer/worker_playback_observer.d.ts.map +1 -0
  220. package/dist/es2017/playback_observer/{core_playback_observer.js → worker_playback_observer.js} +2 -2
  221. package/dist/es2017/transports/smooth/pipelines.d.ts.map +1 -1
  222. package/dist/es2017/transports/smooth/pipelines.js +3 -25
  223. package/dist/es2017/worker_entry_point.js +2 -62
  224. package/dist/mpd-parser.wasm +0 -0
  225. package/dist/rx-player.js +19165 -21886
  226. package/dist/rx-player.min.js +20 -20
  227. package/dist/worker.js +8 -8
  228. package/eslint.config.mjs +647 -348
  229. package/package.json +6 -6
  230. package/src/README.md +198 -88
  231. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  232. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  233. package/src/core/{entry → main}/README.md +1 -1
  234. package/src/core/{entry → main/common}/FreezeResolver.ts +7 -7
  235. package/src/core/{entry → main/common}/content_time_boundaries_observer.ts +10 -10
  236. package/src/core/{entry → main/common}/create_content_time_boundaries_observer.ts +7 -7
  237. package/src/core/{entry → main/common}/get_buffered_data_per_media_buffer.ts +6 -6
  238. package/src/core/{entry → main/common}/get_thumbnail_data.ts +5 -5
  239. package/src/core/{entry → main/common}/synchronize_sinks_on_observation.ts +2 -2
  240. package/src/core/{entry → main/worker}/content_preparer.ts +76 -77
  241. package/src/core/main/worker/globals.ts +38 -0
  242. package/src/core/main/worker/index.ts +2 -0
  243. package/src/core/main/worker/send_message.ts +28 -0
  244. package/src/core/{entry → main/worker}/track_choice_setter.ts +7 -7
  245. package/src/core/{entry/core_entry.ts → main/worker/worker_main.ts} +149 -223
  246. package/src/core/{entry/core_text_displayer_interface.ts → main/worker/worker_text_displayer_interface.ts} +26 -26
  247. package/src/core/types.ts +3 -631
  248. package/src/experimental/features/__tests__/local.test.ts +2 -11
  249. package/src/experimental/features/__tests__/metaplaylist.test.ts +2 -11
  250. package/src/experimental/features/__tests__/multi_thread.test.ts +3 -8
  251. package/src/experimental/features/local.ts +1 -7
  252. package/src/experimental/features/metaplaylist.ts +1 -7
  253. package/src/experimental/features/multi_thread.ts +2 -6
  254. package/src/features/features_object.ts +1 -1
  255. package/src/features/list/__tests__/dash.test.ts +3 -12
  256. package/src/features/list/__tests__/smooth.test.ts +2 -11
  257. package/src/features/list/dash.ts +1 -7
  258. package/src/features/list/dash_wasm.ts +1 -7
  259. package/src/features/list/media_source_main.ts +1 -7
  260. package/src/features/list/smooth.ts +1 -7
  261. package/src/features/types.ts +4 -23
  262. package/src/main_thread/README.md +0 -8
  263. package/src/main_thread/api/public_api.ts +51 -47
  264. package/src/main_thread/init/media_source_content_initializer.ts +1164 -2046
  265. package/src/main_thread/init/multi_thread_content_initializer.ts +2330 -0
  266. package/src/main_thread/init/send_message.ts +15 -0
  267. package/src/main_thread/init/utils/create_core_playback_observer.ts +1 -2
  268. package/src/main_thread/init/utils/update_manifest_codec_support.ts +1 -1
  269. package/src/main_thread/types.ts +0 -610
  270. package/src/manifest/utils.ts +4 -20
  271. package/src/mse/worker_media_source_interface.ts +35 -35
  272. package/src/multithread_types.ts +1095 -0
  273. package/src/parsers/manifest/smooth/create_parser.ts +34 -40
  274. package/src/parsers/manifest/smooth/parse_C_nodes.ts +8 -19
  275. package/src/parsers/manifest/smooth/parse_protection_node.ts +9 -17
  276. package/src/parsers/manifest/smooth/utils/parseBoolean.ts +1 -1
  277. package/src/parsers/manifest/smooth/utils/reduceChildren.ts +7 -10
  278. package/src/playback_observer/media_element_playback_observer.ts +177 -73
  279. package/src/playback_observer/{core_playback_observer.ts → worker_playback_observer.ts} +13 -13
  280. package/src/transports/smooth/pipelines.ts +5 -25
  281. package/src/worker_entry_point.ts +2 -71
  282. package/dist/commonjs/core/entry/FreezeResolver.d.ts.map +0 -1
  283. package/dist/commonjs/core/entry/content_preparer.d.ts.map +0 -1
  284. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +0 -1
  285. package/dist/commonjs/core/entry/core_entry.d.ts +0 -36
  286. package/dist/commonjs/core/entry/core_entry.d.ts.map +0 -1
  287. package/dist/commonjs/core/entry/core_text_displayer_interface.d.ts.map +0 -1
  288. package/dist/commonjs/core/entry/create_content_time_boundaries_observer.d.ts.map +0 -1
  289. package/dist/commonjs/core/entry/get_buffered_data_per_media_buffer.d.ts.map +0 -1
  290. package/dist/commonjs/core/entry/get_thumbnail_data.d.ts.map +0 -1
  291. package/dist/commonjs/core/entry/index.d.ts +0 -5
  292. package/dist/commonjs/core/entry/index.d.ts.map +0 -1
  293. package/dist/commonjs/core/entry/index.js +0 -4
  294. package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
  295. package/dist/commonjs/core/entry/track_choice_setter.d.ts.map +0 -1
  296. package/dist/commonjs/core/entry/utils.d.ts +0 -3
  297. package/dist/commonjs/core/entry/utils.d.ts.map +0 -1
  298. package/dist/commonjs/core/entry/utils.js +0 -11
  299. package/dist/commonjs/main_thread/core_interface/base.d.ts +0 -13
  300. package/dist/commonjs/main_thread/core_interface/base.d.ts.map +0 -1
  301. package/dist/commonjs/main_thread/core_interface/base.js +0 -32
  302. package/dist/commonjs/main_thread/core_interface/monothread.d.ts +0 -13
  303. package/dist/commonjs/main_thread/core_interface/monothread.d.ts.map +0 -1
  304. package/dist/commonjs/main_thread/core_interface/monothread.js +0 -56
  305. package/dist/commonjs/main_thread/core_interface/multithread.d.ts +0 -25
  306. package/dist/commonjs/main_thread/core_interface/multithread.d.ts.map +0 -1
  307. package/dist/commonjs/main_thread/core_interface/multithread.js +0 -67
  308. package/dist/commonjs/main_thread/core_interface/types.d.ts +0 -6
  309. package/dist/commonjs/main_thread/core_interface/types.d.ts.map +0 -1
  310. package/dist/commonjs/main_thread/core_interface/types.js +0 -2
  311. package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +0 -1
  312. package/dist/es2017/core/entry/FreezeResolver.d.ts.map +0 -1
  313. package/dist/es2017/core/entry/content_preparer.d.ts.map +0 -1
  314. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +0 -1
  315. package/dist/es2017/core/entry/core_entry.d.ts +0 -36
  316. package/dist/es2017/core/entry/core_entry.d.ts.map +0 -1
  317. package/dist/es2017/core/entry/core_text_displayer_interface.d.ts.map +0 -1
  318. package/dist/es2017/core/entry/create_content_time_boundaries_observer.d.ts.map +0 -1
  319. package/dist/es2017/core/entry/get_buffered_data_per_media_buffer.d.ts.map +0 -1
  320. package/dist/es2017/core/entry/get_thumbnail_data.d.ts.map +0 -1
  321. package/dist/es2017/core/entry/index.d.ts +0 -5
  322. package/dist/es2017/core/entry/index.d.ts.map +0 -1
  323. package/dist/es2017/core/entry/index.js +0 -2
  324. package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
  325. package/dist/es2017/core/entry/track_choice_setter.d.ts.map +0 -1
  326. package/dist/es2017/core/entry/utils.d.ts +0 -3
  327. package/dist/es2017/core/entry/utils.d.ts.map +0 -1
  328. package/dist/es2017/core/entry/utils.js +0 -8
  329. package/dist/es2017/main_thread/core_interface/base.d.ts +0 -13
  330. package/dist/es2017/main_thread/core_interface/base.d.ts.map +0 -1
  331. package/dist/es2017/main_thread/core_interface/base.js +0 -28
  332. package/dist/es2017/main_thread/core_interface/monothread.d.ts +0 -13
  333. package/dist/es2017/main_thread/core_interface/monothread.d.ts.map +0 -1
  334. package/dist/es2017/main_thread/core_interface/monothread.js +0 -32
  335. package/dist/es2017/main_thread/core_interface/multithread.d.ts +0 -25
  336. package/dist/es2017/main_thread/core_interface/multithread.d.ts.map +0 -1
  337. package/dist/es2017/main_thread/core_interface/multithread.js +0 -45
  338. package/dist/es2017/main_thread/core_interface/types.d.ts +0 -6
  339. package/dist/es2017/main_thread/core_interface/types.d.ts.map +0 -1
  340. package/dist/es2017/main_thread/core_interface/types.js +0 -1
  341. package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +0 -1
  342. package/src/core/entry/index.ts +0 -4
  343. package/src/core/entry/utils.ts +0 -11
  344. package/src/main_thread/core_interface/README.md +0 -22
  345. package/src/main_thread/core_interface/base.ts +0 -36
  346. package/src/main_thread/core_interface/monothread.ts +0 -46
  347. package/src/main_thread/core_interface/multithread.ts +0 -49
  348. package/src/main_thread/core_interface/types.ts +0 -5
  349. /package/dist/commonjs/core/{entry → main/common}/synchronize_sinks_on_observation.js +0 -0
  350. /package/dist/es2017/core/{entry → main/common}/synchronize_sinks_on_observation.js +0 -0
@@ -1,32 +1,63 @@
1
- import getEmeApiImplementation from "../../compat/eme";
1
+ /**
2
+ * Copyright 2015 CANAL+ Group
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import isCodecSupported from "../../compat/is_codec_supported";
2
17
  import mayMediaElementFailOnUndecipherableData from "../../compat/may_media_element_fail_on_undecipherable_data";
3
18
  import shouldReloadMediaSourceOnDecipherabilityUpdate from "../../compat/should_reload_media_source_on_decipherability_update";
4
- import { EncryptedMediaError, MediaError, NetworkError, OtherError, SourceBufferError, } from "../../errors";
19
+ import config from "../../config";
20
+ import AdaptiveRepresentationSelector from "../../core/adaptive";
21
+ import CmcdDataBuilder from "../../core/cmcd";
22
+ import { CdnPrioritizer, createThumbnailFetcher, ManifestFetcher, SegmentQueueCreator, } from "../../core/fetchers";
23
+ import createContentTimeBoundariesObserver from "../../core/main/common/create_content_time_boundaries_observer";
24
+ import FreezeResolver from "../../core/main/common/FreezeResolver";
25
+ import getThumbnailData from "../../core/main/common/get_thumbnail_data";
26
+ import synchronizeSegmentSinksOnObservation from "../../core/main/common/synchronize_sinks_on_observation";
27
+ import SegmentSinksStore from "../../core/segment_sinks";
28
+ import StreamOrchestrator from "../../core/stream";
29
+ import { MediaError } from "../../errors";
5
30
  import features from "../../features";
6
31
  import log from "../../log";
7
- import { replicateUpdatesOnManifestMetadata, updateDecipherabilityFromKeyIds, updateDecipherabilityFromProtectionData, } from "../../manifest";
8
- import MainMediaSourceInterface from "../../mse/main_media_source_interface";
9
- import arrayFind from "../../utils/array_find";
32
+ import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal";
10
33
  import assert, { assertUnreachable } from "../../utils/assert";
11
- import idGenerator from "../../utils/id_generator";
34
+ import createCancellablePromise from "../../utils/create_cancellable_promise";
12
35
  import isNullOrUndefined from "../../utils/is_null_or_undefined";
36
+ import noop from "../../utils/noop";
13
37
  import objectAssign from "../../utils/object_assign";
14
- import SharedReference from "../../utils/reference";
15
- import { RequestError } from "../../utils/request";
16
- import TaskCanceller, { CancellationError } from "../../utils/task_canceller";
38
+ import SyncOrAsync from "../../utils/sync_or_async";
39
+ import TaskCanceller from "../../utils/task_canceller";
17
40
  import { ContentDecryptorState, getKeySystemConfiguration } from "../decrypt";
18
41
  import { ContentInitializer } from "./types";
19
42
  import createCorePlaybackObserver from "./utils/create_core_playback_observer";
20
- import { resetMediaElement, disableRemotePlaybackOnManagedMediaSource, } from "./utils/create_media_source";
43
+ import createMediaSource from "./utils/create_media_source";
21
44
  import getInitialTime from "./utils/get_initial_time";
22
45
  import getLoadedReference from "./utils/get_loaded_reference";
23
46
  import performInitialSeekAndPlay from "./utils/initial_seek_and_play";
47
+ import initializeContentDecryption from "./utils/initialize_content_decryption";
48
+ import MainThreadTextDisplayerInterface from "./utils/main_thread_text_displayer_interface";
24
49
  import RebufferingController from "./utils/rebuffering_controller";
25
- import StreamEventsEmitter from "./utils/stream_events_emitter/stream_events_emitter";
50
+ import StreamEventsEmitter from "./utils/stream_events_emitter";
26
51
  import listenToMediaError from "./utils/throw_on_media_error";
27
- import { updateManifestCodecSupport } from "./utils/update_manifest_codec_support";
28
- const generateContentId = idGenerator();
29
52
  /**
53
+ * Allows to load a new content thanks to the MediaSource Extensions (a.k.a. MSE)
54
+ * Web APIs.
55
+ *
56
+ * Through this `ContentInitializer`, a Manifest will be fetched (and depending
57
+ * on the situation, refreshed), a `MediaSource` instance will be linked to the
58
+ * wanted `HTMLMediaElement` and chunks of media data, called segments, will be
59
+ * pushed on buffers associated to this `MediaSource` instance.
60
+ *
30
61
  * @class MediaSourceContentInitializer
31
62
  */
32
63
  export default class MediaSourceContentInitializer extends ContentInitializer {
@@ -37,146 +68,51 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
37
68
  */
38
69
  constructor(settings) {
39
70
  super();
40
- this._settings = settings;
71
+ this._initSettings = settings;
41
72
  this._initCanceller = new TaskCanceller();
42
- this._currentMediaSourceCanceller = new TaskCanceller();
43
- this._currentMediaSourceCanceller.linkToSignal(this._initCanceller.signal);
44
- this._currentContentInfo = null;
45
- this._awaitingRequests = {
46
- nextRequestId: 0,
47
- pendingSinkMetrics: new Map(),
48
- pendingThumbnailFetching: new Map(),
49
- };
50
- this._queuedCoreMessages = null;
73
+ this._manifest = null;
74
+ this._decryptionCapabilities = { status: "uninitialized", value: null };
75
+ const urls = settings.url === undefined ? undefined : [settings.url];
76
+ this._cmcdDataBuilder =
77
+ settings.cmcd === undefined ? null : new CmcdDataBuilder(settings.cmcd);
78
+ this._manifestFetcher = new ManifestFetcher(urls, settings.transport, Object.assign(Object.assign({}, settings.manifestRequestSettings), { lowLatencyMode: settings.lowLatencyMode, cmcdDataBuilder: this._cmcdDataBuilder }));
51
79
  }
52
80
  /**
53
81
  * Perform non-destructive preparation steps, to prepare a future content.
82
+ * For now, this mainly mean loading the Manifest document.
54
83
  */
55
84
  prepare() {
56
- var _a, _b;
57
- if (this._currentContentInfo !== null || this._initCanceller.isUsed()) {
85
+ if (this._manifest !== null) {
58
86
  return;
59
87
  }
60
- const contentId = generateContentId();
61
- const { adaptiveOptions, transport, transportOptions, useMseInWorker, coreInterface, } = this._settings;
62
- const { wantedBufferAhead, maxVideoBufferSize, maxBufferAhead, maxBufferBehind } = this._settings.bufferOptions;
63
- const initialVideoBitrate = adaptiveOptions.initialBitrates.video;
64
- const initialAudioBitrate = adaptiveOptions.initialBitrates.audio;
65
- this._currentContentInfo = {
66
- contentId,
67
- contentDecryptor: null,
68
- manifest: null,
69
- mediaSourceInfo: null,
70
- rebufferingController: null,
71
- streamEventsEmitter: null,
72
- initialTime: undefined,
73
- autoPlay: undefined,
74
- initialPlayPerformed: null,
75
- useMseInWorker,
76
- };
77
- coreInterface.sendMessage({
78
- type: "prepare" /* MainThreadMessageType.PrepareContent */,
79
- value: {
80
- contentId,
81
- cmcd: this._settings.cmcd,
82
- enableRepresentationAvoidance: this._settings.enableRepresentationAvoidance,
83
- url: this._settings.url,
84
- hasText: this._hasTextBufferFeature(),
85
- transport,
86
- transportOptions,
87
- initialVideoBitrate,
88
- initialAudioBitrate,
89
- manifestRetryOptions: Object.assign(Object.assign({}, this._settings.manifestRequestSettings), { lowLatencyMode: this._settings.lowLatencyMode }),
90
- segmentRetryOptions: this._settings.segmentRequestOptions,
91
- useMseInWorker,
92
- },
93
- });
94
- this._initCanceller.signal.register(() => {
95
- coreInterface.sendMessage({
96
- type: "stop" /* MainThreadMessageType.StopContent */,
97
- contentId,
98
- value: null,
88
+ this._manifest = SyncOrAsync.createAsync(createCancellablePromise(this._initCanceller.signal, (res, rej) => {
89
+ this._manifestFetcher.addEventListener("warning", (err) => this.trigger("warning", err));
90
+ this._manifestFetcher.addEventListener("error", (err) => {
91
+ this.trigger("error", err);
92
+ rej(err);
99
93
  });
100
- });
101
- if (this._initCanceller.isUsed()) {
102
- return;
103
- }
104
- this._queuedCoreMessages = [];
105
- log.debug("Init", "addEventListener prepare buffering core messages");
106
- const onmessage = (msgData) => {
107
- if (msgData.type !== "log" /* CoreMessageType.LogMessage */) {
108
- log.debug("Init", "Core message received", msgData.type);
109
- }
110
- const type = msgData.type;
111
- switch (type) {
112
- case "log" /* CoreMessageType.LogMessage */: {
113
- const formatted = msgData.value.logs.map((l) => {
114
- switch (typeof l) {
115
- case "string":
116
- case "number":
117
- case "boolean":
118
- case "undefined":
119
- return l;
120
- case "object":
121
- if (l === null) {
122
- return null;
123
- }
124
- return formatSentLogObject(l);
125
- default:
126
- assertUnreachable(l);
127
- }
128
- });
129
- switch (msgData.value.logLevel) {
130
- case "NONE":
131
- break;
132
- case "ERROR":
133
- log.error(msgData.value.namespace, ...formatted);
134
- break;
135
- case "WARNING":
136
- log.warn(msgData.value.namespace, ...formatted);
137
- break;
138
- case "INFO":
139
- log.info(msgData.value.namespace, ...formatted);
140
- break;
141
- case "DEBUG":
142
- log.debug(msgData.value.namespace, ...formatted);
143
- break;
144
- default:
145
- assertUnreachable(msgData.value.logLevel);
146
- }
147
- break;
148
- }
149
- default:
150
- if (this._queuedCoreMessages !== null) {
151
- this._queuedCoreMessages.push(msgData);
152
- }
153
- break;
154
- }
155
- };
156
- this._settings.coreInterface.addMessageListener(onmessage);
157
- const onmessageerror = () => {
158
- log.error("Init", "Error when receiving message from core.");
159
- };
160
- this._settings.coreInterface.addErrorListener(onmessageerror);
94
+ this._manifestFetcher.addEventListener("manifestReady", (manifest) => {
95
+ res(manifest);
96
+ });
97
+ }));
98
+ this._manifestFetcher.start();
161
99
  this._initCanceller.signal.register(() => {
162
- log.debug("Init", "removeEventListener prepare for core message");
163
- this._settings.coreInterface.removeMessageListener(onmessage);
164
- this._settings.coreInterface.removeErrorListener(onmessageerror);
100
+ this._manifestFetcher.dispose();
165
101
  });
166
- // Also bind all `SharedReference` objects:
167
- const throttleVideoBitrate = (_a = adaptiveOptions.throttlers.throttleBitrate.video) !== null && _a !== void 0 ? _a : new SharedReference(Infinity);
168
- bindNumberReferencesToCore(coreInterface, this._initCanceller.signal, [wantedBufferAhead, "wantedBufferAhead"], [maxVideoBufferSize, "maxVideoBufferSize"], [maxBufferAhead, "maxBufferAhead"], [maxBufferBehind, "maxBufferBehind"], [throttleVideoBitrate, "throttleVideoBitrate"]);
169
- const limitVideoResolution = (_b = adaptiveOptions.throttlers.limitResolution.video) !== null && _b !== void 0 ? _b : new SharedReference({
170
- height: undefined,
171
- width: undefined,
172
- pixelRatio: 1,
102
+ }
103
+ /**
104
+ * @param {HTMLMediaElement} mediaElement
105
+ * @param {Object} playbackObserver
106
+ */
107
+ start(mediaElement, playbackObserver) {
108
+ this.prepare(); // Load Manifest if not already done
109
+ /** Translate errors coming from the media element into RxPlayer errors. */
110
+ listenToMediaError(mediaElement, (error) => this._onFatalError(error), this._initCanceller.signal);
111
+ this._setupInitialMediaSourceAndDecryption(mediaElement)
112
+ .then((initResult) => this._onInitialMediaSourceReady(mediaElement, initResult.mediaSource, playbackObserver, initResult.drmSystemId, initResult.unlinkMediaSource))
113
+ .catch((err) => {
114
+ this._onFatalError(err);
173
115
  });
174
- limitVideoResolution.onUpdate((newVal) => {
175
- coreInterface.sendMessage({
176
- type: "ref-update" /* MainThreadMessageType.ReferenceUpdate */,
177
- value: { name: "limitVideoResolution", newVal },
178
- });
179
- }, { clearSignal: this._initCanceller.signal, emitCurrentValue: true });
180
116
  }
181
117
  /**
182
118
  * Update URL of the Manifest.
@@ -186,1068 +122,275 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
186
122
  * DASH's MPD) will be refreshed immediately.
187
123
  */
188
124
  updateContentUrls(urls, refreshNow) {
189
- if (this._currentContentInfo === null) {
190
- return;
191
- }
192
- this._settings.coreInterface.sendMessage({
193
- type: "urls-update" /* MainThreadMessageType.ContentUrlsUpdate */,
194
- contentId: this._currentContentInfo.contentId,
195
- value: { urls, refreshNow },
196
- });
125
+ this._manifestFetcher.updateContentUrls(urls, refreshNow);
197
126
  }
198
127
  /**
199
- * @param {HTMLMediaElement} mediaElement
200
- * @param {Object} playbackObserver
128
+ * Stop content and free all resources linked to this
129
+ * `MediaSourceContentInitializer`.
201
130
  */
202
- start(mediaElement, playbackObserver) {
203
- this.prepare(); // Load Manifest if not already done
131
+ dispose() {
132
+ this._initCanceller.cancel();
133
+ }
134
+ /**
135
+ * Callback called when an error interrupting playback arised.
136
+ * @param {*} err
137
+ */
138
+ _onFatalError(err) {
204
139
  if (this._initCanceller.isUsed()) {
205
140
  return;
206
141
  }
207
- let textDisplayer = null;
208
- if (this._settings.textTrackOptions.textTrackMode === "html" &&
209
- features.htmlTextDisplayer !== null) {
210
- assert(this._hasTextBufferFeature());
211
- textDisplayer = new features.htmlTextDisplayer(mediaElement, this._settings.textTrackOptions.textTrackElement);
212
- }
213
- else if (features.nativeTextDisplayer !== null) {
214
- assert(this._hasTextBufferFeature());
215
- textDisplayer = new features.nativeTextDisplayer(mediaElement);
216
- }
217
- else {
218
- assert(!this._hasTextBufferFeature());
219
- }
220
- this._initCanceller.signal.register(() => {
221
- textDisplayer === null || textDisplayer === void 0 ? void 0 : textDisplayer.stop();
222
- });
223
- /** Translate errors coming from the media element into RxPlayer errors. */
224
- listenToMediaError(mediaElement, (error) => this._onFatalError(error), this._initCanceller.signal);
225
- /**
226
- * Send content protection initialization data.
227
- * TODO remove and use ContentDecryptor directly when possible.
228
- */
229
- const lastContentProtection = new SharedReference(null);
230
- const mediaSourceStatus = new SharedReference(0 /* MediaSourceInitializationStatus.Nothing */);
231
- const { statusRef: drmInitializationStatus, contentDecryptor } = this._initializeContentDecryption(mediaElement, lastContentProtection, mediaSourceStatus, () => notifyAndStartMediaSourceReload(0, undefined, undefined), this._initCanceller.signal);
232
- const contentInfo = this._currentContentInfo;
233
- if (contentInfo !== null) {
234
- contentInfo.contentDecryptor = contentDecryptor;
235
- }
236
- const playbackStartParams = {
237
- mediaElement,
238
- textDisplayer,
239
- playbackObserver,
240
- drmInitializationStatus,
241
- mediaSourceStatus,
242
- };
243
- mediaSourceStatus.onUpdate((msInitStatus, stopListeningMSStatus) => {
244
- if (msInitStatus === 2 /* MediaSourceInitializationStatus.Attached */) {
245
- stopListeningMSStatus();
246
- this._startPlaybackIfReady(playbackStartParams);
247
- }
248
- }, { clearSignal: this._initCanceller.signal, emitCurrentValue: true });
249
- drmInitializationStatus.onUpdate((initializationStatus, stopListeningDrm) => {
250
- if (initializationStatus.initializationState.type === "initialized") {
251
- stopListeningDrm();
252
- this._startPlaybackIfReady(playbackStartParams);
253
- }
254
- }, { emitCurrentValue: true, clearSignal: this._initCanceller.signal });
255
- /**
256
- * Reset directly (synchronously) the current `MediaSource` and signal to
257
- * the core that we did so.
258
- * @param {number} deltaPosition - Position you want to seek to after
259
- * reloading, as a delta in seconds from the last polled playing position.
260
- * @param {number|undefined} minimumPosition - If set, minimum time bound
261
- * in seconds after `deltaPosition` has been applied.
262
- * @param {number|undefined} maximumPosition - If set, minimum time bound
263
- * in seconds after `deltaPosition` has been applied.
264
- */
265
- const notifyAndStartMediaSourceReload = (deltaPosition, minimumPosition, maximumPosition) => {
266
- const reloadingContentInfo = this._currentContentInfo;
267
- if (reloadingContentInfo === null) {
268
- log.warn("Init", "Asked to reload when no content is loaded.");
269
- return;
270
- }
271
- if (reloadingContentInfo === null ||
272
- reloadingContentInfo.mediaSourceInfo === null) {
273
- log.warn("Init", "Asked to reload when no MediaSource is active.");
274
- return;
275
- }
276
- const mediaSourceId = reloadingContentInfo.mediaSourceInfo.type === "main"
277
- ? reloadingContentInfo.mediaSourceInfo.mediaSource.id
278
- : reloadingContentInfo.mediaSourceInfo.mediaSourceId;
279
- this._settings.coreInterface.sendMessage({
280
- type: "ms-reload" /* MainThreadMessageType.MediaSourceReload */,
281
- mediaSourceId,
282
- value: null,
283
- });
284
- reloadMediaSource(deltaPosition, minimumPosition, maximumPosition);
285
- };
286
- /**
287
- * Reset directly (synchronously) the current `MediaSource`.
288
- *
289
- * It is assumed that `core` already knows about this action. If not, call
290
- * `notifyAndStartMediaSourceReload` instead.
291
- * @param {number} deltaPosition - Position you want to seek to after
292
- * reloading, as a delta in seconds from the last polled playing position.
293
- * @param {number|undefined} minimumPosition - If set, minimum time bound
294
- * in seconds after `deltaPosition` has been applied.
295
- * @param {number|undefined} maximumPosition - If set, minimum time bound
296
- * in seconds after `deltaPosition` has been applied.
297
- */
298
- const reloadMediaSource = (deltaPosition, minimumPosition, maximumPosition) => {
299
- var _a;
300
- const reloadingContentInfo = this._currentContentInfo;
301
- if (reloadingContentInfo === null) {
302
- log.warn("Init", "Asked to reload when no content is loaded.");
303
- return;
304
- }
305
- const lastObservation = playbackObserver.getReference().getValue();
306
- const currentPosition = lastObservation.position.getWanted();
307
- const isPaused = ((_a = reloadingContentInfo.initialPlayPerformed) === null || _a === void 0 ? void 0 : _a.getValue()) === true ||
308
- reloadingContentInfo.autoPlay === undefined
309
- ? lastObservation.paused
310
- : !reloadingContentInfo.autoPlay;
311
- let position = currentPosition + deltaPosition;
312
- if (minimumPosition !== undefined) {
313
- position = Math.max(minimumPosition, position);
314
- }
315
- if (maximumPosition !== undefined) {
316
- position = Math.min(maximumPosition, position);
317
- }
318
- this._reload(mediaElement, textDisplayer, playbackObserver, mediaSourceStatus, position, !isPaused);
319
- };
320
- const onmessage = (msgData) => {
321
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28;
322
- switch (msgData.type) {
323
- case "attach-media-source" /* CoreMessageType.AttachMediaSource */: {
324
- if (((_a = this._currentContentInfo) === null || _a === void 0 ? void 0 : _a.contentId) !== msgData.contentId) {
325
- return;
326
- }
327
- if (this._currentContentInfo !== null) {
328
- if (((_b = this._currentContentInfo.mediaSourceInfo) === null || _b === void 0 ? void 0 : _b.type) === "main") {
329
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
330
- }
331
- this._currentContentInfo.mediaSourceInfo = {
332
- type: "core",
333
- mediaSourceId: msgData.mediaSourceId,
334
- };
335
- }
336
- const mediaSourceLink = msgData.value;
337
- mediaSourceStatus.onUpdate((currStatus, stopListening) => {
338
- if (currStatus === 1 /* MediaSourceInitializationStatus.AttachNow */) {
339
- stopListening();
340
- log.info("media", "Attaching MediaSource URL to the media element");
341
- if (mediaSourceLink.type === "handle") {
342
- mediaElement.srcObject = mediaSourceLink.value;
343
- this._currentMediaSourceCanceller.signal.register(() => {
344
- mediaElement.srcObject = null;
345
- });
346
- }
347
- else {
348
- mediaElement.src = mediaSourceLink.value;
349
- this._currentMediaSourceCanceller.signal.register(() => {
350
- resetMediaElement(mediaElement, mediaSourceLink.value);
351
- });
352
- }
353
- disableRemotePlaybackOnManagedMediaSource(mediaElement, this._currentMediaSourceCanceller.signal);
354
- mediaSourceStatus.setValue(2 /* MediaSourceInitializationStatus.Attached */);
355
- }
356
- }, { emitCurrentValue: true, clearSignal: this._initCanceller.signal });
357
- break;
358
- }
359
- case "warning" /* CoreMessageType.Warning */:
360
- if (((_c = this._currentContentInfo) === null || _c === void 0 ? void 0 : _c.contentId) !== msgData.contentId) {
361
- return;
362
- }
363
- this.trigger("warning", formatCoreError(msgData.value));
364
- break;
365
- case "error" /* CoreMessageType.Error */:
366
- if (((_d = this._currentContentInfo) === null || _d === void 0 ? void 0 : _d.contentId) !== msgData.contentId) {
367
- return;
368
- }
369
- this._onFatalError(formatCoreError(msgData.value));
370
- break;
371
- case "create-media-source" /* CoreMessageType.CreateMediaSource */:
372
- this._onCreateMediaSourceMessage(msgData, mediaElement, mediaSourceStatus, this._settings.coreInterface);
373
- break;
374
- case "add-source-buffer" /* CoreMessageType.AddSourceBuffer */:
375
- {
376
- if (((_f = (_e = this._currentContentInfo) === null || _e === void 0 ? void 0 : _e.mediaSourceInfo) === null || _f === void 0 ? void 0 : _f.type) !== "main" ||
377
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
378
- msgData.mediaSourceId) {
379
- return;
380
- }
381
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
382
- mediaSource.addSourceBuffer(msgData.value.sourceBufferType, msgData.value.codec);
383
- }
384
- break;
385
- case "source-buffer-append" /* CoreMessageType.SourceBufferAppend */:
386
- {
387
- if (((_h = (_g = this._currentContentInfo) === null || _g === void 0 ? void 0 : _g.mediaSourceInfo) === null || _h === void 0 ? void 0 : _h.type) !== "main" ||
388
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
389
- msgData.mediaSourceId) {
390
- return;
391
- }
392
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
393
- const sourceBuffer = arrayFind(mediaSource.sourceBuffers, (s) => s.type === msgData.sourceBufferType);
394
- if (sourceBuffer === undefined) {
395
- return;
396
- }
397
- sourceBuffer
398
- .appendBuffer(msgData.value.data, msgData.value.params)
399
- .then((buffered) => {
400
- this._settings.coreInterface.sendMessage({
401
- type: "sb-success" /* MainThreadMessageType.SourceBufferSuccess */,
402
- mediaSourceId: mediaSource.id,
403
- sourceBufferType: sourceBuffer.type,
404
- operationId: msgData.operationId,
405
- value: { buffered },
406
- });
407
- })
408
- .catch((error) => {
409
- this._settings.coreInterface.sendMessage({
410
- type: "sb-error" /* MainThreadMessageType.SourceBufferError */,
411
- mediaSourceId: mediaSource.id,
412
- sourceBufferType: sourceBuffer.type,
413
- operationId: msgData.operationId,
414
- value: error instanceof CancellationError
415
- ? { errorName: "CancellationError" }
416
- : formatSourceBufferError(error).serialize(),
417
- });
418
- });
419
- }
420
- break;
421
- case "source-buffer-remove" /* CoreMessageType.SourceBufferRemove */:
422
- {
423
- if (((_k = (_j = this._currentContentInfo) === null || _j === void 0 ? void 0 : _j.mediaSourceInfo) === null || _k === void 0 ? void 0 : _k.type) !== "main" ||
424
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
425
- msgData.mediaSourceId) {
426
- return;
427
- }
428
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
429
- const sourceBuffer = arrayFind(mediaSource.sourceBuffers, (s) => s.type === msgData.sourceBufferType);
430
- if (sourceBuffer === undefined) {
431
- return;
432
- }
433
- sourceBuffer
434
- .remove(msgData.value.start, msgData.value.end)
435
- .then((buffered) => {
436
- this._settings.coreInterface.sendMessage({
437
- type: "sb-success" /* MainThreadMessageType.SourceBufferSuccess */,
438
- mediaSourceId: mediaSource.id,
439
- sourceBufferType: sourceBuffer.type,
440
- operationId: msgData.operationId,
441
- value: { buffered },
442
- });
443
- })
444
- .catch((error) => {
445
- this._settings.coreInterface.sendMessage({
446
- type: "sb-error" /* MainThreadMessageType.SourceBufferError */,
447
- mediaSourceId: mediaSource.id,
448
- sourceBufferType: sourceBuffer.type,
449
- operationId: msgData.operationId,
450
- value: error instanceof CancellationError
451
- ? { errorName: "CancellationError" }
452
- : formatSourceBufferError(error).serialize(),
453
- });
454
- });
455
- }
456
- break;
457
- case "abort-source-buffer" /* CoreMessageType.AbortSourceBuffer */:
458
- {
459
- if (((_m = (_l = this._currentContentInfo) === null || _l === void 0 ? void 0 : _l.mediaSourceInfo) === null || _m === void 0 ? void 0 : _m.type) !== "main" ||
460
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
461
- msgData.mediaSourceId) {
462
- return;
463
- }
464
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
465
- const sourceBuffer = arrayFind(mediaSource.sourceBuffers, (s) => s.type === msgData.sourceBufferType);
466
- if (sourceBuffer === undefined) {
467
- return;
468
- }
469
- sourceBuffer.abort();
470
- }
471
- break;
472
- case "update-media-source-duration" /* CoreMessageType.UpdateMediaSourceDuration */:
473
- {
474
- if (((_p = (_o = this._currentContentInfo) === null || _o === void 0 ? void 0 : _o.mediaSourceInfo) === null || _p === void 0 ? void 0 : _p.type) !== "main" ||
475
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
476
- msgData.mediaSourceId) {
477
- return;
478
- }
479
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
480
- if ((mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.id) !== msgData.mediaSourceId) {
481
- return;
482
- }
483
- mediaSource.setDuration(msgData.value.duration, msgData.value.isRealEndKnown);
484
- }
485
- break;
486
- case "stop-media-source-duration" /* CoreMessageType.InterruptMediaSourceDurationUpdate */:
487
- {
488
- if (((_r = (_q = this._currentContentInfo) === null || _q === void 0 ? void 0 : _q.mediaSourceInfo) === null || _r === void 0 ? void 0 : _r.type) !== "main" ||
489
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
490
- msgData.mediaSourceId) {
491
- return;
492
- }
493
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
494
- if ((mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.id) !== msgData.mediaSourceId) {
495
- return;
496
- }
497
- mediaSource.interruptDurationSetting();
498
- }
499
- break;
500
- case "end-of-stream" /* CoreMessageType.EndOfStream */:
501
- {
502
- if (((_t = (_s = this._currentContentInfo) === null || _s === void 0 ? void 0 : _s.mediaSourceInfo) === null || _t === void 0 ? void 0 : _t.type) !== "main" ||
503
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
504
- msgData.mediaSourceId) {
505
- return;
506
- }
507
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
508
- mediaSource.maintainEndOfStream();
509
- }
510
- break;
511
- case "stop-end-of-stream" /* CoreMessageType.InterruptEndOfStream */:
512
- {
513
- if (((_v = (_u = this._currentContentInfo) === null || _u === void 0 ? void 0 : _u.mediaSourceInfo) === null || _v === void 0 ? void 0 : _v.type) !== "main" ||
514
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
515
- msgData.mediaSourceId) {
516
- return;
517
- }
518
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
519
- mediaSource.stopEndOfStream();
520
- }
521
- break;
522
- case "dispose-media-source" /* CoreMessageType.DisposeMediaSource */:
523
- {
524
- if (((_x = (_w = this._currentContentInfo) === null || _w === void 0 ? void 0 : _w.mediaSourceInfo) === null || _x === void 0 ? void 0 : _x.type) !== "main" ||
525
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
526
- msgData.mediaSourceId) {
527
- return;
528
- }
529
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
530
- mediaSource.dispose();
531
- }
532
- break;
533
- case "needs-buffer-flush" /* CoreMessageType.NeedsBufferFlush */: {
534
- if (((_y = this._currentContentInfo) === null || _y === void 0 ? void 0 : _y.contentId) !== msgData.contentId) {
535
- return;
536
- }
537
- const lastObservation = playbackObserver.getReference().getValue();
538
- const currentTime = lastObservation.position.isAwaitingFuturePosition()
539
- ? lastObservation.position.getWanted()
540
- : mediaElement.currentTime;
541
- const relativeResumingPosition = (_0 = (_z = msgData.value) === null || _z === void 0 ? void 0 : _z.relativeResumingPosition) !== null && _0 !== void 0 ? _0 : 0;
542
- const canBeApproximateSeek = Boolean((_1 = msgData.value) === null || _1 === void 0 ? void 0 : _1.relativePosHasBeenDefaulted);
543
- let wantedSeekingTime;
544
- if (relativeResumingPosition === 0 && canBeApproximateSeek) {
545
- // in case relativeResumingPosition is 0, we still perform
546
- // a tiny seek to be sure that the browser will correclty reload the video.
547
- wantedSeekingTime = currentTime + 0.001;
548
- }
549
- else {
550
- wantedSeekingTime = currentTime + relativeResumingPosition;
551
- }
552
- playbackObserver.setCurrentTime(wantedSeekingTime);
553
- break;
554
- }
555
- case "active-period-changed" /* CoreMessageType.ActivePeriodChanged */: {
556
- if (((_2 = this._currentContentInfo) === null || _2 === void 0 ? void 0 : _2.contentId) !== msgData.contentId ||
557
- this._currentContentInfo.manifest === null) {
558
- return;
559
- }
560
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
561
- if (period !== undefined) {
562
- this.trigger("activePeriodChanged", { period });
563
- }
564
- break;
565
- }
566
- case "adaptation-changed" /* CoreMessageType.AdaptationChanged */: {
567
- if (((_3 = this._currentContentInfo) === null || _3 === void 0 ? void 0 : _3.contentId) !== msgData.contentId ||
568
- this._currentContentInfo.manifest === null) {
569
- return;
570
- }
571
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
572
- if (period === undefined) {
573
- return;
574
- }
575
- if (msgData.value.adaptationId === null) {
576
- this.trigger("adaptationChange", {
577
- period,
578
- adaptation: null,
579
- type: msgData.value.type,
580
- });
581
- return;
582
- }
583
- const adaptations = (_4 = period.adaptations[msgData.value.type]) !== null && _4 !== void 0 ? _4 : [];
584
- const adaptation = arrayFind(adaptations, (a) => a.id === msgData.value.adaptationId);
585
- if (adaptation !== undefined) {
586
- this.trigger("adaptationChange", {
587
- period,
588
- adaptation,
589
- type: msgData.value.type,
590
- });
591
- }
592
- break;
593
- }
594
- case "representation-changed" /* CoreMessageType.RepresentationChanged */: {
595
- if (((_5 = this._currentContentInfo) === null || _5 === void 0 ? void 0 : _5.contentId) !== msgData.contentId ||
596
- this._currentContentInfo.manifest === null) {
597
- return;
598
- }
599
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
600
- if (period === undefined) {
601
- return;
602
- }
603
- if (msgData.value.representationId === null) {
604
- this.trigger("representationChange", {
605
- period,
606
- type: msgData.value.type,
607
- representation: null,
608
- });
609
- return;
610
- }
611
- const adaptations = (_6 = period.adaptations[msgData.value.type]) !== null && _6 !== void 0 ? _6 : [];
612
- const adaptation = arrayFind(adaptations, (a) => a.id === msgData.value.adaptationId);
613
- if (adaptation === undefined) {
614
- return;
615
- }
616
- const representation = arrayFind(adaptation.representations, (r) => r.id === msgData.value.representationId);
617
- if (representation !== undefined) {
618
- this.trigger("representationChange", {
619
- period,
620
- type: msgData.value.type,
621
- representation,
622
- });
623
- }
624
- break;
625
- }
626
- case "encryption-data-encountered" /* CoreMessageType.EncryptionDataEncountered */:
627
- if (((_7 = this._currentContentInfo) === null || _7 === void 0 ? void 0 : _7.contentId) !== msgData.contentId) {
628
- return;
629
- }
630
- lastContentProtection.setValue(msgData.value);
631
- break;
632
- case "manifest-ready" /* CoreMessageType.ManifestReady */: {
633
- if (((_8 = this._currentContentInfo) === null || _8 === void 0 ? void 0 : _8.contentId) !== msgData.contentId) {
634
- return;
635
- }
636
- const manifest = msgData.value.manifest;
637
- this._currentContentInfo.manifest = manifest;
638
- this._updateCodecSupport(manifest, mediaElement);
639
- this._startPlaybackIfReady(playbackStartParams);
640
- break;
641
- }
642
- case "manifest-update" /* CoreMessageType.ManifestUpdate */: {
643
- if (((_9 = this._currentContentInfo) === null || _9 === void 0 ? void 0 : _9.contentId) !== msgData.contentId) {
644
- return;
645
- }
646
- const manifest = (_10 = this._currentContentInfo) === null || _10 === void 0 ? void 0 : _10.manifest;
647
- if (isNullOrUndefined(manifest)) {
648
- log.error("Init", "Manifest update but no Manifest loaded");
649
- return;
650
- }
651
- replicateUpdatesOnManifestMetadata(manifest, msgData.value.manifest, msgData.value.updates);
652
- (_12 = (_11 = this._currentContentInfo) === null || _11 === void 0 ? void 0 : _11.streamEventsEmitter) === null || _12 === void 0 ? void 0 : _12.onManifestUpdate(manifest);
653
- this._updateCodecSupport(manifest, mediaElement);
654
- this.trigger("manifestUpdate", msgData.value.updates);
655
- break;
656
- }
657
- case "update-playback-rate" /* CoreMessageType.UpdatePlaybackRate */:
658
- if (((_13 = this._currentContentInfo) === null || _13 === void 0 ? void 0 : _13.contentId) !== msgData.contentId) {
659
- return;
660
- }
661
- playbackObserver.setPlaybackRate(msgData.value);
662
- break;
663
- case "bitrate-estimate-change" /* CoreMessageType.BitrateEstimateChange */:
664
- if (((_14 = this._currentContentInfo) === null || _14 === void 0 ? void 0 : _14.contentId) !== msgData.contentId) {
665
- return;
666
- }
667
- this.trigger("bitrateEstimateChange", {
668
- type: msgData.value.bufferType,
669
- bitrate: msgData.value.bitrate,
670
- });
671
- break;
672
- case "inband-event" /* CoreMessageType.InbandEvent */:
673
- if (((_15 = this._currentContentInfo) === null || _15 === void 0 ? void 0 : _15.contentId) !== msgData.contentId) {
674
- return;
675
- }
676
- this.trigger("inbandEvents", msgData.value);
677
- break;
678
- case "locked-stream" /* CoreMessageType.LockedStream */: {
679
- if (((_16 = this._currentContentInfo) === null || _16 === void 0 ? void 0 : _16.contentId) !== msgData.contentId ||
680
- this._currentContentInfo.manifest === null) {
681
- return;
682
- }
683
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
684
- if (period === undefined) {
685
- return;
686
- }
687
- (_17 = this._currentContentInfo.rebufferingController) === null || _17 === void 0 ? void 0 : _17.onLockedStream(msgData.value.bufferType, period);
688
- break;
689
- }
690
- case "period-stream-ready" /* CoreMessageType.PeriodStreamReady */: {
691
- if (((_18 = this._currentContentInfo) === null || _18 === void 0 ? void 0 : _18.contentId) !== msgData.contentId ||
692
- this._currentContentInfo.manifest === null) {
693
- return;
694
- }
695
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
696
- if (period === undefined) {
697
- return;
698
- }
699
- const ref = new SharedReference(undefined);
700
- ref.onUpdate((adapChoice) => {
701
- if (this._currentContentInfo === null) {
702
- ref.finish();
703
- return;
704
- }
705
- if (!isNullOrUndefined(adapChoice)) {
706
- adapChoice.representations.onUpdate((repChoice, stopListening) => {
707
- if (this._currentContentInfo === null) {
708
- stopListening();
709
- return;
710
- }
711
- this._settings.coreInterface.sendMessage({
712
- type: "rep-update" /* MainThreadMessageType.RepresentationUpdate */,
713
- contentId: this._currentContentInfo.contentId,
714
- value: {
715
- periodId: msgData.value.periodId,
716
- adaptationId: adapChoice.adaptationId,
717
- bufferType: msgData.value.bufferType,
718
- choice: repChoice,
719
- },
720
- });
721
- }, { clearSignal: this._initCanceller.signal });
722
- }
723
- this._settings.coreInterface.sendMessage({
724
- type: "track-update" /* MainThreadMessageType.TrackUpdate */,
725
- contentId: this._currentContentInfo.contentId,
726
- value: {
727
- periodId: msgData.value.periodId,
728
- bufferType: msgData.value.bufferType,
729
- choice: isNullOrUndefined(adapChoice)
730
- ? adapChoice
731
- : {
732
- adaptationId: adapChoice.adaptationId,
733
- switchingMode: adapChoice.switchingMode,
734
- initialRepresentations: adapChoice.representations.getValue(),
735
- relativeResumingPosition: adapChoice.relativeResumingPosition,
736
- },
737
- },
738
- });
739
- }, { clearSignal: this._initCanceller.signal });
740
- this.trigger("periodStreamReady", {
741
- period,
742
- type: msgData.value.bufferType,
743
- adaptationRef: ref,
744
- });
745
- break;
746
- }
747
- case "period-stream-cleared" /* CoreMessageType.PeriodStreamCleared */: {
748
- if (((_19 = this._currentContentInfo) === null || _19 === void 0 ? void 0 : _19.contentId) !== msgData.contentId ||
749
- this._currentContentInfo.manifest === null) {
750
- return;
751
- }
752
- this.trigger("periodStreamCleared", {
753
- periodId: msgData.value.periodId,
754
- type: msgData.value.bufferType,
755
- });
756
- break;
757
- }
758
- case "discontinuity-update" /* CoreMessageType.DiscontinuityUpdate */: {
759
- if (((_20 = this._currentContentInfo) === null || _20 === void 0 ? void 0 : _20.contentId) !== msgData.contentId ||
760
- this._currentContentInfo.manifest === null) {
761
- return;
762
- }
763
- const period = arrayFind(this._currentContentInfo.manifest.periods, (p) => p.id === msgData.value.periodId);
764
- if (period === undefined) {
765
- log.warn("Init", "Discontinuity's Period not found", {
766
- periodId: msgData.value.periodId,
767
- });
768
- return;
769
- }
770
- (_21 = this._currentContentInfo.rebufferingController) === null || _21 === void 0 ? void 0 : _21.updateDiscontinuityInfo({
771
- period,
772
- bufferType: msgData.value.bufferType,
773
- discontinuity: msgData.value.discontinuity,
774
- position: msgData.value.position,
775
- });
776
- break;
777
- }
778
- case "push-text-data" /* CoreMessageType.PushTextData */: {
779
- if (((_22 = this._currentContentInfo) === null || _22 === void 0 ? void 0 : _22.contentId) !== msgData.contentId) {
780
- return;
781
- }
782
- if (textDisplayer === null) {
783
- log.warn("text", "Received AddTextData message but no text displayer exists");
784
- }
785
- else {
786
- try {
787
- const ranges = textDisplayer.pushTextData(msgData.value);
788
- this._settings.coreInterface.sendMessage({
789
- type: "add-text-success" /* MainThreadMessageType.PushTextDataSuccess */,
790
- contentId: msgData.contentId,
791
- value: { ranges },
792
- });
793
- }
794
- catch (err) {
795
- const message = err instanceof Error ? err.message : "Unknown error";
796
- this._settings.coreInterface.sendMessage({
797
- type: "push-text-error" /* MainThreadMessageType.PushTextDataError */,
798
- contentId: msgData.contentId,
799
- value: { message },
800
- });
801
- }
802
- }
803
- break;
804
- }
805
- case "remove-text-data" /* CoreMessageType.RemoveTextData */: {
806
- if (((_23 = this._currentContentInfo) === null || _23 === void 0 ? void 0 : _23.contentId) !== msgData.contentId) {
807
- return;
808
- }
809
- if (textDisplayer === null) {
810
- log.warn("text", "Received RemoveTextData message but no text displayer exists");
811
- }
812
- else {
813
- try {
814
- const ranges = textDisplayer.removeBuffer(msgData.value.start, msgData.value.end);
815
- this._settings.coreInterface.sendMessage({
816
- type: "remove-text-success" /* MainThreadMessageType.RemoveTextDataSuccess */,
817
- contentId: msgData.contentId,
818
- value: { ranges },
819
- });
820
- }
821
- catch (err) {
822
- const message = err instanceof Error ? err.message : "Unknown error";
823
- this._settings.coreInterface.sendMessage({
824
- type: "remove-text-error" /* MainThreadMessageType.RemoveTextDataError */,
825
- contentId: msgData.contentId,
826
- value: { message },
827
- });
828
- }
829
- }
830
- break;
831
- }
832
- case "reset-text-displayer" /* CoreMessageType.ResetTextDisplayer */: {
833
- if (((_24 = this._currentContentInfo) === null || _24 === void 0 ? void 0 : _24.contentId) !== msgData.contentId) {
834
- return;
835
- }
836
- if (textDisplayer === null) {
837
- log.warn("text", "Received ResetTextDisplayer message but no text displayer exists");
838
- }
839
- else {
840
- textDisplayer.reset();
841
- }
842
- break;
843
- }
844
- case "stop-text-displayer" /* CoreMessageType.StopTextDisplayer */: {
845
- if (((_25 = this._currentContentInfo) === null || _25 === void 0 ? void 0 : _25.contentId) !== msgData.contentId) {
846
- return;
847
- }
848
- if (textDisplayer === null) {
849
- log.warn("text", "Received StopTextDisplayer message but no text displayer exists");
850
- }
851
- else {
852
- textDisplayer.stop();
853
- }
854
- break;
855
- }
856
- case "reloading-media-source" /* CoreMessageType.ReloadingMediaSource */:
857
- {
858
- if (this._currentContentInfo === null ||
859
- this._currentContentInfo.mediaSourceInfo === null) {
860
- return;
861
- }
862
- const mediaSourceId = this._currentContentInfo.mediaSourceInfo.type === "main"
863
- ? this._currentContentInfo.mediaSourceInfo.mediaSource.id
864
- : this._currentContentInfo.mediaSourceInfo.mediaSourceId;
865
- if (mediaSourceId !== msgData.mediaSourceId) {
142
+ this._initCanceller.cancel();
143
+ this.trigger("error", err);
144
+ }
145
+ /**
146
+ * Initialize decryption mechanisms if needed and begin creating and relying
147
+ * on the initial `MediaSourceInterface` for this content.
148
+ * @param {HTMLMediaElement|null} mediaElement
149
+ * @returns {Promise.<Object>}
150
+ */
151
+ _setupInitialMediaSourceAndDecryption(mediaElement) {
152
+ const initCanceller = this._initCanceller;
153
+ return createCancellablePromise(initCanceller.signal, (resolve) => {
154
+ const { keySystems } = this._initSettings;
155
+ /** Initialize decryption capabilities. */
156
+ const { statusRef: drmInitRef, contentDecryptor } = initializeContentDecryption(mediaElement, keySystems, {
157
+ onWarning: (err) => this.trigger("warning", err),
158
+ onError: (err) => this._onFatalError(err),
159
+ onBlackListProtectionData: (val) => {
160
+ // Ugly IIFE workaround to allow async event listener
161
+ (async () => {
162
+ var _a;
163
+ if (this._manifest === null) {
866
164
  return;
867
165
  }
868
- reloadMediaSource(msgData.value.timeOffset, msgData.value.minimumPosition, msgData.value.maximumPosition);
869
- }
870
- break;
871
- case "needs-decipherability-flush" /* CoreMessageType.NeedsDecipherabilityFlush */:
872
- {
873
- if (((_26 = this._currentContentInfo) === null || _26 === void 0 ? void 0 : _26.contentId) !== msgData.contentId) {
166
+ const manifest = (_a = this._manifest.syncValue) !== null && _a !== void 0 ? _a : (await this._manifest.getValueAsAsync());
167
+ blackListProtectionDataOnManifest(manifest, val);
168
+ })().catch(noop);
169
+ },
170
+ onKeyIdsCompatibilityUpdate: (updates) => {
171
+ // Ugly IIFE workaround to allow async event listener
172
+ (async () => {
173
+ var _a;
174
+ if (this._manifest === null) {
874
175
  return;
875
176
  }
876
- const keySystem = getKeySystemConfiguration(mediaElement);
877
- if (shouldReloadMediaSourceOnDecipherabilityUpdate(keySystem === null || keySystem === void 0 ? void 0 : keySystem[0])) {
878
- notifyAndStartMediaSourceReload(0, undefined, undefined);
879
- }
880
- else {
881
- const lastObservation = playbackObserver.getReference().getValue();
882
- const currentPosition = lastObservation.position.getWanted();
883
- // simple seek close to the current position
884
- // to flush the buffers
885
- if (currentPosition + 0.001 < lastObservation.duration) {
886
- playbackObserver.setCurrentTime(mediaElement.currentTime + 0.001);
887
- }
888
- else {
889
- playbackObserver.setCurrentTime(currentPosition);
177
+ const manifest = (_a = this._manifest.syncValue) !== null && _a !== void 0 ? _a : (await this._manifest.getValueAsAsync());
178
+ updateKeyIdsDecipherabilityOnManifest(manifest, updates.whitelistedKeyIds, updates.blacklistedKeyIds, updates.delistedKeyIds);
179
+ })().catch(noop);
180
+ },
181
+ onCodecSupportUpdate: () => {
182
+ var _a, _b;
183
+ const syncManifest = (_a = this._manifest) === null || _a === void 0 ? void 0 : _a.syncValue;
184
+ if (isNullOrUndefined(syncManifest)) {
185
+ // The Manifest is not yet fetched, but we will be able to check
186
+ // the codecs once it is the case
187
+ (_b = this._manifest) === null || _b === void 0 ? void 0 : _b.getValueAsAsync().then((loadedManifest) => {
188
+ if (this._initCanceller.isUsed()) {
189
+ return;
890
190
  }
891
- }
892
- }
893
- break;
894
- case "segment-sink-store-update" /* CoreMessageType.SegmentSinkStoreUpdate */: {
895
- if (((_27 = this._currentContentInfo) === null || _27 === void 0 ? void 0 : _27.contentId) !== msgData.contentId) {
896
- return;
897
- }
898
- const sinkObj = this._awaitingRequests.pendingSinkMetrics.get(msgData.value.requestId);
899
- if (sinkObj !== undefined) {
900
- sinkObj.resolve(msgData.value.segmentSinkMetrics);
191
+ this._refreshManifestCodecSupport(loadedManifest, mediaElement);
192
+ }, noop);
901
193
  }
902
194
  else {
903
- log.error("Init", "Failed to send segment sink store update");
195
+ this._refreshManifestCodecSupport(syncManifest, mediaElement);
904
196
  }
905
- break;
906
- }
907
- case "init-success" /* CoreMessageType.InitSuccess */:
908
- case "init-error" /* CoreMessageType.InitError */:
909
- // Should already be handled by the API
910
- break;
911
- case "log" /* CoreMessageType.LogMessage */:
912
- // Already handled by prepare's handler
913
- break;
914
- case "thumbnail-response" /* CoreMessageType.ThumbnailDataResponse */: {
915
- if (((_28 = this._currentContentInfo) === null || _28 === void 0 ? void 0 : _28.contentId) !== msgData.contentId) {
916
- return;
917
- }
918
- const tObj = this._awaitingRequests.pendingThumbnailFetching.get(msgData.value.requestId);
919
- if (tObj !== undefined) {
920
- if (msgData.value.status === "error") {
921
- tObj.reject(formatCoreError(msgData.value.error));
922
- }
923
- else {
924
- tObj.resolve(msgData.value.data);
925
- }
926
- }
927
- else {
928
- log.error("Init", "Failed to send segment sink store update");
929
- }
930
- break;
931
- }
932
- default:
933
- assertUnreachable(msgData);
934
- }
935
- };
936
- log.debug("Init", "addEventListener for core message");
937
- if (this._queuedCoreMessages !== null) {
938
- const bufferedMessages = this._queuedCoreMessages.slice();
939
- log.debug("Init", "Processing buffered messages", {
940
- ammount: bufferedMessages.length,
941
- });
942
- for (const message of bufferedMessages) {
943
- onmessage(message);
944
- }
945
- this._queuedCoreMessages = null;
946
- }
947
- this._settings.coreInterface.addMessageListener(onmessage);
948
- this._initCanceller.signal.register(() => {
949
- log.debug("Init", "removeEventListener for core message");
950
- this._settings.coreInterface.removeMessageListener(onmessage);
951
- });
952
- }
953
- dispose() {
954
- var _a;
955
- this._initCanceller.cancel();
956
- if (this._currentContentInfo !== null) {
957
- if (((_a = this._currentContentInfo.mediaSourceInfo) === null || _a === void 0 ? void 0 : _a.type) === "main") {
958
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
959
- }
960
- this._currentContentInfo = null;
961
- }
962
- }
963
- _onFatalError(err) {
964
- if (this._initCanceller.isUsed()) {
965
- return;
966
- }
967
- this._initCanceller.cancel();
968
- this.trigger("error", err);
969
- }
970
- _initializeContentDecryption(mediaElement, lastContentProtection, mediaSourceStatus, reloadMediaSource, cancelSignal) {
971
- var _a;
972
- const { keySystems } = this._settings;
973
- // TODO private?
974
- const createEmeDisabledReference = (errMsg) => {
975
- mediaSourceStatus.setValue(1 /* MediaSourceInitializationStatus.AttachNow */);
976
- lastContentProtection.onUpdate((data, stopListening) => {
977
- if (data === null) {
978
- // initial value
979
- return;
980
- }
981
- stopListening();
982
- const err = new EncryptedMediaError("MEDIA_IS_ENCRYPTED_ERROR", errMsg, {
983
- keyStatuses: undefined,
984
- keySystemConfiguration: undefined,
985
- keySystem: undefined,
986
- });
987
- this._onFatalError(err);
988
- }, { clearSignal: cancelSignal });
989
- const ref = new SharedReference({
990
- initializationState: {
991
- type: "initialized",
992
- value: null,
993
197
  },
994
- contentDecryptor: null,
995
- drmSystemId: undefined,
996
- });
997
- ref.finish(); // We know that no new value will be triggered
998
- return { statusRef: ref, contentDecryptor: null };
999
- };
1000
- if (keySystems.length === 0) {
1001
- return createEmeDisabledReference("No `keySystems` option given.");
1002
- }
1003
- else if (features.decrypt === null) {
1004
- return createEmeDisabledReference("EME feature not activated.");
1005
- }
1006
- const emeApi = (_a = mediaElement.FORCED_EME_API) !== null && _a !== void 0 ? _a : getEmeApiImplementation("auto");
1007
- if (emeApi === null) {
1008
- return createEmeDisabledReference("EME API not available on the current page.");
1009
- }
1010
- log.debug("Init", "Creating ContentDecryptor");
1011
- const ContentDecryptor = features.decrypt;
1012
- const contentDecryptor = new ContentDecryptor(emeApi, mediaElement, keySystems);
1013
- const drmStatusRef = new SharedReference({
1014
- initializationState: { type: "uninitialized", value: null },
1015
- drmSystemId: undefined,
1016
- }, cancelSignal);
1017
- const updateCodecSupportOnStateChange = (state) => {
1018
- var _a;
1019
- if (state > ContentDecryptorState.Initializing) {
1020
- const manifest = (_a = this._currentContentInfo) === null || _a === void 0 ? void 0 : _a.manifest;
1021
- if (isNullOrUndefined(manifest)) {
1022
- return;
1023
- }
1024
- this._updateCodecSupport(manifest, mediaElement);
1025
- contentDecryptor.removeEventListener("stateChange", updateCodecSupportOnStateChange);
1026
- }
1027
- };
1028
- contentDecryptor.addEventListener("stateChange", updateCodecSupportOnStateChange);
1029
- contentDecryptor.addEventListener("keyIdsCompatibilityUpdate", (updates) => {
1030
- if (this._currentContentInfo === null ||
1031
- this._currentContentInfo.manifest === null) {
1032
- return;
1033
- }
1034
- const manUpdates = updateDecipherabilityFromKeyIds(this._currentContentInfo.manifest, updates);
1035
- if (mayMediaElementFailOnUndecipherableData() &&
1036
- manUpdates.some((e) => e.representation.decipherable !== true)) {
1037
- reloadMediaSource();
1038
- }
1039
- else {
1040
- this._settings.coreInterface.sendMessage({
1041
- type: "decipherability-update" /* MainThreadMessageType.DecipherabilityStatusUpdate */,
1042
- contentId: this._currentContentInfo.contentId,
1043
- value: manUpdates.map((s) => ({
1044
- representationUniqueId: s.representation.uniqueId,
1045
- decipherable: s.representation.decipherable,
1046
- })),
1047
- });
1048
- }
1049
- this.trigger("decipherabilityUpdate", manUpdates);
1050
- });
1051
- contentDecryptor.addEventListener("blackListProtectionData", (protData) => {
1052
- if (this._currentContentInfo === null ||
1053
- this._currentContentInfo.manifest === null) {
1054
- return;
1055
- }
1056
- const manUpdates = updateDecipherabilityFromProtectionData(this._currentContentInfo.manifest, protData);
1057
- if (mayMediaElementFailOnUndecipherableData() &&
1058
- manUpdates.some((e) => e.representation.decipherable !== true)) {
1059
- reloadMediaSource();
198
+ }, initCanceller.signal);
199
+ if (contentDecryptor.enabled) {
200
+ this._decryptionCapabilities = {
201
+ status: "enabled",
202
+ value: contentDecryptor.value,
203
+ };
1060
204
  }
1061
205
  else {
1062
- this._settings.coreInterface.sendMessage({
1063
- type: "decipherability-update" /* MainThreadMessageType.DecipherabilityStatusUpdate */,
1064
- contentId: this._currentContentInfo.contentId,
1065
- value: manUpdates.map((s) => ({
1066
- representationUniqueId: s.representation.uniqueId,
1067
- decipherable: s.representation.decipherable,
1068
- })),
1069
- });
206
+ this._decryptionCapabilities = {
207
+ status: "disabled",
208
+ value: contentDecryptor.value,
209
+ };
1070
210
  }
1071
- this.trigger("decipherabilityUpdate", manUpdates);
1072
- });
1073
- contentDecryptor.addEventListener("stateChange", (state) => {
1074
- if (state === ContentDecryptorState.WaitingForAttachment) {
1075
- mediaSourceStatus.onUpdate((currStatus, stopListening) => {
1076
- if (currStatus === 0 /* MediaSourceInitializationStatus.Nothing */) {
1077
- mediaSourceStatus.setValue(1 /* MediaSourceInitializationStatus.AttachNow */);
211
+ drmInitRef.onUpdate((drmStatus, stopListeningToDrmUpdates) => {
212
+ if (drmStatus.initializationState.type === "uninitialized") {
213
+ return;
214
+ }
215
+ stopListeningToDrmUpdates();
216
+ const mediaSourceCanceller = new TaskCanceller();
217
+ mediaSourceCanceller.linkToSignal(initCanceller.signal);
218
+ createMediaSource(mediaElement, mediaSourceCanceller.signal)
219
+ .then((mediaSource) => {
220
+ const lastDrmStatus = drmInitRef.getValue();
221
+ if (lastDrmStatus.initializationState.type === "awaiting-media-link") {
222
+ lastDrmStatus.initializationState.value.isMediaLinked.setValue(true);
223
+ drmInitRef.onUpdate((newDrmStatus, stopListeningToDrmUpdatesAgain) => {
224
+ if (newDrmStatus.initializationState.type === "initialized") {
225
+ stopListeningToDrmUpdatesAgain();
226
+ resolve({
227
+ mediaSource,
228
+ drmSystemId: newDrmStatus.drmSystemId,
229
+ unlinkMediaSource: mediaSourceCanceller,
230
+ });
231
+ return;
232
+ }
233
+ }, { emitCurrentValue: true, clearSignal: initCanceller.signal });
234
+ }
235
+ else if (drmStatus.initializationState.type === "initialized") {
236
+ resolve({
237
+ mediaSource,
238
+ drmSystemId: drmStatus.drmSystemId,
239
+ unlinkMediaSource: mediaSourceCanceller,
240
+ });
241
+ return;
1078
242
  }
1079
- else if (currStatus === 2 /* MediaSourceInitializationStatus.Attached */) {
1080
- stopListening();
1081
- if (state === ContentDecryptorState.WaitingForAttachment) {
1082
- contentDecryptor.attach();
1083
- }
243
+ })
244
+ .catch((err) => {
245
+ if (mediaSourceCanceller.isUsed()) {
246
+ return;
1084
247
  }
1085
- }, { clearSignal: cancelSignal, emitCurrentValue: true });
1086
- }
1087
- else if (state === ContentDecryptorState.ReadyForContent) {
1088
- drmStatusRef.setValue({
1089
- initializationState: { type: "initialized", value: null },
1090
- drmSystemId: contentDecryptor.systemId,
248
+ this._onFatalError(err);
1091
249
  });
1092
- contentDecryptor.removeEventListener("stateChange");
1093
- }
1094
- });
1095
- contentDecryptor.addEventListener("error", (error) => {
1096
- this._onFatalError(error);
1097
- });
1098
- contentDecryptor.addEventListener("warning", (error) => {
1099
- this.trigger("warning", error);
250
+ }, { emitCurrentValue: true, clearSignal: initCanceller.signal });
1100
251
  });
1101
- lastContentProtection.onUpdate((data) => {
1102
- if (data === null) {
1103
- return;
1104
- }
1105
- contentDecryptor.onInitializationData(data);
1106
- }, { clearSignal: cancelSignal });
1107
- cancelSignal.register(() => {
1108
- contentDecryptor.dispose();
1109
- });
1110
- return { statusRef: drmStatusRef, contentDecryptor };
1111
252
  }
1112
- /**
1113
- * Retrieves all unknown codecs from the current manifest, checks these unknown codecs
1114
- * to determine if they are supported, updates the manifest with the support
1115
- * status of these codecs, and forwards the list of supported codecs to core.
1116
- * @param manifest
1117
- */
1118
- _updateCodecSupport(manifest, mediaElement) {
1119
- var _a, _b, _c, _d;
253
+ async _onInitialMediaSourceReady(mediaElement, initialMediaSource, playbackObserver, drmSystemId, initialMediaSourceCanceller) {
254
+ var _a;
255
+ const { adaptiveOptions, autoPlay, bufferOptions, lowLatencyMode, segmentRequestOptions, speed, startAt, textTrackOptions, transport, } = this._initSettings;
256
+ const initCanceller = this._initCanceller;
257
+ assert(this._manifest !== null);
258
+ let manifest;
1120
259
  try {
1121
- const updatedCodecs = updateManifestCodecSupport(manifest, (_b = (_a = this._currentContentInfo) === null || _a === void 0 ? void 0 : _a.contentDecryptor) !== null && _b !== void 0 ? _b : null, mediaElement, (_d = (_c = this._currentContentInfo) === null || _c === void 0 ? void 0 : _c.useMseInWorker) !== null && _d !== void 0 ? _d : false);
1122
- if (updatedCodecs.length > 0) {
1123
- this._settings.coreInterface.sendMessage({
1124
- type: "codec-support-update" /* MainThreadMessageType.CodecSupportUpdate */,
1125
- value: updatedCodecs,
1126
- });
1127
- // TODO what if one day the core updates codec support by itself?
1128
- // We wouldn't know...
1129
- this.trigger("codecSupportUpdate", null);
1130
- }
260
+ manifest = (_a = this._manifest.syncValue) !== null && _a !== void 0 ? _a : (await this._manifest.getValueAsAsync());
1131
261
  }
1132
- catch (err) {
1133
- this._onFatalError(err);
262
+ catch (_e) {
263
+ return; // The error should already have been processed through an event listener
264
+ }
265
+ manifest.addEventListener("manifestUpdate", (updates) => {
266
+ this.trigger("manifestUpdate", updates);
267
+ this._refreshManifestCodecSupport(manifest, mediaElement);
268
+ }, initCanceller.signal);
269
+ manifest.addEventListener("decipherabilityUpdate", (elts) => {
270
+ this.trigger("decipherabilityUpdate", elts);
271
+ }, initCanceller.signal);
272
+ manifest.addEventListener("supportUpdate", () => {
273
+ this.trigger("codecSupportUpdate", null);
274
+ }, initCanceller.signal);
275
+ log.debug("Init", "Calculating initial time");
276
+ const initialTime = getInitialTime(manifest, lowLatencyMode, startAt);
277
+ log.debug("Init", "Initial time calculated", { initialTime });
278
+ /** Choose the right "Representation" for a given "Adaptation". */
279
+ const representationEstimator = AdaptiveRepresentationSelector(adaptiveOptions);
280
+ const subBufferOptions = objectAssign({ textTrackOptions, drmSystemId }, bufferOptions);
281
+ const cdnPrioritizer = new CdnPrioritizer(initCanceller.signal);
282
+ const segmentQueueCreator = new SegmentQueueCreator(transport, cdnPrioritizer, this._cmcdDataBuilder, segmentRequestOptions);
283
+ this._refreshManifestCodecSupport(manifest, mediaElement);
284
+ this.trigger("manifestReady", manifest);
285
+ if (initCanceller.isUsed()) {
286
+ return;
1134
287
  }
288
+ // handle initial load and reloads
289
+ this._setupContentWithNewMediaSource({
290
+ mediaElement,
291
+ playbackObserver,
292
+ mediaSource: initialMediaSource,
293
+ initialTime,
294
+ autoPlay,
295
+ manifest,
296
+ representationEstimator,
297
+ cdnPrioritizer,
298
+ segmentQueueCreator,
299
+ speed,
300
+ bufferOptions: subBufferOptions,
301
+ }, initialMediaSourceCanceller);
1135
302
  }
1136
- _hasTextBufferFeature() {
1137
- return ((this._settings.textTrackOptions.textTrackMode === "html" &&
1138
- features.htmlTextDisplayer !== null) ||
1139
- features.nativeTextDisplayer !== null);
303
+ /**
304
+ * Load the content defined by the Manifest in the mediaSource given at the
305
+ * given position and playing status.
306
+ * This function recursively re-call itself when a MediaSource reload is
307
+ * wanted.
308
+ * @param {Object} args
309
+ * @param {Object} currentCanceller
310
+ */
311
+ _setupContentWithNewMediaSource(args, currentCanceller) {
312
+ this._startLoadingContentOnMediaSource(args, this._createReloadMediaSourceCallback(args, currentCanceller), currentCanceller.signal);
1140
313
  }
1141
- _reload(mediaElement, textDisplayer, playbackObserver, mediaSourceStatus, position, autoPlay) {
1142
- this._currentMediaSourceCanceller.cancel();
1143
- this._currentMediaSourceCanceller = new TaskCanceller();
1144
- this._currentMediaSourceCanceller.linkToSignal(this._initCanceller.signal);
1145
- mediaSourceStatus.setValue(1 /* MediaSourceInitializationStatus.AttachNow */);
1146
- this.trigger("reloadingMediaSource", { position, autoPlay });
1147
- mediaSourceStatus.onUpdate((status, stopListeningMSStatusUpdates) => {
1148
- if (status !== 2 /* MediaSourceInitializationStatus.Attached */) {
314
+ /**
315
+ * Create `IReloadMediaSourceCallback` allowing to handle reload orders.
316
+ * @param {Object} args
317
+ * @param {Object} currentCanceller
318
+ */
319
+ _createReloadMediaSourceCallback(args, currentCanceller) {
320
+ const initCanceller = this._initCanceller;
321
+ return (reloadOrder) => {
322
+ currentCanceller.cancel();
323
+ if (initCanceller.isUsed()) {
1149
324
  return;
1150
325
  }
1151
- stopListeningMSStatusUpdates();
1152
- const corePlaybackObserver = this._setUpModulesOnNewMediaSource({
1153
- initialTime: position,
1154
- autoPlay,
1155
- mediaElement,
1156
- textDisplayer,
1157
- playbackObserver,
1158
- }, this._currentMediaSourceCanceller.signal);
1159
- if (!this._currentMediaSourceCanceller.isUsed() &&
1160
- corePlaybackObserver !== null &&
1161
- this._currentContentInfo !== null) {
1162
- const contentId = this._currentContentInfo.contentId;
1163
- corePlaybackObserver.listen((obs) => {
1164
- this._settings.coreInterface.sendMessage({
1165
- type: "observation" /* MainThreadMessageType.PlaybackObservation */,
1166
- contentId,
1167
- value: objectAssign(obs, {
1168
- position: obs.position.serialize(),
1169
- }),
1170
- });
1171
- }, {
1172
- includeLastObservation: true,
1173
- clearSignal: this._currentMediaSourceCanceller.signal,
1174
- });
326
+ this.trigger("reloadingMediaSource", reloadOrder);
327
+ if (initCanceller.isUsed()) {
328
+ return;
1175
329
  }
1176
- }, {
1177
- clearSignal: this._currentMediaSourceCanceller.signal,
1178
- emitCurrentValue: true,
1179
- });
330
+ const newCanceller = new TaskCanceller();
331
+ newCanceller.linkToSignal(initCanceller.signal);
332
+ createMediaSource(args.mediaElement, newCanceller.signal)
333
+ .then((newMediaSource) => {
334
+ this._setupContentWithNewMediaSource(Object.assign(Object.assign({}, args), { mediaSource: newMediaSource, initialTime: reloadOrder.position, autoPlay: reloadOrder.autoPlay }), newCanceller);
335
+ })
336
+ .catch((err) => {
337
+ if (newCanceller.isUsed()) {
338
+ return;
339
+ }
340
+ this._onFatalError(err);
341
+ });
342
+ };
1180
343
  }
1181
344
  /**
1182
- * Start-up modules and mechanisms (initial seek, auto-play etc.) needed each
1183
- * time a content is loaded AND re-loaded on a `HTMLMediaElement`, when the
1184
- * manifest is known.
1185
- *
1186
- * Note that this does not include reacting to incoming core messages nor
1187
- * sending them, those actions have to be handled separately.
1188
- *
1189
- * @param {Object} parameters
345
+ * Buffer the content on the given MediaSource.
346
+ * @param {Object} args
347
+ * @param {function} onReloadOrder
1190
348
  * @param {Object} cancelSignal
1191
- * @returns {Object|null} - Playback Observer created for this content. `null`
1192
- * only if playback initialization failed (most likely because it has been
1193
- * cancelled).
1194
349
  */
1195
- _setUpModulesOnNewMediaSource(parameters, cancelSignal) {
1196
- if (cancelSignal.isCancelled()) {
1197
- return null;
1198
- }
1199
- if (this._currentContentInfo === null) {
1200
- log.error("Init", "Setting up modules without a contentId");
1201
- return null;
350
+ _startLoadingContentOnMediaSource(args, onReloadOrder, cancelSignal) {
351
+ var _a, _b;
352
+ const { autoPlay, bufferOptions, initialTime, manifest, mediaElement, mediaSource, playbackObserver, representationEstimator, cdnPrioritizer, segmentQueueCreator, speed, } = args;
353
+ const { transport } = this._initSettings;
354
+ const initialPeriod = (_a = manifest.getPeriodForTime(initialTime)) !== null && _a !== void 0 ? _a : manifest.getNextPeriod(initialTime);
355
+ if (initialPeriod === undefined) {
356
+ const error = new MediaError("MEDIA_STARTING_TIME_NOT_FOUND", "Wanted starting time not found in the Manifest.");
357
+ return this._onFatalError(error);
1202
358
  }
1203
- if (this._currentContentInfo.manifest === null) {
1204
- log.error("Init", "Setting up modules without a loaded Manifest");
1205
- return null;
359
+ let textDisplayerInterface = null;
360
+ const textDisplayer = createTextDisplayer(mediaElement, this._initSettings.textTrackOptions);
361
+ if (textDisplayer !== null) {
362
+ const sender = new MainThreadTextDisplayerInterface(textDisplayer);
363
+ textDisplayerInterface = sender;
364
+ cancelSignal.register(() => {
365
+ sender.stop();
366
+ textDisplayer === null || textDisplayer === void 0 ? void 0 : textDisplayer.stop();
367
+ });
1206
368
  }
1207
- const { manifest, mediaSourceInfo } = this._currentContentInfo;
1208
- const { speed } = this._settings;
1209
- const { initialTime, autoPlay, mediaElement, textDisplayer, playbackObserver } = parameters;
1210
- this._currentContentInfo.initialTime = initialTime;
1211
- this._currentContentInfo.autoPlay = autoPlay;
369
+ /** Interface to create media buffers. */
370
+ const segmentSinksStore = new SegmentSinksStore(mediaSource, mediaElement.nodeName === "VIDEO", textDisplayerInterface);
371
+ cancelSignal.register(() => {
372
+ segmentSinksStore.disposeAll();
373
+ });
1212
374
  const { autoPlayResult, initialPlayPerformed } = performInitialSeekAndPlay({
1213
375
  mediaElement,
1214
376
  playbackObserver,
1215
377
  startTime: initialTime,
1216
378
  mustAutoPlay: autoPlay,
1217
- onWarning: (err) => this.trigger("warning", err),
379
+ onWarning: (err) => {
380
+ this.trigger("warning", err);
381
+ },
1218
382
  isDirectfile: false,
1219
383
  }, cancelSignal);
1220
- this._currentContentInfo.initialPlayPerformed = initialPlayPerformed;
1221
- const corePlaybackObserver = createCorePlaybackObserver(playbackObserver, {
1222
- autoPlay,
1223
- initialPlayPerformed,
1224
- manifest,
1225
- mediaSource: (mediaSourceInfo === null || mediaSourceInfo === void 0 ? void 0 : mediaSourceInfo.type) === "main" ? mediaSourceInfo.mediaSource : null,
1226
- speed,
1227
- textDisplayer,
1228
- }, cancelSignal);
1229
384
  if (cancelSignal.isCancelled()) {
1230
- return null;
385
+ return;
1231
386
  }
1232
- /**
1233
- * Class trying to avoid various stalling situations, emitting "stalled"
1234
- * events when it cannot, as well as "unstalled" events when it get out of one.
1235
- */
1236
- const rebufferingController = new RebufferingController(playbackObserver, manifest, speed);
1237
- rebufferingController.addEventListener("stalled", (evt) => this.trigger("stalled", evt));
1238
- rebufferingController.addEventListener("unstalled", () => this.trigger("unstalled", null));
1239
- rebufferingController.addEventListener("warning", (err) => this.trigger("warning", err));
1240
- cancelSignal.register(() => {
1241
- rebufferingController.destroy();
1242
- });
1243
- rebufferingController.start();
1244
- this._currentContentInfo.rebufferingController = rebufferingController;
1245
- const currentContentInfo = this._currentContentInfo;
1246
387
  initialPlayPerformed.onUpdate((isPerformed, stopListening) => {
1247
388
  if (isPerformed) {
1248
389
  stopListening();
1249
390
  const streamEventsEmitter = new StreamEventsEmitter(manifest, playbackObserver);
1250
- currentContentInfo.streamEventsEmitter = streamEventsEmitter;
391
+ manifest.addEventListener("manifestUpdate", () => {
392
+ streamEventsEmitter.onManifestUpdate(manifest);
393
+ }, cancelSignal);
1251
394
  streamEventsEmitter.addEventListener("event", (payload) => {
1252
395
  this.trigger("streamEvent", payload);
1253
396
  }, cancelSignal);
@@ -1260,62 +403,59 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
1260
403
  });
1261
404
  }
1262
405
  }, { clearSignal: cancelSignal, emitCurrentValue: true });
1263
- const _getSegmentSinkMetrics = async () => {
1264
- this._awaitingRequests.nextRequestId++;
1265
- const requestId = this._awaitingRequests.nextRequestId;
1266
- this._settings.coreInterface.sendMessage({
1267
- type: "pull-segment-sink-store-infos" /* MainThreadMessageType.PullSegmentSinkStoreInfos */,
1268
- value: { requestId },
1269
- });
1270
- return new Promise((resolve, reject) => {
1271
- const rejectFn = (err) => {
1272
- cancelSignal.deregister(rejectFn);
1273
- this._awaitingRequests.pendingSinkMetrics.delete(requestId);
1274
- return reject(err);
1275
- };
1276
- this._awaitingRequests.pendingSinkMetrics.set(requestId, {
1277
- resolve: (value) => {
1278
- cancelSignal.deregister(rejectFn);
1279
- this._awaitingRequests.pendingSinkMetrics.delete(requestId);
1280
- resolve(value);
1281
- },
1282
- });
1283
- cancelSignal.register(rejectFn);
1284
- });
1285
- };
1286
- const _getThumbnailsData = async (periodId, thumbnailTrackId, time) => {
1287
- if (this._currentContentInfo === null) {
1288
- return Promise.reject(new Error("Cannot fetch thumbnails: No content loaded."));
406
+ const coreObserver = createCorePlaybackObserver(playbackObserver, {
407
+ autoPlay,
408
+ manifest,
409
+ mediaSource,
410
+ textDisplayer,
411
+ initialPlayPerformed,
412
+ speed,
413
+ }, cancelSignal);
414
+ (_b = this._cmcdDataBuilder) === null || _b === void 0 ? void 0 : _b.startMonitoringPlayback(coreObserver);
415
+ cancelSignal.register(() => {
416
+ var _a;
417
+ (_a = this._cmcdDataBuilder) === null || _a === void 0 ? void 0 : _a.stopMonitoringPlayback();
418
+ });
419
+ const rebufferingController = this._createRebufferingController(playbackObserver, manifest, speed, cancelSignal);
420
+ const freezeResolver = new FreezeResolver(segmentSinksStore);
421
+ if (mayMediaElementFailOnUndecipherableData()) {
422
+ // On some devices, just reload immediately when data become undecipherable
423
+ manifest.addEventListener("decipherabilityUpdate", (elts) => {
424
+ if (elts.some((e) => e.representation.decipherable !== true)) {
425
+ reloadMediaSource(0, undefined, undefined);
426
+ }
427
+ }, cancelSignal);
428
+ }
429
+ coreObserver.listen((observation) => {
430
+ synchronizeSegmentSinksOnObservation(observation, segmentSinksStore);
431
+ const freezeResolution = freezeResolver.onNewObservation(observation);
432
+ if (freezeResolution === null) {
433
+ return;
1289
434
  }
1290
- this._awaitingRequests.nextRequestId++;
1291
- const requestId = this._awaitingRequests.nextRequestId;
1292
- this._settings.coreInterface.sendMessage({
1293
- type: "thumbnail-request" /* MainThreadMessageType.ThumbnailDataRequest */,
1294
- contentId: this._currentContentInfo.contentId,
1295
- value: { requestId, periodId, thumbnailTrackId, time },
1296
- });
1297
- return new Promise((resolve, reject) => {
1298
- const rejectFn = (err) => {
1299
- cleanUp();
1300
- reject(err);
1301
- };
1302
- const cleanUp = () => {
1303
- cancelSignal.deregister(rejectFn);
1304
- this._awaitingRequests.pendingThumbnailFetching.delete(requestId);
1305
- };
1306
- this._awaitingRequests.pendingThumbnailFetching.set(requestId, {
1307
- resolve: (value) => {
1308
- cleanUp();
1309
- resolve(value);
1310
- },
1311
- reject: (value) => {
1312
- cleanUp();
1313
- reject(value);
1314
- },
1315
- });
1316
- cancelSignal.register(rejectFn);
435
+ // TODO: The following method looks generic, we may be able to factorize
436
+ // it with other reload handlers after some work.
437
+ const triggerReload = () => {
438
+ var _a;
439
+ const lastObservation = playbackObserver.getReference().getValue();
440
+ const position = lastObservation.position.isAwaitingFuturePosition()
441
+ ? lastObservation.position.getWanted()
442
+ : ((_a = coreObserver.getCurrentTime()) !== null && _a !== void 0 ? _a : lastObservation.position.getPolled());
443
+ const autoplay = initialPlayPerformed.getValue()
444
+ ? !playbackObserver.getIsPaused()
445
+ : autoPlay;
446
+ onReloadOrder({ position, autoPlay: autoplay });
447
+ };
448
+ handleFreezeResolution(freezeResolution, {
449
+ enableRepresentationAvoidance: this._initSettings.enableRepresentationAvoidance,
450
+ manifest,
451
+ triggerReload,
452
+ playbackObserver,
1317
453
  });
1318
- };
454
+ }, { clearSignal: cancelSignal });
455
+ const contentTimeBoundariesObserver = createContentTimeBoundariesObserver(manifest, mediaSource, coreObserver, segmentSinksStore, {
456
+ onWarning: (err) => this.trigger("warning", err),
457
+ onPeriodChanged: (period) => this.trigger("activePeriodChanged", { period }),
458
+ }, cancelSignal);
1319
459
  /**
1320
460
  * Emit a "loaded" events once the initial play has been performed and the
1321
461
  * media can begin playback.
@@ -1325,237 +465,452 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
1325
465
  .then(() => {
1326
466
  getLoadedReference(playbackObserver, false, cancelSignal).onUpdate((isLoaded, stopListening) => {
1327
467
  if (isLoaded) {
468
+ const fetchThumbnails = createThumbnailFetcher(transport.thumbnails, cdnPrioritizer);
1328
469
  stopListening();
1329
470
  this.trigger("loaded", {
1330
- getSegmentSinkMetrics: _getSegmentSinkMetrics,
1331
- getThumbnailData: _getThumbnailsData,
471
+ getSegmentSinkMetrics: async () => {
472
+ return new Promise((resolve) => resolve(segmentSinksStore.getSegmentSinksMetrics()));
473
+ },
474
+ getThumbnailData: async (periodId, thumbnailTrackId, time) => {
475
+ return getThumbnailData(fetchThumbnails, manifest, periodId, thumbnailTrackId, time);
476
+ },
1332
477
  });
1333
478
  }
1334
479
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
1335
480
  })
1336
481
  .catch((err) => {
1337
482
  if (cancelSignal.isCancelled()) {
1338
- return;
483
+ return; // Current loading cancelled, no need to trigger the error
1339
484
  }
1340
485
  this._onFatalError(err);
1341
486
  });
1342
- return corePlaybackObserver;
487
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
488
+ const self = this;
489
+ StreamOrchestrator({ manifest, initialPeriod }, coreObserver, representationEstimator, segmentSinksStore, segmentQueueCreator, bufferOptions, handleStreamOrchestratorCallbacks(), cancelSignal);
490
+ /**
491
+ * Returns Object handling the callbacks from a `StreamOrchestrator`, which
492
+ * are basically how it communicates about events.
493
+ * @returns {Object}
494
+ */
495
+ function handleStreamOrchestratorCallbacks() {
496
+ return {
497
+ needsBufferFlush: (payload) => {
498
+ var _a;
499
+ let wantedSeekingTime;
500
+ const lastObservation = playbackObserver.getReference().getValue();
501
+ const currentTime = lastObservation.position.isAwaitingFuturePosition()
502
+ ? lastObservation.position.getWanted()
503
+ : mediaElement.currentTime;
504
+ const relativeResumingPosition = (_a = payload === null || payload === void 0 ? void 0 : payload.relativeResumingPosition) !== null && _a !== void 0 ? _a : 0;
505
+ const canBeApproximateSeek = Boolean(payload === null || payload === void 0 ? void 0 : payload.relativePosHasBeenDefaulted);
506
+ if (relativeResumingPosition === 0 && canBeApproximateSeek) {
507
+ // in case relativeResumingPosition is 0, we still perform
508
+ // a tiny seek to be sure that the browser will correclty reload the video.
509
+ wantedSeekingTime = currentTime + 0.001;
510
+ }
511
+ else {
512
+ wantedSeekingTime = currentTime + relativeResumingPosition;
513
+ }
514
+ playbackObserver.setCurrentTime(wantedSeekingTime);
515
+ // Seek again once data begins to be buffered.
516
+ // This is sadly necessary on some browsers to avoid decoding
517
+ // issues after a flush.
518
+ //
519
+ // NOTE: there's in theory a potential race condition in the following
520
+ // logic as the callback could be called when media data is still
521
+ // being removed by the browser - which is an asynchronous process.
522
+ // The following condition checking for buffered data could thus lead
523
+ // to a false positive where we're actually checking previous data.
524
+ // For now, such scenario is avoided by setting the
525
+ // `includeLastObservation` option to `false` and calling
526
+ // `needsBufferFlush` once MSE media removal operations have been
527
+ // explicitely validated by the browser, but that's a complex and easy
528
+ // to break system.
529
+ playbackObserver.listen((obs, stopListening) => {
530
+ if (
531
+ // Data is buffered around the current position
532
+ obs.currentRange !== null ||
533
+ // Or, for whatever reason, we have no buffer but we're already advancing
534
+ obs.position.getPolled() > wantedSeekingTime + 0.1) {
535
+ stopListening();
536
+ playbackObserver.setCurrentTime(obs.position.getWanted() + 0.001);
537
+ }
538
+ }, { includeLastObservation: false, clearSignal: cancelSignal });
539
+ },
540
+ streamStatusUpdate(value) {
541
+ // Announce discontinuities if found
542
+ const { period, bufferType, imminentDiscontinuity, position } = value;
543
+ rebufferingController.updateDiscontinuityInfo({
544
+ period,
545
+ bufferType,
546
+ discontinuity: imminentDiscontinuity,
547
+ position,
548
+ });
549
+ if (cancelSignal.isCancelled()) {
550
+ return; // Previous call has stopped streams due to a side-effect
551
+ }
552
+ // If the status for the last Period indicates that segments are all loaded
553
+ // or on the contrary that the loading resumed, announce it to the
554
+ // ContentTimeBoundariesObserver.
555
+ if (manifest.isLastPeriodKnown &&
556
+ value.period.id === manifest.periods[manifest.periods.length - 1].id) {
557
+ const hasFinishedLoadingLastPeriod = value.hasFinishedLoading || value.isEmptyStream;
558
+ if (hasFinishedLoadingLastPeriod) {
559
+ contentTimeBoundariesObserver.onLastSegmentFinishedLoading(value.bufferType);
560
+ }
561
+ else {
562
+ contentTimeBoundariesObserver.onLastSegmentLoadingResume(value.bufferType);
563
+ }
564
+ }
565
+ },
566
+ needsManifestRefresh: () => self._manifestFetcher.scheduleManualRefresh({
567
+ enablePartialRefresh: true,
568
+ canUseUnsafeMode: true,
569
+ }),
570
+ manifestMightBeOufOfSync: () => {
571
+ const { OUT_OF_SYNC_MANIFEST_REFRESH_DELAY } = config.getCurrent();
572
+ self._manifestFetcher.scheduleManualRefresh({
573
+ enablePartialRefresh: false,
574
+ canUseUnsafeMode: false,
575
+ delay: OUT_OF_SYNC_MANIFEST_REFRESH_DELAY,
576
+ });
577
+ },
578
+ lockedStream: (value) => rebufferingController.onLockedStream(value.bufferType, value.period),
579
+ adaptationChange: (value) => {
580
+ self.trigger("adaptationChange", value);
581
+ if (cancelSignal.isCancelled()) {
582
+ return; // Previous call has stopped streams due to a side-effect
583
+ }
584
+ contentTimeBoundariesObserver.onAdaptationChange(value.type, value.period, value.adaptation);
585
+ },
586
+ representationChange: (value) => {
587
+ self.trigger("representationChange", value);
588
+ if (cancelSignal.isCancelled()) {
589
+ return; // Previous call has stopped streams due to a side-effect
590
+ }
591
+ contentTimeBoundariesObserver.onRepresentationChange(value.type, value.period);
592
+ },
593
+ inbandEvent: (value) => self.trigger("inbandEvents", value),
594
+ warning: (value) => self.trigger("warning", value),
595
+ periodStreamReady: (value) => self.trigger("periodStreamReady", value),
596
+ periodStreamCleared: (value) => {
597
+ contentTimeBoundariesObserver.onPeriodCleared(value.type, value.period);
598
+ if (cancelSignal.isCancelled()) {
599
+ return; // Previous call has stopped streams due to a side-effect
600
+ }
601
+ self.trigger("periodStreamCleared", {
602
+ type: value.type,
603
+ periodId: value.period.id,
604
+ });
605
+ },
606
+ bitrateEstimateChange: (value) => {
607
+ var _a;
608
+ (_a = self._cmcdDataBuilder) === null || _a === void 0 ? void 0 : _a.updateThroughput(value.type, value.bitrate);
609
+ self.trigger("bitrateEstimateChange", value);
610
+ },
611
+ needsMediaSourceReload: (payload) => {
612
+ reloadMediaSource(payload.timeOffset, payload.minimumPosition, payload.maximumPosition);
613
+ },
614
+ needsDecipherabilityFlush() {
615
+ var _a, _b, _c, _d;
616
+ const keySystem = getKeySystemConfiguration(mediaElement);
617
+ if (shouldReloadMediaSourceOnDecipherabilityUpdate(keySystem === null || keySystem === void 0 ? void 0 : keySystem[0])) {
618
+ const lastObservation = coreObserver.getReference().getValue();
619
+ const position = lastObservation.position.isAwaitingFuturePosition()
620
+ ? lastObservation.position.getWanted()
621
+ : ((_a = coreObserver.getCurrentTime()) !== null && _a !== void 0 ? _a : lastObservation.position.getPolled());
622
+ const isPaused = (_c = (_b = lastObservation.paused.pending) !== null && _b !== void 0 ? _b : coreObserver.getIsPaused()) !== null && _c !== void 0 ? _c : lastObservation.paused.last;
623
+ onReloadOrder({ position, autoPlay: !isPaused });
624
+ }
625
+ else {
626
+ const lastObservation = coreObserver.getReference().getValue();
627
+ const position = lastObservation.position.isAwaitingFuturePosition()
628
+ ? lastObservation.position.getWanted()
629
+ : ((_d = coreObserver.getCurrentTime()) !== null && _d !== void 0 ? _d : lastObservation.position.getPolled());
630
+ // simple seek close to the current position
631
+ // to flush the buffers
632
+ if (position + 0.001 < lastObservation.duration) {
633
+ playbackObserver.setCurrentTime(mediaElement.currentTime + 0.001);
634
+ }
635
+ else {
636
+ playbackObserver.setCurrentTime(position);
637
+ }
638
+ }
639
+ },
640
+ encryptionDataEncountered: (value) => {
641
+ if (self._decryptionCapabilities.status === "disabled") {
642
+ self._onFatalError(self._decryptionCapabilities.value);
643
+ return;
644
+ }
645
+ else if (self._decryptionCapabilities.status === "uninitialized") {
646
+ // Should never happen
647
+ log.error("Init", "received encryption data without known decryption capabilities");
648
+ return;
649
+ }
650
+ for (const protectionData of value) {
651
+ self._decryptionCapabilities.value.onInitializationData(protectionData);
652
+ if (cancelSignal.isCancelled()) {
653
+ return; // Previous call has stopped streams due to a side-effect
654
+ }
655
+ }
656
+ },
657
+ error: (err) => self._onFatalError(err),
658
+ };
659
+ }
660
+ /**
661
+ * Callback allowing to reload the current content.
662
+ * @param {number} deltaPosition - Position you want to seek to after
663
+ * reloading, as a delta in seconds from the last polled playing position.
664
+ * @param {number|undefined} minimumPosition - If set, minimum time bound
665
+ * in seconds after `deltaPosition` has been applied.
666
+ * @param {number|undefined} maximumPosition - If set, minimum time bound
667
+ * in seconds after `deltaPosition` has been applied.
668
+ */
669
+ function reloadMediaSource(deltaPosition, minimumPosition, maximumPosition) {
670
+ var _a, _b, _c;
671
+ const lastObservation = coreObserver.getReference().getValue();
672
+ const currentPosition = lastObservation.position.isAwaitingFuturePosition()
673
+ ? lastObservation.position.getWanted()
674
+ : ((_a = coreObserver.getCurrentTime()) !== null && _a !== void 0 ? _a : lastObservation.position.getPolled());
675
+ const isPaused = (_c = (_b = lastObservation.paused.pending) !== null && _b !== void 0 ? _b : coreObserver.getIsPaused()) !== null && _c !== void 0 ? _c : lastObservation.paused.last;
676
+ let position = currentPosition + deltaPosition;
677
+ if (minimumPosition !== undefined) {
678
+ position = Math.max(minimumPosition, position);
679
+ }
680
+ if (maximumPosition !== undefined) {
681
+ position = Math.min(maximumPosition, position);
682
+ }
683
+ onReloadOrder({ position, autoPlay: !isPaused });
684
+ }
1343
685
  }
1344
686
  /**
1345
- * Initialize content playback if and only if those conditions are filled:
1346
- * - The Manifest is fetched and stored in `this._currentContentInfo`.
1347
- * - `drmInitializationStatus` indicates that DRM matters are initialized.
1348
- * - `mediaSourceStatus` indicates that the MediaSource is attached to the
1349
- * `mediaElement`.
687
+ * Creates a `RebufferingController`, a class trying to avoid various stalling
688
+ * situations (such as rebuffering periods), and returns it.
1350
689
  *
1351
- * In other cases, this method will do nothing.
690
+ * Various methods from that class need then to be called at various events
691
+ * (see `RebufferingController` definition).
1352
692
  *
1353
- * To call when any of those conditions might become `true`, to start-up
1354
- * playback.
693
+ * This function also handles the `RebufferingController`'s events:
694
+ * - emit "stalled" events when stalling situations cannot be prevented,
695
+ * - emit "unstalled" events when we could get out of one,
696
+ * - emit "warning" on various rebuffering-related minor issues
697
+ * like discontinuity skipping.
698
+ * @param {Object} playbackObserver
699
+ * @param {Object} manifest
700
+ * @param {Object} speed
701
+ * @param {Object} cancelSignal
702
+ * @returns {Object}
703
+ */
704
+ _createRebufferingController(playbackObserver, manifest, speed, cancelSignal) {
705
+ const rebufferingController = new RebufferingController(playbackObserver, manifest, speed);
706
+ // Bubble-up events
707
+ rebufferingController.addEventListener("stalled", (evt) => this.trigger("stalled", evt));
708
+ rebufferingController.addEventListener("unstalled", () => this.trigger("unstalled", null));
709
+ rebufferingController.addEventListener("warning", (err) => this.trigger("warning", err));
710
+ cancelSignal.register(() => rebufferingController.destroy());
711
+ rebufferingController.start();
712
+ return rebufferingController;
713
+ }
714
+ /**
715
+ * Evaluates a list of codecs to determine their support status.
1355
716
  *
1356
- * @param {Object} parameters
1357
- * @returns {boolean} - Returns `true` if all conditions where met for
1358
- * playback start.
717
+ * @param {Array} codecsToCheck - The list of codecs to check.
718
+ * @returns {Array} - The list of evaluated codecs with their support status updated.
1359
719
  */
1360
- _startPlaybackIfReady(parameters) {
1361
- if (this._currentContentInfo === null || this._currentContentInfo.manifest === null) {
1362
- return false;
1363
- }
1364
- const drmInitStatus = parameters.drmInitializationStatus.getValue();
1365
- if (drmInitStatus.initializationState.type !== "initialized") {
1366
- return false;
1367
- }
1368
- const msInitStatus = parameters.mediaSourceStatus.getValue();
1369
- if (msInitStatus !== 2 /* MediaSourceInitializationStatus.Attached */) {
1370
- return false;
1371
- }
1372
- const { contentId, manifest } = this._currentContentInfo;
1373
- log.debug("Init", "Calculating initial time");
1374
- const initialTime = getInitialTime(manifest, this._settings.lowLatencyMode, this._settings.startAt);
1375
- log.debug("Init", "Initial time calculated", { initialTime });
1376
- const { enableFastSwitching, onCodecSwitch } = this._settings.bufferOptions;
1377
- const corePlaybackObserver = this._setUpModulesOnNewMediaSource({
1378
- initialTime,
1379
- autoPlay: this._settings.autoPlay,
1380
- mediaElement: parameters.mediaElement,
1381
- textDisplayer: parameters.textDisplayer,
1382
- playbackObserver: parameters.playbackObserver,
1383
- }, this._currentMediaSourceCanceller.signal);
1384
- if (this._currentMediaSourceCanceller.isUsed() || corePlaybackObserver === null) {
1385
- return true;
1386
- }
1387
- const initialObservation = corePlaybackObserver.getReference().getValue();
1388
- const sentInitialObservation = objectAssign(initialObservation, {
1389
- position: initialObservation.position.serialize(),
1390
- });
1391
- this._settings.coreInterface.sendMessage({
1392
- type: "start" /* MainThreadMessageType.StartPreparedContent */,
1393
- contentId,
1394
- value: {
1395
- initialTime,
1396
- initialObservation: sentInitialObservation,
1397
- drmSystemId: drmInitStatus.drmSystemId,
1398
- enableFastSwitching,
1399
- onCodecSwitch,
1400
- },
1401
- });
1402
- corePlaybackObserver.listen((obs) => {
1403
- this._settings.coreInterface.sendMessage({
1404
- type: "observation" /* MainThreadMessageType.PlaybackObservation */,
1405
- contentId,
1406
- value: objectAssign(obs, { position: obs.position.serialize() }),
1407
- });
1408
- }, {
1409
- includeLastObservation: false,
1410
- clearSignal: this._currentMediaSourceCanceller.signal,
720
+ getCodecsSupportInfo(codecsToCheck, mediaElement) {
721
+ const codecsSupportInfo = codecsToCheck.map((codecToCheck) => {
722
+ var _a;
723
+ const inputCodec = `${codecToCheck.mimeType};codecs="${codecToCheck.codec}"`;
724
+ const isSupported = isCodecSupported(mediaElement, inputCodec);
725
+ if (!isSupported) {
726
+ return {
727
+ mimeType: codecToCheck.mimeType,
728
+ codec: codecToCheck.codec,
729
+ supported: false,
730
+ supportedIfEncrypted: false,
731
+ };
732
+ }
733
+ /**
734
+ * `true` if the codec is supported when encrypted, `false` if it is not
735
+ * supported, or `undefined` if we cannot obtain that information.
736
+ */
737
+ let supportedIfEncrypted;
738
+ if (this._decryptionCapabilities.status === "uninitialized") {
739
+ supportedIfEncrypted = undefined;
740
+ }
741
+ else if (this._decryptionCapabilities.status === "disabled") {
742
+ // It's ambiguous here, but let's say that no ContentDecryptor means that
743
+ // the codec is supported by it.
744
+ supportedIfEncrypted = true;
745
+ }
746
+ else {
747
+ const contentDecryptor = this._decryptionCapabilities.value;
748
+ if (contentDecryptor.getState() !== ContentDecryptorState.Initializing) {
749
+ // No information is available regarding the support status.
750
+ // Defaulting to assume the codec is supported.
751
+ supportedIfEncrypted =
752
+ (_a = contentDecryptor.isCodecSupported(codecToCheck.mimeType, codecToCheck.codec)) !== null && _a !== void 0 ? _a : true;
753
+ }
754
+ }
755
+ return {
756
+ mimeType: codecToCheck.mimeType,
757
+ codec: codecToCheck.codec,
758
+ supported: isSupported,
759
+ supportedIfEncrypted,
760
+ };
1411
761
  });
1412
- this.trigger("manifestReady", manifest);
1413
- return true;
762
+ return codecsSupportInfo;
1414
763
  }
1415
764
  /**
1416
- * Handles core messages asking to create a MediaSource.
1417
- * @param {Object} msg - The core's message received.
1418
- * @param {HTMLMediaElement} mediaElement - HTMLMediaElement on which the
1419
- * content plays.
1420
- * @param {Object} coreInterface - The interface to the core.
765
+ * Update the support status of all Representations in the Manifest.
766
+ *
767
+ * To call anytime either the Manifest is linked to new codecs or new means
768
+ * to test for codec support are available.
769
+ * @param {Object} manifest
1421
770
  */
1422
- _onCreateMediaSourceMessage(msg, mediaElement, mediaSourceStatus, coreInterface) {
1423
- var _a;
1424
- if (((_a = this._currentContentInfo) === null || _a === void 0 ? void 0 : _a.contentId) !== msg.contentId) {
1425
- log.info("Init", "Ignoring MediaSource attachment due to wrong `contentId`");
1426
- }
1427
- else {
1428
- const { mediaSourceId } = msg;
771
+ _refreshManifestCodecSupport(manifest, mediaElement) {
772
+ const codecsToTest = manifest.getCodecsWithUnknownSupport();
773
+ const codecsSupportInfo = this.getCodecsSupportInfo(codecsToTest, mediaElement);
774
+ if (codecsSupportInfo.length > 0) {
1429
775
  try {
1430
- mediaSourceStatus.onUpdate((currStatus, stopListening) => {
1431
- var _a;
1432
- if (this._currentContentInfo === null) {
1433
- stopListening();
1434
- return;
1435
- }
1436
- if (currStatus === 1 /* MediaSourceInitializationStatus.AttachNow */) {
1437
- stopListening();
1438
- const mediaSource = new MainMediaSourceInterface(mediaSourceId, "FORCED_MEDIA_SOURCE" in mediaElement
1439
- ? mediaElement.FORCED_MEDIA_SOURCE
1440
- : undefined);
1441
- if (((_a = this._currentContentInfo.mediaSourceInfo) === null || _a === void 0 ? void 0 : _a.type) === "main") {
1442
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
1443
- }
1444
- this._currentContentInfo.mediaSourceInfo = {
1445
- type: "main",
1446
- mediaSource,
1447
- };
1448
- mediaSource.addEventListener("mediaSourceOpen", () => {
1449
- coreInterface.sendMessage({
1450
- type: "media-source-ready-state-change" /* MainThreadMessageType.MediaSourceReadyStateChange */,
1451
- mediaSourceId,
1452
- value: "open",
1453
- });
1454
- });
1455
- mediaSource.addEventListener("mediaSourceEnded", () => {
1456
- coreInterface.sendMessage({
1457
- type: "media-source-ready-state-change" /* MainThreadMessageType.MediaSourceReadyStateChange */,
1458
- mediaSourceId,
1459
- value: "ended",
1460
- });
1461
- });
1462
- mediaSource.addEventListener("mediaSourceClose", () => {
1463
- coreInterface.sendMessage({
1464
- type: "media-source-ready-state-change" /* MainThreadMessageType.MediaSourceReadyStateChange */,
1465
- mediaSourceId,
1466
- value: "closed",
1467
- });
1468
- });
1469
- let url = null;
1470
- if (mediaSource.handle.type === "handle") {
1471
- mediaElement.srcObject = mediaSource.handle.value;
1472
- }
1473
- else {
1474
- url = URL.createObjectURL(mediaSource.handle.value);
1475
- mediaElement.src = url;
1476
- }
1477
- this._currentMediaSourceCanceller.signal.register(() => {
1478
- mediaSource.dispose();
1479
- resetMediaElement(mediaElement, url);
1480
- });
1481
- mediaSourceStatus.setValue(2 /* MediaSourceInitializationStatus.Attached */);
1482
- disableRemotePlaybackOnManagedMediaSource(mediaElement, this._currentMediaSourceCanceller.signal);
1483
- }
1484
- }, {
1485
- emitCurrentValue: true,
1486
- clearSignal: this._currentMediaSourceCanceller.signal,
1487
- });
776
+ manifest.updateCodecSupport(codecsSupportInfo);
1488
777
  }
1489
- catch (_err) {
1490
- const error = new OtherError("NONE", "Unknown error when creating the MediaSource");
1491
- this._onFatalError(error);
778
+ catch (err) {
779
+ this._onFatalError(err);
1492
780
  }
1493
781
  }
1494
782
  }
1495
783
  }
1496
- function bindNumberReferencesToCore(coreInterface, cancellationSignal, ...refs) {
1497
- for (const ref of refs) {
1498
- ref[0].onUpdate((newVal) => {
1499
- // NOTE: The TypeScript checks have already been made by this function's
1500
- // overload, but the body here is not aware of that.
1501
- coreInterface.sendMessage({
1502
- type: "ref-update" /* MainThreadMessageType.ReferenceUpdate */,
1503
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
1504
- value: { name: ref[1], newVal: newVal },
1505
- });
1506
- }, { clearSignal: cancellationSignal, emitCurrentValue: true });
1507
- }
1508
- }
1509
- function formatCoreError(sentError) {
1510
- switch (sentError.name) {
1511
- case "NetworkError":
1512
- return new NetworkError(sentError.code, new RequestError(sentError.baseError.url, sentError.baseError.status, sentError.baseError.type));
1513
- case "MediaError":
1514
- // eslint-disable-next-line
1515
- return new MediaError(sentError.code, sentError.reason, {
1516
- tracks: sentError.tracks,
1517
- });
1518
- case "EncryptedMediaError":
1519
- // We assume that everything have already been checked Worker-side here
1520
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1521
- return new EncryptedMediaError(sentError.code, sentError.reason, {
1522
- keyStatuses: sentError.keyStatuses,
1523
- keySystemConfiguration: sentError.keySystemConfiguration,
1524
- keySystem: sentError.keySystem,
1525
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1526
- });
1527
- case "OtherError":
1528
- return new OtherError(sentError.code, sentError.reason);
1529
- }
1530
- }
1531
- function formatSourceBufferError(error) {
1532
- if (error instanceof SourceBufferError) {
1533
- return error;
1534
- }
1535
- else if (error instanceof Error) {
1536
- return new SourceBufferError(error.name, error.message, error.name === "QuotaExceededError");
784
+ function createTextDisplayer(mediaElement, textTrackOptions) {
785
+ if (textTrackOptions.textTrackMode === "html" && features.htmlTextDisplayer !== null) {
786
+ return new features.htmlTextDisplayer(mediaElement, textTrackOptions.textTrackElement);
1537
787
  }
1538
- else {
1539
- return new SourceBufferError("Error", "Unknown SourceBufferError Error", false);
788
+ else if (features.nativeTextDisplayer !== null) {
789
+ return new features.nativeTextDisplayer(mediaElement);
1540
790
  }
791
+ return null;
1541
792
  }
1542
793
  /**
1543
- * The Core might send back logs. In that situation, the message might be
1544
- * formatted slightly differently to be able to cross threads (so a
1545
- * serializable format has to be sent).
794
+ * Change the decipherability of Representations which have their key id in one
795
+ * of the given Arrays:
796
+ *
797
+ * - Those who have a key id listed in `whitelistedKeyIds` will have their
798
+ * decipherability updated to `true`
799
+ *
800
+ * - Those who have a key id listed in `blacklistedKeyIds` will have their
801
+ * decipherability updated to `false`
1546
802
  *
1547
- * This function translates that Core format to what's expected by the
1548
- * logger.
803
+ * - Those who have a key id listed in `delistedKeyIds` will have their
804
+ * decipherability updated to `undefined`.
1549
805
  *
1550
- * @param {*} arg
1551
- * @returns {*}
806
+ * @param {Object} manifest
807
+ * @param {Array.<Uint8Array>} whitelistedKeyIds
808
+ * @param {Array.<Uint8Array>} blacklistedKeyIds
809
+ * @param {Array.<Uint8Array>} delistedKeyIds
1552
810
  */
1553
- function formatSentLogObject(arg) {
1554
- if (typeof arg !== "object") {
1555
- return arg;
1556
- }
1557
- if ((arg === null || arg === void 0 ? void 0 : arg.isSerializedError) === true) {
1558
- return formatCoreError(arg);
811
+ function updateKeyIdsDecipherabilityOnManifest(manifest, whitelistedKeyIds, blacklistedKeyIds, delistedKeyIds) {
812
+ manifest.updateRepresentationsDeciperability((ctx) => {
813
+ const { representation } = ctx;
814
+ if (representation.contentProtections === undefined) {
815
+ return representation.decipherable;
816
+ }
817
+ const contentKIDs = representation.contentProtections.keyIds;
818
+ if (contentKIDs !== undefined) {
819
+ for (const elt of contentKIDs) {
820
+ for (const blacklistedKeyId of blacklistedKeyIds) {
821
+ if (areArraysOfNumbersEqual(blacklistedKeyId, elt)) {
822
+ return false;
823
+ }
824
+ }
825
+ for (const whitelistedKeyId of whitelistedKeyIds) {
826
+ if (areArraysOfNumbersEqual(whitelistedKeyId, elt)) {
827
+ return true;
828
+ }
829
+ }
830
+ for (const delistedKeyId of delistedKeyIds) {
831
+ if (areArraysOfNumbersEqual(delistedKeyId, elt)) {
832
+ return undefined;
833
+ }
834
+ }
835
+ }
836
+ }
837
+ return representation.decipherable;
838
+ });
839
+ }
840
+ /**
841
+ * Update decipherability to `false` to any Representation which is linked to
842
+ * the given initialization data.
843
+ * @param {Object} manifest
844
+ * @param {Object} initData
845
+ */
846
+ function blackListProtectionDataOnManifest(manifest, initData) {
847
+ manifest.updateRepresentationsDeciperability((ctx) => {
848
+ var _a, _b;
849
+ const rep = ctx.representation;
850
+ if (rep.decipherable === false) {
851
+ return false;
852
+ }
853
+ const segmentProtections = (_b = (_a = rep.contentProtections) === null || _a === void 0 ? void 0 : _a.initData) !== null && _b !== void 0 ? _b : [];
854
+ for (const protection of segmentProtections) {
855
+ if (initData.type === undefined || protection.type === initData.type) {
856
+ const containedInitData = initData.values
857
+ .getFormattedValues()
858
+ .every((undecipherableVal) => {
859
+ return protection.values.some((currVal) => {
860
+ return ((undecipherableVal.systemId === undefined ||
861
+ currVal.systemId === undecipherableVal.systemId) &&
862
+ areArraysOfNumbersEqual(currVal.data, undecipherableVal.data));
863
+ });
864
+ });
865
+ if (containedInitData) {
866
+ return false;
867
+ }
868
+ }
869
+ }
870
+ return rep.decipherable;
871
+ });
872
+ }
873
+ /**
874
+ * Handle accordingly an `IFreezeResolution` object.
875
+ * @param {Object|null} freezeResolution - The `IFreezeResolution` suggested.
876
+ * @param {Object} param - Parameters that might be needed to implement the
877
+ * resolution.
878
+ * @param {Object} param.manifest - The current content's Manifest object.
879
+ * @param {Object} param.playbackObserver - Object regularly emitting playback
880
+ * conditions.
881
+ * @param {Function} param.triggerReload - Function to call if we need to ask
882
+ * for a "MediaSource reload".
883
+ * @param {Boolean} param.enableRepresentationAvoidance - If `true`, this
884
+ * function is authorized to mark `Representation` as "to avoid" if the
885
+ * `IFreezeResolution` object suggest it.
886
+ */
887
+ function handleFreezeResolution(freezeResolution, { playbackObserver, enableRepresentationAvoidance, manifest, triggerReload, }) {
888
+ switch (freezeResolution.type) {
889
+ case "reload": {
890
+ log.info("Init", "Planning reload due to freeze");
891
+ triggerReload();
892
+ break;
893
+ }
894
+ case "flush": {
895
+ log.info("Init", "Flushing buffer due to freeze");
896
+ const observation = playbackObserver.getReference().getValue();
897
+ const currentTime = observation.position.isAwaitingFuturePosition()
898
+ ? observation.position.getWanted()
899
+ : playbackObserver.getCurrentTime();
900
+ const relativeResumingPosition = freezeResolution.value.relativeSeek;
901
+ const wantedSeekingTime = currentTime + relativeResumingPosition;
902
+ playbackObserver.setCurrentTime(wantedSeekingTime);
903
+ break;
904
+ }
905
+ case "avoid-representations": {
906
+ const contents = freezeResolution.value;
907
+ if (enableRepresentationAvoidance) {
908
+ manifest.addRepresentationsToAvoid(contents);
909
+ }
910
+ triggerReload();
911
+ break;
912
+ }
913
+ default:
914
+ assertUnreachable(freezeResolution);
1559
915
  }
1560
- return arg;
1561
916
  }