shaka-player 4.2.3 → 4.3.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/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +5 -0
- package/CHANGELOG.md +85 -39
- package/CONTRIBUTORS +5 -0
- package/README.md +14 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +83 -58
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +83 -58
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +18 -11
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +23 -19
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +23 -19
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.externs.js +163 -32
- package/dist/shaka-player.compiled.debug.js +488 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +163 -32
- package/dist/shaka-player.compiled.js +895 -857
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +178 -27
- package/dist/shaka-player.ui.debug.d.ts +178 -27
- package/dist/shaka-player.ui.debug.externs.js +163 -32
- package/dist/shaka-player.ui.debug.js +519 -450
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +163 -32
- package/dist/shaka-player.ui.js +1053 -1015
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +4 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +18 -6
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +87 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +799 -376
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_engine.js +112 -26
- package/lib/media/playhead.js +23 -1
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +145 -24
- package/lib/media/transmuxer.js +79 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +477 -85
- package/lib/polyfill/patchedmediakeys_apple.js +55 -1
- package/lib/text/ui_text_displayer.js +5 -5
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +15 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +4 -1
- package/roadmap.md +27 -7
- package/test/ads/ad_manager_unit.js +180 -29
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +500 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +102 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +17 -1
- package/test/media/streaming_engine_unit.js +180 -24
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +92 -2
- package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +7 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/ui/controls.js +4 -1
- package/ui/less/containers.less +7 -0
- package/ui/settings_menu.js +11 -2
- package/ui/ui.js +43 -4
package/lib/player.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.Player');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.Deprecate');
|
|
11
|
+
goog.require('shaka.config.AutoShowText');
|
|
11
12
|
goog.require('shaka.log');
|
|
12
13
|
goog.require('shaka.media.AdaptationSetCriteria');
|
|
13
14
|
goog.require('shaka.media.BufferingObserver');
|
|
@@ -17,7 +18,6 @@ goog.require('shaka.media.ManifestParser');
|
|
|
17
18
|
goog.require('shaka.media.MediaSourceEngine');
|
|
18
19
|
goog.require('shaka.media.MediaSourcePlayhead');
|
|
19
20
|
goog.require('shaka.media.MetaSegmentIndex');
|
|
20
|
-
goog.require('shaka.media.ClosedCaptionParser');
|
|
21
21
|
goog.require('shaka.media.PlayRateController');
|
|
22
22
|
goog.require('shaka.media.Playhead');
|
|
23
23
|
goog.require('shaka.media.PlayheadObserverManager');
|
|
@@ -26,6 +26,7 @@ goog.require('shaka.media.QualityObserver');
|
|
|
26
26
|
goog.require('shaka.media.RegionObserver');
|
|
27
27
|
goog.require('shaka.media.RegionTimeline');
|
|
28
28
|
goog.require('shaka.media.SegmentIndex');
|
|
29
|
+
goog.require('shaka.media.SegmentReference');
|
|
29
30
|
goog.require('shaka.media.SrcEqualsPlayhead');
|
|
30
31
|
goog.require('shaka.media.StreamingEngine');
|
|
31
32
|
goog.require('shaka.media.TimeRangesUtils');
|
|
@@ -55,7 +56,7 @@ goog.require('shaka.util.PublicPromise');
|
|
|
55
56
|
goog.require('shaka.util.Stats');
|
|
56
57
|
goog.require('shaka.util.StreamUtils');
|
|
57
58
|
goog.require('shaka.util.Timer');
|
|
58
|
-
goog.
|
|
59
|
+
goog.require('shaka.lcevc.Dil');
|
|
59
60
|
goog.requireType('shaka.media.PresentationTimeline');
|
|
60
61
|
goog.requireType('shaka.routing.Node');
|
|
61
62
|
goog.requireType('shaka.routing.Payload');
|
|
@@ -325,7 +326,7 @@ goog.requireType('shaka.routing.Payload');
|
|
|
325
326
|
* the cue applies.
|
|
326
327
|
* @property {string} metadataType
|
|
327
328
|
* Type of metadata. Eg: org.id3 or org.mp4ra
|
|
328
|
-
* @property {shaka.extern.
|
|
329
|
+
* @property {shaka.extern.MetadataFrame} payload
|
|
329
330
|
* The metadata itself
|
|
330
331
|
* @exportDoc
|
|
331
332
|
*/
|
|
@@ -367,6 +368,21 @@ goog.requireType('shaka.routing.Payload');
|
|
|
367
368
|
*/
|
|
368
369
|
|
|
369
370
|
|
|
371
|
+
/**
|
|
372
|
+
* @event shaka.Player.SegmentAppended
|
|
373
|
+
* @description Fired when a segment is appended to the media element.
|
|
374
|
+
* @property {string} type
|
|
375
|
+
* 'segmentappended'
|
|
376
|
+
* @property {number} start
|
|
377
|
+
* The start time of the segment.
|
|
378
|
+
* @property {number} end
|
|
379
|
+
* The end time of the segment.
|
|
380
|
+
* @property {string} contentType
|
|
381
|
+
* The content type of the segment. E.g. 'video', 'audio', or 'text'.
|
|
382
|
+
* @exportDoc
|
|
383
|
+
*/
|
|
384
|
+
|
|
385
|
+
|
|
370
386
|
/**
|
|
371
387
|
* @event shaka.Player.SessionDataEvent
|
|
372
388
|
* @description Fired when the manifest parser find info about session data.
|
|
@@ -505,6 +521,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
505
521
|
/** @private {shaka.util.CmcdManager} */
|
|
506
522
|
this.cmcdManager_ = null;
|
|
507
523
|
|
|
524
|
+
// This is the canvas element that will be used for rendering LCEVC
|
|
525
|
+
// enhanced frames.
|
|
526
|
+
/** @private {?HTMLCanvasElement} */
|
|
527
|
+
this.lcevcCanvas_ = null;
|
|
528
|
+
|
|
529
|
+
// This is the LCEVC Dil object to decode LCEVC.
|
|
530
|
+
/** @private {?shaka.lcevc.Dil} */
|
|
531
|
+
this.lcevcDil_ = null;
|
|
532
|
+
|
|
508
533
|
/** @private {shaka.media.QualityObserver} */
|
|
509
534
|
this.qualityObserver_ = null;
|
|
510
535
|
|
|
@@ -523,6 +548,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
523
548
|
/** @private {?string} */
|
|
524
549
|
this.assetUri_ = null;
|
|
525
550
|
|
|
551
|
+
/** @private {boolean} */
|
|
552
|
+
this.fullyLoaded_ = false;
|
|
553
|
+
|
|
526
554
|
/** @private {shaka.extern.AbrManager} */
|
|
527
555
|
this.abrManager_ = null;
|
|
528
556
|
|
|
@@ -726,6 +754,66 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
726
754
|
}
|
|
727
755
|
}
|
|
728
756
|
|
|
757
|
+
/**
|
|
758
|
+
* Create a shaka.lcevc.Dil object
|
|
759
|
+
* @param {shaka.extern.LcevcConfiguration} config
|
|
760
|
+
* @private
|
|
761
|
+
*/
|
|
762
|
+
createLcevcDil_(config) {
|
|
763
|
+
if (this.lcevcDil_ == null) {
|
|
764
|
+
this.lcevcDil_ = new shaka.lcevc.Dil(
|
|
765
|
+
/** @type {HTMLVideoElement} */ (this.video_),
|
|
766
|
+
this.lcevcCanvas_,
|
|
767
|
+
config,
|
|
768
|
+
);
|
|
769
|
+
if (this.mediaSourceEngine_) {
|
|
770
|
+
this.mediaSourceEngine_.updateLcevcDil(this.lcevcDil_);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Close a shaka.lcevc.Dil object if present and hide the canvas.
|
|
777
|
+
* @private
|
|
778
|
+
*/
|
|
779
|
+
closeLcevcDil_() {
|
|
780
|
+
if (this.lcevcDil_ != null) {
|
|
781
|
+
this.lcevcDil_.release();
|
|
782
|
+
this.lcevcDil_ = null;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Setup shaka.lcevc.Dil object
|
|
788
|
+
* @param {?shaka.extern.PlayerConfiguration} config
|
|
789
|
+
* @private
|
|
790
|
+
*/
|
|
791
|
+
setupLcevc_(config) {
|
|
792
|
+
if (config.lcevc.enabled) {
|
|
793
|
+
const tracks = this.getVariantTracks();
|
|
794
|
+
if (tracks && tracks[0] &&
|
|
795
|
+
tracks[0].videoMimeType ==
|
|
796
|
+
shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_['ts']) {
|
|
797
|
+
const edge = shaka.util.Platform.isEdge() ||
|
|
798
|
+
shaka.util.Platform.isLegacyEdge();
|
|
799
|
+
if (edge) {
|
|
800
|
+
if (!config.streaming.forceTransmux) {
|
|
801
|
+
// If forceTransmux is disabled for Microsoft Edge, LCEVC data
|
|
802
|
+
// is stripped out in case of a MPEG-2 TS container.
|
|
803
|
+
// Hence the warning for Microsoft Edge when playing content with
|
|
804
|
+
// MPEG-2 TS container.
|
|
805
|
+
shaka.log.alwaysWarn('LCEVC Warning: For MPEG-2 TS decoding '+
|
|
806
|
+
'the config.streaming.forceTransmux must be enabled.');
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
this.closeLcevcDil_();
|
|
811
|
+
this.createLcevcDil_(config.lcevc);
|
|
812
|
+
} else {
|
|
813
|
+
this.closeLcevcDil_();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
729
817
|
/**
|
|
730
818
|
* @param {!shaka.util.FakeEvent.EventName} name
|
|
731
819
|
* @param {Map.<string, Object>=} data
|
|
@@ -748,6 +836,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
748
836
|
return;
|
|
749
837
|
}
|
|
750
838
|
|
|
839
|
+
// If LCEVC Dil exists close it.
|
|
840
|
+
this.closeLcevcDil_();
|
|
841
|
+
|
|
751
842
|
// Mark as "dead". This should stop external-facing calls from changing our
|
|
752
843
|
// internal state any more. This will stop calls to |attach|, |detach|, etc.
|
|
753
844
|
// from interrupting our final move to the detached state.
|
|
@@ -985,6 +1076,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
985
1076
|
return this.wrapWalkerListenersWithPromise_(events);
|
|
986
1077
|
}
|
|
987
1078
|
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Calling <code>attachCanvas</code> will tell the player to set canvas
|
|
1082
|
+
* element for LCEVC decoding.
|
|
1083
|
+
*
|
|
1084
|
+
* @param {HTMLCanvasElement} canvas
|
|
1085
|
+
* @export
|
|
1086
|
+
*/
|
|
1087
|
+
attachCanvas(canvas) {
|
|
1088
|
+
this.lcevcCanvas_ = canvas;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
988
1091
|
/**
|
|
989
1092
|
* Tell the player to stop using its current media element. If the player is:
|
|
990
1093
|
* <ul>
|
|
@@ -1050,12 +1153,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1050
1153
|
return Promise.reject(this.createAbortLoadError_());
|
|
1051
1154
|
}
|
|
1052
1155
|
|
|
1156
|
+
this.fullyLoaded_ = false;
|
|
1157
|
+
|
|
1053
1158
|
// If the platform does not support media source, we will never want to
|
|
1054
1159
|
// initialize media source.
|
|
1055
1160
|
if (!shaka.util.Platform.supportsMediaSource()) {
|
|
1056
1161
|
initializeMediaSource = false;
|
|
1057
1162
|
}
|
|
1058
1163
|
|
|
1164
|
+
// If LCEVC Dil exists close it.
|
|
1165
|
+
this.closeLcevcDil_();
|
|
1166
|
+
|
|
1059
1167
|
// Since we are going either to attached or detached (through unloaded), we
|
|
1060
1168
|
// can't allow it to be interrupted or else we could lose track of what
|
|
1061
1169
|
// media element we are suppose to use.
|
|
@@ -1141,6 +1249,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1141
1249
|
load(assetUri, startTime, mimeType) {
|
|
1142
1250
|
this.updatedStartTime_ = null;
|
|
1143
1251
|
|
|
1252
|
+
this.fullyLoaded_ = false;
|
|
1253
|
+
|
|
1144
1254
|
// Do not allow the player to be used after |destroy| is called.
|
|
1145
1255
|
if (this.loadMode_ == shaka.Player.LoadMode.DESTROYED) {
|
|
1146
1256
|
return Promise.reject(this.createAbortLoadError_());
|
|
@@ -1578,8 +1688,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1578
1688
|
this.mediaSourceEngine_ == null,
|
|
1579
1689
|
'We should not have a media source engine yet.');
|
|
1580
1690
|
|
|
1581
|
-
const closedCaptionsParser = new shaka.media.ClosedCaptionParser();
|
|
1582
|
-
|
|
1583
1691
|
// When changing text visibility we need to update both the text displayer
|
|
1584
1692
|
// and streaming engine because we don't always stream text. To ensure that
|
|
1585
1693
|
// text displayer and streaming engine are always in sync, wait until they
|
|
@@ -1590,11 +1698,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1590
1698
|
|
|
1591
1699
|
const mediaSourceEngine = this.createMediaSourceEngine(
|
|
1592
1700
|
has.mediaElement,
|
|
1593
|
-
closedCaptionsParser,
|
|
1594
1701
|
textDisplayer,
|
|
1595
1702
|
(metadata, offset, endTime) => {
|
|
1596
1703
|
this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
|
|
1597
|
-
}
|
|
1704
|
+
},
|
|
1705
|
+
this.lcevcDil_);
|
|
1706
|
+
mediaSourceEngine.configure(this.config_.mediaSource);
|
|
1598
1707
|
const {segmentRelativeVttTiming} = this.config_.manifest;
|
|
1599
1708
|
mediaSourceEngine.setSegmentRelativeVttTiming(segmentRelativeVttTiming);
|
|
1600
1709
|
|
|
@@ -1775,6 +1884,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1775
1884
|
this.streamingEngine_.updateDuration();
|
|
1776
1885
|
}
|
|
1777
1886
|
},
|
|
1887
|
+
newDrmInfo: (stream) => {
|
|
1888
|
+
// We may need to create new sessions for any new init data.
|
|
1889
|
+
const currentDrmInfo =
|
|
1890
|
+
this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
|
|
1891
|
+
// DrmEngine.newInitData() requires mediaKeys to be available.
|
|
1892
|
+
if (currentDrmInfo && this.drmEngine_.getMediaKeys()) {
|
|
1893
|
+
this.processDrmInfos_(currentDrmInfo.keySystem, stream);
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1778
1896
|
};
|
|
1779
1897
|
|
|
1780
1898
|
const startTime = Date.now() / 1000;
|
|
@@ -1865,6 +1983,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1865
1983
|
const now = Date.now() / 1000;
|
|
1866
1984
|
const delta = now - startTime;
|
|
1867
1985
|
this.stats_.setDrmTime(delta);
|
|
1986
|
+
// LCEVC data by itself is not encrypted in DRM protected streams and
|
|
1987
|
+
// can therefore be accessed and decoded as normal. However, the LCEVC
|
|
1988
|
+
// decoder needs access to the VideoElement output in order to apply
|
|
1989
|
+
// the enhancement. In DRM contexts where the browser CDM restricts
|
|
1990
|
+
// access from our decoder, the enhancement cannot be applied and
|
|
1991
|
+
// therefore the LCEVC output canvas is hidden accordingly.
|
|
1992
|
+
if (this.lcevcDil_) {
|
|
1993
|
+
this.lcevcDil_.hideCanvas();
|
|
1994
|
+
}
|
|
1868
1995
|
}
|
|
1869
1996
|
},
|
|
1870
1997
|
});
|
|
@@ -1949,6 +2076,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1949
2076
|
this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
|
|
1950
2077
|
this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
|
|
1951
2078
|
|
|
2079
|
+
// Check status of the LCEVC DIL Object and reset or
|
|
2080
|
+
// create or close based on config
|
|
2081
|
+
this.setupLcevc_(this.config_);
|
|
2082
|
+
|
|
1952
2083
|
const abrFactory = this.config_.abrFactory;
|
|
1953
2084
|
if (!this.abrManager_ || this.abrManagerFactory_ != abrFactory) {
|
|
1954
2085
|
this.abrManagerFactory_ = abrFactory;
|
|
@@ -1983,16 +2114,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1983
2114
|
});
|
|
1984
2115
|
this.abrManager_.setMediaElement(mediaElement);
|
|
1985
2116
|
|
|
1986
|
-
this.playhead_ = this.createPlayhead(has.startTime);
|
|
1987
|
-
this.playheadObservers_ = this.createPlayheadObserversForMSE_();
|
|
1988
|
-
|
|
1989
|
-
// We need to start the buffer management code near the end because it will
|
|
1990
|
-
// set the initial buffering state and that depends on other components
|
|
1991
|
-
// being initialized.
|
|
1992
|
-
const rebufferThreshold = Math.max(
|
|
1993
|
-
this.manifest_.minBufferTime, this.config_.streaming.rebufferingGoal);
|
|
1994
|
-
this.startBufferManagement_(rebufferThreshold);
|
|
1995
|
-
|
|
1996
2117
|
// If the content is multi-codec and the browser can play more than one of
|
|
1997
2118
|
// them, choose codecs now before we initialize streaming.
|
|
1998
2119
|
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
|
|
@@ -2036,14 +2157,42 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2036
2157
|
this.makeEvent_(shaka.util.FakeEvent.EventName.Streaming));
|
|
2037
2158
|
|
|
2038
2159
|
// Pick the initial streams to play.
|
|
2039
|
-
//
|
|
2040
|
-
//
|
|
2160
|
+
// Unless the user has already picked a variant, anyway, by calling
|
|
2161
|
+
// selectVariantTrack before this loading stage.
|
|
2041
2162
|
let initialVariant = null;
|
|
2042
2163
|
const activeVariantTrack = this.getVariantTracks().find((t) => t.active);
|
|
2043
|
-
|
|
2044
2164
|
if (!activeVariantTrack) {
|
|
2045
2165
|
initialVariant = this.chooseVariant_();
|
|
2046
2166
|
goog.asserts.assert(initialVariant, 'Must choose an initial variant!');
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Lazy-load the stream, so we will have enough info to make the playhead.
|
|
2170
|
+
const createSegmentIndexPromises = [];
|
|
2171
|
+
const toLazyLoad = activeVariantTrack || initialVariant;
|
|
2172
|
+
for (const stream of [toLazyLoad.video, toLazyLoad.audio]) {
|
|
2173
|
+
if (stream && !stream.segmentIndex) {
|
|
2174
|
+
createSegmentIndexPromises.push(stream.createSegmentIndex());
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
if (createSegmentIndexPromises.length > 0) {
|
|
2178
|
+
await Promise.all(createSegmentIndexPromises);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
this.playhead_ = this.createPlayhead(has.startTime);
|
|
2182
|
+
this.playheadObservers_ = this.createPlayheadObserversForMSE_();
|
|
2183
|
+
|
|
2184
|
+
// We need to start the buffer management code near the end because it will
|
|
2185
|
+
// set the initial buffering state and that depends on other components
|
|
2186
|
+
// being initialized.
|
|
2187
|
+
const rebufferThreshold = Math.max(
|
|
2188
|
+
this.manifest_.minBufferTime, this.config_.streaming.rebufferingGoal);
|
|
2189
|
+
this.startBufferManagement_(rebufferThreshold);
|
|
2190
|
+
|
|
2191
|
+
// Now we can switch to the initial variant.
|
|
2192
|
+
if (!activeVariantTrack) {
|
|
2193
|
+
goog.asserts.assert(initialVariant,
|
|
2194
|
+
'Must have choosen an initial variant!');
|
|
2195
|
+
|
|
2047
2196
|
this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
|
|
2048
2197
|
/* clearBuffer= */ false, /* safeMargin= */ 0);
|
|
2049
2198
|
|
|
@@ -2062,6 +2211,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2062
2211
|
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
2063
2212
|
}
|
|
2064
2213
|
|
|
2214
|
+
this.playhead_.ready();
|
|
2215
|
+
|
|
2065
2216
|
// Decide if text should be shown automatically.
|
|
2066
2217
|
// similar to video/audio track, we would skip switch initial text track
|
|
2067
2218
|
// if user already pick text track (via selectTextTrack api)
|
|
@@ -2115,6 +2266,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2115
2266
|
'arbitrary language initially');
|
|
2116
2267
|
}
|
|
2117
2268
|
|
|
2269
|
+
this.fullyLoaded_ = true;
|
|
2270
|
+
|
|
2118
2271
|
// Wait for the 'loadedmetadata' event to measure load() latency.
|
|
2119
2272
|
this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
|
|
2120
2273
|
const now = Date.now() / 1000;
|
|
@@ -2399,6 +2552,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2399
2552
|
HTMLMediaElement.HAVE_METADATA,
|
|
2400
2553
|
this.loadEventManager_,
|
|
2401
2554
|
() => {
|
|
2555
|
+
this.playhead_.ready();
|
|
2402
2556
|
fullyLoaded.resolve();
|
|
2403
2557
|
});
|
|
2404
2558
|
|
|
@@ -2460,6 +2614,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2460
2614
|
shaka.util.Error.Code.OPERATION_ABORTED);
|
|
2461
2615
|
fullyLoaded.reject(abortedError);
|
|
2462
2616
|
return Promise.resolve(); // Abort complete.
|
|
2617
|
+
}).chain(() => {
|
|
2618
|
+
this.fullyLoaded_ = true;
|
|
2463
2619
|
});
|
|
2464
2620
|
}
|
|
2465
2621
|
|
|
@@ -2572,11 +2728,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2572
2728
|
*/
|
|
2573
2729
|
processTimedMetadataMediaSrc_(metadata, offset, segmentEndTime) {
|
|
2574
2730
|
for (const sample of metadata) {
|
|
2575
|
-
if (sample
|
|
2576
|
-
const start = sample
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2731
|
+
if (sample.data && sample.cueTime && sample.frames) {
|
|
2732
|
+
const start = sample.cueTime + offset;
|
|
2733
|
+
let end = segmentEndTime;
|
|
2734
|
+
// This can happen when the ID3 info arrives in a previous segment.
|
|
2735
|
+
if (end && start > end) {
|
|
2736
|
+
end = start;
|
|
2737
|
+
}
|
|
2738
|
+
const metadataType = 'org.id3';
|
|
2739
|
+
for (const frame of sample.frames) {
|
|
2580
2740
|
const payload = frame;
|
|
2581
2741
|
this.dispatchMetadataEvent_(start, end, metadataType, payload);
|
|
2582
2742
|
}
|
|
@@ -2595,7 +2755,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2595
2755
|
* @param {number} startTime
|
|
2596
2756
|
* @param {?number} endTime
|
|
2597
2757
|
* @param {string} metadataType
|
|
2598
|
-
* @param {shaka.extern.
|
|
2758
|
+
* @param {shaka.extern.MetadataFrame} payload
|
|
2599
2759
|
* @private
|
|
2600
2760
|
*/
|
|
2601
2761
|
dispatchMetadataEvent_(startTime, endTime, metadataType, payload) {
|
|
@@ -2893,17 +3053,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2893
3053
|
* way to inject fake media source engine instances.
|
|
2894
3054
|
*
|
|
2895
3055
|
* @param {!HTMLMediaElement} mediaElement
|
|
2896
|
-
* @param {!shaka.media.IClosedCaptionParser} closedCaptionsParser
|
|
2897
3056
|
* @param {!shaka.extern.TextDisplayer} textDisplayer
|
|
2898
3057
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)}
|
|
2899
3058
|
* onMetadata
|
|
3059
|
+
* @param {shaka.lcevc.Dil} lcevcDil
|
|
2900
3060
|
*
|
|
2901
3061
|
* @return {!shaka.media.MediaSourceEngine}
|
|
2902
3062
|
*/
|
|
2903
|
-
createMediaSourceEngine(mediaElement,
|
|
2904
|
-
onMetadata) {
|
|
3063
|
+
createMediaSourceEngine(mediaElement, textDisplayer, onMetadata, lcevcDil) {
|
|
2905
3064
|
return new shaka.media.MediaSourceEngine(
|
|
2906
|
-
mediaElement,
|
|
3065
|
+
mediaElement,
|
|
3066
|
+
textDisplayer,
|
|
3067
|
+
onMetadata,
|
|
3068
|
+
lcevcDil);
|
|
2907
3069
|
}
|
|
2908
3070
|
|
|
2909
3071
|
/**
|
|
@@ -2934,13 +3096,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2934
3096
|
*/
|
|
2935
3097
|
createStreamingEngine() {
|
|
2936
3098
|
goog.asserts.assert(
|
|
2937
|
-
this.
|
|
3099
|
+
this.abrManager_ && this.mediaSourceEngine_ &&
|
|
2938
3100
|
this.cmcdManager_ && this.manifest_,
|
|
2939
3101
|
'Must not be destroyed');
|
|
2940
3102
|
|
|
2941
3103
|
/** @type {shaka.media.StreamingEngine.PlayerInterface} */
|
|
2942
3104
|
const playerInterface = {
|
|
2943
|
-
getPresentationTime: () => this.playhead_.getTime(),
|
|
3105
|
+
getPresentationTime: () => this.playhead_ ? this.playhead_.getTime() : 0,
|
|
2944
3106
|
getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
|
|
2945
3107
|
modifySegmentRequest: (request, segmentInfo) => {
|
|
2946
3108
|
this.cmcdManager_.applySegmentData(request, segmentInfo);
|
|
@@ -2950,7 +3112,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2950
3112
|
onError: (error) => this.onError_(error),
|
|
2951
3113
|
onEvent: (event) => this.dispatchEvent(event),
|
|
2952
3114
|
onManifestUpdate: () => this.onManifestUpdate_(),
|
|
2953
|
-
onSegmentAppended: () =>
|
|
3115
|
+
onSegmentAppended: (start, end, contentType) => {
|
|
3116
|
+
this.onSegmentAppended_(start, end, contentType);
|
|
3117
|
+
},
|
|
2954
3118
|
onInitSegmentAppended: (position, initSegment) => {
|
|
2955
3119
|
const mediaQuality = initSegment.getMediaQuality();
|
|
2956
3120
|
if (mediaQuality && this.qualityObserver_) {
|
|
@@ -2960,6 +3124,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2960
3124
|
beforeAppendSegment: (contentType, segment) => {
|
|
2961
3125
|
return this.drmEngine_.parseInbandPssh(contentType, segment);
|
|
2962
3126
|
},
|
|
3127
|
+
onMetadata: (metadata, offset, endTime) => {
|
|
3128
|
+
this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
|
|
3129
|
+
},
|
|
2963
3130
|
};
|
|
2964
3131
|
|
|
2965
3132
|
return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
|
|
@@ -3003,6 +3170,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3003
3170
|
|
|
3004
3171
|
goog.asserts.assert(typeof(config) == 'object', 'Should be an object!');
|
|
3005
3172
|
|
|
3173
|
+
// Deprecate 'streaming.forceTransmuxTS' configuration.
|
|
3174
|
+
if (config['streaming'] && 'forceTransmuxTS' in config['streaming']) {
|
|
3175
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3176
|
+
'streaming.forceTransmuxTS configuration',
|
|
3177
|
+
'Please Use streaming.forceTransmux instead.');
|
|
3178
|
+
config['streaming']['forceTransmux'] =
|
|
3179
|
+
config['streaming']['forceTransmuxTS'];
|
|
3180
|
+
delete config['streaming']['forceTransmuxTS'];
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3006
3183
|
// If lowLatencyMode is enabled, and inaccurateManifestTolerance and
|
|
3007
3184
|
// rebufferingGoal are not specified, set inaccurateManifestTolerance to 0
|
|
3008
3185
|
// and rebufferingGoal to 0.01 by default for low latency streaming.
|
|
@@ -3069,6 +3246,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3069
3246
|
}
|
|
3070
3247
|
|
|
3071
3248
|
if (this.mediaSourceEngine_) {
|
|
3249
|
+
this.mediaSourceEngine_.configure(this.config_.mediaSource);
|
|
3072
3250
|
const {segmentRelativeVttTiming} = this.config_.manifest;
|
|
3073
3251
|
this.mediaSourceEngine_.setSegmentRelativeVttTiming(
|
|
3074
3252
|
segmentRelativeVttTiming);
|
|
@@ -3356,6 +3534,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3356
3534
|
* @export
|
|
3357
3535
|
*/
|
|
3358
3536
|
seekRange() {
|
|
3537
|
+
if (!this.fullyLoaded_) {
|
|
3538
|
+
return {'start': 0, 'end': 0};
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3359
3541
|
if (this.manifest_) {
|
|
3360
3542
|
const timeline = this.manifest_.presentationTimeline;
|
|
3361
3543
|
|
|
@@ -3698,8 +3880,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3698
3880
|
const fullImageHeight = imageStream.height || 0;
|
|
3699
3881
|
const columns = parseInt(match[1], 10);
|
|
3700
3882
|
const rows = parseInt(match[2], 10);
|
|
3701
|
-
|
|
3702
|
-
|
|
3883
|
+
let width = fullImageWidth / columns;
|
|
3884
|
+
let height = fullImageHeight / rows;
|
|
3703
3885
|
const totalImages = columns * rows;
|
|
3704
3886
|
const segmentDuration = reference.trueEndTime - reference.startTime;
|
|
3705
3887
|
const thumbnailDuration =
|
|
@@ -3725,6 +3907,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3725
3907
|
positionX = (thumbnailPosition % columns) * width;
|
|
3726
3908
|
positionY = Math.floor(thumbnailPosition / columns) * height;
|
|
3727
3909
|
}
|
|
3910
|
+
let sprite = false;
|
|
3911
|
+
const thumbnailSprite = reference.getThumbnailSprite();
|
|
3912
|
+
if (thumbnailSprite) {
|
|
3913
|
+
sprite = true;
|
|
3914
|
+
height = thumbnailSprite.height;
|
|
3915
|
+
positionX = thumbnailSprite.positionX;
|
|
3916
|
+
positionY = thumbnailSprite.positionY;
|
|
3917
|
+
width = thumbnailSprite.width;
|
|
3918
|
+
}
|
|
3728
3919
|
return {
|
|
3729
3920
|
imageHeight: fullImageHeight,
|
|
3730
3921
|
imageWidth: fullImageWidth,
|
|
@@ -3735,6 +3926,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3735
3926
|
duration: thumbnailDuration,
|
|
3736
3927
|
uris: reference.getUris(),
|
|
3737
3928
|
width: width,
|
|
3929
|
+
sprite: sprite,
|
|
3738
3930
|
};
|
|
3739
3931
|
}
|
|
3740
3932
|
return null;
|
|
@@ -3918,20 +4110,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3918
4110
|
|
|
3919
4111
|
/**
|
|
3920
4112
|
* Sets the current audio language and current variant role to the selected
|
|
3921
|
-
* language and
|
|
3922
|
-
* not loaded any content, this will be a no-op.
|
|
4113
|
+
* language, role and channel count, and chooses a new variant if need be.
|
|
4114
|
+
* If the player has not loaded any content, this will be a no-op.
|
|
3923
4115
|
*
|
|
3924
4116
|
* @param {string} language
|
|
3925
4117
|
* @param {string=} role
|
|
4118
|
+
* @param {number=} channelsCount
|
|
3926
4119
|
* @export
|
|
3927
4120
|
*/
|
|
3928
|
-
selectAudioLanguage(language, role) {
|
|
4121
|
+
selectAudioLanguage(language, role, channelsCount = 0) {
|
|
3929
4122
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
3930
4123
|
|
|
3931
4124
|
if (this.manifest_ && this.playhead_) {
|
|
3932
4125
|
this.currentAdaptationSetCriteria_ =
|
|
3933
4126
|
new shaka.media.PreferenceBasedCriteria(language, role || '',
|
|
3934
|
-
|
|
4127
|
+
channelsCount, /* label= */ '');
|
|
3935
4128
|
|
|
3936
4129
|
const diff = (a, b) => {
|
|
3937
4130
|
if (!a.video && !b.video) {
|
|
@@ -4610,6 +4803,146 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4610
4803
|
return shaka.util.StreamUtils.textStreamToTrack(stream);
|
|
4611
4804
|
}
|
|
4612
4805
|
|
|
4806
|
+
/**
|
|
4807
|
+
* Adds the given thumbnails track to the loaded manifest.
|
|
4808
|
+
* <code>load()</code> must resolve before calling. The presentation must
|
|
4809
|
+
* have a duration.
|
|
4810
|
+
*
|
|
4811
|
+
* This returns the created track, which can immediately be used by the
|
|
4812
|
+
* application.
|
|
4813
|
+
*
|
|
4814
|
+
* @param {string} uri
|
|
4815
|
+
* @param {string=} mimeType
|
|
4816
|
+
* @return {!Promise.<shaka.extern.Track>}
|
|
4817
|
+
* @export
|
|
4818
|
+
*/
|
|
4819
|
+
async addThumbnailsTrack(uri, mimeType) {
|
|
4820
|
+
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE &&
|
|
4821
|
+
this.loadMode_ != shaka.Player.LoadMode.SRC_EQUALS) {
|
|
4822
|
+
shaka.log.error(
|
|
4823
|
+
'Must call load() and wait for it to resolve before adding image ' +
|
|
4824
|
+
'tracks.');
|
|
4825
|
+
throw new shaka.util.Error(
|
|
4826
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4827
|
+
shaka.util.Error.Category.PLAYER,
|
|
4828
|
+
shaka.util.Error.Code.CONTENT_NOT_LOADED);
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4831
|
+
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
|
|
4832
|
+
shaka.log.error('Cannot add this thumbnail track when loaded with src=');
|
|
4833
|
+
throw new shaka.util.Error(
|
|
4834
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4835
|
+
shaka.util.Error.Category.TEXT,
|
|
4836
|
+
shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_THUMBNAILS_TO_SRC_EQUALS);
|
|
4837
|
+
}
|
|
4838
|
+
|
|
4839
|
+
if (!mimeType) {
|
|
4840
|
+
mimeType = await this.getTextMimetype_(uri);
|
|
4841
|
+
}
|
|
4842
|
+
|
|
4843
|
+
if (mimeType != 'text/vtt') {
|
|
4844
|
+
throw new shaka.util.Error(
|
|
4845
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4846
|
+
shaka.util.Error.Category.TEXT,
|
|
4847
|
+
shaka.util.Error.Code.UNSUPPORTED_EXTERNAL_THUMBNAILS_URI,
|
|
4848
|
+
uri);
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
4852
|
+
|
|
4853
|
+
const duration = this.manifest_.presentationTimeline.getDuration();
|
|
4854
|
+
if (duration == Infinity) {
|
|
4855
|
+
throw new shaka.util.Error(
|
|
4856
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4857
|
+
shaka.util.Error.Category.MANIFEST,
|
|
4858
|
+
shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_THUMBNAILS_TO_LIVE_STREAM);
|
|
4859
|
+
}
|
|
4860
|
+
|
|
4861
|
+
goog.asserts.assert(
|
|
4862
|
+
this.networkingEngine_, 'Need networking engine.');
|
|
4863
|
+
const buffer = await this.getTextData_(uri,
|
|
4864
|
+
this.networkingEngine_,
|
|
4865
|
+
this.config_.streaming.retryParameters);
|
|
4866
|
+
|
|
4867
|
+
const factory = shaka.text.TextEngine.findParser(mimeType);
|
|
4868
|
+
if (!factory) {
|
|
4869
|
+
throw new shaka.util.Error(
|
|
4870
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
4871
|
+
shaka.util.Error.Category.TEXT,
|
|
4872
|
+
shaka.util.Error.Code.MISSING_TEXT_PLUGIN,
|
|
4873
|
+
mimeType);
|
|
4874
|
+
}
|
|
4875
|
+
const TextParser = factory();
|
|
4876
|
+
const time = {
|
|
4877
|
+
periodStart: 0,
|
|
4878
|
+
segmentStart: 0,
|
|
4879
|
+
segmentEnd: duration,
|
|
4880
|
+
vttOffset: 0,
|
|
4881
|
+
};
|
|
4882
|
+
const data = shaka.util.BufferUtils.toUint8(buffer);
|
|
4883
|
+
const cues = TextParser.parseMedia(data, time);
|
|
4884
|
+
|
|
4885
|
+
const references = [];
|
|
4886
|
+
for (const cue of cues) {
|
|
4887
|
+
const imageUri = shaka.util.ManifestParserUtils.resolveUris(
|
|
4888
|
+
[uri], [cue.payload])[0];
|
|
4889
|
+
const reference = new shaka.media.SegmentReference(
|
|
4890
|
+
cue.startTime,
|
|
4891
|
+
cue.endTime,
|
|
4892
|
+
() => [imageUri],
|
|
4893
|
+
/* startByte= */ 0,
|
|
4894
|
+
/* endByte= */ null,
|
|
4895
|
+
/* initSegmentReference= */ null,
|
|
4896
|
+
/* timestampOffset= */ 0,
|
|
4897
|
+
/* appendWindowStart= */ 0,
|
|
4898
|
+
/* appendWindowEnd= */ Infinity,
|
|
4899
|
+
);
|
|
4900
|
+
if (imageUri.includes('#xywh')) {
|
|
4901
|
+
const spriteInfo = imageUri.split('#xywh=')[1].split(',');
|
|
4902
|
+
if (spriteInfo.length === 4) {
|
|
4903
|
+
reference.setThumbnailSprite({
|
|
4904
|
+
height: parseInt(spriteInfo[3], 10),
|
|
4905
|
+
positionX: parseInt(spriteInfo[0], 10),
|
|
4906
|
+
positionY: parseInt(spriteInfo[1], 10),
|
|
4907
|
+
width: parseInt(spriteInfo[2], 10),
|
|
4908
|
+
});
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
references.push(reference);
|
|
4912
|
+
}
|
|
4913
|
+
|
|
4914
|
+
/** @type {shaka.extern.Stream} */
|
|
4915
|
+
const stream = {
|
|
4916
|
+
id: this.nextExternalStreamId_++,
|
|
4917
|
+
originalId: null,
|
|
4918
|
+
createSegmentIndex: () => Promise.resolve(),
|
|
4919
|
+
segmentIndex: new shaka.media.SegmentIndex(references),
|
|
4920
|
+
mimeType: mimeType || '',
|
|
4921
|
+
codecs: '',
|
|
4922
|
+
kind: '',
|
|
4923
|
+
encrypted: false,
|
|
4924
|
+
drmInfos: [],
|
|
4925
|
+
keyIds: new Set(),
|
|
4926
|
+
language: 'und',
|
|
4927
|
+
label: null,
|
|
4928
|
+
type: ContentType.IMAGE,
|
|
4929
|
+
primary: false,
|
|
4930
|
+
trickModeVideo: null,
|
|
4931
|
+
emsgSchemeIdUris: null,
|
|
4932
|
+
roles: [],
|
|
4933
|
+
forced: false,
|
|
4934
|
+
channelsCount: null,
|
|
4935
|
+
audioSamplingRate: null,
|
|
4936
|
+
spatialAudio: false,
|
|
4937
|
+
closedCaptions: null,
|
|
4938
|
+
tilesLayout: '1x1',
|
|
4939
|
+
};
|
|
4940
|
+
|
|
4941
|
+
this.manifest_.imageStreams.push(stream);
|
|
4942
|
+
this.onTracksChanged_();
|
|
4943
|
+
return shaka.util.StreamUtils.imageStreamToTrack(stream);
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4613
4946
|
/**
|
|
4614
4947
|
* Adds the given chapters track to the loaded manifest. <code>load()</code>
|
|
4615
4948
|
* must resolve before calling. The presentation must have a duration.
|
|
@@ -4841,12 +5174,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4841
5174
|
* If the player has loaded content, and streaming seen an error, but the
|
|
4842
5175
|
* could not resume streaming, this will return <code>false</code>.
|
|
4843
5176
|
*
|
|
5177
|
+
* @param {number=} retryDelaySeconds
|
|
4844
5178
|
* @return {boolean}
|
|
4845
5179
|
* @export
|
|
4846
5180
|
*/
|
|
4847
|
-
retryStreaming() {
|
|
5181
|
+
retryStreaming(retryDelaySeconds = 0.1) {
|
|
4848
5182
|
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
|
|
4849
|
-
this.streamingEngine_.retry() :
|
|
5183
|
+
this.streamingEngine_.retry(retryDelaySeconds) :
|
|
4850
5184
|
false;
|
|
4851
5185
|
}
|
|
4852
5186
|
|
|
@@ -4942,17 +5276,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4942
5276
|
* @private
|
|
4943
5277
|
*/
|
|
4944
5278
|
defaultStreamingFailureCallback_(error) {
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
5279
|
+
// For live streams, we retry streaming automatically for certain errors.
|
|
5280
|
+
// For VOD streams, all streaming failures are fatal.
|
|
5281
|
+
if (!this.isLive()) {
|
|
5282
|
+
return;
|
|
5283
|
+
}
|
|
4950
5284
|
|
|
4951
|
-
|
|
4952
|
-
|
|
5285
|
+
let retryDelaySeconds = null;
|
|
5286
|
+
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
|
|
5287
|
+
error.code == shaka.util.Error.Code.HTTP_ERROR) {
|
|
5288
|
+
// These errors can be near-instant, so delay a bit before retrying.
|
|
5289
|
+
retryDelaySeconds = 1;
|
|
5290
|
+
} else if (error.code == shaka.util.Error.Code.TIMEOUT) {
|
|
5291
|
+
// We already waited for a timeout, so retry quickly.
|
|
5292
|
+
retryDelaySeconds = 0.1;
|
|
5293
|
+
}
|
|
4953
5294
|
|
|
5295
|
+
if (retryDelaySeconds != null) {
|
|
5296
|
+
error.severity = shaka.util.Error.Severity.RECOVERABLE;
|
|
4954
5297
|
shaka.log.warning('Live streaming error. Retrying automatically...');
|
|
4955
|
-
this.retryStreaming();
|
|
5298
|
+
this.retryStreaming(retryDelaySeconds);
|
|
4956
5299
|
}
|
|
4957
5300
|
}
|
|
4958
5301
|
|
|
@@ -5071,28 +5414,39 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5071
5414
|
}
|
|
5072
5415
|
|
|
5073
5416
|
// We may need to create new sessions for any new init data.
|
|
5074
|
-
const
|
|
5417
|
+
const currentDrmInfo =
|
|
5418
|
+
this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
|
|
5075
5419
|
// DrmEngine.newInitData() requires mediaKeys to be available.
|
|
5076
|
-
if (
|
|
5420
|
+
if (currentDrmInfo && this.drmEngine_.getMediaKeys()) {
|
|
5077
5421
|
for (const variant of manifest.variants) {
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
const drmInfos = videoDrmInfos.concat(audioDrmInfos);
|
|
5081
|
-
for (const drmInfo of drmInfos) {
|
|
5082
|
-
// Ignore any data for different key systems.
|
|
5083
|
-
if (drmInfo.keySystem == curDrmInfo.keySystem) {
|
|
5084
|
-
for (const initData of (drmInfo.initData || [])) {
|
|
5085
|
-
this.drmEngine_.newInitData(
|
|
5086
|
-
initData.initDataType, initData.initData);
|
|
5087
|
-
}
|
|
5088
|
-
}
|
|
5089
|
-
}
|
|
5422
|
+
this.processDrmInfos_(currentDrmInfo.keySystem, variant.video);
|
|
5423
|
+
this.processDrmInfos_(currentDrmInfo.keySystem, variant.audio);
|
|
5090
5424
|
}
|
|
5091
5425
|
}
|
|
5092
|
-
|
|
5093
5426
|
this.checkRestrictedVariants_(manifest);
|
|
5094
5427
|
}
|
|
5095
5428
|
|
|
5429
|
+
/**
|
|
5430
|
+
* @param {string} keySystem
|
|
5431
|
+
* @param {?shaka.extern.Stream} stream
|
|
5432
|
+
* @private
|
|
5433
|
+
*/
|
|
5434
|
+
processDrmInfos_(keySystem, stream) {
|
|
5435
|
+
if (!stream) {
|
|
5436
|
+
return;
|
|
5437
|
+
}
|
|
5438
|
+
|
|
5439
|
+
for (const drmInfo of stream.drmInfos) {
|
|
5440
|
+
// Ignore any data for different key systems.
|
|
5441
|
+
if (drmInfo.keySystem == keySystem) {
|
|
5442
|
+
for (const initData of (drmInfo.initData || [])) {
|
|
5443
|
+
this.drmEngine_.newInitData(
|
|
5444
|
+
initData.initDataType, initData.initData);
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
|
|
5096
5450
|
/**
|
|
5097
5451
|
* @private
|
|
5098
5452
|
*/
|
|
@@ -5468,38 +5822,62 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5468
5822
|
/**
|
|
5469
5823
|
* Check if we should show text on screen automatically.
|
|
5470
5824
|
*
|
|
5471
|
-
* The text should automatically be shown if the text is language-compatible
|
|
5472
|
-
* with the user's text language preference, but not compatible with the
|
|
5473
|
-
* audio.
|
|
5474
|
-
*
|
|
5475
|
-
* For example:
|
|
5476
|
-
* preferred | chosen | chosen |
|
|
5477
|
-
* text | text | audio | show
|
|
5478
|
-
* -----------------------------------
|
|
5479
|
-
* en-CA | en | jp | true
|
|
5480
|
-
* en | en-US | fr | true
|
|
5481
|
-
* fr-CA | en-US | jp | false
|
|
5482
|
-
* en-CA | en-US | en-US | false
|
|
5483
|
-
*
|
|
5484
5825
|
* @param {shaka.extern.Stream} audioStream
|
|
5485
5826
|
* @param {shaka.extern.Stream} textStream
|
|
5486
5827
|
* @return {boolean}
|
|
5487
5828
|
* @private
|
|
5488
5829
|
*/
|
|
5489
5830
|
shouldInitiallyShowText_(audioStream, textStream) {
|
|
5831
|
+
const AutoShowText = shaka.config.AutoShowText;
|
|
5832
|
+
|
|
5833
|
+
if (this.config_.autoShowText == AutoShowText.NEVER) {
|
|
5834
|
+
return false;
|
|
5835
|
+
}
|
|
5836
|
+
if (this.config_.autoShowText == AutoShowText.ALWAYS) {
|
|
5837
|
+
return true;
|
|
5838
|
+
}
|
|
5839
|
+
|
|
5490
5840
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
5491
5841
|
|
|
5492
5842
|
/** @type {string} */
|
|
5493
5843
|
const preferredTextLocale =
|
|
5494
5844
|
LanguageUtils.normalize(this.config_.preferredTextLanguage);
|
|
5495
5845
|
/** @type {string} */
|
|
5496
|
-
const audioLocale = LanguageUtils.normalize(audioStream.language);
|
|
5497
|
-
/** @type {string} */
|
|
5498
5846
|
const textLocale = LanguageUtils.normalize(textStream.language);
|
|
5499
5847
|
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5848
|
+
if (this.config_.autoShowText == AutoShowText.IF_PREFERRED_TEXT_LANGUAGE) {
|
|
5849
|
+
// Only the text language match matters.
|
|
5850
|
+
return LanguageUtils.areLanguageCompatible(
|
|
5851
|
+
textLocale,
|
|
5852
|
+
preferredTextLocale);
|
|
5853
|
+
}
|
|
5854
|
+
|
|
5855
|
+
if (this.config_.autoShowText == AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED) {
|
|
5856
|
+
/* The text should automatically be shown if the text is
|
|
5857
|
+
* language-compatible with the user's text language preference, but not
|
|
5858
|
+
* compatible with the audio. These are cases where we deduce that
|
|
5859
|
+
* subtitles may be needed.
|
|
5860
|
+
*
|
|
5861
|
+
* For example:
|
|
5862
|
+
* preferred | chosen | chosen |
|
|
5863
|
+
* text | text | audio | show
|
|
5864
|
+
* -----------------------------------
|
|
5865
|
+
* en-CA | en | jp | true
|
|
5866
|
+
* en | en-US | fr | true
|
|
5867
|
+
* fr-CA | en-US | jp | false
|
|
5868
|
+
* en-CA | en-US | en-US | false
|
|
5869
|
+
*
|
|
5870
|
+
*/
|
|
5871
|
+
/** @type {string} */
|
|
5872
|
+
const audioLocale = LanguageUtils.normalize(audioStream.language);
|
|
5873
|
+
|
|
5874
|
+
return (
|
|
5875
|
+
LanguageUtils.areLanguageCompatible(textLocale, preferredTextLocale) &&
|
|
5876
|
+
!LanguageUtils.areLanguageCompatible(audioLocale, textLocale));
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
shaka.log.alwaysWarn('Invalid autoShowText setting!');
|
|
5880
|
+
return false;
|
|
5503
5881
|
}
|
|
5504
5882
|
|
|
5505
5883
|
/**
|
|
@@ -5518,7 +5896,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5518
5896
|
*
|
|
5519
5897
|
* @private
|
|
5520
5898
|
*/
|
|
5521
|
-
onSegmentAppended_() {
|
|
5899
|
+
onSegmentAppended_(start, end, contentType) {
|
|
5522
5900
|
// When we append a segment to media source (via streaming engine) we are
|
|
5523
5901
|
// changing what data we have buffered, so notify the playhead of the
|
|
5524
5902
|
// change.
|
|
@@ -5526,6 +5904,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5526
5904
|
this.playhead_.notifyOfBufferingChange();
|
|
5527
5905
|
}
|
|
5528
5906
|
this.pollBufferState_();
|
|
5907
|
+
|
|
5908
|
+
// Dispatch an event for users to consume, too.
|
|
5909
|
+
const data = new Map()
|
|
5910
|
+
.set('start', start)
|
|
5911
|
+
.set('end', end)
|
|
5912
|
+
.set('contentType', contentType);
|
|
5913
|
+
this.dispatchEvent(this.makeEvent_(
|
|
5914
|
+
shaka.util.FakeEvent.EventName.SegmentAppended, data));
|
|
5529
5915
|
}
|
|
5530
5916
|
|
|
5531
5917
|
/**
|
|
@@ -5571,6 +5957,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5571
5957
|
const data = new Map()
|
|
5572
5958
|
.set('oldTrack', from)
|
|
5573
5959
|
.set('newTrack', to);
|
|
5960
|
+
if (this.lcevcDil_) {
|
|
5961
|
+
this.lcevcDil_.updateVariant(to);
|
|
5962
|
+
}
|
|
5574
5963
|
const event =
|
|
5575
5964
|
this.makeEvent_(shaka.util.FakeEvent.EventName.Adaptation, data);
|
|
5576
5965
|
this.delayDispatchEvent_(event);
|
|
@@ -5599,6 +5988,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5599
5988
|
const data = new Map()
|
|
5600
5989
|
.set('oldTrack', from)
|
|
5601
5990
|
.set('newTrack', to);
|
|
5991
|
+
if (this.lcevcDil_) {
|
|
5992
|
+
this.lcevcDil_.updateVariant(to);
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5602
5995
|
const event =
|
|
5603
5996
|
this.makeEvent_(shaka.util.FakeEvent.EventName.VariantChanged, data);
|
|
5604
5997
|
this.delayDispatchEvent_(event);
|
|
@@ -6543,7 +6936,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6543
6936
|
}
|
|
6544
6937
|
};
|
|
6545
6938
|
|
|
6546
|
-
|
|
6547
6939
|
/**
|
|
6548
6940
|
* In order to know what method of loading the player used for some content, we
|
|
6549
6941
|
* have this enum. It lets us know if content has not been loaded, loaded with
|
|
@@ -6582,7 +6974,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6582
6974
|
* @define {string} A version number taken from git at compile time.
|
|
6583
6975
|
* @export
|
|
6584
6976
|
*/
|
|
6585
|
-
shaka.Player.version = 'v4.
|
|
6977
|
+
shaka.Player.version = 'v4.3.0-uncompiled';
|
|
6586
6978
|
|
|
6587
6979
|
// Initialize the deprecation system using the version string we just set
|
|
6588
6980
|
// on the player.
|