shaka-player 4.2.4 → 4.2.5
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/AUTHORS +1 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTORS +1 -0
- package/demo/index.html +1 -1
- package/dist/demo.compiled.debug.js +64 -64
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +64 -64
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +3 -2
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +12 -12
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +12 -12
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.externs.js +93 -79
- package/dist/shaka-player.compiled.debug.js +557 -550
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +93 -79
- package/dist/shaka-player.compiled.js +806 -806
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +10 -0
- package/dist/shaka-player.ui.debug.d.ts +10 -0
- package/dist/shaka-player.ui.debug.externs.js +93 -79
- package/dist/shaka-player.ui.debug.js +583 -576
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +93 -79
- package/dist/shaka-player.ui.js +953 -953
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/player.js +2 -1
- package/lib/cea/cea708_window.js +2 -2
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +12 -2
- package/lib/media/content_workarounds.js +12 -0
- package/lib/player.js +1 -1
- package/lib/polyfill/abort_controller.js +143 -0
- package/lib/polyfill/media_capabilities.js +59 -9
- package/lib/text/ttml_text_parser.js +32 -7
- package/lib/text/ui_text_displayer.js +22 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +10 -3
- package/lib/util/stream_utils.js +30 -10
- package/package.json +1 -1
- package/shaka-player.uncompiled.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +1 -0
- package/test/hls/hls_parser_unit.js +1 -0
- package/test/media/drm_engine_unit.js +11 -1
- package/test/media/media_source_engine_integration.js +78 -2
- package/test/polyfill/media_capabilities_unit.js +261 -0
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +3 -2
- package/test/test/util/util.js +3 -2
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/ttml_text_parser_unit.js +119 -86
- package/test/text/ui_text_displayer_unit.js +76 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/stream_utils_unit.js +18 -0
- package/test/util/xml_utils_unit.js +6 -1
- package/ui/controls.js +45 -36
package/docs/tutorials/ui.md
CHANGED
|
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
|
|
|
30
30
|
The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
|
|
31
31
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
32
32
|
<div data-shaka-player-container style="max-width:40em"
|
|
33
|
-
data-shaka-player-cast-receiver-id="
|
|
33
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
34
34
|
<!-- The data-shaka-player tag will make the UI library use this video element.
|
|
35
35
|
If no video is provided, the UI will automatically make one inside the container div. -->
|
|
36
36
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
|
|
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
|
|
|
111
111
|
<!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
|
|
112
112
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
113
113
|
<div data-shaka-player-container style="max-width:40em"
|
|
114
|
-
data-shaka-player-cast-receiver-id="
|
|
114
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
115
115
|
</div>
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -139,7 +139,7 @@ casting to an Android receiver app.
|
|
|
139
139
|
|
|
140
140
|
```html
|
|
141
141
|
<div data-shaka-player-container style="max-width:40em"
|
|
142
|
-
data-shaka-player-cast-receiver-id="
|
|
142
|
+
data-shaka-player-cast-receiver-id="A67E4E9A"
|
|
143
143
|
data-shaka-player-cast-android-receiver-compatible="true">
|
|
144
144
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
145
145
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
|
|
|
154
154
|
|
|
155
155
|
```html
|
|
156
156
|
<div data-shaka-player-container style="max-width:40em"
|
|
157
|
-
data-shaka-player-cast-receiver-id="
|
|
157
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
158
158
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
159
159
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
160
160
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
@@ -165,7 +165,7 @@ or
|
|
|
165
165
|
|
|
166
166
|
```html
|
|
167
167
|
<div data-shaka-player-container style="max-width:40em"
|
|
168
|
-
data-shaka-player-cast-receiver-id="
|
|
168
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
169
169
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
170
170
|
<!-- The manifest url in the src attribute will be auto loaded -->
|
|
171
171
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -178,7 +178,7 @@ call to the first one fails.
|
|
|
178
178
|
|
|
179
179
|
```html
|
|
180
180
|
<div data-shaka-player-container style="max-width:40em"
|
|
181
|
-
data-shaka-player-cast-receiver-id="
|
|
181
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
182
182
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
183
183
|
<!-- Try this first -->
|
|
184
184
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -222,7 +222,7 @@ const video = controls.getVideo();
|
|
|
222
222
|
// Receiver Compatability.
|
|
223
223
|
ui.configure({
|
|
224
224
|
// Set the castReceiverAppId
|
|
225
|
-
'castReceiverAppId': '
|
|
225
|
+
'castReceiverAppId': 'A67E4E9A',
|
|
226
226
|
// Enable casting to native Android Apps (e.g. Android TV Apps)
|
|
227
227
|
'castAndroidReceiverCompatible': true,
|
|
228
228
|
});
|
|
@@ -29,8 +29,8 @@ You can build Shaka on Linux, Windows, or Mac.
|
|
|
29
29
|
To get the sources and compile the library, you will need:
|
|
30
30
|
* {@link https://git-scm.com/downloads Git v1.9+}
|
|
31
31
|
* {@link https://www.python.org/downloads/ Python v2.7 or v3.5+}
|
|
32
|
-
* {@link https://java.com/en/download/ Java Runtime Environment
|
|
33
|
-
* {@link https://nodejs.org/en/download/ NodeJS
|
|
32
|
+
* {@link https://java.com/en/download/ Java Runtime Environment v14+}
|
|
33
|
+
* {@link https://nodejs.org/en/download/ NodeJS v14+}
|
|
34
34
|
* A local web server, such as {@link https://httpd.apache.org/ Apache}
|
|
35
35
|
* _NOTE: A local web server is required because browsers place restrictions
|
|
36
36
|
on applications from file:/// URLs._
|
package/externs/shaka/player.js
CHANGED
|
@@ -131,7 +131,8 @@ shaka.extern.StateChange;
|
|
|
131
131
|
* @property {number} manifestTimeSeconds
|
|
132
132
|
* The amount of time it took to download and parse the manifest.
|
|
133
133
|
* @property {number} drmTimeSeconds
|
|
134
|
-
* The amount of time it took to download the first drm key
|
|
134
|
+
* The amount of time it took to download the first drm key, and load that key
|
|
135
|
+
* into the drm system.
|
|
135
136
|
* @property {number} playTime
|
|
136
137
|
* The total time spent in a playing state in seconds.
|
|
137
138
|
* @property {number} pauseTime
|
package/lib/cea/cea708_window.js
CHANGED
|
@@ -420,7 +420,7 @@ shaka.cea.Cea708Window.TextJustification = {
|
|
|
420
420
|
shaka.cea.Cea708Window.MAX_COLS = 42;
|
|
421
421
|
|
|
422
422
|
/**
|
|
423
|
-
* Maximum of
|
|
423
|
+
* Maximum of 16 rows that can be indexed from 0 to 15.
|
|
424
424
|
* @private @const {number}
|
|
425
425
|
*/
|
|
426
|
-
shaka.cea.Cea708Window.MAX_ROWS =
|
|
426
|
+
shaka.cea.Cea708Window.MAX_ROWS = 16;
|
|
@@ -67,7 +67,7 @@ shaka.dash.SegmentTemplate = class {
|
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
69
|
} else if (info.segmentDuration) {
|
|
70
|
-
if (!isUpdate) {
|
|
70
|
+
if (!isUpdate && context.adaptationSet.contentType !== 'image') {
|
|
71
71
|
context.presentationTimeline.notifyMaxSegmentDuration(
|
|
72
72
|
info.segmentDuration);
|
|
73
73
|
context.presentationTimeline.notifyMinSegmentStartTime(
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -1049,7 +1049,7 @@ shaka.hls.HlsParser = class {
|
|
|
1049
1049
|
shaka.log.debug('Guessing audio-only.');
|
|
1050
1050
|
type = ContentType.AUDIO;
|
|
1051
1051
|
ignoreStream = true;
|
|
1052
|
-
} else if (res.video.length) {
|
|
1052
|
+
} else if (res.video.length && !res.audio.length) {
|
|
1053
1053
|
// There are associated video streams. Assume this is audio.
|
|
1054
1054
|
shaka.log.debug('Guessing audio-only.');
|
|
1055
1055
|
type = ContentType.AUDIO;
|
|
@@ -1665,9 +1665,16 @@ shaka.hls.HlsParser = class {
|
|
|
1665
1665
|
/** @type {!Map.<number, number>} */
|
|
1666
1666
|
const mediaSequenceToStartTime = new Map();
|
|
1667
1667
|
|
|
1668
|
+
const prevLowLatencyMode = this.lowLatencyMode_;
|
|
1668
1669
|
const segments = this.createSegments_(verbatimMediaPlaylistUri, playlist,
|
|
1669
1670
|
type, mimeType, mediaSequenceToStartTime, mediaVariables, codecs);
|
|
1670
1671
|
|
|
1672
|
+
// This happens when autoLowLatencyMode is true, so we need set the
|
|
1673
|
+
// correct lowLatencyPresentationDelay_
|
|
1674
|
+
if (prevLowLatencyMode != this.lowLatencyMode_) {
|
|
1675
|
+
this.determinePresentationType_(playlist);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1671
1678
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1672
1679
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1673
1680
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -1796,8 +1803,11 @@ shaka.hls.HlsParser = class {
|
|
|
1796
1803
|
shaka.util.Error.Code.HLS_AES_128_INVALID_KEY_LENGTH);
|
|
1797
1804
|
}
|
|
1798
1805
|
|
|
1806
|
+
const algorithm = {
|
|
1807
|
+
name: 'AES-CBC',
|
|
1808
|
+
};
|
|
1799
1809
|
keyInfo.cryptoKey = await window.crypto.subtle.importKey(
|
|
1800
|
-
'raw', keyResponse.data,
|
|
1810
|
+
'raw', keyResponse.data, algorithm, true, ['decrypt']);
|
|
1801
1811
|
keyInfo.fetchKey = undefined; // No longer needed.
|
|
1802
1812
|
};
|
|
1803
1813
|
|
|
@@ -69,6 +69,18 @@ shaka.media.ContentWorkarounds = class {
|
|
|
69
69
|
})
|
|
70
70
|
.fullBox('encv', onEncryptionMetadataBox)
|
|
71
71
|
.fullBox('enca', onEncryptionMetadataBox)
|
|
72
|
+
.fullBox('hev1', (box) => {
|
|
73
|
+
boxesToModify.push({
|
|
74
|
+
box,
|
|
75
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
76
|
+
});
|
|
77
|
+
})
|
|
78
|
+
.fullBox('hvc1', (box) => {
|
|
79
|
+
boxesToModify.push({
|
|
80
|
+
box,
|
|
81
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
82
|
+
});
|
|
83
|
+
})
|
|
72
84
|
.fullBox('avc1', (box) => {
|
|
73
85
|
boxesToModify.push({
|
|
74
86
|
box,
|
package/lib/player.js
CHANGED
|
@@ -6595,7 +6595,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6595
6595
|
* @define {string} A version number taken from git at compile time.
|
|
6596
6596
|
* @export
|
|
6597
6597
|
*/
|
|
6598
|
-
shaka.Player.version = 'v4.2.
|
|
6598
|
+
shaka.Player.version = 'v4.2.5-uncompiled';
|
|
6599
6599
|
|
|
6600
6600
|
// Initialize the deprecation system using the version string we just set
|
|
6601
6601
|
// on the player.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.polyfill.AbortController');
|
|
8
|
+
|
|
9
|
+
goog.require('shaka.polyfill');
|
|
10
|
+
goog.require('shaka.util.FakeEvent');
|
|
11
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @summary A polyfill for systems that do not implement AbortController.
|
|
15
|
+
* This is used both with the fetch API for HTTP requests and inside the HLS
|
|
16
|
+
* parser.
|
|
17
|
+
* @export
|
|
18
|
+
* @extends AbortController
|
|
19
|
+
*/
|
|
20
|
+
shaka.polyfill.AbortController = class {
|
|
21
|
+
/**
|
|
22
|
+
* Install the polyfill if needed.
|
|
23
|
+
* @export
|
|
24
|
+
*/
|
|
25
|
+
static install() {
|
|
26
|
+
if (window.AbortController) {
|
|
27
|
+
// Not needed.
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
window.AbortController = shaka.polyfill.AbortController;
|
|
32
|
+
window.AbortSignal = shaka.polyfill.AbortController.AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** */
|
|
36
|
+
constructor() {
|
|
37
|
+
/** @private {!shaka.polyfill.AbortController.AbortSignal} */
|
|
38
|
+
this.signal_ = new shaka.polyfill.AbortController.AbortSignal();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
* @suppress {const|duplicate} Since the original is defined as "const", we
|
|
44
|
+
* need this suppression to override it.
|
|
45
|
+
*/
|
|
46
|
+
get signal() {
|
|
47
|
+
return this.signal_;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {*=} reason
|
|
52
|
+
* @override
|
|
53
|
+
*/
|
|
54
|
+
abort(reason) {
|
|
55
|
+
this.signal_.doAbort_(reason);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @summary A polyfill for AbortSignal, part of the AbortController API.
|
|
61
|
+
* @implements {AbortSignal}
|
|
62
|
+
*/
|
|
63
|
+
shaka.polyfill.AbortController.AbortSignal =
|
|
64
|
+
class extends shaka.util.FakeEventTarget {
|
|
65
|
+
/** */
|
|
66
|
+
constructor() {
|
|
67
|
+
super();
|
|
68
|
+
|
|
69
|
+
/** @private {boolean} */
|
|
70
|
+
this.aborted_ = false;
|
|
71
|
+
|
|
72
|
+
/** @private {*} */
|
|
73
|
+
this.reason_ = undefined;
|
|
74
|
+
|
|
75
|
+
/** @type {?function(!Event)} */
|
|
76
|
+
this.onabort = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @override */
|
|
80
|
+
get aborted() {
|
|
81
|
+
return this.aborted_;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @return {*} */
|
|
85
|
+
get reason() {
|
|
86
|
+
return this.reason_;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {*} reason
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
doAbort_(reason) {
|
|
94
|
+
if (this.aborted_) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.aborted_ = true;
|
|
99
|
+
this.reason_ = reason;
|
|
100
|
+
if (this.reason_ === undefined) {
|
|
101
|
+
// This is equivalent to a native implementation.
|
|
102
|
+
this.reason_ = new DOMException(
|
|
103
|
+
'signal is aborted without reason', 'AbortError');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// According to MDN:
|
|
107
|
+
// "Event type - A generic Event with no added properties."
|
|
108
|
+
const event = new shaka.util.FakeEvent('abort');
|
|
109
|
+
if (this.onabort) {
|
|
110
|
+
this.onabort(event);
|
|
111
|
+
}
|
|
112
|
+
this.dispatchEvent(event);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {*=} reason
|
|
118
|
+
* @return {!AbortSignal}
|
|
119
|
+
*/
|
|
120
|
+
static abort(reason) {
|
|
121
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
122
|
+
signal.doAbort_(reason);
|
|
123
|
+
return signal;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {number} timeMs
|
|
128
|
+
* @return {!AbortSignal}
|
|
129
|
+
*/
|
|
130
|
+
static timeout(timeMs) {
|
|
131
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
132
|
+
|
|
133
|
+
window.setTimeout(() => {
|
|
134
|
+
// This is equivalent to a native implementation.
|
|
135
|
+
signal.doAbort_(new DOMException('signal timed out', 'TimeoutError'));
|
|
136
|
+
}, timeMs);
|
|
137
|
+
|
|
138
|
+
return signal;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
shaka.polyfill.register(shaka.polyfill.AbortController.install);
|
|
@@ -8,6 +8,7 @@ goog.provide('shaka.polyfill.MediaCapabilities');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.log');
|
|
10
10
|
goog.require('shaka.polyfill');
|
|
11
|
+
goog.require('shaka.util.Error');
|
|
11
12
|
goog.require('shaka.util.Platform');
|
|
12
13
|
|
|
13
14
|
|
|
@@ -96,37 +97,48 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
96
97
|
return res;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
const videoConfig = mediaDecodingConfig['video'];
|
|
101
|
+
const audioConfig = mediaDecodingConfig['audio'];
|
|
102
|
+
|
|
99
103
|
if (mediaDecodingConfig.type == 'media-source') {
|
|
100
104
|
if (!shaka.util.Platform.supportsMediaSource()) {
|
|
101
105
|
return res;
|
|
102
106
|
}
|
|
103
107
|
// Use 'MediaSource.isTypeSupported' to check if the stream is supported.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
// Cast platforms will additionally check canDisplayType(), which
|
|
109
|
+
// accepts extended MIME type parameters.
|
|
110
|
+
// See: https://github.com/shaka-project/shaka-player/issues/4726
|
|
111
|
+
if (videoConfig) {
|
|
112
|
+
let isSupported;
|
|
113
|
+
if (shaka.util.Platform.isChromecast()) {
|
|
114
|
+
isSupported =
|
|
115
|
+
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
116
|
+
} else {
|
|
117
|
+
isSupported = MediaSource.isTypeSupported(videoConfig.contentType);
|
|
118
|
+
}
|
|
107
119
|
if (!isSupported) {
|
|
108
120
|
return res;
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
|
|
112
|
-
if (
|
|
113
|
-
const contentType =
|
|
124
|
+
if (audioConfig) {
|
|
125
|
+
const contentType = audioConfig.contentType;
|
|
114
126
|
const isSupported = MediaSource.isTypeSupported(contentType);
|
|
115
127
|
if (!isSupported) {
|
|
116
128
|
return res;
|
|
117
129
|
}
|
|
118
130
|
}
|
|
119
131
|
} else if (mediaDecodingConfig.type == 'file') {
|
|
120
|
-
if (
|
|
121
|
-
const contentType =
|
|
132
|
+
if (videoConfig) {
|
|
133
|
+
const contentType = videoConfig.contentType;
|
|
122
134
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
123
135
|
if (!isSupported) {
|
|
124
136
|
return res;
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
if (
|
|
129
|
-
const contentType =
|
|
140
|
+
if (audioConfig) {
|
|
141
|
+
const contentType = audioConfig.contentType;
|
|
130
142
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
131
143
|
if (!isSupported) {
|
|
132
144
|
return res;
|
|
@@ -201,6 +213,44 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
201
213
|
|
|
202
214
|
return res;
|
|
203
215
|
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Checks if the given media parameters of the video or audio streams are
|
|
219
|
+
* supported by the Cast platform.
|
|
220
|
+
* @param {!VideoConfiguration} videoConfig The 'video' field of the
|
|
221
|
+
* MediaDecodingConfiguration.
|
|
222
|
+
* @return {boolean} `true` when the stream can be displayed on a Cast device.
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
static canCastDisplayType_(videoConfig) {
|
|
226
|
+
if (!(window.cast)) {
|
|
227
|
+
shaka.log.error('Expected cast namespace to be available!');
|
|
228
|
+
throw new shaka.util.Error(
|
|
229
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
230
|
+
shaka.util.Error.Category.CAST,
|
|
231
|
+
shaka.util.Error.Code.CAST_API_UNAVAILABLE);
|
|
232
|
+
} else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
233
|
+
shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
|
|
234
|
+
'MediaSource.isTypeSupported() for type support.');
|
|
235
|
+
return MediaSource.isTypeSupported(videoConfig.contentType);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let displayType = videoConfig.contentType;
|
|
239
|
+
if (videoConfig.width && videoConfig.height) {
|
|
240
|
+
displayType +=
|
|
241
|
+
`; width=${videoConfig.width}; height=${videoConfig.height}`;
|
|
242
|
+
}
|
|
243
|
+
if (videoConfig.framerate) {
|
|
244
|
+
displayType += `; framerate=${videoConfig.framerate}`;
|
|
245
|
+
}
|
|
246
|
+
if (videoConfig.transferFunction === 'pq') {
|
|
247
|
+
// A "PQ" transfer function indicates this is an HDR-capable stream;
|
|
248
|
+
// "smpte2084" is the published standard. We need to inform the platform
|
|
249
|
+
// this query is specifically for HDR.
|
|
250
|
+
displayType += '; eotf=smpte2084';
|
|
251
|
+
}
|
|
252
|
+
return cast.__platform__.canDisplayType(displayType);
|
|
253
|
+
}
|
|
204
254
|
};
|
|
205
255
|
|
|
206
256
|
/**
|
|
@@ -136,7 +136,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
const cue = TtmlTextParser.parseCue_(
|
|
139
|
-
body, time
|
|
139
|
+
body, time, rateInfo, metadataElements, styles,
|
|
140
140
|
regionElements, cueRegions, whitespaceTrim,
|
|
141
141
|
cellResolutionInfo, /* parentCueElement= */ null,
|
|
142
142
|
/* isContent= */ false);
|
|
@@ -157,7 +157,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
157
157
|
* Parses a TTML node into a Cue.
|
|
158
158
|
*
|
|
159
159
|
* @param {!Node} cueNode
|
|
160
|
-
* @param {
|
|
160
|
+
* @param {shaka.extern.TextParser.TimeContext} timeContext
|
|
161
161
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
162
162
|
* @param {!Array.<!Element>} metadataElements
|
|
163
163
|
* @param {!Array.<!Element>} styles
|
|
@@ -171,7 +171,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
171
171
|
* @private
|
|
172
172
|
*/
|
|
173
173
|
static parseCue_(
|
|
174
|
-
cueNode,
|
|
174
|
+
cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
|
|
175
175
|
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
|
|
176
176
|
/** @type {Element} */
|
|
177
177
|
let cueElement;
|
|
@@ -236,7 +236,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
236
236
|
for (const childNode of cueElement.childNodes) {
|
|
237
237
|
const nestedCue = shaka.text.TtmlTextParser.parseCue_(
|
|
238
238
|
childNode,
|
|
239
|
-
|
|
239
|
+
timeContext,
|
|
240
240
|
rateInfo,
|
|
241
241
|
metadataElements,
|
|
242
242
|
styles,
|
|
@@ -293,15 +293,20 @@ shaka.text.TtmlTextParser = class {
|
|
|
293
293
|
if (start == null) {
|
|
294
294
|
start = 0;
|
|
295
295
|
}
|
|
296
|
-
start +=
|
|
296
|
+
start += timeContext.periodStart;
|
|
297
297
|
|
|
298
298
|
// If end is null, that means the duration is effectively infinite.
|
|
299
299
|
if (end == null) {
|
|
300
300
|
end = Infinity;
|
|
301
301
|
} else {
|
|
302
|
-
end +=
|
|
302
|
+
end += timeContext.periodStart;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
// Clip times to segment boundaries.
|
|
306
|
+
// https://github.com/shaka-project/shaka-player/issues/4631
|
|
307
|
+
start = Math.max(start, timeContext.segmentStart);
|
|
308
|
+
end = Math.min(end, timeContext.segmentEnd);
|
|
309
|
+
|
|
305
310
|
if (!hasTimeAttributes && nestedCues.length > 0) {
|
|
306
311
|
// If no time is defined for this cue, base the timing information on
|
|
307
312
|
// the time of the nested cues. In the case of multiple nested cues with
|
|
@@ -569,8 +574,28 @@ shaka.text.TtmlTextParser = class {
|
|
|
569
574
|
|
|
570
575
|
const fontFamily = TtmlTextParser.getStyleAttribute_(
|
|
571
576
|
cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
|
|
577
|
+
// See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
|
|
572
578
|
if (fontFamily) {
|
|
573
|
-
|
|
579
|
+
switch (fontFamily) {
|
|
580
|
+
case 'monospaceSerif':
|
|
581
|
+
cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
|
|
582
|
+
break;
|
|
583
|
+
case 'proportionalSansSerif':
|
|
584
|
+
cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
|
|
585
|
+
break;
|
|
586
|
+
case 'sansSerif':
|
|
587
|
+
cue.fontFamily = 'sans-serif';
|
|
588
|
+
break;
|
|
589
|
+
case 'monospaceSansSerif':
|
|
590
|
+
cue.fontFamily = 'Consolas,monospace';
|
|
591
|
+
break;
|
|
592
|
+
case 'proportionalSerif':
|
|
593
|
+
cue.fontFamily = 'serif';
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
cue.fontFamily = fontFamily;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
574
599
|
}
|
|
575
600
|
|
|
576
601
|
const fontWeight = TtmlTextParser.getStyleAttribute_(
|
|
@@ -199,6 +199,19 @@ shaka.text.UITextDisplayer = class {
|
|
|
199
199
|
this.isTextVisible_ = on;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
isElementUnderTextContainer_(elemToCheck) {
|
|
206
|
+
while (elemToCheck != null) {
|
|
207
|
+
if (elemToCheck == this.textContainer_) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
elemToCheck = elemToCheck.parentElement;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
/**
|
|
203
216
|
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
204
217
|
* @param {!HTMLElement} container
|
|
@@ -260,6 +273,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
260
273
|
cueRegistry = this.currentCuesMap_.get(cue);
|
|
261
274
|
wrapper = cueRegistry.wrapper;
|
|
262
275
|
updateDOM = true;
|
|
276
|
+
} else if (!this.isElementUnderTextContainer_(wrapper)) {
|
|
277
|
+
// We found that the wrapper needs to be in the DOM
|
|
278
|
+
updateDOM = true;
|
|
263
279
|
}
|
|
264
280
|
}
|
|
265
281
|
|
|
@@ -276,6 +292,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
276
292
|
const topCue = parents.pop();
|
|
277
293
|
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
278
294
|
}
|
|
295
|
+
|
|
279
296
|
if (updateDOM) {
|
|
280
297
|
for (const element of toUproot) {
|
|
281
298
|
// NOTE: Because we uproot shared region elements, too, we might hit an
|
|
@@ -578,7 +595,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
578
595
|
// (for vertical growing left) is aligned at the line.
|
|
579
596
|
// TODO: Implement line alignment with line number.
|
|
580
597
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
581
|
-
if (cue.line) {
|
|
598
|
+
if (cue.line != null) {
|
|
582
599
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
583
600
|
// When setting absolute positioning, you need to set x/y/width/height
|
|
584
601
|
// so the element is positioned correctly. Set these as default and
|
|
@@ -591,21 +608,21 @@ shaka.text.UITextDisplayer = class {
|
|
|
591
608
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
592
609
|
style.top = cue.line + '%';
|
|
593
610
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
594
|
-
style.bottom = cue.line + '%';
|
|
611
|
+
style.bottom = (100 - cue.line) + '%';
|
|
595
612
|
}
|
|
596
613
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
597
614
|
style.height = '100%';
|
|
598
615
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
599
616
|
style.left = cue.line + '%';
|
|
600
617
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
601
|
-
style.right = cue.line + '%';
|
|
618
|
+
style.right = (100 - cue.line) + '%';
|
|
602
619
|
}
|
|
603
620
|
} else {
|
|
604
621
|
style.height = '100%';
|
|
605
622
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
606
623
|
style.right = cue.line + '%';
|
|
607
624
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
608
|
-
style.left = cue.line + '%';
|
|
625
|
+
style.left = (100 - cue.line) + '%';
|
|
609
626
|
}
|
|
610
627
|
}
|
|
611
628
|
}
|
|
@@ -615,7 +632,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
615
632
|
|
|
616
633
|
// The position defines the indent of the text container in the
|
|
617
634
|
// direction defined by the writing direction.
|
|
618
|
-
if (cue.position) {
|
|
635
|
+
if (cue.position != null) {
|
|
619
636
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
620
637
|
style.paddingLeft = cue.position;
|
|
621
638
|
} else {
|
package/lib/util/platform.js
CHANGED
|
@@ -174,7 +174,7 @@ shaka.util.Platform = class {
|
|
|
174
174
|
* @return {?number} A major version number or null if not Chromium-based.
|
|
175
175
|
*/
|
|
176
176
|
static chromeVersion() {
|
|
177
|
-
if (!shaka.util.Platform.
|
|
177
|
+
if (!shaka.util.Platform.isChrome()) {
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -213,6 +213,7 @@ shaka.util.Platform = class {
|
|
|
213
213
|
return !!navigator.vendor && navigator.vendor.includes('Apple') &&
|
|
214
214
|
!shaka.util.Platform.isTizen() &&
|
|
215
215
|
!shaka.util.Platform.isEOS() &&
|
|
216
|
+
!shaka.util.Platform.isVirginMedia() &&
|
|
216
217
|
!shaka.util.Platform.isPS4() &&
|
|
217
218
|
!shaka.util.Platform.isAmazonFireTV();
|
|
218
219
|
}
|
|
@@ -237,6 +238,13 @@ shaka.util.Platform = class {
|
|
|
237
238
|
return shaka.util.Platform.userAgentContains_('PlayStation 4');
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Check if the current platform is Virgin Media device.
|
|
243
|
+
*/
|
|
244
|
+
static isVirginMedia() {
|
|
245
|
+
return shaka.util.Platform.userAgentContains_('VirginMedia');
|
|
246
|
+
}
|
|
247
|
+
|
|
240
248
|
/**
|
|
241
249
|
* Check if the current platform is Amazon Fire TV.
|
|
242
250
|
* https://developer.amazon.com/docs/fire-tv/identify-amazon-fire-tv-devices.html
|
|
@@ -11,6 +11,7 @@ goog.require('shaka.abr.SimpleAbrManager');
|
|
|
11
11
|
goog.require('shaka.log');
|
|
12
12
|
goog.require('shaka.net.NetworkingEngine');
|
|
13
13
|
goog.require('shaka.util.ConfigUtils');
|
|
14
|
+
goog.require('shaka.util.FairPlayUtils');
|
|
14
15
|
goog.require('shaka.util.LanguageUtils');
|
|
15
16
|
goog.require('shaka.util.ManifestParserUtils');
|
|
16
17
|
goog.require('shaka.util.Platform');
|
|
@@ -67,9 +68,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
67
68
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
68
69
|
delayLicenseRequestUntilPlayed: false,
|
|
69
70
|
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const keySystem = drmInfo.keySystem;
|
|
72
|
+
if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
|
|
73
|
+
const cert = drmInfo.serverCertificate;
|
|
74
|
+
const contentId =
|
|
75
|
+
shaka.util.FairPlayUtils.defaultGetContentId(initData);
|
|
76
|
+
initData = shaka.util.FairPlayUtils.initDataTransform(
|
|
77
|
+
initData, contentId, cert);
|
|
78
|
+
}
|
|
79
|
+
return initData;
|
|
73
80
|
},
|
|
74
81
|
logLicenseExchange: false,
|
|
75
82
|
updateExpirationTime: 1,
|