rx-player 3.30.0-dev.2023022200 → 3.30.0
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/.eslintrc.js +8 -0
- package/CHANGELOG.md +4 -1
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/browser_detection.d.ts +23 -12
- package/dist/_esm5.processed/compat/browser_detection.js +80 -38
- package/dist/_esm5.processed/compat/can_reuse_media_keys.js +2 -2
- package/dist/_esm5.processed/config.d.ts +2 -0
- package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +0 -1
- package/dist/_esm5.processed/core/api/public_api.js +3 -3
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +27 -8
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +28 -7
- package/dist/_esm5.processed/core/decrypt/find_key_system.js +33 -24
- package/dist/_esm5.processed/core/decrypt/session_events_listener.js +27 -13
- package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +13 -4
- package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +1 -1
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +1 -1
- package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +15 -5
- package/dist/_esm5.processed/default_config.d.ts +16 -0
- package/dist/_esm5.processed/default_config.js +19 -0
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +5 -2
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +5 -2
- package/dist/rx-player.js +162 -72
- package/dist/rx-player.min.js +1 -1
- package/package.json +2 -1
- package/sonar-project.properties +1 -1
- package/src/compat/browser_detection.ts +99 -52
- package/src/compat/can_reuse_media_keys.ts +5 -2
- package/src/core/api/debug/buffer_size_graph.ts +0 -1
- package/src/core/api/public_api.ts +3 -3
- package/src/core/decrypt/__tests__/__global__/utils.ts +61 -40
- package/src/core/decrypt/find_key_system.ts +36 -35
- package/src/core/decrypt/session_events_listener.ts +28 -15
- package/src/core/fetchers/utils/schedule_request.ts +14 -4
- package/src/core/init/utils/media_duration_updater.ts +1 -1
- package/src/core/init/utils/rebuffering_controller.ts +1 -1
- package/src/core/stream/adaptation/adaptation_stream.ts +18 -8
- package/src/default_config.ts +30 -9
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +4 -1
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +5 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rx-player",
|
|
3
3
|
"author": "Canal+",
|
|
4
|
-
"version": "3.30.0
|
|
4
|
+
"version": "3.30.0",
|
|
5
5
|
"description": "Canal+ HTML5 Video Player",
|
|
6
6
|
"main": "./dist/rx-player.js",
|
|
7
7
|
"keywords": [
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"core-js": "3.28.0",
|
|
98
98
|
"esbuild": "0.17.10",
|
|
99
99
|
"eslint": "8.34.0",
|
|
100
|
+
"eslint-plugin-ban": "1.6.0",
|
|
100
101
|
"eslint-plugin-import": "2.27.5",
|
|
101
102
|
"eslint-plugin-jsdoc": "40.0.0",
|
|
102
103
|
"eslint-plugin-react": "7.32.2",
|
package/sonar-project.properties
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
sonar.projectKey=rx-player
|
|
2
2
|
sonar.organization=rx-player
|
|
3
3
|
sonar.projectName=rx-player
|
|
4
|
-
sonar.projectVersion=3.30.0
|
|
4
|
+
sonar.projectVersion=3.30.0
|
|
5
5
|
sonar.sources=./src,./demo,./tests
|
|
6
6
|
sonar.exclusions=demo/full/bundle.js,demo/standalone/lib.js,demo/bundle.js
|
|
7
7
|
sonar.host.url=https://sonarcloud.io
|
|
@@ -24,71 +24,118 @@ interface IIE11Document extends Document {
|
|
|
24
24
|
documentMode? : unknown;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const isIE11 : boolean =
|
|
30
|
-
!isNode &&
|
|
31
|
-
typeof (window as IIE11WindowObject).MSInputMethodContext !== "undefined" &&
|
|
32
|
-
typeof (document as IIE11Document).documentMode !== "undefined";
|
|
33
|
-
|
|
34
|
-
// true for IE / Edge
|
|
35
|
-
const isIEOrEdge : boolean = isNode ?
|
|
36
|
-
false :
|
|
37
|
-
navigator.appName === "Microsoft Internet Explorer" ||
|
|
38
|
-
navigator.appName === "Netscape" &&
|
|
39
|
-
/(Trident|Edge)\//.test(navigator.userAgent);
|
|
40
|
-
|
|
41
|
-
const isEdgeChromium: boolean = !isNode &&
|
|
42
|
-
navigator.userAgent.toLowerCase().indexOf("edg/") !== -1;
|
|
43
|
-
|
|
44
|
-
const isFirefox : boolean = !isNode &&
|
|
45
|
-
navigator.userAgent.toLowerCase().indexOf("firefox") !== -1;
|
|
46
|
-
|
|
47
|
-
const isSamsungBrowser : boolean = !isNode &&
|
|
48
|
-
/SamsungBrowser/.test(navigator.userAgent);
|
|
49
|
-
|
|
50
|
-
const isTizen : boolean = !isNode &&
|
|
51
|
-
/Tizen/.test(navigator.userAgent);
|
|
52
|
-
|
|
53
|
-
const isWebOs : boolean = !isNode &&
|
|
54
|
-
navigator.userAgent.indexOf("Web0S") >= 0;
|
|
55
|
-
|
|
56
|
-
// Inspired form: http://webostv.developer.lge.com/discover/specifications/web-engine/
|
|
57
|
-
// Note: even that page doesn't correspond to what we've actually seen in the
|
|
58
|
-
// wild
|
|
59
|
-
const isWebOs2021 : boolean = isWebOs &&
|
|
60
|
-
(
|
|
61
|
-
/[Ww]eb[O0]S.TV-2021/.test(navigator.userAgent) ||
|
|
62
|
-
/[Cc]hr[o0]me\/79/.test(navigator.userAgent)
|
|
63
|
-
);
|
|
64
|
-
const isWebOs2022 : boolean = isWebOs &&
|
|
65
|
-
(
|
|
66
|
-
/[Ww]eb[O0]S.TV-2022/.test(navigator.userAgent) ||
|
|
67
|
-
/[Cc]hr[o0]me\/87/.test(navigator.userAgent)
|
|
68
|
-
);
|
|
27
|
+
/** Edge Chromium, regardless of the device */
|
|
28
|
+
let isEdgeChromium = false;
|
|
69
29
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
30
|
+
/** IE11, regardless of the device */
|
|
31
|
+
let isIE11 = false;
|
|
32
|
+
|
|
33
|
+
/** IE11 or Edge __Legacy__ (not Edge Chromium), regardless of the device */
|
|
34
|
+
let isIEOrEdge = false;
|
|
35
|
+
|
|
36
|
+
/** Firefox, regardless of the device */
|
|
37
|
+
let isFirefox = false;
|
|
73
38
|
|
|
74
39
|
/** `true` on Safari on a PC platform (i.e. not iPhone / iPad etc.) */
|
|
75
|
-
|
|
76
|
-
|
|
40
|
+
let isSafariDesktop = false;
|
|
41
|
+
|
|
42
|
+
/** `true` on Safari on an iPhone, iPad & iPod platform */
|
|
43
|
+
let isSafariMobile = false;
|
|
44
|
+
|
|
45
|
+
/** Samsung's own browser application */
|
|
46
|
+
let isSamsungBrowser = false;
|
|
47
|
+
|
|
48
|
+
/** `true` on devices where Tizen is the OS (e.g. Samsung TVs). */
|
|
49
|
+
let isTizen = false;
|
|
50
|
+
|
|
51
|
+
/** `true` on devices where WebOS is the OS (e.g. LG TVs). */
|
|
52
|
+
let isWebOs = false;
|
|
53
|
+
|
|
54
|
+
/** `true` specifically for WebOS 2021 version. */
|
|
55
|
+
let isWebOs2021 = false;
|
|
56
|
+
|
|
57
|
+
/** `true` specifically for WebOS 2022 version. */
|
|
58
|
+
let isWebOs2022 = false;
|
|
59
|
+
|
|
60
|
+
/** `true` for Panasonic devices. */
|
|
61
|
+
let isPanasonic = false;
|
|
62
|
+
|
|
63
|
+
((function findCurrentBrowser() : void {
|
|
64
|
+
if (isNode) {
|
|
65
|
+
return ;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 1 - Find out browser between IE/Edge Legacy/Edge Chromium/Firefox/Safari
|
|
69
|
+
|
|
70
|
+
if (typeof (window as IIE11WindowObject).MSInputMethodContext !== "undefined" &&
|
|
71
|
+
typeof (document as IIE11Document).documentMode !== "undefined")
|
|
72
|
+
{
|
|
73
|
+
isIE11 = true;
|
|
74
|
+
isIEOrEdge = true;
|
|
75
|
+
} else if (
|
|
76
|
+
navigator.appName === "Microsoft Internet Explorer" ||
|
|
77
|
+
navigator.appName === "Netscape" &&
|
|
78
|
+
/(Trident|Edge)\//.test(navigator.userAgent)
|
|
79
|
+
) {
|
|
80
|
+
isIEOrEdge = true;
|
|
81
|
+
} else if (navigator.userAgent.toLowerCase().indexOf("edg/") !== -1) {
|
|
82
|
+
isEdgeChromium = true;
|
|
83
|
+
} else if (navigator.userAgent.toLowerCase().indexOf("firefox") !== -1) {
|
|
84
|
+
isFirefox = true;
|
|
85
|
+
} else if (typeof navigator.platform === "string" &&
|
|
86
|
+
/iPad|iPhone|iPod/.test(navigator.platform))
|
|
87
|
+
{
|
|
88
|
+
isSafariMobile = true;
|
|
89
|
+
} else if (
|
|
77
90
|
Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor") >= 0 ||
|
|
78
91
|
(window as ISafariWindowObject).safari?.pushNotification?.toString() ===
|
|
79
92
|
"[object SafariRemoteNotification]"
|
|
80
|
-
)
|
|
93
|
+
) {
|
|
94
|
+
isSafariDesktop = true;
|
|
95
|
+
}
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
// 2 - Find out specific device/platform information
|
|
98
|
+
|
|
99
|
+
// Samsung browser e.g. on Android
|
|
100
|
+
if (/SamsungBrowser/.test(navigator.userAgent)) {
|
|
101
|
+
isSamsungBrowser = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (/Tizen/.test(navigator.userAgent)) {
|
|
105
|
+
isTizen = true;
|
|
106
|
+
|
|
107
|
+
// Inspired form: http://webostv.developer.lge.com/discover/specifications/web-engine/
|
|
108
|
+
// Note: even that page doesn't correspond to what we've actually seen in the
|
|
109
|
+
// wild
|
|
110
|
+
} else if (/[Ww]eb[O0]S/.test(navigator.userAgent)) {
|
|
111
|
+
isWebOs = true;
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
/[Ww]eb[O0]S.TV-2022/.test(navigator.userAgent) ||
|
|
115
|
+
/[Cc]hr[o0]me\/87/.test(navigator.userAgent)
|
|
116
|
+
) {
|
|
117
|
+
isWebOs2022 = true;
|
|
118
|
+
} else if (
|
|
119
|
+
/[Ww]eb[O0]S.TV-2021/.test(navigator.userAgent) ||
|
|
120
|
+
/[Cc]hr[o0]me\/79/.test(navigator.userAgent)
|
|
121
|
+
) {
|
|
122
|
+
isWebOs2021 = true;
|
|
123
|
+
}
|
|
124
|
+
} else if (/[Pp]anasonic/.test(navigator.userAgent)) {
|
|
125
|
+
isPanasonic = true;
|
|
126
|
+
}
|
|
127
|
+
})());
|
|
128
|
+
|
|
129
|
+
interface ISafariWindowObject extends Window {
|
|
130
|
+
safari? : { pushNotification? : { toString() : string } };
|
|
131
|
+
}
|
|
86
132
|
|
|
87
133
|
export {
|
|
88
134
|
isEdgeChromium,
|
|
89
135
|
isIE11,
|
|
90
136
|
isIEOrEdge,
|
|
91
137
|
isFirefox,
|
|
138
|
+
isPanasonic,
|
|
92
139
|
isSafariDesktop,
|
|
93
140
|
isSafariMobile,
|
|
94
141
|
isSamsungBrowser,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isPanasonic,
|
|
3
|
+
isWebOs,
|
|
4
|
+
} from "./browser_detection";
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Returns `true` if a `MediaKeys` instance (the `Encrypted Media Extension`
|
|
@@ -13,5 +16,5 @@ import { isWebOs } from "./browser_detection";
|
|
|
13
16
|
* @returns {boolean}
|
|
14
17
|
*/
|
|
15
18
|
export default function canReuseMediaKeys() : boolean {
|
|
16
|
-
return !isWebOs;
|
|
19
|
+
return !isWebOs && !isPanasonic;
|
|
17
20
|
}
|
|
@@ -364,7 +364,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
364
364
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
|
|
365
365
|
videoElement.preload = "auto";
|
|
366
366
|
|
|
367
|
-
this.version = /* PLAYER_VERSION */"3.30.0
|
|
367
|
+
this.version = /* PLAYER_VERSION */"3.30.0";
|
|
368
368
|
this.log = log;
|
|
369
369
|
this.state = "STOPPED";
|
|
370
370
|
this.videoElement = videoElement;
|
|
@@ -980,7 +980,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
980
980
|
// Previous call could have performed all kind of side-effects, thus,
|
|
981
981
|
// we re-check the current state associated to the RxPlayer
|
|
982
982
|
if (this.state === PLAYER_STATES.ENDED && this._priv_stopAtEnd) {
|
|
983
|
-
|
|
983
|
+
this.stop();
|
|
984
984
|
}
|
|
985
985
|
}, { emitCurrentValue: true, clearSignal: currentContentCanceller.signal });
|
|
986
986
|
|
|
@@ -2985,7 +2985,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
2985
2985
|
return mediaElementTrackChoiceManager;
|
|
2986
2986
|
}
|
|
2987
2987
|
}
|
|
2988
|
-
Player.version = /* PLAYER_VERSION */"3.30.0
|
|
2988
|
+
Player.version = /* PLAYER_VERSION */"3.30.0";
|
|
2989
2989
|
|
|
2990
2990
|
/** Every events sent by the RxPlayer's public API. */
|
|
2991
2991
|
interface IPublicAPIEvent {
|
|
@@ -40,48 +40,68 @@ import {
|
|
|
40
40
|
import { CancellationSignal } from "../../../../utils/task_canceller";
|
|
41
41
|
|
|
42
42
|
/** Default MediaKeySystemAccess configuration used by the RxPlayer. */
|
|
43
|
-
export const defaultKSConfig = [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}]
|
|
43
|
+
export const defaultKSConfig = [
|
|
44
|
+
{
|
|
45
|
+
audioCapabilities: [ { contentType: "audio/mp4;codecs=\"mp4a.40.2\"" },
|
|
46
|
+
{ contentType: "audio/webm;codecs=opus" } ],
|
|
47
|
+
distinctiveIdentifier: "optional" as const,
|
|
48
|
+
initDataTypes: ["cenc"] as const,
|
|
49
|
+
persistentState: "optional" as const,
|
|
50
|
+
sessionTypes: ["temporary"] as const,
|
|
51
|
+
videoCapabilities: [ { contentType: "video/mp4;codecs=\"avc1.4d401e\"" },
|
|
52
|
+
{ contentType: "video/mp4;codecs=\"avc1.42e01e\"" },
|
|
53
|
+
{ contentType: "video/webm;codecs=\"vp8\"" } ],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
audioCapabilities: undefined,
|
|
57
|
+
distinctiveIdentifier: "optional" as const,
|
|
58
|
+
initDataTypes: ["cenc"] as const,
|
|
59
|
+
persistentState: "optional" as const,
|
|
60
|
+
sessionTypes: ["temporary"] as const,
|
|
61
|
+
videoCapabilities: undefined,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
66
|
* Default "com.microsoft.playready.recommendation" MediaKeySystemAccess
|
|
57
67
|
* configuration used by the RxPlayer.
|
|
58
68
|
*/
|
|
59
|
-
export const defaultPRRecommendationKSConfig = [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}]
|
|
69
|
+
export const defaultPRRecommendationKSConfig = [
|
|
70
|
+
{
|
|
71
|
+
audioCapabilities: [ { robustness: "3000",
|
|
72
|
+
contentType: "audio/mp4;codecs=\"mp4a.40.2\"" },
|
|
73
|
+
{ robustness: "3000",
|
|
74
|
+
contentType: "audio/webm;codecs=opus" },
|
|
75
|
+
{ robustness: "2000",
|
|
76
|
+
contentType: "audio/mp4;codecs=\"mp4a.40.2\"" },
|
|
77
|
+
{ robustness: "2000",
|
|
78
|
+
contentType: "audio/webm;codecs=opus" } ],
|
|
79
|
+
distinctiveIdentifier: "optional" as const,
|
|
80
|
+
initDataTypes: ["cenc"] as const,
|
|
81
|
+
persistentState: "optional" as const,
|
|
82
|
+
sessionTypes: ["temporary"] as const,
|
|
83
|
+
videoCapabilities: [ { robustness: "3000",
|
|
84
|
+
contentType: "video/mp4;codecs=\"avc1.4d401e\"" },
|
|
85
|
+
{ robustness: "3000",
|
|
86
|
+
contentType: "video/mp4;codecs=\"avc1.42e01e\"" },
|
|
87
|
+
{ robustness: "3000",
|
|
88
|
+
contentType: "video/webm;codecs=\"vp8\"" },
|
|
89
|
+
{ robustness: "2000",
|
|
90
|
+
contentType: "video/mp4;codecs=\"avc1.4d401e\"" },
|
|
91
|
+
{ robustness: "2000",
|
|
92
|
+
contentType: "video/mp4;codecs=\"avc1.42e01e\"" },
|
|
93
|
+
{ robustness: "2000",
|
|
94
|
+
contentType: "video/webm;codecs=\"vp8\"" } ],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
audioCapabilities: undefined,
|
|
98
|
+
distinctiveIdentifier: "optional" as const,
|
|
99
|
+
initDataTypes: ["cenc"] as const,
|
|
100
|
+
persistentState: "optional" as const,
|
|
101
|
+
sessionTypes: ["temporary"] as const,
|
|
102
|
+
videoCapabilities: undefined,
|
|
103
|
+
},
|
|
104
|
+
];
|
|
85
105
|
|
|
86
106
|
/** Default Widevine MediaKeySystemAccess configuration used by the RxPlayer. */
|
|
87
107
|
export const defaultWidevineConfig = (() => {
|
|
@@ -104,9 +124,10 @@ export const defaultWidevineConfig = (() => {
|
|
|
104
124
|
{ contentType: "audio/webm;codecs=opus",
|
|
105
125
|
robustness } ];
|
|
106
126
|
});
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
return [
|
|
128
|
+
{ ...defaultKSConfig[0], audioCapabilities, videoCapabilities },
|
|
129
|
+
defaultKSConfig[1],
|
|
130
|
+
];
|
|
110
131
|
})();
|
|
111
132
|
|
|
112
133
|
/**
|
|
@@ -155,7 +155,9 @@ function buildKeySystemConfigurations(
|
|
|
155
155
|
if (keySystem.distinctiveIdentifierRequired === true) {
|
|
156
156
|
distinctiveIdentifier = "required";
|
|
157
157
|
}
|
|
158
|
-
const {
|
|
158
|
+
const { EME_DEFAULT_AUDIO_CODECS,
|
|
159
|
+
EME_DEFAULT_VIDEO_CODECS,
|
|
160
|
+
EME_DEFAULT_WIDEVINE_ROBUSTNESSES,
|
|
159
161
|
EME_DEFAULT_PLAYREADY_ROBUSTNESSES } = config.getCurrent();
|
|
160
162
|
|
|
161
163
|
// Set robustness, in order of consideration:
|
|
@@ -206,42 +208,41 @@ function buildKeySystemConfigurations(
|
|
|
206
208
|
// https://www.w3.org/TR/encrypted-media/#get-supported-configuration-and-consent
|
|
207
209
|
|
|
208
210
|
const videoCapabilities: IMediaCapability[] =
|
|
209
|
-
flatMap(videoRobustnesses, (robustness) =>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}));
|
|
211
|
+
flatMap(videoRobustnesses, (robustness) => {
|
|
212
|
+
return EME_DEFAULT_VIDEO_CODECS.map(contentType => {
|
|
213
|
+
return robustness === undefined ? { contentType } :
|
|
214
|
+
{ contentType, robustness };
|
|
215
|
+
});
|
|
216
|
+
});
|
|
216
217
|
|
|
217
218
|
const audioCapabilities: IMediaCapability[] =
|
|
218
|
-
flatMap(audioRobustnesses, (robustness) =>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
219
|
+
flatMap(audioRobustnesses, (robustness) => {
|
|
220
|
+
return EME_DEFAULT_AUDIO_CODECS.map(contentType => {
|
|
221
|
+
return robustness === undefined ? { contentType } :
|
|
222
|
+
{ contentType, robustness };
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const wantedMediaKeySystemConfiguration : MediaKeySystemConfiguration = {
|
|
227
|
+
initDataTypes: ["cenc"],
|
|
228
|
+
videoCapabilities,
|
|
229
|
+
audioCapabilities,
|
|
230
|
+
distinctiveIdentifier,
|
|
231
|
+
persistentState,
|
|
232
|
+
sessionTypes,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return [
|
|
236
|
+
wantedMediaKeySystemConfiguration,
|
|
237
|
+
|
|
238
|
+
// Some legacy implementations have issues with `audioCapabilities` and
|
|
239
|
+
// `videoCapabilities`, so we're including a supplementary
|
|
240
|
+
// `MediaKeySystemConfiguration` without those properties.
|
|
241
|
+
{ ...wantedMediaKeySystemConfiguration,
|
|
242
|
+
audioCapabilities: undefined ,
|
|
243
|
+
videoCapabilities: undefined,
|
|
244
|
+
} as unknown as MediaKeySystemConfiguration,
|
|
245
|
+
];
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
/**
|
|
@@ -210,23 +210,36 @@ export default function SessionEventsListener(
|
|
|
210
210
|
) : Promise<BufferSource | null> {
|
|
211
211
|
let timeoutId : number | undefined;
|
|
212
212
|
return new Promise<BufferSource | null>((res, rej) => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
try {
|
|
214
|
+
log.debug("DRM: Calling `getLicense`", messageType);
|
|
215
|
+
const getLicense = keySystemOptions.getLicense(message, messageType);
|
|
216
|
+
const getLicenseTimeout = isNullOrUndefined(getLicenseConfig.timeout) ?
|
|
217
|
+
10 * 1000 :
|
|
218
|
+
getLicenseConfig.timeout;
|
|
218
219
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
if (getLicenseTimeout >= 0) {
|
|
221
|
+
timeoutId = setTimeout(() => {
|
|
222
|
+
rej(new GetLicenseTimeoutError(
|
|
223
|
+
`"getLicense" timeout exceeded (${getLicenseTimeout} ms)`
|
|
224
|
+
));
|
|
225
|
+
}, getLicenseTimeout) as unknown as number;
|
|
226
|
+
}
|
|
227
|
+
Promise.resolve(getLicense)
|
|
228
|
+
.then(clearTimeoutAndResolve, clearTimeoutAndReject);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
clearTimeoutAndReject(err);
|
|
231
|
+
}
|
|
232
|
+
function clearTimeoutAndResolve(data : BufferSource | null) {
|
|
233
|
+
if (timeoutId !== undefined) {
|
|
234
|
+
clearTimeout(timeoutId);
|
|
235
|
+
}
|
|
236
|
+
res(data);
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
function clearTimeoutAndReject(err : unknown) {
|
|
239
|
+
if (timeoutId !== undefined) {
|
|
240
|
+
clearTimeout(timeoutId);
|
|
241
|
+
}
|
|
242
|
+
rej(err);
|
|
230
243
|
}
|
|
231
244
|
});
|
|
232
245
|
}
|
|
@@ -368,18 +368,28 @@ export async function scheduleRequestWithCdns<T>(
|
|
|
368
368
|
throw cancellationSignal.cancellationError;
|
|
369
369
|
}
|
|
370
370
|
if (updatedPrioritaryCdn === undefined) {
|
|
371
|
-
return
|
|
371
|
+
return cleanAndReject(prevRequestError);
|
|
372
372
|
}
|
|
373
373
|
if (updatedPrioritaryCdn !== nextWantedCdn) {
|
|
374
374
|
canceller.cancel();
|
|
375
375
|
waitPotentialBackoffAndRequest(updatedPrioritaryCdn, prevRequestError)
|
|
376
|
-
.then(
|
|
376
|
+
.then(cleanAndResolve, cleanAndReject);
|
|
377
377
|
}
|
|
378
378
|
}, canceller.signal);
|
|
379
379
|
|
|
380
380
|
cancellableSleep(blockedFor, canceller.signal)
|
|
381
|
-
.then(() => requestCdn(nextWantedCdn)
|
|
382
|
-
|
|
381
|
+
.then(() => requestCdn(nextWantedCdn)
|
|
382
|
+
.then(cleanAndResolve, cleanAndReject), noop);
|
|
383
|
+
|
|
384
|
+
function cleanAndResolve(response : T) {
|
|
385
|
+
unlinkCanceller();
|
|
386
|
+
res(response);
|
|
387
|
+
}
|
|
388
|
+
function cleanAndReject(err : unknown) {
|
|
389
|
+
unlinkCanceller();
|
|
390
|
+
rej(err);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
383
393
|
}
|
|
384
394
|
|
|
385
395
|
/**
|
|
@@ -209,7 +209,7 @@ function setMediaSourceDuration(
|
|
|
209
209
|
if (maxBufferedEnd < mediaSource.duration) {
|
|
210
210
|
try {
|
|
211
211
|
log.info("Init: Updating duration to what is currently buffered", maxBufferedEnd);
|
|
212
|
-
mediaSource.duration =
|
|
212
|
+
mediaSource.duration = maxBufferedEnd;
|
|
213
213
|
} catch (err) {
|
|
214
214
|
log.warn("Duration Updater: Can't update duration on the MediaSource.",
|
|
215
215
|
err instanceof Error ? err : "");
|
|
@@ -123,7 +123,7 @@ export default class RebufferingController
|
|
|
123
123
|
playbackRateUpdater.dispose();
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
let prevFreezingState : { attemptTimestamp : number } | null;
|
|
126
|
+
let prevFreezingState : { attemptTimestamp : number } | null = null;
|
|
127
127
|
|
|
128
128
|
this._playbackObserver.listen((observation) => {
|
|
129
129
|
const discontinuitiesStore = this._discontinuitiesStore;
|
|
@@ -19,6 +19,8 @@ import config from "../../../config";
|
|
|
19
19
|
import { formatError } from "../../../errors";
|
|
20
20
|
import log from "../../../log";
|
|
21
21
|
import { Representation } from "../../../manifest";
|
|
22
|
+
import cancellableSleep from "../../../utils/cancellable_sleep";
|
|
23
|
+
import noop from "../../../utils/noop";
|
|
22
24
|
import objectAssign from "../../../utils/object_assign";
|
|
23
25
|
import {
|
|
24
26
|
createMappedReference,
|
|
@@ -325,19 +327,27 @@ export default function AdaptationStream<T>(
|
|
|
325
327
|
defaultCode: "NONE",
|
|
326
328
|
defaultReason: "Unknown `RepresentationStream` error",
|
|
327
329
|
});
|
|
328
|
-
if (formattedError.code
|
|
330
|
+
if (formattedError.code !== "BUFFER_FULL_ERROR") {
|
|
331
|
+
representationStreamCallbacks.error(err);
|
|
332
|
+
} else {
|
|
329
333
|
const wba = wantedBufferAhead.getValue();
|
|
330
334
|
const lastBufferGoalRatio = bufferGoalRatioMap.get(representation.id) ?? 1;
|
|
331
|
-
|
|
335
|
+
// 70%, 49%, 34.3%, 24%, 16.81%, 11.76%, 8.24% and 5.76%
|
|
336
|
+
const newBufferGoalRatio = lastBufferGoalRatio * 0.7;
|
|
337
|
+
if (newBufferGoalRatio <= 0.05 || wba * newBufferGoalRatio <= 2) {
|
|
332
338
|
throw formattedError;
|
|
333
339
|
}
|
|
334
|
-
bufferGoalRatioMap.set(representation.id,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
340
|
+
bufferGoalRatioMap.set(representation.id, newBufferGoalRatio);
|
|
341
|
+
|
|
342
|
+
// We wait 4 seconds to let the situation evolve by itself before
|
|
343
|
+
// retrying loading segments with a lower buffer goal
|
|
344
|
+
cancellableSleep(4000, adapStreamCanceller.signal).then(() => {
|
|
345
|
+
return createRepresentationStream(representation,
|
|
346
|
+
terminateCurrentStream,
|
|
347
|
+
fastSwitchThreshold,
|
|
348
|
+
representationStreamCallbacks);
|
|
349
|
+
}).catch(noop);
|
|
339
350
|
}
|
|
340
|
-
representationStreamCallbacks.error(err);
|
|
341
351
|
},
|
|
342
352
|
terminating() {
|
|
343
353
|
terminatingRepStreamCanceller.cancel();
|