shaka-player 3.1.4 → 3.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.
Files changed (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/AUTHORS +6 -5
  5. package/CHANGELOG.md +363 -0
  6. package/CONTRIBUTORS +4 -2
  7. package/README.md +4 -4
  8. package/build/types/core +3 -0
  9. package/build/types/offline +1 -0
  10. package/build/types/polyfill +2 -0
  11. package/build/types/ui +2 -0
  12. package/demo/common/asset.js +9 -7
  13. package/demo/common/assets.js +28 -1
  14. package/demo/common/message_ids.js +15 -2
  15. package/demo/config.js +35 -2
  16. package/demo/index.html +1 -1
  17. package/demo/locales/en.json +14 -1
  18. package/demo/locales/source.json +56 -4
  19. package/demo/main.js +2 -1
  20. package/dist/controls.css +26 -2
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +24 -21
  23. package/dist/demo.compiled.debug.map +3 -3
  24. package/dist/demo.compiled.js +24 -21
  25. package/dist/demo.compiled.map +3 -3
  26. package/dist/deps.js +33 -25
  27. package/dist/locales.js +31 -29
  28. package/dist/receiver.compiled.debug.js +14 -12
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +14 -12
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +258 -20
  33. package/dist/shaka-player.compiled.debug.d.ts +258 -20
  34. package/dist/shaka-player.compiled.debug.externs.js +1005 -695
  35. package/dist/shaka-player.compiled.debug.js +720 -640
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +1005 -695
  38. package/dist/shaka-player.compiled.js +850 -817
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +274 -21
  41. package/dist/shaka-player.ui.debug.d.ts +274 -21
  42. package/dist/shaka-player.ui.debug.externs.js +766 -429
  43. package/dist/shaka-player.ui.debug.js +863 -765
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +766 -429
  46. package/dist/shaka-player.ui.js +1042 -996
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/bg-fetch-after.gv +29 -0
  49. package/docs/design/bg-fetch-after.gv.png +0 -0
  50. package/docs/design/bg-fetch-before.gv +18 -0
  51. package/docs/design/bg-fetch-before.gv.png +0 -0
  52. package/docs/design/bg-fetch.md +134 -0
  53. package/docs/tutorials/blob-url.md +15 -0
  54. package/docs/tutorials/index.json +1 -0
  55. package/docs/tutorials/ui-customization.md +66 -0
  56. package/docs/tutorials/ui.md +5 -5
  57. package/externs/aria.js +6 -0
  58. package/externs/ima.js +3 -0
  59. package/externs/shaka/ads.js +29 -1
  60. package/externs/shaka/manifest.js +18 -2
  61. package/externs/shaka/manifest_parser.js +10 -1
  62. package/externs/shaka/net.js +20 -4
  63. package/externs/shaka/offline.js +36 -2
  64. package/externs/shaka/player.js +182 -13
  65. package/externs/shaka/text.js +24 -10
  66. package/externs/texttrack.js +3 -0
  67. package/externs/webcrypto.js +18 -0
  68. package/lib/abr/ewma.js +14 -0
  69. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  70. package/lib/abr/simple_abr_manager.js +6 -0
  71. package/lib/ads/ad_manager.js +16 -15
  72. package/lib/ads/client_side_ad.js +8 -0
  73. package/lib/ads/client_side_ad_manager.js +14 -6
  74. package/lib/ads/server_side_ad.js +7 -0
  75. package/lib/ads/server_side_ad_manager.js +19 -2
  76. package/lib/cast/cast_proxy.js +8 -0
  77. package/lib/cast/cast_utils.js +7 -1
  78. package/lib/cea/cea608_data_channel.js +56 -56
  79. package/lib/cea/cea608_memory.js +33 -33
  80. package/lib/cea/cea708_service.js +43 -43
  81. package/lib/cea/cea708_window.js +38 -38
  82. package/lib/cea/cea_decoder.js +13 -14
  83. package/lib/cea/cea_utils.js +23 -23
  84. package/lib/cea/dtvcc_packet_builder.js +7 -7
  85. package/lib/cea/i_caption_decoder.js +2 -3
  86. package/lib/cea/i_cea_parser.js +2 -2
  87. package/lib/cea/mp4_cea_parser.js +6 -6
  88. package/lib/dash/dash_parser.js +53 -14
  89. package/lib/dash/segment_base.js +25 -1
  90. package/lib/dash/segment_list.js +18 -9
  91. package/lib/dash/segment_template.js +23 -10
  92. package/lib/hls/hls_parser.js +210 -30
  93. package/lib/media/closed_caption_parser.js +1 -2
  94. package/lib/media/drm_engine.js +162 -192
  95. package/lib/media/quality_observer.js +291 -0
  96. package/lib/media/segment_index.js +9 -1
  97. package/lib/media/segment_reference.js +37 -5
  98. package/lib/media/streaming_engine.js +48 -10
  99. package/lib/net/http_fetch_plugin.js +31 -11
  100. package/lib/net/http_plugin_utils.js +1 -0
  101. package/lib/net/http_xhr_plugin.js +40 -14
  102. package/lib/net/networking_engine.js +82 -18
  103. package/lib/offline/download_info.js +68 -0
  104. package/lib/offline/download_manager.js +14 -7
  105. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  106. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  107. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  108. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  109. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  110. package/lib/offline/manifest_converter.js +3 -1
  111. package/lib/offline/storage.js +404 -297
  112. package/lib/offline/stored_content_utils.js +2 -0
  113. package/lib/player.js +622 -153
  114. package/lib/polyfill/aria.js +82 -0
  115. package/lib/polyfill/random_uuid.js +52 -0
  116. package/lib/routing/walker.js +2 -3
  117. package/lib/text/cue.js +12 -0
  118. package/lib/text/srt_text_parser.js +4 -4
  119. package/lib/text/text_engine.js +2 -2
  120. package/lib/text/ttml_text_parser.js +25 -2
  121. package/lib/text/ui_text_displayer.js +2 -2
  122. package/lib/text/vtt_text_parser.js +2 -2
  123. package/lib/text/web_vtt_generator.js +55 -12
  124. package/lib/util/cmcd_manager.js +826 -0
  125. package/lib/util/error.js +12 -0
  126. package/lib/util/language_utils.js +12 -0
  127. package/lib/util/manifest_parser_utils.js +1 -0
  128. package/lib/util/mp4_box_parsers.js +20 -20
  129. package/lib/util/multi_map.js +8 -0
  130. package/lib/util/mutex.js +52 -0
  131. package/lib/util/periods.js +98 -64
  132. package/lib/util/player_configuration.js +31 -1
  133. package/lib/util/stream_utils.js +140 -85
  134. package/lib/util/xml_utils.js +31 -12
  135. package/package.json +1 -1
  136. package/roadmap.md +24 -22
  137. package/shaka-player.uncompiled.js +4 -0
  138. package/test/cast/cast_utils_unit.js +1 -0
  139. package/test/cea/cea608_memory_unit.js +1 -1
  140. package/test/cea/cea708_service_unit.js +8 -8
  141. package/test/cea/cea708_window_unit.js +6 -6
  142. package/test/cea/cea_decoder_unit.js +1 -1
  143. package/test/cea/dtvcc_packet_builder_unit.js +2 -2
  144. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  145. package/test/dash/dash_parser_live_unit.js +101 -1
  146. package/test/dash/dash_parser_manifest_unit.js +78 -2
  147. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  148. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  149. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  150. package/test/hls/hls_live_unit.js +2 -0
  151. package/test/hls/hls_parser_unit.js +252 -0
  152. package/test/media/drm_engine_integration.js +86 -12
  153. package/test/media/drm_engine_unit.js +310 -450
  154. package/test/media/quality_observer_unit.js +139 -0
  155. package/test/media/segment_reference_unit.js +16 -1
  156. package/test/media/streaming_engine_integration.js +2 -1
  157. package/test/media/streaming_engine_unit.js +2 -0
  158. package/test/net/http_plugin_unit.js +20 -15
  159. package/test/offline/manifest_convert_unit.js +2 -0
  160. package/test/offline/storage_integration.js +117 -23
  161. package/test/player_integration.js +151 -0
  162. package/test/player_src_equals_integration.js +52 -0
  163. package/test/player_unit.js +11 -33
  164. package/test/test/assets/chapters.srt +11 -0
  165. package/test/test/assets/chapters.vtt +10 -0
  166. package/test/test/util/cea_utils.js +12 -12
  167. package/test/test/util/dash_parser_util.js +4 -0
  168. package/test/test/util/fake_ad.js +7 -0
  169. package/test/test/util/fake_ad_manager.js +7 -0
  170. package/test/test/util/fake_drm_engine.js +9 -2
  171. package/test/test/util/manifest_generator.js +5 -2
  172. package/test/test/util/manifest_parser_util.js +3 -1
  173. package/test/test/util/test_scheme.js +14 -1
  174. package/test/text/ttml_text_parser_unit.js +47 -0
  175. package/test/text/web_vtt_generator_unit.js +81 -7
  176. package/test/ui/ui_unit.js +250 -0
  177. package/test/util/cmcd_manager_unit.js +277 -0
  178. package/test/util/stream_utils_unit.js +118 -10
  179. package/ui/airplay_button.js +2 -1
  180. package/ui/audio_language_selection.js +9 -5
  181. package/ui/cast_button.js +5 -6
  182. package/ui/constants.js +0 -3
  183. package/ui/context_menu.js +109 -0
  184. package/ui/controls.js +41 -7
  185. package/ui/controls.less +1 -0
  186. package/ui/enums.js +4 -0
  187. package/ui/externs/ui.js +28 -1
  188. package/ui/fast_forward_button.js +16 -8
  189. package/ui/fullscreen_button.js +2 -3
  190. package/ui/language_utils.js +1 -1
  191. package/ui/less/buttons.less +0 -4
  192. package/ui/less/containers.less +74 -0
  193. package/ui/less/overflow_menu.less +0 -10
  194. package/ui/less/tooltip.less +97 -0
  195. package/ui/locales/en.json +1 -0
  196. package/ui/locales/source.json +4 -0
  197. package/ui/loop_button.js +6 -3
  198. package/ui/mute_button.js +3 -4
  199. package/ui/overflow_menu.js +7 -28
  200. package/ui/pip_button.js +10 -7
  201. package/ui/play_button.js +2 -2
  202. package/ui/playback_rate_selection.js +11 -15
  203. package/ui/presentation_time.js +1 -3
  204. package/ui/resolution_selection.js +11 -7
  205. package/ui/rewind_button.js +17 -9
  206. package/ui/seek_bar.js +6 -6
  207. package/ui/settings_menu.js +36 -4
  208. package/ui/small_play_button.js +1 -0
  209. package/ui/spacer.js +1 -1
  210. package/ui/statistics_button.js +253 -0
  211. package/ui/text_selection.js +19 -15
  212. package/ui/ui.js +30 -0
  213. package/ui/ui_utils.js +1 -1
  214. package/ui/volume_bar.js +2 -4
@@ -0,0 +1,291 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.media.QualityObserver');
8
+
9
+ goog.require('shaka.media.IPlayheadObserver');
10
+ goog.require('shaka.log');
11
+
12
+ /**
13
+ * Monitors the quality of content being appended to the source
14
+ * buffers and fires onQualityChange events when the media quality
15
+ * at the playhead changes.
16
+ *
17
+ * @implements {shaka.media.IPlayheadObserver}
18
+ * @final
19
+ */
20
+ shaka.media.QualityObserver = class {
21
+ /**
22
+ * Creates a new QualityObserver.
23
+ *
24
+ * @param {!function():!shaka.extern.BufferedInfo} getBufferedInfo
25
+ * Buffered info is needed to purge QualityChanges that are no
26
+ * longer relevant.
27
+ */
28
+ constructor(getBufferedInfo) {
29
+ /**
30
+ * @private {!Map.<string,!shaka.media.QualityObserver.ContentTypeState>}
31
+ */
32
+ this.contentTypeStates_ = new Map();
33
+
34
+ /** @private {shaka.media.QualityObserver.EventListener} */
35
+ this.onQualityChange_ = (mediaQuality, position) => {};
36
+
37
+ /** @private function():!shaka.extern.BufferedInfo */
38
+ this.getBufferedInfo_ = getBufferedInfo;
39
+ }
40
+
41
+ /**
42
+ * Set all the listeners. This overrides any previous calls to |setListeners|.
43
+ *
44
+ * @param {shaka.media.QualityObserver.EventListener} onQualityChange
45
+ */
46
+ setListeners(onQualityChange) {
47
+ this.onQualityChange_ = onQualityChange;
48
+ }
49
+
50
+ /** @override */
51
+ release() {
52
+ this.contentTypeStates_.clear();
53
+ }
54
+
55
+ /**
56
+ * Get the ContenTypeState for a contentType, creating a new
57
+ * one if necessary.
58
+ *
59
+ * @param {!string} contentType
60
+ * The contend type e.g. "video" or "audio".
61
+ * @return {!shaka.media.QualityObserver.ContentTypeState}
62
+ * @private
63
+ */
64
+ getContentTypeState_(contentType) {
65
+ let contentTypeState = this.contentTypeStates_.get(contentType);
66
+ if (!contentTypeState) {
67
+ contentTypeState = {
68
+ qualityChangePositions: [],
69
+ currentQuality: null,
70
+ contentType: contentType,
71
+ };
72
+ this.contentTypeStates_.set(contentType, contentTypeState);
73
+ }
74
+ return contentTypeState;
75
+ }
76
+
77
+ /**
78
+ * Adds a QualityChangePosition for the contentType identified by
79
+ * the mediaQuality.contentType.
80
+ *
81
+ * @param {!shaka.extern.MediaQualityInfo} mediaQuality
82
+ * @param {!number} position
83
+ * Position in seconds of the quality change.
84
+ */
85
+ addMediaQualityChange(mediaQuality, position) {
86
+ const contentTypeState =
87
+ this.getContentTypeState_(mediaQuality.contentType);
88
+
89
+ // Remove unneeded QualityChangePosition(s) before adding the new one
90
+ this.purgeQualityChangePositions_(contentTypeState);
91
+
92
+ const newChangePosition = {
93
+ mediaQuality: mediaQuality,
94
+ position: position,
95
+ };
96
+
97
+ const changePositions = contentTypeState.qualityChangePositions;
98
+ const insertBeforeIndex = changePositions.findIndex(
99
+ (qualityChange) => (qualityChange.position >= position));
100
+
101
+ if (insertBeforeIndex >= 0) {
102
+ const duplicatePositions =
103
+ (changePositions[insertBeforeIndex].position == position) ? 1 : 0;
104
+ changePositions.splice(
105
+ insertBeforeIndex, duplicatePositions, newChangePosition);
106
+ } else {
107
+ changePositions.push(newChangePosition);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Determines the media quality at a specific position in the source buffer.
113
+ *
114
+ * @param {!number} position
115
+ * Position in seconds
116
+ * @param {!shaka.media.QualityObserver.ContentTypeState} contentTypeState
117
+ * @return {?shaka.extern.MediaQualityInfo}
118
+ * @private
119
+ */
120
+ static getMediaQualityAtPosition_(position, contentTypeState) {
121
+ // The qualityChangePositions must be ordered by position ascending
122
+ // Find the last QualityChangePosition prior to the position
123
+ const changePositions = contentTypeState.qualityChangePositions;
124
+ for (let i = changePositions.length - 1; i >= 0; i--) {
125
+ const qualityChange = changePositions[i];
126
+ if (qualityChange.position <= position) {
127
+ return qualityChange.mediaQuality;
128
+ }
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Determines if two MediaQualityInfo objects are the same or not.
135
+ *
136
+ * @param {?shaka.extern.MediaQualityInfo} mq1
137
+ * @param {?shaka.extern.MediaQualityInfo} mq2
138
+ * @return {boolean}
139
+ * @private
140
+ */
141
+ static mediaQualitiesAreTheSame_(mq1, mq2) {
142
+ if (mq1 === mq2) {
143
+ return true;
144
+ }
145
+ if (!mq1 || !mq2) {
146
+ return false;
147
+ }
148
+ return (mq1.bandwidth == mq2.bandwidth) &&
149
+ (mq1.audioSamplingRate == mq2.audioSamplingRate) &&
150
+ (mq1.codecs == mq2.codecs) &&
151
+ (mq1.contentType == mq2.contentType) &&
152
+ (mq1.frameRate == mq2.frameRate) &&
153
+ (mq1.height == mq2.height) &&
154
+ (mq1.mimeType == mq2.mimeType) &&
155
+ (mq1.channelsCount == mq2.channelsCount) &&
156
+ (mq1.pixelAspectRatio == mq2.pixelAspectRatio) &&
157
+ (mq1.width == mq2.width);
158
+ }
159
+
160
+ /** @override */
161
+ poll(positionInSeconds, wasSeeking) {
162
+ for (const contentTypeState of this.contentTypeStates_.values()) {
163
+ const qualityAtPosition =
164
+ shaka.media.QualityObserver.getMediaQualityAtPosition_(
165
+ positionInSeconds, contentTypeState);
166
+ if (qualityAtPosition &&
167
+ !shaka.media.QualityObserver.mediaQualitiesAreTheSame_(
168
+ contentTypeState.currentQuality, qualityAtPosition)) {
169
+ if (this.positionIsBuffered_(
170
+ positionInSeconds, qualityAtPosition.contentType)) {
171
+ contentTypeState.currentQuality = qualityAtPosition;
172
+ shaka.log.debug('Media quality changed at position ' +
173
+ positionInSeconds + ' ' + JSON.stringify(qualityAtPosition));
174
+ this.onQualityChange_(
175
+ qualityAtPosition, positionInSeconds);
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Determine if a position is buffered for a given content type.
183
+ *
184
+ * @param {!number} position
185
+ * @param {!string} contentType
186
+ * @private
187
+ */
188
+ positionIsBuffered_(position, contentType) {
189
+ const bufferedInfo = this.getBufferedInfo_();
190
+ const bufferedRanges = bufferedInfo[contentType];
191
+ if (bufferedRanges && bufferedRanges.length > 0) {
192
+ const bufferStart = bufferedRanges[0].start;
193
+ const bufferEnd = bufferedRanges[bufferedRanges.length - 1].end;
194
+ if (position >= bufferStart && position < bufferEnd) {
195
+ return true;
196
+ }
197
+ }
198
+ return false;
199
+ }
200
+
201
+ /**
202
+ * Removes the QualityChangePosition(s) that are not relevant to the buffered
203
+ * content of the specified contentType. Note that this function is
204
+ * invoked just before adding the quality change info associated with
205
+ * the next media segment to be appended.
206
+ *
207
+ * @param {!shaka.media.QualityObserver.ContentTypeState} contentTypeState
208
+ * @private
209
+ */
210
+ purgeQualityChangePositions_(contentTypeState) {
211
+ const bufferedInfo = this.getBufferedInfo_();
212
+ const bufferedRanges = bufferedInfo[contentTypeState.contentType];
213
+
214
+ if (bufferedRanges && bufferedRanges.length > 0) {
215
+ const bufferStart = bufferedRanges[0].start;
216
+ const bufferEnd = bufferedRanges[bufferedRanges.length - 1].end;
217
+ const oldChangePositions = contentTypeState.qualityChangePositions;
218
+ contentTypeState.qualityChangePositions =
219
+ oldChangePositions.filter(
220
+ (qualityChange, index) => {
221
+ // Remove all but last quality change before bufferStart.
222
+ if ((qualityChange.position <= bufferStart) &&
223
+ (index + 1 < oldChangePositions.length) &&
224
+ (oldChangePositions[index + 1].position <= bufferStart)) {
225
+ return false;
226
+ }
227
+ // Remove all quality changes after bufferEnd.
228
+ if (qualityChange.position >= bufferEnd) {
229
+ return false;
230
+ }
231
+ return true;
232
+ });
233
+ } else {
234
+ // Nothing is buffered; so remove all quality changes.
235
+ contentTypeState.qualityChangePositions = [];
236
+ }
237
+ }
238
+ };
239
+
240
+ /**
241
+ * @typedef {function(shaka.extern.MediaQualityInfo, number)}
242
+ *
243
+ * @description
244
+ * A callback function used to notify the player when media quality changes
245
+ * are detected at the playhead.
246
+ *
247
+ * The first argument is information about media quality at the playhead
248
+ * position.
249
+ *
250
+ * The second argument is the playhead position in seconds.
251
+ */
252
+ shaka.media.QualityObserver.EventListener;
253
+
254
+ /**
255
+ * @typedef {{
256
+ * mediaQuality: !shaka.extern.MediaQualityInfo,
257
+ * position: !number
258
+ * }}
259
+ *
260
+ * @description
261
+ * Identifies the position of a media quality change in the
262
+ * source buffer.
263
+ *
264
+ * @property {shaka.extern.MediaQualityInfo} !mediaQuality
265
+ * The new media quality for content after position in the source buffer.
266
+ * @property {number} !position
267
+ * A position in seconds in the source buffer
268
+ */
269
+ shaka.media.QualityObserver.QualityChangePosition;
270
+
271
+ /**
272
+ * @typedef {{
273
+ * qualityChangePositions:
274
+ * !Array.<shaka.media.QualityObserver.QualityChangePosition>,
275
+ * currentQuality: ?shaka.extern.MediaQualityInfo,
276
+ * contentType: !string
277
+ * }}
278
+ *
279
+ * @description
280
+ * Contains media quality information for a specific content type
281
+ * e.g video or audio.
282
+ *
283
+ * @property {!Array.<shaka.media.QualityObserver.QualityChangePosition>}
284
+ * qualityChangePositions
285
+ * Quality changes ordered by position ascending.
286
+ * @property {?shaka.media.MediaQualityInfo} currentMediaQuality
287
+ * The media quality at the playhead position.
288
+ * @property {string} contentType
289
+ * The contentType e.g. 'video' or 'audio'
290
+ */
291
+ shaka.media.QualityObserver.ContentTypeState;
@@ -345,7 +345,9 @@ shaka.media.SegmentIndex = class {
345
345
  lastReference.initSegmentReference,
346
346
  lastReference.timestampOffset,
347
347
  lastReference.appendWindowStart,
348
- lastReference.appendWindowEnd);
348
+ lastReference.appendWindowEnd,
349
+ lastReference.partialReferences,
350
+ lastReference.tilesLayout);
349
351
  }
350
352
 
351
353
 
@@ -425,6 +427,12 @@ shaka.media.SegmentIndex = class {
425
427
  return new shaka.media.SegmentIterator(this, index, partialSegmentIndex);
426
428
  }
427
429
 
430
+ /**
431
+ * @return {boolean}
432
+ */
433
+ isEmpty() {
434
+ return this.references.length == 0;
435
+ }
428
436
 
429
437
  /**
430
438
  * 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 to the
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
@@ -135,12 +150,16 @@ shaka.media.SegmentReference = class {
135
150
  * presentation. Any content from after this time will be removed by
136
151
  * MediaSource.
137
152
  * @param {!Array.<!shaka.media.SegmentReference>=} partialReferences
138
- A list of SegmentReferences for the partial segments.
153
+ * A list of SegmentReferences for the partial segments.
154
+ * @param {?string=} tilesLayout
155
+ * The value is a grid-item-dimension consisting of two positive decimal
156
+ * integers in the format: column-x-row ('4x3'). It describes the
157
+ * arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
139
158
  */
140
159
  constructor(
141
160
  startTime, endTime, uris, startByte, endByte, initSegmentReference,
142
161
  timestampOffset, appendWindowStart, appendWindowEnd,
143
- partialReferences = []) {
162
+ partialReferences = [], tilesLayout = '') {
144
163
  // A preload hinted Partial Segment has the same startTime and endTime.
145
164
  goog.asserts.assert(startTime <= endTime,
146
165
  'startTime must be less than or equal to endTime');
@@ -184,6 +203,9 @@ shaka.media.SegmentReference = class {
184
203
 
185
204
  /** @type {!Array.<!shaka.media.SegmentReference>} */
186
205
  this.partialReferences = partialReferences;
206
+
207
+ /** @type {?string} */
208
+ this.tilesLayout = tilesLayout;
187
209
  }
188
210
 
189
211
  /**
@@ -258,6 +280,16 @@ shaka.media.SegmentReference = class {
258
280
  hasPartialSegments() {
259
281
  return this.partialReferences.length > 0;
260
282
  }
283
+
284
+ /**
285
+ * Returns the segment's tiles layout. Only defined in image segments.
286
+ *
287
+ * @return {?string}
288
+ * @export
289
+ */
290
+ getTilesLayout() {
291
+ return this.tilesLayout;
292
+ }
261
293
  };
262
294
 
263
295
 
@@ -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
 
@@ -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
  }
@@ -1001,10 +1012,10 @@ shaka.media.StreamingEngine = class {
1001
1012
  if (bufferedAhead >= scaledBufferingGoal) {
1002
1013
  shaka.log.v2(logPrefix, 'buffering goal met');
1003
1014
 
1004
- // Do not try to predict the next update. Just poll twice every second.
1005
- // The playback rate can change at any time, so any prediction we make now
1006
- // could be terribly invalid soon.
1007
- return 0.5;
1015
+ // Do not try to predict the next update. Just poll according to
1016
+ // configuration (seconds). The playback rate can change at any time, so
1017
+ // any prediction we make now could be terribly invalid soon.
1018
+ return this.config_.updateIntervalSeconds / 2;
1008
1019
  }
1009
1020
 
1010
1021
  const bufferEnd =
@@ -1016,7 +1027,7 @@ shaka.media.StreamingEngine = class {
1016
1027
  // In any case just try again... if the manifest is incomplete or is not
1017
1028
  // being updated then we'll idle forever; otherwise, we'll end up getting
1018
1029
  // a SegmentReference eventually.
1019
- return 1;
1030
+ return this.config_.updateIntervalSeconds;
1020
1031
  }
1021
1032
 
1022
1033
  // Do not let any one stream get far ahead of any other.
@@ -1047,7 +1058,7 @@ shaka.media.StreamingEngine = class {
1047
1058
  // For example, let video buffering catch up to audio buffering before
1048
1059
  // fetching another audio segment.
1049
1060
  shaka.log.v2(logPrefix, 'waiting for other streams to buffer');
1050
- return 1;
1061
+ return this.config_.updateIntervalSeconds;
1051
1062
  }
1052
1063
 
1053
1064
  const p = this.fetchAndAppend_(mediaState, presentationTime, reference);
@@ -1530,6 +1541,8 @@ shaka.media.StreamingEngine = class {
1530
1541
  throw error;
1531
1542
  }
1532
1543
  };
1544
+ this.playerInterface_.onInitSegmentAppended(
1545
+ reference.startTime, reference.initSegmentReference);
1533
1546
  operations.push(append());
1534
1547
  }
1535
1548
  }
@@ -1555,7 +1568,9 @@ shaka.media.StreamingEngine = class {
1555
1568
 
1556
1569
  const hasClosedCaptions = stream.closedCaptions &&
1557
1570
  stream.closedCaptions.size > 0;
1558
- if (stream.emsgSchemeIdUris != null && stream.emsgSchemeIdUris.length > 0) {
1571
+ if ((stream.emsgSchemeIdUris != null &&
1572
+ stream.emsgSchemeIdUris.length > 0) ||
1573
+ this.config_.dispatchAllEmsgBoxes) {
1559
1574
  new shaka.util.Mp4Parser()
1560
1575
  .fullBox(
1561
1576
  'emsg',
@@ -1641,7 +1656,8 @@ shaka.media.StreamingEngine = class {
1641
1656
  // See DASH sec. 5.10.3.3.1
1642
1657
  // If a DASH client detects an event message box with a scheme that is not
1643
1658
  // defined in MPD, the client is expected to ignore it.
1644
- if (emsgSchemeIdUris.includes(schemeId)) {
1659
+ if (emsgSchemeIdUris.includes(schemeId) ||
1660
+ this.config_.dispatchAllEmsgBoxes) {
1645
1661
  // See DASH sec. 5.10.4.1
1646
1662
  // A special scheme in DASH used to signal manifest updates.
1647
1663
  if (schemeId == 'urn:mpeg:dash:event:2012') {
@@ -1764,6 +1780,19 @@ shaka.media.StreamingEngine = class {
1764
1780
 
1765
1781
  shaka.log.v2('fetching: reference=', reference);
1766
1782
 
1783
+ const stream = mediaState.stream;
1784
+ this.playerInterface_.modifySegmentRequest(
1785
+ request,
1786
+ {
1787
+ type: stream.type,
1788
+ init: reference instanceof shaka.media.InitSegmentReference,
1789
+ duration: reference.endTime - reference.startTime,
1790
+ mimeType: stream.mimeType,
1791
+ codecs: stream.codecs,
1792
+ bandwidth: stream.bandwidth,
1793
+ },
1794
+ );
1795
+
1767
1796
  const op = this.playerInterface_.netEngine.request(requestType, request);
1768
1797
  mediaState.operation = op;
1769
1798
  const response = await op.promise;
@@ -1927,12 +1956,15 @@ shaka.media.StreamingEngine = class {
1927
1956
  * @typedef {{
1928
1957
  * getPresentationTime: function():number,
1929
1958
  * getBandwidthEstimate: function():number,
1959
+ * modifySegmentRequest: function(shaka.extern.Request,
1960
+ * shaka.util.CmcdManager.SegmentInfo),
1930
1961
  * mediaSourceEngine: !shaka.media.MediaSourceEngine,
1931
1962
  * netEngine: shaka.net.NetworkingEngine,
1932
1963
  * onError: function(!shaka.util.Error),
1933
1964
  * onEvent: function(!Event),
1934
1965
  * onManifestUpdate: function(),
1935
- * onSegmentAppended: function()
1966
+ * onSegmentAppended: function(),
1967
+ * onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference)
1936
1968
  * }}
1937
1969
  *
1938
1970
  * @property {function():number} getPresentationTime
@@ -1940,6 +1972,9 @@ shaka.media.StreamingEngine = class {
1940
1972
  * viewer is seeing on screen right now.
1941
1973
  * @property {function():number} getBandwidthEstimate
1942
1974
  * Get the estimated bandwidth in bits per second.
1975
+ * @property {function(shaka.extern.Request,
1976
+ * shaka.extern.Cmcd.SegmentInfo)} modifySegmentRequest
1977
+ * The request modifier
1943
1978
  * @property {!shaka.media.MediaSourceEngine} mediaSourceEngine
1944
1979
  * The MediaSourceEngine. The caller retains ownership.
1945
1980
  * @property {shaka.net.NetworkingEngine} netEngine
@@ -1954,6 +1989,9 @@ shaka.media.StreamingEngine = class {
1954
1989
  * Called when an embedded 'emsg' box should trigger a manifest update.
1955
1990
  * @property {function()} onSegmentAppended
1956
1991
  * Called after a segment is successfully appended to a MediaSource.
1992
+ * @property
1993
+ * {function(!number, !shaka.media.InitSegmentReference)} onInitSegmentAppended
1994
+ * Called when an init segment is appended to a MediaSource.
1957
1995
  */
1958
1996
  shaka.media.StreamingEngine.PlayerInterface;
1959
1997
 
@@ -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
- /** @type {Headers} */
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
  }
@@ -34,6 +34,7 @@ shaka.net.HttpPluginUtils = class {
34
34
  uri: responseURL || uri,
35
35
  originalUri: uri,
36
36
  data: data,
37
+ status: status,
37
38
  headers: headers,
38
39
  fromCache: !!headers['x-shaka-from-cache'],
39
40
  };