shaka-player 4.3.7 → 4.3.9

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 (86) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +11 -0
  2. package/.github/workflows/appspot.yaml +39 -0
  3. package/.github/workflows/build-and-test.yaml +8 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +3 -2
  5. package/.github/workflows/update-issues.yaml +1 -0
  6. package/CHANGELOG.md +44 -0
  7. package/CONTRIBUTORS +1 -0
  8. package/demo/custom.js +1 -1
  9. package/dist/demo.compiled.debug.js +2 -2
  10. package/dist/demo.compiled.debug.map +2 -2
  11. package/dist/demo.compiled.js +2 -2
  12. package/dist/demo.compiled.map +2 -2
  13. package/dist/deps.js +2 -2
  14. package/dist/locales.js +31 -31
  15. package/dist/shaka-player.compiled.d.ts +24 -2
  16. package/dist/shaka-player.compiled.debug.d.ts +24 -2
  17. package/dist/shaka-player.compiled.debug.js +251 -247
  18. package/dist/shaka-player.compiled.debug.map +3 -3
  19. package/dist/shaka-player.compiled.js +796 -793
  20. package/dist/shaka-player.compiled.map +4 -4
  21. package/dist/shaka-player.ui.d.ts +24 -2
  22. package/dist/shaka-player.ui.debug.d.ts +24 -2
  23. package/dist/shaka-player.ui.debug.js +289 -286
  24. package/dist/shaka-player.ui.debug.map +3 -3
  25. package/dist/shaka-player.ui.js +831 -827
  26. package/dist/shaka-player.ui.map +4 -4
  27. package/docs/tutorials/drm-config.md +9 -6
  28. package/docs/tutorials/selenium-grid-config.md +60 -8
  29. package/externs/ima.js +34 -0
  30. package/externs/shaka/manifest.js +5 -1
  31. package/externs/shaka/offline.js +5 -1
  32. package/karma.conf.js +50 -7
  33. package/lib/abr/simple_abr_manager.js +6 -6
  34. package/lib/cea/mp4_cea_parser.js +24 -18
  35. package/lib/dash/dash_parser.js +19 -3
  36. package/lib/hls/hls_parser.js +24 -4
  37. package/lib/media/drm_engine.js +14 -8
  38. package/lib/media/gap_jumping_controller.js +24 -2
  39. package/lib/media/media_source_engine.js +11 -8
  40. package/lib/media/playhead.js +11 -0
  41. package/lib/media/region_observer.js +19 -1
  42. package/lib/media/streaming_engine.js +41 -21
  43. package/lib/media/video_wrapper.js +6 -1
  44. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  45. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  46. package/lib/offline/manifest_converter.js +1 -0
  47. package/lib/offline/storage.js +1 -0
  48. package/lib/player.js +20 -9
  49. package/lib/polyfill/abort_controller.js +7 -0
  50. package/lib/polyfill/orientation.js +34 -7
  51. package/lib/text/vtt_text_parser.js +3 -1
  52. package/lib/util/manifest_parser_utils.js +5 -3
  53. package/lib/util/periods.js +2 -0
  54. package/lib/util/platform.js +14 -0
  55. package/lib/util/stream_utils.js +70 -26
  56. package/package.json +2 -2
  57. package/test/cast/cast_utils_unit.js +4 -1
  58. package/test/cea/mp4_cea_parser_unit.js +28 -5
  59. package/test/dash/dash_parser_manifest_unit.js +120 -0
  60. package/test/hls/hls_parser_unit.js +216 -0
  61. package/test/media/adaptation_set_unit.js +1 -0
  62. package/test/media/drm_engine_integration.js +2 -2
  63. package/test/media/media_source_engine_unit.js +1 -1
  64. package/test/media/region_observer_unit.js +2 -1
  65. package/test/media/segment_index_unit.js +3 -3
  66. package/test/media/streaming_engine_integration.js +14 -11
  67. package/test/media/streaming_engine_unit.js +26 -2
  68. package/test/offline/manifest_convert_unit.js +5 -0
  69. package/test/offline/offline_integration.js +6 -3
  70. package/test/player_external.js +19 -16
  71. package/test/player_integration.js +35 -48
  72. package/test/player_src_equals_integration.js +9 -5
  73. package/test/test/assets/multiple-trun-init.mp4 +0 -0
  74. package/test/test/assets/multiple-trun-segment.mp4 +0 -0
  75. package/test/test/util/fake_demo_main.js +7 -3
  76. package/test/test/util/layout_tests.js +2 -2
  77. package/test/test/util/manifest_generator.js +2 -0
  78. package/test/test/util/offline_utils.js +1 -0
  79. package/test/test/util/streaming_engine_util.js +3 -0
  80. package/test/test/util/ui_utils.js +9 -2
  81. package/test/test/util/util.js +6 -27
  82. package/test/test/util/waiter.js +73 -3
  83. package/test/ui/ad_ui_unit.js +9 -9
  84. package/test/util/stream_utils_unit.js +46 -0
  85. package/ui/range_element.js +5 -0
  86. package/ui/resolution_selection.js +7 -1
