shaka-player 4.2.12 → 4.2.14

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 (77) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +11 -0
  2. package/.github/workflows/build-and-test.yaml +22 -14
  3. package/.github/workflows/compute-incremental-coverage.py +3 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +48 -12
  5. package/.github/workflows/update-issues.yaml +1 -0
  6. package/CHANGELOG.md +28 -0
  7. package/demo/common/assets.js +12 -12
  8. package/demo/main.js +5 -0
  9. package/dist/demo.compiled.debug.js +13 -13
  10. package/dist/demo.compiled.debug.map +2 -2
  11. package/dist/demo.compiled.js +13 -13
  12. package/dist/demo.compiled.map +2 -2
  13. package/dist/deps.js +1 -1
  14. package/dist/locales.js +31 -31
  15. package/dist/receiver.compiled.debug.js +8 -8
  16. package/dist/receiver.compiled.debug.map +1 -1
  17. package/dist/receiver.compiled.js +8 -8
  18. package/dist/receiver.compiled.map +1 -1
  19. package/dist/shaka-player.compiled.d.ts +2 -2
  20. package/dist/shaka-player.compiled.debug.d.ts +2 -2
  21. package/dist/shaka-player.compiled.debug.js +51 -49
  22. package/dist/shaka-player.compiled.debug.map +3 -3
  23. package/dist/shaka-player.compiled.js +403 -404
  24. package/dist/shaka-player.compiled.map +3 -3
  25. package/dist/shaka-player.ui.d.ts +2 -2
  26. package/dist/shaka-player.ui.debug.d.ts +2 -2
  27. package/dist/shaka-player.ui.debug.js +60 -59
  28. package/dist/shaka-player.ui.debug.map +3 -3
  29. package/dist/shaka-player.ui.js +411 -411
  30. package/dist/shaka-player.ui.map +2 -2
  31. package/docs/tutorials/selenium-grid-config.md +60 -8
  32. package/externs/shaka/manifest.js +5 -1
  33. package/externs/shaka/offline.js +5 -1
  34. package/karma.conf.js +52 -7
  35. package/lib/abr/simple_abr_manager.js +5 -5
  36. package/lib/ads/client_side_ad_manager.js +14 -2
  37. package/lib/dash/dash_parser.js +1 -0
  38. package/lib/hls/hls_parser.js +1 -0
  39. package/lib/media/media_source_engine.js +6 -5
  40. package/lib/media/streaming_engine.js +2 -2
  41. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  42. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  43. package/lib/offline/manifest_converter.js +1 -0
  44. package/lib/offline/storage.js +1 -0
  45. package/lib/player.js +4 -5
  46. package/lib/polyfill/orientation.js +34 -7
  47. package/lib/text/vtt_text_parser.js +1 -0
  48. package/lib/util/manifest_parser_utils.js +4 -2
  49. package/lib/util/periods.js +2 -0
  50. package/lib/util/platform.js +22 -0
  51. package/lib/util/stream_utils.js +69 -26
  52. package/package.json +3 -3
  53. package/test/cast/cast_utils_unit.js +4 -1
  54. package/test/hls/hls_parser_unit.js +72 -0
  55. package/test/media/adaptation_set_unit.js +1 -0
  56. package/test/media/drm_engine_integration.js +2 -2
  57. package/test/media/media_source_engine_unit.js +1 -1
  58. package/test/media/segment_index_unit.js +3 -3
  59. package/test/media/streaming_engine_integration.js +14 -11
  60. package/test/offline/manifest_convert_unit.js +5 -0
  61. package/test/offline/offline_integration.js +6 -3
  62. package/test/player_external.js +19 -16
  63. package/test/player_integration.js +39 -49
  64. package/test/player_src_equals_integration.js +9 -5
  65. package/test/test/util/fake_demo_main.js +7 -3
  66. package/test/test/util/layout_tests.js +2 -2
  67. package/test/test/util/manifest_generator.js +2 -0
  68. package/test/test/util/offline_utils.js +1 -0
  69. package/test/test/util/streaming_engine_util.js +3 -0
  70. package/test/test/util/ui_utils.js +14 -3
  71. package/test/test/util/util.js +6 -27
  72. package/test/test/util/waiter.js +73 -3
  73. package/test/ui/ad_ui_unit.js +9 -9
  74. package/test/util/stream_utils_unit.js +46 -0
  75. package/ui/externs/ui.js +2 -2
  76. package/ui/range_element.js +5 -0
  77. package/ui/seek_bar.js +7 -1
