rx-player 4.4.0 → 4.4.1-dev.2025101500

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 (341) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +52 -113
  3. package/VERSION +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  5. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  6. package/dist/commonjs/core/{main/common → entry}/FreezeResolver.d.ts +3 -3
  7. package/dist/commonjs/core/entry/FreezeResolver.d.ts.map +1 -0
  8. package/dist/commonjs/core/{main/common → entry}/FreezeResolver.js +3 -3
  9. package/dist/{es2017/core/main/worker → commonjs/core/entry}/content_preparer.d.ts +22 -15
  10. package/dist/commonjs/core/entry/content_preparer.d.ts.map +1 -0
  11. package/dist/commonjs/core/{main/worker → entry}/content_preparer.js +62 -64
  12. package/dist/{es2017/core/main/common → commonjs/core/entry}/content_time_boundaries_observer.d.ts +5 -5
  13. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +1 -0
  14. package/dist/commonjs/core/{main/common → entry}/content_time_boundaries_observer.js +6 -6
  15. package/dist/commonjs/core/entry/core_entry.d.ts +36 -0
  16. package/dist/commonjs/core/entry/core_entry.d.ts.map +1 -0
  17. package/dist/commonjs/core/{main/worker/worker_main.js → entry/core_entry.js} +192 -153
  18. package/dist/commonjs/core/{main/worker/worker_text_displayer_interface.d.ts → entry/core_text_displayer_interface.d.ts} +11 -11
  19. package/dist/commonjs/core/entry/core_text_displayer_interface.d.ts.map +1 -0
  20. package/dist/commonjs/core/{main/worker/worker_text_displayer_interface.js → entry/core_text_displayer_interface.js} +22 -22
  21. package/dist/commonjs/core/{main/common → entry}/create_content_time_boundaries_observer.d.ts +6 -6
  22. package/dist/commonjs/core/entry/create_content_time_boundaries_observer.d.ts.map +1 -0
  23. package/dist/commonjs/core/{main/common → entry}/create_content_time_boundaries_observer.js +1 -1
  24. package/dist/commonjs/core/{main/common → entry}/get_buffered_data_per_media_buffer.d.ts +4 -4
  25. package/dist/commonjs/core/entry/get_buffered_data_per_media_buffer.d.ts.map +1 -0
  26. package/dist/commonjs/core/{main/common → entry}/get_buffered_data_per_media_buffer.js +1 -1
  27. package/dist/{es2017/core/main/common → commonjs/core/entry}/get_thumbnail_data.d.ts +3 -3
  28. package/dist/commonjs/core/entry/get_thumbnail_data.d.ts.map +1 -0
  29. package/dist/commonjs/core/{main/common → entry}/get_thumbnail_data.js +2 -2
  30. package/dist/commonjs/core/entry/index.d.ts +5 -0
  31. package/dist/commonjs/core/entry/index.d.ts.map +1 -0
  32. package/dist/commonjs/core/entry/index.js +4 -0
  33. package/dist/{es2017/core/main/common → commonjs/core/entry}/synchronize_sinks_on_observation.d.ts +2 -2
  34. package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts.map +1 -0
  35. package/dist/commonjs/core/{main/worker → entry}/track_choice_setter.d.ts +4 -4
  36. package/dist/commonjs/core/entry/track_choice_setter.d.ts.map +1 -0
  37. package/dist/commonjs/core/{main/worker → entry}/track_choice_setter.js +4 -4
  38. package/dist/commonjs/core/entry/utils.d.ts +3 -0
  39. package/dist/commonjs/core/entry/utils.d.ts.map +1 -0
  40. package/dist/commonjs/core/entry/utils.js +11 -0
  41. package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  42. package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.js +17 -0
  43. package/dist/commonjs/core/types.d.ts +519 -1
  44. package/dist/commonjs/core/types.d.ts.map +1 -1
  45. package/dist/commonjs/core/types.js +1 -0
  46. package/dist/commonjs/experimental/features/local.d.ts.map +1 -1
  47. package/dist/commonjs/experimental/features/local.js +7 -1
  48. package/dist/commonjs/experimental/features/metaplaylist.d.ts.map +1 -1
  49. package/dist/commonjs/experimental/features/metaplaylist.js +7 -1
  50. package/dist/commonjs/experimental/features/multi_thread.d.ts.map +1 -1
  51. package/dist/commonjs/experimental/features/multi_thread.js +6 -2
  52. package/dist/commonjs/features/features_object.js +1 -1
  53. package/dist/commonjs/features/list/dash.d.ts.map +1 -1
  54. package/dist/commonjs/features/list/dash.js +7 -1
  55. package/dist/commonjs/features/list/dash_wasm.d.ts.map +1 -1
  56. package/dist/commonjs/features/list/dash_wasm.js +7 -1
  57. package/dist/commonjs/features/list/media_source_main.d.ts.map +1 -1
  58. package/dist/commonjs/features/list/media_source_main.js +7 -1
  59. package/dist/commonjs/features/list/smooth.d.ts.map +1 -1
  60. package/dist/commonjs/features/list/smooth.js +7 -1
  61. package/dist/commonjs/features/types.d.ts +20 -4
  62. package/dist/commonjs/features/types.d.ts.map +1 -1
  63. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  64. package/dist/commonjs/main_thread/api/public_api.js +39 -42
  65. package/dist/commonjs/main_thread/core_interface/base.d.ts +13 -0
  66. package/dist/commonjs/main_thread/core_interface/base.d.ts.map +1 -0
  67. package/dist/commonjs/main_thread/core_interface/base.js +32 -0
  68. package/dist/commonjs/main_thread/core_interface/monothread.d.ts +13 -0
  69. package/dist/commonjs/main_thread/core_interface/monothread.d.ts.map +1 -0
  70. package/dist/commonjs/main_thread/core_interface/monothread.js +56 -0
  71. package/dist/commonjs/main_thread/core_interface/multithread.d.ts +25 -0
  72. package/dist/commonjs/main_thread/core_interface/multithread.d.ts.map +1 -0
  73. package/dist/commonjs/main_thread/core_interface/multithread.js +67 -0
  74. package/dist/commonjs/main_thread/core_interface/types.d.ts +6 -0
  75. package/dist/commonjs/main_thread/core_interface/types.d.ts.map +1 -0
  76. package/dist/commonjs/main_thread/core_interface/types.js +2 -0
  77. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +166 -108
  78. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  79. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +1492 -919
  80. package/dist/commonjs/main_thread/init/utils/create_core_playback_observer.d.ts.map +1 -1
  81. package/dist/commonjs/main_thread/init/utils/create_core_playback_observer.js +2 -1
  82. package/dist/commonjs/main_thread/init/utils/update_manifest_codec_support.d.ts +1 -1
  83. package/dist/commonjs/main_thread/init/utils/update_manifest_codec_support.d.ts.map +1 -1
  84. package/dist/commonjs/main_thread/types.d.ts +537 -0
  85. package/dist/commonjs/main_thread/types.d.ts.map +1 -1
  86. package/dist/commonjs/manifest/utils.d.ts.map +1 -1
  87. package/dist/commonjs/manifest/utils.js +18 -4
  88. package/dist/commonjs/mse/worker_media_source_interface.d.ts +2 -2
  89. package/dist/commonjs/mse/worker_media_source_interface.d.ts.map +1 -1
  90. package/dist/commonjs/mse/worker_media_source_interface.js +12 -12
  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 +31 -27
  94. package/dist/commonjs/parsers/manifest/smooth/parse_C_nodes.d.ts +3 -2
  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 +16 -7
  97. package/dist/commonjs/parsers/manifest/smooth/parse_protection_node.d.ts +3 -2
  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 +37 -6
  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 +3 -2
  103. package/dist/commonjs/parsers/manifest/smooth/utils/reduceChildren.d.ts.map +1 -1
  104. package/dist/commonjs/parsers/manifest/smooth/utils/reduceChildren.js +28 -5
  105. package/dist/{es2017/playback_observer/worker_playback_observer.d.ts → commonjs/playback_observer/core_playback_observer.d.ts} +8 -8
  106. package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +1 -0
  107. package/dist/commonjs/playback_observer/{worker_playback_observer.js → core_playback_observer.js} +13 -13
  108. package/dist/commonjs/transports/smooth/pipelines.d.ts.map +1 -1
  109. package/dist/commonjs/transports/smooth/pipelines.js +25 -3
  110. package/dist/commonjs/worker_entry_point.js +62 -2
  111. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  112. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  113. package/dist/es2017/core/{main/common → entry}/FreezeResolver.d.ts +3 -3
  114. package/dist/es2017/core/entry/FreezeResolver.d.ts.map +1 -0
  115. package/dist/es2017/core/{main/common → entry}/FreezeResolver.js +3 -3
  116. package/dist/{commonjs/core/main/worker → es2017/core/entry}/content_preparer.d.ts +22 -15
  117. package/dist/es2017/core/entry/content_preparer.d.ts.map +1 -0
  118. package/dist/es2017/core/{main/worker → entry}/content_preparer.js +53 -55
  119. package/dist/{commonjs/core/main/common → es2017/core/entry}/content_time_boundaries_observer.d.ts +5 -5
  120. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +1 -0
  121. package/dist/es2017/core/{main/common → entry}/content_time_boundaries_observer.js +6 -6
  122. package/dist/es2017/core/entry/core_entry.d.ts +36 -0
  123. package/dist/es2017/core/entry/core_entry.d.ts.map +1 -0
  124. package/dist/es2017/core/{main/worker/worker_main.js → entry/core_entry.js} +153 -114
  125. package/dist/es2017/core/{main/worker/worker_text_displayer_interface.d.ts → entry/core_text_displayer_interface.d.ts} +11 -11
  126. package/dist/es2017/core/entry/core_text_displayer_interface.d.ts.map +1 -0
  127. package/dist/es2017/core/{main/worker/worker_text_displayer_interface.js → entry/core_text_displayer_interface.js} +10 -10
  128. package/dist/es2017/core/{main/common → entry}/create_content_time_boundaries_observer.d.ts +6 -6
  129. package/dist/es2017/core/entry/create_content_time_boundaries_observer.d.ts.map +1 -0
  130. package/dist/es2017/core/{main/common → entry}/create_content_time_boundaries_observer.js +1 -1
  131. package/dist/es2017/core/{main/common → entry}/get_buffered_data_per_media_buffer.d.ts +4 -4
  132. package/dist/es2017/core/entry/get_buffered_data_per_media_buffer.d.ts.map +1 -0
  133. package/dist/es2017/core/{main/common → entry}/get_buffered_data_per_media_buffer.js +1 -1
  134. package/dist/{commonjs/core/main/common → es2017/core/entry}/get_thumbnail_data.d.ts +3 -3
  135. package/dist/es2017/core/entry/get_thumbnail_data.d.ts.map +1 -0
  136. package/dist/es2017/core/{main/common → entry}/get_thumbnail_data.js +2 -2
  137. package/dist/es2017/core/entry/index.d.ts +5 -0
  138. package/dist/es2017/core/entry/index.d.ts.map +1 -0
  139. package/dist/es2017/core/entry/index.js +2 -0
  140. package/dist/{commonjs/core/main/common → es2017/core/entry}/synchronize_sinks_on_observation.d.ts +2 -2
  141. package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts.map +1 -0
  142. package/dist/es2017/core/{main/worker → entry}/track_choice_setter.d.ts +4 -4
  143. package/dist/es2017/core/entry/track_choice_setter.d.ts.map +1 -0
  144. package/dist/es2017/core/{main/worker → entry}/track_choice_setter.js +4 -4
  145. package/dist/es2017/core/entry/utils.d.ts +3 -0
  146. package/dist/es2017/core/entry/utils.d.ts.map +1 -0
  147. package/dist/es2017/core/entry/utils.js +8 -0
  148. package/dist/es2017/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
  149. package/dist/es2017/core/stream/orchestrator/stream_orchestrator.js +18 -1
  150. package/dist/es2017/core/types.d.ts +519 -1
  151. package/dist/es2017/core/types.d.ts.map +1 -1
  152. package/dist/es2017/core/types.js +1 -0
  153. package/dist/es2017/experimental/features/local.d.ts.map +1 -1
  154. package/dist/es2017/experimental/features/local.js +7 -1
  155. package/dist/es2017/experimental/features/metaplaylist.d.ts.map +1 -1
  156. package/dist/es2017/experimental/features/metaplaylist.js +7 -1
  157. package/dist/es2017/experimental/features/multi_thread.d.ts.map +1 -1
  158. package/dist/es2017/experimental/features/multi_thread.js +6 -2
  159. package/dist/es2017/features/features_object.js +1 -1
  160. package/dist/es2017/features/list/dash.d.ts.map +1 -1
  161. package/dist/es2017/features/list/dash.js +7 -1
  162. package/dist/es2017/features/list/dash_wasm.d.ts.map +1 -1
  163. package/dist/es2017/features/list/dash_wasm.js +7 -1
  164. package/dist/es2017/features/list/media_source_main.d.ts.map +1 -1
  165. package/dist/es2017/features/list/media_source_main.js +7 -1
  166. package/dist/es2017/features/list/smooth.d.ts.map +1 -1
  167. package/dist/es2017/features/list/smooth.js +7 -1
  168. package/dist/es2017/features/types.d.ts +20 -4
  169. package/dist/es2017/features/types.d.ts.map +1 -1
  170. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  171. package/dist/es2017/main_thread/api/public_api.js +40 -43
  172. package/dist/es2017/main_thread/core_interface/base.d.ts +13 -0
  173. package/dist/es2017/main_thread/core_interface/base.d.ts.map +1 -0
  174. package/dist/es2017/main_thread/core_interface/base.js +28 -0
  175. package/dist/es2017/main_thread/core_interface/monothread.d.ts +13 -0
  176. package/dist/es2017/main_thread/core_interface/monothread.d.ts.map +1 -0
  177. package/dist/es2017/main_thread/core_interface/monothread.js +32 -0
  178. package/dist/es2017/main_thread/core_interface/multithread.d.ts +25 -0
  179. package/dist/es2017/main_thread/core_interface/multithread.d.ts.map +1 -0
  180. package/dist/es2017/main_thread/core_interface/multithread.js +45 -0
  181. package/dist/es2017/main_thread/core_interface/types.d.ts +6 -0
  182. package/dist/es2017/main_thread/core_interface/types.d.ts.map +1 -0
  183. package/dist/es2017/main_thread/core_interface/types.js +1 -0
  184. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +166 -108
  185. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  186. package/dist/es2017/main_thread/init/media_source_content_initializer.js +1405 -760
  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 +2 -1
  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 +537 -0
  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 +16 -4
  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/parsers/manifest/smooth/create_parser.d.ts +1 -1
  199. package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  200. package/dist/es2017/parsers/manifest/smooth/create_parser.js +31 -27
  201. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.d.ts +3 -2
  202. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.d.ts.map +1 -1
  203. package/dist/es2017/parsers/manifest/smooth/parse_C_nodes.js +16 -7
  204. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.d.ts +3 -2
  205. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.d.ts.map +1 -1
  206. package/dist/es2017/parsers/manifest/smooth/parse_protection_node.js +15 -6
  207. package/dist/es2017/parsers/manifest/smooth/utils/parseBoolean.d.ts +1 -1
  208. package/dist/es2017/parsers/manifest/smooth/utils/parseBoolean.d.ts.map +1 -1
  209. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.d.ts +3 -2
  210. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.d.ts.map +1 -1
  211. package/dist/es2017/parsers/manifest/smooth/utils/reduceChildren.js +6 -5
  212. package/dist/{commonjs/playback_observer/worker_playback_observer.d.ts → es2017/playback_observer/core_playback_observer.d.ts} +8 -8
  213. package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +1 -0
  214. package/dist/es2017/playback_observer/{worker_playback_observer.js → core_playback_observer.js} +2 -2
  215. package/dist/es2017/transports/smooth/pipelines.d.ts.map +1 -1
  216. package/dist/es2017/transports/smooth/pipelines.js +25 -3
  217. package/dist/es2017/worker_entry_point.js +62 -2
  218. package/dist/rx-player.js +21882 -19021
  219. package/dist/rx-player.min.js +20 -20
  220. package/dist/worker.js +8 -8
  221. package/package.json +1 -1
  222. package/src/README.md +88 -198
  223. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  224. package/src/core/{main/common → entry}/FreezeResolver.ts +7 -7
  225. package/src/core/{main → entry}/README.md +1 -1
  226. package/src/core/{main/worker → entry}/content_preparer.ts +77 -76
  227. package/src/core/{main/common → entry}/content_time_boundaries_observer.ts +10 -10
  228. package/src/core/{main/worker/worker_main.ts → entry/core_entry.ts} +223 -149
  229. package/src/core/{main/worker/worker_text_displayer_interface.ts → entry/core_text_displayer_interface.ts} +26 -26
  230. package/src/core/{main/common → entry}/create_content_time_boundaries_observer.ts +7 -7
  231. package/src/core/{main/common → entry}/get_buffered_data_per_media_buffer.ts +6 -6
  232. package/src/core/{main/common → entry}/get_thumbnail_data.ts +5 -5
  233. package/src/core/entry/index.ts +4 -0
  234. package/src/core/{main/common → entry}/synchronize_sinks_on_observation.ts +2 -2
  235. package/src/core/{main/worker → entry}/track_choice_setter.ts +7 -7
  236. package/src/core/entry/utils.ts +11 -0
  237. package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -1
  238. package/src/core/types.ts +631 -3
  239. package/src/experimental/features/__tests__/local.test.ts +11 -2
  240. package/src/experimental/features/__tests__/metaplaylist.test.ts +11 -2
  241. package/src/experimental/features/__tests__/multi_thread.test.ts +8 -3
  242. package/src/experimental/features/local.ts +7 -1
  243. package/src/experimental/features/metaplaylist.ts +7 -1
  244. package/src/experimental/features/multi_thread.ts +6 -2
  245. package/src/features/features_object.ts +1 -1
  246. package/src/features/list/__tests__/dash.test.ts +12 -3
  247. package/src/features/list/__tests__/smooth.test.ts +11 -2
  248. package/src/features/list/dash.ts +7 -1
  249. package/src/features/list/dash_wasm.ts +7 -1
  250. package/src/features/list/media_source_main.ts +7 -1
  251. package/src/features/list/smooth.ts +7 -1
  252. package/src/features/types.ts +23 -4
  253. package/src/main_thread/README.md +8 -0
  254. package/src/main_thread/api/public_api.ts +46 -48
  255. package/src/main_thread/core_interface/README.md +22 -0
  256. package/src/main_thread/core_interface/base.ts +36 -0
  257. package/src/main_thread/core_interface/monothread.ts +46 -0
  258. package/src/main_thread/core_interface/multithread.ts +49 -0
  259. package/src/main_thread/core_interface/types.ts +5 -0
  260. package/src/main_thread/init/media_source_content_initializer.ts +2034 -1152
  261. package/src/main_thread/init/utils/create_core_playback_observer.ts +2 -1
  262. package/src/main_thread/init/utils/update_manifest_codec_support.ts +1 -1
  263. package/src/main_thread/types.ts +610 -0
  264. package/src/manifest/utils.ts +20 -4
  265. package/src/mse/worker_media_source_interface.ts +35 -35
  266. package/src/parsers/manifest/smooth/create_parser.ts +40 -34
  267. package/src/parsers/manifest/smooth/parse_C_nodes.ts +19 -8
  268. package/src/parsers/manifest/smooth/parse_protection_node.ts +17 -9
  269. package/src/parsers/manifest/smooth/utils/parseBoolean.ts +1 -1
  270. package/src/parsers/manifest/smooth/utils/reduceChildren.ts +10 -7
  271. package/src/playback_observer/{worker_playback_observer.ts → core_playback_observer.ts} +13 -13
  272. package/src/transports/smooth/pipelines.ts +25 -5
  273. package/src/worker_entry_point.ts +71 -2
  274. package/dist/commonjs/core/main/common/FreezeResolver.d.ts.map +0 -1
  275. package/dist/commonjs/core/main/common/content_time_boundaries_observer.d.ts.map +0 -1
  276. package/dist/commonjs/core/main/common/create_content_time_boundaries_observer.d.ts.map +0 -1
  277. package/dist/commonjs/core/main/common/get_buffered_data_per_media_buffer.d.ts.map +0 -1
  278. package/dist/commonjs/core/main/common/get_thumbnail_data.d.ts.map +0 -1
  279. package/dist/commonjs/core/main/common/synchronize_sinks_on_observation.d.ts.map +0 -1
  280. package/dist/commonjs/core/main/worker/content_preparer.d.ts.map +0 -1
  281. package/dist/commonjs/core/main/worker/globals.d.ts +0 -14
  282. package/dist/commonjs/core/main/worker/globals.d.ts.map +0 -1
  283. package/dist/commonjs/core/main/worker/globals.js +0 -26
  284. package/dist/commonjs/core/main/worker/index.d.ts +0 -3
  285. package/dist/commonjs/core/main/worker/index.d.ts.map +0 -1
  286. package/dist/commonjs/core/main/worker/index.js +0 -4
  287. package/dist/commonjs/core/main/worker/send_message.d.ts +0 -4
  288. package/dist/commonjs/core/main/worker/send_message.d.ts.map +0 -1
  289. package/dist/commonjs/core/main/worker/send_message.js +0 -23
  290. package/dist/commonjs/core/main/worker/track_choice_setter.d.ts.map +0 -1
  291. package/dist/commonjs/core/main/worker/worker_main.d.ts +0 -2
  292. package/dist/commonjs/core/main/worker/worker_main.d.ts.map +0 -1
  293. package/dist/commonjs/core/main/worker/worker_text_displayer_interface.d.ts.map +0 -1
  294. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts +0 -308
  295. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts.map +0 -1
  296. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.js +0 -1713
  297. package/dist/commonjs/main_thread/init/send_message.d.ts +0 -3
  298. package/dist/commonjs/main_thread/init/send_message.d.ts.map +0 -1
  299. package/dist/commonjs/main_thread/init/send_message.js +0 -13
  300. package/dist/commonjs/multithread_types.d.ts +0 -915
  301. package/dist/commonjs/multithread_types.d.ts.map +0 -1
  302. package/dist/commonjs/multithread_types.js +0 -7
  303. package/dist/commonjs/playback_observer/worker_playback_observer.d.ts.map +0 -1
  304. package/dist/es2017/core/main/common/FreezeResolver.d.ts.map +0 -1
  305. package/dist/es2017/core/main/common/content_time_boundaries_observer.d.ts.map +0 -1
  306. package/dist/es2017/core/main/common/create_content_time_boundaries_observer.d.ts.map +0 -1
  307. package/dist/es2017/core/main/common/get_buffered_data_per_media_buffer.d.ts.map +0 -1
  308. package/dist/es2017/core/main/common/get_thumbnail_data.d.ts.map +0 -1
  309. package/dist/es2017/core/main/common/synchronize_sinks_on_observation.d.ts.map +0 -1
  310. package/dist/es2017/core/main/worker/content_preparer.d.ts.map +0 -1
  311. package/dist/es2017/core/main/worker/globals.d.ts +0 -14
  312. package/dist/es2017/core/main/worker/globals.d.ts.map +0 -1
  313. package/dist/es2017/core/main/worker/globals.js +0 -18
  314. package/dist/es2017/core/main/worker/index.d.ts +0 -3
  315. package/dist/es2017/core/main/worker/index.d.ts.map +0 -1
  316. package/dist/es2017/core/main/worker/index.js +0 -2
  317. package/dist/es2017/core/main/worker/send_message.d.ts +0 -4
  318. package/dist/es2017/core/main/worker/send_message.d.ts.map +0 -1
  319. package/dist/es2017/core/main/worker/send_message.js +0 -19
  320. package/dist/es2017/core/main/worker/track_choice_setter.d.ts.map +0 -1
  321. package/dist/es2017/core/main/worker/worker_main.d.ts +0 -2
  322. package/dist/es2017/core/main/worker/worker_main.d.ts.map +0 -1
  323. package/dist/es2017/core/main/worker/worker_text_displayer_interface.d.ts.map +0 -1
  324. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts +0 -308
  325. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts.map +0 -1
  326. package/dist/es2017/main_thread/init/multi_thread_content_initializer.js +0 -1559
  327. package/dist/es2017/main_thread/init/send_message.d.ts +0 -3
  328. package/dist/es2017/main_thread/init/send_message.d.ts.map +0 -1
  329. package/dist/es2017/main_thread/init/send_message.js +0 -10
  330. package/dist/es2017/multithread_types.d.ts +0 -915
  331. package/dist/es2017/multithread_types.d.ts.map +0 -1
  332. package/dist/es2017/multithread_types.js +0 -6
  333. package/dist/es2017/playback_observer/worker_playback_observer.d.ts.map +0 -1
  334. package/src/core/main/worker/globals.ts +0 -38
  335. package/src/core/main/worker/index.ts +0 -2
  336. package/src/core/main/worker/send_message.ts +0 -28
  337. package/src/main_thread/init/multi_thread_content_initializer.ts +0 -2330
  338. package/src/main_thread/init/send_message.ts +0 -15
  339. package/src/multithread_types.ts +0 -1095
  340. /package/dist/commonjs/core/{main/common → entry}/synchronize_sinks_on_observation.js +0 -0
  341. /package/dist/es2017/core/{main/common → entry}/synchronize_sinks_on_observation.js +0 -0
