rx-player 4.5.0-dev.2026033100 → 4.5.0-dev.2026041501
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 +22 -5
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +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/compat/can_patch_out_pssh.d.ts +42 -0
- package/dist/commonjs/compat/can_patch_out_pssh.d.ts.map +1 -0
- package/dist/commonjs/compat/can_patch_out_pssh.js +53 -0
- package/dist/commonjs/compat/env_detector.d.ts +2 -0
- package/dist/commonjs/compat/env_detector.d.ts.map +1 -1
- package/dist/commonjs/compat/env_detector.js +5 -0
- package/dist/commonjs/core/adaptive/network_analyzer.d.ts +1 -2
- package/dist/commonjs/core/adaptive/network_analyzer.d.ts.map +1 -1
- package/dist/commonjs/core/adaptive/network_analyzer.js +3 -3
- package/dist/commonjs/core/adaptive/utils/representation_score_calculator.js +2 -2
- package/dist/commonjs/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/commonjs/core/cmcd/cmcd_data_builder.js +9 -3
- package/dist/commonjs/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/commonjs/core/entry/content_preparer.js +5 -7
- package/dist/commonjs/core/entry/core_entry.d.ts.map +1 -1
- package/dist/commonjs/core/entry/core_entry.js +6 -2
- package/dist/commonjs/core/entry/core_text_displayer_interface.js +3 -3
- package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.js +12 -0
- package/dist/commonjs/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
- package/dist/commonjs/core/fetchers/utils/schedule_request.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/utils/schedule_request.js +4 -3
- package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts +0 -2
- package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts.map +1 -1
- package/dist/commonjs/core/segment_sinks/garbage_collector.js +0 -3
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.js +6 -6
- package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
- package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.js +37 -40
- package/dist/commonjs/default_config.d.ts +5 -0
- package/dist/commonjs/default_config.d.ts.map +1 -1
- package/dist/commonjs/default_config.js +5 -0
- package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +23 -16
- package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/content_decryptor.js +4 -1
- package/dist/commonjs/main_thread/decrypt/session_events_listener.js +1 -1
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts +2 -0
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.js +4 -0
- package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +0 -8
- 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 +58 -50
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
- package/dist/commonjs/main_thread/render_thumbnail.d.ts.map +1 -1
- package/dist/commonjs/main_thread/render_thumbnail.js +4 -0
- package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
- package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.js +1 -0
- package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +1 -1
- package/dist/commonjs/main_thread/tracks_store/tracks_store.js +1 -1
- package/dist/{es2017/parsers/containers/isobmff/take_pssh_out.d.ts → commonjs/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
- package/dist/commonjs/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
- package/dist/commonjs/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +22 -17
- package/dist/commonjs/parsers/containers/isobmff/index.d.ts +2 -2
- package/dist/commonjs/parsers/containers/isobmff/index.d.ts.map +1 -1
- package/dist/commonjs/parsers/containers/isobmff/index.js +4 -4
- package/dist/commonjs/playback_observer/core_playback_observer.d.ts +4 -4
- package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +1 -1
- package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts +1 -2
- package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts.map +1 -1
- package/dist/commonjs/transports/dash/segment_parser.js +1 -1
- package/dist/commonjs/transports/local/segment_parser.js +1 -1
- package/dist/commonjs/utils/test-utils.d.ts +30 -0
- package/dist/commonjs/utils/test-utils.d.ts.map +1 -0
- package/dist/commonjs/utils/test-utils.js +79 -0
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- 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/compat/can_patch_out_pssh.d.ts +42 -0
- package/dist/es2017/compat/can_patch_out_pssh.d.ts.map +1 -0
- package/dist/es2017/compat/can_patch_out_pssh.js +50 -0
- package/dist/es2017/compat/env_detector.d.ts +2 -0
- package/dist/es2017/compat/env_detector.d.ts.map +1 -1
- package/dist/es2017/compat/env_detector.js +5 -0
- package/dist/es2017/core/adaptive/network_analyzer.d.ts +1 -2
- package/dist/es2017/core/adaptive/network_analyzer.d.ts.map +1 -1
- package/dist/es2017/core/adaptive/network_analyzer.js +3 -3
- package/dist/es2017/core/adaptive/utils/representation_score_calculator.js +2 -2
- package/dist/es2017/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/es2017/core/cmcd/cmcd_data_builder.js +9 -3
- package/dist/es2017/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/es2017/core/entry/content_preparer.js +3 -5
- package/dist/es2017/core/entry/core_entry.d.ts.map +1 -1
- package/dist/es2017/core/entry/core_entry.js +6 -2
- package/dist/es2017/core/entry/core_text_displayer_interface.js +3 -3
- package/dist/es2017/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/manifest/manifest_fetcher.js +12 -0
- package/dist/es2017/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
- package/dist/es2017/core/fetchers/utils/schedule_request.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/utils/schedule_request.js +2 -3
- package/dist/es2017/core/segment_sinks/garbage_collector.d.ts +0 -2
- package/dist/es2017/core/segment_sinks/garbage_collector.d.ts.map +1 -1
- package/dist/es2017/core/segment_sinks/garbage_collector.js +0 -3
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
- package/dist/es2017/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/es2017/core/stream/adaptation/adaptation_stream.js +6 -6
- package/dist/es2017/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
- package/dist/es2017/core/stream/orchestrator/stream_orchestrator.js +40 -39
- package/dist/es2017/default_config.d.ts +5 -0
- package/dist/es2017/default_config.d.ts.map +1 -1
- package/dist/es2017/default_config.js +5 -0
- package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +20 -13
- package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/content_decryptor.js +4 -1
- package/dist/es2017/main_thread/decrypt/session_events_listener.js +1 -1
- package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts +2 -0
- package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/set_server_certificate.js +4 -0
- package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +0 -8
- 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 +58 -50
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
- package/dist/es2017/main_thread/render_thumbnail.d.ts.map +1 -1
- package/dist/es2017/main_thread/render_thumbnail.js +4 -0
- package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
- package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.js +1 -0
- package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +1 -1
- package/dist/es2017/main_thread/tracks_store/tracks_store.js +1 -1
- package/dist/{commonjs/parsers/containers/isobmff/take_pssh_out.d.ts → es2017/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
- package/dist/es2017/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
- package/dist/es2017/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +21 -16
- package/dist/es2017/parsers/containers/isobmff/index.d.ts +2 -2
- package/dist/es2017/parsers/containers/isobmff/index.d.ts.map +1 -1
- package/dist/es2017/parsers/containers/isobmff/index.js +2 -2
- package/dist/es2017/playback_observer/core_playback_observer.d.ts +4 -4
- package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +1 -1
- package/dist/es2017/playback_observer/media_element_playback_observer.d.ts +1 -2
- package/dist/es2017/playback_observer/media_element_playback_observer.d.ts.map +1 -1
- package/dist/es2017/transports/dash/segment_parser.js +2 -2
- package/dist/es2017/transports/local/segment_parser.js +2 -2
- package/dist/es2017/utils/test-utils.d.ts +30 -0
- package/dist/es2017/utils/test-utils.d.ts.map +1 -0
- package/dist/es2017/utils/test-utils.js +36 -0
- package/dist/mpd-parser.wasm +0 -0
- package/dist/worker.js +6 -6
- package/package.json +4 -2
- package/src/README.md +7 -7
- package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
- package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
- package/src/compat/__tests__/can_patch_out_pssh.test.ts +40 -0
- package/src/compat/can_patch_out_pssh.ts +53 -0
- package/src/compat/env_detector.ts +4 -0
- package/src/core/README.md +1 -1
- package/src/core/adaptive/README.md +3 -3
- package/src/core/adaptive/__tests__/adaptive_representation_selector.test.ts +181 -110
- package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +229 -123
- package/src/core/adaptive/__tests__/mocks.ts +100 -0
- package/src/core/adaptive/__tests__/network_analyzer.test.ts +152 -59
- package/src/core/adaptive/network_analyzer.ts +4 -4
- package/src/core/adaptive/utils/__tests__/filter_by_bitrate.test.ts +11 -19
- package/src/core/adaptive/utils/__tests__/filter_by_resolution.test.ts +12 -12
- package/src/core/adaptive/utils/__tests__/last_estimate_storage.test.ts +25 -12
- package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +13 -9
- package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +11 -11
- package/src/core/adaptive/utils/__tests__/select_optimal_representation.test.ts +13 -23
- package/src/core/adaptive/utils/representation_score_calculator.ts +2 -2
- package/src/core/cmcd/__tests__/cmcd_data_builder.test.ts +60 -15
- package/src/core/cmcd/cmcd_data_builder.ts +9 -3
- package/src/core/entry/README.md +2 -2
- package/src/core/entry/__tests__/core_text_displayer_interface.test.ts +20 -0
- package/src/core/entry/content_preparer.ts +2 -5
- package/src/core/entry/core_entry.ts +6 -2
- package/src/core/entry/core_text_displayer_interface.ts +3 -3
- package/src/core/fetchers/manifest/__tests__/manifest_fetcher.test.ts +52 -3
- package/src/core/fetchers/manifest/manifest_fetcher.ts +12 -0
- package/src/core/fetchers/thumbnails/__tests__/thumbnail_fetcher.test.ts +70 -0
- package/src/core/fetchers/thumbnails/thumbnail_fetcher.ts +1 -1
- package/src/core/fetchers/utils/schedule_request.ts +5 -3
- package/src/core/segment_sinks/__tests__/garbage_collector.test.ts +434 -0
- package/src/core/segment_sinks/__tests__/mocks.ts +49 -0
- package/src/core/segment_sinks/garbage_collector.ts +0 -3
- package/src/core/segment_sinks/implementations/text/__tests__/text_segment_sink.test.ts +177 -0
- package/src/core/segment_sinks/implementations/text/text_segment_sink.ts +2 -2
- package/src/core/segment_sinks/inventory/__tests__/buffered_history.test.ts +215 -0
- package/src/core/segment_sinks/inventory/__tests__/segment_inventory.test.ts +448 -0
- package/src/core/stream/adaptation/__tests__/adaptation_stream.test.ts +973 -0
- package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +283 -374
- package/src/core/stream/adaptation/adaptation_stream.ts +6 -8
- package/src/core/stream/orchestrator/README.md +4 -4
- package/src/core/stream/orchestrator/__tests__/stream_orchestrator.test.ts +707 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +41 -46
- package/src/core/stream/period/utils/__tests__/get_adaptation_switch_strategy.test.ts +290 -220
- package/src/core/stream/representation/__tests__/encryption_data_notifier.test.ts +93 -63
- package/src/core/stream/representation/utils/__tests__/append_segment_to_buffer.test.ts +106 -63
- package/src/core/stream/representation/utils/__tests__/check_for_discontinuity.test.ts +179 -204
- package/src/core/stream/representation/utils/__tests__/get_segment_priority.test.ts +7 -7
- package/src/core/stream/representation/utils/__tests__/push_init_segment.test.ts +103 -60
- package/src/core/stream/representation/utils/__tests__/push_media_segment.test.ts +231 -165
- package/src/default_config.ts +6 -0
- package/src/experimental/README.md +1 -1
- package/src/features/README.md +3 -3
- package/src/main_thread/api/README.md +6 -7
- package/src/main_thread/api/public_api.ts +16 -10
- package/src/main_thread/decrypt/README.md +4 -4
- package/src/main_thread/decrypt/__tests__/__global__/content_decryptor.test.ts +135 -0
- package/src/main_thread/decrypt/__tests__/__global__/get_license.test.ts +70 -0
- package/src/main_thread/decrypt/__tests__/__global__/server_certificate.test.ts +44 -0
- package/src/main_thread/decrypt/__tests__/__global__/utils.ts +2 -2
- package/src/main_thread/decrypt/content_decryptor.ts +6 -1
- package/src/main_thread/decrypt/session_events_listener.ts +1 -1
- package/src/main_thread/decrypt/set_server_certificate.ts +5 -0
- package/src/main_thread/init/media_source_content_initializer.ts +69 -55
- package/src/main_thread/init/utils/__tests__/stream_events_emitter.test.ts +175 -0
- package/src/main_thread/init/utils/stream_events_emitter/stream_events_emitter.ts +90 -26
- package/src/main_thread/render_thumbnail.ts +4 -0
- package/src/main_thread/tracks_store/README.md +12 -0
- package/src/main_thread/tracks_store/__tests__/media_element_tracks_store.test.ts +25 -18
- package/src/main_thread/tracks_store/media_element_tracks_store.ts +1 -0
- package/src/main_thread/tracks_store/tracks_store.ts +1 -1
- package/src/manifest/classes/__tests__/mocks.ts +202 -0
- package/src/parsers/containers/isobmff/__tests__/extract_pssh.test.ts +199 -0
- package/src/parsers/containers/isobmff/{take_pssh_out.ts → extract_pssh.ts} +21 -17
- package/src/parsers/containers/isobmff/index.ts +2 -2
- package/src/parsers/manifest/dash/wasm-parser/README.md +9 -9
- package/src/playback_observer/__tests__/mocks.ts +152 -0
- package/src/playback_observer/core_playback_observer.ts +4 -4
- package/src/playback_observer/media_element_playback_observer.ts +1 -1
- package/src/tools/README.md +2 -2
- package/src/transports/README.md +5 -5
- package/src/transports/dash/segment_parser.ts +2 -2
- package/src/transports/local/segment_parser.ts +2 -2
- package/src/transports/metaplaylist/README.md +4 -4
- package/src/utils/README.md +3 -3
- package/src/utils/test-utils.ts +50 -0
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.js +0 -20
- package/dist/commonjs/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.js +0 -17
- package/dist/es2017/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
- package/src/core/adaptive/utils/__tests__/bandwith_estimator.test.ts +0 -117
- package/src/core/entry/synchronize_sinks_on_observation.ts +0 -22
|
@@ -190,7 +190,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
190
190
|
manifest: null,
|
|
191
191
|
mediaSourceInfo: null,
|
|
192
192
|
rebufferingController: null,
|
|
193
|
-
streamEventsEmitter: null,
|
|
194
193
|
initialTime: undefined,
|
|
195
194
|
autoPlay: undefined,
|
|
196
195
|
initialPlayPerformed: null,
|
|
@@ -353,21 +352,30 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
353
352
|
}
|
|
354
353
|
|
|
355
354
|
let textDisplayer: ITextDisplayer | null = null;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
355
|
+
try {
|
|
356
|
+
if (
|
|
357
|
+
this._settings.textTrackOptions.textTrackMode === "html" &&
|
|
358
|
+
features.htmlTextDisplayer !== null
|
|
359
|
+
) {
|
|
360
|
+
assert(this._hasTextBufferFeature());
|
|
361
|
+
textDisplayer = new features.htmlTextDisplayer(
|
|
362
|
+
mediaElement,
|
|
363
|
+
this._settings.textTrackOptions.textTrackElement,
|
|
364
|
+
);
|
|
365
|
+
} else if (features.nativeTextDisplayer !== null) {
|
|
366
|
+
assert(this._hasTextBufferFeature());
|
|
367
|
+
textDisplayer = new features.nativeTextDisplayer(mediaElement);
|
|
368
|
+
} else {
|
|
369
|
+
assert(!this._hasTextBufferFeature());
|
|
370
|
+
}
|
|
371
|
+
} catch (err) {
|
|
372
|
+
log.error(
|
|
373
|
+
"Init",
|
|
374
|
+
"failed to initialize text displayer",
|
|
375
|
+
err instanceof Error ? err : "Unknown Error",
|
|
364
376
|
);
|
|
365
|
-
} else if (features.nativeTextDisplayer !== null) {
|
|
366
|
-
assert(this._hasTextBufferFeature());
|
|
367
|
-
textDisplayer = new features.nativeTextDisplayer(mediaElement);
|
|
368
|
-
} else {
|
|
369
|
-
assert(!this._hasTextBufferFeature());
|
|
370
377
|
}
|
|
378
|
+
|
|
371
379
|
this._initCanceller.signal.register((err) => {
|
|
372
380
|
textDisplayer?.stop(err.reason);
|
|
373
381
|
});
|
|
@@ -402,12 +410,32 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
402
410
|
contentInfo.contentDecryptor = contentDecryptor;
|
|
403
411
|
}
|
|
404
412
|
|
|
413
|
+
const streamEventsEmitter = new StreamEventsEmitter(playbackObserver);
|
|
414
|
+
streamEventsEmitter.addEventListener(
|
|
415
|
+
"event",
|
|
416
|
+
(payload) => {
|
|
417
|
+
this.trigger("streamEvent", payload);
|
|
418
|
+
},
|
|
419
|
+
this._initCanceller.signal,
|
|
420
|
+
);
|
|
421
|
+
streamEventsEmitter.addEventListener(
|
|
422
|
+
"eventSkip",
|
|
423
|
+
(payload) => {
|
|
424
|
+
this.trigger("streamEventSkip", payload);
|
|
425
|
+
},
|
|
426
|
+
this._initCanceller.signal,
|
|
427
|
+
);
|
|
428
|
+
this._initCanceller.signal.register((err) => {
|
|
429
|
+
streamEventsEmitter.stop(err.reason);
|
|
430
|
+
});
|
|
431
|
+
|
|
405
432
|
const playbackStartParams = {
|
|
406
433
|
mediaElement,
|
|
407
434
|
textDisplayer,
|
|
408
435
|
playbackObserver,
|
|
409
436
|
drmInitializationStatus,
|
|
410
437
|
mediaSourceStatus,
|
|
438
|
+
streamEventsEmitter,
|
|
411
439
|
};
|
|
412
440
|
mediaSourceStatus.onUpdate(
|
|
413
441
|
(msInitStatus, stopListeningMSStatus) => {
|
|
@@ -510,6 +538,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
510
538
|
textDisplayer,
|
|
511
539
|
playbackObserver,
|
|
512
540
|
mediaSourceStatus,
|
|
541
|
+
streamEventsEmitter,
|
|
513
542
|
position,
|
|
514
543
|
!isPaused,
|
|
515
544
|
);
|
|
@@ -921,6 +950,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
921
950
|
return;
|
|
922
951
|
}
|
|
923
952
|
const manifest = msgData.value.manifest;
|
|
953
|
+
streamEventsEmitter.start(manifest);
|
|
924
954
|
this._currentContentInfo.manifest = manifest;
|
|
925
955
|
this._updateCodecSupport(manifest, mediaElement);
|
|
926
956
|
this._startPlaybackIfReady(playbackStartParams);
|
|
@@ -948,8 +978,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
948
978
|
msgData.value.updates,
|
|
949
979
|
);
|
|
950
980
|
}
|
|
951
|
-
|
|
952
|
-
|
|
981
|
+
streamEventsEmitter.onManifestUpdate(manifest);
|
|
953
982
|
this._updateCodecSupport(manifest, mediaElement);
|
|
954
983
|
this.trigger("manifestUpdate", msgData.value.updates);
|
|
955
984
|
break;
|
|
@@ -1558,6 +1587,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1558
1587
|
textDisplayer: ITextDisplayer | null,
|
|
1559
1588
|
playbackObserver: IMediaElementPlaybackObserver,
|
|
1560
1589
|
mediaSourceStatus: SharedReference<MediaSourceInitializationStatus>,
|
|
1590
|
+
streamEventsEmitter: StreamEventsEmitter,
|
|
1561
1591
|
position: number,
|
|
1562
1592
|
autoPlay: boolean,
|
|
1563
1593
|
) {
|
|
@@ -1565,6 +1595,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1565
1595
|
this._currentMediaSourceCanceller = new TaskCanceller("Init MediaSource");
|
|
1566
1596
|
this._currentMediaSourceCanceller.linkToSignal(this._initCanceller.signal);
|
|
1567
1597
|
mediaSourceStatus.setValue(MediaSourceInitializationStatus.AttachNow);
|
|
1598
|
+
streamEventsEmitter.pause();
|
|
1568
1599
|
this.trigger("reloadingMediaSource", { position, autoPlay });
|
|
1569
1600
|
|
|
1570
1601
|
mediaSourceStatus.onUpdate(
|
|
@@ -1580,6 +1611,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1580
1611
|
mediaElement,
|
|
1581
1612
|
textDisplayer,
|
|
1582
1613
|
playbackObserver,
|
|
1614
|
+
streamEventsEmitter,
|
|
1583
1615
|
},
|
|
1584
1616
|
this._currentMediaSourceCanceller.signal,
|
|
1585
1617
|
);
|
|
@@ -1635,6 +1667,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1635
1667
|
mediaElement: IMediaElement;
|
|
1636
1668
|
textDisplayer: ITextDisplayer | null;
|
|
1637
1669
|
playbackObserver: IMediaElementPlaybackObserver;
|
|
1670
|
+
streamEventsEmitter: StreamEventsEmitter;
|
|
1638
1671
|
},
|
|
1639
1672
|
cancelSignal: CancellationSignal,
|
|
1640
1673
|
): IReadOnlyPlaybackObserver<ICorePlaybackObservation> | null {
|
|
@@ -1652,10 +1685,17 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1652
1685
|
|
|
1653
1686
|
const { manifest, mediaSourceInfo } = this._currentContentInfo;
|
|
1654
1687
|
const { speed } = this._settings;
|
|
1655
|
-
const {
|
|
1656
|
-
|
|
1688
|
+
const {
|
|
1689
|
+
initialTime,
|
|
1690
|
+
autoPlay,
|
|
1691
|
+
mediaElement,
|
|
1692
|
+
textDisplayer,
|
|
1693
|
+
playbackObserver,
|
|
1694
|
+
streamEventsEmitter,
|
|
1695
|
+
} = parameters;
|
|
1657
1696
|
this._currentContentInfo.initialTime = initialTime;
|
|
1658
1697
|
this._currentContentInfo.autoPlay = autoPlay;
|
|
1698
|
+
streamEventsEmitter.pause(); // Only start polling events once ready to play
|
|
1659
1699
|
|
|
1660
1700
|
const { autoPlayResult, initialPlayPerformed } = performInitialSeekAndPlay(
|
|
1661
1701
|
{
|
|
@@ -1669,6 +1709,15 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1669
1709
|
cancelSignal,
|
|
1670
1710
|
);
|
|
1671
1711
|
this._currentContentInfo.initialPlayPerformed = initialPlayPerformed;
|
|
1712
|
+
initialPlayPerformed.onUpdate(
|
|
1713
|
+
(isPerformed, stopListening) => {
|
|
1714
|
+
if (isPerformed) {
|
|
1715
|
+
stopListening();
|
|
1716
|
+
streamEventsEmitter.resume(); // We can now start polling events
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
{ clearSignal: cancelSignal, emitCurrentValue: true },
|
|
1720
|
+
);
|
|
1672
1721
|
const corePlaybackObserver = createCorePlaybackObserver(
|
|
1673
1722
|
playbackObserver,
|
|
1674
1723
|
{
|
|
@@ -1711,36 +1760,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1711
1760
|
rebufferingController.start();
|
|
1712
1761
|
this._currentContentInfo.rebufferingController = rebufferingController;
|
|
1713
1762
|
|
|
1714
|
-
const currentContentInfo = this._currentContentInfo;
|
|
1715
|
-
initialPlayPerformed.onUpdate(
|
|
1716
|
-
(isPerformed, stopListening) => {
|
|
1717
|
-
if (isPerformed) {
|
|
1718
|
-
stopListening();
|
|
1719
|
-
const streamEventsEmitter = new StreamEventsEmitter(manifest, playbackObserver);
|
|
1720
|
-
currentContentInfo.streamEventsEmitter = streamEventsEmitter;
|
|
1721
|
-
streamEventsEmitter.addEventListener(
|
|
1722
|
-
"event",
|
|
1723
|
-
(payload) => {
|
|
1724
|
-
this.trigger("streamEvent", payload);
|
|
1725
|
-
},
|
|
1726
|
-
cancelSignal,
|
|
1727
|
-
);
|
|
1728
|
-
streamEventsEmitter.addEventListener(
|
|
1729
|
-
"eventSkip",
|
|
1730
|
-
(payload) => {
|
|
1731
|
-
this.trigger("streamEventSkip", payload);
|
|
1732
|
-
},
|
|
1733
|
-
cancelSignal,
|
|
1734
|
-
);
|
|
1735
|
-
streamEventsEmitter.start();
|
|
1736
|
-
cancelSignal.register((err) => {
|
|
1737
|
-
streamEventsEmitter.stop(err.reason);
|
|
1738
|
-
});
|
|
1739
|
-
}
|
|
1740
|
-
},
|
|
1741
|
-
{ clearSignal: cancelSignal, emitCurrentValue: true },
|
|
1742
|
-
);
|
|
1743
|
-
|
|
1744
1763
|
const _getSegmentSinkMetrics = async (): Promise<ISegmentSinkMetrics | undefined> => {
|
|
1745
1764
|
this._awaitingRequests.nextRequestId++;
|
|
1746
1765
|
const requestId = this._awaitingRequests.nextRequestId;
|
|
@@ -1855,6 +1874,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1855
1874
|
playbackObserver: IMediaElementPlaybackObserver;
|
|
1856
1875
|
drmInitializationStatus: IReadOnlySharedReference<IDrmInitializationStatus>;
|
|
1857
1876
|
mediaSourceStatus: IReadOnlySharedReference<MediaSourceInitializationStatus>;
|
|
1877
|
+
streamEventsEmitter: StreamEventsEmitter;
|
|
1858
1878
|
}): boolean {
|
|
1859
1879
|
if (this._currentContentInfo === null || this._currentContentInfo.manifest === null) {
|
|
1860
1880
|
return false;
|
|
@@ -1884,6 +1904,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1884
1904
|
mediaElement: parameters.mediaElement,
|
|
1885
1905
|
textDisplayer: parameters.textDisplayer,
|
|
1886
1906
|
playbackObserver: parameters.playbackObserver,
|
|
1907
|
+
streamEventsEmitter: parameters.streamEventsEmitter,
|
|
1887
1908
|
},
|
|
1888
1909
|
this._currentMediaSourceCanceller.signal,
|
|
1889
1910
|
);
|
|
@@ -2055,13 +2076,6 @@ export interface IMediaSourceContentInitializerContentInfos {
|
|
|
2055
2076
|
* `null` if none is currently created for the content.
|
|
2056
2077
|
*/
|
|
2057
2078
|
rebufferingController: RebufferingController | null;
|
|
2058
|
-
/**
|
|
2059
|
-
* Current `StreamEventsEmitter` linked to the content, allowing to
|
|
2060
|
-
* send events found in the Manifest.
|
|
2061
|
-
*
|
|
2062
|
-
* `null` if none is currently created for the content.
|
|
2063
|
-
*/
|
|
2064
|
-
streamEventsEmitter: StreamEventsEmitter | null;
|
|
2065
2079
|
/**
|
|
2066
2080
|
* The initial position to seek to in seconds once the content is loadeed.
|
|
2067
2081
|
* `undefined` if unknown yet.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { IManifestMetadata } from "../../../../manifest";
|
|
3
|
+
import type {
|
|
4
|
+
IPlaybackObservation,
|
|
5
|
+
IReadOnlyPlaybackObserver,
|
|
6
|
+
} from "../../../../playback_observer";
|
|
7
|
+
import { SeekingState } from "../../../../playback_observer";
|
|
8
|
+
import SharedReference from "../../../../utils/reference";
|
|
9
|
+
import type { CancellationSignal } from "../../../../utils/task_canceller";
|
|
10
|
+
import TaskCanceller from "../../../../utils/task_canceller";
|
|
11
|
+
import StreamEventsEmitter from "../stream_events_emitter/stream_events_emitter";
|
|
12
|
+
|
|
13
|
+
describe("init - StreamEventsEmitter", () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.useRealTimers();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should not send skipped events for positions crossed while the media is not ready", () => {
|
|
19
|
+
vi.useFakeTimers();
|
|
20
|
+
|
|
21
|
+
const observationRef = new SharedReference(generateObservation(4));
|
|
22
|
+
const playbackObserver = createPlaybackObserver(observationRef);
|
|
23
|
+
const streamEventsEmitter = new StreamEventsEmitter(playbackObserver);
|
|
24
|
+
const skippedEvents: unknown[] = [];
|
|
25
|
+
const regularEvents: unknown[] = [];
|
|
26
|
+
const stopCanceller = new TaskCanceller("test");
|
|
27
|
+
|
|
28
|
+
streamEventsEmitter.addEventListener(
|
|
29
|
+
"event",
|
|
30
|
+
(evt) => {
|
|
31
|
+
regularEvents.push(evt);
|
|
32
|
+
},
|
|
33
|
+
stopCanceller.signal,
|
|
34
|
+
);
|
|
35
|
+
streamEventsEmitter.addEventListener(
|
|
36
|
+
"eventSkip",
|
|
37
|
+
(evt) => {
|
|
38
|
+
skippedEvents.push(evt);
|
|
39
|
+
},
|
|
40
|
+
stopCanceller.signal,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
streamEventsEmitter.start(generateManifest());
|
|
44
|
+
streamEventsEmitter.pause();
|
|
45
|
+
|
|
46
|
+
observationRef.setValue(generateObservation(9));
|
|
47
|
+
vi.advanceTimersByTime(200);
|
|
48
|
+
|
|
49
|
+
streamEventsEmitter.resume();
|
|
50
|
+
observationRef.setValue(generateObservation(9.1));
|
|
51
|
+
vi.advanceTimersByTime(200);
|
|
52
|
+
|
|
53
|
+
expect(regularEvents).toHaveLength(0);
|
|
54
|
+
expect(skippedEvents).toHaveLength(0);
|
|
55
|
+
|
|
56
|
+
streamEventsEmitter.stop("test end");
|
|
57
|
+
stopCanceller.cancel("test end");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should compare from the current position immediately after resuming", () => {
|
|
61
|
+
vi.useFakeTimers();
|
|
62
|
+
|
|
63
|
+
const observationRef = new SharedReference(generateObservation(4));
|
|
64
|
+
const playbackObserver = createPlaybackObserver(observationRef);
|
|
65
|
+
const streamEventsEmitter = new StreamEventsEmitter(playbackObserver);
|
|
66
|
+
const regularEvents: unknown[] = [];
|
|
67
|
+
const stopCanceller = new TaskCanceller("test");
|
|
68
|
+
|
|
69
|
+
streamEventsEmitter.addEventListener(
|
|
70
|
+
"event",
|
|
71
|
+
(evt) => {
|
|
72
|
+
regularEvents.push(evt);
|
|
73
|
+
},
|
|
74
|
+
stopCanceller.signal,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
streamEventsEmitter.start(generateManifest());
|
|
78
|
+
streamEventsEmitter.pause();
|
|
79
|
+
|
|
80
|
+
observationRef.setValue(generateObservation(4.5));
|
|
81
|
+
streamEventsEmitter.resume();
|
|
82
|
+
observationRef.setValue(generateObservation(8.5));
|
|
83
|
+
vi.advanceTimersByTime(200);
|
|
84
|
+
|
|
85
|
+
expect(regularEvents).toHaveLength(1);
|
|
86
|
+
|
|
87
|
+
streamEventsEmitter.stop("test end");
|
|
88
|
+
stopCanceller.cancel("test end");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
function createPlaybackObserver(
|
|
93
|
+
observationRef: SharedReference<IPlaybackObservation>,
|
|
94
|
+
): IReadOnlyPlaybackObserver<IPlaybackObservation> {
|
|
95
|
+
return {
|
|
96
|
+
getCurrentTime() {
|
|
97
|
+
return observationRef.getValue().position.getPolled();
|
|
98
|
+
},
|
|
99
|
+
getPlaybackRate() {
|
|
100
|
+
return 1;
|
|
101
|
+
},
|
|
102
|
+
getReadyState() {
|
|
103
|
+
return 4;
|
|
104
|
+
},
|
|
105
|
+
getIsPaused() {
|
|
106
|
+
return false;
|
|
107
|
+
},
|
|
108
|
+
getReference() {
|
|
109
|
+
return observationRef;
|
|
110
|
+
},
|
|
111
|
+
listen(
|
|
112
|
+
cb: (observation: IPlaybackObservation, stopListening: () => void) => void,
|
|
113
|
+
options: {
|
|
114
|
+
includeLastObservation?: boolean | undefined;
|
|
115
|
+
clearSignal: CancellationSignal;
|
|
116
|
+
},
|
|
117
|
+
) {
|
|
118
|
+
observationRef.onUpdate(cb, {
|
|
119
|
+
clearSignal: options.clearSignal,
|
|
120
|
+
emitCurrentValue: options.includeLastObservation,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
deriveReadOnlyObserver() {
|
|
124
|
+
throw new Error("unused in this test");
|
|
125
|
+
},
|
|
126
|
+
} as unknown as IReadOnlyPlaybackObserver<IPlaybackObservation>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function generateManifest(): IManifestMetadata {
|
|
130
|
+
return {
|
|
131
|
+
periods: [
|
|
132
|
+
{
|
|
133
|
+
start: 0,
|
|
134
|
+
streamEvents: [
|
|
135
|
+
{
|
|
136
|
+
start: 5,
|
|
137
|
+
end: 8,
|
|
138
|
+
id: "evt",
|
|
139
|
+
data: {
|
|
140
|
+
type: "dash-event-stream",
|
|
141
|
+
value: {
|
|
142
|
+
schemeIdUri: "urn:test",
|
|
143
|
+
timescale: 1,
|
|
144
|
+
xmlData: { data: "<Event />", namespaces: [] },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
} as unknown as IManifestMetadata;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function generateObservation(currentTime: number): IPlaybackObservation {
|
|
155
|
+
return {
|
|
156
|
+
event: "timeupdate",
|
|
157
|
+
seeking: SeekingState.None,
|
|
158
|
+
rebuffering: null,
|
|
159
|
+
freezing: null,
|
|
160
|
+
duration: 100,
|
|
161
|
+
ended: false,
|
|
162
|
+
paused: false,
|
|
163
|
+
playbackRate: 1,
|
|
164
|
+
readyState: 4,
|
|
165
|
+
bufferGap: 10,
|
|
166
|
+
fullyLoaded: false,
|
|
167
|
+
currentRange: { start: 0, end: 20 },
|
|
168
|
+
buffered: {} as TimeRanges,
|
|
169
|
+
position: {
|
|
170
|
+
getPolled() {
|
|
171
|
+
return currentTime;
|
|
172
|
+
},
|
|
173
|
+
} as IPlaybackObservation["position"],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
import config from "../../../../config";
|
|
18
18
|
import type { IManifestMetadata } from "../../../../manifest";
|
|
19
|
-
import { SeekingState } from "../../../../playback_observer";
|
|
20
19
|
import type {
|
|
21
20
|
IPlaybackObservation,
|
|
22
21
|
IReadOnlyPlaybackObserver,
|
|
23
22
|
} from "../../../../playback_observer";
|
|
23
|
+
import { SeekingState } from "../../../../playback_observer";
|
|
24
24
|
import EventEmitter from "../../../../utils/event_emitter";
|
|
25
25
|
import SharedReference from "../../../../utils/reference";
|
|
26
26
|
import type { CancellationSignal } from "../../../../utils/task_canceller";
|
|
@@ -42,27 +42,34 @@ interface IStreamEventsEmitterEvent {
|
|
|
42
42
|
* Get events from manifest and emit each time an event has to be emitted
|
|
43
43
|
*/
|
|
44
44
|
export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitterEvent> {
|
|
45
|
-
|
|
45
|
+
/** Regularly emit playback metrics such as the position. */
|
|
46
46
|
private _playbackObserver: IReadOnlyPlaybackObserver<IPlaybackObservation>;
|
|
47
|
+
/** Current stream events tracked for the whole content. */
|
|
47
48
|
private _scheduledEventsRef: SharedReference<
|
|
48
49
|
Array<IStreamEventPayload | INonFiniteStreamEventPayload>
|
|
49
50
|
>;
|
|
51
|
+
/** Events currently considered active, to only emit their enter/exit once. */
|
|
50
52
|
private _eventsBeingPlayed: WeakMap<
|
|
51
53
|
IStreamEventPayload | INonFiniteStreamEventPayload,
|
|
52
54
|
true
|
|
53
55
|
>;
|
|
56
|
+
/** Whether event evaluation is temporarily suspended, e.g. during a reload. */
|
|
57
|
+
private _isPaused: boolean;
|
|
58
|
+
/** Last observation used as comparison point for the next event check. */
|
|
59
|
+
private _previousObservation: {
|
|
60
|
+
/** Media position that is being played. */
|
|
61
|
+
currentTime: number;
|
|
62
|
+
/** If `true`, we're currently seeking in the media. */
|
|
63
|
+
isSeeking: boolean;
|
|
64
|
+
} | null;
|
|
65
|
+
/** Cancels the current emitter lifecycle. */
|
|
54
66
|
private _canceller: TaskCanceller | null;
|
|
55
67
|
|
|
56
68
|
/**
|
|
57
|
-
* @param {Object} manifest
|
|
58
69
|
* @param {Object} playbackObserver
|
|
59
70
|
*/
|
|
60
|
-
constructor(
|
|
61
|
-
manifest: IManifestMetadata,
|
|
62
|
-
playbackObserver: IReadOnlyPlaybackObserver<IPlaybackObservation>,
|
|
63
|
-
) {
|
|
71
|
+
constructor(playbackObserver: IReadOnlyPlaybackObserver<IPlaybackObservation>) {
|
|
64
72
|
super();
|
|
65
|
-
this._manifest = manifest;
|
|
66
73
|
this._playbackObserver = playbackObserver;
|
|
67
74
|
this._canceller = null;
|
|
68
75
|
this._scheduledEventsRef = new SharedReference<
|
|
@@ -72,22 +79,36 @@ export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitt
|
|
|
72
79
|
IStreamEventPayload | INonFiniteStreamEventPayload,
|
|
73
80
|
true
|
|
74
81
|
>();
|
|
82
|
+
this._isPaused = true;
|
|
83
|
+
this._previousObservation = null;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Initialize the emitter for the given content manifest.
|
|
88
|
+
*
|
|
89
|
+
* The emitter starts in a paused state so the caller can resume it only once
|
|
90
|
+
* the corresponding media instance is actually ready.
|
|
91
|
+
* @param {Object} manifest
|
|
92
|
+
*/
|
|
93
|
+
public start(manifest: IManifestMetadata): void {
|
|
78
94
|
if (this._canceller !== null) {
|
|
79
95
|
return;
|
|
80
96
|
}
|
|
81
97
|
this._canceller = new TaskCanceller("StreamEventsEmitter");
|
|
82
98
|
|
|
83
99
|
const cancelSignal = this._canceller.signal;
|
|
84
|
-
|
|
100
|
+
this._isPaused = true;
|
|
101
|
+
this._previousObservation = null;
|
|
102
|
+
this._eventsBeingPlayed = new WeakMap<
|
|
103
|
+
IStreamEventPayload | INonFiniteStreamEventPayload,
|
|
104
|
+
true
|
|
105
|
+
>();
|
|
85
106
|
|
|
86
107
|
let isPollingEvents = false;
|
|
87
108
|
let cancelCurrentPolling = new TaskCanceller("StreamEventsEmitter Polling");
|
|
88
109
|
cancelCurrentPolling.linkToSignal(cancelSignal);
|
|
89
110
|
|
|
90
|
-
this._scheduledEventsRef.setValue(refreshScheduledEventsList([],
|
|
111
|
+
this._scheduledEventsRef.setValue(refreshScheduledEventsList([], manifest));
|
|
91
112
|
|
|
92
113
|
this._scheduledEventsRef.onUpdate(
|
|
93
114
|
({ length: scheduledEventsLength }) => {
|
|
@@ -103,16 +124,22 @@ export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitt
|
|
|
103
124
|
return;
|
|
104
125
|
}
|
|
105
126
|
isPollingEvents = true;
|
|
106
|
-
let oldObservation = constructObservation();
|
|
107
127
|
const checkStreamEvents = () => {
|
|
108
|
-
|
|
128
|
+
if (this._isPaused) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const newObservation = this._constructObservation();
|
|
132
|
+
if (this._previousObservation === null) {
|
|
133
|
+
this._previousObservation = newObservation;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
109
136
|
this._emitStreamEvents(
|
|
110
137
|
this._scheduledEventsRef.getValue(),
|
|
111
|
-
|
|
138
|
+
this._previousObservation,
|
|
112
139
|
newObservation,
|
|
113
140
|
cancelCurrentPolling.signal,
|
|
114
141
|
);
|
|
115
|
-
|
|
142
|
+
this._previousObservation = newObservation;
|
|
116
143
|
};
|
|
117
144
|
|
|
118
145
|
const { STREAM_EVENT_EMITTER_POLL_INTERVAL } = config.getCurrent();
|
|
@@ -120,7 +147,7 @@ export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitt
|
|
|
120
147
|
checkStreamEvents,
|
|
121
148
|
STREAM_EVENT_EMITTER_POLL_INTERVAL,
|
|
122
149
|
);
|
|
123
|
-
|
|
150
|
+
this._playbackObserver.listen(checkStreamEvents, {
|
|
124
151
|
includeLastObservation: false,
|
|
125
152
|
clearSignal: cancelCurrentPolling.signal,
|
|
126
153
|
});
|
|
@@ -128,20 +155,34 @@ export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitt
|
|
|
128
155
|
cancelCurrentPolling.signal.register(() => {
|
|
129
156
|
clearInterval(intervalId);
|
|
130
157
|
});
|
|
131
|
-
|
|
132
|
-
function constructObservation() {
|
|
133
|
-
const lastObservation = playbackObserver.getReference().getValue();
|
|
134
|
-
const currentTime =
|
|
135
|
-
playbackObserver.getCurrentTime() ??
|
|
136
|
-
playbackObserver.getReference().getValue().position.getPolled();
|
|
137
|
-
const isSeeking = lastObservation.seeking !== SeekingState.None;
|
|
138
|
-
return { currentTime, isSeeking };
|
|
139
|
-
}
|
|
140
158
|
},
|
|
141
159
|
{ emitCurrentValue: true, clearSignal: cancelSignal },
|
|
142
160
|
);
|
|
143
161
|
}
|
|
144
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Suspend event evaluation until `resume` has been called.
|
|
165
|
+
*
|
|
166
|
+
* To use when playback metrics should be temporarily ignored, for example
|
|
167
|
+
* while reloading a content.
|
|
168
|
+
*/
|
|
169
|
+
public pause(): void {
|
|
170
|
+
this._isPaused = true;
|
|
171
|
+
this._previousObservation = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resume event evaluation from the current playback state.
|
|
176
|
+
*/
|
|
177
|
+
public resume(): void {
|
|
178
|
+
this._isPaused = false;
|
|
179
|
+
|
|
180
|
+
// Take a snapshot right now to avoid comparing a post-reload position
|
|
181
|
+
// with a stale pre-reload observation.
|
|
182
|
+
this._previousObservation = this._constructObservation();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Refresh the tracked stream-event list after a manifest update. */
|
|
145
186
|
public onManifestUpdate(man: IManifestMetadata) {
|
|
146
187
|
const prev = this._scheduledEventsRef.getValue();
|
|
147
188
|
this._scheduledEventsRef.setValue(refreshScheduledEventsList(prev, man));
|
|
@@ -156,12 +197,35 @@ export default class StreamEventsEmitter extends EventEmitter<IStreamEventsEmitt
|
|
|
156
197
|
if (this._canceller !== null) {
|
|
157
198
|
this._canceller.cancel(reason ?? "StreamEventsEmitter stop");
|
|
158
199
|
this._canceller = null;
|
|
200
|
+
this._previousObservation = null;
|
|
201
|
+
this._eventsBeingPlayed = new WeakMap<
|
|
202
|
+
IStreamEventPayload | INonFiniteStreamEventPayload,
|
|
203
|
+
true
|
|
204
|
+
>();
|
|
159
205
|
}
|
|
160
206
|
}
|
|
161
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Construct observation object relied on by the `StreamEventsEmitted` by
|
|
210
|
+
* generating it from this instance's `PlaybackObserver`.
|
|
211
|
+
*/
|
|
212
|
+
private _constructObservation(): {
|
|
213
|
+
/** Current media position that is being played. */
|
|
214
|
+
currentTime: number;
|
|
215
|
+
/** If `true`, we're currently seeking in the media. */
|
|
216
|
+
isSeeking: boolean;
|
|
217
|
+
} {
|
|
218
|
+
const lastObservation = this._playbackObserver.getReference().getValue();
|
|
219
|
+
const currentTime =
|
|
220
|
+
this._playbackObserver.getCurrentTime() ??
|
|
221
|
+
this._playbackObserver.getReference().getValue().position.getPolled();
|
|
222
|
+
const isSeeking = lastObservation.seeking !== SeekingState.None;
|
|
223
|
+
return { currentTime, isSeeking };
|
|
224
|
+
}
|
|
225
|
+
|
|
162
226
|
/**
|
|
163
227
|
* Examine playback situation from playback observations to emit stream events and
|
|
164
|
-
* prepare
|
|
228
|
+
* prepare `onExit` callbacks if needed.
|
|
165
229
|
* @param {Array.<Object>} scheduledEvents
|
|
166
230
|
* @param {Object} oldObservation
|
|
167
231
|
* @param {Object} newObservation
|
|
@@ -218,6 +218,10 @@ export default async function renderThumbnail(
|
|
|
218
218
|
);
|
|
219
219
|
throw error;
|
|
220
220
|
}
|
|
221
|
+
if (srcError instanceof ThumbnailRenderingError) {
|
|
222
|
+
onFinished();
|
|
223
|
+
throw srcError;
|
|
224
|
+
}
|
|
221
225
|
const formattedErr = formatError(srcError, {
|
|
222
226
|
defaultCode: "NONE",
|
|
223
227
|
defaultReason: "Unknown error",
|
|
@@ -16,6 +16,18 @@ curtains.
|
|
|
16
16
|
|
|
17
17
|
## `TracksStore`
|
|
18
18
|
|
|
19
|
+
`TracksStore` is the main implementation used for Manifest-based contents. It centralizes
|
|
20
|
+
the currently chosen tracks and qualities and exposes simple operations for the API to
|
|
21
|
+
switch them.
|
|
22
|
+
|
|
19
23
|
## `TrackDispatcher`
|
|
20
24
|
|
|
25
|
+
`TrackDispatcher` links track decisions to the underlying streaming logic for a given
|
|
26
|
+
type. It is responsible for propagating updated choices and constraints when the selected
|
|
27
|
+
track or representations change.
|
|
28
|
+
|
|
21
29
|
## `MediaElementTracksStore`
|
|
30
|
+
|
|
31
|
+
`MediaElementTracksStore` is the directfile-oriented counterpart. It relies on the media
|
|
32
|
+
element's native track APIs (`audioTracks`, `videoTracks`, `textTracks`) instead of the
|
|
33
|
+
Manifest-based track model used by `TracksStore`.
|