rx-player 4.0.0-beta.1 → 4.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +48 -168
- package/FILES.md +40 -92
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/browser_detection.d.ts +3 -1
- package/dist/_esm5.processed/compat/browser_detection.js +7 -2
- package/dist/_esm5.processed/compat/eme/load_session.js +1 -1
- package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.d.ts +21 -0
- package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.js +26 -0
- package/dist/_esm5.processed/config.d.ts +2 -0
- package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +5 -4
- package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.d.ts +18 -1
- package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.js +106 -25
- package/dist/_esm5.processed/core/adaptive/guess_based_chooser.js +6 -6
- package/dist/_esm5.processed/core/adaptive/network_analyzer.js +8 -5
- package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.d.ts +19 -1
- package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.js +1 -1
- package/dist/_esm5.processed/core/api/debug/render.js +1 -1
- package/dist/_esm5.processed/core/api/playback_observer.js +1 -0
- package/dist/_esm5.processed/core/api/public_api.d.ts +54 -1
- package/dist/_esm5.processed/core/api/public_api.js +232 -35
- package/dist/_esm5.processed/core/api/track_management/media_element_tracks_store.js +10 -1
- package/dist/_esm5.processed/core/api/track_management/track_dispatcher.d.ts +13 -1
- package/dist/_esm5.processed/core/api/track_management/track_dispatcher.js +30 -15
- package/dist/_esm5.processed/core/api/track_management/tracks_store.d.ts +3 -1
- package/dist/_esm5.processed/core/api/track_management/tracks_store.js +67 -152
- package/dist/_esm5.processed/core/api/utils.d.ts +10 -0
- package/dist/_esm5.processed/core/api/utils.js +20 -0
- package/dist/_esm5.processed/core/decrypt/session_events_listener.js +7 -1
- package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +2 -0
- package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +5 -1
- package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
- package/dist/_esm5.processed/core/init/media_source_content_initializer.js +47 -10
- package/dist/_esm5.processed/core/init/types.d.ts +9 -1
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.d.ts +28 -1
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +22 -9
- package/dist/_esm5.processed/core/init/utils/media_source_duration_updater.d.ts +58 -0
- package/dist/_esm5.processed/core/init/utils/{media_duration_updater.js → media_source_duration_updater.js} +84 -87
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.d.ts +36 -2
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +82 -2
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -7
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +31 -40
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +8 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +12 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +8 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +12 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +11 -4
- package/dist/_esm5.processed/core/segment_buffers/index.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.d.ts +47 -0
- package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.js +70 -0
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +15 -8
- package/dist/_esm5.processed/core/stream/period/period_stream.js +1 -1
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +22 -13
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.d.ts +4 -2
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +2 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.d.ts +3 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.js +8 -8
- package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.js +2 -3
- package/dist/_esm5.processed/default_config.d.ts +25 -0
- package/dist/_esm5.processed/default_config.js +27 -2
- package/dist/_esm5.processed/errors/index.d.ts +2 -2
- package/dist/_esm5.processed/errors/media_error.d.ts +23 -1
- package/dist/_esm5.processed/errors/media_error.js +18 -5
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +1 -1
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +8 -7
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +17 -9
- package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/index.js +0 -2
- package/dist/_esm5.processed/manifest/adaptation.d.ts +21 -2
- package/dist/_esm5.processed/manifest/adaptation.js +76 -1
- package/dist/_esm5.processed/manifest/manifest.js +1 -1
- package/dist/_esm5.processed/manifest/period.js +2 -2
- package/dist/_esm5.processed/manifest/representation.d.ts +33 -2
- package/dist/_esm5.processed/manifest/representation.js +21 -0
- package/dist/_esm5.processed/manifest/utils.js +1 -3
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.d.ts +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.js +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +1 -0
- package/dist/_esm5.processed/public_types.d.ts +13 -3
- package/dist/_esm5.processed/tools/TextTrackRenderer/text_track_renderer.js +1 -1
- package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.d.ts +4 -6
- package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.js +4 -6
- package/dist/_esm5.processed/utils/is_null_or_undefined.d.ts +1 -1
- package/dist/_esm5.processed/utils/is_null_or_undefined.js +1 -1
- package/dist/mpd-parser.wasm +0 -0
- package/dist/rx-player.js +4709 -4218
- package/dist/rx-player.min.js +1 -1
- package/package.json +42 -36
- package/scripts/build/generate_build.js +1 -1
- package/scripts/fast_demo_build.js +4 -3
- package/scripts/generate_full_demo.js +1 -1
- package/sonar-project.properties +1 -1
- package/src/compat/browser_detection.ts +7 -1
- package/src/compat/eme/load_session.ts +1 -1
- package/src/compat/has_issues_with_high_media_source_duration.ts +27 -0
- package/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +147 -48
- package/src/core/adaptive/adaptive_representation_selector.ts +7 -4
- package/src/core/adaptive/buffer_based_chooser.ts +144 -26
- package/src/core/adaptive/guess_based_chooser.ts +9 -8
- package/src/core/adaptive/network_analyzer.ts +9 -4
- package/src/core/adaptive/utils/representation_score_calculator.ts +22 -2
- package/src/core/api/debug/render.ts +1 -1
- package/src/core/api/playback_observer.ts +1 -0
- package/src/core/api/public_api.ts +277 -44
- package/src/core/api/track_management/media_element_tracks_store.ts +17 -8
- package/src/core/api/track_management/track_dispatcher.ts +37 -14
- package/src/core/api/track_management/tracks_store.ts +77 -167
- package/src/core/api/utils.ts +26 -0
- package/src/core/decrypt/session_events_listener.ts +6 -1
- package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +2 -1
- package/src/core/decrypt/utils/loaded_sessions_store.ts +8 -1
- package/src/core/init/directfile_content_initializer.ts +1 -0
- package/src/core/init/media_source_content_initializer.ts +52 -9
- package/src/core/init/types.ts +9 -1
- package/src/core/init/utils/content_time_boundaries_observer.ts +46 -10
- package/src/core/init/utils/{media_duration_updater.ts → media_source_duration_updater.ts} +100 -112
- package/src/core/init/utils/rebuffering_controller.ts +114 -3
- package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +56 -55
- package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +16 -0
- package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +16 -0
- package/src/core/segment_buffers/implementations/types.ts +16 -4
- package/src/core/segment_buffers/index.ts +2 -0
- package/src/core/stream/adaptation/utils/create_representation_estimator.ts +114 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +16 -8
- package/src/core/stream/period/period_stream.ts +2 -1
- package/src/core/stream/representation/representation_stream.ts +34 -22
- package/src/core/stream/representation/utils/append_segment_to_buffer.ts +8 -3
- package/src/core/stream/representation/utils/push_init_segment.ts +11 -6
- package/src/core/stream/representation/utils/push_media_segment.ts +3 -3
- package/src/default_config.ts +29 -2
- package/src/errors/__tests__/media_error.test.ts +6 -6
- package/src/errors/index.ts +4 -1
- package/src/errors/media_error.ts +67 -1
- package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +10 -7
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +17 -6
- package/src/experimental/tools/mediaCapabilitiesProber/index.ts +0 -4
- package/src/manifest/__tests__/manifest.test.ts +7 -7
- package/src/manifest/__tests__/period.test.ts +90 -45
- package/src/manifest/adaptation.ts +89 -1
- package/src/manifest/manifest.ts +1 -1
- package/src/manifest/period.ts +4 -2
- package/src/manifest/representation.ts +67 -1
- package/src/manifest/utils.ts +1 -3
- package/src/parsers/manifest/dash/js-parser/parse_from_document.ts +1 -1
- package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +1 -0
- package/src/parsers/texttracks/ttml/parse_ttml.ts +1 -1
- package/src/public_types.ts +16 -1
- package/src/tools/TextTrackRenderer/text_track_renderer.ts +1 -1
- package/src/transports/smooth/isobmff/create_boxes.ts +4 -6
- package/src/typings/globals.d.ts +20 -20
- package/src/utils/is_null_or_undefined.ts +1 -1
- package/dist/_esm5.processed/core/init/utils/media_duration_updater.d.ts +0 -56
- package/scripts/doc-generator/construct_table_of_contents.js +0 -76
- package/scripts/doc-generator/convert_MD_to_HMTL.js +0 -26
- package/scripts/doc-generator/create_documentation.js +0 -331
- package/scripts/doc-generator/create_documentation_page.js +0 -209
- package/scripts/doc-generator/create_page.js +0 -210
- package/scripts/doc-generator/generate_header_html.js +0 -147
- package/scripts/doc-generator/generate_page_html.js +0 -115
- package/scripts/doc-generator/generate_page_list_html.js +0 -92
- package/scripts/doc-generator/generate_sidebar_html.js +0 -85
- package/scripts/doc-generator/get_search_data_for_content.js +0 -137
- package/scripts/doc-generator/index.js +0 -34
- package/scripts/doc-generator/parse_doc_configs.js +0 -327
- package/scripts/doc-generator/scripts/lunr.js +0 -10
- package/scripts/doc-generator/scripts/script.js +0 -451
- package/scripts/doc-generator/styles/code.css +0 -99
- package/scripts/doc-generator/styles/style.css +0 -835
- package/scripts/doc-generator/utils.js +0 -74
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
ILockedAudioRepresentationsSettings,
|
|
58
58
|
ILockedVideoRepresentationsSettings,
|
|
59
59
|
ITrackUpdateEventPayload,
|
|
60
|
+
IRepresentationListUpdateContext,
|
|
60
61
|
IPeriod,
|
|
61
62
|
IPeriodChangeEvent,
|
|
62
63
|
IPlayerError,
|
|
@@ -70,7 +71,10 @@ import {
|
|
|
70
71
|
IVideoTrack,
|
|
71
72
|
IVideoTrackSetting,
|
|
72
73
|
IVideoTrackSwitchingMode,
|
|
74
|
+
ITrackType,
|
|
73
75
|
} from "../../public_types";
|
|
76
|
+
import arrayFind from "../../utils/array_find";
|
|
77
|
+
import arrayIncludes from "../../utils/array_includes";
|
|
74
78
|
import assert from "../../utils/assert";
|
|
75
79
|
import assertUnreachable from "../../utils/assert_unreachable";
|
|
76
80
|
import EventEmitter, {
|
|
@@ -123,9 +127,9 @@ import TracksStore, {
|
|
|
123
127
|
} from "./track_management/tracks_store";
|
|
124
128
|
import {
|
|
125
129
|
constructPlayerStateReference,
|
|
130
|
+
emitPlayPauseEvents,
|
|
126
131
|
emitSeekEvents,
|
|
127
132
|
isLoadedState,
|
|
128
|
-
// emitSeekEvents,
|
|
129
133
|
PLAYER_STATES,
|
|
130
134
|
} from "./utils";
|
|
131
135
|
|
|
@@ -280,6 +284,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
280
284
|
reloadPosition?: number;
|
|
281
285
|
};
|
|
282
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Store last value of autoPlay, from the last load or reload.
|
|
289
|
+
*/
|
|
290
|
+
private _priv_lastAutoPlay: boolean;
|
|
291
|
+
|
|
283
292
|
/** All possible Error types emitted by the RxPlayer. */
|
|
284
293
|
static get ErrorTypes() : Record<IErrorType, IErrorType> {
|
|
285
294
|
return ErrorTypes;
|
|
@@ -327,7 +336,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
327
336
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
|
|
328
337
|
videoElement.preload = "auto";
|
|
329
338
|
|
|
330
|
-
this.version = /* PLAYER_VERSION */"4.0.0-beta.
|
|
339
|
+
this.version = /* PLAYER_VERSION */"4.0.0-beta.2";
|
|
331
340
|
this.log = log;
|
|
332
341
|
this.state = "STOPPED";
|
|
333
342
|
this.videoElement = videoElement;
|
|
@@ -372,6 +381,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
372
381
|
this._priv_setPlayerState(PLAYER_STATES.STOPPED);
|
|
373
382
|
|
|
374
383
|
this._priv_reloadingMetadata = {};
|
|
384
|
+
|
|
385
|
+
this._priv_lastAutoPlay = false;
|
|
375
386
|
}
|
|
376
387
|
|
|
377
388
|
/**
|
|
@@ -442,6 +453,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
442
453
|
log.info("API: Calling loadvideo", options.url, options.transport);
|
|
443
454
|
this._priv_reloadingMetadata = { options };
|
|
444
455
|
this._priv_initializeContentPlayback(options);
|
|
456
|
+
this._priv_lastAutoPlay = options.autoPlay;
|
|
445
457
|
}
|
|
446
458
|
|
|
447
459
|
/**
|
|
@@ -683,26 +695,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
683
695
|
|
|
684
696
|
// Bind events
|
|
685
697
|
initializer.addEventListener("error", (error) => {
|
|
686
|
-
|
|
687
|
-
defaultCode: "NONE",
|
|
688
|
-
defaultReason: "An unknown error stopped content playback.",
|
|
689
|
-
});
|
|
690
|
-
formattedError.fatal = true;
|
|
691
|
-
|
|
692
|
-
contentInfos.currentContentCanceller.cancel();
|
|
693
|
-
this._priv_cleanUpCurrentContentState();
|
|
694
|
-
this._priv_currentError = formattedError;
|
|
695
|
-
log.error("API: The player stopped because of an error",
|
|
696
|
-
error instanceof Error ? error : "");
|
|
697
|
-
this._priv_setPlayerState(PLAYER_STATES.STOPPED);
|
|
698
|
-
|
|
699
|
-
// TODO This condition is here because the eventual callback called when the
|
|
700
|
-
// player state is updated can launch a new content, thus the error will not
|
|
701
|
-
// be here anymore, in which case triggering the "error" event is unwanted.
|
|
702
|
-
// This is very ugly though, and we should probable have a better solution
|
|
703
|
-
if (this._priv_currentError === formattedError) {
|
|
704
|
-
this.trigger("error", formattedError);
|
|
705
|
-
}
|
|
698
|
+
this._priv_onFatalError(error, contentInfos);
|
|
706
699
|
});
|
|
707
700
|
initializer.addEventListener("warning", (error) => {
|
|
708
701
|
const formattedError = formatError(error, {
|
|
@@ -712,11 +705,12 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
712
705
|
log.warn("API: Sending warning:", formattedError);
|
|
713
706
|
this.trigger("warning", formattedError);
|
|
714
707
|
});
|
|
715
|
-
initializer.addEventListener("reloadingMediaSource", () => {
|
|
708
|
+
initializer.addEventListener("reloadingMediaSource", (payload) => {
|
|
716
709
|
contentInfos.segmentBuffersStore = null;
|
|
717
710
|
if (contentInfos.tracksStore !== null) {
|
|
718
711
|
contentInfos.tracksStore.resetPeriodObjects();
|
|
719
712
|
}
|
|
713
|
+
this._priv_lastAutoPlay = payload.autoPlay;
|
|
720
714
|
});
|
|
721
715
|
initializer.addEventListener("inbandEvents", (inbandEvents) =>
|
|
722
716
|
this.trigger("inbandEvents", inbandEvents));
|
|
@@ -800,6 +794,53 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
800
794
|
}
|
|
801
795
|
};
|
|
802
796
|
|
|
797
|
+
/**
|
|
798
|
+
* `TaskCanceller` allowing to stop emitting `"play"` and `"pause"`
|
|
799
|
+
* events.
|
|
800
|
+
* `null` when such events are not emitted currently.
|
|
801
|
+
*/
|
|
802
|
+
let playPauseEventsCanceller : TaskCanceller | null = null;
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Callback emitting `"play"` and `"pause`" events once the content is
|
|
806
|
+
* loaded, starting from the state indicated in argument.
|
|
807
|
+
* @param {boolean} willAutoPlay - If `false`, we're currently paused.
|
|
808
|
+
*/
|
|
809
|
+
const triggerPlayPauseEventsWhenReady = (willAutoPlay: boolean) => {
|
|
810
|
+
if (playPauseEventsCanceller !== null) {
|
|
811
|
+
playPauseEventsCanceller.cancel(); // cancel previous logic
|
|
812
|
+
playPauseEventsCanceller = null;
|
|
813
|
+
}
|
|
814
|
+
playerStateRef.onUpdate((val, stopListeningToStateUpdates) => {
|
|
815
|
+
if (!isLoadedState(val)) {
|
|
816
|
+
return; // content not loaded yet: no event
|
|
817
|
+
}
|
|
818
|
+
stopListeningToStateUpdates();
|
|
819
|
+
if (playPauseEventsCanceller !== null) {
|
|
820
|
+
playPauseEventsCanceller.cancel();
|
|
821
|
+
}
|
|
822
|
+
playPauseEventsCanceller = new TaskCanceller();
|
|
823
|
+
playPauseEventsCanceller.linkToSignal(currentContentCanceller.signal);
|
|
824
|
+
if (willAutoPlay !== !videoElement.paused) {
|
|
825
|
+
// paused status is not at the expected value on load: emit event
|
|
826
|
+
if (videoElement.paused) {
|
|
827
|
+
this.trigger("pause", null);
|
|
828
|
+
} else {
|
|
829
|
+
this.trigger("play", null);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
emitPlayPauseEvents(videoElement,
|
|
833
|
+
() => this.trigger("play", null),
|
|
834
|
+
() => this.trigger("pause", null),
|
|
835
|
+
currentContentCanceller.signal);
|
|
836
|
+
}, { emitCurrentValue: false, clearSignal: currentContentCanceller.signal });
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
triggerPlayPauseEventsWhenReady(autoPlay);
|
|
840
|
+
initializer.addEventListener("reloadingMediaSource", (payload) => {
|
|
841
|
+
triggerPlayPauseEventsWhenReady(payload.autoPlay);
|
|
842
|
+
});
|
|
843
|
+
|
|
803
844
|
/**
|
|
804
845
|
* `TaskCanceller` allowing to stop emitting `"seeking"` and `"seeked"`
|
|
805
846
|
* events.
|
|
@@ -884,6 +925,42 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
884
925
|
return this.state;
|
|
885
926
|
}
|
|
886
927
|
|
|
928
|
+
/**
|
|
929
|
+
* Returns true if a content is loaded.
|
|
930
|
+
* @returns {Boolean} - `true` if a content is loaded, `false` otherwise.
|
|
931
|
+
*/
|
|
932
|
+
isContentLoaded() : boolean {
|
|
933
|
+
return !arrayIncludes(["LOADING", "RELOADING", "STOPPED"], this.state);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Returns true if the player is buffering.
|
|
938
|
+
* @returns {Boolean} - `true` if the player is buffering, `false` otherwise.
|
|
939
|
+
*/
|
|
940
|
+
isBuffering() : boolean {
|
|
941
|
+
return arrayIncludes(["BUFFERING", "SEEKING", "LOADING", "RELOADING"], this.state);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Returns the play/pause status of the player :
|
|
946
|
+
* - when `LOADING` or `RELOADING`, returns the scheduled play/pause condition
|
|
947
|
+
* for when loading is over,
|
|
948
|
+
* - in other states, returns the `<video>` element .paused value,
|
|
949
|
+
* - if the player is disposed, returns `true`.
|
|
950
|
+
* @returns {Boolean} - `true` if the player is paused or will be after loading,
|
|
951
|
+
* `false` otherwise.
|
|
952
|
+
*/
|
|
953
|
+
isPaused() : boolean {
|
|
954
|
+
if (this.videoElement) {
|
|
955
|
+
if (arrayIncludes(["LOADING", "RELOADING"], this.state)) {
|
|
956
|
+
return !this._priv_lastAutoPlay;
|
|
957
|
+
} else {
|
|
958
|
+
return this.videoElement.paused;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
|
|
887
964
|
/**
|
|
888
965
|
* Returns true if both:
|
|
889
966
|
* - a content is loaded
|
|
@@ -1036,6 +1113,15 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
1036
1113
|
return this.videoElement.currentTime;
|
|
1037
1114
|
}
|
|
1038
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Returns the last stored content position, in seconds.
|
|
1118
|
+
*
|
|
1119
|
+
* @returns {number|undefined}
|
|
1120
|
+
*/
|
|
1121
|
+
getLastStoredContentPosition() : number|undefined {
|
|
1122
|
+
return this._priv_reloadingMetadata.reloadPosition;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1039
1125
|
/**
|
|
1040
1126
|
* Returns the current playback rate at which the video plays.
|
|
1041
1127
|
* @returns {Number}
|
|
@@ -1251,6 +1337,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
1251
1337
|
if (positionWanted === undefined) {
|
|
1252
1338
|
throw new Error("invalid time given");
|
|
1253
1339
|
}
|
|
1340
|
+
log.info("API: API Seek to", positionWanted);
|
|
1254
1341
|
this.videoElement.currentTime = positionWanted;
|
|
1255
1342
|
return positionWanted;
|
|
1256
1343
|
}
|
|
@@ -2001,21 +2088,34 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2001
2088
|
return; // Event for another content
|
|
2002
2089
|
}
|
|
2003
2090
|
contentInfos.manifest = manifest;
|
|
2004
|
-
const cancelSignal = contentInfos.currentContentCanceller.signal;
|
|
2005
2091
|
this._priv_reloadingMetadata.manifest = manifest;
|
|
2006
2092
|
|
|
2007
|
-
|
|
2093
|
+
const tracksStore = new TracksStore({
|
|
2008
2094
|
preferTrickModeTracks: this._priv_preferTrickModeTracks,
|
|
2009
2095
|
defaultAudioTrackSwitchingMode: contentInfos.defaultAudioTrackSwitchingMode,
|
|
2010
2096
|
});
|
|
2011
|
-
contentInfos.tracksStore
|
|
2097
|
+
contentInfos.tracksStore = tracksStore;
|
|
2098
|
+
tracksStore.addEventListener("newAvailablePeriods", (p) => {
|
|
2012
2099
|
this.trigger("newAvailablePeriods", p);
|
|
2013
2100
|
});
|
|
2014
|
-
|
|
2101
|
+
tracksStore.addEventListener("brokenRepresentationsLock", (e) => {
|
|
2015
2102
|
this.trigger("brokenRepresentationsLock", e);
|
|
2016
2103
|
});
|
|
2017
|
-
|
|
2104
|
+
tracksStore.addEventListener("trackUpdate", (e) => {
|
|
2018
2105
|
this.trigger("trackUpdate", e);
|
|
2106
|
+
|
|
2107
|
+
const currentPeriod = this._priv_contentInfos?.currentPeriod ?? undefined;
|
|
2108
|
+
if (e.reason === "no-playable-representation" &&
|
|
2109
|
+
e.period.id === currentPeriod?.id)
|
|
2110
|
+
{
|
|
2111
|
+
this._priv_onAvailableTracksMayHaveChanged(e.trackType);
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
contentInfos.tracksStore.addEventListener("warning", (err) => {
|
|
2115
|
+
this.trigger("warning", err);
|
|
2116
|
+
});
|
|
2117
|
+
contentInfos.tracksStore.addEventListener("error", (err) => {
|
|
2118
|
+
this._priv_onFatalError(err, contentInfos);
|
|
2019
2119
|
});
|
|
2020
2120
|
|
|
2021
2121
|
contentInfos.tracksStore.updatePeriodList(manifest);
|
|
@@ -2026,8 +2126,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2026
2126
|
contentInfos.tracksStore.updatePeriodList(manifest);
|
|
2027
2127
|
}
|
|
2028
2128
|
const currentPeriod = this._priv_contentInfos?.currentPeriod ?? undefined;
|
|
2029
|
-
const
|
|
2030
|
-
if (currentPeriod === undefined || isNullOrUndefined(
|
|
2129
|
+
const currTracksStore = this._priv_contentInfos?.tracksStore;
|
|
2130
|
+
if (currentPeriod === undefined || isNullOrUndefined(currTracksStore)) {
|
|
2031
2131
|
return;
|
|
2032
2132
|
}
|
|
2033
2133
|
for (const update of updates.updatedPeriods) {
|
|
@@ -2036,27 +2136,72 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2036
2136
|
update.result.removedAdaptations.length > 0)
|
|
2037
2137
|
{
|
|
2038
2138
|
// We might have new (or less) tracks, send events just to be sure
|
|
2039
|
-
const periodRef =
|
|
2139
|
+
const periodRef = currTracksStore.getPeriodObjectFromPeriod(currentPeriod);
|
|
2040
2140
|
if (periodRef === undefined) {
|
|
2041
2141
|
return;
|
|
2042
2142
|
}
|
|
2043
|
-
|
|
2044
|
-
this.
|
|
2045
|
-
|
|
2046
|
-
cancelSignal);
|
|
2047
|
-
const textTracks = tracksStore.getAvailableTextTracks(periodRef);
|
|
2048
|
-
this._priv_triggerEventIfNotStopped("availableTextTracksChange",
|
|
2049
|
-
textTracks ?? [],
|
|
2050
|
-
cancelSignal);
|
|
2051
|
-
const videoTracks = tracksStore.getAvailableVideoTracks(periodRef);
|
|
2052
|
-
this._priv_triggerEventIfNotStopped("availableVideoTracksChange",
|
|
2053
|
-
videoTracks ?? [],
|
|
2054
|
-
cancelSignal);
|
|
2143
|
+
this._priv_onAvailableTracksMayHaveChanged("audio");
|
|
2144
|
+
this._priv_onAvailableTracksMayHaveChanged("text");
|
|
2145
|
+
this._priv_onAvailableTracksMayHaveChanged("video");
|
|
2055
2146
|
}
|
|
2056
2147
|
}
|
|
2057
2148
|
return;
|
|
2058
2149
|
}
|
|
2059
2150
|
}, contentInfos.currentContentCanceller.signal);
|
|
2151
|
+
|
|
2152
|
+
manifest.addEventListener("decipherabilityUpdate", (elts) => {
|
|
2153
|
+
/**
|
|
2154
|
+
* Array of tuples only including once the Period/Track combination, and
|
|
2155
|
+
* only when it concerns the currently-selected track.
|
|
2156
|
+
*/
|
|
2157
|
+
const periodsAndTrackTypes = elts.reduce(
|
|
2158
|
+
(acc: Array<[Period, ITrackType]>, elt) => {
|
|
2159
|
+
const isFound = arrayFind(
|
|
2160
|
+
acc,
|
|
2161
|
+
(x) => x[0].id === elt.period.id &&
|
|
2162
|
+
x[1] === elt.adaptation.type
|
|
2163
|
+
) === undefined;
|
|
2164
|
+
|
|
2165
|
+
if (!isFound) {
|
|
2166
|
+
// Only consider the currently-selected tracks.
|
|
2167
|
+
// NOTE: Maybe there's room for optimizations? Unclear.
|
|
2168
|
+
const tStore = contentInfos.tracksStore;
|
|
2169
|
+
if (tStore === null) {
|
|
2170
|
+
return acc;
|
|
2171
|
+
}
|
|
2172
|
+
let isCurrent = false;
|
|
2173
|
+
const periodRef = tStore.getPeriodObjectFromPeriod(elt.period);
|
|
2174
|
+
if (periodRef === undefined) {
|
|
2175
|
+
return acc;
|
|
2176
|
+
}
|
|
2177
|
+
switch (elt.adaptation.type) {
|
|
2178
|
+
case "audio":
|
|
2179
|
+
isCurrent = tStore
|
|
2180
|
+
.getChosenAudioTrack(periodRef)?.id === elt.adaptation.id;
|
|
2181
|
+
break;
|
|
2182
|
+
case "video":
|
|
2183
|
+
isCurrent = tStore
|
|
2184
|
+
.getChosenVideoTrack(periodRef)?.id === elt.adaptation.id;
|
|
2185
|
+
break;
|
|
2186
|
+
case "text":
|
|
2187
|
+
isCurrent = tStore
|
|
2188
|
+
.getChosenTextTrack(periodRef)?.id === elt.adaptation.id;
|
|
2189
|
+
break;
|
|
2190
|
+
}
|
|
2191
|
+
if (isCurrent) {
|
|
2192
|
+
acc.push([elt.period, elt.adaptation.type]);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
return acc;
|
|
2196
|
+
}, []);
|
|
2197
|
+
for (const [period, trackType] of periodsAndTrackTypes) {
|
|
2198
|
+
this._priv_triggerEventIfNotStopped(
|
|
2199
|
+
"representationListUpdate",
|
|
2200
|
+
{ period, trackType, reason: "decipherability-update" },
|
|
2201
|
+
contentInfos.currentContentCanceller.signal
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
}, contentInfos.currentContentCanceller.signal);
|
|
2060
2205
|
}
|
|
2061
2206
|
|
|
2062
2207
|
/**
|
|
@@ -2525,8 +2670,93 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2525
2670
|
}
|
|
2526
2671
|
return cb(tracksStore, periodRef);
|
|
2527
2672
|
}
|
|
2673
|
+
|
|
2674
|
+
/**
|
|
2675
|
+
* Method to call when some event lead to a high for possibility that the
|
|
2676
|
+
* available tracks for the given type have changed.
|
|
2677
|
+
* Send the corresponding `available*Tracks` change event with the last
|
|
2678
|
+
* available tracks.
|
|
2679
|
+
*
|
|
2680
|
+
* @param {string} trackType
|
|
2681
|
+
* @param {Object|undefined} [oPeriodRef] - optional period object used by the
|
|
2682
|
+
* `tracksStore` API, allows to optimize the method by bypassing this step.
|
|
2683
|
+
*/
|
|
2684
|
+
private _priv_onAvailableTracksMayHaveChanged(
|
|
2685
|
+
trackType : IBufferType,
|
|
2686
|
+
oPeriodRef? : ITMPeriodObject
|
|
2687
|
+
): void {
|
|
2688
|
+
const contentInfos = this._priv_contentInfos;
|
|
2689
|
+
if (contentInfos === null) {
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
const { currentPeriod,
|
|
2693
|
+
tracksStore,
|
|
2694
|
+
currentContentCanceller } = contentInfos;
|
|
2695
|
+
const cancelSignal = currentContentCanceller.signal;
|
|
2696
|
+
if (isNullOrUndefined(currentPeriod) || tracksStore === null) {
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
const periodRef = oPeriodRef ?? tracksStore.getPeriodObjectFromPeriod(currentPeriod);
|
|
2700
|
+
if (periodRef === undefined) {
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
switch (trackType) {
|
|
2704
|
+
case "video":
|
|
2705
|
+
const videoTracks = tracksStore.getAvailableVideoTracks(periodRef);
|
|
2706
|
+
this._priv_triggerEventIfNotStopped("availableVideoTracksChange",
|
|
2707
|
+
videoTracks ?? [],
|
|
2708
|
+
cancelSignal);
|
|
2709
|
+
break;
|
|
2710
|
+
case "audio":
|
|
2711
|
+
const audioTracks = tracksStore.getAvailableAudioTracks(periodRef);
|
|
2712
|
+
this._priv_triggerEventIfNotStopped("availableAudioTracksChange",
|
|
2713
|
+
audioTracks ?? [],
|
|
2714
|
+
cancelSignal);
|
|
2715
|
+
break;
|
|
2716
|
+
case "text":
|
|
2717
|
+
const textTracks = tracksStore.getAvailableTextTracks(periodRef);
|
|
2718
|
+
this._priv_triggerEventIfNotStopped("availableTextTracksChange",
|
|
2719
|
+
textTracks ?? [],
|
|
2720
|
+
cancelSignal);
|
|
2721
|
+
break;
|
|
2722
|
+
default:
|
|
2723
|
+
assertUnreachable(trackType);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
/**
|
|
2728
|
+
* Method to call when a fatal error lead to the stopping of the current
|
|
2729
|
+
* content.
|
|
2730
|
+
*
|
|
2731
|
+
* @param {*} err - The error encountered.
|
|
2732
|
+
* @param {Object} contentInfos - The `IPublicApiContentInfos` object linked
|
|
2733
|
+
* to the content for which the error was received.
|
|
2734
|
+
*/
|
|
2735
|
+
private _priv_onFatalError(
|
|
2736
|
+
err : unknown,
|
|
2737
|
+
contentInfos : IPublicApiContentInfos
|
|
2738
|
+
): void {
|
|
2739
|
+
const formattedError = formatError(err, {
|
|
2740
|
+
defaultCode: "NONE",
|
|
2741
|
+
defaultReason: "An unknown error stopped content playback.",
|
|
2742
|
+
});
|
|
2743
|
+
formattedError.fatal = true;
|
|
2744
|
+
contentInfos.currentContentCanceller.cancel();
|
|
2745
|
+
this._priv_cleanUpCurrentContentState();
|
|
2746
|
+
this._priv_currentError = formattedError;
|
|
2747
|
+
log.error("API: The player stopped because of an error", formattedError);
|
|
2748
|
+
this._priv_setPlayerState(PLAYER_STATES.STOPPED);
|
|
2749
|
+
|
|
2750
|
+
// TODO This condition is here because the eventual callback called when the
|
|
2751
|
+
// player state is updated can launch a new content, thus the error will not
|
|
2752
|
+
// be here anymore, in which case triggering the "error" event is unwanted.
|
|
2753
|
+
// This is very ugly though, and we should probable have a better solution
|
|
2754
|
+
if (this._priv_currentError === formattedError) {
|
|
2755
|
+
this.trigger("error", formattedError);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2528
2758
|
}
|
|
2529
|
-
Player.version = /* PLAYER_VERSION */"4.0.0-beta.
|
|
2759
|
+
Player.version = /* PLAYER_VERSION */"4.0.0-beta.2";
|
|
2530
2760
|
|
|
2531
2761
|
/** Every events sent by the RxPlayer's public API. */
|
|
2532
2762
|
interface IPublicAPIEvent {
|
|
@@ -2544,9 +2774,12 @@ interface IPublicAPIEvent {
|
|
|
2544
2774
|
availableAudioTracksChange : IAvailableAudioTrack[];
|
|
2545
2775
|
availableTextTracksChange : IAvailableTextTrack[];
|
|
2546
2776
|
availableVideoTracksChange : IAvailableVideoTrack[];
|
|
2777
|
+
play: null;
|
|
2778
|
+
pause: null;
|
|
2547
2779
|
newAvailablePeriods : IPeriod[];
|
|
2548
2780
|
brokenRepresentationsLock : IBrokenRepresentationsLockContext;
|
|
2549
2781
|
trackUpdate : ITrackUpdateEventPayload;
|
|
2782
|
+
representationListUpdate : IRepresentationListUpdateContext;
|
|
2550
2783
|
seeking : null;
|
|
2551
2784
|
seeked : null;
|
|
2552
2785
|
streamEvent : IStreamEvent;
|
|
@@ -118,10 +118,7 @@ function createAudioTracks(
|
|
|
118
118
|
*/
|
|
119
119
|
function createTextTracks(
|
|
120
120
|
textTracks: ICompatTextTrackList
|
|
121
|
-
): Array<{ track:
|
|
122
|
-
normalized: string;
|
|
123
|
-
language: string;
|
|
124
|
-
closedCaption: boolean; };
|
|
121
|
+
): Array<{ track: ITextTrack;
|
|
125
122
|
nativeTrack: TextTrack; }> {
|
|
126
123
|
const newTextTracks = [];
|
|
127
124
|
const languagesOccurences: Partial<Record<string, number>> = {};
|
|
@@ -135,10 +132,20 @@ function createTextTracks(
|
|
|
135
132
|
"_" +
|
|
136
133
|
occurences.toString();
|
|
137
134
|
languagesOccurences[language] = occurences + 1;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
|
|
136
|
+
// Safari seems to be indicating that the subtitles track is a forced
|
|
137
|
+
// subtitles track by setting the `kind` attribute to `"forced"`.
|
|
138
|
+
// As of now (2023-04-04), this is not standard.
|
|
139
|
+
// @see https://github.com/whatwg/html/issues/4472
|
|
140
|
+
const forced = (textTrack.kind as string) === "forced" ?
|
|
141
|
+
true :
|
|
142
|
+
undefined;
|
|
143
|
+
const track = { language: textTrack.language,
|
|
144
|
+
forced,
|
|
145
|
+
label: textTrack.label,
|
|
146
|
+
id,
|
|
147
|
+
normalized: normalizeLanguage(textTrack.language),
|
|
148
|
+
closedCaption: textTrack.kind === "captions" };
|
|
142
149
|
newTextTracks.push({ track,
|
|
143
150
|
nativeTrack: textTrack });
|
|
144
151
|
}
|
|
@@ -393,6 +400,8 @@ export default class MediaElementTracksStore
|
|
|
393
400
|
public getAvailableTextTracks(): IAvailableTextTrack[] {
|
|
394
401
|
return this._textTracks.map(({ track, nativeTrack }) => {
|
|
395
402
|
return { id: track.id,
|
|
403
|
+
label: track.label,
|
|
404
|
+
forced: track.forced,
|
|
396
405
|
language: track.language,
|
|
397
406
|
normalized: track.normalized,
|
|
398
407
|
closedCaption: track.closedCaption,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MediaError } from "../../../errors";
|
|
2
1
|
import Manifest, {
|
|
3
2
|
Adaptation,
|
|
4
3
|
Representation,
|
|
@@ -63,6 +62,15 @@ export default class TrackDispatcher extends EventEmitter<ITrackDispatcherEvent>
|
|
|
63
62
|
/** Interface allowing to clean-up resources when they are not needed anymore. */
|
|
64
63
|
private _canceller : TaskCanceller;
|
|
65
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Boolean set to `true` when a track-updating method is called and to `false`
|
|
67
|
+
* just before it performs the actual track change to allow checking for
|
|
68
|
+
* re-entrancy: if the token is already reset to `false` before the
|
|
69
|
+
* track change is officialized, then another track update has already been
|
|
70
|
+
* performed in the meantime.
|
|
71
|
+
*/
|
|
72
|
+
private _updateToken: boolean;
|
|
73
|
+
|
|
66
74
|
/**
|
|
67
75
|
* Create a new `TrackDispatcher` by giving its Reference and an initial track
|
|
68
76
|
* setting.
|
|
@@ -70,30 +78,40 @@ export default class TrackDispatcher extends EventEmitter<ITrackDispatcherEvent>
|
|
|
70
78
|
* synchronously.
|
|
71
79
|
* @param {Object} manifest
|
|
72
80
|
* @param {Object} adaptationRef
|
|
73
|
-
* @param {Object|null} initialTrackInfo
|
|
74
81
|
*/
|
|
75
82
|
constructor(
|
|
76
83
|
manifest : Manifest,
|
|
77
|
-
adaptationRef : ISharedReference<IAdaptationChoice | null | undefined
|
|
78
|
-
initialTrackInfo : ITrackSetting | null
|
|
84
|
+
adaptationRef : ISharedReference<IAdaptationChoice | null | undefined>
|
|
79
85
|
) {
|
|
80
86
|
super();
|
|
81
87
|
this._canceller = new TaskCanceller();
|
|
82
88
|
this._manifest = manifest;
|
|
83
89
|
this._adaptationRef = adaptationRef;
|
|
90
|
+
this._updateToken = false;
|
|
91
|
+
}
|
|
84
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @param {Object|null} initialTrackInfo
|
|
95
|
+
*/
|
|
96
|
+
public start(initialTrackInfo : ITrackSetting | null) : void {
|
|
97
|
+
this._updateToken = true;
|
|
85
98
|
if (initialTrackInfo === null) {
|
|
86
|
-
this._lastEmitted =
|
|
87
|
-
|
|
99
|
+
this._lastEmitted = null;
|
|
100
|
+
this._updateToken = false;
|
|
101
|
+
this._adaptationRef.setValue(null);
|
|
88
102
|
return;
|
|
89
103
|
}
|
|
90
104
|
const reference = this._constructLockedRepresentationsReference(initialTrackInfo);
|
|
105
|
+
if (!this._updateToken) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
91
108
|
this._lastEmitted = { adaptation: initialTrackInfo.adaptation,
|
|
92
109
|
switchingMode: initialTrackInfo.switchingMode,
|
|
93
110
|
lockedRepresentations: null };
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
this._updateToken = false;
|
|
112
|
+
this._adaptationRef.setValue({ adaptation: initialTrackInfo.adaptation,
|
|
113
|
+
switchingMode: initialTrackInfo.switchingMode,
|
|
114
|
+
representations: reference });
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
/**
|
|
@@ -101,10 +119,12 @@ export default class TrackDispatcher extends EventEmitter<ITrackDispatcherEvent>
|
|
|
101
119
|
* @param {Object|null} newTrackInfo
|
|
102
120
|
*/
|
|
103
121
|
public updateTrack(newTrackInfo : ITrackSetting | null) : void {
|
|
122
|
+
this._updateToken = true;
|
|
104
123
|
if (newTrackInfo === null) {
|
|
105
124
|
if (this._lastEmitted === null) {
|
|
106
125
|
return;
|
|
107
126
|
}
|
|
127
|
+
this._updateToken = false;
|
|
108
128
|
this._canceller.cancel();
|
|
109
129
|
|
|
110
130
|
// has no point but let's still create one for simplicity sake
|
|
@@ -117,7 +137,11 @@ export default class TrackDispatcher extends EventEmitter<ITrackDispatcherEvent>
|
|
|
117
137
|
this._canceller.cancel();
|
|
118
138
|
this._canceller = new TaskCanceller();
|
|
119
139
|
const reference = this._constructLockedRepresentationsReference(newTrackInfo);
|
|
140
|
+
if (!this._updateToken) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
120
143
|
this._lastEmitted = { adaptation, switchingMode, lockedRepresentations: null };
|
|
144
|
+
this._updateToken = false;
|
|
121
145
|
this._adaptationRef.setValue({ adaptation,
|
|
122
146
|
switchingMode,
|
|
123
147
|
representations: reference });
|
|
@@ -176,11 +200,9 @@ export default class TrackDispatcher extends EventEmitter<ITrackDispatcherEvent>
|
|
|
176
200
|
}
|
|
177
201
|
}
|
|
178
202
|
if (playableRepresentations.length <= 0) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
adaptationType + " Adaptation can be played");
|
|
183
|
-
throw noRepErr;
|
|
203
|
+
trackInfo.adaptation.isSupported = false;
|
|
204
|
+
self.trigger("noPlayableRepresentation", null);
|
|
205
|
+
return;
|
|
184
206
|
}
|
|
185
207
|
|
|
186
208
|
// Check if Locked Representations have changed
|
|
@@ -221,6 +243,7 @@ export interface ITrackDispatcherEvent {
|
|
|
221
243
|
* none of them are currently "playable".
|
|
222
244
|
*/
|
|
223
245
|
noPlayableLockedRepresentation : null;
|
|
246
|
+
noPlayableRepresentation: null;
|
|
224
247
|
}
|
|
225
248
|
|
|
226
249
|
/** Define a new Track preference given to the `TrackDispatcher`. */
|