shaka-player 4.0.0 → 4.1.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 (114) hide show
  1. package/.github/config.yml +1 -0
  2. package/.github/workflows/release-please.yaml +6 -11
  3. package/CHANGELOG.md +33 -0
  4. package/CONTRIBUTORS +1 -0
  5. package/README.md +4 -4
  6. package/build/misspellings.txt +1 -0
  7. package/demo/common/message_ids.js +1 -0
  8. package/demo/config.js +3 -1
  9. package/demo/demo.less +0 -1
  10. package/demo/locales/en.json +1 -0
  11. package/demo/locales/source.json +4 -0
  12. package/dist/controls.css +3 -3
  13. package/dist/controls.css.map +1 -1
  14. package/dist/demo.compiled.debug.js +4 -4
  15. package/dist/demo.compiled.debug.map +2 -2
  16. package/dist/demo.compiled.js +4 -4
  17. package/dist/demo.compiled.map +2 -2
  18. package/dist/demo.css +1 -1
  19. package/dist/demo.css.map +1 -1
  20. package/dist/deps.js +3 -3
  21. package/dist/locales.js +32 -32
  22. package/dist/receiver.compiled.debug.js +2 -2
  23. package/dist/receiver.compiled.debug.map +2 -2
  24. package/dist/receiver.compiled.js +2 -2
  25. package/dist/receiver.compiled.map +2 -2
  26. package/dist/shaka-player.compiled.d.ts +15 -8
  27. package/dist/shaka-player.compiled.debug.d.ts +15 -8
  28. package/dist/shaka-player.compiled.debug.externs.js +11 -2
  29. package/dist/shaka-player.compiled.debug.js +266 -253
  30. package/dist/shaka-player.compiled.debug.map +3 -3
  31. package/dist/shaka-player.compiled.externs.js +11 -2
  32. package/dist/shaka-player.compiled.js +792 -784
  33. package/dist/shaka-player.compiled.map +3 -3
  34. package/dist/shaka-player.ui.d.ts +16 -9
  35. package/dist/shaka-player.ui.debug.d.ts +16 -9
  36. package/dist/shaka-player.ui.debug.externs.js +11 -2
  37. package/dist/shaka-player.ui.debug.js +299 -282
  38. package/dist/shaka-player.ui.debug.map +3 -3
  39. package/dist/shaka-player.ui.externs.js +11 -2
  40. package/dist/shaka-player.ui.js +948 -941
  41. package/dist/shaka-player.ui.map +3 -3
  42. package/docs/design/chromecast.md +3 -1
  43. package/docs/tutorials/drm-config.md +16 -0
  44. package/docs/tutorials/ui.md +26 -0
  45. package/docs/tutorials/upgrade.md +4 -2
  46. package/externs/chromecast.js +9 -2
  47. package/externs/shaka/manifest.js +7 -0
  48. package/externs/shaka/player.js +32 -2
  49. package/externs/shaka/text.js +7 -0
  50. package/karma.conf.js +4 -0
  51. package/lib/abr/simple_abr_manager.js +2 -1
  52. package/lib/cast/cast_proxy.js +19 -8
  53. package/lib/cast/cast_receiver.js +3 -3
  54. package/lib/cast/cast_sender.js +12 -2
  55. package/lib/dash/dash_parser.js +2 -2
  56. package/lib/hls/hls_parser.js +46 -15
  57. package/lib/media/drm_engine.js +36 -8
  58. package/lib/media/gap_jumping_controller.js +23 -1
  59. package/lib/media/media_source_engine.js +91 -37
  60. package/lib/media/playhead.js +47 -4
  61. package/lib/media/stall_detector.js +19 -2
  62. package/lib/media/streaming_engine.js +9 -3
  63. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  64. package/lib/offline/manifest_converter.js +1 -0
  65. package/lib/player.js +339 -175
  66. package/lib/text/cue.js +6 -0
  67. package/lib/text/ui_text_displayer.js +1 -0
  68. package/lib/text/vtt_text_parser.js +11 -7
  69. package/lib/util/config_utils.js +25 -0
  70. package/lib/util/error.js +13 -0
  71. package/lib/util/fake_event.js +39 -0
  72. package/lib/util/manifest_parser_utils.js +1 -1
  73. package/lib/util/mime_utils.js +62 -6
  74. package/lib/util/periods.js +5 -4
  75. package/lib/util/player_configuration.js +16 -14
  76. package/lib/util/stats.js +28 -1
  77. package/lib/util/stream_utils.js +11 -2
  78. package/package.json +5 -2
  79. package/test/cast/cast_proxy_unit.js +3 -1
  80. package/test/cast/cast_sender_unit.js +21 -6
  81. package/test/demo/demo_unit.js +2 -1
  82. package/test/hls/hls_live_unit.js +79 -7
  83. package/test/hls/manifest_text_parser_unit.js +19 -0
  84. package/test/media/adaptation_set_unit.js +1 -0
  85. package/test/media/playhead_unit.js +114 -25
  86. package/test/media/stall_detector_unit.js +20 -2
  87. package/test/media/streaming_engine_integration.js +2 -1
  88. package/test/media/streaming_engine_unit.js +1 -0
  89. package/test/offline/indexeddb_storage_unit.js +1 -1
  90. package/test/offline/storage_compatibility_unit.js +1 -1
  91. package/test/player_external.js +7 -1
  92. package/test/player_integration.js +9 -10
  93. package/test/player_src_equals_integration.js +81 -83
  94. package/test/player_unit.js +240 -2
  95. package/test/test/boot.js +9 -0
  96. package/test/test/externs/filters.js +4 -0
  97. package/test/test/util/manifest_generator.js +2 -0
  98. package/test/test/util/simple_fakes.js +45 -2
  99. package/test/test/util/streaming_engine_util.js +1 -0
  100. package/test/text/vtt_text_parser_unit.js +68 -5
  101. package/test/ui/ui_unit.js +18 -5
  102. package/test/util/mime_utils_unit.js +54 -0
  103. package/test/util/periods_unit.js +79 -0
  104. package/test/util/stream_utils_unit.js +108 -0
  105. package/ui/controls.js +12 -8
  106. package/ui/externs/ui.js +10 -1
  107. package/ui/less/containers.less +1 -1
  108. package/ui/less/general.less +1 -1
  109. package/ui/less/range_elements.less +1 -0
  110. package/ui/resolution_selection.js +1 -1
  111. package/ui/statistics_button.js +10 -0
  112. package/ui/ui.js +12 -1
  113. package/.github/pull_request_template.md +0 -10
  114. package/docs/tutorials/gsoc-ideas-list.md +0 -3
