shaka-player 2.5.1 → 2.5.5

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 (166) hide show
  1. package/.csslintrc +13 -0
  2. package/AUTHORS +3 -0
  3. package/CHANGELOG.md +156 -0
  4. package/CONTRIBUTORS +3 -0
  5. package/README.md +3 -0
  6. package/build/README.md +5 -5
  7. package/build/apps.py +3 -1
  8. package/build/build.py +5 -2
  9. package/build/check.py +17 -0
  10. package/build/compiler.py +49 -0
  11. package/build/conformance.textproto +1 -0
  12. package/build/docs.py +0 -6
  13. package/build/generateExterns.js +72 -20
  14. package/build/install-linux-prereqs.sh +4 -4
  15. package/build/shakaBuildHelpers.py +7 -8
  16. package/build/test.py +21 -9
  17. package/build/types/core +1 -0
  18. package/build/types/ui +6 -1
  19. package/demo/asset_card.js +129 -41
  20. package/demo/cast_receiver/receiver_app.css +27 -13
  21. package/demo/close_button.js +10 -15
  22. package/demo/common/asset.js +6 -6
  23. package/demo/common/assets.js +72 -14
  24. package/demo/config.js +28 -25
  25. package/demo/custom.js +20 -16
  26. package/demo/demo.less +107 -16
  27. package/demo/demo_uncompiled.js +33 -0
  28. package/demo/demo_utils.js +4 -3
  29. package/demo/front.js +12 -8
  30. package/demo/index.html +14 -17
  31. package/demo/input.js +30 -23
  32. package/demo/input_container.js +15 -11
  33. package/demo/load.js +5 -7
  34. package/demo/main.js +138 -49
  35. package/demo/search.js +40 -36
  36. package/demo/service_worker.js +1 -1
  37. package/demo/tooltip.js +16 -24
  38. package/dist/.csslintstamp +0 -0
  39. package/dist/controls.css +1 -1
  40. package/dist/controls.css.map +1 -1
  41. package/dist/demo.compiled.debug.js +165 -152
  42. package/dist/demo.compiled.debug.map +4 -4
  43. package/dist/demo.compiled.js +165 -152
  44. package/dist/demo.compiled.map +4 -4
  45. package/dist/demo.css +1 -1
  46. package/dist/demo.css.map +1 -1
  47. package/dist/deps.js +28 -11
  48. package/dist/receiver.compiled.debug.js +11 -11
  49. package/dist/receiver.compiled.debug.map +3 -3
  50. package/dist/receiver.compiled.js +11 -11
  51. package/dist/receiver.compiled.map +3 -3
  52. package/dist/shaka-player.compiled.debug.externs.js +232 -102
  53. package/dist/shaka-player.compiled.debug.js +189 -165
  54. package/dist/shaka-player.compiled.debug.map +4 -4
  55. package/dist/shaka-player.compiled.externs.js +232 -102
  56. package/dist/shaka-player.compiled.js +567 -553
  57. package/dist/shaka-player.compiled.map +4 -4
  58. package/dist/shaka-player.ui.debug.externs.js +338 -113
  59. package/dist/shaka-player.ui.debug.js +296 -276
  60. package/dist/shaka-player.ui.debug.map +4 -4
  61. package/dist/shaka-player.ui.externs.js +338 -113
  62. package/dist/shaka-player.ui.js +685 -671
  63. package/dist/shaka-player.ui.map +4 -4
  64. package/docs/tutorials/drm-config.md +1 -0
  65. package/docs/tutorials/fairplay.md +77 -0
  66. package/docs/tutorials/index.json +1 -0
  67. package/docs/tutorials/manifest-parser.md +1 -1
  68. package/docs/tutorials/ui-customization.md +5 -2
  69. package/docs/tutorials/ui.md +10 -4
  70. package/docs/tutorials/welcome.md +2 -2
  71. package/externs/mediasession.js +16 -1
  72. package/externs/shaka/net.js +14 -4
  73. package/externs/shaka/player.js +136 -90
  74. package/externs/tippy.js +30 -0
  75. package/externs/webkitmediakeys.js +4 -0
  76. package/karma.conf.js +30 -24
  77. package/lib/cast/cast_proxy.js +94 -23
  78. package/lib/cast/cast_receiver.js +8 -1
  79. package/lib/dash/content_protection.js +33 -1
  80. package/lib/dash/dash_parser.js +13 -5
  81. package/lib/hls/hls_parser.js +20 -4
  82. package/lib/media/drm_engine.js +29 -2
  83. package/lib/media/media_source_engine.js +9 -6
  84. package/lib/media/play_rate_controller.js +14 -3
  85. package/lib/media/playhead.js +8 -3
  86. package/lib/media/streaming_engine.js +68 -20
  87. package/lib/media/transmuxer.js +2 -2
  88. package/lib/media/video_wrapper.js +8 -3
  89. package/lib/net/data_uri_plugin.js +5 -2
  90. package/lib/net/http_fetch_plugin.js +3 -3
  91. package/lib/net/http_plugin_utils.js +2 -4
  92. package/lib/net/http_xhr_plugin.js +2 -2
  93. package/lib/net/networking_engine.js +1 -0
  94. package/lib/offline/offline_scheme.js +7 -2
  95. package/lib/player.js +18 -8
  96. package/lib/polyfill/input_event.js +7 -11
  97. package/lib/polyfill/mediasource.js +1 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +2 -72
  99. package/lib/text/cue.js +1 -1
  100. package/lib/text/ttml_text_parser.js +1 -1
  101. package/lib/util/config_utils.js +1 -0
  102. package/lib/util/error.js +45 -11
  103. package/lib/util/event_manager.js +211 -100
  104. package/lib/util/fairplay_utils.js +122 -0
  105. package/lib/util/i_releasable.js +1 -0
  106. package/lib/util/player_configuration.js +7 -0
  107. package/lib/util/pssh.js +41 -0
  108. package/lib/util/string_utils.js +42 -6
  109. package/lib/util/timer.js +1 -1
  110. package/package.json +33 -33
  111. package/roadmap.md +96 -0
  112. package/shaka-player.uncompiled.js +5 -1
  113. package/test/dash/dash_parser_content_protection_unit.js +35 -1
  114. package/test/dash/dash_parser_manifest_unit.js +60 -0
  115. package/test/hls/hls_live_unit.js +7 -1
  116. package/test/hls/hls_parser_unit.js +50 -0
  117. package/test/media/drm_engine_unit.js +10 -4
  118. package/test/media/media_source_engine_integration.js +12 -0
  119. package/test/media/media_source_engine_unit.js +3 -1
  120. package/test/media/playhead_unit.js +7 -5
  121. package/test/media/streaming_engine_unit.js +46 -1
  122. package/test/media/transmuxer_integration.js +13 -0
  123. package/test/net/data_uri_plugin_unit.js +13 -2
  124. package/test/net/http_plugin_unit.js +17 -12
  125. package/test/net/networking_engine_unit.js +17 -8
  126. package/test/offline/offline_scheme_unit.js +18 -8
  127. package/test/player_external.js +3 -2
  128. package/test/player_integration.js +94 -3
  129. package/test/player_src_equals_integration.js +2 -2
  130. package/test/player_unit.js +21 -8
  131. package/test/test/boot.js +20 -5
  132. package/test/test/externs/jasmine.js +48 -0
  133. package/test/test/util/expect_async_polyfill.js +145 -0
  134. package/test/test/util/fake_networking_engine.js +1 -0
  135. package/test/test/util/manifest_generator.js +2 -1
  136. package/test/test/util/test_scheme.js +34 -2
  137. package/test/test/util/ui_utils.js +41 -0
  138. package/test/test/util/util.js +16 -8
  139. package/test/ui/ui_unit.js +130 -4
  140. package/ui/audio_language_selection.js +17 -96
  141. package/ui/big_play_button.js +61 -0
  142. package/ui/controls.js +226 -335
  143. package/ui/controls.less +3 -5
  144. package/ui/element.js +24 -6
  145. package/ui/externs/ui.js +62 -9
  146. package/ui/fullscreen_button.js +2 -52
  147. package/ui/less/buttons.less +10 -16
  148. package/ui/less/containers.less +75 -20
  149. package/ui/less/general.less +18 -2
  150. package/ui/less/other_elements.less +2 -1
  151. package/ui/less/overflow_menu.less +11 -10
  152. package/ui/less/range_elements.less +12 -10
  153. package/ui/less/spinner.less +2 -0
  154. package/ui/overflow_menu.js +1 -1
  155. package/ui/pip_button.js +1 -1
  156. package/ui/{play_pause_button.js → play_button.js} +17 -47
  157. package/ui/presentation_time.js +17 -12
  158. package/ui/range_element.js +176 -0
  159. package/ui/resolution_selection.js +43 -106
  160. package/ui/seek_bar.js +204 -0
  161. package/ui/settings_menu.js +113 -0
  162. package/ui/small_play_button.js +73 -0
  163. package/ui/text_displayer.js +17 -14
  164. package/ui/text_selection.js +42 -107
  165. package/ui/ui.js +17 -40
  166. package/ui/volume_bar.js +40 -68
