shaka-player 4.3.8 → 4.3.10

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 +15 -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 +29 -0
  7. package/demo/common/assets.js +59 -25
  8. package/demo/main.js +5 -0
  9. package/dist/demo.compiled.debug.js +15 -13
  10. package/dist/demo.compiled.debug.map +3 -3
  11. package/dist/demo.compiled.js +15 -13
  12. package/dist/demo.compiled.map +3 -3
  13. package/dist/deps.js +1 -1
  14. package/dist/locales.js +33 -33
  15. package/dist/receiver.compiled.debug.js +13 -11
  16. package/dist/receiver.compiled.debug.map +3 -3
  17. package/dist/receiver.compiled.js +13 -11
  18. package/dist/receiver.compiled.map +3 -3
  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 +56 -53
  22. package/dist/shaka-player.compiled.debug.map +3 -3
  23. package/dist/shaka-player.compiled.js +418 -418
  24. package/dist/shaka-player.compiled.map +2 -2
  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 +66 -62
  28. package/dist/shaka-player.ui.debug.map +3 -3
  29. package/dist/shaka-player.ui.js +442 -441
  30. package/dist/shaka-player.ui.map +3 -3
  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 +9 -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) {
@@ -182,8 +182,8 @@ shaka.abr.SimpleAbrManager = class {
182
182
  if (this.resizeObserver_ && this.config_.restrictToElementSize) {
183
183
  const devicePixelRatio =
184
184
  this.config_.ignoreDevicePixelRatio ? 1 : window.devicePixelRatio;
185
- maxHeight = this.mediaElement_.clientWidth * devicePixelRatio;
186
- maxWidth = this.mediaElement_.clientHeight * devicePixelRatio;
185
+ maxHeight = this.mediaElement_.clientHeight * devicePixelRatio;
186
+ maxWidth = this.mediaElement_.clientWidth * devicePixelRatio;
187
187
  }
188
188
 
189
189
  // 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
 
@@ -2114,6 +2114,7 @@ shaka.hls.HlsParser = class {
2114
2114
  closedCaptions,
2115
2115
  hdr: undefined,
2116
2116
  tilesLayout: undefined,
2117
+ external: false,
2117
2118
  };
2118
2119
  }
2119
2120
 
@@ -369,7 +369,7 @@ shaka.media.MediaSourceEngine = class {
369
369
  let mimeType = shaka.util.MimeUtils.getFullType(
370
370
  stream.mimeType, stream.codecs);
371
371
  if (contentType == ContentType.TEXT) {
372
- this.reinitText(mimeType, sequenceMode);
372
+ this.reinitText(mimeType, sequenceMode, stream.external);
373
373
  } else {
374
374
  if ((forceTransmux ||
375
375
  !shaka.media.Capabilities.isTypeSupported(mimeType)) &&
@@ -425,13 +425,14 @@ shaka.media.MediaSourceEngine = class {
425
425
  * Reinitialize the TextEngine for a new text type.
426
426
  * @param {string} mimeType
427
427
  * @param {boolean} sequenceMode
428
+ * @param {boolean} external
428
429
  */
429
- reinitText(mimeType, sequenceMode) {
430
+ reinitText(mimeType, sequenceMode, external) {
430
431
  if (!this.textEngine_) {
431
432
  this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
432
433
  }
433
434
  this.textEngine_.initParser(mimeType, sequenceMode,
434
- this.segmentRelativeVttTiming_);
435
+ external || this.segmentRelativeVttTiming_);
435
436
  }
436
437
 
437
438
  /**
@@ -651,7 +652,7 @@ shaka.media.MediaSourceEngine = class {
651
652
  // the video stream, so textEngine may not have been initialized.
652
653
  if (!this.textEngine_) {
653
654
  this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
654
- this.sequenceMode_);
655
+ this.sequenceMode_, /* external= */ false);
655
656
  }
656
657
  // This doesn't work for native TS support (ex. Edge/Chromecast),
657
658
  // since no transmuxing is needed for native TS.
@@ -671,7 +672,7 @@ shaka.media.MediaSourceEngine = class {
671
672
  } else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
672
673
  if (!this.textEngine_) {
673
674
  this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
674
- this.sequenceMode_);
675
+ this.sequenceMode_, /* external= */ false);
675
676
  }
676
677
  if (!this.captionParser_) {
677
678
  this.captionParser_ = this.getCaptionParser(mimeType);
@@ -244,7 +244,7 @@ shaka.media.StreamingEngine = class {
244
244
  const mimeType = shaka.util.MimeUtils.getFullType(
245
245
  stream.mimeType, stream.codecs);
246
246
  this.playerInterface_.mediaSourceEngine.reinitText(
247
- mimeType, this.manifest_.sequenceMode);
247
+ mimeType, this.manifest_.sequenceMode, stream.external);
248
248
 
249
249
  const textDisplayer =
250
250
  this.playerInterface_.mediaSourceEngine.getTextDisplayer();
@@ -458,7 +458,7 @@ shaka.media.StreamingEngine = class {
458
458
  const fullMimeType = shaka.util.MimeUtils.getFullType(
459
459
  stream.mimeType, stream.codecs);
460
460
  this.playerInterface_.mediaSourceEngine.reinitText(
461
- fullMimeType, this.manifest_.sequenceMode);
461
+ fullMimeType, this.manifest_.sequenceMode, stream.external);
462
462
  }
463
463
 
464
464
  // 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;
@@ -1325,6 +1325,7 @@ shaka.offline.Storage = class {
1325
1325
  spatialAudio: stream.spatialAudio,
1326
1326
  closedCaptions: stream.closedCaptions,
1327
1327
  tilesLayout: stream.tilesLayout,
1328
+ external: stream.external,
1328
1329
  };
1329
1330
 
1330
1331
  const startTime =
package/lib/player.js CHANGED
@@ -2192,6 +2192,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2192
2192
  await Promise.all(createSegmentIndexPromises);
2193
2193
  }