@@ -47,17 +47,20 @@ vars:
47
47
  FirefoxMac:
48
48
  browser: firefox
49
49
  os: Mac
50
- extra_config: *firefox_config
50
+ extra_configs:
51
+ - *firefox_config
51
52
 
52
53
  FirefoxWindows:
53
54
  browser: firefox
54
55
  os: Windows
55
- extra_config: *firefox_config
56
+ extra_configs:
57
+ - *firefox_config
56
58
 
57
59
  FirefoxLinux:
58
60
  browser: firefox
59
61
  os: Linux
60
- extra_config: *firefox_config
62
+ extra_configs:
63
+ - *firefox_config
61
64
  ```
62
65
 
63
66
  ### Browsers
@@ -75,8 +78,8 @@ browser made available to Karma. Within each of those keys are the following:
75
78
  and must match the string and case used in the Selenium node config.
76
79
  - `disabled` (optional): If true, this browser is disabled and will not be
77
80
  used unless explicitly requested.
78
- - `extra_config` (optional): A dictionary of extra configs which will be
79
- merged with the WebDriver launcher config in Karma.
81
+ - `extra_configs` (optional): An array of dictionaries of extra configs which
82
+ will be merged with the WebDriver launcher config in Karma.
80
83
 
81
84
  Examples of basic desktop browsers definitions:
82
85
 
@@ -96,9 +99,9 @@ Safari:
96
99
  SafariTP:
97
100
  browser: safari
98
101
  os: Mac
99
- extra_config:
100
- safari.options:
101
- technologyPreview: true
102
+ extra_configs:
103
+ - safari.options:
104
+ technologyPreview: true
102
105
 
103
106
  ChromeWindows:
104
107
  browser: chrome
@@ -120,3 +123,52 @@ FirefoxLinux:
120
123
  browser: firefox
121
124
  os: Linux
122
125
  ```
126
+
127
+ ### Composing configs
128
+
129
+ You can define variables for browser configs that can be composed together in
130
+ `extra_configs`. For example, below you will see some basic definitions for
131
+ Chrome arguments and parameters, and then an additional config with an argument
132
+ that is only needed for some platforms. `karma.conf.js` will merge those
133
+ argument lists intelligently when it loads the YAML config.
134
+
135
+ ```yaml
136
+ vars:
137
+ basic_chrome_config: &basic_chrome_config
138
+ goog:chromeOptions:
139
+ args:
140
+ # Normally, Chrome disallows autoplaying videos in many cases. Enable
141
+ # it for testing.
142
+ - "--autoplay-policy=no-user-gesture-required"
143
+
144
+ # Instruct chromedriver not to disable component updater.
145
+ excludeSwitches:
146
+ - "disable-component-update"
147
+
148
+ headless_chrome_config: &headless_chrome_config
149
+ goog:chromeOptions:
150
+ args:
151
+ # Run in headless mode.
152
+ - "--headless"
153
+
154
+ ChromeLinux:
155
+ browser: chrome
156
+ os: Mac
157
+ extra_configs:
158
+ - *basic_chrome_config
159
+
160
+ ChromeWindows:
161
+ browser: chrome
162
+ os: Mac
163
+ extra_configs:
164
+ - *basic_chrome_config
165
+
166
+ ChromeMac:
167
+ browser: chrome
168
+ os: Mac
169
+ # Take the parameters and arguments for basic_chrome_config, then append the
170
+ # arguments for headless_chrome_config.
171
+ extra_configs:
172
+ - *basic_chrome_config
173
+ - *headless_chrome_config
174
+ ```
@@ -321,7 +321,8 @@ shaka.extern.FetchCryptoKeysFunction;
321
321
  * tilesLayout: (string|undefined),