package/lib/util/error.js CHANGED
@@ -84,17 +84,20 @@ shaka.util.Error = function(severity, category, code, ...varArgs) {
84
84
  */
85
85
  this.message = 'Shaka Error ' + categoryName + '.' + codeName +
86
86
  ' (' + this.data.toString() + ')';
87
- try {
88
- throw new Error(this.message);
89
- } catch (e) {
90
- /**
91
- * A stack-trace showing where the error occurred.
92
- * <i>(Only available in uncompiled mode.)</i>
93
- *
94
- * @const {string}
95
- * @exportDoc
96
- */
97
- this.stack = e.stack;
87
+
88
+ if (shaka.util.Error.createStack) {
89
+ try {
90
+ throw new Error(this.message);
91
+ } catch (e) {
92
+ /**
93
+ * A stack-trace showing where the error occurred.
94
+ * <i>(Only available in uncompiled mode.)</i>
95
+ *
96
+ * @const {string}
97
+ * @exportDoc
98
+ */
99
+ this.stack = e.stack;
100
+ }
98
101
  }
99
102
  }
100
103
  };
@@ -109,6 +112,19 @@ shaka.util.Error.prototype.toString = function() {
109
112
  };
110
113
 
111
114
 
115
+ if (goog.DEBUG) {
116
+ /**
117
+ * If true, create a stack trace in Error objects.
118
+ *
119
+ * Only available in uncompiled mode, and disabled in tests to avoid issues
120
+ * with karma-jasmine. See comments in test/test/boot.js for details.
121
+ *
122
+ * @type {boolean}
123
+ */
124
+ shaka.util.Error.createStack = true;
125
+ }
126
+
127
+
112
128
  /**
113
129
  * @enum {number}
114
130
  * @export
@@ -717,6 +733,18 @@ shaka.util.Error.Code = {
717
733
  */
