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.
- package/.csslintrc +13 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +156 -0
- package/CONTRIBUTORS +3 -0
- package/README.md +3 -0
- package/build/README.md +5 -5
- package/build/apps.py +3 -1
- package/build/build.py +5 -2
- package/build/check.py +17 -0
- package/build/compiler.py +49 -0
- package/build/conformance.textproto +1 -0
- package/build/docs.py +0 -6
- package/build/generateExterns.js +72 -20
- package/build/install-linux-prereqs.sh +4 -4
- package/build/shakaBuildHelpers.py +7 -8
- package/build/test.py +21 -9
- package/build/types/core +1 -0
- package/build/types/ui +6 -1
- package/demo/asset_card.js +129 -41
- package/demo/cast_receiver/receiver_app.css +27 -13
- package/demo/close_button.js +10 -15
- package/demo/common/asset.js +6 -6
- package/demo/common/assets.js +72 -14
- package/demo/config.js +28 -25
- package/demo/custom.js +20 -16
- package/demo/demo.less +107 -16
- package/demo/demo_uncompiled.js +33 -0
- package/demo/demo_utils.js +4 -3
- package/demo/front.js +12 -8
- package/demo/index.html +14 -17
- package/demo/input.js +30 -23
- package/demo/input_container.js +15 -11
- package/demo/load.js +5 -7
- package/demo/main.js +138 -49
- package/demo/search.js +40 -36
- package/demo/service_worker.js +1 -1
- package/demo/tooltip.js +16 -24
- package/dist/.csslintstamp +0 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +165 -152
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +165 -152
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +28 -11
- package/dist/receiver.compiled.debug.js +11 -11
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -11
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.debug.externs.js +232 -102
- package/dist/shaka-player.compiled.debug.js +189 -165
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +232 -102
- package/dist/shaka-player.compiled.js +567 -553
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.debug.externs.js +338 -113
- package/dist/shaka-player.ui.debug.js +296 -276
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +338 -113
- package/dist/shaka-player.ui.js +685 -671
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/drm-config.md +1 -0
- package/docs/tutorials/fairplay.md +77 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/manifest-parser.md +1 -1
- package/docs/tutorials/ui-customization.md +5 -2
- package/docs/tutorials/ui.md +10 -4
- package/docs/tutorials/welcome.md +2 -2
- package/externs/mediasession.js +16 -1
- package/externs/shaka/net.js +14 -4
- package/externs/shaka/player.js +136 -90
- package/externs/tippy.js +30 -0
- package/externs/webkitmediakeys.js +4 -0
- package/karma.conf.js +30 -24
- package/lib/cast/cast_proxy.js +94 -23
- package/lib/cast/cast_receiver.js +8 -1
- package/lib/dash/content_protection.js +33 -1
- package/lib/dash/dash_parser.js +13 -5
- package/lib/hls/hls_parser.js +20 -4
- package/lib/media/drm_engine.js +29 -2
- package/lib/media/media_source_engine.js +9 -6
- package/lib/media/play_rate_controller.js +14 -3
- package/lib/media/playhead.js +8 -3
- package/lib/media/streaming_engine.js +68 -20
- package/lib/media/transmuxer.js +2 -2
- package/lib/media/video_wrapper.js +8 -3
- package/lib/net/data_uri_plugin.js +5 -2
- package/lib/net/http_fetch_plugin.js +3 -3
- package/lib/net/http_plugin_utils.js +2 -4
- package/lib/net/http_xhr_plugin.js +2 -2
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/offline_scheme.js +7 -2
- package/lib/player.js +18 -8
- package/lib/polyfill/input_event.js +7 -11
- package/lib/polyfill/mediasource.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +2 -72
- package/lib/text/cue.js +1 -1
- package/lib/text/ttml_text_parser.js +1 -1
- package/lib/util/config_utils.js +1 -0
- package/lib/util/error.js +45 -11
- package/lib/util/event_manager.js +211 -100
- package/lib/util/fairplay_utils.js +122 -0
- package/lib/util/i_releasable.js +1 -0
- package/lib/util/player_configuration.js +7 -0
- package/lib/util/pssh.js +41 -0
- package/lib/util/string_utils.js +42 -6
- package/lib/util/timer.js +1 -1
- package/package.json +33 -33
- package/roadmap.md +96 -0
- package/shaka-player.uncompiled.js +5 -1
- package/test/dash/dash_parser_content_protection_unit.js +35 -1
- package/test/dash/dash_parser_manifest_unit.js +60 -0
- package/test/hls/hls_live_unit.js +7 -1
- package/test/hls/hls_parser_unit.js +50 -0
- package/test/media/drm_engine_unit.js +10 -4
- package/test/media/media_source_engine_integration.js +12 -0
- package/test/media/media_source_engine_unit.js +3 -1
- package/test/media/playhead_unit.js +7 -5
- package/test/media/streaming_engine_unit.js +46 -1
- package/test/media/transmuxer_integration.js +13 -0
- package/test/net/data_uri_plugin_unit.js +13 -2
- package/test/net/http_plugin_unit.js +17 -12
- package/test/net/networking_engine_unit.js +17 -8
- package/test/offline/offline_scheme_unit.js +18 -8
- package/test/player_external.js +3 -2
- package/test/player_integration.js +94 -3
- package/test/player_src_equals_integration.js +2 -2
- package/test/player_unit.js +21 -8
- package/test/test/boot.js +20 -5
- package/test/test/externs/jasmine.js +48 -0
- package/test/test/util/expect_async_polyfill.js +145 -0
- package/test/test/util/fake_networking_engine.js +1 -0
- package/test/test/util/manifest_generator.js +2 -1
- package/test/test/util/test_scheme.js +34 -2
- package/test/test/util/ui_utils.js +41 -0
- package/test/test/util/util.js +16 -8
- package/test/ui/ui_unit.js +130 -4
- package/ui/audio_language_selection.js +17 -96
- package/ui/big_play_button.js +61 -0
- package/ui/controls.js +226 -335
- package/ui/controls.less +3 -5
- package/ui/element.js +24 -6
- package/ui/externs/ui.js +62 -9
- package/ui/fullscreen_button.js +2 -52
- package/ui/less/buttons.less +10 -16
- package/ui/less/containers.less +75 -20
- package/ui/less/general.less +18 -2
- package/ui/less/other_elements.less +2 -1
- package/ui/less/overflow_menu.less +11 -10
- package/ui/less/range_elements.less +12 -10
- package/ui/less/spinner.less +2 -0
- package/ui/overflow_menu.js +1 -1
- package/ui/pip_button.js +1 -1
- package/ui/{play_pause_button.js → play_button.js} +17 -47
- package/ui/presentation_time.js +17 -12
- package/ui/range_element.js +176 -0
- package/ui/resolution_selection.js +43 -106
- package/ui/seek_bar.js +204 -0
- package/ui/settings_menu.js +113 -0
- package/ui/small_play_button.js +73 -0
- package/ui/text_displayer.js +17 -14
- package/ui/text_selection.js +42 -107
- package/ui/ui.js +17 -40
- package/ui/volume_bar.js +40 -68
package/lib/cast/cast_proxy.js
CHANGED
|
@@ -69,14 +69,28 @@ shaka.cast.CastProxy = function(video, player, receiverAppId) {
|
|
|
69
69
|
/** @private {shaka.util.EventManager} */
|
|
70
70
|
this.eventManager_ = null;
|
|
71
71
|
|
|
72
|
-
/** @private {
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
/** @private {string} */
|
|
73
|
+
this.receiverAppId_ = receiverAppId;
|
|
74
|
+
|
|
75
|
+
if (this.receiverAppId_) {
|
|
76
|
+
// TODO: This is temporary fix to get casting working.
|
|
77
|
+
// It masks the fact that we're at the moment unable to
|
|
78
|
+
// change the receiver id sucessfully.
|
|
79
|
+
// It relies on the assumption that changing one actual
|
|
80
|
+
// id to another is a very edgy use case (usually apps have
|
|
81
|
+
// only one receiver app).
|
|
82
|
+
// It unblocks casting in the world where UI can be configured,
|
|
83
|
+
// but it's hacky and must be changed to a permanent solution
|
|
84
|
+
// allowing to change Receiver App ID for reals.
|
|
85
|
+
/** @private {shaka.cast.CastSender} */
|
|
86
|
+
this.sender_ = new shaka.cast.CastSender(
|
|
87
|
+
receiverAppId,
|
|
88
|
+
() => this.onCastStatusChanged_(),
|
|
89
|
+
() => this.onFirstCastStateUpdate_(),
|
|
90
|
+
(targetName, event) => this.onRemoteEvent_(targetName, event),
|
|
91
|
+
() => this.onResumeLocal_(),
|
|
92
|
+
() => this.getInitState_());
|
|
93
|
+
}
|
|
80
94
|
|
|
81
95
|
this.init_();
|
|
82
96
|
};
|
|
@@ -191,6 +205,13 @@ shaka.cast.CastProxy.prototype.receiverName = function() {
|
|
|
191
205
|
* @export
|
|
192
206
|
*/
|
|
193
207
|
shaka.cast.CastProxy.prototype.cast = function() {
|
|
208
|
+
if (!this.sender_) {
|
|
209
|
+
throw new shaka.util.Error(
|
|
210
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
211
|
+
shaka.util.Error.Category.CAST,
|
|
212
|
+
shaka.util.Error.Code.CAST_RECEIVER_APP_ID_MISSING);
|
|
213
|
+
}
|
|
214
|
+
|
|
194
215
|
let initState = this.getInitState_();
|
|
195
216
|
|
|
196
217
|
// TODO: transfer manually-selected tracks?
|
|
@@ -215,7 +236,9 @@ shaka.cast.CastProxy.prototype.cast = function() {
|
|
|
215
236
|
* @export
|
|
216
237
|
*/
|
|
217
238
|
shaka.cast.CastProxy.prototype.setAppData = function(appData) {
|
|
218
|
-
this.sender_
|
|
239
|
+
if (this.sender_) {
|
|
240
|
+
this.sender_.setAppData(appData);
|
|
241
|
+
}
|
|
219
242
|
};
|
|
220
243
|
|
|
221
244
|
|
|
@@ -224,7 +247,44 @@ shaka.cast.CastProxy.prototype.setAppData = function(appData) {
|
|
|
224
247
|
* @export
|
|
225
248
|
*/
|
|
226
249
|
shaka.cast.CastProxy.prototype.suggestDisconnect = function() {
|
|
227
|
-
this.sender_
|
|
250
|
+
if (this.sender_) {
|
|
251
|
+
this.sender_.showDisconnectDialog();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {string} newAppId
|
|
258
|
+
* @export
|
|
259
|
+
*/
|
|
260
|
+
shaka.cast.CastProxy.prototype.changeReceiverId = async function(newAppId) {
|
|
261
|
+
if (newAppId == this.receiverAppId_) {
|
|
262
|
+
// Nothing to change
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.receiverAppId_ = newAppId;
|
|
267
|
+
|
|
268
|
+
// TODO: This code doesn't work correctly at the moment. Changing
|
|
269
|
+
// one working receiver id to another needs to be figured out.
|
|
270
|
+
// Destroy the old sender
|
|
271
|
+
if (this.sender_) {
|
|
272
|
+
this.sender_.forceDisconnect();
|
|
273
|
+
await this.sender_.destroy();
|
|
274
|
+
this.sender_ = null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
// Create the new one
|
|
279
|
+
this.sender_ = new shaka.cast.CastSender(
|
|
280
|
+
newAppId,
|
|
281
|
+
() => this.onCastStatusChanged_(),
|
|
282
|
+
() => this.onFirstCastStateUpdate_(),
|
|
283
|
+
(targetName, event) => this.onRemoteEvent_(targetName, event),
|
|
284
|
+
() => this.onResumeLocal_(),
|
|
285
|
+
() => this.getInitState_());
|
|
286
|
+
|
|
287
|
+
this.sender_.init();
|
|
228
288
|
};
|
|
229
289
|
|
|
230
290
|
|
|
@@ -233,7 +293,9 @@ shaka.cast.CastProxy.prototype.suggestDisconnect = function() {
|
|
|
233
293
|
* @export
|
|
234
294
|
*/
|
|
235
295
|
shaka.cast.CastProxy.prototype.forceDisconnect = function() {
|
|
236
|
-
this.sender_
|
|
296
|
+
if (this.sender_) {
|
|
297
|
+
this.sender_.forceDisconnect();
|
|
298
|
+
}
|
|
237
299
|
};
|
|
238
300
|
|
|
239
301
|
|
|
@@ -242,7 +304,9 @@ shaka.cast.CastProxy.prototype.forceDisconnect = function() {
|
|
|
242
304
|
* @private
|
|
243
305
|
*/
|
|
244
306
|
shaka.cast.CastProxy.prototype.init_ = function() {
|
|
245
|
-
this.sender_
|
|
307
|
+
if (this.sender_) {
|
|
308
|
+
this.sender_.init();
|
|
309
|
+
}
|
|
246
310
|
|
|
247
311
|
this.eventManager_ = new shaka.util.EventManager();
|
|
248
312
|
|
|
@@ -360,6 +424,9 @@ shaka.cast.CastProxy.prototype.onFirstCastStateUpdate_ = function() {
|
|
|
360
424
|
* @private
|
|
361
425
|
*/
|
|
362
426
|
shaka.cast.CastProxy.prototype.onResumeLocal_ = function() {
|
|
427
|
+
goog.asserts.assert(this.sender_,
|
|
428
|
+
'Cast sender should not be null!');
|
|
429
|
+
|
|
363
430
|
// Transfer back the player state.
|
|
364
431
|
shaka.cast.CastUtils.PlayerInitState.forEach(function(pair) {
|
|
365
432
|
let getter = pair[0];
|
|
@@ -447,7 +514,8 @@ shaka.cast.CastProxy.prototype.videoProxyGet_ = function(name) {
|
|
|
447
514
|
|
|
448
515
|
// If we are casting, but the first update has not come in yet, use local
|
|
449
516
|
// values, but not local methods.
|
|
450
|
-
if (this.sender_
|
|
517
|
+
if (this.sender_ && this.sender_.isCasting() &&
|
|
518
|
+
!this.sender_.hasRemoteProperties()) {
|
|
451
519
|
let value = this.localVideo_[name];
|
|
452
520
|
if (typeof value != 'function') {
|
|
453
521
|
return value;
|
|
@@ -455,7 +523,7 @@ shaka.cast.CastProxy.prototype.videoProxyGet_ = function(name) {
|
|
|
455
523
|
}
|
|
456
524
|
|
|
457
525
|
// Use local values and methods if we are not casting.
|
|
458
|
-
if (!this.sender_.isCasting()) {
|
|
526
|
+
if (!this.sender_ || !this.sender_.isCasting()) {
|
|
459
527
|
let value = this.localVideo_[name];
|
|
460
528
|
if (typeof value == 'function') {
|
|
461
529
|
value = value.bind(this.localVideo_);
|
|
@@ -473,7 +541,7 @@ shaka.cast.CastProxy.prototype.videoProxyGet_ = function(name) {
|
|
|
473
541
|
* @private
|
|
474
542
|
*/
|
|
475
543
|
shaka.cast.CastProxy.prototype.videoProxySet_ = function(name, value) {
|
|
476
|
-
if (!this.sender_.isCasting()) {
|
|
544
|
+
if (!this.sender_ || !this.sender_.isCasting()) {
|
|
477
545
|
this.localVideo_[name] = value;
|
|
478
546
|
return;
|
|
479
547
|
}
|
|
@@ -487,7 +555,7 @@ shaka.cast.CastProxy.prototype.videoProxySet_ = function(name, value) {
|
|
|
487
555
|
* @private
|
|
488
556
|
*/
|
|
489
557
|
shaka.cast.CastProxy.prototype.videoProxyLocalEvent_ = function(event) {
|
|
490
|
-
if (this.sender_.isCasting()) {
|
|
558
|
+
if (this.sender_ && this.sender_.isCasting()) {
|
|
491
559
|
// Ignore any unexpected local events while casting. Events can still be
|
|
492
560
|
// fired by the local video and Player when we unload() after the Cast
|
|
493
561
|
// connection is complete.
|
|
@@ -523,20 +591,22 @@ shaka.cast.CastProxy.prototype.playerProxyGet_ = function(name) {
|
|
|
523
591
|
if (name == 'getSharedConfiguration') {
|
|
524
592
|
shaka.log.warning(
|
|
525
593
|
'Can\'t share configuration across a network. Returning copy.');
|
|
526
|
-
return this.sender_
|
|
594
|
+
return this.sender_ ?
|
|
595
|
+
this.sender_.get('player', 'getConfiguration') :
|
|
596
|
+
this.localPlayer_.getConfiguration();
|
|
527
597
|
}
|
|
528
598
|
|
|
529
599
|
if (name == 'getNetworkingEngine') {
|
|
530
600
|
// Always returns a local instance, in case you need to make a request.
|
|
531
601
|
// Issues a warning, in case you think you are making a remote request
|
|
532
602
|
// or affecting remote filters.
|
|
533
|
-
if (this.sender_.isCasting()) {
|
|
603
|
+
if (this.sender_ && this.sender_.isCasting()) {
|
|
534
604
|
shaka.log.warning('NOTE: getNetworkingEngine() is always local!');
|
|
535
605
|
}
|
|
536
606
|
return this.localPlayer_.getNetworkingEngine.bind(this.localPlayer_);
|
|
537
607
|
}
|
|
538
608
|
|
|
539
|
-
if (this.sender_.isCasting()) {
|
|
609
|
+
if (this.sender_ && this.sender_.isCasting()) {
|
|
540
610
|
// These methods are unavailable or otherwise stubbed during casting.
|
|
541
611
|
if (name == 'getManifest' || name == 'drmInfo') {
|
|
542
612
|
return function() {
|
|
@@ -564,7 +634,8 @@ shaka.cast.CastProxy.prototype.playerProxyGet_ = function(name) {
|
|
|
564
634
|
|
|
565
635
|
// If we are casting, but the first update has not come in yet, use local
|
|
566
636
|
// getters, but not local methods.
|
|
567
|
-
if (this.sender_
|
|
637
|
+
if (this.sender_ && this.sender_.isCasting() &&
|
|
638
|
+
!this.sender_.hasRemoteProperties()) {
|
|
568
639
|
if (shaka.cast.CastUtils.PlayerGetterMethods[name]) {
|
|
569
640
|
let value = /** @type {Object} */(this.localPlayer_)[name];
|
|
570
641
|
goog.asserts.assert(typeof value == 'function', 'only methods on Player');
|
|
@@ -573,7 +644,7 @@ shaka.cast.CastProxy.prototype.playerProxyGet_ = function(name) {
|
|
|
573
644
|
}
|
|
574
645
|
|
|
575
646
|
// Use local getters and methods if we are not casting.
|
|
576
|
-
if (!this.sender_.isCasting()) {
|
|
647
|
+
if (!this.sender_ || !this.sender_.isCasting()) {
|
|
577
648
|
let value = /** @type {Object} */(this.localPlayer_)[name];
|
|
578
649
|
goog.asserts.assert(typeof value == 'function', 'only methods on Player');
|
|
579
650
|
return value.bind(this.localPlayer_);
|
|
@@ -588,7 +659,7 @@ shaka.cast.CastProxy.prototype.playerProxyGet_ = function(name) {
|
|
|
588
659
|
* @private
|
|
589
660
|
*/
|
|
590
661
|
shaka.cast.CastProxy.prototype.playerProxyLocalEvent_ = function(event) {
|
|
591
|
-
if (this.sender_.isCasting()) {
|
|
662
|
+
if (this.sender_ && this.sender_.isCasting()) {
|
|
592
663
|
// Ignore any unexpected local events while casting.
|
|
593
664
|
return;
|
|
594
665
|
}
|
|
@@ -603,7 +674,7 @@ shaka.cast.CastProxy.prototype.playerProxyLocalEvent_ = function(event) {
|
|
|
603
674
|
* @private
|
|
604
675
|
*/
|
|
605
676
|
shaka.cast.CastProxy.prototype.onRemoteEvent_ = function(targetName, event) {
|
|
606
|
-
goog.asserts.assert(this.sender_.isCasting(),
|
|
677
|
+
goog.asserts.assert(this.sender_ && this.sender_.isCasting(),
|
|
607
678
|
'Should only receive remote events while casting');
|
|
608
679
|
if (!this.sender_.isCasting()) {
|
|
609
680
|
// Ignore any unexpected remote events.
|
|
@@ -39,7 +39,10 @@ goog.require('shaka.util.Timer');
|
|
|
39
39
|
* local Player instance.
|
|
40
40
|
* @param {!shaka.Player} player A local Player instance.
|
|
41
41
|
* @param {function(Object)=} appDataCallback A callback to handle
|
|
42
|
-
* application-specific data passed from the sender.
|
|
42
|
+
* application-specific data passed from the sender. This can come either
|
|
43
|
+
* from a Shaka-based sender through CastProxy.setAppData, or from a
|
|
44
|
+
* sender using the customData field of the LOAD message of the standard
|
|
45
|
+
* Cast message namespace. It can also be null if no such data is sent.
|
|
43
46
|
* @param {function(string):string=} contentIdCallback A callback to
|
|
44
47
|
* retrieve manifest URI from the provided content id.
|
|
45
48
|
* @implements {shaka.util.IDestroyable}
|
|
@@ -685,6 +688,10 @@ shaka.cast.CastReceiver.prototype.onGenericMessage_ = function(event) {
|
|
|
685
688
|
let currentTime = message['currentTime'];
|
|
686
689
|
let assetUri = this.contentIdCallback_(contentId);
|
|
687
690
|
let autoplay = message['autoplay'] || true;
|
|
691
|
+
let customData = mediaInfo['customData'];
|
|
692
|
+
|
|
693
|
+
this.appDataCallback_(customData);
|
|
694
|
+
|
|
688
695
|
if (autoplay) {
|
|
689
696
|
this.video_.autoplay = true;
|
|
690
697
|
}
|
|
@@ -21,6 +21,7 @@ goog.require('goog.asserts');
|
|
|
21
21
|
goog.require('shaka.log');
|
|
22
22
|
goog.require('shaka.util.Error');
|
|
23
23
|
goog.require('shaka.util.ManifestParserUtils');
|
|
24
|
+
goog.require('shaka.util.Pssh');
|
|
24
25
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
25
26
|
goog.require('shaka.util.XmlUtils');
|
|
26
27
|
|
|
@@ -442,6 +443,36 @@ shaka.dash.ContentProtection.getPlayReadyLicenseUrl = function(element) {
|
|
|
442
443
|
};
|
|
443
444
|
|
|
444
445
|
|
|
446
|
+
/**
|
|
447
|
+
* Gets a PlayReady initData from a content protection element
|
|
448
|
+
* containing a PlayReady Pro Object
|
|
449
|
+
*
|
|
450
|
+
* @param {shaka.dash.ContentProtection.Element} element
|
|
451
|
+
* @return {?Array.<shaka.extern.InitDataOverride>}
|
|
452
|
+
* @private
|
|
453
|
+
*/
|
|
454
|
+
shaka.dash.ContentProtection.getInitDataFromPro_ = function(element) {
|
|
455
|
+
const proNode = shaka.util.XmlUtils.findChildNS(
|
|
456
|
+
element.node, 'urn:microsoft:playready', 'pro');
|
|
457
|
+
if (!proNode) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
461
|
+
const data = Uint8ArrayUtils.fromBase64(proNode.textContent);
|
|
462
|
+
const systemId = new Uint8Array([
|
|
463
|
+
0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
|
|
464
|
+
0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95,
|
|
465
|
+
]);
|
|
466
|
+
const pssh = shaka.util.Pssh.createPssh(data, systemId);
|
|
467
|
+
return [
|
|
468
|
+
{
|
|
469
|
+
initData: pssh,
|
|
470
|
+
initDataType: 'cenc',
|
|
471
|
+
keyId: element.keyId,
|
|
472
|
+
},
|
|
473
|
+
];
|
|
474
|
+
};
|
|
475
|
+
|
|
445
476
|
/**
|
|
446
477
|
* Creates DrmInfo objects from the given element.
|
|
447
478
|
*
|
|
@@ -468,7 +499,8 @@ shaka.dash.ContentProtection.convertElements_ = function(
|
|
|
468
499
|
!element.init || element.init.length,
|
|
469
500
|
'Init data must be null or non-empty.');
|
|
470
501
|
|
|
471
|
-
|
|
502
|
+
const proInitData = ContentProtection.getInitDataFromPro_(element);
|
|
503
|
+
const initData = element.init || defaultInit || proInitData;
|
|
472
504
|
const info = ManifestParserUtils.createDrmInfo(keySystem, initData);
|
|
473
505
|
const licenseParser = licenseUrlParsers.get(keySystem);
|
|
474
506
|
if (licenseParser) {
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -509,7 +509,7 @@ shaka.dash.DashParser.prototype.processManifest_ =
|
|
|
509
509
|
manifestBaseUris, uris);
|
|
510
510
|
|
|
511
511
|
let ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
|
|
512
|
-
let minBufferTime;
|
|
512
|
+
let minBufferTime = 0;
|
|
513
513
|
if (!ignoreMinBufferTime) {
|
|
514
514
|
minBufferTime =
|
|
515
515
|
XmlUtils.parseAttr(mpd, 'minBufferTime', XmlUtils.parseDuration);
|
|
@@ -1016,7 +1016,7 @@ shaka.dash.DashParser.prototype.parseAdaptationSet_ = function(context, elem) {
|
|
|
1016
1016
|
case 'subtitle':
|
|
1017
1017
|
kind = value;
|
|
1018
1018
|
break;
|
|
1019
|
-
|
|
1019
|
+
}
|
|
1020
1020
|
}
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
@@ -1038,12 +1038,12 @@ shaka.dash.DashParser.prototype.parseAdaptationSet_ = function(context, elem) {
|
|
|
1038
1038
|
let closedCaptions = new Map();
|
|
1039
1039
|
for (const prop of accessibilities) {
|
|
1040
1040
|
let schemeId = prop.getAttribute('schemeIdUri');
|
|
1041
|
+
let value = prop.getAttribute('value');
|
|
1041
1042
|
if (schemeId == 'urn:scte:dash:cc:cea-608:2015' ||
|
|
1042
1043
|
schemeId == 'urn:scte:dash:cc:cea-708:2015') {
|
|
1043
1044
|
let channelId = 1;
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
closedCaptionsValue.split(';').forEach((captionStr) => {
|
|
1045
|
+
if (value != null) {
|
|
1046
|
+
value.split(';').forEach((captionStr) => {
|
|
1047
1047
|
let channel;
|
|
1048
1048
|
let language;
|
|
1049
1049
|
// Some closed caption descriptions have channel number and language,
|
|
@@ -1075,6 +1075,14 @@ shaka.dash.DashParser.prototype.parseAdaptationSet_ = function(context, elem) {
|
|
|
1075
1075
|
// 'CC1' as channel id and 'und' as language info.
|
|
1076
1076
|
closedCaptions.set('CC1', 'und');
|
|
1077
1077
|
}
|
|
1078
|
+
} else if (schemeId == 'urn:mpeg:dash:role:2011') {
|
|
1079
|
+
// See DASH IOP 3.9.2 Table 4.
|
|
1080
|
+
if (value != null) {
|
|
1081
|
+
roleValues.push(value);
|
|
1082
|
+
if (value == 'captions') {
|
|
1083
|
+
kind = ManifestParserUtils.TextStreamKind.CLOSED_CAPTION;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1078
1086
|
}
|
|
1079
1087
|
}
|
|
1080
1088
|
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -539,8 +539,15 @@ shaka.hls.HlsParser.prototype.createPeriod_ = async function(playlist) {
|
|
|
539
539
|
return type == 'SUBTITLES';
|
|
540
540
|
}.bind(this));
|
|
541
541
|
|
|
542
|
-
let textStreamPromises = textStreamTags.map(function(tag) {
|
|
543
|
-
|
|
542
|
+
let textStreamPromises = textStreamTags.map(async function(tag) {
|
|
543
|
+
try {
|
|
544
|
+
return await this.createTextStream_(tag, playlist);
|
|
545
|
+
} catch (e) {
|
|
546
|
+
if (this.config_.hls.ignoreTextStreamFailures) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
throw e;
|
|
550
|
+
}
|
|
544
551
|
}.bind(this));
|
|
545
552
|
|
|
546
553
|
const closedCaptionsTags = mediaTags.filter((tag) => {
|
|
@@ -567,7 +574,7 @@ shaka.hls.HlsParser.prototype.createPeriod_ = async function(playlist) {
|
|
|
567
574
|
return {
|
|
568
575
|
startTime: 0,
|
|
569
576
|
variants: variants,
|
|
570
|
-
textStreams: textStreams,
|
|
577
|
+
textStreams: textStreams.filter((t) => t != null),
|
|
571
578
|
};
|
|
572
579
|
};
|
|
573
580
|
|
|
@@ -660,7 +667,9 @@ shaka.hls.HlsParser.prototype.createVariantsForTag_ =
|
|
|
660
667
|
// attached to the stream.
|
|
661
668
|
const textStreamInfo =
|
|
662
669
|
this.mediaTagsToStreamInfosMap_.get(textTags[0].id);
|
|
663
|
-
textStreamInfo
|
|
670
|
+
if (textStreamInfo) {
|
|
671
|
+
textStreamInfo.stream.codecs = textCodecs;
|
|
672
|
+
}
|
|
664
673
|
}
|
|
665
674
|
}
|
|
666
675
|
|
|
@@ -1672,6 +1681,13 @@ shaka.hls.HlsParser.prototype.getStartTime_ = async function(
|
|
|
1672
1681
|
return this.getStartTimeFromTextSegment_(mimeType, codecs, response.data);
|
|
1673
1682
|
}
|
|
1674
1683
|
|
|
1684
|
+
if (mimeType == 'video/webm') {
|
|
1685
|
+
shaka.log.warning(
|
|
1686
|
+
'Hls+WebM combination is not supported at the moment. Skipping.');
|
|
1687
|
+
return null;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
|
|
1675
1691
|
// TODO(vaage): Add support for additional formats.
|
|
1676
1692
|
//
|
|
1677
1693
|
// Formats:
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -22,6 +22,7 @@ goog.require('shaka.log');
|
|
|
22
22
|
goog.require('shaka.net.NetworkingEngine');
|
|
23
23
|
goog.require('shaka.util.Error');
|
|
24
24
|
goog.require('shaka.util.EventManager');
|
|
25
|
+
goog.require('shaka.util.FairPlayUtils');
|
|
25
26
|
goog.require('shaka.util.FakeEvent');
|
|
26
27
|
goog.require('shaka.util.IDestroyable');
|
|
27
28
|
goog.require('shaka.util.Iterables');
|
|
@@ -1115,6 +1116,29 @@ shaka.media.DrmEngine.prototype.createTemporarySession_ =
|
|
|
1115
1116
|
};
|
|
1116
1117
|
this.activeSessions_.set(session, metadata);
|
|
1117
1118
|
|
|
1119
|
+
try {
|
|
1120
|
+
if (this.config_.initDataTransform) {
|
|
1121
|
+
initData = this.config_.initDataTransform(initData);
|
|
1122
|
+
} else if (this.keySystem().startsWith('com.apple.fps')) {
|
|
1123
|
+
const cert = this.currentDrmInfo_.serverCertificate;
|
|
1124
|
+
const contentId =
|
|
1125
|
+
shaka.util.FairPlayUtils.defaultGetContentId(initData);
|
|
1126
|
+
initData = shaka.util.FairPlayUtils.initDataTransform(
|
|
1127
|
+
initData, contentId, cert);
|
|
1128
|
+
}
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
let shakaError = error;
|
|
1131
|
+
if (!(error instanceof shaka.util.Error)) {
|
|
1132
|
+
shakaError = new shaka.util.Error(
|
|
1133
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
1134
|
+
shaka.util.Error.Category.DRM,
|
|
1135
|
+
shaka.util.Error.Code.INIT_DATA_TRANSFORM_ERROR,
|
|
1136
|
+
error);
|
|
1137
|
+
}
|
|
1138
|
+
this.onError_(shakaError);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1118
1142
|
session.generateRequest(initDataType, initData.buffer).catch((error) => {
|
|
1119
1143
|
if (this.isDestroying_) { return; }
|
|
1120
1144
|
|
|
@@ -1186,13 +1210,15 @@ shaka.media.DrmEngine.prototype.sendLicenseRequest_ = function(event) {
|
|
|
1186
1210
|
request.body = event.message;
|
|
1187
1211
|
request.method = 'POST';
|
|
1188
1212
|
request.licenseRequestType = event.messageType;
|
|
1213
|
+
request.sessionId = session.sessionId;
|
|
1189
1214
|
// NOTE: allowCrossSiteCredentials can be set in a request filter.
|
|
1190
1215
|
|
|
1191
1216
|
if (this.currentDrmInfo_.keySystem == 'com.microsoft.playready' ||
|
|
1192
1217
|
this.currentDrmInfo_.keySystem == 'com.chromecast.playready') {
|
|
1193
1218
|
this.unpackPlayReadyRequest_(request);
|
|
1194
1219
|
}
|
|
1195
|
-
if (this.currentDrmInfo_.keySystem.startsWith('com.apple.fps')
|
|
1220
|
+
if (this.currentDrmInfo_.keySystem.startsWith('com.apple.fps') &&
|
|
1221
|
+
this.config_.fairPlayTransform) {
|
|
1196
1222
|
this.formatFairPlayRequest_(request);
|
|
1197
1223
|
}
|
|
1198
1224
|
|
|
@@ -1200,7 +1226,8 @@ shaka.media.DrmEngine.prototype.sendLicenseRequest_ = function(event) {
|
|
|
1200
1226
|
.then(function(response) {
|
|
1201
1227
|
if (this.isDestroying_) { return Promise.reject(); }
|
|
1202
1228
|
|
|
1203
|
-
if (this.currentDrmInfo_.keySystem.startsWith('com.apple.fps')
|
|
1229
|
+
if (this.currentDrmInfo_.keySystem.startsWith('com.apple.fps') &&
|
|
1230
|
+
this.config_.fairPlayTransform) {
|
|
1204
1231
|
this.parseFairPlayResponse_(response);
|
|
1205
1232
|
}
|
|
1206
1233
|
|
|
@@ -241,7 +241,6 @@ shaka.media.MediaSourceEngine.prototype.destroy = function() {
|
|
|
241
241
|
|
|
242
242
|
// The rest will be rejected silently if possible.
|
|
243
243
|
for (let i = 1; i < q.length; ++i) {
|
|
244
|
-
q[i].p.catch(Functional.noop);
|
|
245
244
|
q[i].p.reject();
|
|
246
245
|
}
|
|
247
246
|
}
|
|
@@ -705,9 +704,13 @@ shaka.media.MediaSourceEngine.prototype.setStreamProperties = function(
|
|
|
705
704
|
*/
|
|
706
705
|
shaka.media.MediaSourceEngine.prototype.endOfStream = function(reason) {
|
|
707
706
|
return this.enqueueBlockingOperation_(function() {
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
|
|
707
|
+
// If endOfStream() has already been called on the media source,
|
|
708
|
+
// don't call it again.
|
|
709
|
+
if (this.ended()) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
// Tizen and IE11 won't let us pass undefined, but it will let us omit the
|
|
713
|
+
// argument.
|
|
711
714
|
if (reason) {
|
|
712
715
|
this.mediaSource_.endOfStream(reason);
|
|
713
716
|
} else {
|
|
@@ -1016,7 +1019,7 @@ shaka.media.MediaSourceEngine.prototype.enqueueBlockingOperation_ =
|
|
|
1016
1019
|
}
|
|
1017
1020
|
|
|
1018
1021
|
return ret;
|
|
1019
|
-
}.bind(this), function() {
|
|
1022
|
+
}.bind(this), function(error) {
|
|
1020
1023
|
// One of the waiters failed, which means we've been destroyed.
|
|
1021
1024
|
goog.asserts.assert(this.destroyed_, 'Should be destroyed by now');
|
|
1022
1025
|
// We haven't popped from the queue. Canceled waiters have been removed by
|
|
@@ -1036,7 +1039,7 @@ shaka.media.MediaSourceEngine.prototype.enqueueBlockingOperation_ =
|
|
|
1036
1039
|
}
|
|
1037
1040
|
}
|
|
1038
1041
|
}
|
|
1039
|
-
|
|
1042
|
+
throw error;
|
|
1040
1043
|
}.bind(this));
|
|
1041
1044
|
};
|
|
1042
1045
|
|
|
@@ -113,11 +113,22 @@ shaka.media.PlayRateController = class {
|
|
|
113
113
|
const rate = this.calculateCurrentRate_();
|
|
114
114
|
|
|
115
115
|
if (rate >= 0) {
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
try {
|
|
117
|
+
this.applyRate_(rate);
|
|
118
|
+
return;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
// Fall through to the next clause.
|
|
121
|
+
//
|
|
122
|
+
// Fast forward is accomplished through setting video.playbackRate.
|
|
123
|
+
// If the play rate value is not supported by the browser (too big),
|
|
124
|
+
// the browsers will throw.
|
|
125
|
+
// Use this as a cue to fall back to fast forward through repeated
|
|
126
|
+
// seeking, which is what we do for rewind as well.
|
|
127
|
+
}
|
|
118
128
|
}
|
|
119
129
|
|
|
120
|
-
// When moving backwards
|
|
130
|
+
// When moving backwards or forwards in large steps,
|
|
131
|
+
// set the playback rate to 0 so that we can manually
|
|
121
132
|
// seek backwards with out fighting the playhead.
|
|
122
133
|
this.timer_.tickEvery(this.pollRate_);
|
|
123
134
|
this.applyRate_(0);
|
package/lib/media/playhead.js
CHANGED
|
@@ -86,9 +86,14 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
86
86
|
// We listen for the loaded-metadata-event so that we know when we can
|
|
87
87
|
// interact with |currentTime|.
|
|
88
88
|
const onLoaded = () => {
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (this.startTime_ == null) {
|
|
90
|
+
this.started_ = true;
|
|
91
|
+
} else {
|
|
92
|
+
// Startup is complete only when the video element acknowledges the
|
|
93
|
+
// seek.
|
|
94
|
+
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
|
|
95
|
+
this.started_ = true;
|
|
96
|
+
});
|
|
92
97
|
this.mediaElement_.currentTime = this.startTime_;
|
|
93
98
|
}
|
|
94
99
|
};
|