@@ -59,15 +59,18 @@ If this is the only `<ContentProtection>` element in the manifest, Shaka will
59
59
  try all key systems it knows. (Based on keySystemsByURI in
60
60
  {@linksource shaka.extern.DashManifestConfiguration}.)
61
61
 
62
- Through `player.configure()`, you can change the dash key systems mapping by
62
+ Through `player.configure()`, you can update the dash key systems mapping by
63
63
  scheme URI:
64
+
64
65
  ```js
65
66
  player.configure({
66
- dash: {
67
- keySystemsByURI: {
68
- 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready.recommendation',
69
- 'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95': 'com.microsoft.playready.recommendation',
70
- }
67
+ manifest: {
68
+ dash: {
69
+ keySystemsByURI: {
70
+ 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready.recommendation',
71
+ 'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95': 'com.microsoft.playready.recommendation',
72
+ }
73
+ }
71
74
  }
72
75
  });
73
76
  ```
@@ -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
+ ```
package/externs/ima.js CHANGED
@@ -513,6 +513,14 @@ google.ima.dai.api.StreamRequest.prototype.streamActivityMonitorId;
513
513
  google.ima.dai.api.StreamRequest.prototype.format;
514
514
 
515
515
 
516
+ /**
517
+ * @type {Object.<
518
+ * google.ima.dai.api.OmidVerificationVendor,
519
+ * (google.ima.dai.api.OmidAccessMode | undefined)>}
520
+ */
521
+ google.ima.dai.api.StreamRequest.prototype.omidAccessModeRules;
522
+
523
+
516
524
  /**
517
525
  * @enum {string}
518
526
  */
@@ -599,3 +607,29 @@ google.ima.dai.api.StreamEvent.Type = {
599
607
  SKIPPED: 'skip',
600
608
  VIDEO_CLICKED: 'videoClicked',
601
609
  };
610
+
611
+
612
+ /**
613
+ * @enum {number}
614
+ */
615
+ google.ima.dai.api.OmidVerificationVendor = {
616
+ OTHER: 1,
617
+ MOAT: 2,
618
+ DOUBLEVERIFY: 3,
619
+ INTEGRAL_AD_SCIENCE: 4,
620
+ PIXELATE: 5,
621
+ NIELSEN: 6,
622
+ COMSCORE: 7,
623
+ MEETRICS: 8,
624
+ GOOGLE: 9,
625
+ };
626
+
627
+
628
+ /**
629
+ * @enum {string}
630
+ */
631
+ google.ima.dai.api.OmidAccessMode = {
632
+ FULL: 'full',
633
+ DOMAIN: 'domain',
634
+ LIMITED: 'limited',
635
+ };
@@ -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
 
@@ -603,15 +633,27 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
603
633
  }
604
634
 
605
635
  /**
636
+ * @param {karma.Launcher.Browser.spec} spec
606
637
  * @param {wd.remote} webDriverClient A WebDriver client, an object from the
607
638
  * "wd" package, created by "wd.remote()".
608
639
  * @return {!Promise.<!Buffer>} A Buffer containing a PNG screenshot
609
640
  */
610
- function getScreenshot(webDriverClient) {
641
+ function getScreenshot(spec, webDriverClient) {
611
642
  return new Promise((resolve, reject) => {
612
643
  webDriverClient.takeScreenshot((error, pngBase64) => {
613
644
  if (error) {
614
645
  reject(error);
646
+ } else if (pngBase64.error) {
647
+ // In some failure cases, pngBase64 is an object with "error",
648
+ // "message", and "stacktrace" fields. This happens, for example,
649
+ // with a timeout from the screenshot command. This is not an
650
+ // expected situation, so log it. The extra newlines keep this from
651
+ // being overwritten on the terminal when running tests against many
652
+ // browsers at once.
653
+ console.log('\n\nUnexpected screenshot failure:\n' +
654
+ ` Error: ${JSON.stringify(pngBase64)}\n` +
655
+ ` WebDriver spec: ${JSON.stringify(spec)}\n\n\n`);
656
+ reject(pngBase64);
615
657
  } else {
616
658
  // Convert the screenshot to a binary buffer.
617
659
  resolve(Buffer.from(pngBase64, 'base64'));
@@ -635,7 +677,8 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
635
677
  }
636
678
 
637
679
  /** @type {!Buffer} */
638
- const fullPageScreenshotData = await getScreenshot(webDriverClient);
680
+ const fullPageScreenshotData =
681
+ await getScreenshot(browser.spec, webDriverClient);
639
682
 
640
683
  // Crop the screenshot to the dimensions specified in the test.
641
684
  // Jimp is picky about types, so convert these strings to numbers.
@@ -754,7 +797,7 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
754
797
  // The result is cached for the sake of performance.
755
798
  if (webDriverClient.canTakeScreenshot === undefined) {
756
799
  try {
757
- await getScreenshot(webDriverClient);
800
+ await getScreenshot(browser.spec, webDriverClient);
758
801
  webDriverClient.canTakeScreenshot = true;
759
802
  } catch (error) {
760
803
  webDriverClient.canTakeScreenshot = false;
@@ -48,13 +48,16 @@ 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
54
57
  // of connectivity changes.
55
58
  if (navigator.connection && navigator.connection.addEventListener) {
56
59
  this.onNetworkInformationChange_ = () => {
57
- if (this.config_.useNetworkInformation && this.enabled_) {
60
+ if (this.enabled_ && this.config_.useNetworkInformation) {
58
61
  this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
59
62
  if (this.config_) {
60
63
  this.bandwidthEstimator_.configure(this.config_.advanced);
@@ -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.
@@ -163,15 +163,17 @@ shaka.cea.Mp4CeaParser = class {
163
163
  // Fields that are found in MOOF boxes
164
164
  let defaultSampleDuration = this.defaultSampleDuration_;
165
165
  let defaultSampleSize = this.defaultSampleSize_;
166
- let sampleData = [];
167
- let moofOffset = null;
168
- let trunOffset = null;
166
+ let moofOffset = 0;
167
+ /** @type {!Array<shaka.util.ParsedTRUNBox>} */
168
+ let parsedTRUNs = [];
169
169
  let baseMediaDecodeTime = null;
170
170
  let timescale = shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE;
171
171
 
172
172
  new Mp4Parser()
173
173
  .box('moof', (box) => {
174
174
  moofOffset = box.start;
175
+ // trun box parsing is reset on each moof.
176
+ parsedTRUNs = [];
175
177
  Mp4Parser.children(box);
176
178
  })
177
179
  .box('traf', Mp4Parser.children)
@@ -182,11 +184,8 @@ shaka.cea.Mp4CeaParser = class {
182
184
 
183
185
  const parsedTRUN = shaka.util.Mp4BoxParsers.parseTRUN(
184
186
  box.reader, box.version, box.flags);
185
-
186
- sampleData = parsedTRUN.sampleData;
187
- trunOffset = parsedTRUN.dataOffset;
187
+ parsedTRUNs.push(parsedTRUN);
188
188
  })
189
-
190
189
  .fullBox('tfhd', (box) => {
191
190
  goog.asserts.assert(
192
191
  box.flags != null,
@@ -210,7 +209,6 @@ shaka.cea.Mp4CeaParser = class {
210
209
  timescale = this.trackIdToTimescale_.get(trackId);
211
210
  }
212
211
  })
213
-
214
212
  .fullBox('tfdt', (box) => {
215
213
  goog.asserts.assert(
216
214
  box.version != null,
@@ -223,16 +221,19 @@ shaka.cea.Mp4CeaParser = class {
223
221
  })
224
222
  .box('mdat', (box) => {
225
223
  if (baseMediaDecodeTime === null) {
226
- // This field should have been populated by
227
- // the Base Media Decode time in the TFDT box
224
+ // This field should have been populated by the Base Media Decode
225
+ // Time in the tfdt box.
226
+ shaka.log.alwaysWarn(
227
+ 'Unable to find base media decode time for CEA captions!');
228
228
  throw new shaka.util.Error(
229
229
  shaka.util.Error.Severity.CRITICAL,
230
230
  shaka.util.Error.Category.TEXT,
231
231
  shaka.util.Error.Code.INVALID_MP4_CEA);
232
232
  }
233
- const offset = (moofOffset || 0) + (trunOffset || 0) - box.start - 8;
233
+
234
+ const offset = moofOffset - box.start - 8;
234
235
  this.parseMdat_(box.reader, baseMediaDecodeTime, timescale,
235
- defaultSampleDuration, defaultSampleSize, offset, sampleData,
236
+ defaultSampleDuration, defaultSampleSize, offset, parsedTRUNs,
236
237
  captionPackets);
237
238
  })
238
239
  .parse(mediaSegment, /* partialOkay= */ false);
@@ -248,12 +249,12 @@ shaka.cea.Mp4CeaParser = class {
248
249
  * @param {number} defaultSampleDuration
249
250
  * @param {number} defaultSampleSize
250
251
  * @param {number} offset
251
- * @param {!Array<shaka.util.ParsedTRUNSample>} sampleData
252
+ * @param {!Array<shaka.util.ParsedTRUNBox>} parsedTRUNs
252
253
  * @param {!Array<!shaka.cea.ICeaParser.CaptionPacket>} captionPackets
253
254
  * @private
254
255
  */
255
256
  parseMdat_(reader, time, timescale, defaultSampleDuration,
256
- defaultSampleSize, offset, sampleData, captionPackets) {
257
+ defaultSampleSize, offset, parsedTRUNs, captionPackets) {
257
258
  const BitstreamFormat = shaka.cea.Mp4CeaParser.BitstreamFormat;
258
259
  const ICeaParser = shaka.cea.ICeaParser;
259
260
  let sampleIndex = 0;
@@ -264,11 +265,16 @@ shaka.cea.Mp4CeaParser = class {
264
265
  // composition time offset, we default to 0.
265
266
  let sampleSize = defaultSampleSize;
266
267
 
268
+ // Combine all sample data. This assumes that the samples described across
269
+ // multiple trun boxes are still continuous in the mdat box.
270
+ const sampleDatas = parsedTRUNs.map((t) => t.sampleData);
271
+ const sampleData = [].concat(...sampleDatas);
272
+
267
273
  if (sampleData.length) {
268
274
  sampleSize = sampleData[0].sampleSize || defaultSampleSize;
269
275
  }
270
276
 
271
- reader.skip(offset);
277
+ reader.skip(offset + parsedTRUNs[0].dataOffset);
272
278
 
273
279
  while (reader.hasMoreData()) {
274
280
  const naluSize = reader.readUint32();
@@ -301,7 +307,7 @@ shaka.cea.Mp4CeaParser = class {
301
307
  if (isSeiMessage) {
302
308
  let timeOffset = 0;
303
309
 
304
- if (sampleData.length > sampleIndex) {
310
+ if (sampleIndex < sampleData.length) {
305
311
  timeOffset = sampleData[sampleIndex].sampleCompositionTimeOffset || 0;
306
312
  }
307
313
 
@@ -324,7 +330,7 @@ shaka.cea.Mp4CeaParser = class {
324
330
  }
325
331
  sampleSize -= (naluSize + 4);
326
332
  if (sampleSize == 0) {
327
- if (sampleData.length > sampleIndex) {
333
+ if (sampleIndex < sampleData.length) {
328
334
  time += sampleData[sampleIndex].sampleDuration ||
329
335
  defaultSampleDuration;
330
336
  } else {
@@ -333,7 +339,7 @@ shaka.cea.Mp4CeaParser = class {
333
339
 
334
340
  sampleIndex++;
335
341
 
336
- if (sampleData.length > sampleIndex) {
342
+ if (sampleIndex < sampleData.length) {
337
343
  sampleSize = sampleData[sampleIndex].sampleSize || defaultSampleSize;
338
344
  } else {
339
345
  sampleSize = defaultSampleSize;
@@ -886,7 +886,19 @@ shaka.dash.DashParser = class {
886
886
 
887
887
  // Parallel for HLS VIDEO-RANGE as defined in DASH-IF IOP v4.3 6.2.5.1.
888
888
  let videoRange;
889
- const videoRangeScheme = 'urn:mpeg:mpegB:cicp:TransferCharacteristics';
889
+
890
+ // Ref. https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf
891
+ // If signaled, a Supplemental or Essential Property descriptor
892
+ // shall be used, with the schemeIdUri set to
893
+ // urn:mpeg:mpegB:cicp:<Parameter> as defined in
894
+ // ISO/IEC 23001-8 [49] and <Parameter> one of the
895
+ // following: ColourPrimaries, TransferCharacteristics,
896
+ // or MatrixCoefficients.
897
+ const scheme = 'urn:mpeg:mpegB:cicp';
898
+ const transferCharacteristicsScheme = `${scheme}:TransferCharacteristics`;
899
+ const colourPrimariesScheme = `${scheme}:ColourPrimaries`;
900
+ const matrixCoefficientsScheme = `${scheme}:MatrixCoefficients`;
901
+
890
902
  const getVideoRangeFromTransferCharacteristicCICP = (cicp) => {
891
903
  switch (cicp) {
892
904
  case 1:
@@ -912,10 +924,13 @@ shaka.dash.DashParser = class {
912
924
  const schemeId = prop.getAttribute('schemeIdUri');
913
925
  if (schemeId == 'http://dashif.org/guidelines/trickmode') {
914
926
  trickModeFor = prop.getAttribute('value');
915
- } else if (schemeId == videoRangeScheme) {
927
+ } else if (schemeId == transferCharacteristicsScheme) {
916
928
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
917
929
  parseInt(prop.getAttribute('value'), 10),
918
930
  );
931
+ } else if (schemeId == colourPrimariesScheme ||
932
+ schemeId == matrixCoefficientsScheme) {
933
+ continue;
919
934
  } else {
920
935
  unrecognizedEssentialProperty = true;
921
936
  }
@@ -925,7 +940,7 @@ shaka.dash.DashParser = class {
925
940
  XmlUtils.findChildren(elem, 'SupplementalProperty');
926
941
  for (const prop of supplementalProperties) {
927
942
  const schemeId = prop.getAttribute('schemeIdUri');
928
- if (schemeId == videoRangeScheme) {
943
+ if (schemeId == transferCharacteristicsScheme) {
929
944
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
930
945
  parseInt(prop.getAttribute('value'), 10),
931
946
  );
@@ -1322,6 +1337,7 @@ shaka.dash.DashParser = class {
1322
1337
  hdr,
1323
1338
  tilesLayout,
1324
1339
  matchedStreams: [],
1340
+ external: false,
1325
1341
  };
1326
1342
  }
1327
1343
 
@@ -2066,6 +2066,22 @@ shaka.hls.HlsParser = class {
2066
2066
  // stream is lazy-loaded.
2067
2067
  const mimeType = this.guessMimeTypeBeforeLoading_(type, codecs) ||
2068
2068
  this.guessMimeTypeFallback_(type);
2069
+ const roles = [];
2070
+ if (characteristics) {
2071
+ for (const characteristic of characteristics.split(',')) {
2072
+ roles.push(characteristic);
2073
+ }
2074
+ }
2075
+
2076
+ const kind = (type == shaka.util.ManifestParserUtils.ContentType.TEXT) ?
2077
+ shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE : undefined;
2078
+
2079
+ // If there are no roles, and we have defaulted to the subtitle "kind" for
2080
+ // this track, add the implied subtitle role.
2081
+ if (!roles.length &&
2082
+ kind === shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE) {
2083
+ roles.push(shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE);
2084
+ }
2069
2085
 
2070
2086
  return {
2071
2087
  id: this.globalId_++,
@@ -2074,8 +2090,7 @@ shaka.hls.HlsParser = class {
2074
2090
  segmentIndex: null,
2075
2091
  mimeType,
2076
2092
  codecs,
2077
- kind: (type == shaka.util.ManifestParserUtils.ContentType.TEXT) ?
2078
- shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE : undefined,
2093
+ kind,
2079
2094
  encrypted: false,
2080
2095
  drmInfos: [],
2081
2096
  keyIds: new Set(),
@@ -2091,7 +2106,7 @@ shaka.hls.HlsParser = class {
2091
2106
  width: undefined,
2092
2107
  height: undefined,
2093
2108
  bandwidth: undefined,
2094
- roles: characteristics ? characteristics.split(',') : [],
2109
+ roles,
2095
2110
  forced,
2096
2111
  channelsCount,
2097
2112
  audioSamplingRate: null,
@@ -2099,6 +2114,7 @@ shaka.hls.HlsParser = class {
2099
2114
  closedCaptions,
2100
2115
  hdr: undefined,
2101
2116
  tilesLayout: undefined,
2117
+ external: false,
2102
2118
  };
2103
2119
  }
2104
2120
 
@@ -2549,6 +2565,10 @@ shaka.hls.HlsParser = class {
2549
2565
  // We're interested in the duration part.
2550
2566
  const extinfValues = extinfTag.value.split(',');
2551
2567
  const duration = Number(extinfValues[0]);
2568
+ // Skip segments without duration
2569
+ if (duration == 0) {
2570
+ return null;
2571
+ }
2552
2572
  endTime = startTime + duration;
2553
2573
  } else {
2554
2574
  endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
@@ -2729,9 +2749,9 @@ shaka.hls.HlsParser = class {
2729
2749
  type,
2730
2750
  lastDiscontinuityStartTime,
2731
2751
  hlsAes128Key);
2732
- previousReference = reference;
2733
2752
 
2734
2753
  if (reference) {
2754
+ previousReference = reference;
2735
2755
  reference.discontinuitySequence = discontinuitySequence;
2736
2756
 
2737
2757
  if (this.config_.hls.ignoreManifestProgramDateTime &&
@@ -354,18 +354,24 @@ shaka.media.DrmEngine = class {
354
354
  shaka.media.DrmEngine.replaceDrmInfo_(variants, servers);
355
355
  }
356
356
 
357
- // Make sure all the drm infos are valid and filled in correctly.
357
+ /** @type {!Set<shaka.extern.DrmInfo>} */
358
+ const drmInfos = new Set();
358
359
  for (const variant of variants) {
359
- const drmInfos = this.getVariantDrmInfos_(variant);
360
- for (const info of drmInfos) {
361
- shaka.media.DrmEngine.fillInDrmInfoDefaults_(
362
- info,
363
- shaka.util.MapUtils.asMap(this.config_.servers),
364
- shaka.util.MapUtils.asMap(this.config_.advanced || {}),
365
- this.config_.keySystemsMapping);
360
+ const variantDrmInfos = this.getVariantDrmInfos_(variant);
361
+ for (const info of variantDrmInfos) {
362
+ drmInfos.add(info);
366
363
  }
367
364
  }
368
365
 
366
+ for (const info of drmInfos) {
367
+ shaka.media.DrmEngine.fillInDrmInfoDefaults_(
368
+ info,
369
+ shaka.util.MapUtils.asMap(this.config_.servers),
370
+ shaka.util.MapUtils.asMap(this.config_.advanced || {}),
371
+ this.config_.keySystemsMapping);
372
+ }
373
+
374
+
369
375
  /** @type {!Map.<string, MediaKeySystemConfiguration>} */
370
376
  let configsByKeySystem;
371
377