718
734
  'EXPIRED': 6014,
719
735
 
736
+ /**
737
+ * A server certificate wasn't given when it is required. FairPlay requires
738
+ * setting an explicit server certificate in the configuration.
739
+ */
740
+ 'SERVER_CERTIFICATE_REQUIRED': 6015,
741
+
742
+ /**
743
+ * An error was thrown while executing the init data transformation.
744
+ * <br> error.data[0] is the original error.
745
+ */
746
+ 'INIT_DATA_TRANSFORM_ERROR': 6016,
747
+
720
748
 
721
749
  /**
722
750
  * The call to Player.load() was interrupted by a call to Player.unload()
@@ -783,6 +811,12 @@ shaka.util.Error.Code = {
783
811
  'CAST_RECEIVER_APP_UNAVAILABLE': 8006,
784
812
 
785
813
 
814
+ /**
815
+ * No receiver app id has been provided, making casting impossible.
816
+ */
817
+ 'CAST_RECEIVER_APP_ID_MISSING': 8007,
818
+
819
+
786
820
  /**
787
821
  * Offline storage is not supported on this browser; it is required for
788
822
  * offline support.
@@ -23,143 +23,254 @@ goog.require('shaka.util.MultiMap');
23
23
 
24
24
 
25
25
  /**
26
- * Creates a new EventManager. An EventManager maintains a collection of "event
26
+ * @summary
27
+ * An EventManager maintains a collection of "event
27
28
  * bindings" between event targets and event listeners.
28
29
  *
29
- * @struct
30
- * @constructor
31
30
  * @implements {shaka.util.IReleasable}
31
+ * @export
32
32
  */