@@ -1,2330 +0,0 @@
1
- import type { IMediaElement } from "../../compat/browser_compatibility_types";
2
- import getEmeApiImplementation from "../../compat/eme";
3
- import mayMediaElementFailOnUndecipherableData from "../../compat/may_media_element_fail_on_undecipherable_data";
4
- import shouldReloadMediaSourceOnDecipherabilityUpdate from "../../compat/should_reload_media_source_on_decipherability_update";
5
- import type { ISegmentSinkMetrics } from "../../core/segment_sinks/segment_sinks_store";
6
- import type {
7
- IAdaptiveRepresentationSelectorArguments,
8
- IAdaptationChoice,
9
- IResolutionInfo,
10
- } from "../../core/types";
11
- import {
12
- EncryptedMediaError,
13
- MediaError,
14
- NetworkError,
15
- OtherError,
16
- SourceBufferError,
17
- } from "../../errors";
18
- import features from "../../features";
19
- import log from "../../log";
20
- import type { IManifestMetadata } from "../../manifest";
21
- import {
22
- replicateUpdatesOnManifestMetadata,
23
- updateDecipherabilityFromKeyIds,
24
- updateDecipherabilityFromProtectionData,
25
- } from "../../manifest";
26
- import MainMediaSourceInterface from "../../mse/main_media_source_interface";
27
- import type {
28
- ICreateMediaSourceWorkerMessage,
29
- ISentError,
30
- ISentLogValue,
31
- IWorkerMessage,
32
- } from "../../multithread_types";
33
- import { MainThreadMessageType, WorkerMessageType } from "../../multithread_types";
34
- import type {
35
- IReadOnlyPlaybackObserver,
36
- IMediaElementPlaybackObserver,
37
- } from "../../playback_observer";
38
- import type { IWorkerPlaybackObservation } from "../../playback_observer/worker_playback_observer";
39
- import type {
40
- ICmcdOptions,
41
- IInitialManifest,
42
- IKeySystemOption,
43
- IPlayerError,
44
- } from "../../public_types";
45
- import type { IThumbnailResponse, ITransportOptions } from "../../transports";
46
- import arrayFind from "../../utils/array_find";
47
- import assert, { assertUnreachable } from "../../utils/assert";
48
- import idGenerator from "../../utils/id_generator";
49
- import isNullOrUndefined from "../../utils/is_null_or_undefined";
50
- import type { IAcceptedLogValue } from "../../utils/logger";
51
- import objectAssign from "../../utils/object_assign";
52
- import type { IReadOnlySharedReference } from "../../utils/reference";
53
- import SharedReference from "../../utils/reference";
54
- import { RequestError } from "../../utils/request";
55
- import type { CancellationSignal } from "../../utils/task_canceller";
56
- import TaskCanceller, { CancellationError } from "../../utils/task_canceller";
57
- import type { IContentProtection } from "../decrypt";
58
- import type IContentDecryptor from "../decrypt";
59
- import { ContentDecryptorState, getKeySystemConfiguration } from "../decrypt";
60
- import type { ITextDisplayer } from "../text_displayer";
61
- import sendMessage from "./send_message";
62
- import type { ITextDisplayerOptions } from "./types";
63
- import { ContentInitializer } from "./types";
64
- import createCorePlaybackObserver from "./utils/create_core_playback_observer";
65
- import {
66
- resetMediaElement,
67
- disableRemotePlaybackOnManagedMediaSource,
68
- } from "./utils/create_media_source";
69
- import type { IInitialTimeOptions } from "./utils/get_initial_time";
70
- import getInitialTime from "./utils/get_initial_time";
71
- import getLoadedReference from "./utils/get_loaded_reference";
72
- import performInitialSeekAndPlay from "./utils/initial_seek_and_play";
73
- import RebufferingController from "./utils/rebuffering_controller";
74
- import StreamEventsEmitter from "./utils/stream_events_emitter/stream_events_emitter";
75
- import listenToMediaError from "./utils/throw_on_media_error";
76
- import { updateManifestCodecSupport } from "./utils/update_manifest_codec_support";
77
-
78
- const generateContentId = idGenerator();
79
-
80
- /**
81
- * @class MultiThreadContentInitializer
82
- */
83
- export default class MultiThreadContentInitializer extends ContentInitializer {
84
- /** Constructor settings associated to this `MultiThreadContentInitializer`. */
85
- private _settings: IInitializeArguments;
86
-
87
- /**
88
- * The WebWorker may be sending messages as soon as we're preparing the
89
- * content but the `MultiThreadContentInitializer` is only able to handle all of
90
- * them only once `start`ed.
91
- *
92
- * As such `_queuedWorkerMessages` is set to an Array when `prepare` has been
93
- * called but not `start` yet, and contains all worker messages that have to
94
- * be processed when `start` is called.
95
- *
96
- * It is set to `null` when there's no need to rely on that queue (either not
97
- * yet `prepare`d or already `start`ed).
98
- */
99
- private _queuedWorkerMessages: MessageEvent[] | null;
100
-
101
- /**
102
- * Information relative to the current loaded content.
103
- *
104
- * `null` when no content is prepared yet.
105
- */
106
- private _currentContentInfo: IMultiThreadContentInitializerContentInfos | null;
107
- /**
108
- * `TaskCanceller` allowing to abort everything that the
109
- * `MultiThreadContentInitializer` is doing.
110
- */
111
- private _initCanceller: TaskCanceller;
112
- /**
113
- * `TaskCanceller` allowing to abort and clean-up every task and resource
114
- * linked to the current `MediaSource` instance.
115
- *
116
- * It may be triggered either at content stop (and thus at the same time than
117
- * the `_initCanceller`) or when reloading the content.
118
- */
119
- private _currentMediaSourceCanceller: TaskCanceller;
120
-
121
- private _awaitingRequests: {
122
- nextRequestId: number;
123
- /**
124
- * Stores the resolvers and the current messageId that is sent to the web worker to
125
- * receive segment sink metrics.
126
- * The purpose of collecting metrics is for monitoring and debugging.
127
- */
128
- pendingSinkMetrics: Map<
129
- number /* request id */,
130
- {
131
- resolve: (value: ISegmentSinkMetrics | undefined) => void;
132
- }
133
- >;
134
- /**
135
- * Stores the resolvers and the current messageId that is sent to the web worker to
136
- * receive image thumbnails.
137
- */
138
- pendingThumbnailFetching: Map<
139
- number /* request id */,
140
- {
141
- resolve: (value: IThumbnailResponse) => void;
142
- reject: (error: Error) => void;
143
- }
144
- >;
145
- };
146
-
147
- /**
148
- * Create a new `MultiThreadContentInitializer`, associated to the given
149
- * settings.
150
- * @param {Object} settings
151
- */
152
- constructor(settings: IInitializeArguments) {
153
- super();
154
- this._settings = settings;
155
- this._initCanceller = new TaskCanceller();
156
- this._currentMediaSourceCanceller = new TaskCanceller();
157
- this._currentMediaSourceCanceller.linkToSignal(this._initCanceller.signal);
158
- this._currentContentInfo = null;
159
- this._awaitingRequests = {
160
- nextRequestId: 0,
161
- pendingSinkMetrics: new Map(),
162
- pendingThumbnailFetching: new Map(),
163
- };
164
- this._queuedWorkerMessages = null;
165
- }
166
-
167
- /**
168
- * Perform non-destructive preparation steps, to prepare a future content.
169
- */
170
- public prepare(): void {
171
- if (this._currentContentInfo !== null || this._initCanceller.isUsed()) {
172
- return;
173
- }
174
- const contentId = generateContentId();
175
- const { adaptiveOptions, transportOptions, useMseInWorker, worker } = this._settings;
176
- const { wantedBufferAhead, maxVideoBufferSize, maxBufferAhead, maxBufferBehind } =
177
- this._settings.bufferOptions;
178
- const initialVideoBitrate = adaptiveOptions.initialBitrates.video;
179
- const initialAudioBitrate = adaptiveOptions.initialBitrates.audio;
180
- this._currentContentInfo = {
181
- contentId,
182
- contentDecryptor: null,
183
- manifest: null,
184
- mediaSourceInfo: null,
185
- rebufferingController: null,
186
- streamEventsEmitter: null,
187
- initialTime: undefined,
188
- autoPlay: undefined,
189
- initialPlayPerformed: null,
190
- useMseInWorker,
191
- };
192
- sendMessage(worker, {
193
- type: MainThreadMessageType.PrepareContent,
194
- value: {
195
- contentId,
196
- cmcd: this._settings.cmcd,
197
- enableRepresentationAvoidance: this._settings.enableRepresentationAvoidance,
198
- url: this._settings.url,
199
- hasText: this._hasTextBufferFeature(),
200
- transportOptions,
201
- initialVideoBitrate,
202
- initialAudioBitrate,
203
- manifestRetryOptions: {
204
- ...this._settings.manifestRequestSettings,
205
- lowLatencyMode: this._settings.lowLatencyMode,
206
- },
207
- segmentRetryOptions: this._settings.segmentRequestOptions,
208
- useMseInWorker,
209
- },
210
- });
211
- this._initCanceller.signal.register(() => {
212
- sendMessage(worker, {
213
- type: MainThreadMessageType.StopContent,
214
- contentId,
215
- value: null,
216
- });
217
- });
218
- if (this._initCanceller.isUsed()) {
219
- return;
220
- }
221
- this._queuedWorkerMessages = [];
222
- log.debug("Init", "addEventListener prepare buffering worker messages");
223
- const onmessage = (evt: MessageEvent): void => {
224
- const msgData = evt.data as unknown as IWorkerMessage;
225
- const type = msgData.type;
226
- switch (type) {
227
- case WorkerMessageType.LogMessage: {
228
- const formatted: IAcceptedLogValue[] = msgData.value.logs.map((l) => {
229
- switch (typeof l) {
230
- case "string":
231
- case "number":
232
- case "boolean":
233
- case "undefined":
234
- return l;
235
- case "object":
236
- if (l === null) {
237
- return null;
238
- }
239
- return formatSentLogObject(l);
240
- default:
241
- assertUnreachable(l);
242
- }
243
- });
244
- switch (msgData.value.logLevel) {
245
- case "NONE":
246
- break;
247
- case "ERROR":
248
- log.error(msgData.value.namespace, ...formatted);
249
- break;
250
- case "WARNING":
251
- log.warn(msgData.value.namespace, ...formatted);
252
- break;
253
- case "INFO":
254
- log.info(msgData.value.namespace, ...formatted);
255
- break;
256
- case "DEBUG":
257
- log.debug(msgData.value.namespace, ...formatted);
258
- break;
259
- default:
260
- assertUnreachable(msgData.value.logLevel);
261
- }
262
- break;
263
- }
264
- default:
265
- if (this._queuedWorkerMessages !== null) {
266
- this._queuedWorkerMessages.push(evt);
267
- }
268
- break;
269
- }
270
- };
271
- this._settings.worker.addEventListener("message", onmessage);
272
- const onmessageerror = (_msg: MessageEvent) => {
273
- log.error("Init", "Error when receiving message from worker.");
274
- };
275
- this._settings.worker.addEventListener("messageerror", onmessageerror);
276
- this._initCanceller.signal.register(() => {
277
- log.debug("Init", "removeEventListener prepare for worker message");
278
- this._settings.worker.removeEventListener("message", onmessage);
279
- this._settings.worker.removeEventListener("messageerror", onmessageerror);
280
- });
281
-
282
- // Also bind all `SharedReference` objects:
283
-
284
- const throttleVideoBitrate =
285
- adaptiveOptions.throttlers.throttleBitrate.video ?? new SharedReference(Infinity);
286
- bindNumberReferencesToWorker(
287
- worker,
288
- this._initCanceller.signal,
289
- [wantedBufferAhead, "wantedBufferAhead"],
290
- [maxVideoBufferSize, "maxVideoBufferSize"],
291
- [maxBufferAhead, "maxBufferAhead"],
292
- [maxBufferBehind, "maxBufferBehind"],
293
- [throttleVideoBitrate, "throttleVideoBitrate"],
294
- );
295
-
296
- const limitVideoResolution =
297
- adaptiveOptions.throttlers.limitResolution.video ??
298
- new SharedReference<IResolutionInfo>({
299
- height: undefined,
300
- width: undefined,
301
- pixelRatio: 1,
302
- });
303
- limitVideoResolution.onUpdate(
304
- (newVal) => {
305
- sendMessage(worker, {
306
- type: MainThreadMessageType.ReferenceUpdate,
307
- value: { name: "limitVideoResolution", newVal },
308
- });
309
- },
310
- { clearSignal: this._initCanceller.signal, emitCurrentValue: true },
311
- );
312
- }
313
-
314
- /**
315
- * Update URL of the Manifest.
316
- * @param {Array.<string>|undefined} urls - URLs to reach that Manifest from
317
- * the most prioritized URL to the least prioritized URL.
318
- * @param {boolean} refreshNow - If `true` the resource in question (e.g.
319
- * DASH's MPD) will be refreshed immediately.
320
- */
321
- public updateContentUrls(urls: string[] | undefined, refreshNow: boolean): void {
322
- if (this._currentContentInfo === null) {
323
- return;
324
- }
325
- sendMessage(this._settings.worker, {
326
- type: MainThreadMessageType.ContentUrlsUpdate,
327
- contentId: this._currentContentInfo.contentId,
328
- value: { urls, refreshNow },
329
- });
330
- }
331
-
332
- /**
333
- * @param {HTMLMediaElement} mediaElement
334
- * @param {Object} playbackObserver
335
- */
336
- public start(
337
- mediaElement: IMediaElement,
338
- playbackObserver: IMediaElementPlaybackObserver,
339
- ): void {
340
- this.prepare(); // Load Manifest if not already done
341
- if (this._initCanceller.isUsed()) {
342
- return;
343
- }
344
-
345
- let textDisplayer: ITextDisplayer | null = null;
346
- if (
347
- this._settings.textTrackOptions.textTrackMode === "html" &&
348
- features.htmlTextDisplayer !== null
349
- ) {
350
- assert(this._hasTextBufferFeature());
351
- textDisplayer = new features.htmlTextDisplayer(
352
- mediaElement,
353
- this._settings.textTrackOptions.textTrackElement,
354
- );
355
- } else if (features.nativeTextDisplayer !== null) {
356
- assert(this._hasTextBufferFeature());
357
- textDisplayer = new features.nativeTextDisplayer(mediaElement);
358
- } else {
359
- assert(!this._hasTextBufferFeature());
360
- }
361
- this._initCanceller.signal.register(() => {
362
- textDisplayer?.stop();
363
- });
364
-
365
- /** Translate errors coming from the media element into RxPlayer errors. */
366
- listenToMediaError(
367
- mediaElement,
368
- (error: MediaError) => this._onFatalError(error),
369
- this._initCanceller.signal,
370
- );
371
-
372
- /**
373
- * Send content protection initialization data.
374
- * TODO remove and use ContentDecryptor directly when possible.
375
- */
376
- const lastContentProtection = new SharedReference<IContentProtection | null>(null);
377
-
378
- const mediaSourceStatus = new SharedReference<MediaSourceInitializationStatus>(
379
- MediaSourceInitializationStatus.Nothing,
380
- );
381
-
382
- const { statusRef: drmInitializationStatus, contentDecryptor } =
383
- this._initializeContentDecryption(
384
- mediaElement,
385
- lastContentProtection,
386
- mediaSourceStatus,
387
- () => notifyAndStartMediaSourceReload(0, undefined, undefined),
388
- this._initCanceller.signal,
389
- );
390
- const contentInfo = this._currentContentInfo;
391
- if (contentInfo !== null) {
392
- contentInfo.contentDecryptor = contentDecryptor;
393
- }
394
-
395
- const playbackStartParams = {
396
- mediaElement,
397
- textDisplayer,
398
- playbackObserver,
399
- drmInitializationStatus,
400
- mediaSourceStatus,
401
- };
402
- mediaSourceStatus.onUpdate(
403
- (msInitStatus, stopListeningMSStatus) => {
404
- if (msInitStatus === MediaSourceInitializationStatus.Attached) {
405
- stopListeningMSStatus();
406
- this._startPlaybackIfReady(playbackStartParams);
407
- }
408
- },
409
- { clearSignal: this._initCanceller.signal, emitCurrentValue: true },
410
- );
411
- drmInitializationStatus.onUpdate(
412
- (initializationStatus, stopListeningDrm) => {
413
- if (initializationStatus.initializationState.type === "initialized") {
414
- stopListeningDrm();
415
- this._startPlaybackIfReady(playbackStartParams);
416
- }
417
- },
418
- { emitCurrentValue: true, clearSignal: this._initCanceller.signal },
419
- );
420
-
421
- /**
422
- * Reset directly (synchronously) the current `MediaSource` and signal to
423
- * the core that we did so.
424
- * @param {number} deltaPosition - Position you want to seek to after
425
- * reloading, as a delta in seconds from the last polled playing position.
426
- * @param {number|undefined} minimumPosition - If set, minimum time bound
427
- * in seconds after `deltaPosition` has been applied.
428
- * @param {number|undefined} maximumPosition - If set, minimum time bound
429
- * in seconds after `deltaPosition` has been applied.
430
- */
431
- const notifyAndStartMediaSourceReload = (
432
- deltaPosition: number,
433
- minimumPosition: number | undefined,
434
- maximumPosition: number | undefined,
435
- ): void => {
436
- const reloadingContentInfo = this._currentContentInfo;
437
- if (reloadingContentInfo === null) {
438
- log.warn("Init", "Asked to reload when no content is loaded.");
439
- return;
440
- }
441
- if (
442
- reloadingContentInfo === null ||
443
- reloadingContentInfo.mediaSourceInfo === null
444
- ) {
445
- log.warn("Init", "Asked to reload when no MediaSource is active.");
446
- return;
447
- }
448
-
449
- const mediaSourceId =
450
- reloadingContentInfo.mediaSourceInfo.type === "main"
451
- ? reloadingContentInfo.mediaSourceInfo.mediaSource.id
452
- : reloadingContentInfo.mediaSourceInfo.mediaSourceId;
453
- sendMessage(this._settings.worker, {
454
- type: MainThreadMessageType.MediaSourceReload,
455
- mediaSourceId,
456
- value: null,
457
- });
458
- reloadMediaSource(deltaPosition, minimumPosition, maximumPosition);
459
- };
460
-
461
- /**
462
- * Reset directly (synchronously) the current `MediaSource`.
463
- *
464
- * It is assumed that `core` already knows about this action. If not, call
465
- * `notifyAndStartMediaSourceReload` instead.
466
- * @param {number} deltaPosition - Position you want to seek to after
467
- * reloading, as a delta in seconds from the last polled playing position.
468
- * @param {number|undefined} minimumPosition - If set, minimum time bound
469
- * in seconds after `deltaPosition` has been applied.
470
- * @param {number|undefined} maximumPosition - If set, minimum time bound
471
- * in seconds after `deltaPosition` has been applied.
472
- */
473
- const reloadMediaSource = (
474
- deltaPosition: number,
475
- minimumPosition: number | undefined,
476
- maximumPosition: number | undefined,
477
- ): void => {
478
- const reloadingContentInfo = this._currentContentInfo;
479
- if (reloadingContentInfo === null) {
480
- log.warn("Init", "Asked to reload when no content is loaded.");
481
- return;
482
- }
483
- const lastObservation = playbackObserver.getReference().getValue();
484
- const currentPosition = lastObservation.position.getWanted();
485
- const isPaused =
486
- reloadingContentInfo.initialPlayPerformed?.getValue() === true ||
487
- reloadingContentInfo.autoPlay === undefined
488
- ? lastObservation.paused
489
- : !reloadingContentInfo.autoPlay;
490
- let position = currentPosition + deltaPosition;
491
- if (minimumPosition !== undefined) {
492
- position = Math.max(minimumPosition, position);
493
- }
494
- if (maximumPosition !== undefined) {
495
- position = Math.min(maximumPosition, position);
496
- }
497
-
498
- this._reload(
499
- mediaElement,
500
- textDisplayer,
501
- playbackObserver,
502
- mediaSourceStatus,
503
- position,
504
- !isPaused,
505
- );
506
- };
507
-
508
- const onmessage = (msg: MessageEvent) => {
509
- const msgData = msg.data as unknown as IWorkerMessage;
510
- switch (msgData.type) {
511
- case WorkerMessageType.AttachMediaSource: {
512
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
513
- return;
514
- }
515
- if (this._currentContentInfo !== null) {
516
- if (this._currentContentInfo.mediaSourceInfo?.type === "main") {
517
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
518
- }
519
- this._currentContentInfo.mediaSourceInfo = {
520
- type: "core",
521
- mediaSourceId: msgData.mediaSourceId,
522
- };
523
- }
524
-
525
- const mediaSourceLink = msgData.value;
526
- mediaSourceStatus.onUpdate(
527
- (currStatus, stopListening) => {
528
- if (currStatus === MediaSourceInitializationStatus.AttachNow) {
529
- stopListening();
530
- log.info("media", "Attaching MediaSource URL to the media element");
531
- if (mediaSourceLink.type === "handle") {
532
- mediaElement.srcObject = mediaSourceLink.value;
533
- this._currentMediaSourceCanceller.signal.register(() => {
534
- mediaElement.srcObject = null;
535
- });
536
- } else {
537
- mediaElement.src = mediaSourceLink.value;
538
- this._currentMediaSourceCanceller.signal.register(() => {
539
- resetMediaElement(mediaElement, mediaSourceLink.value);
540
- });
541
- }
542
- disableRemotePlaybackOnManagedMediaSource(
543
- mediaElement,
544
- this._currentMediaSourceCanceller.signal,
545
- );
546
- mediaSourceStatus.setValue(MediaSourceInitializationStatus.Attached);
547
- }
548
- },
549
- { emitCurrentValue: true, clearSignal: this._initCanceller.signal },
550
- );
551
- break;
552
- }
553
-
554
- case WorkerMessageType.Warning:
555
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
556
- return;
557
- }
558
- this.trigger("warning", formatWorkerError(msgData.value));
559
- break;
560
-
561
- case WorkerMessageType.Error:
562
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
563
- return;
564
- }
565
- this._onFatalError(formatWorkerError(msgData.value));
566
- break;
567
-
568
- case WorkerMessageType.CreateMediaSource:
569
- this._onCreateMediaSourceMessage(
570
- msgData,
571
- mediaElement,
572
- mediaSourceStatus,
573
- this._settings.worker,
574
- );
575
- break;
576
-
577
- case WorkerMessageType.AddSourceBuffer:
578
- {
579
- if (
580
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
581
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
582
- msgData.mediaSourceId
583
- ) {
584
- return;
585
- }
586
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
587
- mediaSource.addSourceBuffer(
588
- msgData.value.sourceBufferType,
589
- msgData.value.codec,
590
- );
591
- }
592
- break;
593
-
594
- case WorkerMessageType.SourceBufferAppend:
595
- {
596
- if (
597
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
598
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
599
- msgData.mediaSourceId
600
- ) {
601
- return;
602
- }
603
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
604
- const sourceBuffer = arrayFind(
605
- mediaSource.sourceBuffers,
606
- (s) => s.type === msgData.sourceBufferType,
607
- );
608
- if (sourceBuffer === undefined) {
609
- return;
610
- }
611
- sourceBuffer
612
- .appendBuffer(msgData.value.data, msgData.value.params)
613
- .then((buffered) => {
614
- sendMessage(this._settings.worker, {
615
- type: MainThreadMessageType.SourceBufferSuccess,
616
- mediaSourceId: mediaSource.id,
617
- sourceBufferType: sourceBuffer.type,
618
- operationId: msgData.operationId,
619
- value: { buffered },
620
- });
621
- })
622
- .catch((error) => {
623
- sendMessage(this._settings.worker, {
624
- type: MainThreadMessageType.SourceBufferError,
625
- mediaSourceId: mediaSource.id,
626
- sourceBufferType: sourceBuffer.type,
627
- operationId: msgData.operationId,
628
- value:
629
- error instanceof CancellationError
630
- ? { errorName: "CancellationError" }
631
- : formatSourceBufferError(error).serialize(),
632
- });
633
- });
634
- }
635
- break;
636
-
637
- case WorkerMessageType.SourceBufferRemove:
638
- {
639
- if (
640
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
641
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
642
- msgData.mediaSourceId
643
- ) {
644
- return;
645
- }
646
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
647
- const sourceBuffer = arrayFind(
648
- mediaSource.sourceBuffers,
649
- (s) => s.type === msgData.sourceBufferType,
650
- );
651
- if (sourceBuffer === undefined) {
652
- return;
653
- }
654
- sourceBuffer
655
- .remove(msgData.value.start, msgData.value.end)
656
- .then((buffered) => {
657
- sendMessage(this._settings.worker, {
658
- type: MainThreadMessageType.SourceBufferSuccess,
659
- mediaSourceId: mediaSource.id,
660
- sourceBufferType: sourceBuffer.type,
661
- operationId: msgData.operationId,
662
- value: { buffered },
663
- });
664
- })
665
- .catch((error) => {
666
- sendMessage(this._settings.worker, {
667
- type: MainThreadMessageType.SourceBufferError,
668
- mediaSourceId: mediaSource.id,
669
- sourceBufferType: sourceBuffer.type,
670
- operationId: msgData.operationId,
671
- value:
672
- error instanceof CancellationError
673
- ? { errorName: "CancellationError" }
674
- : formatSourceBufferError(error).serialize(),
675
- });
676
- });
677
- }
678
- break;
679
-
680
- case WorkerMessageType.AbortSourceBuffer:
681
- {
682
- if (
683
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
684
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
685
- msgData.mediaSourceId
686
- ) {
687
- return;
688
- }
689
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
690
- const sourceBuffer = arrayFind(
691
- mediaSource.sourceBuffers,
692
- (s) => s.type === msgData.sourceBufferType,
693
- );
694
- if (sourceBuffer === undefined) {
695
- return;
696
- }
697
- sourceBuffer.abort();
698
- }
699
- break;
700
-
701
- case WorkerMessageType.UpdateMediaSourceDuration:
702
- {
703
- if (
704
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
705
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
706
- msgData.mediaSourceId
707
- ) {
708
- return;
709
- }
710
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
711
- if (mediaSource?.id !== msgData.mediaSourceId) {
712
- return;
713
- }
714
- mediaSource.setDuration(msgData.value.duration, msgData.value.isRealEndKnown);
715
- }
716
- break;
717
-
718
- case WorkerMessageType.InterruptMediaSourceDurationUpdate:
719
- {
720
- if (
721
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
722
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
723
- msgData.mediaSourceId
724
- ) {
725
- return;
726
- }
727
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
728
- if (mediaSource?.id !== msgData.mediaSourceId) {
729
- return;
730
- }
731
- mediaSource.interruptDurationSetting();
732
- }
733
- break;
734
-
735
- case WorkerMessageType.EndOfStream:
736
- {
737
- if (
738
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
739
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
740
- msgData.mediaSourceId
741
- ) {
742
- return;
743
- }
744
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
745
- mediaSource.maintainEndOfStream();
746
- }
747
- break;
748
-
749
- case WorkerMessageType.InterruptEndOfStream:
750
- {
751
- if (
752
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
753
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
754
- msgData.mediaSourceId
755
- ) {
756
- return;
757
- }
758
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
759
- mediaSource.stopEndOfStream();
760
- }
761
- break;
762
-
763
- case WorkerMessageType.DisposeMediaSource:
764
- {
765
- if (
766
- this._currentContentInfo?.mediaSourceInfo?.type !== "main" ||
767
- this._currentContentInfo.mediaSourceInfo.mediaSource.id !==
768
- msgData.mediaSourceId
769
- ) {
770
- return;
771
- }
772
- const { mediaSource } = this._currentContentInfo.mediaSourceInfo;
773
- mediaSource.dispose();
774
- }
775
- break;
776
-
777
- case WorkerMessageType.NeedsBufferFlush: {
778
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
779
- return;
780
- }
781
- const lastObservation = playbackObserver.getReference().getValue();
782
- const currentTime = lastObservation.position.isAwaitingFuturePosition()
783
- ? lastObservation.position.getWanted()
784
- : mediaElement.currentTime;
785
- const relativeResumingPosition = msgData.value?.relativeResumingPosition ?? 0;
786
- const canBeApproximateSeek = Boolean(
787
- msgData.value?.relativePosHasBeenDefaulted,
788
- );
789
- let wantedSeekingTime: number;
790
-
791
- if (relativeResumingPosition === 0 && canBeApproximateSeek) {
792
- // in case relativeResumingPosition is 0, we still perform
793
- // a tiny seek to be sure that the browser will correclty reload the video.
794
- wantedSeekingTime = currentTime + 0.001;
795
- } else {
796
- wantedSeekingTime = currentTime + relativeResumingPosition;
797
- }
798
- playbackObserver.setCurrentTime(wantedSeekingTime);
799
- break;
800
- }
801
-
802
- case WorkerMessageType.ActivePeriodChanged: {
803
- if (
804
- this._currentContentInfo?.contentId !== msgData.contentId ||
805
- this._currentContentInfo.manifest === null
806
- ) {
807
- return;
808
- }
809
- const period = arrayFind(
810
- this._currentContentInfo.manifest.periods,
811
- (p) => p.id === msgData.value.periodId,
812
- );
813
- if (period !== undefined) {
814
- this.trigger("activePeriodChanged", { period });
815
- }
816
- break;
817
- }
818
-
819
- case WorkerMessageType.AdaptationChanged: {
820
- if (
821
- this._currentContentInfo?.contentId !== msgData.contentId ||
822
- this._currentContentInfo.manifest === null
823
- ) {
824
- return;
825
- }
826
- const period = arrayFind(
827
- this._currentContentInfo.manifest.periods,
828
- (p) => p.id === msgData.value.periodId,
829
- );
830
- if (period === undefined) {
831
- return;
832
- }
833
- if (msgData.value.adaptationId === null) {
834
- this.trigger("adaptationChange", {
835
- period,
836
- adaptation: null,
837
- type: msgData.value.type,
838
- });
839
- return;
840
- }
841
- const adaptations = period.adaptations[msgData.value.type] ?? [];
842
- const adaptation = arrayFind(
843
- adaptations,
844
- (a) => a.id === msgData.value.adaptationId,
845
- );
846
- if (adaptation !== undefined) {
847
- this.trigger("adaptationChange", {
848
- period,
849
- adaptation,
850
- type: msgData.value.type,
851
- });
852
- }
853
- break;
854
- }
855
-
856
- case WorkerMessageType.RepresentationChanged: {
857
- if (
858
- this._currentContentInfo?.contentId !== msgData.contentId ||
859
- this._currentContentInfo.manifest === null
860
- ) {
861
- return;
862
- }
863
- const period = arrayFind(
864
- this._currentContentInfo.manifest.periods,
865
- (p) => p.id === msgData.value.periodId,
866
- );
867
- if (period === undefined) {
868
- return;
869
- }
870
- if (msgData.value.representationId === null) {
871
- this.trigger("representationChange", {
872
- period,
873
- type: msgData.value.type,
874
- representation: null,
875
- });
876
- return;
877
- }
878
- const adaptations = period.adaptations[msgData.value.type] ?? [];
879
- const adaptation = arrayFind(
880
- adaptations,
881
- (a) => a.id === msgData.value.adaptationId,
882
- );
883
- if (adaptation === undefined) {
884
- return;
885
- }
886
- const representation = arrayFind(
887
- adaptation.representations,
888
- (r) => r.id === msgData.value.representationId,
889
- );
890
- if (representation !== undefined) {
891
- this.trigger("representationChange", {
892
- period,
893
- type: msgData.value.type,
894
- representation,
895
- });
896
- }
897
- break;
898
- }
899
-
900
- case WorkerMessageType.EncryptionDataEncountered:
901
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
902
- return;
903
- }
904
- lastContentProtection.setValue(msgData.value);
905
- break;
906
-
907
- case WorkerMessageType.ManifestReady: {
908
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
909
- return;
910
- }
911
- const manifest = msgData.value.manifest;
912
- this._currentContentInfo.manifest = manifest;
913
- this._updateCodecSupport(manifest, mediaElement);
914
- this._startPlaybackIfReady(playbackStartParams);
915
- break;
916
- }
917
-
918
- case WorkerMessageType.ManifestUpdate: {
919
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
920
- return;
921
- }
922
- const manifest = this._currentContentInfo?.manifest;
923
- if (isNullOrUndefined(manifest)) {
924
- log.error("Init", "Manifest update but no Manifest loaded");
925
- return;
926
- }
927
-
928
- replicateUpdatesOnManifestMetadata(
929
- manifest,
930
- msgData.value.manifest,
931
- msgData.value.updates,
932
- );
933
- this._currentContentInfo?.streamEventsEmitter?.onManifestUpdate(manifest);
934
-
935
- this._updateCodecSupport(manifest, mediaElement);
936
- this.trigger("manifestUpdate", msgData.value.updates);
937
- break;
938
- }
939
-
940
- case WorkerMessageType.UpdatePlaybackRate:
941
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
942
- return;
943
- }
944
- playbackObserver.setPlaybackRate(msgData.value);
945
- break;
946
-
947
- case WorkerMessageType.BitrateEstimateChange:
948
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
949
- return;
950
- }
951
- this.trigger("bitrateEstimateChange", {
952
- type: msgData.value.bufferType,
953
- bitrate: msgData.value.bitrate,
954
- });
955
- break;
956
-
957
- case WorkerMessageType.InbandEvent:
958
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
959
- return;
960
- }
961
- this.trigger("inbandEvents", msgData.value);
962
- break;
963
-
964
- case WorkerMessageType.LockedStream: {
965
- if (
966
- this._currentContentInfo?.contentId !== msgData.contentId ||
967
- this._currentContentInfo.manifest === null
968
- ) {
969
- return;
970
- }
971
- const period = arrayFind(
972
- this._currentContentInfo.manifest.periods,
973
- (p) => p.id === msgData.value.periodId,
974
- );
975
- if (period === undefined) {
976
- return;
977
- }
978
- this._currentContentInfo.rebufferingController?.onLockedStream(
979
- msgData.value.bufferType,
980
- period,
981
- );
982
- break;
983
- }
984
-
985
- case WorkerMessageType.PeriodStreamReady: {
986
- if (
987
- this._currentContentInfo?.contentId !== msgData.contentId ||
988
- this._currentContentInfo.manifest === null
989
- ) {
990
- return;
991
- }
992
- const period = arrayFind(
993
- this._currentContentInfo.manifest.periods,
994
- (p) => p.id === msgData.value.periodId,
995
- );
996
- if (period === undefined) {
997
- return;
998
- }
999
- const ref = new SharedReference<IAdaptationChoice | null | undefined>(
1000
- undefined,
1001
- );
1002
- ref.onUpdate(
1003
- (adapChoice) => {
1004
- if (this._currentContentInfo === null) {
1005
- ref.finish();
1006
- return;
1007
- }
1008
- if (!isNullOrUndefined(adapChoice)) {
1009
- adapChoice.representations.onUpdate(
1010
- (repChoice, stopListening) => {
1011
- if (this._currentContentInfo === null) {
1012
- stopListening();
1013
- return;
1014
- }
1015
- sendMessage(this._settings.worker, {
1016
- type: MainThreadMessageType.RepresentationUpdate,
1017
- contentId: this._currentContentInfo.contentId,
1018
- value: {
1019
- periodId: msgData.value.periodId,
1020
- adaptationId: adapChoice.adaptationId,
1021
- bufferType: msgData.value.bufferType,
1022
- choice: repChoice,
1023
- },
1024
- });
1025
- },
1026
- { clearSignal: this._initCanceller.signal },
1027
- );
1028
- }
1029
- sendMessage(this._settings.worker, {
1030
- type: MainThreadMessageType.TrackUpdate,
1031
- contentId: this._currentContentInfo.contentId,
1032
- value: {
1033
- periodId: msgData.value.periodId,
1034
- bufferType: msgData.value.bufferType,
1035
- choice: isNullOrUndefined(adapChoice)
1036
- ? adapChoice
1037
- : {
1038
- adaptationId: adapChoice.adaptationId,
1039
- switchingMode: adapChoice.switchingMode,
1040
- initialRepresentations: adapChoice.representations.getValue(),
1041
- relativeResumingPosition: adapChoice.relativeResumingPosition,
1042
- },
1043
- },
1044
- });
1045
- },
1046
- { clearSignal: this._initCanceller.signal },
1047
- );
1048
- this.trigger("periodStreamReady", {
1049
- period,
1050
- type: msgData.value.bufferType,
1051
- adaptationRef: ref,
1052
- });
1053
- break;
1054
- }
1055
-
1056
- case WorkerMessageType.PeriodStreamCleared: {
1057
- if (
1058
- this._currentContentInfo?.contentId !== msgData.contentId ||
1059
- this._currentContentInfo.manifest === null
1060
- ) {
1061
- return;
1062
- }
1063
- this.trigger("periodStreamCleared", {
1064
- periodId: msgData.value.periodId,
1065
- type: msgData.value.bufferType,
1066
- });
1067
- break;
1068
- }
1069
-
1070
- case WorkerMessageType.DiscontinuityUpdate: {
1071
- if (
1072
- this._currentContentInfo?.contentId !== msgData.contentId ||
1073
- this._currentContentInfo.manifest === null
1074
- ) {
1075
- return;
1076
- }
1077
- const period = arrayFind(
1078
- this._currentContentInfo.manifest.periods,
1079
- (p) => p.id === msgData.value.periodId,
1080
- );
1081
- if (period === undefined) {
1082
- log.warn("Init", "Discontinuity's Period not found", {
1083
- periodId: msgData.value.periodId,
1084
- });
1085
- return;
1086
- }
1087
- this._currentContentInfo.rebufferingController?.updateDiscontinuityInfo({
1088
- period,
1089
- bufferType: msgData.value.bufferType,
1090
- discontinuity: msgData.value.discontinuity,
1091
- position: msgData.value.position,
1092
- });
1093
- break;
1094
- }
1095
-
1096
- case WorkerMessageType.PushTextData: {
1097
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1098
- return;
1099
- }
1100
- if (textDisplayer === null) {
1101
- log.warn("text", "Received AddTextData message but no text displayer exists");
1102
- } else {
1103
- try {
1104
- const ranges = textDisplayer.pushTextData(msgData.value);
1105
- sendMessage(this._settings.worker, {
1106
- type: MainThreadMessageType.PushTextDataSuccess,
1107
- contentId: msgData.contentId,
1108
- value: { ranges },
1109
- });
1110
- } catch (err) {
1111
- const message = err instanceof Error ? err.message : "Unknown error";
1112
- sendMessage(this._settings.worker, {
1113
- type: MainThreadMessageType.PushTextDataError,
1114
- contentId: msgData.contentId,
1115
- value: { message },
1116
- });
1117
- }
1118
- }
1119
- break;
1120
- }
1121
-
1122
- case WorkerMessageType.RemoveTextData: {
1123
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1124
- return;
1125
- }
1126
- if (textDisplayer === null) {
1127
- log.warn(
1128
- "text",
1129
- "Received RemoveTextData message but no text displayer exists",
1130
- );
1131
- } else {
1132
- try {
1133
- const ranges = textDisplayer.removeBuffer(
1134
- msgData.value.start,
1135
- msgData.value.end,
1136
- );
1137
- sendMessage(this._settings.worker, {
1138
- type: MainThreadMessageType.RemoveTextDataSuccess,
1139
- contentId: msgData.contentId,
1140
- value: { ranges },
1141
- });
1142
- } catch (err) {
1143
- const message = err instanceof Error ? err.message : "Unknown error";
1144
- sendMessage(this._settings.worker, {
1145
- type: MainThreadMessageType.RemoveTextDataError,
1146
- contentId: msgData.contentId,
1147
- value: { message },
1148
- });
1149
- }
1150
- }
1151
- break;
1152
- }
1153
-
1154
- case WorkerMessageType.ResetTextDisplayer: {
1155
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1156
- return;
1157
- }
1158
- if (textDisplayer === null) {
1159
- log.warn(
1160
- "text",
1161
- "Received ResetTextDisplayer message but no text displayer exists",
1162
- );
1163
- } else {
1164
- textDisplayer.reset();
1165
- }
1166
- break;
1167
- }
1168
-
1169
- case WorkerMessageType.StopTextDisplayer: {
1170
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1171
- return;
1172
- }
1173
- if (textDisplayer === null) {
1174
- log.warn(
1175
- "text",
1176
- "Received StopTextDisplayer message but no text displayer exists",
1177
- );
1178
- } else {
1179
- textDisplayer.stop();
1180
- }
1181
- break;
1182
- }
1183
-
1184
- case WorkerMessageType.ReloadingMediaSource:
1185
- {
1186
- if (
1187
- this._currentContentInfo === null ||
1188
- this._currentContentInfo.mediaSourceInfo === null
1189
- ) {
1190
- return;
1191
- }
1192
- const mediaSourceId =
1193
- this._currentContentInfo.mediaSourceInfo.type === "main"
1194
- ? this._currentContentInfo.mediaSourceInfo.mediaSource.id
1195
- : this._currentContentInfo.mediaSourceInfo.mediaSourceId;
1196
- if (mediaSourceId !== msgData.mediaSourceId) {
1197
- return;
1198
- }
1199
- reloadMediaSource(
1200
- msgData.value.timeOffset,
1201
- msgData.value.minimumPosition,
1202
- msgData.value.maximumPosition,
1203
- );
1204
- }
1205
- break;
1206
-
1207
- case WorkerMessageType.NeedsDecipherabilityFlush:
1208
- {
1209
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1210
- return;
1211
- }
1212
-
1213
- const keySystem = getKeySystemConfiguration(mediaElement);
1214
- if (shouldReloadMediaSourceOnDecipherabilityUpdate(keySystem?.[0])) {
1215
- notifyAndStartMediaSourceReload(0, undefined, undefined);
1216
- } else {
1217
- const lastObservation = playbackObserver.getReference().getValue();
1218
-
1219
- const currentPosition = lastObservation.position.getWanted();
1220
-
1221
- // simple seek close to the current position
1222
- // to flush the buffers
1223
- if (currentPosition + 0.001 < lastObservation.duration) {
1224
- playbackObserver.setCurrentTime(mediaElement.currentTime + 0.001);
1225
- } else {
1226
- playbackObserver.setCurrentTime(currentPosition);
1227
- }
1228
- }
1229
- }
1230
- break;
1231
-
1232
- case WorkerMessageType.SegmentSinkStoreUpdate: {
1233
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1234
- return;
1235
- }
1236
- const sinkObj = this._awaitingRequests.pendingSinkMetrics.get(
1237
- msgData.value.requestId,
1238
- );
1239
- if (sinkObj !== undefined) {
1240
- sinkObj.resolve(msgData.value.segmentSinkMetrics);
1241
- } else {
1242
- log.error("Init", "Failed to send segment sink store update");
1243
- }
1244
- break;
1245
- }
1246
-
1247
- case WorkerMessageType.InitSuccess:
1248
- case WorkerMessageType.InitError:
1249
- // Should already be handled by the API
1250
- break;
1251
-
1252
- case WorkerMessageType.LogMessage:
1253
- // Already handled by prepare's handler
1254
- break;
1255
- case WorkerMessageType.ThumbnailDataResponse: {
1256
- if (this._currentContentInfo?.contentId !== msgData.contentId) {
1257
- return;
1258
- }
1259
- const tObj = this._awaitingRequests.pendingThumbnailFetching.get(
1260
- msgData.value.requestId,
1261
- );
1262
- if (tObj !== undefined) {
1263
- if (msgData.value.status === "error") {
1264
- tObj.reject(formatWorkerError(msgData.value.error));
1265
- } else {
1266
- tObj.resolve(msgData.value.data);
1267
- }
1268
- } else {
1269
- log.error("Init", "Failed to send segment sink store update");
1270
- }
1271
- break;
1272
- }
1273
- default:
1274
- assertUnreachable(msgData);
1275
- }
1276
- };
1277
-
1278
- log.debug("Init", "addEventListener for worker message");
1279
- if (this._queuedWorkerMessages !== null) {
1280
- const bufferedMessages = this._queuedWorkerMessages.slice();
1281
- log.debug("Init", "Processing buffered messages", {
1282
- ammount: bufferedMessages.length,
1283
- });
1284
- for (const message of bufferedMessages) {
1285
- onmessage(message);
1286
- }
1287
- this._queuedWorkerMessages = null;
1288
- }
1289
- this._settings.worker.addEventListener("message", onmessage);
1290
- this._initCanceller.signal.register(() => {
1291
- log.debug("Init", "removeEventListener for worker message");
1292
- this._settings.worker.removeEventListener("message", onmessage);
1293
- });
1294
- }
1295
-
1296
- public dispose(): void {
1297
- this._initCanceller.cancel();
1298
- if (this._currentContentInfo !== null) {
1299
- if (this._currentContentInfo.mediaSourceInfo?.type === "main") {
1300
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
1301
- }
1302
- this._currentContentInfo = null;
1303
- }
1304
- }
1305
-
1306
- private _onFatalError(err: unknown) {
1307
- if (this._initCanceller.isUsed()) {
1308
- return;
1309
- }
1310
- this._initCanceller.cancel();
1311
- this.trigger("error", err);
1312
- }
1313
-
1314
- private _initializeContentDecryption(
1315
- mediaElement: IMediaElement,
1316
- lastContentProtection: IReadOnlySharedReference<null | IContentProtection>,
1317
- mediaSourceStatus: SharedReference<MediaSourceInitializationStatus>,
1318
- reloadMediaSource: () => void,
1319
- cancelSignal: CancellationSignal,
1320
- ): {
1321
- statusRef: IReadOnlySharedReference<IDrmInitializationStatus>;
1322
- contentDecryptor: IContentDecryptor | null;
1323
- } {
1324
- const { keySystems } = this._settings;
1325
-
1326
- // TODO private?
1327
- const createEmeDisabledReference = (errMsg: string) => {
1328
- mediaSourceStatus.setValue(MediaSourceInitializationStatus.AttachNow);
1329
- lastContentProtection.onUpdate(
1330
- (data, stopListening) => {
1331
- if (data === null) {
1332
- // initial value
1333
- return;
1334
- }
1335
- stopListening();
1336
- const err = new EncryptedMediaError("MEDIA_IS_ENCRYPTED_ERROR", errMsg, {
1337
- keyStatuses: undefined,
1338
- keySystemConfiguration: undefined,
1339
- keySystem: undefined,
1340
- });
1341
- this._onFatalError(err);
1342
- },
1343
- { clearSignal: cancelSignal },
1344
- );
1345
- const ref = new SharedReference({
1346
- initializationState: {
1347
- type: "initialized" as const,
1348
- value: null,
1349
- },
1350
- contentDecryptor: null,
1351
- drmSystemId: undefined,
1352
- });
1353
- ref.finish(); // We know that no new value will be triggered
1354
- return { statusRef: ref, contentDecryptor: null };
1355
- };
1356
-
1357
- if (keySystems.length === 0) {
1358
- return createEmeDisabledReference("No `keySystems` option given.");
1359
- } else if (features.decrypt === null) {
1360
- return createEmeDisabledReference("EME feature not activated.");
1361
- }
1362
-
1363
- const ContentDecryptor = features.decrypt;
1364
- const emeApi = mediaElement.FORCED_EME_API ?? getEmeApiImplementation("auto");
1365
- if (emeApi === null) {
1366
- return createEmeDisabledReference("EME API not available on the current page.");
1367
- }
1368
- log.debug("Init", "Creating ContentDecryptor");
1369
- const contentDecryptor = new ContentDecryptor(emeApi, mediaElement, keySystems);
1370
- const drmStatusRef = new SharedReference<IDrmInitializationStatus>(
1371
- {
1372
- initializationState: { type: "uninitialized", value: null },
1373
- drmSystemId: undefined,
1374
- },
1375
- cancelSignal,
1376
- );
1377
-
1378
- const updateCodecSupportOnStateChange = (state: ContentDecryptorState) => {
1379
- if (state > ContentDecryptorState.Initializing) {
1380
- const manifest = this._currentContentInfo?.manifest;
1381
- if (isNullOrUndefined(manifest)) {
1382
- return;
1383
- }
1384
- this._updateCodecSupport(manifest, mediaElement);
1385
- contentDecryptor.removeEventListener(
1386
- "stateChange",
1387
- updateCodecSupportOnStateChange,
1388
- );
1389
- }
1390
- };
1391
- contentDecryptor.addEventListener("stateChange", updateCodecSupportOnStateChange);
1392
-
1393
- contentDecryptor.addEventListener("keyIdsCompatibilityUpdate", (updates) => {
1394
- if (
1395
- this._currentContentInfo === null ||
1396
- this._currentContentInfo.manifest === null
1397
- ) {
1398
- return;
1399
- }
1400
- const manUpdates = updateDecipherabilityFromKeyIds(
1401
- this._currentContentInfo.manifest,
1402
- updates,
1403
- );
1404
- if (
1405
- mayMediaElementFailOnUndecipherableData() &&
1406
- manUpdates.some((e) => e.representation.decipherable !== true)
1407
- ) {
1408
- reloadMediaSource();
1409
- } else {
1410
- sendMessage(this._settings.worker, {
1411
- type: MainThreadMessageType.DecipherabilityStatusUpdate,
1412
- contentId: this._currentContentInfo.contentId,
1413
- value: manUpdates.map((s) => ({
1414
- representationUniqueId: s.representation.uniqueId,
1415
- decipherable: s.representation.decipherable,
1416
- })),
1417
- });
1418
- }
1419
- this.trigger("decipherabilityUpdate", manUpdates);
1420
- });
1421
- contentDecryptor.addEventListener("blackListProtectionData", (protData) => {
1422
- if (
1423
- this._currentContentInfo === null ||
1424
- this._currentContentInfo.manifest === null
1425
- ) {
1426
- return;
1427
- }
1428
- const manUpdates = updateDecipherabilityFromProtectionData(
1429
- this._currentContentInfo.manifest,
1430
- protData,
1431
- );
1432
- if (
1433
- mayMediaElementFailOnUndecipherableData() &&
1434
- manUpdates.some((e) => e.representation.decipherable !== true)
1435
- ) {
1436
- reloadMediaSource();
1437
- } else {
1438
- sendMessage(this._settings.worker, {
1439
- type: MainThreadMessageType.DecipherabilityStatusUpdate,
1440
- contentId: this._currentContentInfo.contentId,
1441
- value: manUpdates.map((s) => ({
1442
- representationUniqueId: s.representation.uniqueId,
1443
- decipherable: s.representation.decipherable,
1444
- })),
1445
- });
1446
- }
1447
- this.trigger("decipherabilityUpdate", manUpdates);
1448
- });
1449
- contentDecryptor.addEventListener("stateChange", (state) => {
1450
- if (state === ContentDecryptorState.WaitingForAttachment) {
1451
- mediaSourceStatus.onUpdate(
1452
- (currStatus, stopListening) => {
1453
- if (currStatus === MediaSourceInitializationStatus.Nothing) {
1454
- mediaSourceStatus.setValue(MediaSourceInitializationStatus.AttachNow);
1455
- } else if (currStatus === MediaSourceInitializationStatus.Attached) {
1456
- stopListening();
1457
- if (state === ContentDecryptorState.WaitingForAttachment) {
1458
- contentDecryptor.attach();
1459
- }
1460
- }
1461
- },
1462
- { clearSignal: cancelSignal, emitCurrentValue: true },
1463
- );
1464
- } else if (state === ContentDecryptorState.ReadyForContent) {
1465
- drmStatusRef.setValue({
1466
- initializationState: { type: "initialized", value: null },
1467
- drmSystemId: contentDecryptor.systemId,
1468
- });
1469
- contentDecryptor.removeEventListener("stateChange");
1470
- }
1471
- });
1472
-
1473
- contentDecryptor.addEventListener("error", (error) => {
1474
- this._onFatalError(error);
1475
- });
1476
-
1477
- contentDecryptor.addEventListener("warning", (error) => {
1478
- this.trigger("warning", error);
1479
- });
1480
-
1481
- lastContentProtection.onUpdate(
1482
- (data) => {
1483
- if (data === null) {
1484
- return;
1485
- }
1486
- contentDecryptor.onInitializationData(data);
1487
- },
1488
- { clearSignal: cancelSignal },
1489
- );
1490
-
1491
- cancelSignal.register(() => {
1492
- contentDecryptor.dispose();
1493
- });
1494
-
1495
- return { statusRef: drmStatusRef, contentDecryptor };
1496
- }
1497
- /**
1498
- * Retrieves all unknown codecs from the current manifest, checks these unknown codecs
1499
- * to determine if they are supported, updates the manifest with the support
1500
- * status of these codecs, and forwards the list of supported codecs to the web worker.
1501
- * @param manifest
1502
- */
1503
- private _updateCodecSupport(manifest: IManifestMetadata, mediaElement: IMediaElement) {
1504
- try {
1505
- const updatedCodecs = updateManifestCodecSupport(
1506
- manifest,
1507
- this._currentContentInfo?.contentDecryptor ?? null,
1508
- mediaElement,
1509
- this._currentContentInfo?.useMseInWorker ?? false,
1510
- );
1511
- if (updatedCodecs.length > 0) {
1512
- sendMessage(this._settings.worker, {
1513
- type: MainThreadMessageType.CodecSupportUpdate,
1514
- value: updatedCodecs,
1515
- });
1516
- // TODO what if one day the worker updates codec support by itself?
1517
- // We wouldn't know...
1518
- this.trigger("codecSupportUpdate", null);
1519
- }
1520
- } catch (err) {
1521
- this._onFatalError(err);
1522
- }
1523
- }
1524
-
1525
- private _hasTextBufferFeature(): boolean {
1526
- return (
1527
- (this._settings.textTrackOptions.textTrackMode === "html" &&
1528
- features.htmlTextDisplayer !== null) ||
1529
- features.nativeTextDisplayer !== null
1530
- );
1531
- }
1532
-
1533
- private _reload(
1534
- mediaElement: IMediaElement,
1535
- textDisplayer: ITextDisplayer | null,
1536
- playbackObserver: IMediaElementPlaybackObserver,
1537
- mediaSourceStatus: SharedReference<MediaSourceInitializationStatus>,
1538
- position: number,
1539
- autoPlay: boolean,
1540
- ) {
1541
- this._currentMediaSourceCanceller.cancel();
1542
- this._currentMediaSourceCanceller = new TaskCanceller();
1543
- this._currentMediaSourceCanceller.linkToSignal(this._initCanceller.signal);
1544
- mediaSourceStatus.setValue(MediaSourceInitializationStatus.AttachNow);
1545
- this.trigger("reloadingMediaSource", { position, autoPlay });
1546
-
1547
- mediaSourceStatus.onUpdate(
1548
- (status, stopListeningMSStatusUpdates) => {
1549
- if (status !== MediaSourceInitializationStatus.Attached) {
1550
- return;
1551
- }
1552
- stopListeningMSStatusUpdates();
1553
- const corePlaybackObserver = this._setUpModulesOnNewMediaSource(
1554
- {
1555
- initialTime: position,
1556
- autoPlay,
1557
- mediaElement,
1558
- textDisplayer,
1559
- playbackObserver,
1560
- },
1561
- this._currentMediaSourceCanceller.signal,
1562
- );
1563
-
1564
- if (
1565
- !this._currentMediaSourceCanceller.isUsed() &&
1566
- corePlaybackObserver !== null &&
1567
- this._currentContentInfo !== null
1568
- ) {
1569
- const contentId = this._currentContentInfo.contentId;
1570
- corePlaybackObserver.listen(
1571
- (obs) => {
1572
- sendMessage(this._settings.worker, {
1573
- type: MainThreadMessageType.PlaybackObservation,
1574
- contentId,
1575
- value: objectAssign(obs, {
1576
- position: obs.position.serialize(),
1577
- }),
1578
- });
1579
- },
1580
- {
1581
- includeLastObservation: true,
1582
- clearSignal: this._currentMediaSourceCanceller.signal,
1583
- },
1584
- );
1585
- }
1586
- },
1587
- {
1588
- clearSignal: this._currentMediaSourceCanceller.signal,
1589
- emitCurrentValue: true,
1590
- },
1591
- );
1592
- }
1593
-
1594
- /**
1595
- * Start-up modules and mechanisms (initial seek, auto-play etc.) needed each
1596
- * time a content is loaded AND re-loaded on a `HTMLMediaElement`, when the
1597
- * manifest is known.
1598
- *
1599
- * Note that this does not include reacting to incoming worker messages nor
1600
- * sending them, those actions have to be handled separately.
1601
- *
1602
- * @param {Object} parameters
1603
- * @param {Object} cancelSignal
1604
- * @returns {Object|null} - Playback Observer created for this content. `null`
1605
- * only if playback initialization failed (most likely because it has been
1606
- * cancelled).
1607
- */
1608
- private _setUpModulesOnNewMediaSource(
1609
- parameters: {
1610
- initialTime: number;
1611
- autoPlay: boolean;
1612
- mediaElement: IMediaElement;
1613
- textDisplayer: ITextDisplayer | null;
1614
- playbackObserver: IMediaElementPlaybackObserver;
1615
- },
1616
- cancelSignal: CancellationSignal,
1617
- ): IReadOnlyPlaybackObserver<IWorkerPlaybackObservation> | null {
1618
- if (cancelSignal.isCancelled()) {
1619
- return null;
1620
- }
1621
- if (this._currentContentInfo === null) {
1622
- log.error("Init", "Setting up modules without a contentId");
1623
- return null;
1624
- }
1625
- if (this._currentContentInfo.manifest === null) {
1626
- log.error("Init", "Setting up modules without a loaded Manifest");
1627
- return null;
1628
- }
1629
-
1630
- const { manifest, mediaSourceInfo } = this._currentContentInfo;
1631
- const { speed } = this._settings;
1632
- const { initialTime, autoPlay, mediaElement, textDisplayer, playbackObserver } =
1633
- parameters;
1634
- this._currentContentInfo.initialTime = initialTime;
1635
- this._currentContentInfo.autoPlay = autoPlay;
1636
-
1637
- const { autoPlayResult, initialPlayPerformed } = performInitialSeekAndPlay(
1638
- {
1639
- mediaElement,
1640
- playbackObserver,
1641
- startTime: initialTime,
1642
- mustAutoPlay: autoPlay,
1643
- onWarning: (err) => this.trigger("warning", err),
1644
- isDirectfile: false,
1645
- },
1646
- cancelSignal,
1647
- );
1648
- this._currentContentInfo.initialPlayPerformed = initialPlayPerformed;
1649
- const corePlaybackObserver = createCorePlaybackObserver(
1650
- playbackObserver,
1651
- {
1652
- autoPlay,
1653
- initialPlayPerformed,
1654
- manifest,
1655
- mediaSource:
1656
- mediaSourceInfo?.type === "main" ? mediaSourceInfo.mediaSource : null,
1657
- speed,
1658
- textDisplayer,
1659
- },
1660
- cancelSignal,
1661
- );
1662
-
1663
- if (cancelSignal.isCancelled()) {
1664
- return null;
1665
- }
1666
-
1667
- /**
1668
- * Class trying to avoid various stalling situations, emitting "stalled"
1669
- * events when it cannot, as well as "unstalled" events when it get out of one.
1670
- */
1671
- const rebufferingController = new RebufferingController(
1672
- playbackObserver,
1673
- manifest,
1674
- speed,
1675
- );
1676
- rebufferingController.addEventListener("stalled", (evt) =>
1677
- this.trigger("stalled", evt),
1678
- );
1679
- rebufferingController.addEventListener("unstalled", () =>
1680
- this.trigger("unstalled", null),
1681
- );
1682
- rebufferingController.addEventListener("warning", (err) =>
1683
- this.trigger("warning", err),
1684
- );
1685
- cancelSignal.register(() => {
1686
- rebufferingController.destroy();
1687
- });
1688
- rebufferingController.start();
1689
- this._currentContentInfo.rebufferingController = rebufferingController;
1690
-
1691
- const currentContentInfo = this._currentContentInfo;
1692
- initialPlayPerformed.onUpdate(
1693
- (isPerformed, stopListening) => {
1694
- if (isPerformed) {
1695
- stopListening();
1696
- const streamEventsEmitter = new StreamEventsEmitter(manifest, playbackObserver);
1697
- currentContentInfo.streamEventsEmitter = streamEventsEmitter;
1698
- streamEventsEmitter.addEventListener(
1699
- "event",
1700
- (payload) => {
1701
- this.trigger("streamEvent", payload);
1702
- },
1703
- cancelSignal,
1704
- );
1705
- streamEventsEmitter.addEventListener(
1706
- "eventSkip",
1707
- (payload) => {
1708
- this.trigger("streamEventSkip", payload);
1709
- },
1710
- cancelSignal,
1711
- );
1712
- streamEventsEmitter.start();
1713
- cancelSignal.register(() => {
1714
- streamEventsEmitter.stop();
1715
- });
1716
- }
1717
- },
1718
- { clearSignal: cancelSignal, emitCurrentValue: true },
1719
- );
1720
-
1721
- const _getSegmentSinkMetrics = async (): Promise<ISegmentSinkMetrics | undefined> => {
1722
- this._awaitingRequests.nextRequestId++;
1723
- const requestId = this._awaitingRequests.nextRequestId;
1724
- sendMessage(this._settings.worker, {
1725
- type: MainThreadMessageType.PullSegmentSinkStoreInfos,
1726
- value: { requestId },
1727
- });
1728
- return new Promise((resolve, reject) => {
1729
- const rejectFn = (err: CancellationError) => {
1730
- cancelSignal.deregister(rejectFn);
1731
- this._awaitingRequests.pendingSinkMetrics.delete(requestId);
1732
- return reject(err);
1733
- };
1734
- this._awaitingRequests.pendingSinkMetrics.set(requestId, {
1735
- resolve: (value: ISegmentSinkMetrics | undefined) => {
1736
- cancelSignal.deregister(rejectFn);
1737
- this._awaitingRequests.pendingSinkMetrics.delete(requestId);
1738
- resolve(value);
1739
- },
1740
- });
1741
- cancelSignal.register(rejectFn);
1742
- });
1743
- };
1744
- const _getThumbnailsData = async (
1745
- periodId: string,
1746
- thumbnailTrackId: string,
1747
- time: number,
1748
- ): Promise<IThumbnailResponse> => {
1749
- if (this._currentContentInfo === null) {
1750
- return Promise.reject(new Error("Cannot fetch thumbnails: No content loaded."));
1751
- }
1752
- this._awaitingRequests.nextRequestId++;
1753
- const requestId = this._awaitingRequests.nextRequestId;
1754
- sendMessage(this._settings.worker, {
1755
- type: MainThreadMessageType.ThumbnailDataRequest,
1756
- contentId: this._currentContentInfo.contentId,
1757
- value: { requestId, periodId, thumbnailTrackId, time },
1758
- });
1759
-
1760
- return new Promise((resolve, reject) => {
1761
- const rejectFn = (err: CancellationError) => {
1762
- cleanUp();
1763
- reject(err);
1764
- };
1765
- const cleanUp = () => {
1766
- cancelSignal.deregister(rejectFn);
1767
- this._awaitingRequests.pendingThumbnailFetching.delete(requestId);
1768
- };
1769
-
1770
- this._awaitingRequests.pendingThumbnailFetching.set(requestId, {
1771
- resolve: (value: IThumbnailResponse) => {
1772
- cleanUp();
1773
- resolve(value);
1774
- },
1775
- reject: (value: unknown) => {
1776
- cleanUp();
1777
- reject(value);
1778
- },
1779
- });
1780
- cancelSignal.register(rejectFn);
1781
- });
1782
- };
1783
- /**
1784
- * Emit a "loaded" events once the initial play has been performed and the
1785
- * media can begin playback.
1786
- * Also emits warning events if issues arise when doing so.
1787
- */
1788
- autoPlayResult
1789
- .then(() => {
1790
- getLoadedReference(playbackObserver, false, cancelSignal).onUpdate(
1791
- (isLoaded, stopListening) => {
1792
- if (isLoaded) {
1793
- stopListening();
1794
- this.trigger("loaded", {
1795
- getSegmentSinkMetrics: _getSegmentSinkMetrics,
1796
- getThumbnailData: _getThumbnailsData,
1797
- });
1798
- }
1799
- },
1800
- { emitCurrentValue: true, clearSignal: cancelSignal },
1801
- );
1802
- })
1803
- .catch((err) => {
1804
- if (cancelSignal.isCancelled()) {
1805
- return;
1806
- }
1807
- this._onFatalError(err);
1808
- });
1809
-
1810
- return corePlaybackObserver;
1811
- }
1812
-
1813
- /**
1814
- * Initialize content playback if and only if those conditions are filled:
1815
- * - The Manifest is fetched and stored in `this._currentContentInfo`.
1816
- * - `drmInitializationStatus` indicates that DRM matters are initialized.
1817
- * - `mediaSourceStatus` indicates that the MediaSource is attached to the
1818
- * `mediaElement`.
1819
- *
1820
- * In other cases, this method will do nothing.
1821
- *
1822
- * To call when any of those conditions might become `true`, to start-up
1823
- * playback.
1824
- *
1825
- * @param {Object} parameters
1826
- * @returns {boolean} - Returns `true` if all conditions where met for
1827
- * playback start.
1828
- */
1829
- private _startPlaybackIfReady(parameters: {
1830
- mediaElement: IMediaElement;
1831
- textDisplayer: ITextDisplayer | null;
1832
- playbackObserver: IMediaElementPlaybackObserver;
1833
- drmInitializationStatus: IReadOnlySharedReference<IDrmInitializationStatus>;
1834
- mediaSourceStatus: IReadOnlySharedReference<MediaSourceInitializationStatus>;
1835
- }): boolean {
1836
- if (this._currentContentInfo === null || this._currentContentInfo.manifest === null) {
1837
- return false;
1838
- }
1839
- const drmInitStatus = parameters.drmInitializationStatus.getValue();
1840
- if (drmInitStatus.initializationState.type !== "initialized") {
1841
- return false;
1842
- }
1843
- const msInitStatus = parameters.mediaSourceStatus.getValue();
1844
- if (msInitStatus !== MediaSourceInitializationStatus.Attached) {
1845
- return false;
1846
- }
1847
-
1848
- const { contentId, manifest } = this._currentContentInfo;
1849
- log.debug("Init", "Calculating initial time");
1850
- const initialTime = getInitialTime(
1851
- manifest,
1852
- this._settings.lowLatencyMode,
1853
- this._settings.startAt,
1854
- );
1855
- log.debug("Init", "Initial time calculated", { initialTime });
1856
- const { enableFastSwitching, onCodecSwitch } = this._settings.bufferOptions;
1857
- const corePlaybackObserver = this._setUpModulesOnNewMediaSource(
1858
- {
1859
- initialTime,
1860
- autoPlay: this._settings.autoPlay,
1861
- mediaElement: parameters.mediaElement,
1862
- textDisplayer: parameters.textDisplayer,
1863
- playbackObserver: parameters.playbackObserver,
1864
- },
1865
- this._currentMediaSourceCanceller.signal,
1866
- );
1867
-
1868
- if (this._currentMediaSourceCanceller.isUsed() || corePlaybackObserver === null) {
1869
- return true;
1870
- }
1871
- const initialObservation = corePlaybackObserver.getReference().getValue();
1872
- const sentInitialObservation = objectAssign(initialObservation, {
1873
- position: initialObservation.position.serialize(),
1874
- });
1875
- sendMessage(this._settings.worker, {
1876
- type: MainThreadMessageType.StartPreparedContent,
1877
- contentId,
1878
- value: {
1879
- initialTime,
1880
- initialObservation: sentInitialObservation,
1881
- drmSystemId: drmInitStatus.drmSystemId,
1882
- enableFastSwitching,
1883
- onCodecSwitch,
1884
- },
1885
- });
1886
-
1887
- corePlaybackObserver.listen(
1888
- (obs) => {
1889
- sendMessage(this._settings.worker, {
1890
- type: MainThreadMessageType.PlaybackObservation,
1891
- contentId,
1892
- value: objectAssign(obs, { position: obs.position.serialize() }),
1893
- });
1894
- },
1895
- {
1896
- includeLastObservation: false,
1897
- clearSignal: this._currentMediaSourceCanceller.signal,
1898
- },
1899
- );
1900
- this.trigger("manifestReady", manifest);
1901
- return true;
1902
- }
1903
-
1904
- /**
1905
- * Handles Worker messages asking to create a MediaSource.
1906
- * @param {Object} msg - The worker's message received.
1907
- * @param {HTMLMediaElement} mediaElement - HTMLMediaElement on which the
1908
- * content plays.
1909
- * @param {Worker} worker - The WebWorker concerned, messages may be sent back
1910
- * to it.
1911
- */
1912
- private _onCreateMediaSourceMessage(
1913
- msg: ICreateMediaSourceWorkerMessage,
1914
- mediaElement: IMediaElement,
1915
- mediaSourceStatus: SharedReference<MediaSourceInitializationStatus>,
1916
- worker: Worker,
1917
- ): void {
1918
- if (this._currentContentInfo?.contentId !== msg.contentId) {
1919
- log.info("Init", "Ignoring MediaSource attachment due to wrong `contentId`");
1920
- } else {
1921
- const { mediaSourceId } = msg;
1922
- try {
1923
- mediaSourceStatus.onUpdate(
1924
- (currStatus, stopListening) => {
1925
- if (this._currentContentInfo === null) {
1926
- stopListening();
1927
- return;
1928
- }
1929
- if (currStatus === MediaSourceInitializationStatus.AttachNow) {
1930
- stopListening();
1931
- const mediaSource = new MainMediaSourceInterface(mediaSourceId);
1932
- if (this._currentContentInfo.mediaSourceInfo?.type === "main") {
1933
- this._currentContentInfo.mediaSourceInfo.mediaSource.dispose();
1934
- }
1935
- this._currentContentInfo.mediaSourceInfo = {
1936
- type: "main",
1937
- mediaSource,
1938
- };
1939
- mediaSource.addEventListener("mediaSourceOpen", () => {
1940
- sendMessage(worker, {
1941
- type: MainThreadMessageType.MediaSourceReadyStateChange,
1942
- mediaSourceId,
1943
- value: "open",
1944
- });
1945
- });
1946
- mediaSource.addEventListener("mediaSourceEnded", () => {
1947
- sendMessage(worker, {
1948
- type: MainThreadMessageType.MediaSourceReadyStateChange,
1949
- mediaSourceId,
1950
- value: "ended",
1951
- });
1952
- });
1953
- mediaSource.addEventListener("mediaSourceClose", () => {
1954
- sendMessage(worker, {
1955
- type: MainThreadMessageType.MediaSourceReadyStateChange,
1956
- mediaSourceId,
1957
- value: "closed",
1958
- });
1959
- });
1960
- let url: string | null = null;
1961
- if (mediaSource.handle.type === "handle") {
1962
- mediaElement.srcObject = mediaSource.handle.value;
1963
- } else {
1964
- url = URL.createObjectURL(mediaSource.handle.value);
1965
- mediaElement.src = url;
1966
- }
1967
- this._currentMediaSourceCanceller.signal.register(() => {
1968
- mediaSource.dispose();
1969
- resetMediaElement(mediaElement, url);
1970
- });
1971
- mediaSourceStatus.setValue(MediaSourceInitializationStatus.Attached);
1972
- disableRemotePlaybackOnManagedMediaSource(
1973
- mediaElement,
1974
- this._currentMediaSourceCanceller.signal,
1975
- );
1976
- }
1977
- },
1978
- {
1979
- emitCurrentValue: true,
1980
- clearSignal: this._currentMediaSourceCanceller.signal,
1981
- },
1982
- );
1983
- } catch (_err) {
1984
- const error = new OtherError(
1985
- "NONE",
1986
- "Unknown error when creating the MediaSource",
1987
- );
1988
- this._onFatalError(error);
1989
- }
1990
- }
1991
- }
1992
- }
1993
-
1994
- export interface IMultiThreadContentInitializerContentInfos {
1995
- /**
1996
- * "contentId", which is the identifier for the currently loaded content.
1997
- * Allows to ensure that the WebWorker is referencing the current content, not
1998
- * a previously stopped one.
1999
- */
2000
- contentId: string;
2001
- /**
2002
- * Current parsed Manifest.
2003
- * `null` if not fetched / parsed yet.
2004
- */
2005
- manifest: IManifestMetadata | null;
2006
- /**
2007
- * Current MediaSource linked to the content.
2008
- *
2009
- * `null` if no MediaSource is currently created for the content.
2010
- */
2011
- mediaSourceInfo:
2012
- | {
2013
- type: "main";
2014
- mediaSource: MainMediaSourceInterface;
2015
- }
2016
- | {
2017
- type: "core";
2018
- mediaSourceId: string;
2019
- }
2020
- | null;
2021
-
2022
- /**
2023
- * Current `RebufferingController` linked to the content, allowing to
2024
- * detect and handle rebuffering situations.
2025
- *
2026
- * `null` if none is currently created for the content.
2027
- */
2028
- rebufferingController: RebufferingController | null;
2029
- /**
2030
- * Current `StreamEventsEmitter` linked to the content, allowing to
2031
- * send events found in the Manifest.
2032
- *
2033
- * `null` if none is currently created for the content.
2034
- */
2035
- streamEventsEmitter: StreamEventsEmitter | null;
2036
- /**
2037
- * The initial position to seek to in seconds once the content is loadeed.
2038
- * `undefined` if unknown yet.
2039
- */
2040
- initialTime: number | undefined;
2041
- /**
2042
- * Whether to automatically play once the content is loaded.
2043
- * `undefined` if unknown yet.
2044
- */
2045
- autoPlay: boolean | undefined;
2046
- /**
2047
- * Set to `true` once the initial play (or skipping the initial play when
2048
- * autoplay is not enabled) has been done.
2049
- * Set to `false` when it hasn't been done yet.
2050
- *
2051
- * Set to `null` when those considerations are not taken yet.
2052
- */
2053
- initialPlayPerformed: IReadOnlySharedReference<boolean> | null;
2054
- /**
2055
- * Set to the initialized `ContentDecryptor` instance linked to that content.
2056
- *
2057
- * Set to `null` when those considerations are not taken.
2058
- */
2059
- contentDecryptor: IContentDecryptor | null;
2060
- /**
2061
- * If `true`, MSE API should be used in the core part of the RxPlayer (in the
2062
- * WebWorker).
2063
- * If `false`, they should be relied on on main thread.
2064
- */
2065
- useMseInWorker: boolean;
2066
- }
2067
-
2068
- /** Arguments to give to the `InitializeOnMediaSource` function. */
2069
- export interface IInitializeArguments {
2070
- /** WebWorker inside which the core code runs. */
2071
- worker: Worker;
2072
- /**
2073
- * If `true`, MSE API should be used in the core part of the RxPlayer (in the
2074
- * WebWorker).
2075
- * If `false`, they should be relied on on main thread.
2076
- *
2077
- * This might depend on both browser capabilities and preferences. It is
2078
- * assumed that the caller perform all those checks, the `ContentInitializer`
2079
- * won't check again the validity of this value.
2080
- */
2081
- useMseInWorker: boolean;
2082
- /** Options concerning the ABR logic. */
2083
- adaptiveOptions: IAdaptiveRepresentationSelectorArguments;
2084
- /** `true` if we should play when loaded. */
2085
- autoPlay: boolean;
2086
- /** Options concerning the media buffers. */
2087
- bufferOptions: {
2088
- /** Buffer "goal" at which we stop downloading new segments. */
2089
- wantedBufferAhead: IReadOnlySharedReference<number>;
2090
- /** Buffer maximum size in kiloBytes at which we stop downloading */
2091
- maxVideoBufferSize: IReadOnlySharedReference<number>;
2092
- /** Max buffer size after the current position, in seconds (we GC further up). */
2093
- maxBufferAhead: IReadOnlySharedReference<number>;
2094
- /** Max buffer size before the current position, in seconds (we GC further down). */
2095
- maxBufferBehind: IReadOnlySharedReference<number>;
2096
- /**
2097
- * Enable/Disable fastSwitching: allow to replace lower-quality segments by
2098
- * higher-quality ones to have a faster transition.
2099
- */
2100
- enableFastSwitching: boolean;
2101
- /** Behavior when a new video and/or audio codec is encountered. */
2102
- onCodecSwitch: "continue" | "reload";
2103
- };
2104
- /**
2105
- * When set to an object, enable "Common Media Client Data", or "CMCD".
2106
- */
2107
- cmcd?: ICmcdOptions | undefined;
2108
- /**
2109
- * If `true`, the RxPlayer can enable its "Representation avoidance"
2110
- * mechanism, where it avoid loading Representation that it suspect
2111
- * have issues being decoded on the current device.
2112
- */
2113
- enableRepresentationAvoidance: boolean;
2114
- /** Every encryption configuration set. */
2115
- keySystems: IKeySystemOption[];
2116
- /** `true` to play low-latency contents optimally. */
2117
- lowLatencyMode: boolean;
2118
- /** Options relative to the streaming protocol. */
2119
- transportOptions: Omit<
2120
- ITransportOptions,
2121
- "manifestLoader" | "segmentLoader" | "representationFilter"
2122
- > & {
2123
- // Unsupported features have to be disabled explicitely
2124
- // TODO support them
2125
- manifestLoader: undefined;
2126
- segmentLoader: undefined;
2127
-
2128
- // Option which has to be set as a Funtion string to work.
2129
- representationFilter: string | undefined;
2130
- };
2131
- /** Settings linked to Manifest requests. */
2132
- manifestRequestSettings: {
2133
- /** Maximum number of time a request on error will be retried. */
2134
- maxRetry: number | undefined;
2135
- /**
2136
- * Timeout after which request are aborted and, depending on other options,
2137
- * retried.
2138
- * To set to `-1` for no timeout.
2139
- * `undefined` will lead to a default, large, timeout being used.
2140
- */
2141
- requestTimeout: number | undefined;
2142
- /**
2143
- * Connection timeout, in milliseconds, after which the request is canceled
2144
- * if the responses headers has not being received.
2145
- * Do not set or set to "undefined" to disable it.
2146
- */
2147
- connectionTimeout: number | undefined;
2148
- /** Limit the frequency of Manifest updates. */
2149
- minimumManifestUpdateInterval: number;
2150
- /**
2151
- * Potential first Manifest to rely on, allowing to skip the initial Manifest
2152
- * request.
2153
- */
2154
- initialManifest: IInitialManifest | undefined;
2155
- };
2156
- /** Configuration for the segment requesting logic. */
2157
- segmentRequestOptions: {
2158
- lowLatencyMode: boolean;
2159
- /**
2160
- * Amount of time after which a request should be aborted.
2161
- * `undefined` indicates that a default value is wanted.
2162
- * `-1` indicates no timeout.
2163
- */
2164
- requestTimeout: number | undefined;
2165
- /**
2166
- * Amount of time, in milliseconds, after which a request that hasn't receive
2167
- * the headers and status code should be aborted and optionnaly retried,
2168
- * depending on the maxRetry configuration.
2169
- */
2170
- connectionTimeout: number | undefined;
2171
- /** Maximum number of time a request on error will be retried. */
2172
- maxRetry: number | undefined;
2173
- };
2174
- /** Emit the playback rate (speed) set by the user. */
2175
- speed: IReadOnlySharedReference<number>;
2176
- /** The configured starting position. */
2177
- startAt?: IInitialTimeOptions | undefined;
2178
- /** Configuration specific to the text track. */
2179
- textTrackOptions: ITextDisplayerOptions;
2180
- /** URL of the Manifest. `undefined` if unknown or not pertinent. */
2181
- url: string | undefined;
2182
- }
2183
-
2184
- function bindNumberReferencesToWorker(
2185
- worker: Worker,
2186
- cancellationSignal: CancellationSignal,
2187
- ...refs: Array<
2188
- [
2189
- IReadOnlySharedReference<number>,
2190
- (
2191
- | "wantedBufferAhead"
2192
- | "maxVideoBufferSize"
2193
- | "maxBufferBehind"
2194
- | "maxBufferAhead"
2195
- | "throttleVideoBitrate"
2196
- ),
2197
- ]
2198
- >
2199
- ): void {
2200
- for (const ref of refs) {
2201
- ref[0].onUpdate(
2202
- (newVal) => {
2203
- // NOTE: The TypeScript checks have already been made by this function's
2204
- // overload, but the body here is not aware of that.
2205
- sendMessage(worker, {
2206
- type: MainThreadMessageType.ReferenceUpdate,
2207
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
2208
- value: { name: ref[1] as any, newVal: newVal as any },
2209
- });
2210
- },
2211
- { clearSignal: cancellationSignal, emitCurrentValue: true },
2212
- );
2213
- }
2214
- }
2215
-
2216
- function formatWorkerError(sentError: ISentError): IPlayerError {
2217
- switch (sentError.name) {
2218
- case "NetworkError":
2219
- return new NetworkError(
2220
- sentError.code,
2221
- new RequestError(
2222
- sentError.baseError.url,
2223
- sentError.baseError.status,
2224
- sentError.baseError.type,
2225
- ),
2226
- );
2227
- case "MediaError":
2228
- // eslint-disable-next-line
2229
- return new MediaError(sentError.code as any, sentError.reason, {
2230
- tracks: sentError.tracks,
2231
- });
2232
- case "EncryptedMediaError":
2233
- // We assume that everything have already been checked Worker-side here
2234
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
2235
- return new EncryptedMediaError(sentError.code, sentError.reason, {
2236
- keyStatuses: sentError.keyStatuses,
2237
- keySystemConfiguration: sentError.keySystemConfiguration,
2238
- keySystem: sentError.keySystem,
2239
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2240
- } as any);
2241
- case "OtherError":
2242
- return new OtherError(sentError.code, sentError.reason);
2243
- }
2244
- }
2245
-
2246
- /** Enume allowing to state what is the current status of MediaSource initialization. */
2247
- const enum MediaSourceInitializationStatus {
2248
- /**
2249
- * The `MediaSource` is not attached to the `HTMLMediaElement` and shouldn't
2250
- * be yet.
2251
- */
2252
- Nothing,
2253
- /**
2254
- * The `MediaSource` is not yet attached to the `HTMLMediaElement` but it
2255
- * now can and should be.
2256
- *
2257
- * The purpose of this enum variant is to be set when wanting to indicate
2258
- * that `MediaSource` attachment has to be done, in code that do not have
2259
- * the capability to do so.
2260
- *
2261
- * The code that can do so would then read that value and then set this enum
2262
- * to `Attached` once the `MediaSource` is attached.
2263
- */
2264
- AttachNow,
2265
- /** The `MediaSource` is attached to the `HTMLMediaElement`. */
2266
- Attached,
2267
- }
2268
-
2269
- interface IDrmInitializationStatus {
2270
- /** Current initialization state the decryption logic is in. */
2271
- initializationState: IDecryptionInitializationState;
2272
- /**
2273
- * If set, corresponds to the hex string describing the current key system
2274
- * used.
2275
- * `undefined` if unknown or if it does not apply.
2276
- */
2277
- drmSystemId: string | undefined;
2278
- }
2279
-
2280
- /** Initialization steps to add decryption capabilities to an `HTMLMediaElement`. */
2281
- type IDecryptionInitializationState =
2282
- /**
2283
- * Decryption capabilities have not been initialized yet.
2284
- * You should wait before performing any action on the concerned
2285
- * `HTMLMediaElement` (such as linking a content / `MediaSource` to it).
2286
- */
2287
- | { type: "uninitialized"; value: null }
2288
- /**
2289
- * The `MediaSource` or media url can be linked AND segments can be pushed to
2290
- * the `HTMLMediaElement` on which decryption capabilities were wanted.
2291
- */
2292
- | {
2293
- type: "initialized";
2294
- value: null;
2295
- };
2296
-
2297
- function formatSourceBufferError(error: unknown): SourceBufferError {
2298
- if (error instanceof SourceBufferError) {
2299
- return error;
2300
- } else if (error instanceof Error) {
2301
- return new SourceBufferError(
2302
- error.name,
2303
- error.message,
2304
- error.name === "QuotaExceededError",
2305
- );
2306
- } else {
2307
- return new SourceBufferError("Error", "Unknown SourceBufferError Error", false);
2308
- }
2309
- }
2310
-
2311
- /**
2312
- * The Core might send back logs. In that situation, the message might be
2313
- * formatted slightly differently to be able to cross threads (so a
2314
- * serializable format has to be sent).
2315
- *
2316
- * This function translates that Core format to what's expected by the
2317
- * logger.
2318
- *
2319
- * @param {*} arg
2320
- * @returns {*}
2321
- */
2322
- function formatSentLogObject(arg: ISentLogValue): IAcceptedLogValue {
2323
- if (typeof arg !== "object") {
2324
- return arg;
2325
- }
2326
- if (arg?.isSerializedError === true) {
2327
- return formatWorkerError(arg as ISentError);
2328
- }
2329
- return arg as Exclude<ISentLogValue, ISentError>;
2330
- }