@@ -26,11 +26,13 @@ the `Player` and `HTMLMediaElement` objects. Receiver apps only have to worry
26
26
  about their UI, while the `CastReceiver` takes care of playback and
27
27
  communication.
28
28
 
29
+ To enable an Android (TV) receiver apps set `androidReceiverCompatible` to true.
30
+
29
31
 
30
32
  #### `CastProxy` API sketch
31
33
 
32
34
  ```js
33
- new shaka.cast.CastProxy(video, player, receiverAppId)
35
+ new shaka.cast.CastProxy(video, player, receiverAppId, androidReceiverCompatible)
34
36
 
35
37
  // Also destroys the underlying local Player object
36
38
  shaka.cast.CastProxy.prototype.destroy() => Promise
@@ -75,6 +75,22 @@ player.configure({
75
75
  If the browser supports it and you configured a license server URL for it, we'll
76
76
  use it.
77
77
 
78
+ Alternative there are a config for make a mapping of keysystem if you know that
79
+ is broadly supported. For example, `com.microsoft.playready.recommendation`:
80
+
81
+ ```js
82
+ player.configure({
83
+ drm: {
84
+ keySystemsMapping: {
85
+ 'com.microsoft.playready': 'com.microsoft.playready.recommendation',
86
+ }
87
+ }
88
+ });
89
+ ```
90
+
91
+ With the previous configuration you will choose the `recommendation` keySystem
92
+ when your manifest (HLS or DASH) uses PlayReady.
93
+
78
94
 
79
95
  #### Clear Key
80
96
 
@@ -130,6 +130,23 @@ Next, let's add a listener to the 'caststatuschanged' event in myapp.js:
130
130
  }
131
131
  ```
132
132
 
133
+
134
+ #### Enabling Android Receiver Apps
135
+
136
+ If you'd like to take advantage of Android Receiver App support,
137
+ you will need to provide a boolean flag to enable support for
138
+ casting to an Android receiver app.
139
+
140
+ ```html
141
+ <div data-shaka-player-container style="max-width:40em"
142
+ data-shaka-player-cast-receiver-id="E7271BEC"
143
+ data-shaka-player-cast-android-receiver-compatible="true">
144
+ <!-- The manifest url in the src attribute will be automatically loaded -->
145
+ <video autoplay data-shaka-player id="video" style="width:100%;height:100%"
146
+ src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
147
+ </div
148
+
149
+
133
150
  #### Providing source(s) for auto load.
134
151
 
135
152
  It's also possible to provide the `src` attribute on the `<video>` element
@@ -200,6 +217,15 @@ const controls = ui.getControls();
200
217
  // your API calls will be routed to the remote playback session.
201
218
  const player = controls.getPlayer();
202
219
  const video = controls.getVideo();
220
+
221
+ // Programatically configure the Chromecast Receiver App Id and Android
222
+ // Receiver Compatability.
223
+ ui.configure({
224
+ // Set the castReceiverAppId
225
+ 'castReceiverAppId': 'BBED8D28',
226
+ // Enable casting to native Android Apps (e.g. Android TV Apps)
227
+ 'castAndroidReceiverCompatible': true,
228
+ });
203
229
  ```
