shaka-player 4.7.13 → 4.8.1
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 +2 -0
- package/CONTRIBUTORS +2 -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 +34 -23
- package/dist/locales.js +41 -31
- package/dist/shaka-player.compiled.d.ts +196 -45
- package/dist/shaka-player.compiled.debug.d.ts +196 -45
- package/dist/shaka-player.compiled.debug.externs.js +491 -348
- package/dist/shaka-player.compiled.debug.js +969 -847
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +491 -348
- package/dist/shaka-player.compiled.js +1238 -1171
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +259 -48
- package/dist/shaka-player.ui.debug.d.ts +259 -48
- package/dist/shaka-player.ui.debug.externs.js +598 -377
- package/dist/shaka-player.ui.debug.js +1185 -980
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +598 -377
- package/dist/shaka-player.ui.js +1526 -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 +58 -6
- package/externs/shaka/net.js +13 -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 +237 -177
- package/lib/dash/mpd_utils.js +72 -65
- package/lib/dash/segment_base.js +27 -21
- package/lib/dash/segment_list.js +18 -16
- package/lib/dash/segment_template.js +45 -22
- package/lib/hls/hls_parser.js +358 -164
- 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 +34 -5
- package/lib/media/preload_manager.js +827 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/segment_index.js +93 -47
- package/lib/media/segment_prefetch.js +140 -43
- package/lib/media/segment_reference.js +43 -8
- package/lib/media/segment_utils.js +208 -25
- package/lib/media/streaming_engine.js +355 -66
- package/lib/mss/content_protection.js +31 -25
- package/lib/mss/mss_parser.js +79 -55
- package/lib/net/networking_engine.js +49 -4
- package/lib/offline/storage.js +3 -3
- package/lib/player.js +882 -672
- 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 +153 -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/mp4_box_parsers.js +346 -17
- package/lib/util/mp4_generator.js +23 -0
- package/lib/util/periods.js +19 -0
- package/lib/util/platform.js +161 -9
- package/lib/util/player_configuration.js +79 -13
- package/lib/util/stats.js +16 -0
- package/lib/util/stream_utils.js +22 -8
- 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
package/lib/dash/dash_parser.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.dash.DashParser');
|
|
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.dash.ContentProtection');
|
|
12
13
|
goog.require('shaka.dash.MpdUtils');
|
|
@@ -29,8 +30,10 @@ goog.require('shaka.util.MimeUtils');
|
|
|
29
30
|
goog.require('shaka.util.Networking');
|
|
30
31
|
goog.require('shaka.util.OperationManager');
|
|
31
32
|
goog.require('shaka.util.PeriodCombiner');
|
|
33
|
+
goog.require('shaka.util.PlayerConfiguration');
|
|
32
34
|
goog.require('shaka.util.StringUtils');
|
|
33
35
|
goog.require('shaka.util.Timer');
|
|
36
|
+
goog.require('shaka.util.TXml');
|
|
34
37
|
goog.require('shaka.util.XmlUtils');
|
|
35
38
|
|
|
36
39
|
|
|
@@ -129,9 +132,14 @@ shaka.dash.DashParser = class {
|
|
|
129
132
|
configure(config) {
|
|
130
133
|
goog.asserts.assert(config.dash != null,
|
|
131
134
|
'DashManifestConfiguration should not be null!');
|
|
132
|
-
|
|
135
|
+
const needFireUpdate = this.playerInterface_ &&
|
|
136
|
+
config.dash.updatePeriod != this.config_.dash.updatePeriod &&
|
|
137
|
+
config.dash.updatePeriod >= 0;
|
|
133
138
|
this.config_ = config;
|
|
134
|
-
|
|
139
|
+
if (needFireUpdate && this.manifest_ &&
|
|
140
|
+
this.manifest_.presentationTimeline.isLive()) {
|
|
141
|
+
this.updateNow_();
|
|
142
|
+
}
|
|
135
143
|
if (this.contentSteeringManager_) {
|
|
136
144
|
this.contentSteeringManager_.configure(this.config_);
|
|
137
145
|
}
|
|
@@ -140,6 +148,8 @@ shaka.dash.DashParser = class {
|
|
|
140
148
|
this.periodCombiner_.setAllowMultiTypeVariants(
|
|
141
149
|
this.config_.dash.multiTypeVariantsAllowed &&
|
|
142
150
|
shaka.media.Capabilities.isChangeTypeSupported());
|
|
151
|
+
this.periodCombiner_.setUseStreamOnce(
|
|
152
|
+
this.config_.dash.useStreamOnceInPeriodFlattening);
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
155
|
|
|
@@ -327,15 +337,42 @@ shaka.dash.DashParser = class {
|
|
|
327
337
|
* @private
|
|
328
338
|
*/
|
|
329
339
|
async parseManifest_(data, finalManifestUri) {
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
340
|
+
let manifestData = data;
|
|
341
|
+
const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
|
|
342
|
+
const defaultManifestPreprocessor =
|
|
343
|
+
shaka.util.PlayerConfiguration.defaultManifestPreprocessor;
|
|
344
|
+
if (manifestPreprocessor != defaultManifestPreprocessor) {
|
|
345
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
346
|
+
'manifest.dash.manifestPreprocessor configuration',
|
|
347
|
+
'Please Use manifest.dash.manifestPreprocessorTXml instead.');
|
|
348
|
+
const mpdElement =
|
|
349
|
+
shaka.util.XmlUtils.parseXml(manifestData, 'MPD');
|
|
350
|
+
if (!mpdElement) {
|
|
351
|
+
throw new shaka.util.Error(
|
|
352
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
353
|
+
shaka.util.Error.Category.MANIFEST,
|
|
354
|
+
shaka.util.Error.Code.DASH_INVALID_XML,
|
|
355
|
+
finalManifestUri);
|
|
356
|
+
}
|
|
357
|
+
manifestPreprocessor(mpdElement);
|
|
358
|
+
manifestData = shaka.util.XmlUtils.toArrayBuffer(mpdElement);
|
|
359
|
+
}
|
|
360
|
+
const mpd = shaka.util.TXml.parseXml(manifestData, 'MPD');
|
|
334
361
|
if (!mpd) {
|
|
335
|
-
throw new Error(
|
|
336
|
-
Error.Severity.CRITICAL,
|
|
337
|
-
Error.
|
|
362
|
+
throw new shaka.util.Error(
|
|
363
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
364
|
+
shaka.util.Error.Category.MANIFEST,
|
|
365
|
+
shaka.util.Error.Code.DASH_INVALID_XML,
|
|
366
|
+
finalManifestUri);
|
|
338
367
|
}
|
|
368
|
+
const manifestPreprocessorTXml =
|
|
369
|
+
this.config_.dash.manifestPreprocessorTXml;
|
|
370
|
+
const defaultManifestPreprocessorTXml =
|
|
371
|
+
shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml;
|
|
372
|
+
if (manifestPreprocessorTXml != defaultManifestPreprocessorTXml) {
|
|
373
|
+
manifestPreprocessorTXml(mpd);
|
|
374
|
+
}
|
|
375
|
+
|
|
339
376
|
const disableXlinkProcessing = this.config_.dash.disableXlinkProcessing;
|
|
340
377
|
if (disableXlinkProcessing) {
|
|
341
378
|
return this.processManifest_(mpd, finalManifestUri);
|
|
@@ -343,7 +380,7 @@ shaka.dash.DashParser = class {
|
|
|
343
380
|
|
|
344
381
|
// Process the mpd to account for xlink connections.
|
|
345
382
|
const failGracefully = this.config_.dash.xlinkFailGracefully;
|
|
346
|
-
const xlinkOperation = MpdUtils.processXlinks(
|
|
383
|
+
const xlinkOperation = shaka.dash.MpdUtils.processXlinks(
|
|
347
384
|
mpd, this.config_.retryParameters, failGracefully, finalManifestUri,
|
|
348
385
|
this.playerInterface_.networkingEngine);
|
|
349
386
|
this.operationManager_.manage(xlinkOperation);
|
|
@@ -355,19 +392,17 @@ shaka.dash.DashParser = class {
|
|
|
355
392
|
/**
|
|
356
393
|
* Takes a formatted MPD and converts it into a manifest.
|
|
357
394
|
*
|
|
358
|
-
* @param {!
|
|
395
|
+
* @param {!shaka.extern.xml.Node} mpd
|
|
359
396
|
* @param {string} finalManifestUri The final manifest URI, which may
|
|
360
397
|
* differ from this.manifestUri_ if there has been a redirect.
|
|
361
398
|
* @return {!Promise}
|
|
362
399
|
* @private
|
|
363
400
|
*/
|
|
364
401
|
async processManifest_(mpd, finalManifestUri) {
|
|
365
|
-
const
|
|
402
|
+
const TXml = shaka.util.TXml;
|
|
366
403
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
manifestPreprocessor(mpd);
|
|
370
|
-
}
|
|
404
|
+
goog.asserts.assert(this.config_,
|
|
405
|
+
'Must call configure() before processManifest_()!');
|
|
371
406
|
|
|
372
407
|
if (this.contentSteeringManager_) {
|
|
373
408
|
this.contentSteeringManager_.clearPreviousLocations();
|
|
@@ -381,10 +416,10 @@ shaka.dash.DashParser = class {
|
|
|
381
416
|
const locations = [];
|
|
382
417
|
/** @type {!Map.<string, string>} */
|
|
383
418
|
const locationsMapping = new Map();
|
|
384
|
-
const locationsObjs =
|
|
419
|
+
const locationsObjs = TXml.findChildren(mpd, 'Location');
|
|
385
420
|
for (const locationsObj of locationsObjs) {
|
|
386
|
-
const serviceLocation = locationsObj.
|
|
387
|
-
const uri =
|
|
421
|
+
const serviceLocation = locationsObj.attributes['serviceLocation'];
|
|
422
|
+
const uri = TXml.getContents(locationsObj);
|
|
388
423
|
if (!uri) {
|
|
389
424
|
continue;
|
|
390
425
|
}
|
|
@@ -414,10 +449,10 @@ shaka.dash.DashParser = class {
|
|
|
414
449
|
|
|
415
450
|
let contentSteeringPromise = Promise.resolve();
|
|
416
451
|
|
|
417
|
-
const contentSteering =
|
|
452
|
+
const contentSteering = TXml.findChild(mpd, 'ContentSteering');
|
|
418
453
|
if (contentSteering && this.playerInterface_) {
|
|
419
454
|
const defaultPathwayId =
|
|
420
|
-
contentSteering.
|
|
455
|
+
contentSteering.attributes['defaultServiceLocation'];
|
|
421
456
|
if (!this.contentSteeringManager_) {
|
|
422
457
|
this.contentSteeringManager_ =
|
|
423
458
|
new shaka.util.ContentSteeringManager(this.playerInterface_);
|
|
@@ -426,11 +461,11 @@ shaka.dash.DashParser = class {
|
|
|
426
461
|
shaka.media.ManifestParser.DASH);
|
|
427
462
|
this.contentSteeringManager_.setBaseUris(manifestBaseUris);
|
|
428
463
|
this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
|
|
429
|
-
const uri =
|
|
464
|
+
const uri = TXml.getContents(contentSteering);
|
|
430
465
|
if (uri) {
|
|
431
466
|
const queryBeforeStart =
|
|
432
|
-
|
|
433
|
-
|
|
467
|
+
TXml.parseAttr(contentSteering, 'queryBeforeStart',
|
|
468
|
+
TXml.parseBoolean, /* defaultValue= */ false);
|
|
434
469
|
if (queryBeforeStart) {
|
|
435
470
|
contentSteeringPromise =
|
|
436
471
|
this.contentSteeringManager_.requestInfo(uri);
|
|
@@ -449,13 +484,13 @@ shaka.dash.DashParser = class {
|
|
|
449
484
|
}
|
|
450
485
|
}
|
|
451
486
|
|
|
452
|
-
const uriObjs =
|
|
487
|
+
const uriObjs = TXml.findChildren(mpd, 'BaseURL');
|
|
453
488
|
let calculatedBaseUris;
|
|
454
489
|
let someLocationValid = false;
|
|
455
490
|
if (this.contentSteeringManager_) {
|
|
456
491
|
for (const uriObj of uriObjs) {
|
|
457
|
-
const serviceLocation = uriObj.
|
|
458
|
-
const uri =
|
|
492
|
+
const serviceLocation = uriObj.attributes['serviceLocation'];
|
|
493
|
+
const uri = TXml.getContents(uriObj);
|
|
459
494
|
if (serviceLocation && uri) {
|
|
460
495
|
this.contentSteeringManager_.addLocation(
|
|
461
496
|
'BaseURL', serviceLocation, uri);
|
|
@@ -464,7 +499,7 @@ shaka.dash.DashParser = class {
|
|
|
464
499
|
}
|
|
465
500
|
}
|
|
466
501
|
if (!someLocationValid || !this.contentSteeringManager_) {
|
|
467
|
-
const uris = uriObjs.map(
|
|
502
|
+
const uris = uriObjs.map(TXml.getContents);
|
|
468
503
|
calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
|
|
469
504
|
manifestBaseUris, uris);
|
|
470
505
|
}
|
|
@@ -481,41 +516,41 @@ shaka.dash.DashParser = class {
|
|
|
481
516
|
|
|
482
517
|
let availabilityTimeOffset = 0;
|
|
483
518
|
if (uriObjs && uriObjs.length) {
|
|
484
|
-
availabilityTimeOffset =
|
|
485
|
-
|
|
519
|
+
availabilityTimeOffset = TXml.parseAttr(uriObjs[0],
|
|
520
|
+
'availabilityTimeOffset', TXml.parseFloat) || 0;
|
|
486
521
|
}
|
|
487
522
|
|
|
488
523
|
const ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
|
|
489
524
|
let minBufferTime = 0;
|
|
490
525
|
if (!ignoreMinBufferTime) {
|
|
491
526
|
minBufferTime =
|
|
492
|
-
|
|
527
|
+
TXml.parseAttr(mpd, 'minBufferTime', TXml.parseDuration) || 0;
|
|
493
528
|
}
|
|
494
529
|
|
|
495
|
-
this.updatePeriod_ = /** @type {number} */ (
|
|
496
|
-
mpd, 'minimumUpdatePeriod',
|
|
530
|
+
this.updatePeriod_ = /** @type {number} */ (TXml.parseAttr(
|
|
531
|
+
mpd, 'minimumUpdatePeriod', TXml.parseDuration, -1));
|
|
497
532
|
|
|
498
|
-
const presentationStartTime =
|
|
499
|
-
mpd, 'availabilityStartTime',
|
|
500
|
-
let segmentAvailabilityDuration =
|
|
501
|
-
mpd, 'timeShiftBufferDepth',
|
|
533
|
+
const presentationStartTime = TXml.parseAttr(
|
|
534
|
+
mpd, 'availabilityStartTime', TXml.parseDate);
|
|
535
|
+
let segmentAvailabilityDuration = TXml.parseAttr(
|
|
536
|
+
mpd, 'timeShiftBufferDepth', TXml.parseDuration);
|
|
502
537
|
|
|
503
538
|
const ignoreSuggestedPresentationDelay =
|
|
504
539
|
this.config_.dash.ignoreSuggestedPresentationDelay;
|
|
505
540
|
let suggestedPresentationDelay = null;
|
|
506
541
|
if (!ignoreSuggestedPresentationDelay) {
|
|
507
|
-
suggestedPresentationDelay =
|
|
508
|
-
mpd, 'suggestedPresentationDelay',
|
|
542
|
+
suggestedPresentationDelay = TXml.parseAttr(
|
|
543
|
+
mpd, 'suggestedPresentationDelay', TXml.parseDuration);
|
|
509
544
|
}
|
|
510
545
|
|
|
511
546
|
const ignoreMaxSegmentDuration =
|
|
512
547
|
this.config_.dash.ignoreMaxSegmentDuration;
|
|
513
548
|
let maxSegmentDuration = null;
|
|
514
549
|
if (!ignoreMaxSegmentDuration) {
|
|
515
|
-
maxSegmentDuration =
|
|
516
|
-
mpd, 'maxSegmentDuration',
|
|
550
|
+
maxSegmentDuration = TXml.parseAttr(
|
|
551
|
+
mpd, 'maxSegmentDuration', TXml.parseDuration);
|
|
517
552
|
}
|
|
518
|
-
const mpdType = mpd.
|
|
553
|
+
const mpdType = mpd.attributes['type'] || 'static';
|
|
519
554
|
|
|
520
555
|
/** @type {!shaka.media.PresentationTimeline} */
|
|
521
556
|
let presentationTimeline;
|
|
@@ -575,7 +610,7 @@ shaka.dash.DashParser = class {
|
|
|
575
610
|
presentationTimeline.setSegmentAvailabilityDuration(
|
|
576
611
|
segmentAvailabilityDuration);
|
|
577
612
|
|
|
578
|
-
const profiles = mpd.
|
|
613
|
+
const profiles = mpd.attributes['profiles'] || '';
|
|
579
614
|
|
|
580
615
|
/** @type {shaka.dash.DashParser.Context} */
|
|
581
616
|
const context = {
|
|
@@ -666,8 +701,8 @@ shaka.dash.DashParser = class {
|
|
|
666
701
|
// We only need to do clock sync when we're using presentation start
|
|
667
702
|
// time. This condition also excludes VOD streams.
|
|
668
703
|
if (presentationTimeline.usingPresentationStartTime()) {
|
|
669
|
-
const
|
|
670
|
-
const timingElements =
|
|
704
|
+
const TXml = shaka.util.TXml;
|
|
705
|
+
const timingElements = TXml.findChildren(mpd, 'UTCTiming');
|
|
671
706
|
const offset = await this.parseUtcTiming_(getBaseUris, timingElements);
|
|
672
707
|
// Detect calls to stop().
|
|
673
708
|
if (!this.playerInterface_) {
|
|
@@ -706,33 +741,33 @@ shaka.dash.DashParser = class {
|
|
|
706
741
|
* Reads maxLatency and maxPlaybackRate properties from service
|
|
707
742
|
* description element.
|
|
708
743
|
*
|
|
709
|
-
* @param {!
|
|
744
|
+
* @param {!shaka.extern.xml.Node} mpd
|
|
710
745
|
* @return {?shaka.extern.ServiceDescription}
|
|
711
746
|
* @private
|
|
712
747
|
*/
|
|
713
748
|
parseServiceDescription_(mpd) {
|
|
714
|
-
const
|
|
715
|
-
const elem =
|
|
749
|
+
const TXml = shaka.util.TXml;
|
|
750
|
+
const elem = TXml.findChild(mpd, 'ServiceDescription');
|
|
716
751
|
|
|
717
752
|
if (!elem ) {
|
|
718
753
|
return null;
|
|
719
754
|
}
|
|
720
755
|
|
|
721
|
-
const latencyNode =
|
|
722
|
-
const playbackRateNode =
|
|
756
|
+
const latencyNode = TXml.findChild(elem, 'Latency');
|
|
757
|
+
const playbackRateNode = TXml.findChild(elem, 'PlaybackRate');
|
|
723
758
|
|
|
724
|
-
if ((latencyNode && latencyNode.
|
|
725
|
-
const maxLatency = latencyNode && latencyNode.
|
|
726
|
-
parseInt(latencyNode.
|
|
759
|
+
if ((latencyNode && latencyNode.attributes['max']) || playbackRateNode) {
|
|
760
|
+
const maxLatency = latencyNode && latencyNode.attributes['max'] ?
|
|
761
|
+
parseInt(latencyNode.attributes['max'], 10) / 1000 :
|
|
727
762
|
null;
|
|
728
763
|
const maxPlaybackRate = playbackRateNode ?
|
|
729
|
-
parseFloat(playbackRateNode.
|
|
764
|
+
parseFloat(playbackRateNode.attributes['max']) :
|
|
730
765
|
null;
|
|
731
|
-
const minLatency = latencyNode && latencyNode.
|
|
732
|
-
parseInt(latencyNode.
|
|
766
|
+
const minLatency = latencyNode && latencyNode.attributes['min'] ?
|
|
767
|
+
parseInt(latencyNode.attributes['min'], 10) / 1000 :
|
|
733
768
|
null;
|
|
734
769
|
const minPlaybackRate = playbackRateNode ?
|
|
735
|
-
parseFloat(playbackRateNode.
|
|
770
|
+
parseFloat(playbackRateNode.attributes['min']) :
|
|
736
771
|
null;
|
|
737
772
|
|
|
738
773
|
return {
|
|
@@ -753,7 +788,7 @@ shaka.dash.DashParser = class {
|
|
|
753
788
|
*
|
|
754
789
|
* @param {shaka.dash.DashParser.Context} context
|
|
755
790
|
* @param {function():!Array.<string>} getBaseUris
|
|
756
|
-
* @param {!
|
|
791
|
+
* @param {!shaka.extern.xml.Node} mpd
|
|
757
792
|
* @return {{
|
|
758
793
|
* periods: !Array.<shaka.extern.Period>,
|
|
759
794
|
* duration: ?number,
|
|
@@ -762,21 +797,21 @@ shaka.dash.DashParser = class {
|
|
|
762
797
|
* @private
|
|
763
798
|
*/
|
|
764
799
|
parsePeriods_(context, getBaseUris, mpd) {
|
|
765
|
-
const
|
|
766
|
-
const presentationDuration =
|
|
767
|
-
mpd, 'mediaPresentationDuration',
|
|
800
|
+
const TXml = shaka.util.TXml;
|
|
801
|
+
const presentationDuration = TXml.parseAttr(
|
|
802
|
+
mpd, 'mediaPresentationDuration', TXml.parseDuration);
|
|
768
803
|
|
|
769
804
|
const periods = [];
|
|
770
805
|
let prevEnd = 0;
|
|
771
|
-
const periodNodes =
|
|
806
|
+
const periodNodes = TXml.findChildren(mpd, 'Period');
|
|
772
807
|
for (let i = 0; i < periodNodes.length; i++) {
|
|
773
808
|
const elem = periodNodes[i];
|
|
774
809
|
const next = periodNodes[i + 1];
|
|
775
810
|
const start = /** @type {number} */ (
|
|
776
|
-
|
|
777
|
-
const periodId = elem.id;
|
|
811
|
+
TXml.parseAttr(elem, 'start', TXml.parseDuration, prevEnd));
|
|
812
|
+
const periodId = elem.attributes['id'];
|
|
778
813
|
const givenDuration =
|
|
779
|
-
|
|
814
|
+
TXml.parseAttr(elem, 'duration', TXml.parseDuration);
|
|
780
815
|
|
|
781
816
|
let periodDuration = null;
|
|
782
817
|
if (next) {
|
|
@@ -784,7 +819,7 @@ shaka.dash.DashParser = class {
|
|
|
784
819
|
// of the following Period is the duration of the media content
|
|
785
820
|
// represented by this Period."
|
|
786
821
|
const nextStart =
|
|
787
|
-
|
|
822
|
+
TXml.parseAttr(next, 'start', TXml.parseDuration);
|
|
788
823
|
if (nextStart != null) {
|
|
789
824
|
periodDuration = nextStart - start;
|
|
790
825
|
}
|
|
@@ -912,7 +947,7 @@ shaka.dash.DashParser = class {
|
|
|
912
947
|
*/
|
|
913
948
|
parsePeriod_(context, getBaseUris, periodInfo) {
|
|
914
949
|
const Functional = shaka.util.Functional;
|
|
915
|
-
const
|
|
950
|
+
const TXml = shaka.util.TXml;
|
|
916
951
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
917
952
|
|
|
918
953
|
context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
|
|
@@ -928,7 +963,7 @@ shaka.dash.DashParser = class {
|
|
|
928
963
|
}
|
|
929
964
|
|
|
930
965
|
const eventStreamNodes =
|
|
931
|
-
|
|
966
|
+
TXml.findChildren(periodInfo.node, 'EventStream');
|
|
932
967
|
const availabilityStart =
|
|
933
968
|
context.presentationTimeline.getSegmentAvailabilityStart();
|
|
934
969
|
|
|
@@ -938,7 +973,7 @@ shaka.dash.DashParser = class {
|
|
|
938
973
|
}
|
|
939
974
|
|
|
940
975
|
const adaptationSetNodes =
|
|
941
|
-
|
|
976
|
+
TXml.findChildren(periodInfo.node, 'AdaptationSet');
|
|
942
977
|
const adaptationSets = adaptationSetNodes
|
|
943
978
|
.map((node) => this.parseAdaptationSet_(context, node))
|
|
944
979
|
.filter(Functional.isNotNull);
|
|
@@ -1047,12 +1082,12 @@ shaka.dash.DashParser = class {
|
|
|
1047
1082
|
* Parses an AdaptationSet XML element.
|
|
1048
1083
|
*
|
|
1049
1084
|
* @param {shaka.dash.DashParser.Context} context
|
|
1050
|
-
* @param {!
|
|
1085
|
+
* @param {!shaka.extern.xml.Node} elem The AdaptationSet element.
|
|
1051
1086
|
* @return {?shaka.dash.DashParser.AdaptationInfo}
|
|
1052
1087
|
* @private
|
|
1053
1088
|
*/
|
|
1054
1089
|
parseAdaptationSet_(context, elem) {
|
|
1055
|
-
const
|
|
1090
|
+
const TXml = shaka.util.TXml;
|
|
1056
1091
|
const Functional = shaka.util.Functional;
|
|
1057
1092
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
1058
1093
|
const ContentType = ManifestParserUtils.ContentType;
|
|
@@ -1061,9 +1096,9 @@ shaka.dash.DashParser = class {
|
|
|
1061
1096
|
context.adaptationSet = this.createFrame_(elem, context.period, null);
|
|
1062
1097
|
|
|
1063
1098
|
let main = false;
|
|
1064
|
-
const roleElements =
|
|
1099
|
+
const roleElements = TXml.findChildren(elem, 'Role');
|
|
1065
1100
|
const roleValues = roleElements.map((role) => {
|
|
1066
|
-
return role.
|
|
1101
|
+
return role.attributes['value'];
|
|
1067
1102
|
}).filter(Functional.isNotNull);
|
|
1068
1103
|
|
|
1069
1104
|
// Default kind for text streams is 'subtitle' if unspecified in the
|
|
@@ -1075,12 +1110,12 @@ shaka.dash.DashParser = class {
|
|
|
1075
1110
|
}
|
|
1076
1111
|
|
|
1077
1112
|
for (const roleElement of roleElements) {
|
|
1078
|
-
const scheme = roleElement.
|
|
1113
|
+
const scheme = roleElement.attributes['schemeIdUri'];
|
|
1079
1114
|
if (scheme == null || scheme == 'urn:mpeg:dash:role:2011') {
|
|
1080
1115
|
// These only apply for the given scheme, but allow them to be specified
|
|
1081
1116
|
// if there is no scheme specified.
|
|
1082
1117
|
// See: DASH section 5.8.5.5
|
|
1083
|
-
const value = roleElement.
|
|
1118
|
+
const value = roleElement.attributes['value'];
|
|
1084
1119
|
switch (value) {
|
|
1085
1120
|
case 'main':
|
|
1086
1121
|
main = true;
|
|
@@ -1125,18 +1160,18 @@ shaka.dash.DashParser = class {
|
|
|
1125
1160
|
};
|
|
1126
1161
|
|
|
1127
1162
|
const essentialProperties =
|
|
1128
|
-
|
|
1163
|
+
TXml.findChildren(elem, 'EssentialProperty');
|
|
1129
1164
|
// ID of real AdaptationSet if this is a trick mode set:
|
|
1130
1165
|
let trickModeFor = null;
|
|
1131
1166
|
let isFastSwitching = false;
|
|
1132
1167
|
let unrecognizedEssentialProperty = false;
|
|
1133
1168
|
for (const prop of essentialProperties) {
|
|
1134
|
-
const schemeId = prop.
|
|
1169
|
+
const schemeId = prop.attributes['schemeIdUri'];
|
|
1135
1170
|
if (schemeId == 'http://dashif.org/guidelines/trickmode') {
|
|
1136
|
-
trickModeFor = prop.
|
|
1171
|
+
trickModeFor = prop.attributes['value'];
|
|
1137
1172
|
} else if (schemeId == transferCharacteristicsScheme) {
|
|
1138
1173
|
videoRange = getVideoRangeFromTransferCharacteristicCICP(
|
|
1139
|
-
parseInt(prop.
|
|
1174
|
+
parseInt(prop.attributes['value'], 10),
|
|
1140
1175
|
);
|
|
1141
1176
|
} else if (schemeId == colourPrimariesScheme ||
|
|
1142
1177
|
schemeId == matrixCoefficientsScheme) {
|
|
@@ -1148,25 +1183,29 @@ shaka.dash.DashParser = class {
|
|
|
1148
1183
|
}
|
|
1149
1184
|
}
|
|
1150
1185
|
|
|
1186
|
+
let lastSegmentNumber = null;
|
|
1187
|
+
|
|
1151
1188
|
const supplementalProperties =
|
|
1152
|
-
|
|
1189
|
+
TXml.findChildren(elem, 'SupplementalProperty');
|
|
1153
1190
|
for (const prop of supplementalProperties) {
|
|
1154
|
-
const schemeId = prop.
|
|
1155
|
-
if (schemeId ==
|
|
1191
|
+
const schemeId = prop.attributes['schemeIdUri'];
|
|
1192
|
+
if (schemeId == 'http://dashif.org/guidelines/last-segment-number') {
|
|
1193
|
+
lastSegmentNumber = parseInt(prop.attributes['value'], 10) - 1;
|
|
1194
|
+
} else if (schemeId == transferCharacteristicsScheme) {
|
|
1156
1195
|
videoRange = getVideoRangeFromTransferCharacteristicCICP(
|
|
1157
|
-
parseInt(prop.
|
|
1196
|
+
parseInt(prop.attributes['value'], 10),
|
|
1158
1197
|
);
|
|
1159
1198
|
}
|
|
1160
1199
|
}
|
|
1161
1200
|
|
|
1162
|
-
const accessibilities =
|
|
1201
|
+
const accessibilities = TXml.findChildren(elem, 'Accessibility');
|
|
1163
1202
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
1164
1203
|
const closedCaptions = new Map();
|
|
1165
1204
|
/** @type {?shaka.media.ManifestParser.AccessibilityPurpose} */
|
|
1166
1205
|
let accessibilityPurpose;
|
|
1167
1206
|
for (const prop of accessibilities) {
|
|
1168
|
-
const schemeId = prop.
|
|
1169
|
-
const value = prop.
|
|
1207
|
+
const schemeId = prop.attributes['schemeIdUri'];
|
|
1208
|
+
const value = prop.attributes['value'];
|
|
1170
1209
|
if (schemeId == 'urn:scte:dash:cc:cea-608:2015' ) {
|
|
1171
1210
|
let channelId = 1;
|
|
1172
1211
|
if (value != null) {
|
|
@@ -1270,7 +1309,7 @@ shaka.dash.DashParser = class {
|
|
|
1270
1309
|
}
|
|
1271
1310
|
|
|
1272
1311
|
const contentProtectionElems =
|
|
1273
|
-
|
|
1312
|
+
TXml.findChildren(elem, 'ContentProtection');
|
|
1274
1313
|
const contentProtection = ContentProtection.parseFromAdaptationSet(
|
|
1275
1314
|
contentProtectionElems,
|
|
1276
1315
|
this.config_.dash.ignoreDrmInfo,
|
|
@@ -1280,24 +1319,26 @@ shaka.dash.DashParser = class {
|
|
|
1280
1319
|
context.adaptationSet.language || 'und');
|
|
1281
1320
|
|
|
1282
1321
|
// This attribute is currently non-standard, but it is supported by Kaltura.
|
|
1283
|
-
let label = elem.
|
|
1322
|
+
let label = elem.attributes['label'];
|
|
1284
1323
|
|
|
1285
1324
|
// See DASH IOP 4.3 here https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf (page 35)
|
|
1286
|
-
const labelElements =
|
|
1325
|
+
const labelElements = TXml.findChildren(elem, 'Label');
|
|
1287
1326
|
if (labelElements && labelElements.length) {
|
|
1288
1327
|
// NOTE: Right now only one label field is supported.
|
|
1289
1328
|
const firstLabelElement = labelElements[0];
|
|
1290
|
-
|
|
1291
|
-
|
|
1329
|
+
const textContent = shaka.util.TXml.getTextContents(firstLabelElement);
|
|
1330
|
+
if (textContent) {
|
|
1331
|
+
label = textContent;
|
|
1292
1332
|
}
|
|
1293
1333
|
}
|
|
1294
1334
|
|
|
1295
1335
|
// Parse Representations into Streams.
|
|
1296
|
-
const representations =
|
|
1336
|
+
const representations = TXml.findChildren(elem, 'Representation');
|
|
1297
1337
|
const streams = representations.map((representation) => {
|
|
1298
1338
|
const parsedRepresentation = this.parseRepresentation_(context,
|
|
1299
1339
|
contentProtection, kind, language, label, main, roleValues,
|
|
1300
|
-
closedCaptions, representation, accessibilityPurpose
|
|
1340
|
+
closedCaptions, representation, accessibilityPurpose,
|
|
1341
|
+
lastSegmentNumber);
|
|
1301
1342
|
if (parsedRepresentation) {
|
|
1302
1343
|
parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
|
|
1303
1344
|
parsedRepresentation.fastSwitching = isFastSwitching;
|
|
@@ -1353,7 +1394,7 @@ shaka.dash.DashParser = class {
|
|
|
1353
1394
|
}
|
|
1354
1395
|
|
|
1355
1396
|
const repIds = representations
|
|
1356
|
-
.map((node) => { return node.
|
|
1397
|
+
.map((node) => { return node.attributes['id']; })
|
|
1357
1398
|
.filter(shaka.util.Functional.isNotNull);
|
|
1358
1399
|
|
|
1359
1400
|
return {
|
|
@@ -1379,17 +1420,19 @@ shaka.dash.DashParser = class {
|
|
|
1379
1420
|
* @param {boolean} isPrimary
|
|
1380
1421
|
* @param {!Array.<string>} roles
|
|
1381
1422
|
* @param {Map.<string, string>} closedCaptions
|
|
1382
|
-
* @param {!
|
|
1423
|
+
* @param {!shaka.extern.xml.Node} node
|
|
1383
1424
|
* @param {?shaka.media.ManifestParser.AccessibilityPurpose}
|
|
1384
1425
|
* accessibilityPurpose
|
|
1426
|
+
* @param {?number} lastSegmentNumber
|
|
1385
1427
|
*
|
|
1386
1428
|
* @return {?shaka.extern.Stream} The Stream, or null when there is a
|
|
1387
1429
|
* non-critical parsing error.
|
|
1388
1430
|
* @private
|
|
1389
1431
|
*/
|
|
1390
1432
|
parseRepresentation_(context, contentProtection, kind, language, label,
|
|
1391
|
-
isPrimary, roles, closedCaptions, node, accessibilityPurpose
|
|
1392
|
-
|
|
1433
|
+
isPrimary, roles, closedCaptions, node, accessibilityPurpose,
|
|
1434
|
+
lastSegmentNumber) {
|
|
1435
|
+
const TXml = shaka.util.TXml;
|
|
1393
1436
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1394
1437
|
|
|
1395
1438
|
context.representation =
|
|
@@ -1411,7 +1454,7 @@ shaka.dash.DashParser = class {
|
|
|
1411
1454
|
// To avoid NaN at the variant level on broken content, fall back to zero.
|
|
1412
1455
|
// https://github.com/shaka-project/shaka-player/issues/938#issuecomment-317278180
|
|
1413
1456
|
context.bandwidth =
|
|
1414
|
-
|
|
1457
|
+
TXml.parseAttr(node, 'bandwidth', TXml.parsePositiveInt) || 0;
|
|
1415
1458
|
|
|
1416
1459
|
/** @type {?shaka.dash.DashParser.StreamInfo} */
|
|
1417
1460
|
let streamInfo;
|
|
@@ -1422,8 +1465,8 @@ shaka.dash.DashParser = class {
|
|
|
1422
1465
|
const isImage = contentType == ContentType.IMAGE;
|
|
1423
1466
|
|
|
1424
1467
|
try {
|
|
1425
|
-
/** @type {shaka.extern.
|
|
1426
|
-
let
|
|
1468
|
+
/** @type {shaka.extern.aesKey|undefined} */
|
|
1469
|
+
let aesKey = undefined;
|
|
1427
1470
|
if (contentProtection.aes128Info) {
|
|
1428
1471
|
const getBaseUris = context.representation.getBaseUris;
|
|
1429
1472
|
const uris = shaka.util.ManifestParserUtils.resolveUris(
|
|
@@ -1432,15 +1475,16 @@ shaka.dash.DashParser = class {
|
|
|
1432
1475
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
1433
1476
|
uris, this.config_.retryParameters);
|
|
1434
1477
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1478
|
+
aesKey = {
|
|
1479
|
+
bitsKey: 128,
|
|
1480
|
+
blockCipherMode: 'CBC',
|
|
1437
1481
|
iv: contentProtection.aes128Info.iv,
|
|
1438
1482
|
firstMediaSequenceNumber: 0,
|
|
1439
1483
|
};
|
|
1440
1484
|
|
|
1441
1485
|
// Don't download the key object until the segment is parsed, to
|
|
1442
1486
|
// avoid a startup delay for long manifests with lots of keys.
|
|
1443
|
-
|
|
1487
|
+
aesKey.fetchKey = async () => {
|
|
1444
1488
|
const keyResponse =
|
|
1445
1489
|
await this.makeNetworkRequest_(request, requestType);
|
|
1446
1490
|
|
|
@@ -1456,9 +1500,9 @@ shaka.dash.DashParser = class {
|
|
|
1456
1500
|
const algorithm = {
|
|
1457
1501
|
name: 'AES-CBC',
|
|
1458
1502
|
};
|
|
1459
|
-
|
|
1503
|
+
aesKey.cryptoKey = await window.crypto.subtle.importKey(
|
|
1460
1504
|
'raw', keyResponse.data, algorithm, true, ['decrypt']);
|
|
1461
|
-
|
|
1505
|
+
aesKey.fetchKey = undefined; // No longer needed.
|
|
1462
1506
|
};
|
|
1463
1507
|
}
|
|
1464
1508
|
const requestSegment = (uris, startByte, endByte, isInit) => {
|
|
@@ -1466,17 +1510,17 @@ shaka.dash.DashParser = class {
|
|
|
1466
1510
|
};
|
|
1467
1511
|
if (context.representation.segmentBase) {
|
|
1468
1512
|
streamInfo = shaka.dash.SegmentBase.createStreamInfo(
|
|
1469
|
-
context, requestSegment,
|
|
1513
|
+
context, requestSegment, aesKey);
|
|
1470
1514
|
} else if (context.representation.segmentList) {
|
|
1471
1515
|
streamInfo = shaka.dash.SegmentList.createStreamInfo(
|
|
1472
|
-
context, this.streamMap_,
|
|
1516
|
+
context, this.streamMap_, aesKey);
|
|
1473
1517
|
} else if (context.representation.segmentTemplate) {
|
|
1474
1518
|
const hasManifest = !!this.manifest_;
|
|
1475
1519
|
|
|
1476
1520
|
streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
|
|
1477
1521
|
context, requestSegment, this.streamMap_, hasManifest,
|
|
1478
1522
|
this.config_.dash.initialSegmentLimit, this.periodDurations_,
|
|
1479
|
-
|
|
1523
|
+
aesKey, lastSegmentNumber);
|
|
1480
1524
|
} else {
|
|
1481
1525
|
goog.asserts.assert(isText,
|
|
1482
1526
|
'Must have Segment* with non-text streams.');
|
|
@@ -1508,7 +1552,7 @@ shaka.dash.DashParser = class {
|
|
|
1508
1552
|
}
|
|
1509
1553
|
|
|
1510
1554
|
const contentProtectionElems =
|
|
1511
|
-
|
|
1555
|
+
TXml.findChildren(node, 'ContentProtection');
|
|
1512
1556
|
const keyId = shaka.dash.ContentProtection.parseFromRepresentation(
|
|
1513
1557
|
contentProtectionElems, contentProtection,
|
|
1514
1558
|
this.config_.dash.ignoreDrmInfo,
|
|
@@ -1518,12 +1562,12 @@ shaka.dash.DashParser = class {
|
|
|
1518
1562
|
// Detect the presence of E-AC3 JOC audio content, using DD+JOC signaling.
|
|
1519
1563
|
// See: ETSI TS 103 420 V1.2.1 (2018-10)
|
|
1520
1564
|
const supplementalPropertyElems =
|
|
1521
|
-
|
|
1565
|
+
TXml.findChildren(node, 'SupplementalProperty');
|
|
1522
1566
|
const hasJoc = supplementalPropertyElems.some((element) => {
|
|
1523
1567
|
const expectedUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
|
|
1524
1568
|
const expectedValue = 'JOC';
|
|
1525
|
-
return element.
|
|
1526
|
-
element.
|
|
1569
|
+
return element.attributes['schemeIdUri'] == expectedUri &&
|
|
1570
|
+
element.attributes['value'] == expectedValue;
|
|
1527
1571
|
});
|
|
1528
1572
|
let spatialAudio = false;
|
|
1529
1573
|
if (hasJoc) {
|
|
@@ -1541,16 +1585,16 @@ shaka.dash.DashParser = class {
|
|
|
1541
1585
|
let tilesLayout;
|
|
1542
1586
|
if (isImage) {
|
|
1543
1587
|
const essentialPropertyElems =
|
|
1544
|
-
|
|
1588
|
+
TXml.findChildren(node, 'EssentialProperty');
|
|
1545
1589
|
const thumbnailTileElem = essentialPropertyElems.find((element) => {
|
|
1546
1590
|
const expectedUris = [
|
|
1547
1591
|
'http://dashif.org/thumbnail_tile',
|
|
1548
1592
|
'http://dashif.org/guidelines/thumbnail_tile',
|
|
1549
1593
|
];
|
|
1550
|
-
return expectedUris.includes(element.
|
|
1594
|
+
return expectedUris.includes(element.attributes['schemeIdUri']);
|
|
1551
1595
|
});
|
|
1552
1596
|
if (thumbnailTileElem) {
|
|
1553
|
-
tilesLayout = thumbnailTileElem.
|
|
1597
|
+
tilesLayout = thumbnailTileElem.attributes['value'];
|
|
1554
1598
|
}
|
|
1555
1599
|
// Filter image adaptation sets that has no tilesLayout.
|
|
1556
1600
|
if (!tilesLayout) {
|
|
@@ -1682,6 +1726,15 @@ shaka.dash.DashParser = class {
|
|
|
1682
1726
|
this.setUpdateTimer_(updateDelay);
|
|
1683
1727
|
}
|
|
1684
1728
|
|
|
1729
|
+
/**
|
|
1730
|
+
* Update now the manifest
|
|
1731
|
+
*
|
|
1732
|
+
* @private
|
|
1733
|
+
*/
|
|
1734
|
+
updateNow_() {
|
|
1735
|
+
this.updateTimer_.tickNow();
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1685
1738
|
/**
|
|
1686
1739
|
* Sets the update timer. Does nothing if the manifest does not specify an
|
|
1687
1740
|
* update period.
|
|
@@ -1698,9 +1751,13 @@ shaka.dash.DashParser = class {
|
|
|
1698
1751
|
if (this.updatePeriod_ < 0) {
|
|
1699
1752
|
return;
|
|
1700
1753
|
}
|
|
1754
|
+
let updateTime = this.updatePeriod_;
|
|
1755
|
+
if (this.config_.dash.updatePeriod >= 0) {
|
|
1756
|
+
updateTime = this.config_.dash.updatePeriod;
|
|
1757
|
+
}
|
|
1701
1758
|
|
|
1702
1759
|
const finalDelay = Math.max(
|
|
1703
|
-
|
|
1760
|
+
updateTime - offset,
|
|
1704
1761
|
this.averageUpdateDuration_.getEstimate());
|
|
1705
1762
|
|
|
1706
1763
|
// We do not run the timer as repeating because part of update is async and
|
|
@@ -1711,7 +1768,7 @@ shaka.dash.DashParser = class {
|
|
|
1711
1768
|
/**
|
|
1712
1769
|
* Creates a new inheritance frame for the given element.
|
|
1713
1770
|
*
|
|
1714
|
-
* @param {!
|
|
1771
|
+
* @param {!shaka.extern.xml.Node} elem
|
|
1715
1772
|
* @param {?shaka.dash.DashParser.InheritanceFrame} parent
|
|
1716
1773
|
* @param {?function():!Array.<string>} getBaseUris
|
|
1717
1774
|
* @return {shaka.dash.DashParser.InheritanceFrame}
|
|
@@ -1721,7 +1778,7 @@ shaka.dash.DashParser = class {
|
|
|
1721
1778
|
goog.asserts.assert(parent || getBaseUris,
|
|
1722
1779
|
'Must provide either parent or getBaseUris');
|
|
1723
1780
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
1724
|
-
const
|
|
1781
|
+
const TXml = shaka.util.TXml;
|
|
1725
1782
|
parent = parent || /** @type {shaka.dash.DashParser.InheritanceFrame} */ ({
|
|
1726
1783
|
contentType: '',
|
|
1727
1784
|
mimeType: '',
|
|
@@ -1736,17 +1793,17 @@ shaka.dash.DashParser = class {
|
|
|
1736
1793
|
});
|
|
1737
1794
|
getBaseUris = getBaseUris || parent.getBaseUris;
|
|
1738
1795
|
|
|
1739
|
-
const parseNumber =
|
|
1740
|
-
const evalDivision =
|
|
1796
|
+
const parseNumber = TXml.parseNonNegativeInt;
|
|
1797
|
+
const evalDivision = TXml.evalDivision;
|
|
1741
1798
|
|
|
1742
|
-
const id = elem.
|
|
1743
|
-
const uriObjs =
|
|
1799
|
+
const id = elem.attributes['id'];
|
|
1800
|
+
const uriObjs = TXml.findChildren(elem, 'BaseURL');
|
|
1744
1801
|
let calculatedBaseUris;
|
|
1745
1802
|
let someLocationValid = false;
|
|
1746
1803
|
if (this.contentSteeringManager_) {
|
|
1747
1804
|
for (const uriObj of uriObjs) {
|
|
1748
|
-
const serviceLocation = uriObj.
|
|
1749
|
-
const uri =
|
|
1805
|
+
const serviceLocation = uriObj.attributes['serviceLocation'];
|
|
1806
|
+
const uri = TXml.getContents(uriObj);
|
|
1750
1807
|
if (serviceLocation && uri) {
|
|
1751
1808
|
this.contentSteeringManager_.addLocation(
|
|
1752
1809
|
id, serviceLocation, uri);
|
|
@@ -1755,7 +1812,7 @@ shaka.dash.DashParser = class {
|
|
|
1755
1812
|
}
|
|
1756
1813
|
}
|
|
1757
1814
|
if (!someLocationValid || !this.contentSteeringManager_) {
|
|
1758
|
-
calculatedBaseUris = uriObjs.map(
|
|
1815
|
+
calculatedBaseUris = uriObjs.map(TXml.getContents);
|
|
1759
1816
|
}
|
|
1760
1817
|
|
|
1761
1818
|
const getFrameUris = () => {
|
|
@@ -1771,55 +1828,55 @@ shaka.dash.DashParser = class {
|
|
|
1771
1828
|
return [];
|
|
1772
1829
|
};
|
|
1773
1830
|
|
|
1774
|
-
let contentType = elem.
|
|
1775
|
-
const mimeType = elem.
|
|
1776
|
-
const codecs = elem.
|
|
1831
|
+
let contentType = elem.attributes['contentType'] || parent.contentType;
|
|
1832
|
+
const mimeType = elem.attributes['mimeType'] || parent.mimeType;
|
|
1833
|
+
const codecs = elem.attributes['codecs'] || parent.codecs;
|
|
1777
1834
|
const frameRate =
|
|
1778
|
-
|
|
1835
|
+
TXml.parseAttr(elem, 'frameRate', evalDivision) || parent.frameRate;
|
|
1779
1836
|
const pixelAspectRatio =
|
|
1780
|
-
elem.
|
|
1837
|
+
elem.attributes['sar'] || parent.pixelAspectRatio;
|
|
1781
1838
|
const emsgSchemeIdUris = this.emsgSchemeIdUris_(
|
|
1782
|
-
|
|
1839
|
+
TXml.findChildren(elem, 'InbandEventStream'),
|
|
1783
1840
|
parent.emsgSchemeIdUris);
|
|
1784
1841
|
const audioChannelConfigs =
|
|
1785
|
-
|
|
1842
|
+
TXml.findChildren(elem, 'AudioChannelConfiguration');
|
|
1786
1843
|
const numChannels =
|
|
1787
1844
|
this.parseAudioChannels_(audioChannelConfigs) || parent.numChannels;
|
|
1788
1845
|
const audioSamplingRate =
|
|
1789
|
-
|
|
1846
|
+
TXml.parseAttr(elem, 'audioSamplingRate', parseNumber) ||
|
|
1790
1847
|
parent.audioSamplingRate;
|
|
1791
1848
|
|
|
1792
1849
|
if (!contentType) {
|
|
1793
1850
|
contentType = shaka.dash.DashParser.guessContentType_(mimeType, codecs);
|
|
1794
1851
|
}
|
|
1795
1852
|
|
|
1796
|
-
const segmentBase =
|
|
1797
|
-
const segmentTemplate =
|
|
1853
|
+
const segmentBase = TXml.findChild(elem, 'SegmentBase');
|
|
1854
|
+
const segmentTemplate = TXml.findChild(elem, 'SegmentTemplate');
|
|
1798
1855
|
|
|
1799
1856
|
// The availabilityTimeOffset is the sum of all @availabilityTimeOffset
|
|
1800
1857
|
// values that apply to the adaptation set, via BaseURL, SegmentBase,
|
|
1801
1858
|
// or SegmentTemplate elements.
|
|
1802
1859
|
const segmentBaseAto = segmentBase ?
|
|
1803
|
-
(
|
|
1804
|
-
|
|
1860
|
+
(TXml.parseAttr(segmentBase, 'availabilityTimeOffset',
|
|
1861
|
+
TXml.parseFloat) || 0) : 0;
|
|
1805
1862
|
const segmentTemplateAto = segmentTemplate ?
|
|
1806
|
-
(
|
|
1807
|
-
|
|
1863
|
+
(TXml.parseAttr(segmentTemplate, 'availabilityTimeOffset',
|
|
1864
|
+
TXml.parseFloat) || 0) : 0;
|
|
1808
1865
|
const baseUriAto = uriObjs && uriObjs.length ?
|
|
1809
|
-
(
|
|
1810
|
-
|
|
1866
|
+
(TXml.parseAttr(uriObjs[0], 'availabilityTimeOffset',
|
|
1867
|
+
TXml.parseFloat) || 0) : 0;
|
|
1811
1868
|
|
|
1812
1869
|
const availabilityTimeOffset = parent.availabilityTimeOffset + baseUriAto +
|
|
1813
1870
|
segmentBaseAto + segmentTemplateAto;
|
|
1814
1871
|
|
|
1815
1872
|
let segmentSequenceCadence = null;
|
|
1816
1873
|
const segmentSequenceProperties =
|
|
1817
|
-
|
|
1874
|
+
TXml.findChild(elem, 'SegmentSequenceProperties');
|
|
1818
1875
|
if (segmentSequenceProperties) {
|
|
1819
|
-
const sap =
|
|
1876
|
+
const sap = TXml.findChild(segmentSequenceProperties, 'SAP');
|
|
1820
1877
|
if (sap) {
|
|
1821
|
-
segmentSequenceCadence =
|
|
1822
|
-
|
|
1878
|
+
segmentSequenceCadence = TXml.parseAttr(sap, 'cadence',
|
|
1879
|
+
TXml.parseInt);
|
|
1823
1880
|
}
|
|
1824
1881
|
}
|
|
1825
1882
|
|
|
@@ -1828,10 +1885,10 @@ shaka.dash.DashParser = class {
|
|
|
1828
1885
|
() => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
|
|
1829
1886
|
segmentBase: segmentBase || parent.segmentBase,
|
|
1830
1887
|
segmentList:
|
|
1831
|
-
|
|
1888
|
+
TXml.findChild(elem, 'SegmentList') || parent.segmentList,
|
|
1832
1889
|
segmentTemplate: segmentTemplate || parent.segmentTemplate,
|
|
1833
|
-
width:
|
|
1834
|
-
height:
|
|
1890
|
+
width: TXml.parseAttr(elem, 'width', parseNumber) || parent.width,
|
|
1891
|
+
height: TXml.parseAttr(elem, 'height', parseNumber) || parent.height,
|
|
1835
1892
|
contentType: contentType,
|
|
1836
1893
|
mimeType: mimeType,
|
|
1837
1894
|
codecs: codecs,
|
|
@@ -1839,7 +1896,7 @@ shaka.dash.DashParser = class {
|
|
|
1839
1896
|
pixelAspectRatio: pixelAspectRatio,
|
|
1840
1897
|
emsgSchemeIdUris: emsgSchemeIdUris,
|
|
1841
1898
|
id: id,
|
|
1842
|
-
language: elem.
|
|
1899
|
+
language: elem.attributes['lang'],
|
|
1843
1900
|
numChannels: numChannels,
|
|
1844
1901
|
audioSamplingRate: audioSamplingRate,
|
|
1845
1902
|
availabilityTimeOffset: availabilityTimeOffset,
|
|
@@ -1853,7 +1910,8 @@ shaka.dash.DashParser = class {
|
|
|
1853
1910
|
* of the ones parsed from inBandEventStreams and the ones provided in
|
|
1854
1911
|
* emsgSchemeIdUris.
|
|
1855
1912
|
*
|
|
1856
|
-
* @param {!Array.<!
|
|
1913
|
+
* @param {!Array.<!shaka.extern.xml.Node>} inBandEventStreams
|
|
1914
|
+
* Array of InbandEventStream
|
|
1857
1915
|
* elements to parse and add to the returned array.
|
|
1858
1916
|
* @param {!Array.<string>} emsgSchemeIdUris Array of parsed
|
|
1859
1917
|
* InbandEventStream schemeIdUri attributes to add to the returned array.
|
|
@@ -1864,7 +1922,7 @@ shaka.dash.DashParser = class {
|
|
|
1864
1922
|
emsgSchemeIdUris_(inBandEventStreams, emsgSchemeIdUris) {
|
|
1865
1923
|
const schemeIdUris = emsgSchemeIdUris.slice();
|
|
1866
1924
|
for (const event of inBandEventStreams) {
|
|
1867
|
-
const schemeIdUri = event.
|
|
1925
|
+
const schemeIdUri = event.attributes['schemeIdUri'];
|
|
1868
1926
|
if (!schemeIdUris.includes(schemeIdUri)) {
|
|
1869
1927
|
schemeIdUris.push(schemeIdUri);
|
|
1870
1928
|
}
|
|
@@ -1873,19 +1931,19 @@ shaka.dash.DashParser = class {
|
|
|
1873
1931
|
}
|
|
1874
1932
|
|
|
1875
1933
|
/**
|
|
1876
|
-
* @param {!Array.<!
|
|
1934
|
+
* @param {!Array.<!shaka.extern.xml.Node>} audioChannelConfigs An array of
|
|
1877
1935
|
* AudioChannelConfiguration elements.
|
|
1878
1936
|
* @return {?number} The number of audio channels, or null if unknown.
|
|
1879
1937
|
* @private
|
|
1880
1938
|
*/
|
|
1881
1939
|
parseAudioChannels_(audioChannelConfigs) {
|
|
1882
1940
|
for (const elem of audioChannelConfigs) {
|
|
1883
|
-
const scheme = elem.
|
|
1941
|
+
const scheme = elem.attributes['schemeIdUri'];
|
|
1884
1942
|
if (!scheme) {
|
|
1885
1943
|
continue;
|
|
1886
1944
|
}
|
|
1887
1945
|
|
|
1888
|
-
const value = elem.
|
|
1946
|
+
const value = elem.attributes['value'];
|
|
1889
1947
|
if (!value) {
|
|
1890
1948
|
continue;
|
|
1891
1949
|
}
|
|
@@ -2021,8 +2079,9 @@ shaka.dash.DashParser = class {
|
|
|
2021
2079
|
* @private
|
|
2022
2080
|
*/
|
|
2023
2081
|
async requestForTiming_(getBaseUris, uri, method) {
|
|
2082
|
+
const uris = [shaka.util.StringUtils.htmlUnescape(uri)];
|
|
2024
2083
|
const requestUris =
|
|
2025
|
-
shaka.util.ManifestParserUtils.resolveUris(getBaseUris(),
|
|
2084
|
+
shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), uris);
|
|
2026
2085
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
2027
2086
|
requestUris, this.config_.retryParameters);
|
|
2028
2087
|
request.method = method;
|
|
@@ -2056,15 +2115,15 @@ shaka.dash.DashParser = class {
|
|
|
2056
2115
|
* Parses an array of UTCTiming elements.
|
|
2057
2116
|
*
|
|
2058
2117
|
* @param {function():!Array.<string>} getBaseUris
|
|
2059
|
-
* @param {!Array.<!
|
|
2118
|
+
* @param {!Array.<!shaka.extern.xml.Node>} elems
|
|
2060
2119
|
* @return {!Promise.<number>}
|
|
2061
2120
|
* @private
|
|
2062
2121
|
*/
|
|
2063
2122
|
async parseUtcTiming_(getBaseUris, elems) {
|
|
2064
2123
|
const schemesAndValues = elems.map((elem) => {
|
|
2065
2124
|
return {
|
|
2066
|
-
scheme: elem.
|
|
2067
|
-
value: elem.
|
|
2125
|
+
scheme: elem.attributes['schemeIdUri'],
|
|
2126
|
+
value: elem.attributes['value'],
|
|
2068
2127
|
};
|
|
2069
2128
|
});
|
|
2070
2129
|
|
|
@@ -2128,23 +2187,23 @@ shaka.dash.DashParser = class {
|
|
|
2128
2187
|
*
|
|
2129
2188
|
* @param {number} periodStart
|
|
2130
2189
|
* @param {?number} periodDuration
|
|
2131
|
-
* @param {!
|
|
2190
|
+
* @param {!shaka.extern.xml.Node} elem
|
|
2132
2191
|
* @param {number} availabilityStart
|
|
2133
2192
|
* @private
|
|
2134
2193
|
*/
|
|
2135
2194
|
parseEventStream_(periodStart, periodDuration, elem, availabilityStart) {
|
|
2136
|
-
const
|
|
2137
|
-
const parseNumber =
|
|
2195
|
+
const TXml = shaka.util.TXml;
|
|
2196
|
+
const parseNumber = shaka.util.TXml.parseNonNegativeInt;
|
|
2138
2197
|
|
|
2139
|
-
const schemeIdUri = elem.
|
|
2140
|
-
const value = elem.
|
|
2141
|
-
const timescale =
|
|
2198
|
+
const schemeIdUri = elem.attributes['schemeIdUri'] || '';
|
|
2199
|
+
const value = elem.attributes['value'] || '';
|
|
2200
|
+
const timescale = TXml.parseAttr(elem, 'timescale', parseNumber) || 1;
|
|
2142
2201
|
|
|
2143
|
-
for (const eventNode of
|
|
2202
|
+
for (const eventNode of TXml.findChildren(elem, 'Event')) {
|
|
2144
2203
|
const presentationTime =
|
|
2145
|
-
|
|
2204
|
+
TXml.parseAttr(eventNode, 'presentationTime', parseNumber) || 0;
|
|
2146
2205
|
const duration =
|
|
2147
|
-
|
|
2206
|
+
TXml.parseAttr(eventNode, 'duration', parseNumber) || 0;
|
|
2148
2207
|
|
|
2149
2208
|
let startTime = presentationTime / timescale + periodStart;
|
|
2150
2209
|
let endTime = startTime + (duration / timescale);
|
|
@@ -2166,8 +2225,9 @@ shaka.dash.DashParser = class {
|
|
|
2166
2225
|
value: value,
|
|
2167
2226
|
startTime: startTime,
|
|
2168
2227
|
endTime: endTime,
|
|
2169
|
-
id: eventNode.
|
|
2170
|
-
eventElement: eventNode,
|
|
2228
|
+
id: eventNode.attributes['id'] || '',
|
|
2229
|
+
eventElement: TXml.txmlNodeToDomElement(eventNode),
|
|
2230
|
+
eventNode: eventNode,
|
|
2171
2231
|
};
|
|
2172
2232
|
|
|
2173
2233
|
this.playerInterface_.onTimelineRegionAdded(region);
|
|
@@ -2255,9 +2315,9 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2255
2315
|
|
|
2256
2316
|
/**
|
|
2257
2317
|
* @typedef {{
|
|
2258
|
-
* segmentBase:
|
|
2259
|
-
* segmentList:
|
|
2260
|
-
* segmentTemplate:
|
|
2318
|
+
* segmentBase: ?shaka.extern.xml.Node,
|
|
2319
|
+
* segmentList: ?shaka.extern.xml.Node,
|
|
2320
|
+
* segmentTemplate: ?shaka.extern.xml.Node,
|
|
2261
2321
|
* getBaseUris: function():!Array.<string>,
|
|
2262
2322
|
* width: (number|undefined),
|
|
2263
2323
|
* height: (number|undefined),
|
|
@@ -2279,11 +2339,11 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2279
2339
|
* A collection of elements and properties which are inherited across levels
|
|
2280
2340
|
* of a DASH manifest.
|
|
2281
2341
|
*
|
|
2282
|
-
* @property {
|
|
2342
|
+
* @property {?shaka.extern.xml.Node} segmentBase
|
|
2283
2343
|
* The XML node for SegmentBase.
|
|
2284
|
-
* @property {
|
|
2344
|
+
* @property {?shaka.extern.xml.Node} segmentList
|
|
2285
2345
|
* The XML node for SegmentList.
|
|
2286
|
-
* @property {
|
|
2346
|
+
* @property {?shaka.extern.xml.Node} segmentTemplate
|
|
2287
2347
|
* The XML node for SegmentTemplate.
|
|
2288
2348
|
* @property {function():!Array.<string>} getBaseUris
|
|
2289
2349
|
* Function than returns an array of absolute base URIs for the frame.
|
|
@@ -2368,7 +2428,7 @@ shaka.dash.DashParser.Context;
|
|
|
2368
2428
|
* @typedef {{
|
|
2369
2429
|
* start: number,
|
|
2370
2430
|
* duration: ?number,
|
|
2371
|
-
* node: !
|
|
2431
|
+
* node: !shaka.extern.xml.Node,
|
|
2372
2432
|
* isLastPeriod: boolean
|
|
2373
2433
|
* }}
|
|
2374
2434
|
*
|
|
@@ -2380,7 +2440,7 @@ shaka.dash.DashParser.Context;
|
|
|
2380
2440
|
* @property {?number} duration
|
|
2381
2441
|
* The duration of the period; or null if the duration is not given. This
|
|
2382
2442
|
* will be non-null for all periods except the last.
|
|
2383
|
-
* @property {!
|
|
2443
|
+
* @property {!shaka.extern.xml.Node} node
|
|
2384
2444
|
* The XML Node for the Period.
|
|
2385
2445
|
* @property {boolean} isLastPeriod
|
|
2386
2446
|
* Whether this Period is the last one in the manifest.
|