shaka-player 4.3.5 → 4.3.6

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 (88) hide show
  1. package/.github/workflows/selenium-lab-tests.yaml +23 -1
  2. package/AUTHORS +2 -1
  3. package/CHANGELOG.md +32 -0
  4. package/CONTRIBUTORS +4 -1
  5. package/dist/controls.css +2 -2
  6. package/dist/controls.css.map +1 -1
  7. package/dist/demo.compiled.debug.js +167 -296
  8. package/dist/demo.compiled.debug.map +4 -4
  9. package/dist/demo.compiled.js +167 -296
  10. package/dist/demo.compiled.map +4 -4
  11. package/dist/deps.js +1 -1
  12. package/dist/locales.js +32 -32
  13. package/dist/receiver.compiled.debug.js +34 -63
  14. package/dist/receiver.compiled.debug.map +4 -4
  15. package/dist/receiver.compiled.js +34 -63
  16. package/dist/receiver.compiled.map +4 -4
  17. package/dist/shaka-player.compiled.d.ts +12 -3
  18. package/dist/shaka-player.compiled.debug.d.ts +12 -3
  19. package/dist/shaka-player.compiled.debug.externs.js +20 -14
  20. package/dist/shaka-player.compiled.debug.js +261 -255
  21. package/dist/shaka-player.compiled.debug.map +4 -4
  22. package/dist/shaka-player.compiled.externs.js +20 -14
  23. package/dist/shaka-player.compiled.js +862 -858
  24. package/dist/shaka-player.compiled.map +3 -3
  25. package/dist/shaka-player.ui.d.ts +12 -3
  26. package/dist/shaka-player.ui.debug.d.ts +12 -3
  27. package/dist/shaka-player.ui.debug.externs.js +8 -2
  28. package/dist/shaka-player.ui.debug.js +271 -264
  29. package/dist/shaka-player.ui.debug.map +3 -3
  30. package/dist/shaka-player.ui.externs.js +8 -2
  31. package/dist/shaka-player.ui.js +1005 -1002
  32. package/dist/shaka-player.ui.map +3 -3
  33. package/docs/tutorials/a11y.md +0 -4
  34. package/docs/tutorials/ad_monetization.md +0 -4
  35. package/docs/tutorials/basic-usage.md +0 -5
  36. package/docs/tutorials/config.md +0 -5
  37. package/docs/tutorials/debugging.md +0 -5
  38. package/docs/tutorials/drm-config.md +0 -5
  39. package/docs/tutorials/fairplay.md +2 -2
  40. package/docs/tutorials/license-server-auth.md +0 -5
  41. package/docs/tutorials/license-wrapping.md +0 -5
  42. package/docs/tutorials/network-and-buffering-config.md +0 -5
  43. package/docs/tutorials/ui-customization.md +0 -5
  44. package/docs/tutorials/ui.md +0 -7
  45. package/docs/tutorials/welcome.md +0 -5
  46. package/externs/network_information.js +7 -2
  47. package/externs/shaka/abr_manager.js +6 -0
  48. package/externs/shaka/net.js +1 -0
  49. package/externs/shaka/player.js +13 -7
  50. package/lib/abr/simple_abr_manager.js +27 -3
  51. package/lib/ads/client_side_ad_manager.js +6 -3
  52. package/lib/dash/dash_parser.js +49 -48
  53. package/lib/debug/log.js +9 -13
  54. package/lib/hls/hls_parser.js +17 -5
  55. package/lib/media/media_source_engine.js +27 -9
  56. package/lib/media/presentation_timeline.js +9 -2
  57. package/lib/media/segment_index.js +2 -0
  58. package/lib/media/segment_reference.js +3 -2
  59. package/lib/media/streaming_engine.js +31 -7
  60. package/lib/net/http_fetch_plugin.js +79 -57
  61. package/lib/net/networking_engine.js +1 -0
  62. package/lib/player.js +22 -3
  63. package/lib/polyfill/media_capabilities.js +13 -0
  64. package/lib/text/mp4_vtt_parser.js +4 -0
  65. package/lib/text/text_engine.js +1 -0
  66. package/lib/util/manifest_parser_utils.js +1 -2
  67. package/lib/util/mime_utils.js +11 -0
  68. package/lib/util/periods.js +5 -5
  69. package/lib/util/stream_utils.js +2 -2
  70. package/maintained-branches.md +2 -3
  71. package/package.json +1 -1
  72. package/test/dash/dash_parser_live_unit.js +3 -2
  73. package/test/hls/hls_parser_unit.js +156 -0
  74. package/test/media/content_workarounds_unit.js +43 -0
  75. package/test/media/drm_engine_integration.js +4 -17
  76. package/test/media/streaming_engine_unit.js +6 -1
  77. package/test/net/networking_engine_unit.js +6 -1
  78. package/test/player_integration.js +39 -0
  79. package/test/player_unit.js +57 -0
  80. package/test/polyfill/media_capabilities_unit.js +1 -0
  81. package/test/test/assets/text-clip-alt.vtt +63 -0
  82. package/test/test/util/fake_drm_engine.js +3 -0
  83. package/test/test/util/fake_media_source_engine.js +4 -0
  84. package/test/test/util/simple_fakes.js +6 -0
  85. package/test/test/util/test_scheme.js +9 -14
  86. package/test/text/mp4_vtt_parser_unit.js +8 -0
  87. package/test/util/mime_utils_unit.js +10 -0
  88. package/ui/less/containers.less +4 -0