322
322
  * matchedStreams:
323
323
  * (!Array.<shaka.extern.Stream>|!Array.<shaka.extern.StreamDB>|
324
- * undefined)
324
+ * undefined),
325
+ * external: boolean
325
326
  * }}
326
327
  *
327
328
  * @description
@@ -434,6 +435,9 @@ shaka.extern.FetchCryptoKeysFunction;
434
435
  * @property {(!Array.<shaka.extern.Stream>|!Array.<shaka.extern.StreamDB>|
435
436
  * undefined)} matchedStreams
436
437
  * The streams in all periods which match the stream. Used for Dash.
438
+ * @property {boolean} external
439
+ * Indicate if the stream was added externally.
440
+ * Eg: external text tracks.
437
441
  *
438
442
  * @exportDoc
439
443
  */
@@ -132,7 +132,8 @@ shaka.extern.ManifestDB;
132
132
  * audioSamplingRate: ?number,
133
133
  * spatialAudio: boolean,
134
134
  * closedCaptions: Map.<string, string>,
135
- * tilesLayout: (string|undefined)
135
+ * tilesLayout: (string|undefined),
136
+ * external: boolean
136
137
  * }}
137
138
  *
138
139
  * @property {number} id
@@ -193,6 +194,9 @@ shaka.extern.ManifestDB;
193
194
  * The value is a grid-item-dimension consisting of two positive decimal
194
195
  * integers in the format: column-x-row ('4x3'). It describes the arrangement
195
196
  * of Images in a Grid. The minimum valid LAYOUT is '1x1'.
197
+ * @property {boolean} external
198
+ * Indicate if the stream was added externally.
199
+ * Eg: external text tracks.
196
200
  */
197
201
  shaka.extern.StreamDB;
198
202
 
package/karma.conf.js CHANGED
@@ -7,9 +7,11 @@
7
7
  // Karma configuration
8
8
  // Install required modules by running "npm install"
9
9
 
10
- const Jimp = require('jimp');
10
+ // lodash is an indirect dependency, depended on by Karma
11
+ const _ = require('lodash');
11
12
  const fs = require('fs');
12
13
  const glob = require('glob');
14
+ const Jimp = require('jimp');
13
15
  const path = require('path');
14
16
  const rimraf = require('rimraf');
15
17
  const {ssim} = require('ssim.js');
@@ -17,6 +19,32 @@ const util = require('karma/common/util');
17
19
  const which = require('which');
18
20
  const yaml = require('js-yaml');
19
21
 
22
+ /**
23
+ * Like Object.assign, but recursive and doesn't clobber objects and arrays.
24
+ * If two arrays are merged, they are concatenated.
25
+ * Ex:
26
+ * mergeConfigs({ foo: 'bar', args: [1, 2, 3] },
27
+ * { baz: 'blah', args: [4, 5, 6] })
28
+ * => { foo: 'bar', baz: 'blah', args: [1, 2, 3, 4, 5, 6] }
29
+ *
30
+ * @param {Object} first
31
+ * @param {Object} second
32
+ * @return {Object}
33
+ */
34
+ function mergeConfigs(first, second) {
35
+ return _.mergeWith(
36
+ first,
37
+ second,
38
+ (firstValue, secondValue) => {
39
+ // Merge arrays by concatenation.
40
+ if (Array.isArray(firstValue)) {
41
+ return firstValue.concat(secondValue);
42
+ }
43
+ // Use lodash's default merge behavior for everything else.
44
+ return undefined;
45
+ });
46
+ }
47
+
20
48
  /**
21
49
  * @param {Object} config
22
50
  */
@@ -78,7 +106,7 @@ module.exports = (config) => {
78
106
  }
79
107
 
80
108
  // Add standard WebDriver configs.