204
230
 
205
231
 
@@ -42,10 +42,12 @@ application:
42
42
  `manifest.defaultPresentationDelay` (deprecated in v3.0.0)
43
43
  - Configuration of factories should be plain factory functions, not
44
44
  constructors; these will not be invoked with `new` (deprecated in v3.1.0)
45
- - `drm.initDataTransform` has been removed (no longer needed since the
46
- minimum supported version of iOS is now 13)
45
+ - `drm.initDataTransform` now defaults to a no-op
47
46
  - `streaming.smallGapLimit` and `streaming.jumpLargeGaps` have been removed;
48
47
  all gaps will now be jumped
48
+ - `manifest.hls.useFullSegmentsForStartTime` has been removed; this setting
49
+ is no longer necessary, as we no longer fetch segments for start times in
50
+ the HLS parser
49
51
 
50
52
  - Player API changes:
51
53
  - `shaka.Player.prototype.addTextTrack()` has been replaced by
@@ -270,6 +270,13 @@ chrome.cast.Session = class {
270
270
 
271
271
 
272
272
  chrome.cast.SessionRequest = class {
273
- /** @param {string} appId */
274
- constructor(appId) {}
273
+ /**
274
+ * @param {string} appId
275
+ * @param {Array.<Object>} capabilities
276
+ * @param {?number} timeout
277
+ * @param {boolean} androidReceiverCompatible
278
+ * @param {Object} credentialsData
279
+ */
280
+ constructor(appId, capabilities, timeout, androidReceiverCompatible,
281
+ credentialsData) {}
275
282
  };
@@ -176,6 +176,7 @@ shaka.extern.DrmInfo;
176
176
  * @typedef {{
177
177
  * id: number,
178
178
  * language: string,
179
+ * disabledUntilTime: number,
179
180
  * primary: boolean,
180
181
  * audio: ?shaka.extern.Stream,
181
182
  * video: ?shaka.extern.Stream,
@@ -198,6 +199,12 @@ shaka.extern.DrmInfo;
198
199
  * The Variant's language, specified as a language code. <br>
199
200
  * See {@link https://tools.ietf.org/html/rfc5646} <br>
200
201
  * See {@link http://www.iso.org/iso/home/standards/language_codes.htm}
202
+ * @property {number} disabledUntilTime
203
+ * <i>Defaults to 0.</i> <br>
204
+ * 0 means the variant is enabled. The Player will set this value to
205
+ * "(Date.now() / 1000) + config.streaming.maxDisabledTime" and once this
206
+ * maxDisabledTime has passed Player will set the value to 0 in order to
207
+ * reenable the variant.
201
208
  * @property {boolean} primary
202
209
  * <i>Defaults to false.</i> <br>
203
210
  * True indicates that the player should use this Variant over others if user
@@ -82,6 +82,9 @@ shaka.extern.StateChange;
82
82
  *
83
83
  * maxSegmentDuration: number,
84
84
  *
85
+ * gapsJumped: number,
86
+ * stallsDetected: number,
87
+ *
85
88
  * switchHistory: !Array.<shaka.extern.TrackChoice>,
86
89
  * stateHistory: !Array.<shaka.extern.StateChange>
87
90
  * }}
@@ -112,6 +115,11 @@ shaka.extern.StateChange;
112
115
  * @property {number} estimatedBandwidth
113
116
  * The current estimated network bandwidth (in bit/sec).
114
117
  *
118
+ * @property {number} gapsJumped
119
+ * The total number of playback gaps jumped by the GapJumpingController.
120
+ * @property {number} stallsDetected
121
+ * The total number of playback stalls detected by the StallDetector.
122
+ *
115
123
  * @property {number} completionPercent
116
124
  * This is the greatest completion percent that the user has experienced in
117
125
  * playback. Also known as the "high water mark". Is NaN when there is no
@@ -618,9 +626,13 @@ shaka.extern.AdvancedDrmConfiguration;
618
626
  * clearKeys: !Object.<string, string>,
619
627
  * delayLicenseRequestUntilPlayed: boolean,
620
628
  * advanced: Object.<string, shaka.extern.AdvancedDrmConfiguration>,
629
+ * initDataTransform:
630
+ * ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
631
+ * undefined),
621
632
  * logLicenseExchange: boolean,
622
633
  * updateExpirationTime: number,
623
- * preferredKeySystems: !Array.<string>
634
+ * preferredKeySystems: !Array.<string>,
635
+ * keySystemsMapping: !Object.<string, string>
624
636
  * }}
625
637
  *
626
638
  * @property {shaka.extern.RetryParameters} retryParameters
