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,139 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.require('shaka.media.QualityObserver');
8
+ goog.require('shaka.test.Util');
9
+
10
+
11
+ describe('QualityObserver', () => {
12
+ /** @type {!shaka.media.QualityObserver} */
13
+ let observer;
14
+
15
+ /** @type {!jasmine.Spy} */
16
+ let onQualityChange;
17
+
18
+ const createQualityInfo = (contentType, bandwidth) => {
19
+ return {
20
+ bandwidth: bandwidth,
21
+ audioSamplingRate: 444000,
22
+ codecs: 'my codec',
23
+ contentType: contentType,
24
+ frameRate: 30,
25
+ height: 720,
26
+ mimeType: 'mime type',
27
+ channelsCount: 2,
28
+ pixelAspectRatio: '1:1',
29
+ width: 1280,
30
+ };
31
+ };
32
+ let emptyBuffer = true;
33
+ let bufferStart = 0;
34
+ let bufferEnd = 0;
35
+
36
+ const quality1 = createQualityInfo('video', 1);
37
+ const quality2 = createQualityInfo('video', 2);
38
+
39
+ const getBufferedInfo = () => {
40
+ if (emptyBuffer) {
41
+ return {
42
+ video: [],
43
+ };
44
+ }
45
+ return {
46
+ video: [{start: bufferStart, end: bufferEnd}],
47
+ };
48
+ };
49
+
50
+ beforeEach(() => {
51
+ onQualityChange = jasmine.createSpy('onQualityChange');
52
+ observer = new shaka.media.QualityObserver(getBufferedInfo);
53
+ observer.setListeners(shaka.test.Util.spyFunc(onQualityChange));
54
+ emptyBuffer = true;
55
+ bufferStart = 0;
56
+ bufferEnd = 0;
57
+ });
58
+
59
+ it('does not call onQualityChange when there are no quality changes', () => {
60
+ observer.poll(10, false);
61
+ expect(onQualityChange).not.toHaveBeenCalled();
62
+ });
63
+
64
+ it('calls onQualityChange when position is after 1st quality change', () => {
65
+ observer.addMediaQualityChange(quality1, 10);
66
+ emptyBuffer = false;
67
+ bufferStart = 10;
68
+ bufferEnd = 20;
69
+ observer.poll(10, false);
70
+ expect(onQualityChange).toHaveBeenCalledWith(quality1, 10);
71
+ });
72
+
73
+ it('does not call onQualityChange when pos advances with no change', () => {
74
+ observer.addMediaQualityChange(quality1, 10);
75
+ emptyBuffer = false;
76
+ bufferStart = 10;
77
+ bufferEnd = 20;
78
+ observer.poll(10, false);
79
+ expect(onQualityChange).toHaveBeenCalledWith(quality1, 10);
80
+ observer.poll(11, false);
81
+ expect(onQualityChange).toHaveBeenCalledTimes(1);
82
+ });
83
+
84
+ it('does not call onQualityChange on seek to unbuffered position', () => {
85
+ observer.addMediaQualityChange(quality1, 10);
86
+ emptyBuffer = false;
87
+ bufferStart = 10;
88
+ bufferEnd = 20;
89
+ observer.poll(15, false);
90
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 15]);
91
+ observer.addMediaQualityChange(quality2, 20);
92
+ observer.poll(25, true);
93
+ expect(onQualityChange).not.toHaveBeenCalledOnceMore();
94
+ bufferEnd = 30;
95
+ observer.poll(26, false);
96
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 26]);
97
+ });
98
+
99
+ it('calls onQualityChange when position advances over 2nd quality change',
100
+ () => {
101
+ observer.addMediaQualityChange(quality1, 10);
102
+ emptyBuffer = false;
103
+ bufferStart = 10;
104
+ bufferEnd = 20;
105
+ observer.poll(10, false);
106
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 10]);
107
+ observer.addMediaQualityChange(quality2, 20);
108
+ bufferStart = 10;
109
+ bufferEnd = 30;
110
+ observer.poll(20, false);
111
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 20]);
112
+ });
113
+
114
+ it('calls onQualityChange when position moves back over a quality chanage',
115
+ () => {
116
+ observer.addMediaQualityChange(quality1, 10);
117
+ emptyBuffer = false;
118
+ bufferStart = 10;
119
+ bufferEnd = 20;
120
+ observer.addMediaQualityChange(quality2, 20);
121
+ bufferStart = 10;
122
+ bufferEnd = 30;
123
+ observer.poll(25, false);
124
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 25]);
125
+ observer.poll(15, false);
126
+ expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 15]);
127
+ });
128
+
129
+ it('uses last applied quality when there are two at the same position',
130
+ () => {
131
+ observer.addMediaQualityChange(quality1, 10);
132
+ observer.addMediaQualityChange(quality2, 10);
133
+ emptyBuffer = false;
134
+ bufferStart = 10;
135
+ bufferEnd = 20;
136
+ observer.poll(15, false);
137
+ expect(onQualityChange).toHaveBeenCalledWith(quality2, 15);
138
+ });
139
+ });
@@ -38,14 +38,29 @@ describe('SegmentReference', () => {
38
38
  });
