shaka-player 4.8.8 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +5 -0
  2. package/dist/controls.css +1 -1
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +5 -5
  5. package/dist/locales.js +33 -33
  6. package/dist/shaka-player.compiled.d.ts +39 -17
  7. package/dist/shaka-player.compiled.debug.d.ts +39 -17
  8. package/dist/shaka-player.compiled.debug.externs.js +32 -11
  9. package/dist/shaka-player.compiled.debug.js +574 -516
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +32 -11
  12. package/dist/shaka-player.compiled.js +1270 -1236
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +39 -17
  15. package/dist/shaka-player.ui.debug.d.ts +39 -17
  16. package/dist/shaka-player.ui.debug.externs.js +43 -22
  17. package/dist/shaka-player.ui.debug.js +601 -543
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +43 -22
  20. package/dist/shaka-player.ui.js +1483 -1448
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/shaka/manifest.js +11 -1
  23. package/externs/shaka/offline.js +3 -0
  24. package/externs/shaka/player.js +105 -12
  25. package/externs/tizen.js +1 -1
  26. package/lib/abr/simple_abr_manager.js +5 -0
  27. package/lib/ads/media_tailor_ad.js +11 -0
  28. package/lib/ads/media_tailor_ad_manager.js +37 -3
  29. package/lib/cea/cea608_data_channel.js +9 -0
  30. package/lib/cea/cea_decoder.js +6 -2
  31. package/lib/dash/dash_parser.js +655 -46
  32. package/lib/dash/mpd_utils.js +7 -8
  33. package/lib/dash/segment_template.js +65 -10
  34. package/lib/hls/hls_parser.js +92 -34
  35. package/lib/media/drm_engine.js +274 -40
  36. package/lib/media/manifest_filterer.js +10 -14
  37. package/lib/media/preload_manager.js +3 -10
  38. package/lib/media/segment_utils.js +14 -3
  39. package/lib/mss/mss_parser.js +2 -0
  40. package/lib/net/http_plugin_utils.js +2 -1
  41. package/lib/net/networking_engine.js +27 -0
  42. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  43. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  44. package/lib/offline/manifest_converter.js +2 -0
  45. package/lib/offline/storage.js +1 -0
  46. package/lib/player.js +221 -14
  47. package/lib/polyfill/media_capabilities.js +10 -35
  48. package/lib/util/config_utils.js +74 -6
  49. package/lib/util/error.js +7 -0
  50. package/lib/util/mp4_box_parsers.js +23 -2
  51. package/lib/util/periods.js +51 -28
  52. package/lib/util/platform.js +3 -1
  53. package/lib/util/player_configuration.js +20 -8
  54. package/lib/util/stream_utils.js +62 -30
  55. package/lib/util/tXml.js +74 -6
  56. package/package.json +2 -2
  57. package/support.html +1 -0
  58. package/ui/controls.js +4 -0
  59. package/ui/less/ad_controls.less +2 -1
  60. package/ui/resolution_selection.js +12 -3