@@ -641,6 +653,14 @@ shaka.extern.AdvancedDrmConfiguration;
641
653
  * <i>Optional.</i> <br>
642
654
  * A dictionary which maps key system IDs to advanced DRM configuration for
643
655
  * those key systems.
656
+ * @property
657
+ * {((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
658
+ * undefined)}
659
+ * initDataTransform
660
+ * <i>Optional.</i><br>
661
+ * If given, this function is called with the init data from the
662
+ * manifest/media and should return the (possibly transformed) init data to
663
+ * pass to the browser.
644
664
  * @property {boolean} logLicenseExchange
645
665
  * <i>Optional.</i><br>
646
666
  * If set to <code>true</code>, prints logs containing the license exchange.
@@ -653,6 +673,8 @@ shaka.extern.AdvancedDrmConfiguration;
653
673
  * @property {!Array.<string>} preferredKeySystems
654
674
  * <i>Defaults to an empty array. </i> <br>
655
675
  * Specifies the priorties of available DRM key systems.
676
+ * @property {Object.<string, string>} keySystemsMapping
677
+ * A map of key system name to key system name.
656
678
  *
657
679
  * @exportDoc
658
680
  */
@@ -852,7 +874,8 @@ shaka.extern.ManifestConfiguration;
852
874
  * preferNativeHls: boolean,
853
875
  * updateIntervalSeconds: number,
854
876
  * dispatchAllEmsgBoxes: boolean,
855
- * observeQualityChanges: boolean
877
+ * observeQualityChanges: boolean,
878
+ * maxDisabledTime: number
856
879
  * }}
857
880
  *
858
881
  * @description
@@ -957,6 +980,10 @@ shaka.extern.ManifestConfiguration;
957
980
  * @property {boolean} observeQualityChanges
958
981
  * If true, monitor media quality changes and emit
959
982
  * <code.shaka.Player.MediaQualityChangedEvent</code>.
983
+ * @property {number} maxDisabledTime
984
+ * The maximum time a variant can be disabled when NETWORK HTTP_ERROR
985
+ * is reached, in seconds.
986
+ * If all variants are disabled this way, NETWORK HTTP_ERROR will be thrown.
960
987
  * @exportDoc
961
988
  */
962
989
  shaka.extern.StreamingConfiguration;
@@ -1253,11 +1280,14 @@ shaka.extern.Thumbnail;
1253
1280
 
1254
1281
  /**
1255
1282
  * @typedef {{
1283
+ * id: string,
1256
1284
  * title: string,
1257
1285
  * startTime: number,
1258
1286
  * endTime: number
1259
1287
  * }}
1260
1288
  *
1289
+ * @property {string} id
1290
+ * The id of the chapter.
1261
1291
  * @property {string} title
1262
1292
  * The title of the chapter.
1263
1293
  * @property {number} startTime
@@ -297,6 +297,13 @@ shaka.extern.Cue = class {
297
297
  */
298
298
  this.fontFamily;
299
299
 
300
+ /**
301
+ * Text shadow color as a CSS text-shadow value.
302
+ * @type {string}
303
+ * @exportDoc
304
+ */
305
+ this.textShadow = '';
306
+
300
307
  /**
301
308
  * Text stroke color as a CSS color, e.g. "#FFFFFF" or "white".
302
309
  * @type {string}
package/karma.conf.js CHANGED
@@ -363,6 +363,8 @@ module.exports = (config) => {
363
363
  // If testing custom assets, we don't serve other unit or integration tests.
364
364
  // External asset tests are the basis for custom asset testing, so this file
365
365
  // is automatically included.
366
+ clientArgs.testFiles.push('demo/common/asset.js');
367
+ clientArgs.testFiles.push('demo/common/assets.js');
366
368
  clientArgs.testFiles.push('test/player_external.js');
367
369
  } else {
368
370
  // In a normal test run, we serve unit tests.
@@ -374,6 +376,8 @@ module.exports = (config) => {
374
376
  }
375
377
  if (settings.external) {
376
378
  // If --external is present, we serve external asset tests.
379
+ clientArgs.testFiles.push('demo/common/asset.js');
380
+ clientArgs.testFiles.push('demo/common/assets.js');
377
381
  clientArgs.testFiles.push('test/**/*_external.js');
378
382
  }
379
383
  }
@@ -219,8 +219,9 @@ shaka.abr.SimpleAbrManager = class {
219
219
  * @export
220
220
  */
221
221
  getBandwidthEstimate() {
222
+ const defaultBandwidthEstimate = this.getDefaultBandwidth_();
222
223
  return this.bandwidthEstimator_.getBandwidthEstimate(
223
- this.config_.defaultBandwidthEstimate);
224
+ defaultBandwidthEstimate);
224
225
  }
225
226
 
226
227
 
