shaka-player 3.2.2 → 3.3.2
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/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +6 -5
- package/CHANGELOG.md +314 -1
- package/CONTRIBUTORS +4 -2
- package/build/checkversion.py +1 -1
- package/build/conformance.textproto +183 -169
- package/build/test.py +6 -0
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +18 -0
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +19 -16
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +19 -16
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +21 -14
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +11 -9
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -9
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.externs.js +293 -17
- package/dist/shaka-player.compiled.debug.js +481 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +293 -17
- package/dist/shaka-player.compiled.js +846 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +250 -30
- package/dist/shaka-player.ui.debug.d.ts +250 -30
- package/dist/shaka-player.ui.debug.externs.js +322 -28
- package/dist/shaka-player.ui.debug.js +630 -553
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +322 -28
- package/dist/shaka-player.ui.js +1048 -1006
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +19 -15
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +1 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +8 -4
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +79 -33
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +116 -23
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +274 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +47 -6
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +85 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +401 -293
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +254 -27
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/ttml_text_parser.js +109 -60
- package/lib/text/ui_text_displayer.js +19 -13
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/shaka-player.uncompiled.js +3 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +144 -1
- package/test/dash/dash_parser_manifest_unit.js +37 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +76 -0
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/quality_observer_unit.js +142 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +140 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +544 -228
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +36 -30
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +1 -0
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +44 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +12 -36
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +5 -0
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
|
@@ -8,16 +8,19 @@ goog.provide('shaka.media.RegionObserver');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.media.IPlayheadObserver');
|
|
10
10
|
goog.require('shaka.media.RegionTimeline');
|
|
11
|
+
goog.require('shaka.util.EventManager');
|
|
12
|
+
goog.require('shaka.util.FakeEvent');
|
|
13
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* The region observer watches a region timeline and playhead, and fires events
|
|
15
|
-
* (
|
|
18
|
+
* ('enter', 'exit', 'skip') as the playhead moves.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.media.IPlayheadObserver}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionObserver = class {
|
|
23
|
+
shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* Create a region observer for the given timeline. The observer does not
|
|
23
26
|
* own the timeline, only uses it. This means that the observer should NOT
|
|
@@ -26,6 +29,8 @@ shaka.media.RegionObserver = class {
|
|
|
26
29
|
* @param {!shaka.media.RegionTimeline} timeline
|
|
27
30
|
*/
|
|
28
31
|
constructor(timeline) {
|
|
32
|
+
super();
|
|
33
|
+
|
|
29
34
|
/** @private {shaka.media.RegionTimeline} */
|
|
30
35
|
this.timeline_ = timeline;
|
|
31
36
|
|
|
@@ -39,13 +44,6 @@ shaka.media.RegionObserver = class {
|
|
|
39
44
|
*/
|
|
40
45
|
this.oldPosition_ = new Map();
|
|
41
46
|
|
|
42
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
43
|
-
this.onEnter_ = (region, seeking) => {};
|
|
44
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
45
|
-
this.onExit_ = (region, seeking) => {};
|
|
46
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
47
|
-
this.onSkip_ = (region, seeking) => {};
|
|
48
|
-
|
|
49
47
|
// To make the rules easier to read, alias all the relative positions.
|
|
50
48
|
const RelativePosition = shaka.media.RegionObserver.RelativePosition_;
|
|
51
49
|
const BEFORE_THE_REGION = RelativePosition.BEFORE_THE_REGION;
|
|
@@ -62,39 +60,48 @@ shaka.media.RegionObserver = class {
|
|
|
62
60
|
{
|
|
63
61
|
weWere: null,
|
|
64
62
|
weAre: IN_THE_REGION,
|
|
65
|
-
invoke: (region, seeking) => this.
|
|
63
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
66
64
|
},
|
|
67
65
|
{
|
|
68
66
|
weWere: BEFORE_THE_REGION,
|
|
69
67
|
weAre: IN_THE_REGION,
|
|
70
|
-
invoke: (region, seeking) => this.
|
|
68
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
71
69
|
},
|
|
72
70
|
{
|
|
73
71
|
weWere: AFTER_THE_REGION,
|
|
74
72
|
weAre: IN_THE_REGION,
|
|
75
|
-
invoke: (region, seeking) => this.
|
|
73
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
76
74
|
},
|
|
77
75
|
{
|
|
78
76
|
weWere: IN_THE_REGION,
|
|
79
77
|
weAre: BEFORE_THE_REGION,
|
|
80
|
-
invoke: (region, seeking) => this.
|
|
78
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
81
79
|
},
|
|
82
80
|
{
|
|
83
81
|
weWere: IN_THE_REGION,
|
|
84
82
|
weAre: AFTER_THE_REGION,
|
|
85
|
-
invoke: (region, seeking) => this.
|
|
83
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
86
84
|
},
|
|
87
85
|
{
|
|
88
86
|
weWere: BEFORE_THE_REGION,
|
|
89
87
|
weAre: AFTER_THE_REGION,
|
|
90
|
-
invoke: (region, seeking) => this.
|
|
88
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
91
89
|
},
|
|
92
90
|
{
|
|
93
91
|
weWere: AFTER_THE_REGION,
|
|
94
92
|
weAre: BEFORE_THE_REGION,
|
|
95
|
-
invoke: (region, seeking) => this.
|
|
93
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
96
94
|
},
|
|
97
95
|
];
|
|
96
|
+
|
|
97
|
+
/** @private {shaka.util.EventManager} */
|
|
98
|
+
this.eventManager_ = new shaka.util.EventManager();
|
|
99
|
+
|
|
100
|
+
this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
|
|
101
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
102
|
+
const region = event['region'];
|
|
103
|
+
this.oldPosition_.delete(region);
|
|
104
|
+
});
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
/** @override */
|
|
@@ -105,11 +112,10 @@ shaka.media.RegionObserver = class {
|
|
|
105
112
|
// needed.
|
|
106
113
|
this.oldPosition_.clear();
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.onSkip_ = (region, seeking) => {};
|
|
115
|
+
this.eventManager_.release();
|
|
116
|
+
this.eventManager_ = null;
|
|
117
|
+
|
|
118
|
+
super.release();
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/** @override */
|
|
@@ -134,20 +140,20 @@ shaka.media.RegionObserver = class {
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
/**
|
|
137
|
-
*
|
|
143
|
+
* Dispatch events of the given type. All event types in this class have the
|
|
144
|
+
* same parameters: region and seeking.
|
|
138
145
|
*
|
|
139
|
-
* @param {
|
|
140
|
-
*
|
|
141
|
-
* @param {
|
|
142
|
-
*
|
|
143
|
-
* @param {shaka.media.RegionObserver.EventListener} onSkip
|
|
144
|
-
* The callback for when we move from before to after a region or from
|
|
145
|
-
* after to before a region.
|
|
146
|
+
* @param {string} eventType
|
|
147
|
+
* @param {shaka.extern.TimelineRegionInfo} region
|
|
148
|
+
* @param {boolean} seeking
|
|
149
|
+
* @private
|
|
146
150
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
onEvent_(eventType, region, seeking) {
|
|
152
|
+
const event = new shaka.util.FakeEvent(eventType, new Map([
|
|
153
|
+
['region', region],
|
|
154
|
+
['seeking', seeking],
|
|
155
|
+
]));
|
|
156
|
+
this.dispatchEvent(event);
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/**
|
|
@@ -6,26 +6,30 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.media.RegionTimeline');
|
|
8
8
|
|
|
9
|
+
goog.require('shaka.util.FakeEvent');
|
|
10
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
9
11
|
goog.require('shaka.util.IReleasable');
|
|
10
12
|
goog.require('shaka.util.Timer');
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The region timeline is a set of unique timeline region info entries. When
|
|
15
|
-
* a new entry is added, the
|
|
17
|
+
* a new entry is added, the 'regionadd' event will be fired. When an entry is
|
|
18
|
+
* deleted, the 'regionremove' event will be fired.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.util.IReleasable}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionTimeline = class {
|
|
23
|
+
shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* @param {!function():{start: number, end: number}} getSeekRange
|
|
23
26
|
*/
|
|
24
27
|
constructor(getSeekRange) {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
super();
|
|
29
|
+
|
|
27
30
|
/** @private {!Set.<shaka.extern.TimelineRegionInfo>} */
|
|
28
31
|
this.regions_ = new Set();
|
|
32
|
+
|
|
29
33
|
/** @private {!function():{start: number, end: number}} */
|
|
30
34
|
this.getSeekRange_ = getSeekRange;
|
|
31
35
|
|
|
@@ -44,22 +48,9 @@ shaka.media.RegionTimeline = class {
|
|
|
44
48
|
|
|
45
49
|
/** @override */
|
|
46
50
|
release() {
|
|
47
|
-
// Prevent us from holding onto any external references via the callback.
|
|
48
|
-
this.onAddRegion_ = (region) => {};
|
|
49
51
|
this.regions_.clear();
|
|
50
52
|
this.filterTimer_.stop();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Set the callbacks for events. This will override any previous calls to
|
|
55
|
-
* |setListeners|.
|
|
56
|
-
*
|
|
57
|
-
* @param {function(shaka.extern.TimelineRegionInfo)} onAddRegion
|
|
58
|
-
* Set the callback for when we add a new region. This callback will only
|
|
59
|
-
* be called when a region is unique (we reject duplicate regions).
|
|
60
|
-
*/
|
|
61
|
-
setListeners(onAddRegion) {
|
|
62
|
-
this.onAddRegion_ = onAddRegion;
|
|
53
|
+
super.release();
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
/**
|
|
@@ -72,7 +63,10 @@ shaka.media.RegionTimeline = class {
|
|
|
72
63
|
// instead of making the parser track it.
|
|
73
64
|
if (similarRegion == null) {
|
|
74
65
|
this.regions_.add(region);
|
|
75
|
-
|
|
66
|
+
const event = new shaka.util.FakeEvent('regionadd', new Map([
|
|
67
|
+
['region', region],
|
|
68
|
+
]));
|
|
69
|
+
this.dispatchEvent(event);
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
|
|
@@ -89,6 +83,10 @@ shaka.media.RegionTimeline = class {
|
|
|
89
83
|
// reson to store or process them.
|
|
90
84
|
if (region.endTime < seekRange.start) {
|
|
91
85
|
this.regions_.delete(region);
|
|
86
|
+
const event = new shaka.util.FakeEvent('regionremove', new Map([
|
|
87
|
+
['region', region],
|
|
88
|
+
]));
|
|
89
|
+
this.dispatchEvent(event);
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
@@ -347,7 +347,8 @@ shaka.media.SegmentIndex = class {
|
|
|
347
347
|
lastReference.appendWindowStart,
|
|
348
348
|
lastReference.appendWindowEnd,
|
|
349
349
|
lastReference.partialReferences,
|
|
350
|
-
lastReference.tilesLayout
|
|
350
|
+
lastReference.tilesLayout,
|
|
351
|
+
lastReference.tileDuration);
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
|
|
@@ -427,6 +428,12 @@ shaka.media.SegmentIndex = class {
|
|
|
427
428
|
return new shaka.media.SegmentIterator(this, index, partialSegmentIndex);
|
|
428
429
|
}
|
|
429
430
|
|
|
431
|
+
/**
|
|
432
|
+
* @return {boolean}
|
|
433
|
+
*/
|
|
434
|
+
isEmpty() {
|
|
435
|
+
return this.references.length == 0;
|
|
436
|
+
}
|
|
430
437
|
|
|
431
438
|
/**
|
|
432
439
|
* Create a SegmentIndex for a single segment of the given start time and
|
|
@@ -23,11 +23,13 @@ shaka.media.InitSegmentReference = class {
|
|
|
23
23
|
* of the resource containing the segment.
|
|
24
24
|
* @param {number} startByte The offset from the start of the resource to the
|
|
25
25
|
* start of the segment.
|
|
26
|
-
* @param {?number} endByte The offset from the start of the resource
|
|
27
|
-
* end of the segment, inclusive. A value of null indicates that the
|
|
26
|
+
* @param {?number} endByte The offset from the start of the resource
|
|
27
|
+
* to the end of the segment, inclusive. A value of null indicates that the
|
|
28
28
|
* segment extends to the end of the resource.
|
|
29
|
+
* @param {null|shaka.extern.MediaQualityInfo=} mediaQuality Information about
|
|
30
|
+
* the quality of the media associated with this init segment.
|
|
29
31
|
*/
|
|
30
|
-
constructor(uris, startByte, endByte) {
|
|
32
|
+
constructor(uris, startByte, endByte, mediaQuality = null) {
|
|
31
33
|
/** @type {function():!Array.<string>} */
|
|
32
34
|
this.getUris = uris;
|
|
33
35
|
|
|
@@ -36,6 +38,9 @@ shaka.media.InitSegmentReference = class {
|
|
|
36
38
|
|
|
37
39
|
/** @const {?number} */
|
|
38
40
|
this.endByte = endByte;
|
|
41
|
+
|
|
42
|
+
/** @const {shaka.extern.MediaQualityInfo|null} */
|
|
43
|
+
this.mediaQuality = mediaQuality;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -73,6 +78,16 @@ shaka.media.InitSegmentReference = class {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Returns media quality information for the segments associated with
|
|
83
|
+
* this init segment.
|
|
84
|
+
*
|
|
85
|
+
* @return {?shaka.extern.MediaQualityInfo}
|
|
86
|
+
*/
|
|
87
|
+
getMediaQuality() {
|
|
88
|
+
return this.mediaQuality;
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* Check if two initSegmentReference have all the same values.
|
|
78
93
|
* @param {?shaka.media.InitSegmentReference} reference1
|
|
@@ -140,11 +155,15 @@ shaka.media.SegmentReference = class {
|
|
|
140
155
|
* The value is a grid-item-dimension consisting of two positive decimal
|
|
141
156
|
* integers in the format: column-x-row ('4x3'). It describes the
|
|
142
157
|
* arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
|
|
158
|
+
* @param {?number=} tileDuration
|
|
159
|
+
* The explicit duration of an individual tile within the tiles grid.
|
|
160
|
+
* If not provided, the duration should be automatically calculated based on
|
|
161
|
+
* the duration of the reference.
|
|
143
162
|
*/
|
|
144
163
|
constructor(
|
|
145
164
|
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
|
146
165
|
timestampOffset, appendWindowStart, appendWindowEnd,
|
|
147
|
-
partialReferences = [], tilesLayout = '') {
|
|
166
|
+
partialReferences = [], tilesLayout = '', tileDuration = null) {
|
|
148
167
|
// A preload hinted Partial Segment has the same startTime and endTime.
|
|
149
168
|
goog.asserts.assert(startTime <= endTime,
|
|
150
169
|
'startTime must be less than or equal to endTime');
|
|
@@ -191,6 +210,9 @@ shaka.media.SegmentReference = class {
|
|
|
191
210
|
|
|
192
211
|
/** @type {?string} */
|
|
193
212
|
this.tilesLayout = tilesLayout;
|
|
213
|
+
|
|
214
|
+
/** @type {?number} */
|
|
215
|
+
this.tileDuration = tileDuration;
|
|
194
216
|
}
|
|
195
217
|
|
|
196
218
|
/**
|
|
@@ -275,6 +297,17 @@ shaka.media.SegmentReference = class {
|
|
|
275
297
|
getTilesLayout() {
|
|
276
298
|
return this.tilesLayout;
|
|
277
299
|
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Returns the segment's explicit tile duration.
|
|
303
|
+
* Only defined in image segments.
|
|
304
|
+
*
|
|
305
|
+
* @return {?number}
|
|
306
|
+
* @export
|
|
307
|
+
*/
|
|
308
|
+
getTileDuration() {
|
|
309
|
+
return this.tileDuration;
|
|
310
|
+
}
|
|
278
311
|
};
|
|
279
312
|
|
|
280
313
|
|
|
@@ -431,6 +431,11 @@ shaka.media.StreamingEngine = class {
|
|
|
431
431
|
this.playerInterface_.mediaSourceEngine.reinitText(fullMimeType);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
// Releases the segmentIndex of the old stream.
|
|
435
|
+
if (mediaState.stream.closeSegmentIndex) {
|
|
436
|
+
mediaState.stream.closeSegmentIndex();
|
|
437
|
+
}
|
|
438
|
+
|
|
434
439
|
mediaState.stream = stream;
|
|
435
440
|
mediaState.segmentIterator = null;
|
|
436
441
|
|
|
@@ -623,7 +628,7 @@ shaka.media.StreamingEngine = class {
|
|
|
623
628
|
|
|
624
629
|
// Don't clear the buffer unless something is buffered. This extra
|
|
625
630
|
// check prevents extra, useless calls to clear the buffer.
|
|
626
|
-
if (somethingBuffered) {
|
|
631
|
+
if (somethingBuffered || mediaState.performingUpdate) {
|
|
627
632
|
this.forceClearBuffer_(mediaState);
|
|
628
633
|
streamCleared = true;
|
|
629
634
|
}
|
|
@@ -863,7 +868,7 @@ shaka.media.StreamingEngine = class {
|
|
|
863
868
|
return;
|
|
864
869
|
}
|
|
865
870
|
|
|
866
|
-
// Make sure the segment index exists.
|
|
871
|
+
// Make sure the segment index exists. If not, create the segment index.
|
|
867
872
|
if (!mediaState.stream.segmentIndex) {
|
|
868
873
|
const thisStream = mediaState.stream;
|
|
869
874
|
|
|
@@ -873,6 +878,12 @@ shaka.media.StreamingEngine = class {
|
|
|
873
878
|
// We switched streams while in the middle of this async call to
|
|
874
879
|
// createSegmentIndex. Abandon this update and schedule a new one if
|
|
875
880
|
// there's not already one pending.
|
|
881
|
+
// Releases the segmentIndex of the old stream.
|
|
882
|
+
if (thisStream.closeSegmentIndex) {
|
|
883
|
+
goog.asserts.assert(!mediaState.stream.segmentIndex,
|
|
884
|
+
'mediastate.stream should not have segmentIndex yet.');
|
|
885
|
+
thisStream.closeSegmentIndex();
|
|
886
|
+
}
|
|
876
887
|
if (mediaState.updateTimer == null) {
|
|
877
888
|
this.scheduleUpdate_(mediaState, 0);
|
|
878
889
|
}
|
|
@@ -977,7 +988,10 @@ shaka.media.StreamingEngine = class {
|
|
|
977
988
|
unscaledBufferingGoal * this.bufferingGoalScale_;
|
|
978
989
|
|
|
979
990
|
// Check if we've buffered to the end of the presentation.
|
|
980
|
-
|
|
991
|
+
const timeUntilEnd =
|
|
992
|
+
this.manifest_.presentationTimeline.getDuration() - timeNeeded;
|
|
993
|
+
const oneMicrosecond = 1e-6;
|
|
994
|
+
if (timeUntilEnd < oneMicrosecond) {
|
|
981
995
|
// We shouldn't rebuffer if the playhead is close to the end of the
|
|
982
996
|
// presentation.
|
|
983
997
|
shaka.log.debug(logPrefix, 'buffered to end of presentation');
|
|
@@ -1530,6 +1544,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1530
1544
|
throw error;
|
|
1531
1545
|
}
|
|
1532
1546
|
};
|
|
1547
|
+
this.playerInterface_.onInitSegmentAppended(
|
|
1548
|
+
reference.startTime, reference.initSegmentReference);
|
|
1533
1549
|
operations.push(append());
|
|
1534
1550
|
}
|
|
1535
1551
|
}
|
|
@@ -1555,7 +1571,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1555
1571
|
|
|
1556
1572
|
const hasClosedCaptions = stream.closedCaptions &&
|
|
1557
1573
|
stream.closedCaptions.size > 0;
|
|
1558
|
-
if (stream.emsgSchemeIdUris != null &&
|
|
1574
|
+
if ((stream.emsgSchemeIdUris != null &&
|
|
1575
|
+
stream.emsgSchemeIdUris.length > 0) ||
|
|
1576
|
+
this.config_.dispatchAllEmsgBoxes) {
|
|
1559
1577
|
new shaka.util.Mp4Parser()
|
|
1560
1578
|
.fullBox(
|
|
1561
1579
|
'emsg',
|
|
@@ -1641,7 +1659,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1641
1659
|
// See DASH sec. 5.10.3.3.1
|
|
1642
1660
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1643
1661
|
// defined in MPD, the client is expected to ignore it.
|
|
1644
|
-
if (emsgSchemeIdUris.includes(schemeId))
|
|
1662
|
+
if ((emsgSchemeIdUris && emsgSchemeIdUris.includes(schemeId)) ||
|
|
1663
|
+
this.config_.dispatchAllEmsgBoxes) {
|
|
1645
1664
|
// See DASH sec. 5.10.4.1
|
|
1646
1665
|
// A special scheme in DASH used to signal manifest updates.
|
|
1647
1666
|
if (schemeId == 'urn:mpeg:dash:event:2012') {
|
|
@@ -1764,6 +1783,19 @@ shaka.media.StreamingEngine = class {
|
|
|
1764
1783
|
|
|
1765
1784
|
shaka.log.v2('fetching: reference=', reference);
|
|
1766
1785
|
|
|
1786
|
+
const stream = mediaState.stream;
|
|
1787
|
+
this.playerInterface_.modifySegmentRequest(
|
|
1788
|
+
request,
|
|
1789
|
+
{
|
|
1790
|
+
type: stream.type,
|
|
1791
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
1792
|
+
duration: reference.endTime - reference.startTime,
|
|
1793
|
+
mimeType: stream.mimeType,
|
|
1794
|
+
codecs: stream.codecs,
|
|
1795
|
+
bandwidth: stream.bandwidth,
|
|
1796
|
+
},
|
|
1797
|
+
);
|
|
1798
|
+
|
|
1767
1799
|
const op = this.playerInterface_.netEngine.request(requestType, request);
|
|
1768
1800
|
mediaState.operation = op;
|
|
1769
1801
|
const response = await op.promise;
|
|
@@ -1927,12 +1959,15 @@ shaka.media.StreamingEngine = class {
|
|
|
1927
1959
|
* @typedef {{
|
|
1928
1960
|
* getPresentationTime: function():number,
|
|
1929
1961
|
* getBandwidthEstimate: function():number,
|
|
1962
|
+
* modifySegmentRequest: function(shaka.extern.Request,
|
|
1963
|
+
* shaka.util.CmcdManager.SegmentInfo),
|
|
1930
1964
|
* mediaSourceEngine: !shaka.media.MediaSourceEngine,
|
|
1931
1965
|
* netEngine: shaka.net.NetworkingEngine,
|
|
1932
1966
|
* onError: function(!shaka.util.Error),
|
|
1933
1967
|
* onEvent: function(!Event),
|
|
1934
1968
|
* onManifestUpdate: function(),
|
|
1935
|
-
* onSegmentAppended: function()
|
|
1969
|
+
* onSegmentAppended: function(),
|
|
1970
|
+
* onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference)
|
|
1936
1971
|
* }}
|
|
1937
1972
|
*
|
|
1938
1973
|
* @property {function():number} getPresentationTime
|
|
@@ -1940,6 +1975,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1940
1975
|
* viewer is seeing on screen right now.
|
|
1941
1976
|
* @property {function():number} getBandwidthEstimate
|
|
1942
1977
|
* Get the estimated bandwidth in bits per second.
|
|
1978
|
+
* @property {function(shaka.extern.Request,
|
|
1979
|
+
* shaka.extern.Cmcd.SegmentInfo)} modifySegmentRequest
|
|
1980
|
+
* The request modifier
|
|
1943
1981
|
* @property {!shaka.media.MediaSourceEngine} mediaSourceEngine
|
|
1944
1982
|
* The MediaSourceEngine. The caller retains ownership.
|
|
1945
1983
|
* @property {shaka.net.NetworkingEngine} netEngine
|
|
@@ -1954,6 +1992,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1954
1992
|
* Called when an embedded 'emsg' box should trigger a manifest update.
|
|
1955
1993
|
* @property {function()} onSegmentAppended
|
|
1956
1994
|
* Called after a segment is successfully appended to a MediaSource.
|
|
1995
|
+
* @property
|
|
1996
|
+
* {function(!number, !shaka.media.InitSegmentReference)} onInitSegmentAppended
|
|
1997
|
+
* Called when an init segment is appended to a MediaSource.
|
|
1957
1998
|
*/
|
|
1958
1999
|
shaka.media.StreamingEngine.PlayerInterface;
|
|
1959
2000
|
|
|
@@ -27,10 +27,12 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
27
27
|
* @param {shaka.net.NetworkingEngine.RequestType} requestType
|
|
28
28
|
* @param {shaka.extern.ProgressUpdated} progressUpdated Called when a
|
|
29
29
|
* progress event happened.
|
|
30
|
+
* @param {shaka.extern.HeadersReceived} headersReceived Called when the
|
|
31
|
+
* headers for the download are received, but before the body is.
|
|
30
32
|
* @return {!shaka.extern.IAbortableOperation.<shaka.extern.Response>}
|
|
31
33
|
* @export
|
|
32
34
|
*/
|
|
33
|
-
static parse(uri, request, requestType, progressUpdated) {
|
|
35
|
+
static parse(uri, request, requestType, progressUpdated, headersReceived) {
|
|
34
36
|
const headers = new shaka.net.HttpFetchPlugin.Headers_();
|
|
35
37
|
shaka.util.MapUtils.asMap(request.headers).forEach((value, key) => {
|
|
36
38
|
headers.append(key, value);
|
|
@@ -55,7 +57,7 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
const pendingRequest = shaka.net.HttpFetchPlugin.request_(
|
|
58
|
-
uri, requestType, init, abortStatus, progressUpdated,
|
|
60
|
+
uri, requestType, init, abortStatus, progressUpdated, headersReceived,
|
|
59
61
|
request.streamDataCallback);
|
|
60
62
|
|
|
61
63
|
/** @type {!shaka.util.AbortableOperation} */
|
|
@@ -92,12 +94,13 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
92
94
|
* @param {!RequestInit} init
|
|
93
95
|
* @param {shaka.net.HttpFetchPlugin.AbortStatus} abortStatus
|
|
94
96
|
* @param {shaka.extern.ProgressUpdated} progressUpdated
|
|
97
|
+
* @param {shaka.extern.HeadersReceived} headersReceived
|
|
95
98
|
* @param {?function(BufferSource):!Promise} streamDataCallback
|
|
96
99
|
* @return {!Promise<!shaka.extern.Response>}
|
|
97
100
|
* @private
|
|
98
101
|
*/
|
|
99
102
|
static async request_(uri, requestType, init, abortStatus, progressUpdated,
|
|
100
|
-
streamDataCallback) {
|
|
103
|
+
headersReceived, streamDataCallback) {
|
|
101
104
|
const fetch = shaka.net.HttpFetchPlugin.fetch_;
|
|
102
105
|
const ReadableStream = shaka.net.HttpFetchPlugin.ReadableStream_;
|
|
103
106
|
let response;
|
|
@@ -113,6 +116,10 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
113
116
|
// headers are available. The download itself isn't done until the promise
|
|
114
117
|
// for retrieving the data (arrayBuffer, blob, etc) has resolved.
|
|
115
118
|
response = await fetch(uri, init);
|
|
119
|
+
// At this point in the process, we have the headers of the response, but
|
|
120
|
+
// not the body yet.
|
|
121
|
+
headersReceived(shaka.net.HttpFetchPlugin.headersToGenericObject_(
|
|
122
|
+
response.headers));
|
|
116
123
|
// Getting the reader in this way allows us to observe the process of
|
|
117
124
|
// downloading the body, instead of just waiting for an opaque promise to
|
|
118
125
|
// resolve.
|
|
@@ -194,19 +201,28 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
194
201
|
}
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
const headers =
|
|
198
|
-
|
|
199
|
-
const responseHeaders = response.headers;
|
|
200
|
-
responseHeaders.forEach((value, key) => {
|
|
201
|
-
// Since Edge incorrectly return the header with a leading new line
|
|
202
|
-
// character ('\n'), we trim the header here.
|
|
203
|
-
headers[key.trim()] = value;
|
|
204
|
-
});
|
|
204
|
+
const headers = shaka.net.HttpFetchPlugin.headersToGenericObject_(
|
|
205
|
+
response.headers);
|
|
205
206
|
|
|
206
207
|
return shaka.net.HttpPluginUtils.makeResponse(
|
|
207
208
|
headers, arrayBuffer, response.status, uri, response.url, requestType);
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @param {!Headers} headers
|
|
213
|
+
* @return {!Object.<string, string>}
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
static headersToGenericObject_(headers) {
|
|
217
|
+
const headersObj = {};
|
|
218
|
+
headers.forEach((value, key) => {
|
|
219
|
+
// Since Edge incorrectly return the header with a leading new line
|
|
220
|
+
// character ('\n'), we trim the header here.
|
|
221
|
+
headersObj[key.trim()] = value;
|
|
222
|
+
});
|
|
223
|
+
return headersObj;
|
|
224
|
+
}
|
|
225
|
+
|
|
210
226
|
/**
|
|
211
227
|
* Determine if the Fetch API is supported in the browser. Note: this is
|
|
212
228
|
* deliberately exposed as a method to allow the client app to use the same
|
|
@@ -290,4 +306,8 @@ if (shaka.net.HttpFetchPlugin.isSupported()) {
|
|
|
290
306
|
'https', shaka.net.HttpFetchPlugin.parse,
|
|
291
307
|
shaka.net.NetworkingEngine.PluginPriority.PREFERRED,
|
|
292
308
|
/* progressSupport= */ true);
|
|
309
|
+
shaka.net.NetworkingEngine.registerScheme(
|
|
310
|
+
'blob', shaka.net.HttpFetchPlugin.parse,
|
|
311
|
+
shaka.net.NetworkingEngine.PluginPriority.PREFERRED,
|
|
312
|
+
/* progressSupport= */ true);
|
|
293
313
|
}
|
|
@@ -24,10 +24,12 @@ shaka.net.HttpXHRPlugin = class {
|
|
|
24
24
|
* @param {shaka.net.NetworkingEngine.RequestType} requestType
|
|
25
25
|
* @param {shaka.extern.ProgressUpdated} progressUpdated Called when a
|
|
26
26
|
* progress event happened.
|
|
27
|
+
* @param {shaka.extern.HeadersReceived} headersReceived Called when the
|
|
28
|
+
* headers for the download are received, but before the body is.
|
|
27
29
|
* @return {!shaka.extern.IAbortableOperation.<shaka.extern.Response>}
|
|
28
30
|
* @export
|
|
29
31
|
*/
|
|
30
|
-
static parse(uri, request, requestType, progressUpdated) {
|
|
32
|
+
static parse(uri, request, requestType, progressUpdated, headersReceived) {
|
|
31
33
|
const xhr = new shaka.net.HttpXHRPlugin.Xhr_();
|
|
32
34
|
|
|
33
35
|
// Last time stamp when we got a progress event.
|
|
@@ -48,23 +50,25 @@ shaka.net.HttpXHRPlugin = class {
|
|
|
48
50
|
shaka.util.Error.Code.OPERATION_ABORTED,
|
|
49
51
|
uri, requestType));
|
|
50
52
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/** @type {!Array.<string>} */
|
|
60
|
-
const parts = header.split(': ');
|
|
61
|
-
headers[parts[0].toLowerCase()] = parts.slice(1).join(': ');
|
|
53
|
+
let calledHeadersReceived = false;
|
|
54
|
+
xhr.onreadystatechange = (event) => {
|
|
55
|
+
// See if the readyState is 2 ("HEADERS_RECEIVED").
|
|
56
|
+
if (xhr.readyState == 2 && !calledHeadersReceived) {
|
|
57
|
+
const headers = shaka.net.HttpXHRPlugin.headersToGenericObject_(xhr);
|
|
58
|
+
headersReceived(headers);
|
|
59
|
+
// Don't send out this event twice.
|
|
60
|
+
calledHeadersReceived = true;
|
|
62
61
|
}
|
|
62
|
+
};
|
|
63
|
+
xhr.onload = (event) => {
|
|
64
|
+
const headers = shaka.net.HttpXHRPlugin.headersToGenericObject_(xhr);
|
|
65
|
+
goog.asserts.assert(xhr.response instanceof ArrayBuffer,
|
|
66
|
+
'XHR should have a response by now!');
|
|
67
|
+
const xhrResponse = xhr.response;
|
|
63
68
|
|
|
64
69
|
try {
|
|
65
70
|
const response = shaka.net.HttpPluginUtils.makeResponse(headers,
|
|
66
|
-
|
|
67
|
-
requestType);
|
|
71
|
+
xhrResponse, xhr.status, uri, xhr.responseURL, requestType);
|
|
68
72
|
resolve(response);
|
|
69
73
|
} catch (error) {
|
|
70
74
|
goog.asserts.assert(error instanceof shaka.util.Error,
|
|
@@ -116,6 +120,24 @@ shaka.net.HttpXHRPlugin = class {
|
|
|
116
120
|
return Promise.resolve();
|
|
117
121
|
});
|
|
118
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {!XMLHttpRequest} xhr
|
|
126
|
+
* @return {!Object.<string, string>}
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
static headersToGenericObject_(xhr) {
|
|
130
|
+
// Since Edge incorrectly return the header with a leading new
|
|
131
|
+
// line character ('\n'), we trim the header here.
|
|
132
|
+
const headerLines = xhr.getAllResponseHeaders().trim().split('\r\n');
|
|
133
|
+
const headers = {};
|
|
134
|
+
for (const header of headerLines) {
|
|
135
|
+
/** @type {!Array.<string>} */
|
|
136
|
+
const parts = header.split(': ');
|
|
137
|
+
headers[parts[0].toLowerCase()] = parts.slice(1).join(': ');
|
|
138
|
+
}
|
|
139
|
+
return headers;
|
|
140
|
+
}
|
|
119
141
|
};
|
|
120
142
|
|
|
121
143
|
|
|
@@ -136,4 +158,8 @@ shaka.net.NetworkingEngine.registerScheme(
|
|
|
136
158
|
'https', shaka.net.HttpXHRPlugin.parse,
|
|
137
159
|
shaka.net.NetworkingEngine.PluginPriority.FALLBACK,
|
|
138
160
|
/* progressSupport= */ true);
|
|
161
|
+
shaka.net.NetworkingEngine.registerScheme(
|
|
162
|
+
'blob', shaka.net.HttpXHRPlugin.parse,
|
|
163
|
+
shaka.net.NetworkingEngine.PluginPriority.FALLBACK,
|
|
164
|
+
/* progressSupport= */ true);
|
|
139
165
|
|