33
- shaka.util.EventManager = function() {
33
+ shaka.util.EventManager = class {
34
+ constructor() {
35
+ /**
36
+ * Maps an event type to an array of event bindings.
37
+ * @private {shaka.util.MultiMap.<!shaka.util.EventManager.Binding_>}
38
+ */
39
+ this.bindingMap_ = new shaka.util.MultiMap();
40
+ }
41
+
42
+
34
43
  /**
35
- * Maps an event type to an array of event bindings.
36
- * @private {shaka.util.MultiMap.<!shaka.util.EventManager.Binding_>}
44
+ * Detaches all event listeners.
45
+ * @override
46
+ * @export
37
47
  */
38
- this.bindingMap_ = new shaka.util.MultiMap();
39
- };
40
-
48
+ release() {
49
+ this.removeAll();
50
+ this.bindingMap_ = null;
51
+ }
41
52
 
42
- /**
43
- * @typedef {function(!Event)}
44
- */
45
- shaka.util.EventManager.ListenerType;
46
53
 
54
+ /**
55
+ * Attaches an event listener to an event target.
56
+ * @param {EventTarget} target The event target.
57
+ * @param {string} type The event type.
58
+ * @param {shaka.util.EventManager.ListenerType} listener The event listener.
59
+ * @param {(boolean|!AddEventListenerOptions)=} options An object that
60
+ * specifies characteristics about the event listener.
61
+ * The passive option, if true, indicates that this function will never
62
+ * call preventDefault(), which improves scrolling performance.
63
+ * @export
64
+ */
65
+ listen(target, type, listener, options) {
66
+ if (!this.bindingMap_) {
67
+ return;
68
+ }
47
69
 
48
- /**
49
- * Detaches all event listeners.
50
- * @override
51
- */
52
- shaka.util.EventManager.prototype.release = function() {
53
- this.removeAll();
54
- this.bindingMap_ = null;
55
- };
70
+ const binding =
71
+ new shaka.util.EventManager.Binding_(target, type, listener, options);
72
+ this.bindingMap_.push(type, binding);
73
+ }
56
74
 
57
75
 
58
- /**
59
- * Attaches an event listener to an event target.
60
- * @param {EventTarget} target The event target.
61
- * @param {string} type The event type.
62
- * @param {shaka.util.EventManager.ListenerType} listener The event listener.
63
- */
64
- shaka.util.EventManager.prototype.listen = function(target, type, listener) {
65
- if (!this.bindingMap_) return;
76
+ /**
77
+ * Attaches an event listener to an event target. The listener will be
78
+ * removed when the first instance of the event is fired.
79
+ * @param {EventTarget} target The event target.
80
+ * @param {string} type The event type.
81
+ * @param {shaka.util.EventManager.ListenerType} listener The event listener.
82
+ * @param {(boolean|!AddEventListenerOptions)=} options An object that
83
+ * specifies characteristics about the event listener.
84
+ * The passive option, if true, indicates that this function will never
85
+ * call preventDefault(), which improves scrolling performance.
86
+ * @export
87
+ */
88
+ listenOnce(target, type, listener, options) {
89
+ // Install a shim listener that will stop listening after the first event.
90
+ const shim = (event) => {
91
+ // Stop listening to this event.
92
+ this.unlisten(target, type, shim);
93
+ // Call the original listener.
94
+ listener(event);
95
+ };
96
+ this.listen(target, type, shim, options);
97
+ }
66
98
 
67
- let binding = new shaka.util.EventManager.Binding_(target, type, listener);
68
- this.bindingMap_.push(type, binding);
69
- };
70
99
 
100
+ /**
101
+ * Detaches an event listener from an event target.
102
+ * @param {EventTarget} target The event target.
103
+ * @param {string} type The event type.
104
+ * @param {shaka.util.EventManager.ListenerType=} listener The event listener.
105
+ * @export
106
+ */
107
+ unlisten(target, type, listener) {
108
+ if (!this.bindingMap_) {
109
+ return;
110
+ }
71
111
 
72
- /**
73
- * Attaches an event listener to an event target. The listener will be removed
74
- * when the first instance of the event is fired.
75
- * @param {EventTarget} target The event target.
76
- * @param {string} type The event type.
77
- * @param {shaka.util.EventManager.ListenerType} listener The event listener.
78
- */
79
- shaka.util.EventManager.prototype.listenOnce =
80
- function(target, type, listener) {
81
- // Install a shim listener that will stop listening after the first event.
82
- const shim = (event) => {
83
- // Stop listening to this event.
84
- this.unlisten(target, type, shim);
85
- // Call the original listener.
86
- listener(event);
87
- };
88
- this.listen(target, type, shim);
89
- };
112
+ const list = this.bindingMap_.get(type) || [];
90
113
 
114
+ for (const binding of list) {
115
+ if (binding.target == target) {
116
+ if (listener == binding.listener || !listener) {
117
+ binding.unlisten();
118
+ this.bindingMap_.remove(type, binding);
119
+ }
120
+ }
121
+ }
122
+ }
91
123
 
92
- /**
93
- * Detaches an event listener from an event target.
94
- * @param {EventTarget} target The event target.
95
- * @param {string} type The event type.
96
- * @param {shaka.util.EventManager.ListenerType=} listener The event listener.
97
- */
98
- shaka.util.EventManager.prototype.unlisten = function(target, type, listener) {
99
- if (!this.bindingMap_) return;
100
124
 
101
- let list = this.bindingMap_.get(type) || [];
125
+ /**
126
+ * Detaches all event listeners from all targets.
127
+ * @export
128
+ */
129
+ removeAll() {
130
+ if (!this.bindingMap_) {
131
+ return;
132
+ }
102
133
 
103
- for (let i = 0; i < list.length; ++i) {
104
- let binding = list[i];
134
+ const list = this.bindingMap_.getAll();
105
135
 
106
- if (binding.target == target) {
107
- if (listener == binding.listener || !listener) {
108
- binding.unlisten();
109
- this.bindingMap_.remove(type, binding);
110
- }
136
+ for (const binding of list) {
137
+ binding.unlisten();
111
138
  }
139
+
140
+ this.bindingMap_.clear();
112
141
  }
113
142
  };
114
143
 
115
144
 
116
145
  /**
117
- * Detaches all event listeners from all targets.
146
+ * @typedef {function(!Event)}
147
+ * @export
118
148
  */
119
- shaka.util.EventManager.prototype.removeAll = function() {
120
- if (!this.bindingMap_) return;
121
-
122
- let list = this.bindingMap_.getAll();
123
-
124
- for (let i = 0; i < list.length; ++i) {
125
- list[i].unlisten();
126
- }
127
-
128
- this.bindingMap_.clear();
129
- };
149
+ shaka.util.EventManager.ListenerType;
130
150
 
131
151
 
132
152
  /**
133
153
  * Creates a new Binding_ and attaches the event listener to the event target.
134
- * @param {EventTarget} target The event target.
135
- * @param {string} type The event type.
136
- * @param {shaka.util.EventManager.ListenerType} listener The event listener.
137
- * @constructor
154
+ *
138
155
  * @private
139
156
  */
140
- shaka.util.EventManager.Binding_ = function(target, type, listener) {
141
- /** @type {EventTarget} */
142
- this.target = target;
157
+ shaka.util.EventManager.Binding_ = class {
158
+ /**
159
+ * @param {EventTarget} target The event target.
160
+ * @param {string} type The event type.
161
+ * @param {shaka.util.EventManager.ListenerType} listener The event listener.
162
+ * @param {(boolean|!AddEventListenerOptions)=} options An object that
163
+ * specifies characteristics about the event listener.
164
+ * The passive option, if true, indicates that this function will never
165
+ * call preventDefault(), which improves scrolling performance.
166
+ */
167
+ constructor(target, type, listener, options) {
168
+ /** @type {EventTarget} */
169
+ this.target = target;
143
170
 
144
- /** @type {string} */
145
- this.type = type;
171
+ /** @type {string} */
172
+ this.type = type;
146
173
 
147
- /** @type {?shaka.util.EventManager.ListenerType} */
148
- this.listener = listener;
174
+ /** @type {?shaka.util.EventManager.ListenerType} */
175
+ this.listener = listener;
149
176
 
150
- this.target.addEventListener(type, listener, false);
151
- };
177
+ /** @type {(boolean|!AddEventListenerOptions)} */
178
+ this.options =
179
+ shaka.util.EventManager.Binding_.convertOptions_(target, options);
152
180
 
181
+ this.target.addEventListener(type, listener, this.options);
182
+ }
153
183
 
154
- /**
155
- * Detaches the event listener from the event target. This does nothing if the
156
- * event listener is already detached.
157
- */
158
- shaka.util.EventManager.Binding_.prototype.unlisten = function() {
159
- goog.asserts.assert(this.target, 'Missing target');
160
- this.target.removeEventListener(this.type, this.listener, false);
161
184
 
162
- this.target = null;
163
- this.listener = null;
185
+ /**
186
+ * Detaches the event listener from the event target. This does nothing if
187
+ * the event listener is already detached.
188
+ * @export
189
+ */
190
+ unlisten() {
191
+ goog.asserts.assert(this.target, 'Missing target');
192
+ this.target.removeEventListener(this.type, this.listener, this.options);
193
+
194
+ this.target = null;
195
+ this.listener = null;
196
+ this.options = false;
197
+ }
198
+
199
+ /**
200
+ * Converts the provided options value into a value accepted by the browser.
201
+ * Some browsers (e.g. IE11 and Tizen) don't support passing options as an
202
+ * object. So this detects this case and converts it.
203
+ *
204
+ * @param {EventTarget} target
205
+ * @param {(boolean|!AddEventListenerOptions)=} value
206
+ * @return {(boolean|!AddEventListenerOptions)}
207
+ * @private
208
+ */
209
+ static convertOptions_(target, value) {
210
+ if (value == undefined) {
211
+ return false;
212
+ } else if (typeof value == 'boolean') {
213
+ return value;
214
+ } else {
215
+ // Ignore the 'passive' option since it is just an optimization and
216
+ // doesn't affect behavior. Assert there aren't any other settings to
217
+ // ensure we don't have different behavior on different browsers by
218
+ // ignoring an important option.
219
+ const ignored = new Set(['passive', 'capture']);
220
+ const keys = Object.keys(value).filter((k) => !ignored.has(k));
221
+ goog.asserts.assert(
222
+ keys.length == 0,
223
+ 'Unsupported flag(s) to addEventListener: ' + keys.join(','));
224
+
225
+ const supports =
226
+ shaka.util.EventManager.Binding_.doesSupportObject_(target);
227
+ if (supports) {
228
+ return value;
229
+ } else {
230
+ return value['capture'] || false;
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Checks whether the browser supports passing objects as the third argument
237
+ * to addEventListener. This caches the result value in a static field to
238
+ * avoid a bunch of checks.
239
+ *
240
+ * @param {EventTarget} target
241
+ * @return {boolean}
242
+ * @private
243
+ */
244
+ static doesSupportObject_(target) {
245
+ // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
246
+ let supports = shaka.util.EventManager.Binding_.supportsObject_;
247
+ if (supports == undefined) {
248
+ supports = false;
249
+ try {
250
+ const options = {};
251
+ // This defines a getter that will set this variable if called. So if
252
+ // the browser gets this property, it supports using an object. If the
253
+ // browser doesn't get these fields, it won't support objects.
254
+ const prop = {
255
+ get: () => {
256
+ supports = true;
257
+ return false;
258
+ },
259
+ };
260
+ Object.defineProperty(options, 'passive', prop);
261
+ Object.defineProperty(options, 'capture', prop);
262
+
263
+ const call = () => {};
264
+ target.addEventListener('test', call, options);
265
+ target.removeEventListener('test', call, options);
266
+ } catch (e) {
267
+ supports = false;
268
+ }
269
+ shaka.util.EventManager.Binding_.supportsObject_ = supports;
270
+ }
271
+ return supports || false; // "false" fallback needed for compiler.
272
+ }
164
273
  };
165
274
 
275
+ /** @private {(boolean|undefined)} */
276
+ shaka.util.EventManager.Binding_.supportsObject_ = undefined;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2016 Google Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ goog.provide('shaka.util.FairPlayUtils');
19
+
20
+ goog.require('goog.Uri');
21
+
22
+
23
+ /**
24
+ * @summary A set of FairPlay utility functions.
25
+ * @exportInterface
26
+ */
27
+ shaka.util.FairPlayUtils = class {
28
+ /**
29
+ * Using the default method, extract a content ID from the init data. This is
30
+ * based on the FairPlay example documentation.
31
+ *
32
+ * @param {!BufferSource} initData
33
+ * @return {string}
34
+ * @export
35
+ */
36
+ static defaultGetContentId(initData) {
37
+ const uint8 = new Uint8Array(initData);
38
+ const dataview = new DataView(
39
+ uint8.buffer, uint8.byteOffset, uint8.byteLength);
40
+ // The first part is a 4 byte little-endian int, which is the length of
41
+ // the second part.
42
+ const length = dataview.getUint32(
43
+ /* position= */ 0, /* littleEndian= */ true);
44
+ if (length + 4 != uint8.byteLength) {
45
+ throw new RangeError('Malformed FairPlay init data');
46
+ }
47
+
48
+ // The second part is a UTF-16 LE URI from the manifest.
49
+ const uriString = shaka.util.StringUtils.fromUTF16(
50
+ uint8.subarray(4), /* littleEndian= */ true);
51
+
52
+ // The domain of that URI is the content ID according to Apple's FPS
53
+ // sample.
54
+ const uri = new goog.Uri(uriString);
55
+ return uri.getDomain();
56
+ }
57
+
58
+ /**
59
+ * Transforms the init data buffer using the given data. The format is:
60
+ *
61
+ * <pre>
62
+ * [4 bytes] initDataSize
63
+ * [initDataSize bytes] initData
64
+ * [4 bytes] contentIdSize
65
+ * [contentIdSize bytes] contentId
66
+ * [4 bytes] certSize
67
+ * [certSize bytes] cert
68
+ * </pre>
69
+ *
70
+ * @param {!BufferSource} initData
71
+ * @param {!BufferSource|string} contentId
72
+ * @param {?BufferSource} cert The server certificate; this will throw if not
73
+ * provided.
74
+ * @return {!Uint8Array}
75
+ * @export
76
+ */
77
+ static initDataTransform(initData, contentId, cert) {
78
+ if (!cert || !cert.byteLength) {
79
+ throw new shaka.util.Error(
80
+ shaka.util.Error.Severity.CRITICAL,
81
+ shaka.util.Error.Category.DRM,
82
+ shaka.util.Error.Code.SERVER_CERTIFICATE_REQUIRED);
83
+ }
84
+
85
+ // From that, we build a new init data to use in the session. This is
86
+ // composed of several parts. First, the raw init data we already got.
87
+ // Second, a 4-byte LE length followed by the content ID in UTF-16-LE.
88
+ // Third, a 4-byte LE length followed by the certificate.
89
+ /** @type {!Uint8Array} */
90
+ let contentIdArray;
91
+ if (typeof contentId == 'string') {
92
+ contentIdArray = new Uint8Array(
93
+ shaka.util.StringUtils.toUTF16(contentId, /* littleEndian= */ true));
94
+ } else {
95
+ contentIdArray = new Uint8Array(contentId);
96
+ }
97
+
98
+ const rebuiltInitData = new Uint8Array(
99
+ 8 + initData.byteLength + contentIdArray.byteLength + cert.byteLength);
100
+
101
+ let offset = 0;
102
+ /** @param {!Uint8Array} array */
103
+ const append = (array) => {
104
+ rebuiltInitData.set(array, offset);
105
+ offset += array.byteLength;
106
+ };
107
+ /** @param {!Uint8Array} array */
108
+ const appendWithLength = (array) => {
109
+ const view = new DataView(rebuiltInitData.buffer);
110
+ const value = array.byteLength;
111
+ view.setUint32(offset, value, /* littleEndian= */ true);
112
+ offset += 4;
113
+ append(array);
114
+ };
115
+
116
+ append(new Uint8Array(initData));
117
+ appendWithLength(contentIdArray);
118
+ appendWithLength(new Uint8Array(cert));
119
+
120
+ return rebuiltInitData;
121
+ }
122
+ };
@@ -24,6 +24,7 @@ goog.provide('shaka.util.IReleasable');
24
24
  * it should use 'shaka.util.IDestroyable'.
25
25
  *
26
26
  * @interface
27
+ * @export
27
28
  */
28
29
  shaka.util.IReleasable = class {
29
30
  /**
@@ -31,6 +31,7 @@ goog.require('shaka.util.ConfigUtils');
31
31
 
32
32
  /**
33
33
  * @final
34
+ * @export
34
35
  */
35
36
  shaka.util.PlayerConfiguration = class {
36
37
  /** @return {shaka.extern.PlayerConfiguration} */
@@ -82,6 +83,8 @@ shaka.util.PlayerConfiguration = class {
82
83
  clearKeys: {}, // key is arbitrary key system ID, value must be string
83
84
  advanced: {}, // key is arbitrary key system ID, value is a record type
84
85
  delayLicenseRequestUntilPlayed: false,
86
+ initDataTransform: (initData) => initData,
87
+ fairPlayTransform: true,
85
88
  };
86
89
 
87
90
  const manifest = {
@@ -101,6 +104,9 @@ shaka.util.PlayerConfiguration = class {
101
104
  ignoreMinBufferTime: false,
102
105
  autoCorrectDrift: true,
103
106
  },
107
+ hls: {
108
+ ignoreTextStreamFailures: false,
109
+ },
104
110
  };
105
111
 
106
112
  const streaming = {
@@ -223,6 +229,7 @@ shaka.util.PlayerConfiguration = class {
223
229
  * @param {!Object} updates
224
230
  * @param {shaka.extern.PlayerConfiguration=} template
225
231
  * @return {boolean}
232
+ * @export
226
233
  */
227
234
  static mergeConfigObjects(destination, updates, template) {
228
235
  const overrides = {
package/lib/util/pssh.js CHANGED
@@ -104,3 +104,44 @@ shaka.util.Pssh.prototype.parseBox_ = function(box) {
104
104
  shaka.log.warning('Mismatch between box size and data size!');
105
105
  }
106
106
  };
107
+
108
+
109
+ /**
110
+ * Creates a pssh blob from the given system ID and data.
111
+ *
112
+ * @param {!Uint8Array} data
113
+ * @param {!Uint8Array} systemId
114
+ * @return {!Uint8Array}
115
+ */
116
+ shaka.util.Pssh.createPssh = function(data, systemId) {
117
+ const dataLength = data.length;
118
+ const psshSize = 0x4 + 0x4 + 0x4 + systemId.length + 0x4 + dataLength;
119
+
120
+ /** @type {!ArrayBuffer} */
121
+ const psshBoxBuffer = new ArrayBuffer(psshSize);
122
+
123
+ /** @type {!Uint8Array} */
124
+ const psshBox = new Uint8Array(psshBoxBuffer);
125
+
126
+ /** @type {!DataView} */
127
+ const psshData = new DataView(psshBoxBuffer);
128
+
129
+ let byteCursor = 0;
130
+ psshData.setUint32(byteCursor, psshSize);
131
+ byteCursor += 0x4;
132
+ psshData.setUint32(byteCursor, 0x70737368); // 'pssh'
133
+ byteCursor += 0x4;
134
+ psshData.setUint32(byteCursor, 0); // flags
135
+ byteCursor += 0x4;
136
+ psshBox.set(systemId, byteCursor);
137
+ byteCursor += systemId.length;
138
+ psshData.setUint32(byteCursor, dataLength);
139
+ byteCursor += 0x4;
140
+ psshBox.set(data, byteCursor);
141
+ byteCursor += dataLength;
142
+
143
+ goog.asserts.assert(byteCursor === psshSize,
144
+ 'MS PRO invalid length.');
145
+
146
+ return psshBox;
147
+ };