shaka-player 4.5.0 → 4.6.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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/AUTHORS +2 -0
- package/CHANGELOG.md +98 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +2 -0
- package/README.md +38 -32
- package/demo/common/asset.js +11 -17
- package/demo/common/assets.js +117 -11
- package/demo/config.js +28 -1
- package/demo/custom.js +52 -38
- package/demo/main.js +11 -3
- package/demo/search.js +7 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +133 -124
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +133 -124
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +20 -18
- package/dist/locales.js +35 -31
- package/dist/receiver.compiled.debug.js +58 -50
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +58 -50
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.externs.js +162 -77
- package/dist/shaka-player.compiled.debug.js +786 -736
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +162 -77
- package/dist/shaka-player.compiled.js +1154 -1121
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +491 -437
- package/dist/shaka-player.ui.debug.d.ts +491 -437
- package/dist/shaka-player.ui.debug.externs.js +167 -77
- package/dist/shaka-player.ui.debug.js +877 -811
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +167 -77
- package/dist/shaka-player.ui.js +1362 -1318
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/basic-usage.md +2 -1
- package/docs/tutorials/errors.md +2 -1
- package/docs/tutorials/offline.md +10 -8
- package/docs/tutorials/plugins.md +5 -0
- package/docs/tutorials/ui-customization.md +13 -0
- package/docs/tutorials/ui.md +2 -1
- package/docs/tutorials/upgrade.md +3 -0
- package/externs/managedmediasource.js +24 -0
- package/externs/shaka/abr_manager.js +6 -2
- package/externs/shaka/cea.js +7 -0
- package/externs/shaka/codecs.js +5 -3
- package/externs/shaka/manifest.js +21 -5
- package/externs/shaka/manifest_parser.js +17 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/offline.js +9 -2
- package/externs/shaka/player.js +53 -7
- package/externs/shaka/text.js +3 -1
- package/externs/shaka/transmuxer.js +1 -1
- package/lib/abr/simple_abr_manager.js +25 -8
- package/lib/ads/media_tailor_ad_manager.js +14 -3
- package/lib/cast/cast_receiver.js +1 -1
- package/lib/cea/cea_decoder.js +28 -0
- package/lib/cea/dummy_caption_decoder.js +5 -0
- package/lib/dash/dash_parser.js +249 -83
- package/lib/dash/mpd_utils.js +52 -31
- package/lib/dash/segment_base.js +4 -5
- package/lib/dash/segment_list.js +5 -4
- package/lib/dash/segment_template.js +83 -26
- package/lib/hls/hls_classes.js +28 -43
- package/lib/hls/hls_parser.js +761 -649
- package/lib/hls/hls_utils.js +64 -5
- package/lib/hls/manifest_text_parser.js +7 -11
- package/lib/media/adaptation_set.js +20 -13
- package/lib/media/adaptation_set_criteria.js +70 -18
- package/lib/media/closed_caption_parser.js +13 -0
- package/lib/media/content_workarounds.js +6 -0
- package/lib/media/drm_engine.js +8 -13
- package/lib/media/manifest_parser.js +8 -113
- package/lib/media/media_source_capabilities.js +10 -3
- package/lib/media/media_source_engine.js +75 -21
- package/lib/media/play_rate_controller.js +8 -38
- package/lib/media/presentation_timeline.js +7 -1
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_index.js +5 -5
- package/lib/media/segment_prefetch.js +92 -24
- package/lib/media/segment_reference.js +67 -1
- package/lib/media/segment_utils.js +343 -0
- package/lib/media/stall_detector.js +0 -3
- package/lib/media/streaming_engine.js +41 -21
- package/lib/mss/mss_parser.js +18 -2
- package/lib/net/networking_engine.js +66 -12
- package/lib/net/networking_utils.js +95 -0
- 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 +2 -0
- package/lib/offline/offline_manifest_parser.js +10 -0
- package/lib/offline/storage.js +11 -7
- package/lib/player.js +993 -1502
- package/lib/polyfill/mediasource.js +22 -1
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/stub_text_displayer.js +51 -0
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +28 -5
- package/lib/text/ui_text_displayer.js +4 -0
- package/lib/text/vtt_text_parser.js +7 -2
- package/lib/transmuxer/aac_transmuxer.js +1 -1
- package/lib/transmuxer/ac3_transmuxer.js +1 -1
- package/lib/transmuxer/ec3_transmuxer.js +1 -1
- package/lib/transmuxer/h264.js +5 -4
- package/lib/transmuxer/h265.js +5 -4
- package/lib/transmuxer/mp3_transmuxer.js +1 -1
- package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
- package/lib/transmuxer/mss_transmuxer.js +1 -1
- package/lib/transmuxer/ts_transmuxer.js +33 -13
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/content_steering_manager.js +358 -0
- package/lib/util/error.js +2 -6
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/fake_event.js +5 -1
- package/lib/util/id3_utils.js +50 -1
- package/lib/util/manifest_parser_utils.js +12 -8
- package/lib/util/mp4_generator.js +2 -4
- package/lib/util/mutex.js +59 -0
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +154 -179
- package/lib/util/platform.js +4 -3
- package/lib/util/player_configuration.js +35 -6
- package/lib/util/stream_utils.js +105 -98
- package/lib/util/ts_parser.js +124 -73
- package/lib/util/uint8array_utils.js +5 -1
- package/lib/util/xml_utils.js +5 -0
- package/maintained-branches.md +2 -2
- package/package.json +1 -1
- package/roadmap.md +15 -5
- package/shaka-player.uncompiled.js +0 -1
- package/test/abr/simple_abr_manager_unit.js +33 -33
- package/test/ads/ad_manager_unit.js +3 -2
- package/test/cast/cast_receiver_integration.js +3 -4
- package/test/cast/cast_utils_unit.js +2 -0
- package/test/cea/cea_decoder_unit.js +7 -0
- package/test/codec_switching/codec_switching_integration.js +2 -1
- package/test/dash/dash_parser_integration.js +2 -1
- package/test/dash/dash_parser_live_unit.js +0 -19
- package/test/dash/dash_parser_manifest_unit.js +494 -2
- package/test/dash/mpd_utils_unit.js +61 -30
- package/test/hls/hls_live_unit.js +12 -3
- package/test/hls/hls_parser_unit.js +377 -27
- package/test/hls/manifest_text_parser_unit.js +32 -99
- package/test/media/adaptation_set_criteria_unit.js +172 -21
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/play_rate_controller_unit.js +0 -48
- package/test/media/segment_index_unit.js +2 -1
- package/test/mss/mss_player_integration.js +3 -2
- package/test/net/networking_engine_unit.js +3 -3
- package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
- package/test/offline/manifest_convert_unit.js +9 -0
- package/test/offline/offline_integration.js +2 -1
- package/test/offline/storage_integration.js +21 -9
- package/test/player_external.js +4 -2
- package/test/player_integration.js +15 -4
- package/test/player_load_graph_integration.js +47 -151
- package/test/player_src_equals_external.js +1 -0
- package/test/player_src_equals_integration.js +71 -4
- package/test/player_unit.js +80 -27
- package/test/test/util/fake_media_source_engine.js +5 -0
- package/test/test/util/fake_networking_engine.js +9 -1
- package/test/test/util/fake_segment_prefetch.js +3 -0
- package/test/test/util/manifest_generator.js +4 -0
- package/test/test/util/manifest_parser_util.js +14 -2
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/streaming_engine_util.js +3 -0
- package/test/test/util/test_scheme.js +6 -0
- package/test/test/util/ui_utils.js +4 -3
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_displayer_layout_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +33 -2
- package/test/transmuxer/transmuxer_engine_integration.js +9 -0
- package/test/transmuxer/transmuxer_integration.js +33 -3
- package/test/ui/ad_ui_unit.js +2 -2
- package/test/ui/ui_customization_unit.js +17 -16
- package/test/ui/ui_integration.js +30 -20
- package/test/ui/ui_unit.js +25 -19
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/content_steering_manager_unit.js +427 -0
- package/test/util/id3_utils_unit.js +52 -0
- package/test/util/stream_utils_unit.js +17 -113
- package/test/util/ts_parser_unit.js +3 -3
- package/third_party/closure-uri/uri.js +13 -1
- package/ui/audio_language_selection.js +9 -2
- package/ui/externs/ui.js +17 -2
- package/ui/language_utils.js +43 -8
- package/ui/less/range_elements.less +33 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/es-419.json +1 -0
- package/ui/locales/es.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +10 -0
- package/ui/remote_button.js +3 -1
- package/ui/resolution_selection.js +47 -10
- package/ui/seek_bar.js +296 -3
- package/ui/text_selection.js +2 -1
- package/ui/ui.js +6 -2
- package/lib/routing/node.js +0 -23
- package/lib/routing/payload.js +0 -47
- package/lib/routing/walker.js +0 -587
- package/test/routing/walker_unit.js +0 -505
package/lib/dash/dash_parser.js
CHANGED
|
@@ -19,6 +19,7 @@ goog.require('shaka.media.PresentationTimeline');
|
|
|
19
19
|
goog.require('shaka.media.SegmentIndex');
|
|
20
20
|
goog.require('shaka.net.NetworkingEngine');
|
|
21
21
|
goog.require('shaka.text.TextEngine');
|
|
22
|
+
goog.require('shaka.util.ContentSteeringManager');
|
|
22
23
|
goog.require('shaka.util.Error');
|
|
23
24
|
goog.require('shaka.util.Functional');
|
|
24
25
|
goog.require('shaka.util.LanguageUtils');
|
|
@@ -115,6 +116,9 @@ shaka.dash.DashParser = class {
|
|
|
115
116
|
|
|
116
117
|
/** @private {boolean} */
|
|
117
118
|
this.lowLatencyMode_ = false;
|
|
119
|
+
|
|
120
|
+
/** @private {?shaka.util.ContentSteeringManager} */
|
|
121
|
+
this.contentSteeringManager_ = null;
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
/**
|
|
@@ -126,6 +130,10 @@ shaka.dash.DashParser = class {
|
|
|
126
130
|
'DashManifestConfiguration should not be null!');
|
|
127
131
|
|
|
128
132
|
this.config_ = config;
|
|
133
|
+
|
|
134
|
+
if (this.contentSteeringManager_) {
|
|
135
|
+
this.contentSteeringManager_.configure(this.config_);
|
|
136
|
+
}
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
/**
|
|
@@ -185,6 +193,10 @@ shaka.dash.DashParser = class {
|
|
|
185
193
|
this.updateTimer_ = null;
|
|
186
194
|
}
|
|
187
195
|
|
|
196
|
+
if (this.contentSteeringManager_) {
|
|
197
|
+
this.contentSteeringManager_.destroy();
|
|
198
|
+
}
|
|
199
|
+
|
|
188
200
|
return this.operationManager_.destroy();
|
|
189
201
|
}
|
|
190
202
|
|
|
@@ -212,6 +224,43 @@ shaka.dash.DashParser = class {
|
|
|
212
224
|
// No-op
|
|
213
225
|
}
|
|
214
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @override
|
|
229
|
+
* @exportInterface
|
|
230
|
+
*/
|
|
231
|
+
onInitialVariantChosen(variant) {
|
|
232
|
+
// For live it is necessary that the first time we update the manifest with
|
|
233
|
+
// a shorter time than indicated to take into account that the last segment
|
|
234
|
+
// added could be halfway, for example
|
|
235
|
+
if (this.manifest_ && this.manifest_.presentationTimeline.isLive()) {
|
|
236
|
+
const stream = variant.video || variant.audio;
|
|
237
|
+
if (stream && stream.segmentIndex) {
|
|
238
|
+
const availabilityEnd =
|
|
239
|
+
this.manifest_.presentationTimeline.getSegmentAvailabilityEnd();
|
|
240
|
+
const position = stream.segmentIndex.find(availabilityEnd);
|
|
241
|
+
if (position == null) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const reference = stream.segmentIndex.get(position);
|
|
245
|
+
if (!reference) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.updatePeriod_ = reference.endTime - availabilityEnd;
|
|
249
|
+
this.setUpdateTimer_(/* offset= */ 0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @override
|
|
256
|
+
* @exportInterface
|
|
257
|
+
*/
|
|
258
|
+
banLocation(uri) {
|
|
259
|
+
if (this.contentSteeringManager_) {
|
|
260
|
+
this.contentSteeringManager_.banLocation(uri);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
215
264
|
/**
|
|
216
265
|
* Makes a network request for the manifest and parses the resulting data.
|
|
217
266
|
*
|
|
@@ -320,10 +369,68 @@ shaka.dash.DashParser = class {
|
|
|
320
369
|
manifestBaseUris = absoluteLocations;
|
|
321
370
|
}
|
|
322
371
|
|
|
372
|
+
let contentSteeringPromise = Promise.resolve();
|
|
373
|
+
|
|
374
|
+
const contentSteering = XmlUtils.findChild(mpd, 'ContentSteering');
|
|
375
|
+
if (contentSteering && this.playerInterface_) {
|
|
376
|
+
const hasPrevContentSteeringManager = !!this.contentSteeringManager_;
|
|
377
|
+
if (!this.contentSteeringManager_) {
|
|
378
|
+
this.contentSteeringManager_ =
|
|
379
|
+
new shaka.util.ContentSteeringManager(this.playerInterface_);
|
|
380
|
+
}
|
|
381
|
+
this.contentSteeringManager_.configure(this.config_);
|
|
382
|
+
this.contentSteeringManager_.setBaseUris(manifestBaseUris);
|
|
383
|
+
this.contentSteeringManager_.setManifestType(
|
|
384
|
+
shaka.media.ManifestParser.DASH);
|
|
385
|
+
const defaultPathwayId =
|
|
386
|
+
contentSteering.getAttribute('defaultServiceLocation');
|
|
387
|
+
this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
|
|
388
|
+
if (!hasPrevContentSteeringManager) {
|
|
389
|
+
const uri = XmlUtils.getContents(contentSteering);
|
|
390
|
+
if (uri) {
|
|
391
|
+
const queryBeforeStart =
|
|
392
|
+
XmlUtils.parseAttr(contentSteering, 'queryBeforeStart',
|
|
393
|
+
XmlUtils.parseBoolean, /* defaultValue= */ false);
|
|
394
|
+
if (queryBeforeStart) {
|
|
395
|
+
contentSteeringPromise =
|
|
396
|
+
this.contentSteeringManager_.requestInfo(uri);
|
|
397
|
+
} else {
|
|
398
|
+
this.contentSteeringManager_.requestInfo(uri);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
323
404
|
const uriObjs = XmlUtils.findChildren(mpd, 'BaseURL');
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
405
|
+
let calculatedBaseUris;
|
|
406
|
+
let someLocationValid = false;
|
|
407
|
+
if (this.contentSteeringManager_) {
|
|
408
|
+
this.contentSteeringManager_.clearPreviousLocations();
|
|
409
|
+
for (const uriObj of uriObjs) {
|
|
410
|
+
const serviceLocation = uriObj.getAttribute('serviceLocation');
|
|
411
|
+
const uri = XmlUtils.getContents(uriObj);
|
|
412
|
+
if (serviceLocation && uri) {
|
|
413
|
+
this.contentSteeringManager_.addLocation(
|
|
414
|
+
'BaseURL', serviceLocation, uri);
|
|
415
|
+
someLocationValid = true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!someLocationValid || !this.contentSteeringManager_) {
|
|
420
|
+
const uris = uriObjs.map(XmlUtils.getContents);
|
|
421
|
+
calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
|
|
422
|
+
manifestBaseUris, uris);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const getBaseUris = () => {
|
|
426
|
+
if (this.contentSteeringManager_ && someLocationValid) {
|
|
427
|
+
return this.contentSteeringManager_.getLocations('BaseURL');
|
|
428
|
+
}
|
|
429
|
+
if (calculatedBaseUris) {
|
|
430
|
+
return calculatedBaseUris;
|
|
431
|
+
}
|
|
432
|
+
return [];
|
|
433
|
+
};
|
|
327
434
|
|
|
328
435
|
let availabilityTimeOffset = 0;
|
|
329
436
|
if (uriObjs && uriObjs.length) {
|
|
@@ -439,7 +546,7 @@ shaka.dash.DashParser = class {
|
|
|
439
546
|
profiles: profiles.split(','),
|
|
440
547
|
};
|
|
441
548
|
|
|
442
|
-
const periodsAndDuration = this.parsePeriods_(context,
|
|
549
|
+
const periodsAndDuration = this.parsePeriods_(context, getBaseUris, mpd);
|
|
443
550
|
const duration = periodsAndDuration.duration;
|
|
444
551
|
const periods = periodsAndDuration.periods;
|
|
445
552
|
|
|
@@ -483,18 +590,14 @@ shaka.dash.DashParser = class {
|
|
|
483
590
|
|
|
484
591
|
await this.periodCombiner_.combinePeriods(periods, context.dynamic);
|
|
485
592
|
|
|
593
|
+
await contentSteeringPromise;
|
|
594
|
+
|
|
486
595
|
// Set minBufferTime to 0 for low-latency DASH live stream to achieve the
|
|
487
596
|
// best latency
|
|
488
597
|
if (this.lowLatencyMode_) {
|
|
489
598
|
minBufferTime = 0;
|
|
490
599
|
}
|
|
491
600
|
|
|
492
|
-
// Set updatePeriod_ to minTotalAvailabilityTimeOffset_ to achieve the best
|
|
493
|
-
// latency in LL streams
|
|
494
|
-
if (this.lowLatencyMode_ && this.minTotalAvailabilityTimeOffset_) {
|
|
495
|
-
this.updatePeriod_ = this.minTotalAvailabilityTimeOffset_;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
601
|
// These steps are not done on manifest update.
|
|
499
602
|
if (!this.manifest_) {
|
|
500
603
|
this.manifest_ = {
|
|
@@ -515,7 +618,7 @@ shaka.dash.DashParser = class {
|
|
|
515
618
|
if (presentationTimeline.usingPresentationStartTime()) {
|
|
516
619
|
const XmlUtils = shaka.util.XmlUtils;
|
|
517
620
|
const timingElements = XmlUtils.findChildren(mpd, 'UTCTiming');
|
|
518
|
-
const offset = await this.parseUtcTiming_(
|
|
621
|
+
const offset = await this.parseUtcTiming_(getBaseUris, timingElements);
|
|
519
622
|
// Detect calls to stop().
|
|
520
623
|
if (!this.playerInterface_) {
|
|
521
624
|
return;
|
|
@@ -575,8 +678,19 @@ shaka.dash.DashParser = class {
|
|
|
575
678
|
const maxPlaybackRate = playbackRateNode ?
|
|
576
679
|
parseFloat(playbackRateNode.getAttribute('max')) :
|
|
577
680
|
null;
|
|
681
|
+
const minLatency = latencyNode && latencyNode.getAttribute('min') ?
|
|
682
|
+
parseInt(latencyNode.getAttribute('min'), 10) / 1000 :
|
|
683
|
+
null;
|
|
684
|
+
const minPlaybackRate = playbackRateNode ?
|
|
685
|
+
parseFloat(playbackRateNode.getAttribute('min')) :
|
|
686
|
+
null;
|
|
578
687
|
|
|
579
|
-
return {
|
|
688
|
+
return {
|
|
689
|
+
maxLatency,
|
|
690
|
+
maxPlaybackRate,
|
|
691
|
+
minLatency,
|
|
692
|
+
minPlaybackRate,
|
|
693
|
+
};
|
|
580
694
|
}
|
|
581
695
|
|
|
582
696
|
return null;
|
|
@@ -588,7 +702,7 @@ shaka.dash.DashParser = class {
|
|
|
588
702
|
* children.
|
|
589
703
|
*
|
|
590
704
|
* @param {shaka.dash.DashParser.Context} context
|
|
591
|
-
* @param {
|
|
705
|
+
* @param {function():!Array.<string>} getBaseUris
|
|
592
706
|
* @param {!Element} mpd
|
|
593
707
|
* @return {{
|
|
594
708
|
* periods: !Array.<shaka.extern.Period>,
|
|
@@ -597,7 +711,7 @@ shaka.dash.DashParser = class {
|
|
|
597
711
|
* }}
|
|
598
712
|
* @private
|
|
599
713
|
*/
|
|
600
|
-
parsePeriods_(context,
|
|
714
|
+
parsePeriods_(context, getBaseUris, mpd) {
|
|
601
715
|
const XmlUtils = shaka.util.XmlUtils;
|
|
602
716
|
const presentationDuration = XmlUtils.parseAttr(
|
|
603
717
|
mpd, 'mediaPresentationDuration', XmlUtils.parseDuration);
|
|
@@ -687,7 +801,7 @@ shaka.dash.DashParser = class {
|
|
|
687
801
|
node: elem,
|
|
688
802
|
isLastPeriod: periodDuration == null || !next,
|
|
689
803
|
};
|
|
690
|
-
const period = this.parsePeriod_(context,
|
|
804
|
+
const period = this.parsePeriod_(context, getBaseUris, info);
|
|
691
805
|
periods.push(period);
|
|
692
806
|
|
|
693
807
|
if (context.period.id && periodDuration) {
|
|
@@ -741,17 +855,17 @@ shaka.dash.DashParser = class {
|
|
|
741
855
|
* was done before this was called so start and duration are valid.
|
|
742
856
|
*
|
|
743
857
|
* @param {shaka.dash.DashParser.Context} context
|
|
744
|
-
* @param {
|
|
858
|
+
* @param {function():!Array.<string>} getBaseUris
|
|
745
859
|
* @param {shaka.dash.DashParser.PeriodInfo} periodInfo
|
|
746
860
|
* @return {shaka.extern.Period}
|
|
747
861
|
* @private
|
|
748
862
|
*/
|
|
749
|
-
parsePeriod_(context,
|
|
863
|
+
parsePeriod_(context, getBaseUris, periodInfo) {
|
|
750
864
|
const Functional = shaka.util.Functional;
|
|
751
865
|
const XmlUtils = shaka.util.XmlUtils;
|
|
752
866
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
753
867
|
|
|
754
|
-
context.period = this.createFrame_(periodInfo.node, null,
|
|
868
|
+
context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
|
|
755
869
|
context.periodInfo = periodInfo;
|
|
756
870
|
context.period.availabilityTimeOffset = context.availabilityTimeOffset;
|
|
757
871
|
|
|
@@ -822,40 +936,29 @@ shaka.dash.DashParser = class {
|
|
|
822
936
|
}
|
|
823
937
|
}
|
|
824
938
|
|
|
825
|
-
const
|
|
826
|
-
this.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
this.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
939
|
+
const audioStreams = this.getStreamsFromSets_(
|
|
940
|
+
this.config_.disableAudio,
|
|
941
|
+
normalAdaptationSets,
|
|
942
|
+
ContentType.AUDIO);
|
|
943
|
+
const videoStreams = this.getStreamsFromSets_(
|
|
944
|
+
this.config_.disableVideo,
|
|
945
|
+
normalAdaptationSets,
|
|
946
|
+
ContentType.VIDEO);
|
|
947
|
+
const textStreams = this.getStreamsFromSets_(
|
|
948
|
+
this.config_.disableText,
|
|
949
|
+
normalAdaptationSets,
|
|
950
|
+
ContentType.TEXT);
|
|
951
|
+
const imageStreams = this.getStreamsFromSets_(
|
|
952
|
+
this.config_.disableThumbnails,
|
|
953
|
+
normalAdaptationSets,
|
|
954
|
+
ContentType.IMAGE);
|
|
955
|
+
|
|
956
|
+
if (videoStreams.length === 0 && audioStreams.length === 0) {
|
|
835
957
|
throw new shaka.util.Error(
|
|
836
958
|
shaka.util.Error.Severity.CRITICAL,
|
|
837
959
|
shaka.util.Error.Category.MANIFEST,
|
|
838
|
-
shaka.util.Error.Code.DASH_EMPTY_PERIOD
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const audioStreams = [];
|
|
842
|
-
for (const audioSet of audioSets) {
|
|
843
|
-
audioStreams.push(...audioSet.streams);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
const videoStreams = [];
|
|
847
|
-
for (const videoSet of videoSets) {
|
|
848
|
-
videoStreams.push(...videoSet.streams);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const textStreams = [];
|
|
852
|
-
for (const textSet of textSets) {
|
|
853
|
-
textStreams.push(...textSet.streams);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const imageStreams = [];
|
|
857
|
-
for (const imageSet of imageSets) {
|
|
858
|
-
imageStreams.push(...imageSet.streams);
|
|
960
|
+
shaka.util.Error.Code.DASH_EMPTY_PERIOD,
|
|
961
|
+
);
|
|
859
962
|
}
|
|
860
963
|
|
|
861
964
|
return {
|
|
@@ -868,15 +971,26 @@ shaka.dash.DashParser = class {
|
|
|
868
971
|
}
|
|
869
972
|
|
|
870
973
|
/**
|
|
974
|
+
* Gets the streams from the given sets or returns an empty array if disabled
|
|
975
|
+
* or no streams are found.
|
|
976
|
+
* @param {boolean} disabled
|
|
871
977
|
* @param {!Array.<!shaka.dash.DashParser.AdaptationInfo>} adaptationSets
|
|
872
|
-
* @param {string}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
978
|
+
* @param {string} contentType
|
|
979
|
+
@private
|
|
980
|
+
*/
|
|
981
|
+
getStreamsFromSets_(disabled, adaptationSets, contentType) {
|
|
982
|
+
if (disabled || !adaptationSets.length) {
|
|
983
|
+
return [];
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return adaptationSets.reduce((all, part) => {
|
|
987
|
+
if (part.contentType != contentType) {
|
|
988
|
+
return all;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
all.push(...part.streams);
|
|
992
|
+
return all;
|
|
993
|
+
}, []);
|
|
880
994
|
}
|
|
881
995
|
|
|
882
996
|
/**
|
|
@@ -964,6 +1078,7 @@ shaka.dash.DashParser = class {
|
|
|
964
1078
|
XmlUtils.findChildren(elem, 'EssentialProperty');
|
|
965
1079
|
// ID of real AdaptationSet if this is a trick mode set:
|
|
966
1080
|
let trickModeFor = null;
|
|
1081
|
+
let isFastSwitching = false;
|
|
967
1082
|
let unrecognizedEssentialProperty = false;
|
|
968
1083
|
for (const prop of essentialProperties) {
|
|
969
1084
|
const schemeId = prop.getAttribute('schemeIdUri');
|
|
@@ -976,6 +1091,8 @@ shaka.dash.DashParser = class {
|
|
|
976
1091
|
} else if (schemeId == colourPrimariesScheme ||
|
|
977
1092
|
schemeId == matrixCoefficientsScheme) {
|
|
978
1093
|
continue;
|
|
1094
|
+
} else if (schemeId == 'urn:mpeg:dash:ssr:2023') {
|
|
1095
|
+
isFastSwitching = true;
|
|
979
1096
|
} else {
|
|
980
1097
|
unrecognizedEssentialProperty = true;
|
|
981
1098
|
}
|
|
@@ -1133,6 +1250,7 @@ shaka.dash.DashParser = class {
|
|
|
1133
1250
|
closedCaptions, representation, accessibilityPurpose);
|
|
1134
1251
|
if (parsedRepresentation) {
|
|
1135
1252
|
parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
|
|
1253
|
+
parsedRepresentation.fastSwitching = isFastSwitching;
|
|
1136
1254
|
}
|
|
1137
1255
|
return parsedRepresentation;
|
|
1138
1256
|
}).filter((s) => !!s);
|
|
@@ -1257,9 +1375,9 @@ shaka.dash.DashParser = class {
|
|
|
1257
1375
|
/** @type {shaka.extern.aes128Key|undefined} */
|
|
1258
1376
|
let aes128Key = undefined;
|
|
1259
1377
|
if (contentProtection.aes128Info) {
|
|
1260
|
-
const
|
|
1378
|
+
const getBaseUris = context.representation.getBaseUris;
|
|
1261
1379
|
const uris = shaka.util.ManifestParserUtils.resolveUris(
|
|
1262
|
-
|
|
1380
|
+
getBaseUris(), [contentProtection.aes128Info.keyUri]);
|
|
1263
1381
|
const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
|
|
1264
1382
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
1265
1383
|
uris, this.config_.retryParameters);
|
|
@@ -1313,10 +1431,10 @@ shaka.dash.DashParser = class {
|
|
|
1313
1431
|
goog.asserts.assert(isText,
|
|
1314
1432
|
'Must have Segment* with non-text streams.');
|
|
1315
1433
|
|
|
1316
|
-
const baseUris = context.representation.baseUris;
|
|
1317
1434
|
const duration = context.periodInfo.duration || 0;
|
|
1318
1435
|
streamInfo = {
|
|
1319
1436
|
generateSegmentIndex: () => {
|
|
1437
|
+
const baseUris = context.representation.getBaseUris();
|
|
1320
1438
|
return Promise.resolve(shaka.media.SegmentIndex.forSingleSegment(
|
|
1321
1439
|
periodStart, duration, baseUris));
|
|
1322
1440
|
},
|
|
@@ -1442,10 +1560,12 @@ shaka.dash.DashParser = class {
|
|
|
1442
1560
|
spatialAudio,
|
|
1443
1561
|
closedCaptions,
|
|
1444
1562
|
hdr,
|
|
1563
|
+
videoLayout: undefined,
|
|
1445
1564
|
tilesLayout,
|
|
1446
1565
|
matchedStreams: [],
|
|
1447
1566
|
accessibilityPurpose,
|
|
1448
1567
|
external: false,
|
|
1568
|
+
fastSwitching: false,
|
|
1449
1569
|
};
|
|
1450
1570
|
}
|
|
1451
1571
|
|
|
@@ -1518,8 +1638,8 @@ shaka.dash.DashParser = class {
|
|
|
1518
1638
|
// NOTE: An updatePeriod_ of -1 means the attribute was missing.
|
|
1519
1639
|
// An attribute which is present and set to 0 should still result in
|
|
1520
1640
|
// periodic updates. For more, see:
|
|
1521
|
-
// https://github.com/
|
|
1522
|
-
if (this.updatePeriod_
|
|
1641
|
+
// https://github.com/Dash-Industry-Forum/Guidelines-TimingModel/issues/48
|
|
1642
|
+
if (this.updatePeriod_ <= 0) {
|
|
1523
1643
|
return;
|
|
1524
1644
|
}
|
|
1525
1645
|
|
|
@@ -1537,13 +1657,13 @@ shaka.dash.DashParser = class {
|
|
|
1537
1657
|
*
|
|
1538
1658
|
* @param {!Element} elem
|
|
1539
1659
|
* @param {?shaka.dash.DashParser.InheritanceFrame} parent
|
|
1540
|
-
* @param {Array.<string>}
|
|
1660
|
+
* @param {?function():!Array.<string>} getBaseUris
|
|
1541
1661
|
* @return {shaka.dash.DashParser.InheritanceFrame}
|
|
1542
1662
|
* @private
|
|
1543
1663
|
*/
|
|
1544
|
-
createFrame_(elem, parent,
|
|
1545
|
-
goog.asserts.assert(parent ||
|
|
1546
|
-
'Must provide either parent or
|
|
1664
|
+
createFrame_(elem, parent, getBaseUris) {
|
|
1665
|
+
goog.asserts.assert(parent || getBaseUris,
|
|
1666
|
+
'Must provide either parent or getBaseUris');
|
|
1547
1667
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
1548
1668
|
const XmlUtils = shaka.util.XmlUtils;
|
|
1549
1669
|
parent = parent || /** @type {shaka.dash.DashParser.InheritanceFrame} */ ({
|
|
@@ -1556,14 +1676,44 @@ shaka.dash.DashParser = class {
|
|
|
1556
1676
|
numChannels: null,
|
|
1557
1677
|
audioSamplingRate: null,
|
|
1558
1678
|
availabilityTimeOffset: 0,
|
|
1679
|
+
segmentSequenceCadence: 0,
|
|
1559
1680
|
});
|
|
1560
|
-
|
|
1681
|
+
getBaseUris = getBaseUris || parent.getBaseUris;
|
|
1561
1682
|
|
|
1562
1683
|
const parseNumber = XmlUtils.parseNonNegativeInt;
|
|
1563
1684
|
const evalDivision = XmlUtils.evalDivision;
|
|
1564
1685
|
|
|
1686
|
+
const id = elem.getAttribute('id');
|
|
1565
1687
|
const uriObjs = XmlUtils.findChildren(elem, 'BaseURL');
|
|
1566
|
-
|
|
1688
|
+
let calculatedBaseUris;
|
|
1689
|
+
let someLocationValid = false;
|
|
1690
|
+
if (this.contentSteeringManager_) {
|
|
1691
|
+
for (const uriObj of uriObjs) {
|
|
1692
|
+
const serviceLocation = uriObj.getAttribute('serviceLocation');
|
|
1693
|
+
const uri = XmlUtils.getContents(uriObj);
|
|
1694
|
+
if (serviceLocation && uri) {
|
|
1695
|
+
this.contentSteeringManager_.addLocation(
|
|
1696
|
+
id, serviceLocation, uri);
|
|
1697
|
+
someLocationValid = true;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (!someLocationValid || !this.contentSteeringManager_) {
|
|
1702
|
+
calculatedBaseUris = uriObjs.map(XmlUtils.getContents);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
const getFrameUris = () => {
|
|
1706
|
+
if (!uriObjs.length) {
|
|
1707
|
+
return [];
|
|
1708
|
+
}
|
|
1709
|
+
if (this.contentSteeringManager_ && someLocationValid) {
|
|
1710
|
+
return this.contentSteeringManager_.getLocations(id);
|
|
1711
|
+
}
|
|
1712
|
+
if (calculatedBaseUris) {
|
|
1713
|
+
return calculatedBaseUris;
|
|
1714
|
+
}
|
|
1715
|
+
return [];
|
|
1716
|
+
};
|
|
1567
1717
|
|
|
1568
1718
|
let contentType = elem.getAttribute('contentType') || parent.contentType;
|
|
1569
1719
|
const mimeType = elem.getAttribute('mimeType') || parent.mimeType;
|
|
@@ -1606,8 +1756,20 @@ shaka.dash.DashParser = class {
|
|
|
1606
1756
|
const availabilityTimeOffset = parent.availabilityTimeOffset + baseUriAto +
|
|
1607
1757
|
segmentBaseAto + segmentTemplateAto;
|
|
1608
1758
|
|
|
1759
|
+
let segmentSequenceCadence = null;
|
|
1760
|
+
const segmentSequenceProperties =
|
|
1761
|
+
XmlUtils.findChild(elem, 'SegmentSequenceProperties');
|
|
1762
|
+
if (segmentSequenceProperties) {
|
|
1763
|
+
const sap = XmlUtils.findChild(segmentSequenceProperties, 'SAP');
|
|
1764
|
+
if (sap) {
|
|
1765
|
+
segmentSequenceCadence = XmlUtils.parseAttr(sap, 'cadence',
|
|
1766
|
+
XmlUtils.parseInt);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1609
1770
|
return {
|
|
1610
|
-
|
|
1771
|
+
getBaseUris:
|
|
1772
|
+
() => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
|
|
1611
1773
|
segmentBase: segmentBase || parent.segmentBase,
|
|
1612
1774
|
segmentList:
|
|
1613
1775
|
XmlUtils.findChild(elem, 'SegmentList') || parent.segmentList,
|
|
@@ -1620,11 +1782,13 @@ shaka.dash.DashParser = class {
|
|
|
1620
1782
|
frameRate: frameRate,
|
|
1621
1783
|
pixelAspectRatio: pixelAspectRatio,
|
|
1622
1784
|
emsgSchemeIdUris: emsgSchemeIdUris,
|
|
1623
|
-
id:
|
|
1785
|
+
id: id,
|
|
1624
1786
|
language: elem.getAttribute('lang'),
|
|
1625
1787
|
numChannels: numChannels,
|
|
1626
1788
|
audioSamplingRate: audioSamplingRate,
|
|
1627
1789
|
availabilityTimeOffset: availabilityTimeOffset,
|
|
1790
|
+
segmentSequenceCadence:
|
|
1791
|
+
segmentSequenceCadence || parent.segmentSequenceCadence,
|
|
1628
1792
|
};
|
|
1629
1793
|
}
|
|
1630
1794
|
|
|
@@ -1794,15 +1958,15 @@ shaka.dash.DashParser = class {
|
|
|
1794
1958
|
/**
|
|
1795
1959
|
* Makes a request to the given URI and calculates the clock offset.
|
|
1796
1960
|
*
|
|
1797
|
-
* @param {
|
|
1961
|
+
* @param {function():!Array.<string>} getBaseUris
|
|
1798
1962
|
* @param {string} uri
|
|
1799
1963
|
* @param {string} method
|
|
1800
1964
|
* @return {!Promise.<number>}
|
|
1801
1965
|
* @private
|
|
1802
1966
|
*/
|
|
1803
|
-
async requestForTiming_(
|
|
1967
|
+
async requestForTiming_(getBaseUris, uri, method) {
|
|
1804
1968
|
const requestUris =
|
|
1805
|
-
shaka.util.ManifestParserUtils.resolveUris(
|
|
1969
|
+
shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), [uri]);
|
|
1806
1970
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
1807
1971
|
requestUris, this.config_.retryParameters);
|
|
1808
1972
|
request.method = method;
|
|
@@ -1835,12 +1999,12 @@ shaka.dash.DashParser = class {
|
|
|
1835
1999
|
/**
|
|
1836
2000
|
* Parses an array of UTCTiming elements.
|
|
1837
2001
|
*
|
|
1838
|
-
* @param {
|
|
2002
|
+
* @param {function():!Array.<string>} getBaseUris
|
|
1839
2003
|
* @param {!Array.<!Element>} elems
|
|
1840
2004
|
* @return {!Promise.<number>}
|
|
1841
2005
|
* @private
|
|
1842
2006
|
*/
|
|
1843
|
-
async parseUtcTiming_(
|
|
2007
|
+
async parseUtcTiming_(getBaseUris, elems) {
|
|
1844
2008
|
const schemesAndValues = elems.map((elem) => {
|
|
1845
2009
|
return {
|
|
1846
2010
|
scheme: elem.getAttribute('schemeIdUri'),
|
|
@@ -1869,13 +2033,13 @@ shaka.dash.DashParser = class {
|
|
|
1869
2033
|
case 'urn:mpeg:dash:utc:http-head:2014':
|
|
1870
2034
|
case 'urn:mpeg:dash:utc:http-head:2012':
|
|
1871
2035
|
// eslint-disable-next-line no-await-in-loop
|
|
1872
|
-
return await this.requestForTiming_(
|
|
2036
|
+
return await this.requestForTiming_(getBaseUris, value, 'HEAD');
|
|
1873
2037
|
case 'urn:mpeg:dash:utc:http-xsdate:2014':
|
|
1874
2038
|
case 'urn:mpeg:dash:utc:http-iso:2014':
|
|
1875
2039
|
case 'urn:mpeg:dash:utc:http-xsdate:2012':
|
|
1876
2040
|
case 'urn:mpeg:dash:utc:http-iso:2012':
|
|
1877
2041
|
// eslint-disable-next-line no-await-in-loop
|
|
1878
|
-
return await this.requestForTiming_(
|
|
2042
|
+
return await this.requestForTiming_(getBaseUris, value, 'GET');
|
|
1879
2043
|
case 'urn:mpeg:dash:utc:direct:2014':
|
|
1880
2044
|
case 'urn:mpeg:dash:utc:direct:2012': {
|
|
1881
2045
|
const date = Date.parse(value);
|
|
@@ -2038,7 +2202,7 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2038
2202
|
* segmentBase: Element,
|
|
2039
2203
|
* segmentList: Element,
|
|
2040
2204
|
* segmentTemplate: Element,
|
|
2041
|
-
*
|
|
2205
|
+
* getBaseUris: function():!Array.<string>,
|
|
2042
2206
|
* width: (number|undefined),
|
|
2043
2207
|
* height: (number|undefined),
|
|
2044
2208
|
* contentType: string,
|
|
@@ -2051,7 +2215,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2051
2215
|
* language: ?string,
|
|
2052
2216
|
* numChannels: ?number,
|
|
2053
2217
|
* audioSamplingRate: ?number,
|
|
2054
|
-
* availabilityTimeOffset: number
|
|
2218
|
+
* availabilityTimeOffset: number,
|
|
2219
|
+
* segmentSequenceCadence: number
|
|
2055
2220
|
* }}
|
|
2056
2221
|
*
|
|
2057
2222
|
* @description
|
|
@@ -2064,8 +2229,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2064
2229
|
* The XML node for SegmentList.
|
|
2065
2230
|
* @property {Element} segmentTemplate
|
|
2066
2231
|
* The XML node for SegmentTemplate.
|
|
2067
|
-
* @property {
|
|
2068
|
-
*
|
|
2232
|
+
* @property {function():!Array.<string>} getBaseUris
|
|
2233
|
+
* Function than returns an array of absolute base URIs for the frame.
|
|
2069
2234
|
* @property {(number|undefined)} width
|
|
2070
2235
|
* The inherited width value.
|
|
2071
2236
|
* @property {(number|undefined)} height
|
|
@@ -2092,6 +2257,9 @@ shaka.dash.DashParser.RequestSegmentCallback;
|
|
|
2092
2257
|
* Specifies the maximum sampling rate of the content, or null if unknown.
|
|
2093
2258
|
* @property {number} availabilityTimeOffset
|
|
2094
2259
|
* Specifies the total availabilityTimeOffset of the segment, or 0 if unknown.
|
|
2260
|
+
* @property {number} segmentSequenceCadence
|
|
2261
|
+
* Specifies the cadence of independent segments in Segment Sequence
|
|
2262
|
+
* Representation.
|
|
2095
2263
|
*/
|
|
2096
2264
|
shaka.dash.DashParser.InheritanceFrame;
|
|
2097
2265
|
|
|
@@ -2225,8 +2393,6 @@ shaka.dash.DashParser.GenerateSegmentIndexFunction;
|
|
|
2225
2393
|
shaka.dash.DashParser.StreamInfo;
|
|
2226
2394
|
|
|
2227
2395
|
|
|
2228
|
-
shaka.media.ManifestParser.registerParserByExtension(
|
|
2229
|
-
'mpd', () => new shaka.dash.DashParser());
|
|
2230
2396
|
shaka.media.ManifestParser.registerParserByMime(
|
|
2231
2397
|
'application/dash+xml', () => new shaka.dash.DashParser());
|
|
2232
2398
|
shaka.media.ManifestParser.registerParserByMime(
|