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