shaka-player 4.9.35 → 4.9.36
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/CONTRIBUTORS +1 -0
- package/README.md +9 -3
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +5 -5
- package/dist/locales.js +36 -36
- package/dist/shaka-player.compiled.d.ts +1 -0
- package/dist/shaka-player.compiled.debug.d.ts +1 -0
- package/dist/shaka-player.compiled.debug.externs.js +4 -3
- package/dist/shaka-player.compiled.debug.js +436 -421
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +4 -3
- package/dist/shaka-player.compiled.js +1263 -1251
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.dash.d.ts +4099 -0
- package/dist/shaka-player.dash.debug.d.ts +4099 -0
- package/dist/shaka-player.dash.debug.externs.js +4188 -0
- package/dist/shaka-player.dash.debug.js +2161 -0
- package/dist/shaka-player.dash.debug.map +8 -0
- package/dist/shaka-player.dash.externs.js +4188 -0
- package/dist/shaka-player.dash.js +1007 -0
- package/dist/shaka-player.dash.map +8 -0
- package/dist/shaka-player.hls.d.ts +4196 -0
- package/dist/shaka-player.hls.debug.d.ts +4196 -0
- package/dist/shaka-player.hls.debug.externs.js +4403 -0
- package/dist/shaka-player.hls.debug.js +2243 -0
- package/dist/shaka-player.hls.debug.map +8 -0
- package/dist/shaka-player.hls.externs.js +4403 -0
- package/dist/shaka-player.hls.js +1076 -0
- package/dist/shaka-player.hls.map +8 -0
- package/dist/shaka-player.ui.d.ts +1 -0
- package/dist/shaka-player.ui.debug.d.ts +1 -0
- package/dist/shaka-player.ui.debug.externs.js +4 -3
- package/dist/shaka-player.ui.debug.js +460 -445
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +4 -3
- package/dist/shaka-player.ui.js +1462 -1453
- package/dist/shaka-player.ui.map +3 -3
- package/externs/shaka/player.js +8 -4
- package/lib/cast/cast_proxy.js +48 -10
- package/lib/cea/cea708_service.js +9 -16
- package/lib/dash/dash_parser.js +23 -16
- package/lib/dash/segment_template.js +41 -40
- package/lib/hls/hls_parser.js +2 -2
- package/lib/media/content_workarounds.js +110 -72
- package/lib/media/drm_engine.js +72 -11
- package/lib/media/gap_jumping_controller.js +14 -1
- package/lib/media/media_source_engine.js +29 -11
- package/lib/media/region_timeline.js +4 -3
- package/lib/media/streaming_engine.js +37 -11
- package/lib/net/http_plugin_utils.js +1 -1
- package/lib/net/networking_engine.js +8 -0
- package/lib/net/networking_utils.js +1 -0
- package/lib/player.js +109 -37
- package/lib/polyfill/patchedmediakeys_apple.js +21 -0
- package/lib/text/cue.js +6 -2
- package/lib/text/mp4_ttml_parser.js +94 -6
- package/lib/text/ui_text_displayer.js +35 -4
- package/lib/transmuxer/ac3_transmuxer.js +6 -1
- package/lib/util/config_utils.js +13 -3
- package/lib/util/drm_utils.js +8 -0
- package/lib/util/error.js +6 -0
- package/lib/util/mp4_generator.js +19 -4
- package/lib/util/periods.js +4 -2
- package/lib/util/platform.js +60 -12
- package/lib/util/player_configuration.js +6 -1
- package/lib/util/stream_utils.js +18 -0
- package/lib/util/tXml.js +3 -2
- package/package.json +3 -3
- package/ui/controls.js +2 -6
- package/ui/less/thumbnails.less +1 -0
- package/ui/statistics_button.js +3 -1
- package/ui/ui.js +44 -35
|
@@ -25,6 +25,25 @@ goog.require('shaka.util.StringUtils');
|
|
|
25
25
|
* @export
|
|
26
26
|
*/
|
|
27
27
|
shaka.polyfill.PatchedMediaKeysApple = class {
|
|
28
|
+
/**
|
|
29
|
+
* Installs the polyfill if needed.
|
|
30
|
+
*/
|
|
31
|
+
static defaultInstall() {
|
|
32
|
+
if (!window.HTMLVideoElement || !window.WebKitMediaKeys) {
|
|
33
|
+
// No HTML5 video or no prefixed EME.
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (navigator.requestMediaKeySystemAccess &&
|
|
37
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
38
|
+
MediaKeySystemAccess.prototype.getConfiguration) {
|
|
39
|
+
// Unprefixed EME available
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// If there is no unprefixed EME and prefixed EME exists, apply installation
|
|
43
|
+
// by default. Eg: older versions of Safari.
|
|
44
|
+
shaka.polyfill.PatchedMediaKeysApple.install();
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
/**
|
|
29
48
|
* Installs the polyfill if needed.
|
|
30
49
|
* @param {boolean=} enableUninstall enables uninstalling the polyfill
|
|
@@ -798,3 +817,5 @@ shaka.polyfill.PatchedMediaKeysApple.MediaKeyStatusMap = class {
|
|
|
798
817
|
* @private {string}
|
|
799
818
|
*/
|
|
800
819
|
shaka.polyfill.PatchedMediaKeysApple.apiName_ = 'apple';
|
|
820
|
+
|
|
821
|
+
shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysApple.defaultInstall);
|
package/lib/text/cue.js
CHANGED
|
@@ -367,8 +367,12 @@ shaka.text.Cue = class {
|
|
|
367
367
|
// performance optimization. We can avoid the more expensive recursive
|
|
368
368
|
// checks if the top-level properties don't match.
|
|
369
369
|
// See: https://github.com/shaka-project/shaka-player/issues/3018
|
|
370
|
-
if (cue1.
|
|
371
|
-
|
|
370
|
+
if (cue1.payload != cue2.payload) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.001;
|
|
374
|
+
if (!isDiffNegligible(cue1.startTime, cue2.startTime) ||
|
|
375
|
+
!isDiffNegligible(cue1.endTime, cue2.endTime)) {
|
|
372
376
|
return false;
|
|
373
377
|
}
|
|
374
378
|
for (const k in cue1) {
|
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.text.Mp4TtmlParser');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
9
10
|
goog.require('shaka.text.TextEngine');
|
|
10
11
|
goog.require('shaka.text.TtmlTextParser');
|
|
12
|
+
goog.require('shaka.util.BufferUtils');
|
|
11
13
|
goog.require('shaka.util.Error');
|
|
14
|
+
goog.require('shaka.util.Mp4BoxParsers');
|
|
12
15
|
goog.require('shaka.util.Mp4Parser');
|
|
16
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
/**
|
|
@@ -78,25 +82,109 @@ shaka.text.Mp4TtmlParser = class {
|
|
|
78
82
|
parseMedia(data, time, uri) {
|
|
79
83
|
const Mp4Parser = shaka.util.Mp4Parser;
|
|
80
84
|
|
|
81
|
-
let sawMDAT = false;
|
|
82
85
|
let payload = [];
|
|
86
|
+
let defaultSampleSize = null;
|
|
87
|
+
|
|
88
|
+
/** @type {!Array<Uint8Array>} */
|
|
89
|
+
const mdats = [];
|
|
90
|
+
|
|
91
|
+
/* @type {!Map<number,!Array<number>>} */
|
|
92
|
+
const subSampleSizesPerSample = new Map();
|
|
93
|
+
|
|
94
|
+
/** @type {!Array<number>} */
|
|
95
|
+
const sampleSizes = [];
|
|
83
96
|
|
|
84
97
|
const parser = new Mp4Parser()
|
|
98
|
+
.box('moof', Mp4Parser.children)
|
|
99
|
+
.box('traf', Mp4Parser.children)
|
|
100
|
+
.fullBox('tfhd', (box) => {
|
|
101
|
+
goog.asserts.assert(
|
|
102
|
+
box.flags != null,
|
|
103
|
+
'A TFHD box should have a valid flags value');
|
|
104
|
+
const parsedTFHDBox = shaka.util.Mp4BoxParsers.parseTFHD(
|
|
105
|
+
box.reader, box.flags);
|
|
106
|
+
defaultSampleSize = parsedTFHDBox.defaultSampleSize;
|
|
107
|
+
})
|
|
108
|
+
.fullBox('trun', (box) => {
|
|
109
|
+
goog.asserts.assert(
|
|
110
|
+
box.version != null,
|
|
111
|
+
'A TRUN box should have a valid version value');
|
|
112
|
+
goog.asserts.assert(
|
|
113
|
+
box.flags != null,
|
|
114
|
+
'A TRUN box should have a valid flags value');
|
|
115
|
+
|
|
116
|
+
const parsedTRUNBox = shaka.util.Mp4BoxParsers.parseTRUN(
|
|
117
|
+
box.reader, box.version, box.flags);
|
|
118
|
+
|
|
119
|
+
for (const sample of parsedTRUNBox.sampleData) {
|
|
120
|
+
const sampleSize =
|
|
121
|
+
sample.sampleSize || defaultSampleSize || 0;
|
|
122
|
+
sampleSizes.push(sampleSize);
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
.fullBox('subs', (box) => {
|
|
126
|
+
const reader = box.reader;
|
|
127
|
+
const entryCount = reader.readUint32();
|
|
128
|
+
let currentSampleNum = -1;
|
|
129
|
+
for (let i = 0; i < entryCount; i++) {
|
|
130
|
+
const sampleDelta = reader.readUint32();
|
|
131
|
+
currentSampleNum += sampleDelta;
|
|
132
|
+
const subsampleCount = reader.readUint16();
|
|
133
|
+
const subsampleSizes = [];
|
|
134
|
+
for (let j = 0; j < subsampleCount; j++) {
|
|
135
|
+
if (box.version == 1) {
|
|
136
|
+
subsampleSizes.push(reader.readUint32());
|
|
137
|
+
} else {
|
|
138
|
+
subsampleSizes.push(reader.readUint16());
|
|
139
|
+
}
|
|
140
|
+
reader.readUint8(); // priority
|
|
141
|
+
reader.readUint8(); // discardable
|
|
142
|
+
reader.readUint32(); // codec_specific_parameters
|
|
143
|
+
}
|
|
144
|
+
subSampleSizesPerSample.set(currentSampleNum, subsampleSizes);
|
|
145
|
+
}
|
|
146
|
+
})
|
|
85
147
|
.box('mdat', Mp4Parser.allData((data) => {
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
payload = payload.concat(this.parser_.parseMedia(data, time, uri));
|
|
148
|
+
// We collect all of the mdats first, before parsing any of them.
|
|
149
|
+
// This is necessary in case the mp4 has multiple mdats.
|
|
150
|
+
mdats.push(data);
|
|
90
151
|
}));
|
|
91
152
|
parser.parse(data, /* partialOkay= */ false);
|
|
92
153
|
|
|
93
|
-
if (
|
|
154
|
+
if (mdats.length == 0) {
|
|
94
155
|
throw new shaka.util.Error(
|
|
95
156
|
shaka.util.Error.Severity.CRITICAL,
|
|
96
157
|
shaka.util.Error.Category.TEXT,
|
|
97
158
|
shaka.util.Error.Code.INVALID_MP4_TTML);
|
|
98
159
|
}
|
|
99
160
|
|
|
161
|
+
const fullData =
|
|
162
|
+
shaka.util.Uint8ArrayUtils.concat(...mdats);
|
|
163
|
+
|
|
164
|
+
let sampleOffset = 0;
|
|
165
|
+
for (let sampleNum = 0; sampleNum < sampleSizes.length; sampleNum++) {
|
|
166
|
+
const sampleData =
|
|
167
|
+
shaka.util.BufferUtils.toUint8(fullData, sampleOffset,
|
|
168
|
+
sampleSizes[sampleNum]);
|
|
169
|
+
sampleOffset += sampleSizes[sampleNum];
|
|
170
|
+
|
|
171
|
+
const subSampleSizes = subSampleSizesPerSample.get(sampleNum);
|
|
172
|
+
|
|
173
|
+
if (subSampleSizes && subSampleSizes.length) {
|
|
174
|
+
const contentData =
|
|
175
|
+
shaka.util.BufferUtils.toUint8(sampleData, 0, subSampleSizes[0]);
|
|
176
|
+
let subOffset = subSampleSizes[0];
|
|
177
|
+
for (let i = 1; i < subSampleSizes.length; i++) {
|
|
178
|
+
subOffset += subSampleSizes[i];
|
|
179
|
+
}
|
|
180
|
+
payload = payload.concat(
|
|
181
|
+
this.parser_.parseMedia(contentData, time, uri));
|
|
182
|
+
} else {
|
|
183
|
+
payload = payload.concat(
|
|
184
|
+
this.parser_.parseMedia(sampleData, time, uri));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
100
188
|
return payload;
|
|
101
189
|
}
|
|
102
190
|
};
|
|
@@ -55,6 +55,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
55
55
|
/** @private {?number} */
|
|
56
56
|
this.aspectRatio_ = null;
|
|
57
57
|
|
|
58
|
+
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
|
59
|
+
this.config_ = null;
|
|
60
|
+
|
|
58
61
|
/** @type {HTMLElement} */
|
|
59
62
|
this.textContainer_ = shaka.util.Dom.createHTMLElement('div');
|
|
60
63
|
this.textContainer_.classList.add('shaka-text-container');
|
|
@@ -77,7 +80,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
77
80
|
if (!this.video_.paused) {
|
|
78
81
|
this.updateCaptions_();
|
|
79
82
|
}
|
|
80
|
-
})
|
|
83
|
+
});
|
|
84
|
+
this.configureCaptionsTimer_();
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
@@ -101,6 +105,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
101
105
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
this.eventManager_.listen(this.video_, 'ratechange', () => {
|
|
109
|
+
this.configureCaptionsTimer_();
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
105
113
|
// Whenever the natural width or natural height of the video changes
|
|
106
114
|
// (including, for example, because the selected video track was changed),
|
|
@@ -137,9 +145,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
137
145
|
* @export
|
|
138
146
|
*/
|
|
139
147
|
configure(config) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
148
|
+
this.config_ = config;
|
|
149
|
+
this.configureCaptionsTimer_();
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
|
|
@@ -163,6 +170,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
163
170
|
this.cues_.push(cue);
|
|
164
171
|
}
|
|
165
172
|
}
|
|
173
|
+
if (this.cues_.length) {
|
|
174
|
+
this.configureCaptionsTimer_();
|
|
175
|
+
}
|
|
166
176
|
|
|
167
177
|
this.updateCaptions_();
|
|
168
178
|
}
|
|
@@ -186,6 +196,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
186
196
|
this.cues_ = [];
|
|
187
197
|
if (this.captionsTimer_) {
|
|
188
198
|
this.captionsTimer_.stop();
|
|
199
|
+
this.captionsTimer_ = null;
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
this.currentCuesMap_.clear();
|
|
@@ -224,6 +235,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
224
235
|
// displaying if removed (say, due to the user changing languages).
|
|
225
236
|
const forceUpdate = oldNumCues > this.cues_.length;
|
|
226
237
|
this.updateCaptions_(forceUpdate);
|
|
238
|
+
if (!this.cues_.length) {
|
|
239
|
+
this.configureCaptionsTimer_();
|
|
240
|
+
}
|
|
227
241
|
|
|
228
242
|
return true;
|
|
229
243
|
}
|
|
@@ -246,6 +260,23 @@ shaka.text.UITextDisplayer = class {
|
|
|
246
260
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
247
261
|
}
|
|
248
262
|
|
|
263
|
+
/**
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
configureCaptionsTimer_() {
|
|
267
|
+
if (this.captionsTimer_) {
|
|
268
|
+
if (this.cues_.length) {
|
|
269
|
+
const captionsUpdatePeriod = this.config_ ?
|
|
270
|
+
this.config_.captionsUpdatePeriod : 0.25;
|
|
271
|
+
const updateTime = captionsUpdatePeriod /
|
|
272
|
+
Math.max(1, Math.abs(this.video_.playbackRate));
|
|
273
|
+
this.captionsTimer_.tickEvery(updateTime);
|
|
274
|
+
} else {
|
|
275
|
+
this.captionsTimer_.stop();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
249
280
|
/**
|
|
250
281
|
* @private
|
|
251
282
|
*/
|
|
@@ -14,6 +14,7 @@ goog.require('shaka.util.Error');
|
|
|
14
14
|
goog.require('shaka.util.Id3Utils');
|
|
15
15
|
goog.require('shaka.util.ManifestParserUtils');
|
|
16
16
|
goog.require('shaka.util.Mp4Generator');
|
|
17
|
+
goog.require('shaka.util.Platform');
|
|
17
18
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
18
19
|
|
|
19
20
|
|
|
@@ -86,7 +87,11 @@ shaka.transmuxer.Ac3Transmuxer = class {
|
|
|
86
87
|
*/
|
|
87
88
|
convertCodecs(contentType, mimeType) {
|
|
88
89
|
if (this.isAc3Container_(mimeType)) {
|
|
89
|
-
|
|
90
|
+
if (shaka.util.Platform.requiresEC3InitSegments()) {
|
|
91
|
+
return 'audio/mp4; codecs="ec-3"';
|
|
92
|
+
} else {
|
|
93
|
+
return 'audio/mp4; codecs="ac-3"';
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
return mimeType;
|
|
92
97
|
}
|
package/lib/util/config_utils.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.ConfigUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.util.ArrayUtils');
|
|
11
12
|
goog.require('shaka.util.ObjectUtils');
|
|
12
13
|
|
|
13
14
|
|
|
@@ -189,13 +190,20 @@ shaka.util.ConfigUtils = class {
|
|
|
189
190
|
// eslint-disable-next-line no-prototype-builtins
|
|
190
191
|
if (!base.hasOwnProperty(key)) {
|
|
191
192
|
acc[key] = value;
|
|
193
|
+
} else if (value instanceof HTMLElement &&
|
|
194
|
+
base[key] instanceof HTMLElement) {
|
|
195
|
+
if (!value.isEqualNode(base[key])) {
|
|
196
|
+
acc[key] = value;
|
|
197
|
+
}
|
|
192
198
|
} else if (isObject(value) && isObject(base[key])) {
|
|
193
199
|
const diff = changes(value, base[key]);
|
|
194
200
|
if (Object.keys(diff).length > 0 || !isObject(diff)) {
|
|
195
201
|
acc[key] = diff;
|
|
196
202
|
}
|
|
197
|
-
} else if (
|
|
198
|
-
|
|
203
|
+
} else if (Array.isArray(value) && Array.isArray(base[key])) {
|
|
204
|
+
if (!shaka.util.ArrayUtils.hasSameElements(value, base[key])) {
|
|
205
|
+
acc[key] = value;
|
|
206
|
+
}
|
|
199
207
|
} else if (Number.isNaN(value) && Number.isNaN(base[key])) {
|
|
200
208
|
// Do nothing if both are NaN
|
|
201
209
|
} else if (value !== base[key]) {
|
|
@@ -209,7 +217,9 @@ shaka.util.ConfigUtils = class {
|
|
|
209
217
|
|
|
210
218
|
const removeEmpty = (obj) => {
|
|
211
219
|
for (const key of Object.keys(obj)) {
|
|
212
|
-
if (
|
|
220
|
+
if (obj[key] instanceof HTMLElement) {
|
|
221
|
+
// Do nothing if it's a HTMLElement
|
|
222
|
+
} else if (isObject(obj[key]) && Object.keys(obj[key]).length === 0) {
|
|
213
223
|
delete obj[key];
|
|
214
224
|
} else if (isArrayEmpty(obj[key])) {
|
|
215
225
|
delete obj[key];
|
package/lib/util/drm_utils.js
CHANGED
|
@@ -157,6 +157,14 @@ shaka.util.DrmUtils = class {
|
|
|
157
157
|
return false;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* @param {?string} keySystem
|
|
162
|
+
* @return {boolean}
|
|
163
|
+
*/
|
|
164
|
+
static isWisePlayKeySystem(keySystem) {
|
|
165
|
+
return keySystem === 'com.huawei.wiseplay';
|
|
166
|
+
}
|
|
167
|
+
|
|
160
168
|
/**
|
|
161
169
|
* A method for generating a key for the MediaKeySystemAccessRequests cache.
|
|
162
170
|
*
|
package/lib/util/error.js
CHANGED
|
@@ -960,6 +960,12 @@ shaka.util.Error.Code = {
|
|
|
960
960
|
*/
|
|
961
961
|
'SRC_EQUALS_PRELOAD_NOT_SUPPORTED': 7005,
|
|
962
962
|
|
|
963
|
+
/**
|
|
964
|
+
* The operation failed because the preload has been destroyed. This can
|
|
965
|
+
* happen by reusing the same preload in multiple load calls.
|
|
966
|
+
*/
|
|
967
|
+
'PRELOAD_DESTROYED': 7006,
|
|
968
|
+
|
|
963
969
|
/**
|
|
964
970
|
* The Cast API is unavailable. This may be because of one of the following:
|
|
965
971
|
* 1. The browser may not have Cast support
|
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.Mp4Generator');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.util.ManifestParserUtils');
|
|
11
|
+
goog.require('shaka.util.Platform');
|
|
11
12
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
12
13
|
|
|
13
14
|
|
|
@@ -298,6 +299,20 @@ shaka.util.Mp4Generator = class {
|
|
|
298
299
|
stsd_(streamInfo) {
|
|
299
300
|
const Mp4Generator = shaka.util.Mp4Generator;
|
|
300
301
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
302
|
+
let audioCodec = 'aac';
|
|
303
|
+
if (streamInfo.codecs.includes('mp3')) {
|
|
304
|
+
audioCodec = 'mp3';
|
|
305
|
+
} else if (streamInfo.codecs.includes('ac-3')) {
|
|
306
|
+
if (shaka.util.Platform.requiresEC3InitSegments()) {
|
|
307
|
+
audioCodec = 'ec-3';
|
|
308
|
+
} else {
|
|
309
|
+
audioCodec = 'ac-3';
|
|
310
|
+
}
|
|
311
|
+
} else if (streamInfo.codecs.includes('ec-3')) {
|
|
312
|
+
audioCodec = 'ec-3';
|
|
313
|
+
} else if (streamInfo.codecs.includes('opus')) {
|
|
314
|
+
audioCodec = 'opus';
|
|
315
|
+
}
|
|
301
316
|
let bytes = new Uint8Array([]);
|
|
302
317
|
switch (streamInfo.type) {
|
|
303
318
|
case ContentType.VIDEO:
|
|
@@ -308,13 +323,13 @@ shaka.util.Mp4Generator = class {
|
|
|
308
323
|
}
|
|
309
324
|
break;
|
|
310
325
|
case ContentType.AUDIO:
|
|
311
|
-
if (
|
|
326
|
+
if (audioCodec == 'mp3') {
|
|
312
327
|
bytes = this.mp3_(streamInfo);
|
|
313
|
-
} else if (
|
|
328
|
+
} else if (audioCodec == 'ac-3') {
|
|
314
329
|
bytes = this.ac3_(streamInfo);
|
|
315
|
-
} else if (
|
|
330
|
+
} else if (audioCodec == 'ec-3') {
|
|
316
331
|
bytes = this.ec3_(streamInfo);
|
|
317
|
-
} else if (
|
|
332
|
+
} else if (audioCodec == 'opus') {
|
|
318
333
|
bytes = this.opus_(streamInfo);
|
|
319
334
|
} else {
|
|
320
335
|
bytes = this.mp4a_(streamInfo);
|
package/lib/util/periods.js
CHANGED
|
@@ -836,7 +836,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
836
836
|
clone.roles = clone.roles.slice();
|
|
837
837
|
clone.segmentIndex = null;
|
|
838
838
|
clone.emsgSchemeIdUris = [];
|
|
839
|
-
clone.keyIds = new Set();
|
|
839
|
+
clone.keyIds = new Set(stream.keyIds);
|
|
840
840
|
clone.closedCaptions = stream.closedCaptions ?
|
|
841
841
|
new Map(stream.closedCaptions) : null;
|
|
842
842
|
clone.trickModeVideo = null;
|
|
@@ -860,7 +860,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
860
860
|
clone.roles = clone.roles.slice();
|
|
861
861
|
// These are wiped out now and rebuilt later from the various per-period
|
|
862
862
|
// streams that match this output.
|
|
863
|
-
clone.keyIds = new Set();
|
|
863
|
+
clone.keyIds = new Set(streamDb.keyIds);
|
|
864
864
|
clone.segments = [];
|
|
865
865
|
clone.variantIds = [];
|
|
866
866
|
clone.closedCaptions = streamDb.closedCaptions ?
|
|
@@ -1772,6 +1772,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1772
1772
|
v.roles,
|
|
1773
1773
|
v.closedCaptions ? Array.from(v.closedCaptions.entries()) : null,
|
|
1774
1774
|
v.bandwidth,
|
|
1775
|
+
Array.from(v.keyIds),
|
|
1775
1776
|
]);
|
|
1776
1777
|
}
|
|
1777
1778
|
|
|
@@ -1794,6 +1795,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1794
1795
|
a.roles,
|
|
1795
1796
|
a.audioSamplingRate,
|
|
1796
1797
|
a.primary,
|
|
1798
|
+
Array.from(a.keyIds),
|
|
1797
1799
|
]);
|
|
1798
1800
|
}
|
|
1799
1801
|
|
package/lib/util/platform.js
CHANGED
|
@@ -307,13 +307,15 @@ shaka.util.Platform = class {
|
|
|
307
307
|
!shaka.util.Platform.isTizen() &&
|
|
308
308
|
!shaka.util.Platform.isEOS() &&
|
|
309
309
|
!shaka.util.Platform.isAPL() &&
|
|
310
|
+
!shaka.util.Platform.isAPP() &&
|
|
310
311
|
!shaka.util.Platform.isVirginMedia() &&
|
|
311
312
|
!shaka.util.Platform.isOrange() &&
|
|
312
313
|
!shaka.util.Platform.isPS4() &&
|
|
313
314
|
!shaka.util.Platform.isAmazonFireTV() &&
|
|
314
315
|
!shaka.util.Platform.isComcastX1() &&
|
|
315
316
|
!shaka.util.Platform.isZenterio() &&
|
|
316
|
-
!shaka.util.Platform.isSkyQ()
|
|
317
|
+
!shaka.util.Platform.isSkyQ() &&
|
|
318
|
+
!shaka.util.Platform.isMultiChoice();
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
/**
|
|
@@ -391,6 +393,16 @@ shaka.util.Platform = class {
|
|
|
391
393
|
return shaka.util.Platform.userAgentContains_('DT_STB_BCM');
|
|
392
394
|
}
|
|
393
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Check if the current platform is a MultiChoice STB.
|
|
398
|
+
* @return {boolean}
|
|
399
|
+
*/
|
|
400
|
+
static isMultiChoice() {
|
|
401
|
+
// cspell: ignore MDMP
|
|
402
|
+
return shaka.util.Platform.userAgentContains_('MDMP1001S') ||
|
|
403
|
+
shaka.util.Platform.userAgentContains_('PS5525IMC');
|
|
404
|
+
}
|
|
405
|
+
|
|
394
406
|
/**
|
|
395
407
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
396
408
|
*
|
|
@@ -456,6 +468,14 @@ shaka.util.Platform = class {
|
|
|
456
468
|
return shaka.util.Platform.userAgentContains_('PC=APL');
|
|
457
469
|
}
|
|
458
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Check if the current platform is an APPSTB set-top box.
|
|
473
|
+
* @return {boolean}
|
|
474
|
+
*/
|
|
475
|
+
static isAPP() {
|
|
476
|
+
return shaka.util.Platform.userAgentContains_('PC=APPSTB');
|
|
477
|
+
}
|
|
478
|
+
|
|
459
479
|
/**
|
|
460
480
|
* Guesses if the platform is a mobile one (iOS or Android).
|
|
461
481
|
*
|
|
@@ -523,6 +543,9 @@ shaka.util.Platform = class {
|
|
|
523
543
|
* @return {boolean}
|
|
524
544
|
*/
|
|
525
545
|
static isAndroid() {
|
|
546
|
+
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
|
547
|
+
return navigator.userAgentData.platform.toLowerCase() == 'android';
|
|
548
|
+
}
|
|
526
549
|
return shaka.util.Platform.userAgentContains_('Android');
|
|
527
550
|
}
|
|
528
551
|
|
|
@@ -532,6 +555,9 @@ shaka.util.Platform = class {
|
|
|
532
555
|
* @return {boolean}
|
|
533
556
|
*/
|
|
534
557
|
static isFuchsia() {
|
|
558
|
+
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
|
559
|
+
return navigator.userAgentData.platform.toLowerCase() == 'fuchsia';
|
|
560
|
+
}
|
|
535
561
|
return shaka.util.Platform.userAgentContains_('Fuchsia');
|
|
536
562
|
}
|
|
537
563
|
|
|
@@ -548,7 +574,8 @@ shaka.util.Platform = class {
|
|
|
548
574
|
Platform.isEOS() || Platform.isAPL() ||
|
|
549
575
|
Platform.isVirginMedia() || Platform.isOrange() ||
|
|
550
576
|
Platform.isComcastX1() || Platform.isChromecast() ||
|
|
551
|
-
Platform.isHisense() || Platform.isZenterio()
|
|
577
|
+
Platform.isHisense() || Platform.isZenterio() ||
|
|
578
|
+
Platform.isAPP() || Platform.isMultiChoice()) {
|
|
552
579
|
return true;
|
|
553
580
|
}
|
|
554
581
|
return false;
|
|
@@ -621,6 +648,19 @@ shaka.util.Platform = class {
|
|
|
621
648
|
(Platform.isEdge() && Platform.isWindows() && isPlayReady);
|
|
622
649
|
}
|
|
623
650
|
|
|
651
|
+
/**
|
|
652
|
+
* Returns true if the platform requires AC-3 signalling in init
|
|
653
|
+
* segments to be replaced with EC-3 signalling.
|
|
654
|
+
* For such platforms, MediaSourceEngine will attempt to work
|
|
655
|
+
* around it by inserting fake EC-3 signalling into
|
|
656
|
+
* initialization segments.
|
|
657
|
+
*
|
|
658
|
+
* @return {boolean}
|
|
659
|
+
*/
|
|
660
|
+
static requiresEC3InitSegments() {
|
|
661
|
+
return shaka.util.Platform.isTizen3();
|
|
662
|
+
}
|
|
663
|
+
|
|
624
664
|
/**
|
|
625
665
|
* Returns true if the platform supports SourceBuffer "sequence mode".
|
|
626
666
|
*
|
|
@@ -629,7 +669,7 @@ shaka.util.Platform = class {
|
|
|
629
669
|
static supportsSequenceMode() {
|
|
630
670
|
const Platform = shaka.util.Platform;
|
|
631
671
|
if (Platform.isTizen3() || Platform.isTizen2() ||
|
|
632
|
-
Platform.isWebOS3() || Platform.isPS4()) {
|
|
672
|
+
Platform.isWebOS3() || Platform.isPS4() || Platform.isPS5()) {
|
|
633
673
|
return false;
|
|
634
674
|
}
|
|
635
675
|
return true;
|
|
@@ -685,16 +725,13 @@ shaka.util.Platform = class {
|
|
|
685
725
|
}
|
|
686
726
|
|
|
687
727
|
/**
|
|
688
|
-
* Returns true if MediaKeys is polyfilled
|
|
728
|
+
* Returns true if MediaKeys is polyfilled by the specified polyfill.
|
|
689
729
|
*
|
|
690
|
-
* @param {string
|
|
730
|
+
* @param {string} polyfillType
|
|
691
731
|
* @return {boolean}
|
|
692
732
|
*/
|
|
693
733
|
static isMediaKeysPolyfilled(polyfillType) {
|
|
694
|
-
|
|
695
|
-
return polyfillType === window.shakaMediaKeysPolyfill;
|
|
696
|
-
}
|
|
697
|
-
return !!window.shakaMediaKeysPolyfill;
|
|
734
|
+
return polyfillType === window.shakaMediaKeysPolyfill;
|
|
698
735
|
}
|
|
699
736
|
|
|
700
737
|
|
|
@@ -780,10 +817,21 @@ shaka.util.Platform = class {
|
|
|
780
817
|
maxResolution.height = 1080;
|
|
781
818
|
}
|
|
782
819
|
} else if (Platform.isHisense()) {
|
|
783
|
-
|
|
784
|
-
if (window.Hisense_Get4KSupportState
|
|
820
|
+
let supports4k = null;
|
|
821
|
+
if (window.Hisense_Get4KSupportState) {
|
|
822
|
+
try {
|
|
785
823
|
// eslint-disable-next-line new-cap
|
|
786
|
-
window.Hisense_Get4KSupportState()
|
|
824
|
+
supports4k = window.Hisense_Get4KSupportState();
|
|
825
|
+
} catch (e) {
|
|
826
|
+
shaka.log.debug('Hisense: Failed to get 4K support state', e);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (supports4k == null) {
|
|
830
|
+
// If API is not there or not working for whatever reason, fallback to
|
|
831
|
+
// user agent check, as it contains UHD or FHD info.
|
|
832
|
+
supports4k = Platform.userAgentContains_('UHD');
|
|
833
|
+
}
|
|
834
|
+
if (supports4k) {
|
|
787
835
|
maxResolution.width = 3840;
|
|
788
836
|
maxResolution.height = 2160;
|
|
789
837
|
} else {
|
|
@@ -73,7 +73,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
73
73
|
persistentSessionOnlinePlayback: false,
|
|
74
74
|
persistentSessionsMetadata: [],
|
|
75
75
|
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
76
|
-
if (shaka.util.Platform.isMediaKeysPolyfilled() &&
|
|
76
|
+
if (shaka.util.Platform.isMediaKeysPolyfilled('apple') &&
|
|
77
77
|
initDataType == 'skd') {
|
|
78
78
|
const cert = drmInfo.serverCertificate;
|
|
79
79
|
const contentId =
|
|
@@ -261,6 +261,11 @@ shaka.util.PlayerConfiguration = class {
|
|
|
261
261
|
shaka.util.Platform.isPS5(),
|
|
262
262
|
};
|
|
263
263
|
|
|
264
|
+
const safariVersion = shaka.util.Platform.safariVersion();
|
|
265
|
+
if (safariVersion && safariVersion >= 17) {
|
|
266
|
+
streaming.infiniteLiveStreamDuration = true;
|
|
267
|
+
}
|
|
268
|
+
|
|
264
269
|
// WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
|
|
265
270
|
// that respond slowly to seeking.
|
|
266
271
|
// Therefore we should not seek when we detect a stall
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -996,6 +996,11 @@ shaka.util.StreamUtils = class {
|
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
998
|
|
|
999
|
+
if (codecs.toLowerCase() == 'ac-3' &&
|
|
1000
|
+
shaka.util.Platform.requiresEC3InitSegments()) {
|
|
1001
|
+
return 'ec-3';
|
|
1002
|
+
}
|
|
1003
|
+
|
|
999
1004
|
return codecs;
|
|
1000
1005
|
}
|
|
1001
1006
|
|
|
@@ -1191,6 +1196,9 @@ shaka.util.StreamUtils = class {
|
|
|
1191
1196
|
* @return {shaka.extern.Track}
|
|
1192
1197
|
*/
|
|
1193
1198
|
static variantToTrack(variant) {
|
|
1199
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
1200
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1201
|
+
|
|
1194
1202
|
/** @type {?shaka.extern.Stream} */
|
|
1195
1203
|
const audio = variant.audio;
|
|
1196
1204
|
/** @type {?shaka.extern.Stream} */
|
|
@@ -1309,6 +1317,16 @@ shaka.util.StreamUtils = class {
|
|
|
1309
1317
|
track.audioSamplingRate = video.audioSamplingRate;
|
|
1310
1318
|
track.spatialAudio = video.spatialAudio;
|
|
1311
1319
|
track.originalLanguage = video.originalLanguage;
|
|
1320
|
+
track.audioMimeType = videoMimeType;
|
|
1321
|
+
const allCodecs = videoCodec.split(',');
|
|
1322
|
+
try {
|
|
1323
|
+
track.videoCodec = ManifestParserUtils.guessCodecs(
|
|
1324
|
+
ContentType.VIDEO, allCodecs);
|
|
1325
|
+
track.audioCodec = ManifestParserUtils.guessCodecs(
|
|
1326
|
+
ContentType.AUDIO, allCodecs);
|
|
1327
|
+
} catch (e) {
|
|
1328
|
+
// Ignore this error.
|
|
1329
|
+
}
|
|
1312
1330
|
}
|
|
1313
1331
|
}
|
|
1314
1332
|
|
package/lib/util/tXml.js
CHANGED
|
@@ -555,8 +555,9 @@ shaka.util.TXml = class {
|
|
|
555
555
|
const schemaNS = shaka.util.TXml.getKnownNameSpace(ns);
|
|
556
556
|
const found = [];
|
|
557
557
|
if (elem.children) {
|
|
558
|
+
const tagName = schemaNS ? `${schemaNS}:${name}` : name;
|
|
558
559
|
for (const child of elem.children) {
|
|
559
|
-
if (child && child.tagName ===
|
|
560
|
+
if (child && child.tagName === tagName) {
|
|
560
561
|
found.push(child);
|
|
561
562
|
}
|
|
562
563
|
}
|
|
@@ -625,7 +626,7 @@ shaka.util.TXml = class {
|
|
|
625
626
|
|
|
626
627
|
const re = '^P(?:([0-9]*)Y)?(?:([0-9]*)M)?(?:([0-9]*)D)?' +
|
|
627
628
|
'(?:T(?:([0-9]*)H)?(?:([0-9]*)M)?(?:([0-9.]*)S)?)?$';
|
|
628
|
-
const matches = new RegExp(re).exec(durationString);
|
|
629
|
+
const matches = new RegExp(re, 'i').exec(durationString);
|
|
629
630
|
|
|
630
631
|
if (!matches) {
|
|
631
632
|
shaka.log.warning('Invalid duration string:', durationString);
|