2194
2194
 
2195
+ shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
2196
+ this.config_.playRangeStart,
2197
+ this.config_.playRangeEnd);
2198
+
2195
2199
  this.playhead_ = this.createPlayhead(has.startTime);
2196
2200
  this.playheadObservers_ =
2197
2201
  this.createPlayheadObserversForMSE_(has.startTimeOfLoad);
@@ -2220,10 +2224,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2220
2224
 
2221
2225
  this.playhead_.setStartTime(adjustedTime);
2222
2226
  }
2223
-
2224
- // Since the first streams just became active, send an adaptation event.
2225
- this.onAdaptation_(null,
2226
- shaka.util.StreamUtils.variantToTrack(initialVariant));
2227
2227
  }
2228
2228
 
2229
2229
  this.playhead_.ready();
@@ -2390,6 +2390,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2390
2390
  audioSamplingRate: null,
2391
2391
  spatialAudio: false,
2392
2392
  closedCaptions: null,
2393
+ external: false,
2393
2394
  },
2394
2395
  bandwidth: 100,
2395
2396
  allowedByApplication: true,
@@ -4823,6 +4824,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4823
4824
  audioSamplingRate: null,
4824
4825
  spatialAudio: false,
4825
4826
  closedCaptions: null,
4827
+ external: true,
4826
4828
  };
4827
4829
 
4828
4830
  const fullMimeType = shaka.util.MimeUtils.getFullType(
@@ -4974,6 +4976,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4974
4976
  spatialAudio: false,
4975
4977
  closedCaptions: null,
4976
4978
  tilesLayout: '1x1',
4979
+ external: true,
4977
4980
  };
4978
4981
 
4979
4982
  this.manifest_.imageStreams.push(stream);
@@ -5393,6 +5396,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5393
5396
  audioSamplingRate: null,
5394
5397
  spatialAudio: false,
5395
5398
  closedCaptions: null,
5399
+ external: false,
5396
5400
  };
5397
5401
  manifest.textStreams.push(textStream);
5398
5402
  closedCaptionsSet.add(id);
@@ -7016,7 +7020,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7016
7020
  * @define {string} A version number taken from git at compile time.
7017
7021
  * @export
7018
7022
  */
7019
- shaka.Player.version = 'v4.3.8-uncompiled';
7023
+ shaka.Player.version = 'v4.3.10-uncompiled';
7020
7024
 
7021
7025
  // Initialize the deprecation system using the version string we just set
7022
7026
  // 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