81
- Object.assign(launcher, {
109
+ mergeConfigs(launcher, {
82
110
  base: 'WebDriver',
83
111
  config: {hostname: gridHostname, port: gridPort},
84
112
  pseudoActivityInterval: 20000,
@@ -87,8 +115,10 @@ module.exports = (config) => {
87
115
  version: metadata.version,
88
116
  });
89
117
 
90
- if (metadata.extra_config) {
91
- Object.assign(launcher, metadata.extra_config);
118
+ if (metadata.extra_configs) {
119
+ for (const config of metadata.extra_configs) {
120
+ mergeConfigs(launcher, config);
121
+ }
92
122
  }
93
123
  }
94
124
 
@@ -321,6 +351,8 @@ module.exports = (config) => {
321
351
  },
322
352
 
323
353
  babelPreprocessor: {
354
+ // Cache results in .babel-cache
355
+ cachePath: '.babel-cache',
324
356
  options: {
325
357
  presets: ['@babel/preset-env'],
326
358
  // Add source maps so that backtraces refer to the original code.
@@ -603,15 +635,27 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
603
635
  }
604
636
 
605
637
  /**
638
+ * @param {karma.Launcher.Browser.spec} spec
606
639
  * @param {wd.remote} webDriverClient A WebDriver client, an object from the
607
640
  * "wd" package, created by "wd.remote()".
608
641
  * @return {!Promise.<!Buffer>} A Buffer containing a PNG screenshot
609
642
  */
610
- function getScreenshot(webDriverClient) {
643
+ function getScreenshot(spec, webDriverClient) {
611
644
  return new Promise((resolve, reject) => {
612
645
  webDriverClient.takeScreenshot((error, pngBase64) => {
613
646
  if (error) {
614
647
  reject(error);
648
+ } else if (pngBase64.error) {
649
+ // In some failure cases, pngBase64 is an object with "error",
650
+ // "message", and "stacktrace" fields. This happens, for example,
651
+ // with a timeout from the screenshot command. This is not an
652
+ // expected situation, so log it. The extra newlines keep this from
653
+ // being overwritten on the terminal when running tests against many
654
+ // browsers at once.
655
+ console.log('\n\nUnexpected screenshot failure:\n' +
656
+ ` Error: ${JSON.stringify(pngBase64)}\n` +
657
+ ` WebDriver spec: ${JSON.stringify(spec)}\n\n\n`);
658
+ reject(pngBase64);
615
659
  } else {
616
660
  // Convert the screenshot to a binary buffer.
617
661
  resolve(Buffer.from(pngBase64, 'base64'));
@@ -635,7 +679,8 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
635
679
  }
636
680
 
637
681
  /** @type {!Buffer} */
638
- const fullPageScreenshotData = await getScreenshot(webDriverClient);
682
+ const fullPageScreenshotData =
683
+ await getScreenshot(browser.spec, webDriverClient);
639
684
 
640
685
  // Crop the screenshot to the dimensions specified in the test.
641
686
  // Jimp is picky about types, so convert these strings to numbers.
@@ -754,7 +799,7 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
754
799
  // The result is cached for the sake of performance.
755
800
  if (webDriverClient.canTakeScreenshot === undefined) {
756
801
  try {
757
- await getScreenshot(webDriverClient);
802
+ await getScreenshot(browser.spec, webDriverClient);
758
803
  webDriverClient.canTakeScreenshot = true;
759
804
  } catch (error) {
760
805
  webDriverClient.canTakeScreenshot = false;
@@ -48,6 +48,9 @@ shaka.abr.SimpleAbrManager = class {
48
48
  /** @private {shaka.abr.EwmaBandwidthEstimator} */
49
49
  this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
50
50
 
51
+ /** @private {?function():void} */
52
+ this.onNetworkInformationChange_ = null;
53
+
51
54
  // Some browsers implement the Network Information API, which allows
52
55
  // retrieving information about a user's network connection. We listen
53
56
  // to the change event to be able to make quick changes in case the type
@@ -98,9 +101,6 @@ shaka.abr.SimpleAbrManager = class {
98
101
  /** @private {ResizeObserver} */
99
102
  this.resizeObserver_ = null;
100
103
 
101
- /** @private {?function():void} */
102
- this.onNetworkInformationChange_ = null;
103
-
104
104
  /** @private {shaka.util.Timer} */
105
105
  this.resizeObserverTimer_ = new shaka.util.Timer(() => {
106
106
  if (this.config_.restrictToElementSize) {
@@ -175,8 +175,8 @@ shaka.abr.SimpleAbrManager = class {
175
175
  if (this.resizeObserver_ && this.config_.restrictToElementSize) {
176
176
  const devicePixelRatio =
177
177
  this.config_.ignoreDevicePixelRatio ? 1 : window.devicePixelRatio;
178
- maxHeight = this.mediaElement_.clientWidth * devicePixelRatio;
179
- maxWidth = this.mediaElement_.clientHeight * devicePixelRatio;
178
+ maxHeight = this.mediaElement_.clientHeight * devicePixelRatio;
179
+ maxWidth = this.mediaElement_.clientWidth * devicePixelRatio;
180
180
  }
181
181
 
182
182
  // Get sorted Variants.
@@ -33,6 +33,9 @@ shaka.ads.ClientSideAdManager = class {
33
33
  /** @private {HTMLMediaElement} */
34
34
  this.video_ = video;
35
35
 
36
+ /** @private {boolean} */
37
+ this.videoPlayed_ = false;
38
+
36
39
  /** @private {ResizeObserver} */
37
40
  this.resizeObserver_ = null;
38
41
 
@@ -82,6 +85,10 @@ shaka.ads.ClientSideAdManager = class {
82
85
  this.eventManager_.listen(this.video_, 'ended', () => {
83
86
  this.adsLoader_.contentComplete();
84
87
  });
88
+
89
+ this.eventManager_.listenOnce(this.video_, 'play', () => {
90
+ this.videoPlayed_ = true;
91
+ });
85
92
  }
86
93
 
87
94
  /**
@@ -219,9 +226,14 @@ shaka.ads.ClientSideAdManager = class {
219
226
  // seen the ads actually play until requestAds() is called.
220
227
  // Note: We listen for a play event to avoid autoplay issues that might
221
228
  // crash IMA.
222
- this.eventManager_.listenOnce(this.video_, 'play', () => {
229
+ if (this.videoPlayed_) {
223
230
  this.imaAdsManager_.start();
224
- });
231
+ } else {
232
+ this.eventManager_.listenOnce(this.video_, 'play', () => {
233
+ this.videoPlayed_ = true;
234
+ this.imaAdsManager_.start();
235
+ });
236
+ }
225
237
  } catch (adError) {
226
238
  // If there was a problem with the VAST response,
227
239
  // we we won't be getting an ad. Hide ad UI if we showed it already
@@ -1337,6 +1337,7 @@ shaka.dash.DashParser = class {
1337
1337
  hdr,
1338
1338
  tilesLayout,
1339
1339
  matchedStreams: [],
1340
+ external: false,
1340
1341
  };
1341
1342
  }
1342
1343
 
@@ -1757,6 +1757,7 @@ shaka.hls.HlsParser = class {
1757
1757
  closedCaptions,
1758
1758
  hdr: undefined,
1759
1759
  tilesLayout: undefined,
1760
+ external: false,
1760
1761
  };
1761
1762
 
1762
1763
  return {
@@ -354,7 +354,7 @@ shaka.media.MediaSourceEngine = class {
354
354
  let mimeType = shaka.util.MimeUtils.getFullType(
355
355
  stream.mimeType, stream.codecs);
356
356
  if (contentType == ContentType.TEXT) {
357
- this.reinitText(mimeType, sequenceMode);
357
+ this.reinitText(mimeType, sequenceMode, stream.external);
358
358
  } else {
359
359
  if ((forceTransmuxTS || !MediaSource.isTypeSupported(mimeType)) &&
360
360
  shaka.media.Transmuxer.isSupported(mimeType, contentType)) {
@@ -383,13 +383,14 @@ shaka.media.MediaSourceEngine = class {
383
383
  * Reinitialize the TextEngine for a new text type.
384
384
  * @param {string} mimeType
385
385
  * @param {boolean} sequenceMode
386
+ * @param {boolean} external
386
387
  */
387
- reinitText(mimeType, sequenceMode) {
388
+ reinitText(mimeType, sequenceMode, external) {
388
389
  if (!this.textEngine_) {
389
390
  this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
390
391
  }
391
392
  this.textEngine_.initParser(mimeType, sequenceMode,
392
- this.segmentRelativeVttTiming_);
393
+ external || this.segmentRelativeVttTiming_);
393
394
  }
394
395
 
395
396
  /**
@@ -564,7 +565,7 @@ shaka.media.MediaSourceEngine = class {
564
565
  // the video stream, so textEngine may not have been initialized.
565
566
  if (!this.textEngine_) {
566
567
  this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
567
- this.sequenceMode_);
568
+ this.sequenceMode_, /* external= */ false);
568
569
  }
569
570
 
570
571
  if (transmuxedData.metadata) {
@@ -593,7 +594,7 @@ shaka.media.MediaSourceEngine = class {
593
594
  } else if (hasClosedCaptions) {
594
595
  if (!this.textEngine_) {
595
596
  this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
596
- this.sequenceMode_);
597
+ this.sequenceMode_, /* external= */ false);
597
598
  }
598
599
  // If it is the init segment for closed captions, initialize the closed
599
600
  // caption parser.
@@ -240,7 +240,7 @@ shaka.media.StreamingEngine = class {
240
240
  const mimeType = shaka.util.MimeUtils.getFullType(
241
241
  stream.mimeType, stream.codecs);
242
242
  this.playerInterface_.mediaSourceEngine.reinitText(
243
- mimeType, this.manifest_.sequenceMode);
243
+ mimeType, this.manifest_.sequenceMode, stream.external);
244
244
 
245
245
  const textDisplayer =
246
246
  this.playerInterface_.mediaSourceEngine.getTextDisplayer();
@@ -447,7 +447,7 @@ shaka.media.StreamingEngine = class {
447
447
  const fullMimeType = shaka.util.MimeUtils.getFullType(
448
448
  stream.mimeType, stream.codecs);
449
449
  this.playerInterface_.mediaSourceEngine.reinitText(
450
- fullMimeType, this.manifest_.sequenceMode);
450
+ fullMimeType, this.manifest_.sequenceMode, stream.external);
451
451
  }
452
452
 
453
453
  // Releases the segmentIndex of the old stream.
@@ -176,6 +176,7 @@ shaka.offline.indexeddb.V1StorageCell = class
176
176
  spatialAudio: false,
177
177
  closedCaptions: null,
178
178
  tilesLayout: undefined,
179
+ external: false,
179
180
  };
180
181
  }
181
182
 
@@ -125,6 +125,7 @@ shaka.offline.indexeddb.V2StorageCell = class
125
125
  spatialAudio: false,
126
126
  closedCaptions: null,
127
127
  tilesLayout: undefined,
128
+ external: false,
128
129
  };
129
130
  }
130
131
 
@@ -202,6 +202,7 @@ shaka.offline.ManifestConverter = class {
202
202
  spatialAudio: streamDB.spatialAudio,
203
203
  closedCaptions: streamDB.closedCaptions,
204
204
  tilesLayout: streamDB.tilesLayout,
205
+ external: streamDB.external,
205
206
  };
206
207
 
207
208
  return stream;
@@ -1324,6 +1324,7 @@ shaka.offline.Storage = class {
1324
1324
  spatialAudio: stream.spatialAudio,
1325
1325
  closedCaptions: stream.closedCaptions,
1326
1326
  tilesLayout: stream.tilesLayout,
1327
+ external: stream.external,
1327
1328
  };
1328
1329
 
1329
1330
  const startTime =
package/lib/player.js CHANGED
@@ -2069,10 +2069,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2069
2069
 
2070
2070
  this.playhead_.setStartTime(adjustedTime);
2071
2071
  }
2072
-
2073
- // Since the first streams just became active, send an adaptation event.
2074
- this.onAdaptation_(null,
2075
- shaka.util.StreamUtils.variantToTrack(initialVariant));
2076
2072
  }
2077
2073
 
2078
2074
  this.playhead_.ready();
@@ -2237,6 +2233,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2237
2233
  audioSamplingRate: null,
2238
2234
  spatialAudio: false,
2239
2235
  closedCaptions: null,
2236
+ external: false,
2240
2237
  },
2241
2238
  bandwidth: 100,
2242
2239
  allowedByApplication: true,
@@ -4631,6 +4628,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4631
4628
  audioSamplingRate: null,
4632
4629
  spatialAudio: false,
4633
4630
  closedCaptions: null,
4631
+ external: true,
4634
4632
  };
4635
4633
 
4636
4634
  const fullMimeType = shaka.util.MimeUtils.getFullType(
@@ -5061,6 +5059,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5061
5059
  audioSamplingRate: null,
5062
5060
  spatialAudio: false,
5063
5061
  closedCaptions: null,
5062
+ external: false,
5064
5063
  };
5065
5064
  manifest.textStreams.push(textStream);
5066
5065
  closedCaptionsSet.add(id);
@@ -6635,7 +6634,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6635
6634
  * @define {string} A version number taken from git at compile time.
6636
6635
  * @export
6637
6636
  */
6638
- shaka.Player.version = 'v4.2.12-uncompiled';
6637
+ shaka.Player.version = 'v4.2.14-uncompiled';
6639
6638
 
6640
6639
  // Initialize the deprecation system using the version string we just set
6641
6640
  // on the player.
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.polyfill.Orientation');
8
8
 
9
+ goog.require('shaka.log');
9
10
  goog.require('shaka.util.FakeEvent');
10
11
  goog.require('shaka.util.FakeEventTarget');
11
12
  goog.require('shaka.polyfill');
@@ -23,21 +24,47 @@ shaka.polyfill.Orientation = class {
23
24
  * @export
24
25
  */
25
26
  static install() {
26
- if (screen.orientation) {
27
+ if (screen.orientation && screen.orientation.unlock) {
27
28
  // Not needed.
29
+ shaka.log.info('Using native screen.orientation');
28
30
  return;
29
31
  }
30
32
 
31
- // There is no way to check to see if the 'orientationchange' event exists
32
- // on window, which could theoretically lead to this making a
33
- // screen.orientation object that doesn't actually work.
34
- // However, it looks like all platforms that support the deprecated
35
- // window.orientation feature also support that event.
36
- if (window.orientation != undefined) {
33
+ if (screen.orientation != undefined) {
34
+ // There are some platforms where screen.orientation is defined but
35
+ // incomplete (e.g. Safari).
36
+ // Install a very simple polyfill in that case.
37
+ shaka.polyfill.Orientation.installBasedOnScreenMethods_();
38
+ } else if (window.orientation != undefined) {
39
+ // There is no way to check to see if the 'orientationchange' event exists
40
+ // on window, which could theoretically lead to this making a
41
+ // screen.orientation object that doesn't actually work.
42
+ // However, it looks like all platforms that support the deprecated
43
+ // window.orientation feature also support that event.
37
44
  shaka.polyfill.Orientation.installBasedOnWindowMethods_();
38
45
  }
39
46
  }
40
47
 
48
+ /**
49
+ * Modifies screen.orientation to add no-ops for missing methods.
50
+ * Meant for cases where screen.orientation is defined, but has missing
51
+ * methods that cannot be properly polyfilled.
52
+ * @private
53
+ */
54
+ static installBasedOnScreenMethods_() {
55
+ if (screen.orientation.lock === undefined) {
56
+ screen.orientation.lock = (orientation) => {
57
+ shaka.log.info('screen.orientation.lock is a no-op');
58
+ return Promise.resolve();
59
+ };
60
+ }
61
+ if (screen.orientation.unlock === undefined) {
62
+ screen.orientation.unlock = () => {
63
+ shaka.log.info('screen.orientation.unlock is a no-op');
64
+ };
65
+ }
66
+ }
67
+
41
68
  /**
42
69
  * Makes a polyfill for orientation, based on window methods.
43
70
  * Note that some of the features this is based on are deprecated, so this
@@ -674,6 +674,7 @@ shaka.text.VttTextParser = class {
674
674
  cue.fontWeight = refCue.fontWeight;
675
675
  cue.fontStyle = refCue.fontStyle;
676
676
  cue.opacity = refCue.opacity;
677
+ cue.textShadow = refCue.textShadow;
677
678
  cue.wrapLine = refCue.wrapLine;
678
679
  }
679
680
 
@@ -180,8 +180,10 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
180
180
  */
181
181
  shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
182
182
  /^vorbis$/,
183
- /^opus$/,
184
- /^flac$/,
183
+ /^Opus$/, // correct codec string according to RFC 6381 section 3.3
184
+ /^opus$/, // some manifests wrongfully use this
185
+ /^fLaC$/, // correct codec string according to RFC 6381 section 3.3
186
+ /^flac$/, // some manifests wrongfully use this
185
187
  /^mp4a/,
186
188
  /^[ae]c-3$/,
187
189
  /^ac-4$/,
@@ -1491,6 +1491,7 @@ shaka.util.PeriodCombiner = class {
1491
1491
  audioSamplingRate: null,
1492
1492
  spatialAudio: false,
1493
1493
  closedCaptions: null,
1494
+ external: false,
1494
1495
  };
1495
1496
  }
1496
1497
 
@@ -1527,6 +1528,7 @@ shaka.util.PeriodCombiner = class {
1527
1528
  audioSamplingRate: null,
1528
1529
  spatialAudio: false,
1529
1530
  closedCaptions: null,
1531
+ external: false,
1530
1532
  };
1531
1533
  }
1532
1534
 
@@ -214,6 +214,7 @@ shaka.util.Platform = class {
214
214
  !shaka.util.Platform.isTizen() &&
215
215
  !shaka.util.Platform.isEOS() &&
216
216
  !shaka.util.Platform.isVirginMedia() &&
217
+ !shaka.util.Platform.isOrange() &&
217
218
  !shaka.util.Platform.isPS4() &&
218
219
  !shaka.util.Platform.isAmazonFireTV();
219
220
  }
@@ -253,6 +254,13 @@ shaka.util.Platform = class {
253
254
  return shaka.util.Platform.userAgentContains_('VirginMedia');
254
255
  }
255
256
 
257
+ /**
258
+ * Check if the current platform is Orange.
259
+ */
260
+ static isOrange() {
261
+ return shaka.util.Platform.userAgentContains_('SOPOpenBrowser');
262
+ }
263
+
256
264
  /**
257
265
  * Check if the current platform is Amazon Fire TV.
258
266
  * https://developer.amazon.com/docs/fire-tv/identify-amazon-fire-tv-devices.html
@@ -346,6 +354,20 @@ shaka.util.Platform = class {
346
354
  return shaka.util.Platform.isApple() && navigator.maxTouchPoints > 1;
347
355
  }
348
356
 
357
+ /**
358
+ * Return true if the platform is a Mac, regardless of the browser.
359
+ *
360
+ * @return {boolean}
361
+ */
362
+ static isMac() {
363
+ // Try the newer standard first.
364
+ if (navigator.userAgentData && navigator.userAgentData.platform) {
365
+ return navigator.userAgentData.platform.toLowerCase() == 'macos';
366
+ }
367
+ // Fall back to the old API, with less strict matching.
368
+ return navigator.platform.toLowerCase().includes('mac');
369
+ }
370
+
349
371
  /**
350
372
  * Check if the user agent contains a key. This is the best way we know of
351
373
  * right now to detect platforms. If there is a better way, please send a