@@ -123,7 +123,7 @@ shaka.dash.MpdUtils = class {
123
123
  * Expands a SegmentTimeline into an array-based timeline. The results are in
124
124
  * seconds.
125
125
  *
126
- * @param {!shaka.extern.xml.Node} segmentTimeline
126
+ * @param {Array<!shaka.extern.xml.Node>} timePoints
127
127
  * @param {number} timescale
128
128
  * @param {number} unscaledPresentationTimeOffset
129
129
  * @param {number} periodDuration The Period's duration in seconds.
@@ -132,7 +132,7 @@ shaka.dash.MpdUtils = class {
132
132
  * @return {!Array.<shaka.media.PresentationTimeline.TimeRange>}
133
133
  */
134
134
  static createTimeline(
135
- segmentTimeline, timescale, unscaledPresentationTimeOffset,
135
+ timePoints, timescale, unscaledPresentationTimeOffset,
136
136
  periodDuration, startNumber) {
137
137
  goog.asserts.assert(
138
138
  timescale > 0 && timescale < Infinity,
@@ -143,8 +143,6 @@ shaka.dash.MpdUtils = class {
143
143
  // Alias.
144
144
  const TXml = shaka.util.TXml;
145
145
 
146
- const timePoints = TXml.findChildren(segmentTimeline, 'S');
147
-
148
146
  /** @type {!Array.<shaka.media.PresentationTimeline.TimeRange>} */
149
147
  const timeline = [];
150
148
  let lastEndTime = -unscaledPresentationTimeOffset;
@@ -296,13 +294,14 @@ shaka.dash.MpdUtils = class {
296
294
  startNumber = 1;
297
295
  }
298
296
 
299
- const timelineNode =
300
- MpdUtils.inheritChild(context, callback, 'SegmentTimeline');
301
297
  /** @type {Array.<shaka.media.PresentationTimeline.TimeRange>} */
302
298
  let timeline = null;
299
+ const timelineNode =
300
+ MpdUtils.inheritChild(context, callback, 'SegmentTimeline');
303
301
  if (timelineNode) {
302
+ const timePoints = TXml.findChildren(timelineNode, 'S');
304
303
  timeline = MpdUtils.createTimeline(
305
- timelineNode, timescale, unscaledPresentationTimeOffset,
304
+ timePoints, timescale, unscaledPresentationTimeOffset,
306
305
  context.periodInfo.duration || Infinity, startNumber);
307
306
  }
308
307
 
@@ -329,7 +328,7 @@ shaka.dash.MpdUtils = class {
329
328
  const Functional = shaka.util.Functional;
330
329
  goog.asserts.assert(
331
330
  callback(context.representation),
332
- 'There must be at least one element of the given type',
331
+ 'There must be at least one element of the given type.',
333
332
  );
334
333
 
335
334
  return [
@@ -18,6 +18,7 @@ goog.require('shaka.util.IReleasable');
18
18
  goog.require('shaka.util.ManifestParserUtils');
19
19
  goog.require('shaka.util.ObjectUtils');
20
20
  goog.require('shaka.util.StringUtils');
21
+ goog.require('shaka.util.TXml');
21
22
  goog.requireType('shaka.dash.DashParser');
22
23
  goog.requireType('shaka.media.PresentationTimeline');
23
24
 
@@ -39,18 +40,27 @@ shaka.dash.SegmentTemplate = class {
39
40
  * @param {!Object.<string, number>} periodDurationMap
40
41
  * @param {shaka.extern.aesKey|undefined} aesKey
41
42
  * @param {?number} lastSegmentNumber
43
+ * @param {boolean} isPatchUpdate
42
44
  * @return {shaka.dash.DashParser.StreamInfo}
43
45
  */
44
46
  static createStreamInfo(
45
47
  context, requestSegment, streamMap, isUpdate, segmentLimit,
46
- periodDurationMap, aesKey, lastSegmentNumber) {
48
+ periodDurationMap, aesKey, lastSegmentNumber, isPatchUpdate) {
47
49
  goog.asserts.assert(context.representation.segmentTemplate,
48
- 'Should only be called with SegmentTemplate');
50
+ 'Should only be called with SegmentTemplate ' +
51
+ 'or segment info defined');
52
+ const MpdUtils = shaka.dash.MpdUtils;
49
53
  const SegmentTemplate = shaka.dash.SegmentTemplate;
50
54
  const TimelineSegmentIndex = shaka.dash.TimelineSegmentIndex;
51
55
 
52
- const initSegmentReference =
53
- SegmentTemplate.createInitSegment_(context, aesKey);
56
+ if (!isPatchUpdate && !context.representation.initialization) {
57
+ context.representation.initialization =
58
+ MpdUtils.inheritAttribute(
59
+ context, SegmentTemplate.fromInheritance_, 'initialization');
60
+ }
61
+
62
+ const initSegmentReference = context.representation.initialization ?
63
+ SegmentTemplate.createInitSegment_(context, aesKey) : null;
54
64
 
55
65
  /** @type {shaka.dash.SegmentTemplate.SegmentTemplateInfo} */
56
66
  const info = SegmentTemplate.parseSegmentTemplateInfo_(context);
@@ -178,6 +188,42 @@ shaka.dash.SegmentTemplate = class {
178
188
  }
179
189
  }
180
190
 
191
+ /**
192
+ * Ingests Patch MPD segments into timeline.
193
+ *
194
+ * @param {!shaka.dash.DashParser.Context} context
195
+ * @param {shaka.extern.xml.Node} patchNode
196
+ */
197
+ static modifyTimepoints(context, patchNode) {
198
+ const MpdUtils = shaka.dash.MpdUtils;
199
+ const SegmentTemplate = shaka.dash.SegmentTemplate;
200
+ const TXml = shaka.util.TXml;
201
+
202
+ const timelineNode = MpdUtils.inheritChild(context,
203
+ SegmentTemplate.fromInheritance_, 'SegmentTimeline');
204
+ goog.asserts.assert(timelineNode, 'timeline node not found');
205
+ const timepoints = TXml.findChildren(timelineNode, 'S');
206
+
207
+ goog.asserts.assert(timepoints, 'timepoints should exist');
208
+ TXml.modifyNodes(timepoints, patchNode);
209
+ timelineNode.children = timepoints;
210
+ }
211
+
212
+ /**
213
+ * Removes all segments from timeline.
214
+ *
215
+ * @param {!shaka.dash.DashParser.Context} context
216
+ */
217
+ static removeTimepoints(context) {
218
+ const MpdUtils = shaka.dash.MpdUtils;
219
+ const SegmentTemplate = shaka.dash.SegmentTemplate;
220
+
221
+ const timelineNode = MpdUtils.inheritChild(context,
222
+ SegmentTemplate.fromInheritance_, 'SegmentTimeline');
223
+ goog.asserts.assert(timelineNode, 'timeline node not found');
224
+ timelineNode.children = [];
225
+ }
226
+
181
227
  /**
182
228
  * @param {?shaka.dash.DashParser.InheritanceFrame} frame
183
229
  * @return {?shaka.extern.xml.Node}
@@ -203,6 +249,7 @@ shaka.dash.SegmentTemplate = class {
203
249
 
204
250
  const media = MpdUtils.inheritAttribute(
205
251
  context, SegmentTemplate.fromInheritance_, 'media');
252
+
206
253
  const index = MpdUtils.inheritAttribute(
207
254
  context, SegmentTemplate.fromInheritance_, 'index');
208
255
 
@@ -552,8 +599,11 @@ shaka.dash.SegmentTemplate = class {
552
599
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
553
600
  const SegmentTemplate = shaka.dash.SegmentTemplate;
554
601
 
555
- let initialization = MpdUtils.inheritAttribute(
556
- context, SegmentTemplate.fromInheritance_, 'initialization');
602
+ let initialization = context.representation.initialization;
603
+ if (!initialization) {
604
+ initialization = MpdUtils.inheritAttribute(
605
+ context, SegmentTemplate.fromInheritance_, 'initialization');
606
+ }
557
607
  if (!initialization) {
558
608
  return null;
559
609
  }
@@ -722,10 +772,15 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
722
772
  this.templateInfo_.mediaTemplate = info.mediaTemplate;
723
773
 
724
774
  // Append timeline
725
- const lastCurrentEntry = currentTimeline[currentTimeline.length - 1];
726
- const newEntries = info.timeline.filter((entry) => {
727
- return entry.start >= lastCurrentEntry.end;
728
- });
775
+ let newEntries;
776
+ if (currentTimeline.length) {
777
+ const lastCurrentEntry = currentTimeline[currentTimeline.length - 1];
778
+ newEntries = info.timeline.filter((entry) => {
779
+ return entry.start >= lastCurrentEntry.end;
780
+ });
781
+ } else {
782
+ newEntries = info.timeline.slice();
783
+ }
729
784
 
730
785
  if (newEntries.length > 0) {
731
786
  shaka.log.debug(`Appending ${newEntries.length} entries`);
@@ -244,6 +244,9 @@ shaka.hls.HlsParser = class {
244
244
 
245
245
  /** @private {?shaka.util.ContentSteeringManager} */
246
246
  this.contentSteeringManager_ = null;
247
+
248
+ /** @private {boolean} */
249
+ this.needsClosedCaptionsDetection_ = true;
247
250
  }
248
251
 
249
252
 
@@ -759,10 +762,19 @@ shaka.hls.HlsParser = class {
759
762
 
760
763
  // Parsing a media playlist results in a single-variant stream.
761
764
  if (playlist.type == shaka.hls.PlaylistType.MEDIA) {
765
+ this.needsClosedCaptionsDetection_ = false;
766
+
767
+ /** @type {!Array.<!shaka.hls.Tag>} */
768
+ const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
769
+ 'EXT-X-DEFINE');
770
+
771
+ const mediaVariables =
772
+ this.parseMediaVariables_(variablesTags, this.masterPlaylistUri_);
773
+
762
774
  // Get necessary info for this stream. These are things we would normally
763
775
  // find from the master playlist (e.g. from values on EXT-X-MEDIA tags).
764
- const basicInfo =
765
- await this.getMediaPlaylistBasicInfo_(playlist, getUris);
776
+ const basicInfo = await this.getMediaPlaylistBasicInfo_(
777
+ playlist, getUris, mediaVariables);
766
778
  const type = basicInfo.type;
767
779
  const mimeType = basicInfo.mimeType;
768
780
  const codecs = basicInfo.codecs;
@@ -773,6 +785,7 @@ shaka.hls.HlsParser = class {
773
785
  const sampleRate = basicInfo.sampleRate;
774
786
  const closedCaptions = basicInfo.closedCaptions;
775
787
  const videoRange = basicInfo.videoRange;
788
+ const colorGamut = basicInfo.colorGamut;
776
789
 
777
790
  // Some values we cannot figure out, and aren't important enough to ask
778
791
  // the user to provide through config values. A lot of these are only
@@ -786,14 +799,15 @@ shaka.hls.HlsParser = class {
786
799
 
787
800
  // Make the stream info, with those values.
788
801
  const streamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
789
- this.globalId_++, playlist, getUris, uri, codecs, type,
790
- languageValue, primary, name, channelsCount, closedCaptions,
802
+ this.globalId_++, mediaVariables, playlist, getUris, uri, codecs,
803
+ type, languageValue, primary, name, channelsCount, closedCaptions,
791
804
  characteristics, forced, sampleRate, spatialAudio, mimeType);
792
805
  this.uriToStreamInfosMap_.set(uri, streamInfo);
793
806
 
794
807
  if (type == 'video') {
795
808
  this.addVideoAttributes_(streamInfo.stream, width, height,
796
- /* frameRate= */ null, videoRange, /* videoLayout= */ null);
809
+ /* frameRate= */ null, videoRange, /* videoLayout= */ null,
810
+ colorGamut);
797
811
  }
798
812
 
799
813
  // Wrap the stream from that stream info with a variant.
@@ -880,6 +894,7 @@ shaka.hls.HlsParser = class {
880
894
  this.config_.hls.ignoreManifestTimestampsInSegmentsMode,
881
895
  type: shaka.media.ManifestParser.HLS,
882
896
  serviceDescription: null,
897
+ nextUrl: null,
883
898
  };
884
899
 
885
900
  // If there is no 'CODECS' attribute in the manifest and codec guessing is
@@ -918,10 +933,11 @@ shaka.hls.HlsParser = class {
918
933
  /**
919
934
  * @param {shaka.hls.Playlist} playlist
920
935
  * @param {function():!Array.<string>} getUris
936
+ * @param {?Map.<string, string>=} variables
921
937
  * @return {!Promise.<shaka.media.SegmentUtils.BasicInfo>}
922
938
  * @private
923
939
  */
924
- async getMediaPlaylistBasicInfo_(playlist, getUris) {
940
+ async getMediaPlaylistBasicInfo_(playlist, getUris, variables) {
925
941
  const HlsParser = shaka.hls.HlsParser;
926
942
  const defaultBasicInfo = shaka.media.SegmentUtils.getBasicInfoFromMimeType(
927
943
  this.config_.hls.mediaPlaylistFullMimeType);
@@ -931,7 +947,8 @@ shaka.hls.HlsParser = class {
931
947
  const firstSegment = playlist.segments[0];
932
948
  const firstSegmentUris = shaka.hls.Utils.constructSegmentUris(
933
949
  getUris(),
934
- firstSegment.verbatimSegmentUri);
950
+ firstSegment.verbatimSegmentUri,
951
+ variables);
935
952
  const firstSegmentUri = firstSegmentUris[0];
936
953
  const parsedUri = new goog.Uri(firstSegmentUri);
937
954
  const extension = parsedUri.getPath().split('.').pop();
@@ -945,7 +962,7 @@ shaka.hls.HlsParser = class {
945
962
 
946
963
  let initData = null;
947
964
  const initSegmentRef = this.getInitSegmentReference_(
948
- playlist, firstSegment.tags, getUris);
965
+ playlist, firstSegment.tags, getUris, variables);
949
966
  this.mapTagToInitSegmentRefMap_.clear();
950
967
  if (initSegmentRef) {
951
968
  const initSegmentRequest = shaka.util.Networking.createSegmentRequest(
@@ -1388,7 +1405,19 @@ shaka.hls.HlsParser = class {
1388
1405
  const keyFormat =
1389
1406
  drmTag.getAttributeValue('KEYFORMAT') || 'identity';
1390
1407
  let drmInfo = null;
1391
- if (method == 'NONE' || this.isAesMethod_(method)) {
1408
+ if (method == 'NONE') {
1409
+ continue;
1410
+ } else if (this.isAesMethod_(method)) {
1411
+ const keyUris = shaka.hls.Utils.constructSegmentUris(
1412
+ getUris(), drmTag.getRequiredAttrValue('URI'), variables);
1413
+ const keyMapKey = keyUris.sort().join('');
1414
+ if (!this.aesKeyMap_.has(keyMapKey)) {
1415
+ const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
1416
+ const request = shaka.net.NetworkingEngine.makeRequest(
1417
+ keyUris, this.config_.retryParameters);
1418
+ const keyResponse = this.makeNetworkRequest_(request, requestType);
1419
+ this.aesKeyMap_.set(keyMapKey, keyResponse);
1420
+ }
1392
1421
  continue;
1393
1422
  } else if (keyFormat == 'identity') {
1394
1423
  // eslint-disable-next-line no-await-in-loop
@@ -1755,10 +1784,12 @@ shaka.hls.HlsParser = class {
1755
1784
  // value of the GROUP-ID attribute of an EXT-X-MEDIA tag elsewhere in the
1756
1785
  // Playlist whose TYPE attribute is CLOSED-CAPTIONS.
1757
1786
  if (type == ContentType.VIDEO) {
1758
- if (closedCaptionsAttr && closedCaptionsAttr != 'NONE') {
1759
- return this.groupIdToClosedCaptionsMap_.get(closedCaptionsAttr);
1760
- } else if (!closedCaptionsAttr &&
1761
- this.groupIdToClosedCaptionsMap_.size) {
1787
+ if (closedCaptionsAttr) {
1788
+ if (closedCaptionsAttr != 'NONE') {
1789
+ return this.groupIdToClosedCaptionsMap_.get(closedCaptionsAttr);
1790
+ }
1791
+ this.needsClosedCaptionsDetection_ = false;
1792
+ } else if (!closedCaptionsAttr && this.groupIdToClosedCaptionsMap_.size) {
1762
1793
  for (const key of this.groupIdToClosedCaptionsMap_.keys()) {
1763
1794
  return this.groupIdToClosedCaptionsMap_.get(key);
1764
1795
  }
@@ -1817,7 +1848,8 @@ shaka.hls.HlsParser = class {
1817
1848
 
1818
1849
  for (const info of videoInfos) {
1819
1850
  this.addVideoAttributes_(
1820
- info.stream, width, height, frameRate, videoRange, videoLayout);
1851
+ info.stream, width, height, frameRate, videoRange, videoLayout,
1852
+ /** colorGamut= */ null);
1821
1853
  }
1822
1854
 
1823
1855
  // In case of audio-only or video-only content or the audio/video is
@@ -1907,6 +1939,7 @@ shaka.hls.HlsParser = class {
1907
1939
  parseClosedCaptions_(mediaTags) {
1908
1940
  const closedCaptionsTags =
1909
1941
  shaka.hls.Utils.filterTagsByType(mediaTags, 'CLOSED-CAPTIONS');
1942
+ this.needsClosedCaptionsDetection_ = closedCaptionsTags.length == 0;
1910
1943
  for (const tag of closedCaptionsTags) {
1911
1944
  goog.asserts.assert(tag.name == 'EXT-X-MEDIA',
1912
1945
  'Should only be called on media tags!');
@@ -2248,6 +2281,8 @@ shaka.hls.HlsParser = class {
2248
2281
 
2249
2282
  /** @param {!AbortSignal} abortSignal */
2250
2283
  const downloadSegmentIndex = async (abortSignal) => {
2284
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
2285
+
2251
2286
  const uris = streamInfo.getUris();
2252
2287
  // Download the actual manifest.
2253
2288
  const response = await this.requestManifest_(
@@ -2267,24 +2302,47 @@ shaka.hls.HlsParser = class {
2267
2302
  /** @type {!shaka.hls.Playlist} */
2268
2303
  const playlist = this.manifestTextParser_.parsePlaylist(response.data);
2269
2304
 
2305
+ /** @type {!Array.<!shaka.hls.Tag>} */
2306
+ const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
2307
+ 'EXT-X-DEFINE');
2308
+
2309
+ const mediaVariables =
2310
+ this.parseMediaVariables_(variablesTags, responseUri);
2311
+
2270
2312
  let mimeType = undefined;
2271
2313
 
2314
+ let closedCaptionsUpdated = false;
2315
+
2272
2316
  // If no codec info was provided in the manifest and codec guessing is
2273
2317
  // disabled we try to get necessary info from the media data.
2274
- if (!this.codecInfoInManifest_ && this.config_.hls.disableCodecGuessing) {
2275
- const basicInfo =
2276
- await this.getMediaPlaylistBasicInfo_(playlist, getUris);
2318
+ if ((!this.codecInfoInManifest_ &&
2319
+ this.config_.hls.disableCodecGuessing) ||
2320
+ (this.needsClosedCaptionsDetection_ && type == ContentType.VIDEO &&
2321
+ !this.config_.hls.disableClosedCaptionsDetection)) {
2322
+ this.needsClosedCaptionsDetection_ = false;
2323
+
2324
+ const basicInfo = await this.getMediaPlaylistBasicInfo_(
2325
+ playlist, getUris, mediaVariables);
2277
2326
 
2278
2327
  goog.asserts.assert(
2279
2328
  type === basicInfo.type, 'Media types should match!');
2280
2329
 
2281
- mimeType = basicInfo.mimeType;
2282
- codecs = basicInfo.codecs;
2330
+ if (basicInfo.closedCaptions.size && (!closedCaptions ||
2331
+ closedCaptions.size != basicInfo.closedCaptions.size)) {
2332
+ closedCaptions = basicInfo.closedCaptions;
2333
+ closedCaptionsUpdated = true;
2334
+ }
2335
+
2336
+ if (!this.codecInfoInManifest_ &&
2337
+ this.config_.hls.disableCodecGuessing) {
2338
+ mimeType = basicInfo.mimeType;
2339
+ codecs = basicInfo.codecs;
2340
+ }
2283
2341
  }
2284
2342
 
2285
2343
  const wasLive = this.isLive_();
2286
2344
  const realStreamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
2287
- streamId, playlist, getUris, responseUri, codecs,
2345
+ streamId, mediaVariables, playlist, getUris, responseUri, codecs,
2288
2346
  type, languageValue, primary, name, channelsCount, closedCaptions,
2289
2347
  characteristics, forced, sampleRate, spatialAudio, mimeType);
2290
2348
  if (abortSignal.aborted) {
@@ -2317,6 +2375,8 @@ shaka.hls.HlsParser = class {
2317
2375
  stream.mimeType = realStream.mimeType;
2318
2376
  stream.bandwidth = stream.bandwidth || realStream.bandwidth;
2319
2377
  stream.codecs = stream.codecs || realStream.codecs;
2378
+ stream.closedCaptions =
2379
+ stream.closedCaptions || realStream.closedCaptions;
2320
2380
  this.setFullTypeForStream_(stream);
2321
2381
 
2322
2382
  // Since we lazy-loaded this content, the player may need to create new
@@ -2325,7 +2385,10 @@ shaka.hls.HlsParser = class {
2325
2385
  this.playerInterface_.newDrmInfo(stream);
2326
2386
  }
2327
2387
 
2328
- const ContentType = shaka.util.ManifestParserUtils.ContentType;
2388
+ if (this.manifest_ && closedCaptionsUpdated) {
2389
+ this.playerInterface_.makeTextStreamsForClosedCaptions(this.manifest_);
2390
+ }
2391
+
2329
2392
  if (type == ContentType.VIDEO || type == ContentType.AUDIO) {
2330
2393
  for (const otherStreamInfo of this.uriToStreamInfosMap_.values()) {
2331
2394
  if (!otherStreamInfo.loadedOnce && otherStreamInfo.type == type) {
@@ -2556,7 +2619,7 @@ shaka.hls.HlsParser = class {
2556
2619
  * @return {!Promise.<!shaka.hls.HlsParser.StreamInfo>}
2557
2620
  * @private
2558
2621
  */
2559
- async convertParsedPlaylistIntoStreamInfo_(streamId, playlist,
2622
+ async convertParsedPlaylistIntoStreamInfo_(streamId, variables, playlist,
2560
2623
  getUris, responseUri, codecs, type, languageValue, primary, name,
2561
2624
  channelsCount, closedCaptions, characteristics, forced, sampleRate,
2562
2625
  spatialAudio, mimeType = undefined) {
@@ -2569,13 +2632,6 @@ shaka.hls.HlsParser = class {
2569
2632
  shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
2570
2633
  }
2571
2634
 
2572
- /** @type {!Array.<!shaka.hls.Tag>} */
2573
- const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
2574
- 'EXT-X-DEFINE');
2575
-
2576
- const mediaVariables =
2577
- this.parseMediaVariables_(variablesTags, responseUri);
2578
-
2579
2635
  goog.asserts.assert(playlist.segments != null,
2580
2636
  'Media playlist should have segments!');
2581
2637
 
@@ -2587,11 +2643,11 @@ shaka.hls.HlsParser = class {
2587
2643
 
2588
2644
  if (!mimeType) {
2589
2645
  mimeType = await this.guessMimeType_(type, codecs, playlist,
2590
- mediaVariables, getUris);
2646
+ variables, getUris);
2591
2647
  }
2592
2648
 
2593
2649
  const {drmInfos, keyIds, encrypted, aesEncrypted} =
2594
- await this.parseDrmInfo_(playlist, mimeType, getUris, mediaVariables);
2650
+ await this.parseDrmInfo_(playlist, mimeType, getUris, variables);
2595
2651
 
2596
2652
  if (encrypted && !drmInfos.length && !aesEncrypted) {
2597
2653
  throw new shaka.util.Error(
@@ -2613,8 +2669,7 @@ shaka.hls.HlsParser = class {
2613
2669
  this.mediaSequenceToStartTimeByType_.get(type) : new Map();
2614
2670
 
2615
2671
  const {segments, bandwidth} = this.createSegments_(
2616
- playlist, stream, mediaSequenceToStartTime, mediaVariables, getUris,
2617
- type);
2672
+ playlist, stream, mediaSequenceToStartTime, variables, getUris, type);
2618
2673
  if (bandwidth) {
2619
2674
  stream.bandwidth = bandwidth;
2620
2675
  }
@@ -2797,6 +2852,7 @@ shaka.hls.HlsParser = class {
2797
2852
  spatialAudio,
2798
2853
  closedCaptions,
2799
2854
  hdr: undefined,
2855
+ colorGamut: undefined,
2800
2856
  videoLayout: undefined,
2801
2857
  tilesLayout: undefined,
2802
2858
  accessibilityPurpose: accessibilityPurpose,
@@ -3973,16 +4029,18 @@ shaka.hls.HlsParser = class {
3973
4029
  * @param {?string} frameRate
3974
4030
  * @param {?string} videoRange
3975
4031
  * @param {?string} videoLayout
4032
+ * @param {?string} colorGamut
3976
4033
  * @private
3977
4034
  */
3978
4035
  addVideoAttributes_(stream, width, height, frameRate, videoRange,
3979
- videoLayout) {
4036
+ videoLayout, colorGamut) {
3980
4037
  if (stream) {
3981
4038
  stream.width = Number(width) || undefined;
3982
4039
  stream.height = Number(height) || undefined;
3983
4040
  stream.frameRate = Number(frameRate) || undefined;
3984
4041
  stream.hdr = videoRange || undefined;
3985
4042
  stream.videoLayout = videoLayout || undefined;
4043
+ stream.colorGamut = colorGamut || undefined;
3986
4044
  }
3987
4045
  }
3988
4046