@@ -43,8 +43,11 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
43
43
  * @param {string} receiverAppId The ID of the cast receiver application.
44
44
  * If blank, casting will not be available, but the proxy will still
45
45
  * function otherwise.
46
+ * @param {boolean} androidReceiverCompatible Indicates if the app is
47
+ * compatible with an Android Receiver.
46
48
  */
47
- constructor(video, player, receiverAppId) {
49
+ constructor(video, player, receiverAppId,
50
+ androidReceiverCompatible = false) {
48
51
  super();
49
52
 
50
53
  /** @private {HTMLMediaElement} */
@@ -71,6 +74,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
71
74
  /** @private {string} */
72
75
  this.receiverAppId_ = receiverAppId;
73
76
 
77
+ /** @private {boolean} */
78
+ this.androidReceiverCompatible_ = androidReceiverCompatible;
79
+
74
80
  /** @private {!Map} */
75
81
  this.compiledToExternNames_ = new Map();
76
82
 
@@ -81,7 +87,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
81
87
  () => this.onFirstCastStateUpdate_(),
82
88
  (targetName, event) => this.onRemoteEvent_(targetName, event),
83
89
  () => this.onResumeLocal_(),
84
- () => this.getInitState_());
90
+ () => this.getInitState_(),
91
+ androidReceiverCompatible);
85
92
 
86
93
 
87
94
  this.init_();
@@ -225,15 +232,18 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
225
232
 
226
233
  /**
227
234
  * @param {string} newAppId
235
+ * @param {boolean=} newCastAndroidReceiver
228
236
  * @export
229
237
  */
230
- async changeReceiverId(newAppId) {
231
- if (newAppId == this.receiverAppId_) {
238
+ async changeReceiverId(newAppId, newCastAndroidReceiver = false) {
239
+ if (newAppId == this.receiverAppId_ &&
240
+ newCastAndroidReceiver == this.androidReceiverCompatible_) {
232
241
  // Nothing to change
233
242
  return;
234
243
  }
235
244
 
236
245
  this.receiverAppId_ = newAppId;
246
+ this.androidReceiverCompatible_ = newCastAndroidReceiver;
237
247
 
238
248
  // Destroy the old sender
239
249
  this.sender_.forceDisconnect();
@@ -248,7 +258,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
248
258
  () => this.onFirstCastStateUpdate_(),
249
259
  (targetName, event) => this.onRemoteEvent_(targetName, event),
250
260
  () => this.onResumeLocal_(),
251
- () => this.getInitState_());
261
+ () => this.getInitState_(),
262
+ newCastAndroidReceiver);
252
263
 
253
264
  this.sender_.init();
254
265
  }
@@ -267,8 +278,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
267
278
  (event) => this.videoProxyLocalEvent_(event));
268
279
  }
269
280
 
270
- for (const key in shaka.Player.EventName) {
271
- const name = shaka.Player.EventName[key];
281
+ for (const key in shaka.util.FakeEvent.EventName) {
282
+ const name = shaka.util.FakeEvent.EventName[key];
272
283
  this.eventManager_.listen(this.localPlayer_, name,
273
284
  (event) => this.playerProxyLocalEvent_(event));
274
285
  }
@@ -526,7 +537,7 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
526
537
  // Pass any errors through to the app.
527
538
  goog.asserts.assert(error instanceof shaka.util.Error,
528
539
  'Wrong error type!');
529
- const eventType = shaka.Player.EventName.Error;
540
+ const eventType = shaka.util.FakeEvent.EventName.Error;
530
541
  const data = (new Map()).set('detail', error);
531
542
  const event = new shaka.util.FakeEvent(eventType, data);
532
543
  this.localPlayer_.dispatchEvent(event);
@@ -281,8 +281,8 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
281
281
  this.video_, name, (event) => this.proxyEvent_('video', event));
282
282
  }
283
283
 
