shaka-player 4.7.12 → 4.8.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/AUTHORS +1 -0
- package/CONTRIBUTORS +1 -0
- package/LICENSE +54 -0
- package/README.md +22 -1
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +36 -25
- package/dist/locales.js +40 -30
- package/dist/shaka-player.compiled.d.ts +196 -46
- package/dist/shaka-player.compiled.debug.d.ts +196 -46
- package/dist/shaka-player.compiled.debug.externs.js +474 -337
- package/dist/shaka-player.compiled.debug.js +992 -871
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +474 -337
- package/dist/shaka-player.compiled.js +1248 -1174
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +259 -49
- package/dist/shaka-player.ui.debug.d.ts +259 -49
- package/dist/shaka-player.ui.debug.externs.js +591 -376
- package/dist/shaka-player.ui.debug.js +1208 -1003
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +591 -376
- package/dist/shaka-player.ui.js +1533 -1408
- package/dist/shaka-player.ui.map +4 -4
- package/externs/device_sensor_event.js +18 -0
- package/externs/hisense.js +16 -0
- package/externs/playstation.js +31 -0
- package/externs/shaka/abr_manager.js +3 -1
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/codecs.js +13 -0
- package/externs/shaka/manifest.js +69 -7
- package/externs/shaka/net.js +13 -1
- package/externs/shaka/offline.js +7 -1
- package/externs/shaka/player.js +184 -13
- package/externs/shaka/text.js +19 -0
- package/externs/tizen.js +41 -0
- package/externs/webos.js +19 -0
- package/externs/xbox.js +43 -0
- package/lib/abr/simple_abr_manager.js +9 -1
- package/lib/ads/ad_manager.js +19 -2
- package/lib/ads/client_side_ad_manager.js +5 -3
- package/lib/ads/media_tailor_ad_manager.js +9 -0
- package/lib/cast/cast_receiver.js +0 -14
- package/lib/cast/cast_utils.js +1 -0
- package/lib/cea/mp4_cea_parser.js +6 -1
- package/lib/dash/content_protection.js +97 -53
- package/lib/dash/dash_parser.js +240 -179
- package/lib/dash/mpd_utils.js +72 -65
- package/lib/dash/segment_base.js +35 -22
- package/lib/dash/segment_list.js +39 -31
- package/lib/dash/segment_template.js +47 -23
- package/lib/hls/hls_parser.js +376 -165
- package/lib/media/buffering_observer.js +25 -1
- package/lib/media/drm_engine.js +91 -71
- package/lib/media/manifest_filterer.js +220 -0
- package/lib/media/media_source_engine.js +108 -29
- package/lib/media/playhead.js +3 -3
- package/lib/media/preload_manager.js +827 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/segment_index.js +60 -49
- package/lib/media/segment_prefetch.js +140 -43
- package/lib/media/segment_reference.js +31 -8
- package/lib/media/segment_utils.js +208 -25
- package/lib/media/streaming_engine.js +321 -77
- package/lib/mss/content_protection.js +31 -25
- package/lib/mss/mss_parser.js +78 -55
- package/lib/net/networking_engine.js +49 -4
- package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
- package/lib/offline/manifest_converter.js +10 -3
- package/lib/offline/storage.js +5 -3
- package/lib/player.js +896 -674
- package/lib/polyfill/media_capabilities.js +16 -1
- package/lib/text/simple_text_displayer.js +9 -0
- package/lib/text/stub_text_displayer.js +7 -0
- package/lib/text/text_engine.js +16 -3
- package/lib/text/ttml_text_parser.js +116 -114
- package/lib/text/ui_text_displayer.js +28 -11
- package/lib/text/vtt_text_parser.js +54 -115
- package/lib/transmuxer/opus.js +91 -0
- package/lib/transmuxer/ts_transmuxer.js +115 -0
- package/lib/util/cmcd_manager.js +152 -3
- package/lib/util/dom_utils.js +11 -0
- package/lib/util/error.js +5 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/manifest_parser_utils.js +97 -3
- package/lib/util/mime_utils.js +10 -5
- package/lib/util/mp4_box_parsers.js +346 -17
- package/lib/util/mp4_generator.js +23 -0
- package/lib/util/periods.js +24 -37
- package/lib/util/platform.js +161 -9
- package/lib/util/player_configuration.js +61 -3
- package/lib/util/stats.js +16 -0
- package/lib/util/stream_utils.js +350 -277
- package/lib/util/string_utils.js +36 -0
- package/lib/util/tXml.js +814 -0
- package/lib/util/ts_parser.js +50 -0
- package/lib/util/xml_utils.js +10 -338
- package/package.json +2 -2
- package/shaka-player.uncompiled.js +8 -0
- package/ui/controls.js +187 -5
- package/ui/enums.js +2 -0
- package/ui/externs/ui.js +30 -3
- package/ui/gl_matrix/matrix_4x4.js +568 -0
- package/ui/gl_matrix/matrix_quaternion.js +45 -0
- package/ui/less/containers.less +11 -0
- package/ui/locales/en-GB.json +2 -0
- package/ui/locales/en.json +2 -0
- package/ui/locales/es-419.json +2 -0
- package/ui/locales/es.json +2 -0
- package/ui/locales/source.json +8 -0
- package/ui/recenter_vr.js +126 -0
- package/ui/resolution_selection.js +25 -5
- package/ui/statistics_button.js +12 -0
- package/ui/text_selection.js +1 -1
- package/ui/toggle_stereoscopic.js +127 -0
- package/ui/ui.js +74 -38
- package/ui/vr_manager.js +557 -0
- package/ui/vr_utils.js +232 -0
- package/ui/vr_webgl.js +697 -0
|
@@ -11,8 +11,8 @@ goog.require('shaka.util.BufferUtils');
|
|
|
11
11
|
goog.require('shaka.util.ManifestParserUtils');
|
|
12
12
|
goog.require('shaka.util.Pssh');
|
|
13
13
|
goog.require('shaka.util.StringUtils');
|
|
14
|
+
goog.require('shaka.util.TXml');
|
|
14
15
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
15
|
-
goog.require('shaka.util.XmlUtils');
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -23,19 +23,19 @@ shaka.mss.ContentProtection = class {
|
|
|
23
23
|
/**
|
|
24
24
|
* Parses info from the Protection elements.
|
|
25
25
|
*
|
|
26
|
-
* @param {!Array.<!
|
|
26
|
+
* @param {!Array.<!shaka.extern.xml.Node>} elems
|
|
27
27
|
* @param {!Object.<string, string>} keySystemsBySystemId
|
|
28
28
|
* @return {!Array.<shaka.extern.DrmInfo>}
|
|
29
29
|
*/
|
|
30
30
|
static parseFromProtection(elems, keySystemsBySystemId) {
|
|
31
31
|
const ContentProtection = shaka.mss.ContentProtection;
|
|
32
|
-
const
|
|
32
|
+
const TXml = shaka.util.TXml;
|
|
33
33
|
|
|
34
|
-
/** @type {!Array.<!
|
|
34
|
+
/** @type {!Array.<!shaka.extern.xml.Node>} */
|
|
35
35
|
let protectionHeader = [];
|
|
36
36
|
for (const elem of elems) {
|
|
37
37
|
protectionHeader = protectionHeader.concat(
|
|
38
|
-
|
|
38
|
+
TXml.findChildren(elem, 'ProtectionHeader'));
|
|
39
39
|
}
|
|
40
40
|
if (!protectionHeader.length) {
|
|
41
41
|
return [];
|
|
@@ -123,18 +123,19 @@ shaka.mss.ContentProtection = class {
|
|
|
123
123
|
* Parse a PlayReady Header format: https://goo.gl/dBzxNA
|
|
124
124
|
* a try to find the LA_URL value.
|
|
125
125
|
*
|
|
126
|
-
* @param {!
|
|
126
|
+
* @param {!shaka.extern.xml.Node} xml
|
|
127
127
|
* @return {string}
|
|
128
128
|
* @private
|
|
129
129
|
*/
|
|
130
130
|
static getLaurl_(xml) {
|
|
131
|
+
const TXml = shaka.util.TXml;
|
|
131
132
|
// LA_URL element is optional and no more than one is
|
|
132
133
|
// allowed inside the DATA element. Only absolute URLs are allowed.
|
|
133
134
|
// If the LA_URL element exists, it must not be empty.
|
|
134
|
-
for (const elem of
|
|
135
|
-
const laUrl =
|
|
135
|
+
for (const elem of TXml.getElementsByTagName(xml, 'DATA')) {
|
|
136
|
+
const laUrl = TXml.findChild(elem, 'LA_URL');
|
|
136
137
|
if (laUrl) {
|
|
137
|
-
return laUrl
|
|
138
|
+
return /** @type {string} */ (shaka.util.TXml.getTextContents(laUrl));
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
|
|
@@ -148,7 +149,7 @@ shaka.mss.ContentProtection = class {
|
|
|
148
149
|
* Gets a PlayReady license URL from a protection element
|
|
149
150
|
* containing a PlayReady Header Object
|
|
150
151
|
*
|
|
151
|
-
* @param {!
|
|
152
|
+
* @param {!shaka.extern.xml.Node} element
|
|
152
153
|
* @return {string}
|
|
153
154
|
*/
|
|
154
155
|
static getPlayReadyLicenseUrl(element) {
|
|
@@ -165,19 +166,21 @@ shaka.mss.ContentProtection = class {
|
|
|
165
166
|
* Parse a PlayReady Header format: https://goo.gl/dBzxNA
|
|
166
167
|
* a try to find the KID value.
|
|
167
168
|
*
|
|
168
|
-
* @param {!
|
|
169
|
+
* @param {!shaka.extern.xml.Node} xml
|
|
169
170
|
* @return {?string}
|
|
170
171
|
* @private
|
|
171
172
|
*/
|
|
172
173
|
static getKID_(xml) {
|
|
174
|
+
const TXml = shaka.util.TXml;
|
|
173
175
|
// KID element is optional and no more than one is
|
|
174
176
|
// allowed inside the DATA element.
|
|
175
|
-
for (const elem of
|
|
176
|
-
const kid =
|
|
177
|
+
for (const elem of TXml.getElementsByTagName(xml, 'DATA')) {
|
|
178
|
+
const kid = TXml.findChild(elem, 'KID');
|
|
177
179
|
if (kid) {
|
|
178
180
|
// GUID: [DWORD, WORD, WORD, 8-BYTE]
|
|
179
181
|
const guidBytes =
|
|
180
|
-
shaka.util.Uint8ArrayUtils.fromBase64(
|
|
182
|
+
shaka.util.Uint8ArrayUtils.fromBase64(
|
|
183
|
+
/** @type{string} */ (shaka.util.TXml.getTextContents(kid)));
|
|
181
184
|
// Reverse byte order from little-endian to big-endian
|
|
182
185
|
const kidBytes = new Uint8Array([
|
|
183
186
|
guidBytes[3], guidBytes[2], guidBytes[1], guidBytes[0],
|
|
@@ -197,7 +200,7 @@ shaka.mss.ContentProtection = class {
|
|
|
197
200
|
* Gets a PlayReady KID from a protection element
|
|
198
201
|
* containing a PlayReady Header Object
|
|
199
202
|
*
|
|
200
|
-
* @param {!
|
|
203
|
+
* @param {!shaka.extern.xml.Node} element
|
|
201
204
|
* @return {?string}
|
|
202
205
|
* @private
|
|
203
206
|
*/
|
|
@@ -214,15 +217,16 @@ shaka.mss.ContentProtection = class {
|
|
|
214
217
|
/**
|
|
215
218
|
* Gets a PlayReady Header Object from a protection element
|
|
216
219
|
*
|
|
217
|
-
* @param {!
|
|
218
|
-
* @return {?
|
|
220
|
+
* @param {!shaka.extern.xml.Node} element
|
|
221
|
+
* @return {?shaka.extern.xml.Node}
|
|
219
222
|
* @private
|
|
220
223
|
*/
|
|
221
224
|
static getPlayReadyHeaderObject_(element) {
|
|
222
225
|
const ContentProtection = shaka.mss.ContentProtection;
|
|
223
226
|
const PLAYREADY_RECORD_TYPES = ContentProtection.PLAYREADY_RECORD_TYPES;
|
|
224
227
|
|
|
225
|
-
const bytes = shaka.util.Uint8ArrayUtils.fromBase64(
|
|
228
|
+
const bytes = shaka.util.Uint8ArrayUtils.fromBase64(
|
|
229
|
+
/** @type{string} */ (shaka.util.TXml.getTextContents(element)));
|
|
226
230
|
const records = ContentProtection.parseMsPro_(bytes);
|
|
227
231
|
const record = records.filter((record) => {
|
|
228
232
|
return record.type === PLAYREADY_RECORD_TYPES.RIGHTS_MANAGEMENT;
|
|
@@ -233,7 +237,7 @@ shaka.mss.ContentProtection = class {
|
|
|
233
237
|
}
|
|
234
238
|
|
|
235
239
|
const xml = shaka.util.StringUtils.fromUTF16(record.value, true);
|
|
236
|
-
const rootElement = shaka.util.
|
|
240
|
+
const rootElement = shaka.util.TXml.parseXmlString(xml, 'WRMHEADER');
|
|
237
241
|
if (!rootElement) {
|
|
238
242
|
return null;
|
|
239
243
|
}
|
|
@@ -243,7 +247,7 @@ shaka.mss.ContentProtection = class {
|
|
|
243
247
|
/**
|
|
244
248
|
* Gets a initData from a protection element.
|
|
245
249
|
*
|
|
246
|
-
* @param {!
|
|
250
|
+
* @param {!shaka.extern.xml.Node} element
|
|
247
251
|
* @param {string} systemID
|
|
248
252
|
* @param {?string} keyId
|
|
249
253
|
* @return {?Array.<shaka.extern.InitDataOverride>}
|
|
@@ -251,7 +255,8 @@ shaka.mss.ContentProtection = class {
|
|
|
251
255
|
*/
|
|
252
256
|
static getInitDataFromPro_(element, systemID, keyId) {
|
|
253
257
|
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
254
|
-
const data = Uint8ArrayUtils.fromBase64(
|
|
258
|
+
const data = Uint8ArrayUtils.fromBase64(
|
|
259
|
+
/** @type{string} */ (shaka.util.TXml.getTextContents(element)));
|
|
255
260
|
const systemId = Uint8ArrayUtils.fromHex(systemID.replace(/-/g, ''));
|
|
256
261
|
const keyIds = new Set();
|
|
257
262
|
const psshVersion = 0;
|
|
@@ -269,7 +274,7 @@ shaka.mss.ContentProtection = class {
|
|
|
269
274
|
/**
|
|
270
275
|
* Creates DrmInfo objects from an array of elements.
|
|
271
276
|
*
|
|
272
|
-
* @param {!Array.<!
|
|
277
|
+
* @param {!Array.<!shaka.extern.xml.Node>} elements
|
|
273
278
|
* @param {!Object.<string, string>} keySystemsBySystemId
|
|
274
279
|
* @return {!Array.<shaka.extern.DrmInfo>}
|
|
275
280
|
* @private
|
|
@@ -284,14 +289,15 @@ shaka.mss.ContentProtection = class {
|
|
|
284
289
|
|
|
285
290
|
for (let i = 0; i < elements.length; i++) {
|
|
286
291
|
const element = elements[i];
|
|
287
|
-
const systemID = element.
|
|
292
|
+
const systemID = element.attributes['SystemID'].toLowerCase();
|
|
288
293
|
const keySystem = keySystemsBySystemId[systemID];
|
|
289
294
|
if (keySystem) {
|
|
290
295
|
const KID = ContentProtection.getPlayReadyKID_(element);
|
|
291
296
|
const initData = ContentProtection.getInitDataFromPro_(
|
|
292
297
|
element, systemID, KID);
|
|
293
298
|
|
|
294
|
-
const info = ManifestParserUtils.createDrmInfo(
|
|
299
|
+
const info = ManifestParserUtils.createDrmInfo(
|
|
300
|
+
keySystem, /* encryptionScheme= */ 'cenc', initData);
|
|
295
301
|
if (KID) {
|
|
296
302
|
info.keyIds.add(KID);
|
|
297
303
|
}
|
|
@@ -338,7 +344,7 @@ shaka.mss.ContentProtection.PLAYREADY_RECORD_TYPES = {
|
|
|
338
344
|
/**
|
|
339
345
|
* A map of key system name to license server url parser.
|
|
340
346
|
*
|
|
341
|
-
* @const {!Map.<string, function(!
|
|
347
|
+
* @const {!Map.<string, function(!shaka.extern.xml.Node)>}
|
|
342
348
|
* @private
|
|
343
349
|
*/
|
|
344
350
|
shaka.mss.ContentProtection.licenseUrlParsers_ = new Map()
|
package/lib/mss/mss_parser.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.mss.MssParser');
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
|
+
goog.require('shaka.Deprecate');
|
|
10
11
|
goog.require('shaka.abr.Ewma');
|
|
11
12
|
goog.require('shaka.log');
|
|
12
13
|
goog.require('shaka.media.InitSegmentReference');
|
|
@@ -19,9 +20,11 @@ goog.require('shaka.net.NetworkingEngine');
|
|
|
19
20
|
goog.require('shaka.util.Error');
|
|
20
21
|
goog.require('shaka.util.LanguageUtils');
|
|
21
22
|
goog.require('shaka.util.ManifestParserUtils');
|
|
23
|
+
goog.require('shaka.util.MimeUtils');
|
|
22
24
|
goog.require('shaka.util.Mp4Generator');
|
|
23
25
|
goog.require('shaka.util.OperationManager');
|
|
24
26
|
goog.require('shaka.util.Timer');
|
|
27
|
+
goog.require('shaka.util.TXml');
|
|
25
28
|
goog.require('shaka.util.XmlUtils');
|
|
26
29
|
|
|
27
30
|
|
|
@@ -277,7 +280,25 @@ shaka.mss.MssParser = class {
|
|
|
277
280
|
* @private
|
|
278
281
|
*/
|
|
279
282
|
parseManifest_(data, finalManifestUri) {
|
|
280
|
-
|
|
283
|
+
let manifestData = data;
|
|
284
|
+
const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
|
|
285
|
+
if (manifestPreprocessor) {
|
|
286
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
287
|
+
'manifest.mss.manifestPreprocessor configuration',
|
|
288
|
+
'Please Use manifest.mss.manifestPreprocessorTXml instead.');
|
|
289
|
+
const mssElement =
|
|
290
|
+
shaka.util.XmlUtils.parseXml(manifestData, 'SmoothStreamingMedia');
|
|
291
|
+
if (!mssElement) {
|
|
292
|
+
throw new shaka.util.Error(
|
|
293
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
294
|
+
shaka.util.Error.Category.MANIFEST,
|
|
295
|
+
shaka.util.Error.Code.MSS_INVALID_XML,
|
|
296
|
+
finalManifestUri);
|
|
297
|
+
}
|
|
298
|
+
manifestPreprocessor(mssElement);
|
|
299
|
+
manifestData = shaka.util.XmlUtils.toArrayBuffer(mssElement);
|
|
300
|
+
}
|
|
301
|
+
const mss = shaka.util.TXml.parseXml(manifestData, 'SmoothStreamingMedia');
|
|
281
302
|
if (!mss) {
|
|
282
303
|
throw new shaka.util.Error(
|
|
283
304
|
shaka.util.Error.Severity.CRITICAL,
|
|
@@ -285,6 +306,10 @@ shaka.mss.MssParser = class {
|
|
|
285
306
|
shaka.util.Error.Code.MSS_INVALID_XML,
|
|
286
307
|
finalManifestUri);
|
|
287
308
|
}
|
|
309
|
+
const manifestPreprocessorTXml = this.config_.mss.manifestPreprocessorTXml;
|
|
310
|
+
if (manifestPreprocessorTXml) {
|
|
311
|
+
manifestPreprocessorTXml(mss);
|
|
312
|
+
}
|
|
288
313
|
this.processManifest_(mss, finalManifestUri);
|
|
289
314
|
return Promise.resolve();
|
|
290
315
|
}
|
|
@@ -293,26 +318,21 @@ shaka.mss.MssParser = class {
|
|
|
293
318
|
/**
|
|
294
319
|
* Takes a formatted MSS and converts it into a manifest.
|
|
295
320
|
*
|
|
296
|
-
* @param {!
|
|
321
|
+
* @param {!shaka.extern.xml.Node} mss
|
|
297
322
|
* @param {string} finalManifestUri The final manifest URI, which may
|
|
298
323
|
* differ from this.manifestUri_ if there has been a redirect.
|
|
299
324
|
* @private
|
|
300
325
|
*/
|
|
301
326
|
processManifest_(mss, finalManifestUri) {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
|
|
305
|
-
if (manifestPreprocessor) {
|
|
306
|
-
manifestPreprocessor(mss);
|
|
307
|
-
}
|
|
327
|
+
const TXml = shaka.util.TXml;
|
|
308
328
|
|
|
309
329
|
if (!this.presentationTimeline_) {
|
|
310
330
|
this.presentationTimeline_ = new shaka.media.PresentationTimeline(
|
|
311
331
|
/* presentationStartTime= */ null, /* delay= */ 0);
|
|
312
332
|
}
|
|
313
333
|
|
|
314
|
-
const isLive =
|
|
315
|
-
|
|
334
|
+
const isLive = TXml.parseAttr(mss, 'IsLive',
|
|
335
|
+
TXml.parseBoolean, /* defaultValue= */ false);
|
|
316
336
|
|
|
317
337
|
if (isLive) {
|
|
318
338
|
throw new shaka.util.Error(
|
|
@@ -323,21 +343,21 @@ shaka.mss.MssParser = class {
|
|
|
323
343
|
|
|
324
344
|
this.presentationTimeline_.setStatic(!isLive);
|
|
325
345
|
|
|
326
|
-
const timescale =
|
|
327
|
-
|
|
346
|
+
const timescale = TXml.parseAttr(mss, 'TimeScale',
|
|
347
|
+
TXml.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
|
|
328
348
|
goog.asserts.assert(timescale && timescale >= 0,
|
|
329
349
|
'Timescale must be defined!');
|
|
330
350
|
|
|
331
|
-
let dvrWindowLength =
|
|
332
|
-
|
|
351
|
+
let dvrWindowLength = TXml.parseAttr(mss, 'DVRWindowLength',
|
|
352
|
+
TXml.parseNonNegativeInt);
|
|
333
353
|
// If the DVRWindowLength field is omitted for a live presentation or set
|
|
334
354
|
// to 0, the DVR window is effectively infinite
|
|
335
355
|
if (isLive && (dvrWindowLength === 0 || isNaN(dvrWindowLength))) {
|
|
336
356
|
dvrWindowLength = Infinity;
|
|
337
357
|
}
|
|
338
358
|
// Start-over
|
|
339
|
-
const canSeek =
|
|
340
|
-
|
|
359
|
+
const canSeek = TXml.parseAttr(mss, 'CanSeek',
|
|
360
|
+
TXml.parseBoolean, /* defaultValue= */ false);
|
|
341
361
|
if (dvrWindowLength === 0 && canSeek) {
|
|
342
362
|
dvrWindowLength = Infinity;
|
|
343
363
|
}
|
|
@@ -362,8 +382,8 @@ shaka.mss.MssParser = class {
|
|
|
362
382
|
segmentAvailabilityDuration);
|
|
363
383
|
|
|
364
384
|
// Duration in timescale units.
|
|
365
|
-
const duration =
|
|
366
|
-
|
|
385
|
+
const duration = TXml.parseAttr(mss, 'Duration',
|
|
386
|
+
TXml.parseNonNegativeInt, Infinity);
|
|
367
387
|
goog.asserts.assert(duration && duration >= 0,
|
|
368
388
|
'Duration must be defined!');
|
|
369
389
|
|
|
@@ -413,25 +433,25 @@ shaka.mss.MssParser = class {
|
|
|
413
433
|
}
|
|
414
434
|
|
|
415
435
|
/**
|
|
416
|
-
* @param {!
|
|
436
|
+
* @param {!shaka.extern.xml.Node} mss
|
|
417
437
|
* @param {!shaka.mss.MssParser.Context} context
|
|
418
438
|
* @private
|
|
419
439
|
*/
|
|
420
440
|
parseStreamIndexes_(mss, context) {
|
|
421
441
|
const ContentProtection = shaka.mss.ContentProtection;
|
|
422
|
-
const
|
|
442
|
+
const TXml = shaka.util.TXml;
|
|
423
443
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
424
444
|
|
|
425
|
-
const protectionElems =
|
|
445
|
+
const protectionElems = TXml.findChildren(mss, 'Protection');
|
|
426
446
|
const drmInfos = ContentProtection.parseFromProtection(
|
|
427
447
|
protectionElems, this.config_.mss.keySystemsBySystemId);
|
|
428
448
|
|
|
429
449
|
const audioStreams = [];
|
|
430
450
|
const videoStreams = [];
|
|
431
451
|
const textStreams = [];
|
|
432
|
-
const streamIndexes =
|
|
452
|
+
const streamIndexes = TXml.findChildren(mss, 'StreamIndex');
|
|
433
453
|
for (const streamIndex of streamIndexes) {
|
|
434
|
-
const qualityLevels =
|
|
454
|
+
const qualityLevels = TXml.findChildren(streamIndex, 'QualityLevel');
|
|
435
455
|
const timeline = this.createTimeline_(
|
|
436
456
|
streamIndex, context.timescale, context.duration);
|
|
437
457
|
// For each QualityLevel node, create a stream element
|
|
@@ -466,8 +486,8 @@ shaka.mss.MssParser = class {
|
|
|
466
486
|
}
|
|
467
487
|
|
|
468
488
|
/**
|
|
469
|
-
* @param {!
|
|
470
|
-
* @param {!
|
|
489
|
+
* @param {!shaka.extern.xml.Node} streamIndex
|
|
490
|
+
* @param {!shaka.extern.xml.Node} qualityLevel
|
|
471
491
|
* @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
|
|
472
492
|
* @param {!Array.<shaka.extern.DrmInfo>} drmInfos
|
|
473
493
|
* @param {!shaka.mss.MssParser.Context} context
|
|
@@ -475,11 +495,11 @@ shaka.mss.MssParser = class {
|
|
|
475
495
|
* @private
|
|
476
496
|
*/
|
|
477
497
|
createStream_(streamIndex, qualityLevel, timeline, drmInfos, context) {
|
|
478
|
-
const
|
|
498
|
+
const TXml = shaka.util.TXml;
|
|
479
499
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
480
500
|
const MssParser = shaka.mss.MssParser;
|
|
481
501
|
|
|
482
|
-
const type = streamIndex.
|
|
502
|
+
const type = streamIndex.attributes['Type'];
|
|
483
503
|
const isValidType = type === 'audio' || type === 'video' ||
|
|
484
504
|
type === 'text';
|
|
485
505
|
if (!isValidType) {
|
|
@@ -487,19 +507,19 @@ shaka.mss.MssParser = class {
|
|
|
487
507
|
return null;
|
|
488
508
|
}
|
|
489
509
|
|
|
490
|
-
const lang = streamIndex.
|
|
510
|
+
const lang = streamIndex.attributes['Language'];
|
|
491
511
|
const id = this.globalId_++;
|
|
492
512
|
|
|
493
|
-
const bandwidth =
|
|
494
|
-
qualityLevel, 'Bitrate',
|
|
495
|
-
const width =
|
|
496
|
-
qualityLevel, 'MaxWidth',
|
|
497
|
-
const height =
|
|
498
|
-
qualityLevel, 'MaxHeight',
|
|
499
|
-
const channelsCount =
|
|
500
|
-
qualityLevel, 'Channels',
|
|
501
|
-
const audioSamplingRate =
|
|
502
|
-
qualityLevel, 'SamplingRate',
|
|
513
|
+
const bandwidth = TXml.parseAttr(
|
|
514
|
+
qualityLevel, 'Bitrate', TXml.parsePositiveInt);
|
|
515
|
+
const width = TXml.parseAttr(
|
|
516
|
+
qualityLevel, 'MaxWidth', TXml.parsePositiveInt);
|
|
517
|
+
const height = TXml.parseAttr(
|
|
518
|
+
qualityLevel, 'MaxHeight', TXml.parsePositiveInt);
|
|
519
|
+
const channelsCount = TXml.parseAttr(
|
|
520
|
+
qualityLevel, 'Channels', TXml.parsePositiveInt);
|
|
521
|
+
const audioSamplingRate = TXml.parseAttr(
|
|
522
|
+
qualityLevel, 'SamplingRate', TXml.parsePositiveInt);
|
|
503
523
|
|
|
504
524
|
let duration = context.duration;
|
|
505
525
|
if (timeline.length) {
|
|
@@ -515,7 +535,7 @@ shaka.mss.MssParser = class {
|
|
|
515
535
|
/** @type {!shaka.extern.Stream} */
|
|
516
536
|
const stream = {
|
|
517
537
|
id: id,
|
|
518
|
-
originalId: streamIndex.
|
|
538
|
+
originalId: streamIndex.attributes['Name'] || String(id),
|
|
519
539
|
groupId: null,
|
|
520
540
|
createSegmentIndex: () => Promise.resolve(),
|
|
521
541
|
closeSegmentIndex: () => Promise.resolve(),
|
|
@@ -556,10 +576,11 @@ shaka.mss.MssParser = class {
|
|
|
556
576
|
accessibilityPurpose: null,
|
|
557
577
|
external: false,
|
|
558
578
|
fastSwitching: false,
|
|
579
|
+
fullMimeTypes: new Set(),
|
|
559
580
|
};
|
|
560
581
|
|
|
561
582
|
// This is specifically for text tracks.
|
|
562
|
-
const subType = streamIndex.
|
|
583
|
+
const subType = streamIndex.attributes['Subtype'];
|
|
563
584
|
if (subType) {
|
|
564
585
|
const role = MssParser.ROLE_MAPPING_[subType];
|
|
565
586
|
if (role) {
|
|
@@ -570,12 +591,12 @@ shaka.mss.MssParser = class {
|
|
|
570
591
|
}
|
|
571
592
|
}
|
|
572
593
|
|
|
573
|
-
let fourCCValue = qualityLevel.
|
|
594
|
+
let fourCCValue = qualityLevel.attributes['FourCC'];
|
|
574
595
|
|
|
575
596
|
// If FourCC not defined at QualityLevel level,
|
|
576
597
|
// then get it from StreamIndex level
|
|
577
598
|
if (fourCCValue === null || fourCCValue === '') {
|
|
578
|
-
fourCCValue = streamIndex.
|
|
599
|
+
fourCCValue = streamIndex.attributes['FourCC'];
|
|
579
600
|
}
|
|
580
601
|
|
|
581
602
|
// If still not defined (optional for audio stream,
|
|
@@ -634,6 +655,8 @@ shaka.mss.MssParser = class {
|
|
|
634
655
|
}
|
|
635
656
|
break;
|
|
636
657
|
}
|
|
658
|
+
stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
|
|
659
|
+
stream.mimeType, stream.codecs));
|
|
637
660
|
|
|
638
661
|
// Lazy-Load the segment index to avoid create all init segment at the
|
|
639
662
|
// same time
|
|
@@ -696,7 +719,7 @@ shaka.mss.MssParser = class {
|
|
|
696
719
|
}
|
|
697
720
|
|
|
698
721
|
/**
|
|
699
|
-
* @param {!
|
|
722
|
+
* @param {!shaka.extern.xml.Node} qualityLevel
|
|
700
723
|
* @param {string} type
|
|
701
724
|
* @param {string} fourCCValue
|
|
702
725
|
* @param {!shaka.extern.Stream} stream
|
|
@@ -704,7 +727,7 @@ shaka.mss.MssParser = class {
|
|
|
704
727
|
* @private
|
|
705
728
|
*/
|
|
706
729
|
getCodecPrivateData_(qualityLevel, type, fourCCValue, stream) {
|
|
707
|
-
const codecPrivateData = qualityLevel.
|
|
730
|
+
const codecPrivateData = qualityLevel.attributes['CodecPrivateData'];
|
|
708
731
|
if (codecPrivateData) {
|
|
709
732
|
return codecPrivateData;
|
|
710
733
|
}
|
|
@@ -782,7 +805,7 @@ shaka.mss.MssParser = class {
|
|
|
782
805
|
}
|
|
783
806
|
|
|
784
807
|
/**
|
|
785
|
-
* @param {!
|
|
808
|
+
* @param {!shaka.extern.xml.Node} qualityLevel
|
|
786
809
|
* @param {string} fourCCValue
|
|
787
810
|
* @param {?string} codecPrivateData
|
|
788
811
|
* @return {string}
|
|
@@ -811,7 +834,7 @@ shaka.mss.MssParser = class {
|
|
|
811
834
|
}
|
|
812
835
|
|
|
813
836
|
/**
|
|
814
|
-
* @param {!
|
|
837
|
+
* @param {!shaka.extern.xml.Node} qualityLevel
|
|
815
838
|
* @param {?string} codecPrivateData
|
|
816
839
|
* @return {string}
|
|
817
840
|
* @private
|
|
@@ -837,14 +860,14 @@ shaka.mss.MssParser = class {
|
|
|
837
860
|
/**
|
|
838
861
|
* @param {!shaka.media.InitSegmentReference} initSegmentRef
|
|
839
862
|
* @param {!shaka.extern.Stream} stream
|
|
840
|
-
* @param {!
|
|
863
|
+
* @param {!shaka.extern.xml.Node} streamIndex
|
|
841
864
|
* @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
|
|
842
865
|
* @return {!Array.<!shaka.media.SegmentReference>}
|
|
843
866
|
* @private
|
|
844
867
|
*/
|
|
845
868
|
createSegments_(initSegmentRef, stream, streamIndex, timeline) {
|
|
846
869
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
847
|
-
const url = streamIndex.
|
|
870
|
+
const url = streamIndex.attributes['Url'];
|
|
848
871
|
goog.asserts.assert(url, 'Missing URL for segments');
|
|
849
872
|
|
|
850
873
|
const mediaUrl = url.replace('{bitrate}', String(stream.bandwidth));
|
|
@@ -873,7 +896,7 @@ shaka.mss.MssParser = class {
|
|
|
873
896
|
* Expands a streamIndex into an array-based timeline. The results are in
|
|
874
897
|
* seconds.
|
|
875
898
|
*
|
|
876
|
-
* @param {!
|
|
899
|
+
* @param {!shaka.extern.xml.Node} streamIndex
|
|
877
900
|
* @param {number} timescale
|
|
878
901
|
* @param {number} duration The duration in seconds.
|
|
879
902
|
* @return {!Array.<shaka.mss.MssParser.TimeRange>}
|
|
@@ -886,9 +909,9 @@ shaka.mss.MssParser = class {
|
|
|
886
909
|
goog.asserts.assert(
|
|
887
910
|
duration > 0, 'duration must be a positive integer');
|
|
888
911
|
|
|
889
|
-
const
|
|
912
|
+
const TXml = shaka.util.TXml;
|
|
890
913
|
|
|
891
|
-
const timePoints =
|
|
914
|
+
const timePoints = TXml.findChildren(streamIndex, 'c');
|
|
892
915
|
|
|
893
916
|
/** @type {!Array.<shaka.mss.MssParser.TimeRange>} */
|
|
894
917
|
const timeline = [];
|
|
@@ -898,10 +921,10 @@ shaka.mss.MssParser = class {
|
|
|
898
921
|
const timePoint = timePoints[i];
|
|
899
922
|
const next = timePoints[i + 1];
|
|
900
923
|
const t =
|
|
901
|
-
|
|
924
|
+
TXml.parseAttr(timePoint, 't', TXml.parseNonNegativeInt);
|
|
902
925
|
const d =
|
|
903
|
-
|
|
904
|
-
const r =
|
|
926
|
+
TXml.parseAttr(timePoint, 'd', TXml.parseNonNegativeInt);
|
|
927
|
+
const r = TXml.parseAttr(timePoint, 'r', TXml.parseInt);
|
|
905
928
|
|
|
906
929
|
if (!d) {
|
|
907
930
|
shaka.log.warning(
|
|
@@ -916,7 +939,7 @@ shaka.mss.MssParser = class {
|
|
|
916
939
|
if (repeat < 0) {
|
|
917
940
|
if (next) {
|
|
918
941
|
const nextStartTime =
|
|
919
|
-
|
|
942
|
+
TXml.parseAttr(next, 't', TXml.parseNonNegativeInt);
|
|
920
943
|
if (nextStartTime == null) {
|
|
921
944
|
shaka.log.warning(
|
|
922
945
|
'An "c" element cannot have a negative repeat',
|
|
@@ -46,7 +46,8 @@ goog.require('shaka.util.Timer');
|
|
|
46
46
|
*/
|
|
47
47
|
shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
48
48
|
/**
|
|
49
|
-
* @param {
|
|
49
|
+
* @param {shaka.net.NetworkingEngine.onProgressUpdated=} onProgressUpdated
|
|
50
|
+
* Called when
|
|
50
51
|
* a progress event is triggered. Passed the duration, in milliseconds,
|
|
51
52
|
* that the request took, the number of bytes transferred, and the boolean
|
|
52
53
|
* of whether the switching is allowed.
|
|
@@ -58,9 +59,11 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
58
59
|
* Called when a request is made
|
|
59
60
|
* @param {shaka.net.NetworkingEngine.OnRetry=} onRetry
|
|
60
61
|
* Called when a request retry is made
|
|
62
|
+
* @param {shaka.net.NetworkingEngine.OnResponse=} onResponse
|
|
63
|
+
* Called when receive the response
|
|
61
64
|
*/
|
|
62
65
|
constructor(onProgressUpdated, onHeadersReceived, onDownloadFailed,
|
|
63
|
-
onRequest, onRetry) {
|
|
66
|
+
onRequest, onRetry, onResponse) {
|
|
64
67
|
super();
|
|
65
68
|
|
|
66
69
|
/** @private {boolean} */
|
|
@@ -75,7 +78,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
75
78
|
/** @private {!Set.<shaka.extern.ResponseFilter>} */
|
|
76
79
|
this.responseFilters_ = new Set();
|
|
77
80
|
|
|
78
|
-
/** @private {?
|
|
81
|
+
/** @private {?shaka.net.NetworkingEngine.onProgressUpdated} */
|
|
79
82
|
this.onProgressUpdated_ = onProgressUpdated || null;
|
|
80
83
|
|
|
81
84
|
/** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */
|
|
@@ -90,6 +93,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
90
93
|
/** @private {?shaka.net.NetworkingEngine.OnRetry} */
|
|
91
94
|
this.onRetry_ = onRetry || null;
|
|
92
95
|
|
|
96
|
+
/** @private {?shaka.net.NetworkingEngine.OnResponse} */
|
|
97
|
+
this.onResponse_ = onResponse || null;
|
|
98
|
+
|
|
93
99
|
/** @private {boolean} */
|
|
94
100
|
this.forceHTTPS_ = false;
|
|
95
101
|
}
|
|
@@ -333,6 +339,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
333
339
|
this.onProgressUpdated_(
|
|
334
340
|
response.timeMs, response.data.byteLength, allowSwitch);
|
|
335
341
|
}
|
|
342
|
+
if (this.onResponse_) {
|
|
343
|
+
this.onResponse_(type, response, context);
|
|
344
|
+
}
|
|
336
345
|
return response;
|
|
337
346
|
}, (e) => {
|
|
338
347
|
// Any error thrown from elsewhere should be recategorized as CRITICAL
|
|
@@ -510,6 +519,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
510
519
|
|
|
511
520
|
startTimeMs = Date.now();
|
|
512
521
|
const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
522
|
+
let packetNumber = 0;
|
|
513
523
|
|
|
514
524
|
const progressUpdated = (time, bytes, numBytesRemaining) => {
|
|
515
525
|
if (connectionTimer) {
|
|
@@ -519,8 +529,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
519
529
|
stallTimer.tickAfter(stallTimeoutMs / 1000);
|
|
520
530
|
}
|
|
521
531
|
if (this.onProgressUpdated_ && type == segment) {
|
|
532
|
+
packetNumber++;
|
|
533
|
+
request.packetNumber = packetNumber;
|
|
522
534
|
const allowSwitch = this.allowSwitch_(context);
|
|
523
|
-
this.onProgressUpdated_(time, bytes, allowSwitch);
|
|
535
|
+
this.onProgressUpdated_(time, bytes, allowSwitch, request);
|
|
524
536
|
gotProgress = true;
|
|
525
537
|
numBytesRemainingObj.setBytes(numBytesRemaining);
|
|
526
538
|
}
|
|
@@ -530,7 +542,11 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
530
542
|
this.onHeadersReceived_(headers, request, type);
|
|
531
543
|
}
|
|
532
544
|
headersReceivedCalled = true;
|
|
545
|
+
request.timeToFirstByte = Date.now() -
|
|
546
|
+
/** @type {number} */ (request.requestStartTime);
|
|
533
547
|
};
|
|
548
|
+
request.requestStartTime = Date.now();
|
|
549
|
+
|
|
534
550
|
const requestPlugin = plugin(
|
|
535
551
|
request.uris[index], request, type, progressUpdated, headersReceived);
|
|
536
552
|
|
|
@@ -899,6 +915,21 @@ shaka.net.NetworkingEngine.ResponseAndGotProgress;
|
|
|
899
915
|
*/
|
|
900
916
|
shaka.net.NetworkingEngine.OnHeadersReceived;
|
|
901
917
|
|
|
918
|
+
/**
|
|
919
|
+
* @typedef {function(
|
|
920
|
+
* number,
|
|
921
|
+
* number,
|
|
922
|
+
* boolean,
|
|
923
|
+
* shaka.extern.Request=)}
|
|
924
|
+
*
|
|
925
|
+
* @description
|
|
926
|
+
* A callback that is passed the duration, in milliseconds,
|
|
927
|
+
* that the request took, the number of bytes transferred, a boolean
|
|
928
|
+
* representing whether the switching is allowed and a ref to the
|
|
929
|
+
* original request.
|
|
930
|
+
* @export
|
|
931
|
+
*/
|
|
932
|
+
shaka.net.NetworkingEngine.onProgressUpdated;
|
|
902
933
|
|
|
903
934
|
/**
|
|
904
935
|
* @typedef {function(
|
|
@@ -941,3 +972,17 @@ shaka.net.NetworkingEngine.OnRequest;
|
|
|
941
972
|
* @export
|
|
942
973
|
*/
|
|
943
974
|
shaka.net.NetworkingEngine.OnRetry;
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* @typedef {function(
|
|
979
|
+
* !shaka.net.NetworkingEngine.RequestType,
|
|
980
|
+
* !shaka.extern.Response,
|
|
981
|
+
* (shaka.extern.RequestContext|undefined))}
|
|
982
|
+
*
|
|
983
|
+
* @description
|
|
984
|
+
* A callback function called on every request
|
|
985
|
+
* @export
|
|
986
|
+
*/
|
|
987
|
+
shaka.net.NetworkingEngine.OnResponse;
|
|
988
|
+
|