39
39
 
40
40
  describe('InitSegmentReference', () => {
41
+ const mediaQuality = {
42
+ bandwidth: 1,
43
+ audioSamplingRate: 444000,
44
+ codecs: 'my codec',
45
+ contentType: 'video',
46
+ frameRate: 30,
47
+ height: 720,
48
+ mimeType: 'mime type',
49
+ channelsCount: 2,
50
+ pixelAspectRatio: '1:1',
51
+ width: 1280,
52
+ };
53
+
41
54
  it('returns in getters values from constructor parameters', () => {
42
55
  const reference = new shaka.media.InitSegmentReference(
43
56
  /* getUris= */ () => ['x', 'y'],
44
57
  /* startByte= */ 4,
45
- /* endByte= */ 5);
58
+ /* endByte= */ 5,
59
+ mediaQuality);
46
60
 
47
61
  expect(reference.getUris()).toEqual(['x', 'y']);
48
62
  expect(reference.getStartByte()).toBe(4);
49
63
  expect(reference.getEndByte()).toBe(5);
64
+ expect(reference.getMediaQuality()).toBe(mediaQuality);
50
65
  });
51
66
  });
@@ -60,7 +60,6 @@ describe('StreamingEngine', () => {
60
60
  /** @type {!shaka.media.StreamingEngine} */
61
61
  let streamingEngine;
62
62
 
63
-
64
63
  /** @type {shaka.extern.Variant} */
65
64
  let variant;
66
65
 
@@ -262,6 +261,7 @@ describe('StreamingEngine', () => {
262
261
 
263
262
  function createStreamingEngine() {
264
263
  const playerInterface = {
264
+ modifySegmentRequest: (request, segmentInfo) => {},
265
265
  getPresentationTime: () => playhead.getTime(),
266
266
  getBandwidthEstimate: () => 1e6,
267
267
  mediaSourceEngine: mediaSourceEngine,
@@ -270,6 +270,7 @@ describe('StreamingEngine', () => {
270
270
  onEvent: Util.spyFunc(onEvent),
271
271
  onManifestUpdate: () => {},
272
272
  onSegmentAppended: () => playhead.notifyOfBufferingChange(),
273
+ onInitSegmentAppended: () => {},
273
274
  };
274
275
  streamingEngine = new shaka.media.StreamingEngine(
275
276
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);
@@ -451,6 +451,7 @@ describe('StreamingEngine', () => {
451
451
  presentationTimeInSeconds != undefined,
452
452
  'All tests should have defined an initial presentation time by now!');
453
453
  const playerInterface = {
454
+ modifySegmentRequest: (request, segmentInfo) => {},
454
455
  getPresentationTime: () => presentationTimeInSeconds,
455
456
  getBandwidthEstimate: Util.spyFunc(getBandwidthEstimate),
456
457
  mediaSourceEngine: mediaSourceEngine,
@@ -459,6 +460,7 @@ describe('StreamingEngine', () => {
459
460
  onEvent: Util.spyFunc(onEvent),
460
461
  onManifestUpdate: Util.spyFunc(onManifestUpdate),
461
462
  onSegmentAppended: Util.spyFunc(onSegmentAppended),
463
+ onInitSegmentAppended: () => {},
462
464
  };
463
465
  streamingEngine = new shaka.media.StreamingEngine(
464
466
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);
@@ -26,6 +26,9 @@ function httpPluginTests(usingFetch) {
26
26
  // A dummy progress callback.
27
27
  const progressUpdated = (elapsedMs, bytes, bytesRemaining) => {};
28
28
 
29
+ // A dummy headers callback.
30
+ const headersReceived = (headers) => {};
31
+
29
32
  /** @type {shaka.extern.RetryParameters} */
30
33
  let retryParameters;
31
34
 
@@ -168,7 +171,8 @@ function httpPluginTests(usingFetch) {
168
171
  request.method = 'POST';
169
172
  request.headers['BAZ'] = '123';
170
173
 
171
- await plugin(request.uris[0], request, requestType, progressUpdated)
174
+ await plugin(
175
+ request.uris[0], request, requestType, progressUpdated, headersReceived)
172
176
  .promise;
173
177
 
174
178
  const actual = mostRecentRequest();
@@ -189,8 +193,8 @@ function httpPluginTests(usingFetch) {
189
193
  request.body = null;
190
194
  request.method = 'GET';
191
195
 
192
- await plugin(request.uris[0], request, requestType, progressUpdated)
193
- .promise;
196
+ await plugin(request.uris[0], request, requestType, progressUpdated,
197
+ headersReceived).promise;
194
198
 
195
199
  const actual = jasmine.Fetch.requests.mostRecent();
196
200
  expect(actual).toBeTruthy();
@@ -205,8 +209,8 @@ function httpPluginTests(usingFetch) {
205
209
 
206
210
  const request = shaka.net.NetworkingEngine.makeRequest(
207
211
  [uri], retryParameters, Util.spyFunc(streamDataCallback));
208
- const response =
209
- await plugin(uri, request, requestType, progressUpdated).promise;
212
+ const response = await plugin(
213
+ uri, request, requestType, progressUpdated, headersReceived).promise;
210
214
 
211
215
  expect(mostRecentRequest().url).toBe(uri);
212
216
  expect(response).toBeTruthy();
@@ -297,9 +301,9 @@ function httpPluginTests(usingFetch) {
297
301
  const request = shaka.net.NetworkingEngine.makeRequest(
298
302
  ['https://foo.bar/cache'], retryParameters);
299
303
 
300
- const response =
301
- await plugin(request.uris[0], request, requestType, progressUpdated)
302
- .promise;
304
+ const response = await plugin(
305
+ request.uris[0], request, requestType, progressUpdated, headersReceived)
306
+ .promise;
303
307
  expect(response).toBeTruthy();
304
308
  expect(response.fromCache).toBe(true);
305
309
  });
@@ -312,8 +316,8 @@ function httpPluginTests(usingFetch) {
312
316
  uri = 'https://foo.bar/timeout';
313
317
  const request = shaka.net.NetworkingEngine.makeRequest(
314
318
  [uri], retryParameters);
315
- const operation = plugin(
316
- request.uris[0], request, requestType, progressUpdated);
319
+ const operation = plugin(request.uris[0], request, requestType,
320
+ progressUpdated, headersReceived);
317
321
 
318
322
  /** @type {jasmine.Fetch.RequestStub} */
319
323
  const actual = jasmine.Fetch.requests.mostRecent();
@@ -354,8 +358,8 @@ function httpPluginTests(usingFetch) {
354
358
  uri = 'https://foo.bar/';
355
359
  const request = shaka.net.NetworkingEngine.makeRequest(
356
360
  [uri], retryParameters);
357
- operation = plugin(
358
- request.uris[0], request, requestType, progressUpdated);
361
+ operation = plugin(request.uris[0], request, requestType, progressUpdated,
362
+ headersReceived);
359
363
  requestPromise = operation.promise;
360
364
  }
361
365
 
@@ -388,8 +392,8 @@ function httpPluginTests(usingFetch) {
388
392
  async function testSucceeds(uri, overrideUri) {
389
393
  const request = shaka.net.NetworkingEngine.makeRequest(
390
394
  [uri], retryParameters);
391
- const response =
392
- await plugin(uri, request, requestType, progressUpdated).promise;
395
+ const response = await plugin(
396
+ uri, request, requestType, progressUpdated, headersReceived).promise;
393
397
 
394
398
  expect(mostRecentRequest().url).toBe(uri);
395
399
  expect(response).toBeTruthy();
@@ -410,7 +414,8 @@ function httpPluginTests(usingFetch) {
410
414
  const request = shaka.net.NetworkingEngine.makeRequest(
411
415
  [uri], retryParameters);
412
416
 
413
- const p = plugin(uri, request, requestType, progressUpdated).promise;
417
+ const p = plugin(
418
+ uri, request, requestType, progressUpdated, headersReceived).promise;
414
419
  if (expected.code == shaka.util.Error.Code.TIMEOUT) {
415
420
  jasmine.clock().tick(5000);
416
421
  }
@@ -101,6 +101,7 @@ describe('ManifestConverter', () => {
101
101
  audioRobustness: 'very',
102
102
  videoRobustness: 'kinda_sorta',
103
103
  serverCertificate: new Uint8Array([1, 2, 3]),
104
+ serverCertificateUri: '',
104
105
  sessionType: '',
105
106
  initData: [{
106
107
  initData: new Uint8Array([4, 5, 6]),
@@ -316,6 +317,7 @@ describe('ManifestConverter', () => {
316
317
  appendWindowStart: 0,
317
318
  appendWindowEnd: Infinity,
318
319
  timestampOffset: 0,
320
+ tilesLayout: '',
319
321
  };
320
322
 
321
323
  return segment;
@@ -22,10 +22,12 @@ goog.require('shaka.test.ManifestGenerator');
22
22
  goog.require('shaka.test.TestScheme');
23
23
  goog.require('shaka.test.Util');
24
24
  goog.require('shaka.util.AbortableOperation');
25
+ goog.require('shaka.util.BufferUtils');
25
26
  goog.require('shaka.util.Error');
26
27
  goog.require('shaka.util.EventManager');
27
28
  goog.require('shaka.util.PlayerConfiguration');
28
29
  goog.require('shaka.util.PublicPromise');
30
+ goog.require('shaka.util.Uint8ArrayUtils');
29
31
  goog.requireType('shaka.media.SegmentReference');
30
32
 
31
33
  /** @return {boolean} */
@@ -58,12 +60,16 @@ filterDescribe('Storage', storageSupport, () => {
58
60
  const manifestWithNonZeroStartUri = 'fake:manifest-with-non-zero-start';
59
61
  const manifestWithLiveTimelineUri = 'fake:manifest-with-live-timeline';
60
62
  const manifestWithAlternateSegmentsUri = 'fake:manifest-with-alt-segments';
63
+ const manifestWithVideoInitSegmentsUri =
64
+ 'fake:manifest-with-video-init-segments';
61
65
 
66
+ const initSegmentUri = 'fake:init-segment';
62
67
  const segment1Uri = 'fake:segment-1';
63
68
  const segment2Uri = 'fake:segment-2';
64
69
  const segment3Uri = 'fake:segment-3';
65
70
  const segment4Uri = 'fake:segment-4';
66
71
 
72
+ const alternateInitSegmentUri = 'fake:alt-init-segment';
67
73
  const alternateSegment1Uri = 'fake:alt-segment-1';
68
74
  const alternateSegment2Uri = 'fake:alt-segment-2';
69
75
  const alternateSegment3Uri = 'fake:alt-segment-3';
@@ -147,15 +153,7 @@ filterDescribe('Storage', storageSupport, () => {
147
153
  }
148
154
  });
149
155
 
150
- for (const useMediaCapabilities of [true, false]) {
151
- const isEnabled = useMediaCapabilities ? 'enabled' : 'disabled';
152
- filterDescribe('persistent license with MediaCapabilities ' + isEnabled,
153
- drmStorageSupport, () => {
154
- testPersistentLicense(useMediaCapabilities);
155
- });
156
- }
157
-
158
- function testPersistentLicense(useMediaCapabilities) {
156
+ filterDescribe('persistent license', drmStorageSupport, () => {
159
157
  /** @type {!shaka.Player} */
160
158
  let player;
161
159
  /** @type {!shaka.offline.Storage} */
@@ -166,7 +164,6 @@ filterDescribe('Storage', storageSupport, () => {
166
164
  // networking engine. This allows us to use Player.configure in these
167
165
  // tests.
168
166
  player = new shaka.Player();
169
- player.configure({'useMediaCapabilities': useMediaCapabilities});
170
167
  storage = new shaka.offline.Storage(player);
171
168
  });
172
169
 
@@ -201,7 +198,7 @@ filterDescribe('Storage', storageSupport, () => {
201
198
  expect(manifest.offlineSessionIds.length).toBeTruthy();
202
199
 
203
200
  // PART 2 - Check that the licences are stored.
204
- await withDrm(player, manifest, useMediaCapabilities, (drm) => {
201
+ await withDrm(player, manifest, (drm) => {
205
202
  return Promise.all(manifest.offlineSessionIds.map(async (session) => {
206
203
  const foundSession = await loadOfflineSession(drm, session);
207
204
  expect(foundSession).toBeTruthy();
@@ -213,7 +210,7 @@ filterDescribe('Storage', storageSupport, () => {
213
210
  await storage.remove(uri.toString());
214
211
 
215
212
  // PART 4 - Check that the licenses were removed.
216
- const p = withDrm(player, manifest, useMediaCapabilities, (drm) => {
213
+ const p = withDrm(player, manifest, (drm) => {
217
214
  return Promise.all(manifest.offlineSessionIds.map(async (session) => {
218
215
  const notFoundSession = await loadOfflineSession(drm, session);
219
216
  expect(notFoundSession).toBeFalsy();
@@ -257,7 +254,7 @@ filterDescribe('Storage', storageSupport, () => {
257
254
  const storedContents = await storage.list();
258
255
  expect(storedContents).toEqual([]);
259
256
 
260
- await withDrm(player, manifest, useMediaCapabilities, (drm) => {
257
+ await withDrm(player, manifest, (drm) => {
261
258
  return Promise.all(manifest.offlineSessionIds.map(async (session) => {
262
259
  const foundSession = await loadOfflineSession(drm, session);
263
260
  expect(foundSession).toBeTruthy();
@@ -270,7 +267,7 @@ filterDescribe('Storage', storageSupport, () => {
270
267
  expect(didRemoveAll).toBe(true);
271
268
 
272
269
  // PART 6 - Check that the licenses were removed.
273
- const p = withDrm(player, manifest, useMediaCapabilities, (drm) => {
270
+ const p = withDrm(player, manifest, (drm) => {
274
271
  return Promise.all(manifest.offlineSessionIds.map(async (session) => {
275
272
  const notFoundSession = await loadOfflineSession(drm, session);
276
273
  expect(notFoundSession).toBeFalsy();
@@ -282,7 +279,7 @@ filterDescribe('Storage', storageSupport, () => {
282
279
  shaka.util.Error.Code.OFFLINE_SESSION_REMOVED));
283
280
  await expectAsync(p).toBeRejectedWith(expected);
284
281
  });
285
- }
282
+ });
286
283
 
287
284
  describe('default track selection callback', () => {
288
285
  const PlayerConfiguration = shaka.util.PlayerConfiguration;
@@ -954,6 +951,46 @@ filterDescribe('Storage', storageSupport, () => {
954
951
  }
955
952
  });
956
953
 
954
+ it('can extract DRM info from segments', async () => {
955
+ const pssh1 =
956
+ '00000028' + // atom size
957
+ '70737368' + // atom type='pssh'
958
+ '00000000' + // v0, flags=0
959
+ 'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
960
+ '00000008' + // data size
961
+ '0102030405060708'; // data
962
+ const psshData1 = shaka.util.Uint8ArrayUtils.fromHex(pssh1);
963
+ const pssh2 =
964
+ '00000028' + // atom size
965
+ '70737368' + // atom type='pssh'
966
+ '00000000' + // v0, flags=0
967
+ 'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
968
+ '00000008' + // data size
969
+ '1337420123456789'; // data
970
+ const psshData2 = shaka.util.Uint8ArrayUtils.fromHex(pssh2);
971
+ netEngine.setResponseValue(initSegmentUri,
972
+ shaka.util.BufferUtils.toArrayBuffer(psshData1));
973
+ netEngine.setResponseValue(alternateInitSegmentUri,
974
+ shaka.util.BufferUtils.toArrayBuffer(psshData2));
975
+
976
+ const drm = new shaka.test.FakeDrmEngine();
977
+ const drmInfo = makeDrmInfo();
978
+ drmInfo.keySystem = 'com.widevine.alpha';
979
+ drm.setDrmInfo(drmInfo);
980
+ overrideDrmAndManifest(
981
+ storage,
982
+ drm,
983
+ makeManifestWithVideoInitSegments());
984
+
985
+ const stored = await storage.store(
986
+ manifestWithVideoInitSegmentsUri, noMetadata, fakeMimeType).promise;
987
+ goog.asserts.assert(stored.offlineUri != null, 'URI should not be null!');
988
+
989
+ // The manifest chooses the alternate stream, so expect only the alt init
990
+ // segment.
991
+ expect(drm.newInitData).toHaveBeenCalledWith('cenc', psshData2);
992
+ });
993
+
957
994
  it('can store multiple assets at once', async () => {
958
995
  // Block the network so that we won't finish the first store command.
959
996
  /** @type {!shaka.util.PublicPromise} */
@@ -1243,11 +1280,11 @@ filterDescribe('Storage', storageSupport, () => {
1243
1280
  goog.asserts.assert(
1244
1281
  content.offlineUri != null, 'URI should not be null!');
1245
1282
 
1246
- /**
1247
- * @type {!Array.<number>}
1248
- */
1283
+ // We expect 5 progress events because there are 4 unique segments, plus
1284
+ // the manifest.
1285
+ /** @type {!Array.<number>}*/
1249
1286
  const progressSteps = [
1250
- 0.111, 0.222, 0.333, 0.444, 0.555, 0.666, 0.777, 0.888, 1.0,
1287
+ 0.2, 0.4, 0.6, 0.8, 1,
1251
1288
  ];
1252
1289
 
1253
1290
  const progressCallback = (content, progress) => {
@@ -1408,6 +1445,62 @@ filterDescribe('Storage', storageSupport, () => {
1408
1445
  };
1409
1446
  }
1410
1447
 
1448
+ /** @return {shaka.extern.Manifest} */
1449
+ function makeManifestWithVideoInitSegments() {
1450
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
1451
+ manifest.presentationTimeline.setDuration(20);
1452
+
1453
+ manifest.addVariant(0, (variant) => {
1454
+ variant.bandwidth = kbps(13);
1455
+ variant.addVideo(1, (stream) => {
1456
+ stream.bandwidth = kbps(13);
1457
+ stream.size(100, 200);
1458
+ });
1459
+ });
1460
+ manifest.addVariant(2, (variant) => {
1461
+ variant.bandwidth = kbps(20);
1462
+ variant.addVideo(3, (stream) => {
1463
+ stream.bandwidth = kbps(20);
1464
+ stream.size(200, 400);
1465
+ });
1466
+ });
1467
+ });
1468
+
1469
+ const stream = manifest.variants[0].video;
1470
+ goog.asserts.assert(stream, 'The first stream should exist');
1471
+ stream.encrypted = true;
1472
+ const init = new shaka.media.InitSegmentReference(
1473
+ () => [initSegmentUri], 0, null);
1474
+ const refs = [
1475
+ makeReference(segment1Uri, 0, 1),
1476
+ makeReference(segment2Uri, 1, 2),
1477
+ makeReference(segment3Uri, 2, 3),
1478
+ makeReference(segment4Uri, 3, 4),
1479
+ ];
1480
+ for (const ref of refs) {
1481
+ ref.initSegmentReference = init;
1482
+ }
1483
+ overrideSegmentIndex(stream, refs);
1484
+
1485
+ const streamAlt = manifest.variants[1].video;
1486
+ goog.asserts.assert(streamAlt, 'The second stream should exist');
1487
+ streamAlt.encrypted = true;
1488
+ const initAlt = new shaka.media.InitSegmentReference(
1489
+ () => [alternateInitSegmentUri], 0, null);
1490
+ const refsAlt = [
1491
+ makeReference(alternateSegment1Uri, 0, 1),
1492
+ makeReference(alternateSegment2Uri, 1, 2),
1493
+ makeReference(alternateSegment3Uri, 2, 3),
1494
+ makeReference(alternateSegment4Uri, 3, 4),
1495
+ ];
1496
+ for (const ref of refsAlt) {
1497
+ ref.initSegmentReference = initAlt;
1498
+ }
1499
+ overrideSegmentIndex(streamAlt, refsAlt);
1500
+
1501
+ return manifest;
1502
+ }
1503
+
1411
1504
  /** @return {shaka.extern.Manifest} */
1412
1505
  function makeManifestWithPerStreamBandwidth() {
1413
1506
  const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
@@ -1598,6 +1691,7 @@ filterDescribe('Storage', storageSupport, () => {
1598
1691
  keyIds: null,
1599
1692
  sessionType: 'temporary',
1600
1693
  serverCertificate: null,
1694
+ serverCertificateUri: '',
1601
1695
  audioRobustness: 'HARDY',
1602
1696
  videoRobustness: 'OTHER',
1603
1697
  };
@@ -1620,6 +1714,8 @@ filterDescribe('Storage', storageSupport, () => {
1620
1714
  makeManifestWithLiveTimeline();
1621
1715
  this.map_[manifestWithAlternateSegmentsUri] =
1622
1716
  makeManifestWithAlternateSegments();
1717
+ this.map_[manifestWithVideoInitSegmentsUri] =
1718
+ makeManifestWithVideoInitSegments();
1623
1719
  }
1624
1720
 
1625
1721
  /** @override */
@@ -1681,11 +1777,10 @@ filterDescribe('Storage', storageSupport, () => {
1681
1777
  /**
1682
1778
  * @param {!shaka.Player} player
1683
1779
  * @param {shaka.extern.Manifest} manifest
1684
- * @param {boolean} useMediaCapabilities
1685
1780
  * @param {function(!shaka.media.DrmEngine):Promise} action
1686
1781
  * @return {!Promise}
1687
1782
  */
1688
- async function withDrm(player, manifest, useMediaCapabilities, action) {
1783
+ async function withDrm(player, manifest, action) {
1689
1784
  const net = player.getNetworkingEngine();
1690
1785
  goog.asserts.assert(net, 'Player should have a net engine right now');
1691
1786
 
@@ -1703,8 +1798,7 @@ filterDescribe('Storage', storageSupport, () => {
1703
1798
  try {
1704
1799
  drm.configure(player.getConfiguration().drm);
1705
1800
  const variants = manifest.variants;
1706
- await drm.initForStorage(variants, /* usePersistentLicenses= */ true,
1707
- useMediaCapabilities);
1801
+ await drm.initForStorage(variants, /* usePersistentLicenses= */ true);
1708
1802
  await action(drm);
1709
1803
  } finally {
1710
1804
  await drm.destroy();