@@ -56,10 +56,6 @@ this.eventManager.listen(this.button_, 'click', () => {
56
56
  });
57
57
  ```
58
58
 
59
- #### Continue the Tutorials
60
-
61
- Next, check out {@tutorial ad_monetization}.
62
-
63
59
 
64
60
 
65
61
  [Web Accessibility Standards]: https://www.w3.org/WAI/standards-guidelines/wcag/
@@ -222,7 +222,3 @@ before instantiating the player.
222
222
  // myapp.CustomAdManager is a placeholder name for your ad manager implementation.
223
223
  shaka.Player.setAdManagerFactory(() => new myapp.CustomAdManager());
224
224
  ```
225
-
226
- #### Continue the Tutorials
227
-
228
- Next, check out {@tutorial plugins}.
@@ -86,8 +86,3 @@ document.addEventListener('DOMContentLoaded', initApp);
86
86
  ```
87
87
 
88
88
  That's it!
89
-
90
-
91
- #### Continue the Tutorials
92
-
93
- Next, check out {@tutorial debugging}.
@@ -142,8 +142,3 @@ player.configure({
142
142
 
143
143
  For more detail on individual configuration options, please see the API docs for
144
144
  {@link shaka.extern.PlayerConfiguration} and {@link shaka.Player#configure}.
145
-
146
-
147
- #### Continue the Tutorials
148
-
149
- Next, check out {@tutorial network-and-buffering-config}.
@@ -187,8 +187,3 @@ To sum up, remember these points when debugging your application:
187
187
  - Use the debug version of Shaka Player for debugging and integration work
188
188
  - Refer to the docs for error codes
189
189
  - Increase the log level when you need more detail
190
-
191
-
192
- #### Continue the Tutorials
193
-
194
- Next, check out {@tutorial config}.
@@ -208,8 +208,3 @@ you should provide an empty string as robustness
208
208
  ##### Other key-systems
209
209
 
210
210
  Values for other key systems are not known to us at this time.
211
-
212
- #### Continue the Tutorials
213
-
214
- Next, check out {@tutorial license-server-auth}.
215
- Or check out {@tutorial fairplay}.
@@ -72,13 +72,13 @@ is used by the browser to generate the license request. If you don't use the
72
72
  default content ID derivation, you need to specify a custom init data transform:
73
73
 
74
74
  ```js