284
- for (const key in shaka.Player.EventName) {
285
- const name = shaka.Player.EventName[key];
284
+ for (const key in shaka.util.FakeEvent.EventName) {
285
+ const name = shaka.util.FakeEvent.EventName[key];
286
286
  this.eventManager_.listen(
287
287
  this.player_, name, (event) => this.proxyEvent_('player', event));
288
288
  }
@@ -409,7 +409,7 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
409
409
  // Pass any errors through to the app.
410
410
  goog.asserts.assert(error instanceof shaka.util.Error,
411
411
  'Wrong error type!');
412
- const eventType = shaka.Player.EventName.Error;
412
+ const eventType = shaka.util.FakeEvent.EventName.Error;
413
413
  const data = (new Map()).set('detail', error);
414
414
  const event = new shaka.util.FakeEvent(eventType, data);
415
415
  // Only dispatch the event if the player still exists.
@@ -32,12 +32,18 @@ shaka.cast.CastSender = class {
32
32
  * should resume playback. Called before the cached remote state is wiped.
33
33
  * @param {function()} onInitStateRequired A callback to get local player's.
34
34
  * state. Invoked when casting is initiated from Chrome's cast button.
35
+ * @param {boolean} androidReceiverCompatible Indicates if the app is
36
+ * compatible with an Android Receiver.
35
37
  */
36
38
  constructor(receiverAppId, onStatusChanged, onFirstCastStateUpdate,
37
- onRemoteEvent, onResumeLocal, onInitStateRequired) {
39
+ onRemoteEvent, onResumeLocal, onInitStateRequired,
40
+ androidReceiverCompatible) {
38
41
  /** @private {string} */
39
42
  this.receiverAppId_ = receiverAppId;
40
43
 
44
+ /** @private {boolean} */
45
+ this.androidReceiverCompatible_ = androidReceiverCompatible;
46
+
41
47
  /** @private {shaka.util.Timer} */
42
48
  this.statusChangeTimer_ = new shaka.util.Timer(onStatusChanged);
43
49
 
@@ -211,7 +217,11 @@ shaka.cast.CastSender = class {
211
217
  // Use static versions of the API callbacks, since the ChromeCast API is
212
218
  // static. If we used local versions, we might end up retaining references
213
219
  // to destroyed players here.
214
- const sessionRequest = new chrome.cast.SessionRequest(this.receiverAppId_);
220
+ const sessionRequest = new chrome.cast.SessionRequest(this.receiverAppId_,
221
+ /* capabilities= */ [],
222
+ /* timeout= */ null,
223
+ this.androidReceiverCompatible_,
224
+ /* credentialsData= */null);
215
225
  const apiConfig = new chrome.cast.ApiConfig(sessionRequest,
216
226
  (session) => CastSender.onExistingSessionJoined_(session),
217
227
  (availability) => CastSender.onReceiverStatusChanged_(availability),
@@ -765,8 +765,8 @@ shaka.dash.DashParser = class {
765
765
  // currently support that. Just choose one.
766
766
  // TODO: https://github.com/shaka-project/shaka-player/issues/1528
767
767
  stream.trickModeVideo = trickModeSet.streams.find((trickStream) =>
768
- shaka.util.MimeUtils.getCodecBase(stream.codecs) ==
769
- shaka.util.MimeUtils.getCodecBase(trickStream.codecs));
768
+ shaka.util.MimeUtils.getNormalizedCodec(stream.codecs) ==
769
+ shaka.util.MimeUtils.getNormalizedCodec(trickStream.codecs));
770
770
  }
771
771
  }
772
772
  }
@@ -460,6 +460,7 @@ shaka.hls.HlsParser = class {
460
460
  variants.push({
461
461
  id: 0,
462
462
  language: 'und',
463
+ disabledUntilTime: 0,
463
464
  primary: true,
464
465
  audio: type == 'audio' ? streamInfo.stream : null,
465
466
  video: type == 'video' ? streamInfo.stream : null,
@@ -1825,7 +1826,7 @@ shaka.hls.HlsParser = class {
1825
1826
  * @param {!Map.<string, string>} variables
1826
1827
  * @param {string} absoluteMediaPlaylistUri
1827
1828
  * @param {string} type
1828
- * @return {!shaka.media.SegmentReference}
1829
+ * @return {shaka.media.SegmentReference}
1829
1830
  * @private
1830
1831
  */
1831
1832
  createSegmentReference_(
@@ -1870,17 +1871,42 @@ shaka.hls.HlsParser = class {
1870
1871
  status = shaka.media.SegmentReference.Status.MISSING;
1871
1872
  }
1872
1873
 
1874
+ if (!extinfTag) {
1875
+ if (hlsSegment.partialSegments.length == 0) {
1876
+ // EXTINF tag must be available if the segment has no partial segments.
1877
+ throw new shaka.util.Error(
1878
+ shaka.util.Error.Severity.CRITICAL,
1879
+ shaka.util.Error.Category.MANIFEST,
1880
+ shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
1881
+ } else if (!this.lowLatencyMode_) {
1882
+ // Without EXTINF and without low-latency mode, partial segments get
1883
+ // ignored.
1884
+ return null;
1885
+ }
1886
+ }
1887
+
1873
1888
  // Create SegmentReferences for the partial segments.
1874
1889
  const partialSegmentRefs = [];
1875
- if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
1890
+ if (this.lowLatencyMode_) {
1876
1891
  for (let i = 0; i < hlsSegment.partialSegments.length; i++) {
1877
1892
  const item = hlsSegment.partialSegments[i];
1878
1893
  const pPreviousReference = i == 0 ?
1879
1894
  previousReference : partialSegmentRefs[partialSegmentRefs.length - 1];
1880
1895
  const pStartTime = (i == 0) ? startTime : pPreviousReference.endTime;
1881
- const pDuration = Number(item.getAttributeValue('DURATION'));
1882
- // A preload hinted partial segment doesn't have duration information,
1883
- // so its startTime and endTime are the same.
1896
+
1897
+ // If DURATION is missing from this partial segment, use the target
1898
+ // partial duration from the top of the playlist, which is a required
1899
+ // attribute for content with partial segments.
1900
+ const pDuration = Number(item.getAttributeValue('DURATION')) ||
1901
+ this.partialTargetDuration_;
1902
+
1903
+ // If for some reason we have neither an explicit duration, nor a target
1904
+ // partial duration, we should SKIP this partial segment to avoid
1905
+ // duplicating content in the presentation timeline.
1906
+ if (!pDuration) {
1907
+ continue;
1908
+ }
1909
+
1884
1910
  const pEndTime = pStartTime + pDuration;
1885
1911
 
1886
1912
  let pStartByte = 0;
@@ -1901,6 +1927,11 @@ shaka.hls.HlsParser = class {
1901
1927
  const pAbsoluteUri = shaka.hls.Utils.constructAbsoluteUri(
1902
1928
  absoluteMediaPlaylistUri, pUri);
1903
1929
 
1930
+ let partialStatus = shaka.media.SegmentReference.Status.AVAILABLE;
1931
+ if (item.getAttributeValue('GAP') == 'YES') {
1932
+ partialStatus = shaka.media.SegmentReference.Status.MISSING;
1933
+ }
1934
+
1904
1935
  const partial = new shaka.media.SegmentReference(
1905
1936
  pStartTime,
1906
1937
  pEndTime,
@@ -1910,17 +1941,14 @@ shaka.hls.HlsParser = class {
1910
1941
  initSegmentReference,
1911
1942
  /* timestampOffset= */ 0, // This value is ignored in sequence mode.
1912
1943
  /* appendWindowStart= */ 0,
1913
- /* appendWindowEnd= */ Infinity);
1944
+ /* appendWindowEnd= */ Infinity,
1945
+ /* partialReferences= */ [],
1946
+ /* tilesLayout= */ '',
1947
+ /* tileDuration= */ null,
1948
+ /* syncTime= */ null,
1949
+ partialStatus);
1914
1950
  partialSegmentRefs.push(partial);
1915
1951
  } // for-loop of hlsSegment.partialSegments
1916
- } else {
1917
- // EXTINF tag must be available if the segment has no partial segments.
1918
- if (!extinfTag) {
1919
- throw new shaka.util.Error(
1920
- shaka.util.Error.Severity.CRITICAL,
1921
- shaka.util.Error.Category.MANIFEST,
1922
- shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
1923
- }
1924
1952
  }
1925
1953
 
1926
1954
  // If the segment has EXTINF tag, set the segment's end time, start byte
@@ -1939,6 +1967,7 @@ shaka.hls.HlsParser = class {
1939
1967
  } else {
1940
1968
  endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
1941
1969
  }
1970
+
1942
1971
  // If the segment has EXT-X-BYTERANGE tag, set the start byte and end byte
1943
1972
  // base on the byterange information. If segment has no EXT-X-BYTERANGE tag
1944
1973
  // and has partial segments, set the start byte and end byte base on the
@@ -2107,7 +2136,9 @@ shaka.hls.HlsParser = class {
2107
2136
  playlist.absoluteUri,
2108
2137
  type);
2109
2138
 
2110
- references.push(reference);
2139
+ if (reference) {
2140
+ references.push(reference);
2141
+ }
2111
2142
  }
2112
2143
 
2113
2144
  // If some segments have sync times, but not all, extrapolate the sync
@@ -355,7 +355,8 @@ shaka.media.DrmEngine = class {
355
355
  shaka.media.DrmEngine.fillInDrmInfoDefaults_(
356
356
  info,
357
357
  shaka.util.MapUtils.asMap(this.config_.servers),
358
- shaka.util.MapUtils.asMap(this.config_.advanced || {}));
358
+ shaka.util.MapUtils.asMap(this.config_.advanced || {}),
359
+ this.config_.keySystemsMapping);
359
360
  }
360
361
  }
361
362
 
@@ -1253,6 +1254,22 @@ shaka.media.DrmEngine = class {
1253
1254
  };
1254
1255
  this.activeSessions_.set(session, metadata);
1255
1256
 
1257
+ try {
1258
+ initData = this.config_.initDataTransform(
1259
+ initData, initDataType, this.currentDrmInfo_);
1260
+ } catch (error) {
1261
+ let shakaError = error;
1262
+ if (!(error instanceof shaka.util.Error)) {
1263
+ shakaError = new shaka.util.Error(
1264
+ shaka.util.Error.Severity.CRITICAL,
1265
+ shaka.util.Error.Category.DRM,
1266
+ shaka.util.Error.Code.INIT_DATA_TRANSFORM_ERROR,
1267
+ error);
1268
+ }
1269
+ this.onError_(shakaError);
1270
+ return;
1271
+ }
1272
+
1256
1273
  if (this.config_.logLicenseExchange) {
1257
1274
  const str = shaka.util.Uint8ArrayUtils.toBase64(initData);
1258
1275
  shaka.log.info('EME init data: type=', initDataType, 'data=', str);
@@ -1408,6 +1425,9 @@ shaka.media.DrmEngine = class {
1408
1425
  }
1409
1426
  return;
1410
1427
  }
1428
+ if (this.destroyer_.destroyed()) {
1429
+ return;
1430
+ }
1411
1431
 
1412
1432
  const updateEvent = new shaka.util.FakeEvent('drmsessionupdate');
1413
1433
  this.playerInterface_.onEvent(updateEvent);
@@ -2205,16 +2225,20 @@ shaka.media.DrmEngine = class {
2205
2225
  * that the parser left blank. Before working with any drmInfo, it should be
2206
2226
  * passed through here as it is uncommon for drmInfo to be complete when
2207
2227
  * fetched from a manifest because most manifest formats do not have the
2208
- * required information.
2228
+ * required information. Also applies the key systems mapping.
2209
2229
  *
2210
2230
  * @param {shaka.extern.DrmInfo} drmInfo
2211
2231
  * @param {!Map.<string, string>} servers
2212
2232
  * @param {!Map.<string, shaka.extern.AdvancedDrmConfiguration>}
2213
2233
  * advancedConfigs
2234
+ * @param {!Object.<string, string>} keySystemsMapping
2214
2235
  * @private
2215
2236
  */
2216
- static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs) {
2217
- if (!drmInfo.keySystem) {
2237
+ static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs,
2238
+ keySystemsMapping) {
2239
+ const originalKeySystem = drmInfo.keySystem;
2240
+
2241
+ if (!originalKeySystem) {
2218
2242
  // This is a placeholder from the manifest parser for an unrecognized key
2219
2243
  // system. Skip this entry, to avoid logging nonsensical errors.
2220
2244
  return;
@@ -2235,14 +2259,14 @@ shaka.media.DrmEngine = class {
2235
2259
  // The only way to get license servers from the manifest is not to specify
2236
2260
  // any in your player config.
2237
2261
 
2238
- if (drmInfo.keySystem == 'org.w3.clearkey' && drmInfo.licenseServerUri) {
2262
+ if (originalKeySystem == 'org.w3.clearkey' && drmInfo.licenseServerUri) {
2239
2263
  // Preference 1: Clear Key with pre-configured keys will have a data URI
2240
2264
  // assigned as its license server. Don't change anything.
2241
2265
  return;
2242
2266
  } else if (servers.size) {
2243
2267
  // Preference 2: If anything is configured at the application level,
2244
2268
  // override whatever was in the manifest.
2245
- const server = servers.get(drmInfo.keySystem) || '';
2269
+ const server = servers.get(originalKeySystem) || '';
2246
2270
 
2247
2271
  drmInfo.licenseServerUri = server;
2248
2272
  } else {
@@ -2254,7 +2278,7 @@ shaka.media.DrmEngine = class {
2254
2278
  drmInfo.keyIds = new Set();
2255
2279
  }
2256
2280
 
2257
- const advancedConfig = advancedConfigs.get(drmInfo.keySystem);
2281
+ const advancedConfig = advancedConfigs.get(originalKeySystem);
2258
2282
 
2259
2283
  if (advancedConfig) {
2260
2284
  if (!drmInfo.distinctiveIdentifierRequired) {
@@ -2288,6 +2312,10 @@ shaka.media.DrmEngine = class {
2288
2312
  }
2289
2313
  }
2290
2314
 
2315
+ if (keySystemsMapping[originalKeySystem]) {
2316
+ drmInfo.keySystem = keySystemsMapping[originalKeySystem];
2317
+ }
2318
+
2291
2319
  // Chromecast has a variant of PlayReady that uses a different key
2292
2320
  // system ID. Since manifest parsers convert the standard PlayReady
2293
2321
  // UUID to the standard PlayReady key system ID, here we will switch
@@ -2296,7 +2324,7 @@ shaka.media.DrmEngine = class {
2296
2324
  // player config uses the standard PlayReady ID for license server
2297
2325
  // configuration.
2298
2326
  if (window.cast && window.cast.__platform__) {
2299
- if (drmInfo.keySystem == 'com.microsoft.playready') {
2327
+ if (originalKeySystem == 'com.microsoft.playready') {
2300
2328
  drmInfo.keySystem = 'com.chromecast.playready';
2301
2329
  }
2302
2330
  }