75
- player.configure('drm.initDataTransform', (initData, initDataType) => {
75
+ player.configure('drm.initDataTransform', (initData, initDataType, drmInfo) => {
76
76
  if (initDataType != 'skd')
77
77
  return initData;
78
78
  // 'initData' is a buffer containing an 'skd://' URL as a UTF-8 string.
79
79
  const skdUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
80
80
  const contentId = getMyContentId(skdUri);
81
- const cert = player.drmInfo().serverCertificate;
81
+ const cert = drmInfo.serverCertificate;
82
82
  return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
83
83
  });
84
84
  ```
@@ -246,8 +246,3 @@ License request can now continue.
246
246
 
247
247
  If you need them, you can also create asynchronous response filters in the same
248
248
  way.
249
-
250
-
251
- #### Continue the Tutorials
252
-
253
- Next, check out {@tutorial license-wrapping}.
@@ -166,8 +166,3 @@ using a request filter:
166
166
 
167
167
  Load the page again, and the license response will be accepted by the Widevine
168
168
  CDM. Open the JavaScript console to see what the server sent back.
169
-
170
-
171
- #### Continue the Tutorials
172
-
173
- Next, check out {@tutorial ui}.
@@ -117,8 +117,3 @@ URLs in your manifests always use `https:`, or by having it not include the
117
117
  scheme (e.g., `//example.com/file.mp4`).
118
118
 
119
119
  [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
120
-
121
-
122
- #### Continue the Tutorials
123
-
124
- Next, check out {@tutorial drm-config}.
@@ -265,8 +265,3 @@ PR contributions to [the gallery repo][] are welcome.
265
265
  [@lucksy]: https://github.com/lucksy
266
266
  [pre-packaged Shaka UI themes]: https://lucksy.github.io/shaka-player-themes/
267
267
  [the gallery repo]: https://github.com/lucksy/shaka-player-themes
268
-
269
-
270
- #### Continue the Tutorials
271
-
272
- Next, check out {@tutorial a11y} to make your custom buttons accessible to screen readers.
@@ -227,10 +227,3 @@ ui.configure({
227
227
  'castAndroidReceiverCompatible': true,
228
228
  });
229
229
  ```
230
-
231
-
232
-
233
-
234
- #### Continue the Tutorials
235
-
236
- Next, check out {@tutorial ui-customization}.
@@ -106,8 +106,3 @@ To subscribe to new releases on GitHub, you can follow
106
106
  To receive infrequent announcements and surveys from us, you can join our
107
107
  [mailing list](https://groups.google.com/forum/#!forum/shaka-player-users).
108
108
  The list is very low volume, and can only be written to by us.
109
-
110
-
111
- #### Continue the Tutorials
112
-
113
- Next, check out {@tutorial basic-usage}.
@@ -19,5 +19,10 @@ NetworkInformation.prototype.saveData;
19
19
  * @param {string} type
20
20
  * @param {Function} listener
21
21
  */
22
- NetworkInformation.prototype.addEventListener =
23
- function(type, listener) {};
22
+ NetworkInformation.prototype.addEventListener = function(type, listener) {};
23
+
24
+ /**
25
+ * @param {string} type
26
+ * @param {Function} listener
27
+ */
28
+ NetworkInformation.prototype.removeEventListener = function(type, listener) {};
@@ -44,6 +44,12 @@ shaka.extern.AbrManager = class {
44
44
  */
45
45
  stop() {}
46
46
 
47
+ /**
48
+ * Request that this object release all internal references.
49
+ * @exportDoc
50
+ */
51
+ release() {}
52
+
47
53
  /**
48
54
  * Updates manager's variants collection.
49
55
  *
@@ -112,6 +112,7 @@ shaka.extern.Request;
112
112
  /**
113
113
  * @typedef {{
114
114
  * uri: string,
115
+ * originalUri: string,
115
116
  * data: BufferSource,
116
117
  * status: (number|undefined),
117
118
  * headers: !Object.<string, string>,
@@ -688,9 +688,7 @@ shaka.extern.AdvancedDrmConfiguration;
688
688
  * clearKeys: !Object.<string, string>,
689
689
  * delayLicenseRequestUntilPlayed: boolean,
690
690
  * advanced: Object.<string, shaka.extern.AdvancedDrmConfiguration>,
691
- * initDataTransform:
692
- * ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
693
- * undefined),
691
+ * initDataTransform:(shaka.extern.InitDataTransform|undefined),
694
692
  * logLicenseExchange: boolean,
695
693
  * updateExpirationTime: number,
696
694
  * preferredKeySystems: !Array.<string>,
@@ -716,10 +714,7 @@ shaka.extern.AdvancedDrmConfiguration;
716
714
  * <i>Optional.</i> <br>
717
715
  * A dictionary which maps key system IDs to advanced DRM configuration for
718
716
  * those key systems.
719
- * @property
720
- * {((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
721
- * undefined)}
722
- * initDataTransform
717
+ * @property {shaka.extern.InitDataTransform|undefined} initDataTransform
723
718
  * <i>Optional.</i><br>
724
719
  * If given, this function is called with the init data from the
725
720
  * manifest/media and should return the (possibly transformed) init data to
@@ -748,6 +743,17 @@ shaka.extern.AdvancedDrmConfiguration;
748
743
  */
749
744
  shaka.extern.DrmConfiguration;
750
745
 
746
+ /**
747
+ * @typedef {function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array}
748
+ *
749
+ * @description
750
+ * A callback function to handle custom content ID signaling for FairPlay
751
+ * content.
752
+ *
753
+ * @exportDoc
754
+ */
755
+ shaka.extern.InitDataTransform;
756
+
751
757
 
752
758
  /**
753
759
  * @typedef {{
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.abr.SimpleAbrManager');
8
8
 
9
+ goog.require('shaka.util.IReleasable');
9
10
  goog.require('goog.asserts');
10
11
  goog.require('shaka.abr.EwmaBandwidthEstimator');
11
12
  goog.require('shaka.log');
@@ -32,6 +33,7 @@ goog.require('shaka.util.Timer');
32
33
  * </p>
33
34
  *
34
35
  * @implements {shaka.extern.AbrManager}
36
+ * @implements {shaka.util.IReleasable}
35
37
  * @export
36
38
  */
37
39
  shaka.abr.SimpleAbrManager = class {
@@ -50,8 +52,8 @@ shaka.abr.SimpleAbrManager = class {
50
52
  // retrieving information about a user's network connection. We listen
51
53
  // to the change event to be able to make quick changes in case the type
52
54
  // of connectivity changes.
53
- if (navigator.connection) {
54
- navigator.connection.addEventListener('change', () => {
55
+ if (navigator.connection && navigator.connection.addEventListener) {
56
+ this.onNetworkInformationChange_ = () => {
55
57
  if (this.config_.useNetworkInformation && this.enabled_) {
56
58
  this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
57
59
  if (this.config_) {
@@ -62,7 +64,10 @@ shaka.abr.SimpleAbrManager = class {
62
64
  this.switch_(chosenVariant);
63
65
  }
64
66
  }
65
- });
67
+ };
68
+
69
+ navigator.connection.addEventListener(
70
+ 'change', this.onNetworkInformationChange_);
66
71
  }
67
72
 
68
73
  /**
@@ -93,6 +98,9 @@ shaka.abr.SimpleAbrManager = class {
93
98
  /** @private {ResizeObserver} */
94
99
  this.resizeObserver_ = null;
95
100
 
101
+ /** @private {?function():void} */
102
+ this.onNetworkInformationChange_ = null;
103
+
96
104
  /** @private {shaka.util.Timer} */
97
105
  this.resizeObserverTimer_ = new shaka.util.Timer(() => {
98
106
  if (this.config_.restrictToElementSize) {
@@ -128,6 +136,22 @@ shaka.abr.SimpleAbrManager = class {
128
136
  // can start using bandwidth estimates right away after init() is called.
129
137
  }
130
138
 
139
+ /**
140
+ * @override
141
+ * @export
142
+ */
143
+ release() {
144
+ // stop() should already have been called for unload
145
+
146
+ if (navigator.connection && navigator.connection.removeEventListener) {
147
+ navigator.connection.removeEventListener(
148
+ 'change', this.onNetworkInformationChange_);
149
+ this.onNetworkInformationChange_ = null;
150
+ }
151
+
152
+ this.resizeObserverTimer_ = null;
153
+ }
154
+
131
155
 
132
156
  /**
133
157
  * @override
@@ -219,9 +219,9 @@ shaka.ads.ClientSideAdManager = class {
219
219
  // seen the ads actually play until requestAds() is called.
220
220
  // Note: We listen for a play event to avoid autoplay issues that might
221
221
  // crash IMA.
222
- this.video_.addEventListener('play', () => {
222
+ this.eventManager_.listenOnce(this.video_, 'play', () => {
223
223
  this.imaAdsManager_.start();
224
- }, {once: true});
224
+ });
225
225
  } catch (adError) {
226
226
  // If there was a problem with the VAST response,
227
227
  // we we won't be getting an ad. Hide ad UI if we showed it already
@@ -440,7 +440,10 @@ shaka.ads.ClientSideAdManager = class {
440
440
  if (this.ad_.isLinear()) {
441
441
  this.adContainer_.setAttribute('ad-active', 'true');
442
442
  this.video_.pause();
443
- this.ad_.setVolume(this.video_.muted ? 0 : this.video_.volume);
443
+ this.ad_.setVolume(this.video_.volume);
444
+ if (this.video_.muted) {
445
+ this.ad_.setMuted(true);
446
+ }
444
447
  }
445
448
  }
446
449
 
@@ -1279,55 +1279,56 @@ shaka.dash.DashParser = class {
1279
1279
  context.period.id + ',' + context.representation.id : '';
1280
1280
 
1281
1281
  /** @type {shaka.extern.Stream} */
1282
- const stream = {
1283
- id: this.globalId_++,
1284
- originalId: context.representation.id,
1285
- createSegmentIndex: async () => {
1286
- // If we have a stream with the same context id stored in the map
1287
- // that has no segmentIndex, we should set the segmentIndex for it.
1288
- const storedInMap = contextId && context.dynamic &&
1289
- this.streamMap_[contextId];
1290
-
1291
- const currentStream = storedInMap ? this.streamMap_[contextId] : stream;
1292
- if (!currentStream.segmentIndex) {
1293
- currentStream.segmentIndex = await streamInfo.generateSegmentIndex();
1294
- }
1295
- },
1282
+ let stream;
1296
1283
 
1297
- closeSegmentIndex: () => {
1298
- if (stream.segmentIndex) {
1299
- stream.segmentIndex.release();
1300
- stream.segmentIndex = null;
1301
- }
1302
- },
1303
- segmentIndex: null,
1304
- mimeType: context.representation.mimeType,
1305
- codecs: context.representation.codecs,
1306
- frameRate: context.representation.frameRate,
1307
- pixelAspectRatio: context.representation.pixelAspectRatio,
1308
- bandwidth: context.bandwidth,
1309
- width: context.representation.width,
1310
- height: context.representation.height,
1311
- kind,
1312
- encrypted: contentProtection.drmInfos.length > 0,
1313
- drmInfos: contentProtection.drmInfos,
1314
- keyIds,
1315
- language,
1316
- label,
1317
- type: context.adaptationSet.contentType,
1318
- primary: isPrimary,
1319
- trickModeVideo: null,
1320
- emsgSchemeIdUris:
1321
- context.representation.emsgSchemeIdUris,
1322
- roles,
1323
- forced: forced,
1324
- channelsCount: context.representation.numChannels,
1325
- audioSamplingRate: context.representation.audioSamplingRate,
1326
- spatialAudio: spatialAudio,
1327
- closedCaptions,
1328
- hdr,
1329
- tilesLayout,
1330
- matchedStreams: [],
1284
+ if (contextId && this.streamMap_[contextId]) {
1285
+ stream = this.streamMap_[contextId];
1286
+ } else {
1287
+ stream = {
1288
+ id: this.globalId_++,
1289
+ originalId: context.representation.id,
1290
+ createSegmentIndex: () => Promise.resolve(),
1291
+ closeSegmentIndex: () => {
1292
+ if (stream.segmentIndex) {
1293
+ stream.segmentIndex.release();
1294
+ stream.segmentIndex = null;
1295
+ }
1296
+ },
1297
+ segmentIndex: null,
1298
+ mimeType: context.representation.mimeType,
1299
+ codecs: context.representation.codecs,
1300
+ frameRate: context.representation.frameRate,
1301
+ pixelAspectRatio: context.representation.pixelAspectRatio,
1302
+ bandwidth: context.bandwidth,
1303
+ width: context.representation.width,
1304
+ height: context.representation.height,
1305
+ kind,
1306
+ encrypted: contentProtection.drmInfos.length > 0,
1307
+ drmInfos: contentProtection.drmInfos,
1308
+ keyIds,
1309
+ language,
1310
+ label,
1311
+ type: context.adaptationSet.contentType,
1312
+ primary: isPrimary,
1313
+ trickModeVideo: null,
1314
+ emsgSchemeIdUris:
1315
+ context.representation.emsgSchemeIdUris,
1316
+ roles,
1317
+ forced,
1318
+ channelsCount: context.representation.numChannels,
1319
+ audioSamplingRate: context.representation.audioSamplingRate,
1320
+ spatialAudio,
1321
+ closedCaptions,
1322
+ hdr,
1323
+ tilesLayout,
1324
+ matchedStreams: [],
1325
+ };
1326
+ }
1327
+
1328
+ stream.createSegmentIndex = async () => {
1329
+ if (!stream.segmentIndex) {
1330
+ stream.segmentIndex = await streamInfo.generateSegmentIndex();
1331
+ }
1331
1332
  };
1332
1333
 
1333
1334
  if (contextId && context.dynamic && !this.streamMap_[contextId]) {
package/lib/debug/log.js CHANGED
@@ -134,23 +134,19 @@ shaka.log.MAX_LOG_LEVEL = 3;
134
134
  shaka.log.oneTimeWarningIssued_ = new Set();
135
135
 
136
136
 
137
- // IE8 has no console unless it is opened in advance.
138
- // IE9 console methods are not Functions and have no bind.
139
- if (window.console && window.console.log.bind) {
137
+ if (window.console) {
140
138
  /** @private {!Object.<shaka.log.Level, function(...*)>} */
141
139
  shaka.log.logMap_ = {
142
- /* eslint-disable no-restricted-syntax */
143
- [shaka.log.Level.ERROR]: console.error.bind(console),
144
- [shaka.log.Level.WARNING]: console.warn.bind(console),
145
- [shaka.log.Level.INFO]: console.info.bind(console),
146
- [shaka.log.Level.DEBUG]: console.log.bind(console),
147
- [shaka.log.Level.V1]: console.debug.bind(console),
148
- [shaka.log.Level.V2]: console.debug.bind(console),
149
- /* eslint-enable no-restricted-syntax */
140
+ [shaka.log.Level.ERROR]: (...args) => console.error(...args),
141
+ [shaka.log.Level.WARNING]: (...args) => console.warn(...args),
142
+ [shaka.log.Level.INFO]: (...args) => console.info(...args),
143
+ [shaka.log.Level.DEBUG]: (...args) => console.log(...args),
144
+ [shaka.log.Level.V1]: (...args) => console.debug(...args),
145
+ [shaka.log.Level.V2]: (...args) => console.debug(...args),
150
146
  };
151
147
 
152
- shaka.log.alwaysWarn = shaka.log.logMap_[shaka.log.Level.WARNING];
153
- shaka.log.alwaysError = shaka.log.logMap_[shaka.log.Level.ERROR];
148
+ shaka.log.alwaysWarn = (...args) => console.warn(...args);
149
+ shaka.log.alwaysError = (...args) => console.error(...args);
154
150
 
155
151
  if (goog.DEBUG) {
156
152
  // Since we don't want to export shaka.log in production builds, we don't
@@ -1458,9 +1458,9 @@ shaka.hls.HlsParser = class {
1458
1458
  let codecs = '';
1459
1459
  /** @type {string} */
1460
1460
  const type = this.getType_(tag);
1461
- // Text does not require a codec.
1462
- if (type != shaka.util.ManifestParserUtils.ContentType.TEXT && groupId &&
1463
- this.groupIdToCodecsMap_.has(groupId)) {
1461
+ if (type == shaka.util.ManifestParserUtils.ContentType.TEXT) {
1462
+ codecs = tag.getAttributeValue('CODECS') || '';
1463
+ } else if (groupId && this.groupIdToCodecsMap_.has(groupId)) {
1464
1464
  codecs = this.groupIdToCodecsMap_.get(groupId);
1465
1465
  }
1466
1466
 
@@ -1736,6 +1736,13 @@ shaka.hls.HlsParser = class {
1736
1736
  }
1737
1737
  }
1738
1738
 
1739
+ if (type == ContentType.TEXT) {
1740
+ const firstSegment = realStream.segmentIndex.get(0);
1741
+ if (firstSegment && firstSegment.initSegmentReference) {
1742
+ stream.mimeType = 'application/mp4';
1743
+ }
1744
+ }
1745
+
1739
1746
  // Add finishing touches to the stream that can only be done once we have
1740
1747
  // more full context on the media as a whole.
1741
1748
  if (this.hasEnoughInfoToFinalizeStreams_()) {
@@ -2371,13 +2378,15 @@ shaka.hls.HlsParser = class {
2371
2378
  * @param {!Map.<string, string>} variables
2372
2379
  * @param {string} absoluteMediaPlaylistUri
2373
2380
  * @param {string} type
2381
+ * @param {number} timestampOffset
2374
2382
  * @param {shaka.extern.HlsAes128Key=} hlsAes128Key
2375
2383
  * @return {shaka.media.SegmentReference}
2376
2384
  * @private
2377
2385
  */
2378
2386
  createSegmentReference_(
2379
2387
  initSegmentReference, previousReference, hlsSegment, startTime,
2380
- variables, absoluteMediaPlaylistUri, type, hlsAes128Key) {
2388
+ variables, absoluteMediaPlaylistUri, type, timestampOffset,
2389
+ hlsAes128Key) {
2381
2390
  const tags = hlsSegment.tags;
2382
2391
  const absoluteSegmentUri = this.variableSubstitution_(
2383
2392
  hlsSegment.absoluteUri, variables);
@@ -2547,7 +2556,7 @@ shaka.hls.HlsParser = class {
2547
2556
  startByte,
2548
2557
  endByte,
2549
2558
  initSegmentReference,
2550
- /* timestampOffset= */ 0, // This value is ignored in sequence mode.
2559
+ timestampOffset, // This value is ignored in sequence mode.
2551
2560
  /* appendWindowStart= */ 0,
2552
2561
  /* appendWindowEnd= */ Infinity,
2553
2562
  partialSegmentRefs,
@@ -2632,6 +2641,7 @@ shaka.hls.HlsParser = class {
2632
2641
  const references = [];
2633
2642
 
2634
2643
  let previousReference = null;
2644
+ let lastDiscontinuityStartTime = firstStartTime;
2635
2645
 
2636
2646
  for (let i = 0; i < hlsSegments.length; i++) {
2637
2647
  const item = hlsSegments[i];
@@ -2643,6 +2653,7 @@ shaka.hls.HlsParser = class {
2643
2653
  item.tags, 'EXT-X-DISCONTINUITY');
2644
2654
  if (discontinuityTag) {
2645
2655
  discontinuitySequence++;
2656
+ lastDiscontinuityStartTime = startTime;
2646
2657
  }
2647
2658
 
2648
2659
  // Apply new AES-128 tags as you see them, keeping a running total.
@@ -2681,6 +2692,7 @@ shaka.hls.HlsParser = class {
2681
2692
  variables,
2682
2693
  playlist.absoluteUri,
2683
2694
  type,
2695
+ lastDiscontinuityStartTime,
2684
2696
  hlsAes128Key);
2685
2697
  previousReference = reference;
2686
2698
 
@@ -202,6 +202,9 @@ shaka.media.MediaSourceEngine = class {
202
202
  'audio/mp4; codecs="ec-3"',
203
203
  'audio/mp4; codecs="opus"',
204
204
  'audio/mp4; codecs="flac"',
205
+ 'audio/mp4; codecs="dtsc"', // DTS Digital Surround
206
+ 'audio/mp4; codecs="dtse"', // DTS Express
207
+ 'audio/mp4; codecs="dtsx"', // DTS:X
205
208
  // WebM types
206
209
  'video/webm; codecs="vp8"',
207
210
  'video/webm; codecs="vp9"',
@@ -375,7 +378,22 @@ shaka.media.MediaSourceEngine = class {
375
378
  shaka.media.Transmuxer.convertCodecs(contentType, mimeType);
376
379
  }
377
380
  const type = mimeType + this.config_.sourceBufferExtraFeatures;
378
- const sourceBuffer = this.mediaSource_.addSourceBuffer(type);
381
+
382
+ this.destroyer_.ensureNotDestroyed();
383
+
384
+ let sourceBuffer;
385
+
386
+ try {
387
+ sourceBuffer = this.mediaSource_.addSourceBuffer(type);
388
+ } catch (exception) {
389
+ throw new shaka.util.Error(
390
+ shaka.util.Error.Severity.CRITICAL,
391
+ shaka.util.Error.Category.MEDIA,
392
+ shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
393
+ exception,
394
+ 'The mediaSource_ status was' + this.mediaSource_.readyState +
395
+ 'expected \'open\'');
396
+ }
379
397
 
380
398
  this.eventManager_.listen(
381
399
  sourceBuffer, 'error',
@@ -628,7 +646,8 @@ shaka.media.MediaSourceEngine = class {
628
646
  // For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
629
647
  // the video stream, so textEngine may not have been initialized.
630
648
  if (!this.textEngine_) {
631
- this.reinitText('text/vtt', this.sequenceMode_);
649
+ this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
650
+ this.sequenceMode_);
632
651
  }
633
652
  // This doesn't work for native TS support (ex. Edge/Chromecast),
634
653
  // since no transmuxing is needed for native TS.
@@ -647,7 +666,8 @@ shaka.media.MediaSourceEngine = class {
647
666
  data = transmuxedData.data;
648
667
  } else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
649
668
  if (!this.textEngine_) {
650
- this.reinitText('text/vtt', this.sequenceMode_);
669
+ this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
670
+ this.sequenceMode_);
651
671
  }
652
672
  if (!this.captionParser_) {
653
673
  this.captionParser_ = this.getCaptionParser(mimeType);
@@ -937,7 +957,6 @@ shaka.media.MediaSourceEngine = class {
937
957
 
938
958
  /**
939
959
  * Adjust timestamp offset to maintain AV sync across discontinuities.
940
- * Only used in sequence mode.
941
960
  *
942
961
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
943
962
  * @param {number} timestampOffset
@@ -946,9 +965,6 @@ shaka.media.MediaSourceEngine = class {
946
965
  async resync(contentType, timestampOffset) {
947
966
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
948
967
 
949
- goog.asserts.assert(this.sequenceMode_,
950
- 'resync only used with sequence mode!');
951
-
952
968
  if (contentType == ContentType.TEXT) {
953
969
  // This operation is for audio and video only.
954
970
  return;
@@ -978,8 +994,10 @@ shaka.media.MediaSourceEngine = class {
978
994
  async endOfStream(reason) {
979
995
  await this.enqueueBlockingOperation_(() => {
980
996
  // If endOfStream() has already been called on the media source,
981
- // don't call it again.
982
- if (this.ended()) {
997
+ // don't call it again. Also do not call if readyState is
998
+ // 'closed' (not attached to video element) since it is not a
999
+ // valid operation.
1000
+ if (this.ended() || this.mediaSource_.readyState === 'closed') {
983
1001
  return;
984
1002
  }
985
1003
  // Tizen won't let us pass undefined, but it will let us omit the
@@ -236,7 +236,16 @@ shaka.media.PresentationTimeline = class {
236
236
 
237
237
  let firstReferenceStartTime = references[0].startTime;
238
238
  let lastReferenceEndTime = references[0].endTime;
239
+
240
+ // Date.now() is in milliseconds, from which we compute "now" in seconds.
241
+ const now = (Date.now() + this.clockOffset_) / 1000.0;
242
+
239
243
  for (const reference of references) {
244
+ // Exclude segments that are in the "future".
245
+ if (now < reference.startTime) {
246
+ continue;
247
+ }
248
+
240
249
  firstReferenceStartTime = Math.min(
241
250
  firstReferenceStartTime, reference.startTime);
242
251
  lastReferenceEndTime = Math.max(lastReferenceEndTime, reference.endTime);
@@ -251,8 +260,6 @@ shaka.media.PresentationTimeline = class {
251
260
  !this.startTimeLocked_) {
252
261
  // Since we have explicit segment end times, calculate a presentation
253
262
  // start based on them. This start time accounts for drift.
254
- // Date.now() is in milliseconds, from which we compute "now" in seconds.
255
- const now = (Date.now() + this.clockOffset_) / 1000.0;
256
263
  this.presentationStartTime_ =
257
264
  now - this.maxSegmentEndTime_ - this.maxSegmentDuration_;
258
265
  }
@@ -363,6 +363,8 @@ shaka.media.SegmentIndex = class {
363
363
  lastReference.status,
364
364
  lastReference.hlsAes128Key,
365
365
  );
366
+ this.references[this.references.length - 1].discontinuitySequence =
367
+ lastReference.discontinuitySequence;
366
368
  }
367
369
 
368
370
 
@@ -144,8 +144,9 @@ shaka.media.SegmentReference = class {
144
144
  * minus the first segment's tfdt box's 'baseMediaDecodeTime' field (after
145
145
  * it has been converted to seconds).
146
146
  * <br>
147
- * For HLS, this value should be 0 to keep the presentation time at the most
148
- * recent discontinuity minus the corresponding media time.
147
+ * For HLS, this value should be the start time of the most recent
148
+ * discontinuity, or 0 if there is no preceding discontinuity. Only used
149
+ * in segments mode.
149
150
  * @param {number} appendWindowStart
150
151
  * The start of the append window for this reference, relative to the
151
152
  * presentation. Any content from before this time will be removed by