shaka-player 3.3.0 → 3.3.1

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 (53) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/build/conformance.textproto +183 -169
  3. package/dist/controls.css +1 -1
  4. package/dist/deps.js +1 -1
  5. package/dist/locales.js +30 -30
  6. package/dist/shaka-player.compiled.d.ts +25 -2
  7. package/dist/shaka-player.compiled.debug.d.ts +25 -2
  8. package/dist/shaka-player.compiled.debug.externs.js +22 -1
  9. package/dist/shaka-player.compiled.debug.js +226 -222
  10. package/dist/shaka-player.compiled.debug.map +3 -3
  11. package/dist/shaka-player.compiled.externs.js +22 -1
  12. package/dist/shaka-player.compiled.js +814 -812
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +25 -2
  15. package/dist/shaka-player.ui.debug.d.ts +25 -2
  16. package/dist/shaka-player.ui.debug.externs.js +22 -1
  17. package/dist/shaka-player.ui.debug.js +244 -240
  18. package/dist/shaka-player.ui.debug.map +3 -3
  19. package/dist/shaka-player.ui.externs.js +22 -1
  20. package/dist/shaka-player.ui.js +995 -992
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/cmcd.js +186 -0
  23. package/externs/shaka/player.js +8 -0
  24. package/externs/shaka/text.js +10 -0
  25. package/lib/hls/hls_parser.js +30 -0
  26. package/lib/media/segment_index.js +2 -1
  27. package/lib/media/segment_reference.js +19 -1
  28. package/lib/media/streaming_engine.js +5 -2
  29. package/lib/player.js +23 -17
  30. package/lib/polyfill/media_capabilities.js +30 -15
  31. package/lib/text/cue.js +6 -0
  32. package/lib/text/ttml_text_parser.js +84 -58
  33. package/lib/text/ui_text_displayer.js +17 -11
  34. package/lib/util/cmcd_manager.js +47 -201
  35. package/lib/util/stream_utils.js +86 -10
  36. package/lib/util/xml_utils.js +19 -0
  37. package/package.json +2 -2
  38. package/test/media/drm_engine_unit.js +3 -3
  39. package/test/media/streaming_engine_integration.js +2 -1
  40. package/test/media/streaming_engine_unit.js +145 -0
  41. package/test/player_integration.js +41 -8
  42. package/test/player_src_equals_integration.js +41 -8
  43. package/test/player_unit.js +19 -9
  44. package/test/test/assets/chapters.vtt +2 -2
  45. package/test/test/assets/chapters2.vtt +10 -0
  46. package/test/test/util/ttml_utils.js +81 -0
  47. package/test/text/mp4_ttml_parser_unit.js +11 -18
  48. package/test/text/ttml_text_parser_unit.js +499 -230
  49. package/test/util/cmcd_manager_unit.js +29 -15
  50. package/test/util/stream_utils_unit.js +36 -30
  51. package/third_party/closure-uri/uri.js +21 -0
  52. package/ui/text_selection.js +1 -0
  53. package/externs/media_capabilities.js +0 -16
@@ -0,0 +1,186 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Externs for CMCD data.
9
+ * @see https://github.com/google/shaka-player/issues/3619
10
+ * @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf
11
+ *
12
+ * @externs
13
+ */
14
+
15
+ /**
16
+ * @typedef {{
17
+ * br: (number|undefined),
18
+ * d: (number|undefined),
19
+ * ot: (string|undefined),
20
+ * tb: (number|undefined),
21
+ * bl: (number|undefined),
22
+ * dl: (number|undefined),
23
+ * mtp: (number|undefined),
24
+ * nor: (string|undefined),
25
+ * nrr: (string|undefined),
26
+ * su: (boolean|undefined),
27
+ * cid: (string|undefined),
28
+ * pr: (number|undefined),
29
+ * sf: (string|undefined),
30
+ * sid: (string|undefined),
31
+ * st: (string|undefined),
32
+ * v: (number|undefined),
33
+ * bs: (boolean|undefined),
34
+ * rtp: (number|undefined)
35
+ * }}
36
+ *
37
+ * @description
38
+ * Client Media Common Data (CMCD) data.
39
+ *
40
+ * @property {number} br
41
+ * The encoded bitrate of the audio or video object being requested. This may
42
+ * not be known precisely by the player; however, it MAY be estimated based
43
+ * upon playlist/manifest declarations. If the playlist declares both peak and
44
+ * average bitrate values, the peak value should be transmitted.
45
+ *
46
+ * @property {number} d
47
+ * The playback duration in milliseconds of the object being requested. If a
48
+ * partial segment is being requested, then this value MUST indicate the
49
+ * playback duration of that part and not that of its parent segment. This
50
+ * value can be an approximation of the estimated duration if the explicit
51
+ * value is not known.
52
+ *
53
+ * @property {string} ot
54
+ * The media type of the current object being requested:
55
+ * - `m` = text file, such as a manifest or playlist
56
+ * - `a` = audio only
57
+ * - `v` = video only
58
+ * - `av` = muxed audio and video
59
+ * - `i` = init segment
60
+ * - `c` = caption or subtitle
61
+ * - `tt` = ISOBMFF timed text track
62
+ * - `k` = cryptographic key, license or certificate.
63
+ * - `o` = other
64
+ *
65
+ * If the object type being requested is unknown, then this key MUST NOT be
66
+ * used.
67
+ *
68
+ * @property {number} tb
69
+ * The highest bitrate rendition in the manifest or playlist that the client
70
+ * is allowed to play, given current codec, licensing and sizing constraints.
71
+ *
72
+ * @property {number} bl
73
+ * The buffer length associated with the media object being requested. This
74
+ * value MUST be rounded to the nearest 100 ms. This key SHOULD only be sent
75
+ * with an object type of ‘a’, ‘v’ or ‘av’.
76
+ *
77
+ * @property {number} dl
78
+ * Deadline from the request time until the first sample of this
79
+ * Segment/Object needs to be available in order to not create a buffer
80
+ * underrun or any other playback problems. This value MUST be rounded to the
81
+ * nearest 100ms. For a playback rate of 1, this may be equivalent to the
82
+ * player’s remaining buffer length.
83
+ *
84
+ * @property {number} mtp
85
+ * The throughput between client and server, as measured by the client and
86
+ * MUST be rounded to the nearest 100 kbps. This value, however derived,
87
+ * SHOULD be the value that the client is using to make its next Adaptive
88
+ * Bitrate switching decision. If the client is connected to multiple
89
+ * servers concurrently, it must take care to report only the throughput
90
+ * measured against the receiving server. If the client has multiple
91
+ * concurrent connections to the server, then the intent is that this value
92
+ * communicates the aggregate throughput the client sees across all those
93
+ * connections.
94
+ *
95
+ * @property {string} nor
96
+ * Relative path of the next object to be requested. This can be used to
97
+ * trigger pre-fetching by the CDN. This MUST be a path relative to the
98
+ * current request. This string MUST be URLEncoded. The client SHOULD NOT
99
+ * depend upon any pre-fetch action being taken - it is merely a request for
100
+ * such a pre-fetch to take place.
101
+ *
102
+ * @property {string} nrr
103
+ * If the next request will be a partial object request, then this string
104
+ * denotes the byte range to be requested. If the ‘nor’ field is not set, then
105
+ * the object is assumed to match the object currently being requested. The
106
+ * client SHOULD NOT depend upon any pre-fetch action being taken – it is
107
+ * merely a request for such a pre-fetch to take place. Formatting is similar
108
+ * to the HTTP Range header, except that the unit MUST be ‘byte’, the ‘Range:’
109
+ * prefix is NOT required and specifying multiple ranges is NOT allowed. Valid
110
+ * combinations are:
111
+ *
112
+ * - `"\<range-start\>-"`
113
+ * - `"\<range-start\>-\<range-end\>"`
114
+ * - `"-\<suffix-length\>"`
115
+ *
116
+ * @property {boolean} su
117
+ * Key is included without a value if the object is needed urgently due to
118
+ * startup, seeking or recovery after a buffer-empty event. The media SHOULD
119
+ * not be rendering when this request is made. This key MUST not be sent if it
120
+ * is FALSE.
121
+ *
122
+ * @property {string} cid
123
+ * A unique string identifying the current content. Maximum length is 64
124
+ * characters. This value is consistent across multiple different sessions and
125
+ * devices and is defined and updated at the discretion of the service
126
+ * provider.
127
+ *
128
+ * @property {number} pr
129
+ * The playback rate. `1` if real-time, `2` if double speed, `0` if not
130
+ * playing. SHOULD only be sent if not equal to `1`.
131
+ *
132
+ * @property {string} sf
133
+ * The streaming format that defines the current request.
134
+ *
135
+ * - `d` = MPEG DASH
136
+ * - `h` = HTTP Live Streaming (HLS)
137
+ * - `s` = Smooth Streaming
138
+ * - `o` = other
139
+ *
140
+ * If the streaming format being requested is unknown, then this key MUST NOT
141
+ * be used.
142
+ *
143
+ * @property {string} sid
144
+ * A GUID identifying the current playback session. A playback session
145
+ * typically ties together segments belonging to a single media asset. Maximum
146
+ * length is 64 characters. It is RECOMMENDED to conform to the UUID
147
+ * specification.
148
+ *
149
+ * @property {string} st
150
+ * Stream type
151
+ * - `v` = all segments are available – e.g., VOD
152
+ * - `l` = segments become available over time – e.g., LIVE
153
+ *
154
+ * @property {number} v
155
+ * The version of this specification used for interpreting the defined key
156
+ * names and values. If this key is omitted, the client and server MUST
157
+ * interpret the values as being defined by version 1. Client SHOULD omit this
158
+ * field if the version is 1.
159
+ *
160
+ * @property {boolean} bs
161
+ * Buffer starvation key is included without a value if the buffer was starved
162
+ * at some point between the prior request and this object request, resulting
163
+ * in the player being in a rebuffering state and the video or audio playback
164
+ * being stalled. This key MUST NOT be sent if the buffer was not starved
165
+ * since the prior request.
166
+ *
167
+ * If the object type `ot` key is sent along with this key, then the `bs` key
168
+ * refers to the buffer associated with the particular object type. If no
169
+ * object type is communicated, then the buffer state applies to the current
170
+ * session.
171
+ *
172
+ * @property {number} rtp
173
+ * Requested maximum throughput
174
+ *
175
+ * The requested maximum throughput that the client considers sufficient for
176
+ * delivery of the asset. Values MUST be rounded to the nearest 100kbps. For
177
+ * example, a client would indicate that the current segment, encoded at
178
+ * 2Mbps, is to be delivered at no more than 10Mbps, by using rtp=10000.
179
+ *
180
+ * Note: This can benefit clients by preventing buffer saturation through
181
+ * over-delivery and can also deliver a community benefit through fair-share
182
+ * delivery. The concept is that each client receives the throughput necessary
183
+ * for great performance, but no more. The CDN may not support the rtp
184
+ * feature.
185
+ */
186
+ var CmcdData;
@@ -1207,6 +1207,8 @@ shaka.extern.LanguageRole;
1207
1207
 
1208
1208
  /**
1209
1209
  * @typedef {{
1210
+ * imageHeight: number,
1211
+ * imageWidth: number,
1210
1212
  * height: number,
1211
1213
  * positionX: number,
1212
1214
  * positionY: number,
@@ -1216,6 +1218,12 @@ shaka.extern.LanguageRole;
1216
1218
  * width: number
1217
1219
  * }}
1218
1220
  *
1221
+ * @property {number} imageHeight
1222
+ * The image height in px. The image height could be different to height if
1223
+ * the layout is different to 1x1.
1224
+ * @property {number} imageWidth
1225
+ * The image width in px. The image width could be different to width if
1226
+ * the layout is different to 1x1.
1219
1227
  * @property {number} height
1220
1228
  * The thumbnail height in px.
1221
1229
  * @property {number} positionX
@@ -363,6 +363,16 @@ shaka.extern.Cue = class {
363
363
  */
364
364
  this.nestedCues;
365
365
 
366
+ /**
367
+ * If true, this represents a container element that is "above" the main
368
+ * cues. For example, the <body> and <div> tags that contain the <p> tags
369
+ * in a TTML file. This controls the flow of the final cues; any nested cues
370
+ * within an "isContainer" cue will be laid out as separate lines.
371
+ * @type {boolean}
372
+ * @exportDoc
373
+ */
374
+ this.isContainer;
375
+
366
376
  /**
367
377
  * Whether or not the cue only acts as a line break between two nested cues.
368
378
  * Should only appear in nested cues.
@@ -1281,6 +1281,30 @@ shaka.hls.HlsParser = class {
1281
1281
  if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
1282
1282
  return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
1283
1283
  }
1284
+
1285
+ // Parse misc attributes.
1286
+ const resolution = tag.getAttributeValue('RESOLUTION');
1287
+ if (resolution) {
1288
+ // The RESOLUTION tag represents the resolution of a single thumbnail, not
1289
+ // of the entire sheet at once (like we expect in the output).
1290
+ // So multiply by the layout size.
1291
+
1292
+ const reference = streamInfo.stream.segmentIndex.get(0);
1293
+ const layout = reference.getTilesLayout();
1294
+ if (layout) {
1295
+ streamInfo.stream.width =
1296
+ Number(resolution.split('x')[0]) * Number(layout.split('x')[0]);
1297
+ streamInfo.stream.height =
1298
+ Number(resolution.split('x')[1]) * Number(layout.split('x')[1]);
1299
+ // TODO: What happens if there are multiple grids, with different
1300
+ // layout sizes, inside this image stream?
1301
+ }
1302
+ }
1303
+ const bandwidth = tag.getAttributeValue('BANDWIDTH');
1304
+ if (bandwidth) {
1305
+ streamInfo.stream.bandwidth = Number(bandwidth);
1306
+ }
1307
+
1284
1308
  this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
1285
1309
  return streamInfo;
1286
1310
  }
@@ -1814,6 +1838,7 @@ shaka.hls.HlsParser = class {
1814
1838
  }
1815
1839
 
1816
1840
  let tilesLayout = '';
1841
+ let tileDuration = null;
1817
1842
  if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
1818
1843
  // By default in HLS the tilesLayout is 1x1
1819
1844
  tilesLayout = '1x1';
@@ -1821,6 +1846,10 @@ shaka.hls.HlsParser = class {
1821
1846
  shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
1822
1847
  if (tilesTag) {
1823
1848
  tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
1849
+ const duration = tilesTag.getAttributeValue('DURATION');
1850
+ if (duration) {
1851
+ tileDuration = Number(duration);
1852
+ }
1824
1853
  }
1825
1854
  }
1826
1855
 
@@ -1836,6 +1865,7 @@ shaka.hls.HlsParser = class {
1836
1865
  /* appendWindowEnd= */ Infinity,
1837
1866
  partialSegmentRefs,
1838
1867
  tilesLayout,
1868
+ tileDuration,
1839
1869
  );
1840
1870
  }
1841
1871
 
@@ -347,7 +347,8 @@ shaka.media.SegmentIndex = class {
347
347
  lastReference.appendWindowStart,
348
348
  lastReference.appendWindowEnd,
349
349
  lastReference.partialReferences,
350
- lastReference.tilesLayout);
350
+ lastReference.tilesLayout,
351
+ lastReference.tileDuration);
351
352
  }
352
353
 
353
354
 
@@ -155,11 +155,15 @@ shaka.media.SegmentReference = class {
155
155
  * The value is a grid-item-dimension consisting of two positive decimal
156
156
  * integers in the format: column-x-row ('4x3'). It describes the
157
157
  * arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
158
+ * @param {?number=} tileDuration
159
+ * The explicit duration of an individual tile within the tiles grid.
160
+ * If not provided, the duration should be automatically calculated based on
161
+ * the duration of the reference.
158
162
  */
159
163
  constructor(
160
164
  startTime, endTime, uris, startByte, endByte, initSegmentReference,
161
165
  timestampOffset, appendWindowStart, appendWindowEnd,
162
- partialReferences = [], tilesLayout = '') {
166
+ partialReferences = [], tilesLayout = '', tileDuration = null) {
163
167
  // A preload hinted Partial Segment has the same startTime and endTime.
164
168
  goog.asserts.assert(startTime <= endTime,
165
169
  'startTime must be less than or equal to endTime');
@@ -206,6 +210,9 @@ shaka.media.SegmentReference = class {
206
210
 
207
211
  /** @type {?string} */
208
212
  this.tilesLayout = tilesLayout;
213
+
214
+ /** @type {?number} */
215
+ this.tileDuration = tileDuration;
209
216
  }
210
217
 
211
218
  /**
@@ -290,6 +297,17 @@ shaka.media.SegmentReference = class {
290
297
  getTilesLayout() {
291
298
  return this.tilesLayout;
292
299
  }
300
+
301
+ /**
302
+ * Returns the segment's explicit tile duration.
303
+ * Only defined in image segments.
304
+ *
305
+ * @return {?number}
306
+ * @export
307
+ */
308
+ getTileDuration() {
309
+ return this.tileDuration;
310
+ }
293
311
  };
294
312
 
295
313
 
@@ -628,7 +628,7 @@ shaka.media.StreamingEngine = class {
628
628
 
629
629
  // Don't clear the buffer unless something is buffered. This extra
630
630
  // check prevents extra, useless calls to clear the buffer.
631
- if (somethingBuffered) {
631
+ if (somethingBuffered || mediaState.performingUpdate) {
632
632
  this.forceClearBuffer_(mediaState);
633
633
  streamCleared = true;
634
634
  }
@@ -988,7 +988,10 @@ shaka.media.StreamingEngine = class {
988
988
  unscaledBufferingGoal * this.bufferingGoalScale_;
989
989
 
990
990
  // Check if we've buffered to the end of the presentation.
991
- if (timeNeeded >= this.manifest_.presentationTimeline.getDuration()) {
991
+ const timeUntilEnd =
992
+ this.manifest_.presentationTimeline.getDuration() - timeNeeded;
993
+ const oneMicrosecond = 1e-6;
994
+ if (timeUntilEnd < oneMicrosecond) {
992
995
  // We shouldn't rebuffer if the playhead is close to the end of the
993
996
  // presentation.
994
997
  shaka.log.debug(logPrefix, 'buffered to end of presentation');
package/lib/player.js CHANGED
@@ -2817,14 +2817,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2817
2817
  * @private
2818
2818
  */
2819
2819
  createCmcd_() {
2820
- const abr = this.abrManager_;
2821
-
2822
2820
  /** @type {shaka.util.CmcdManager.PlayerInterface} */
2823
2821
  const playerInterface = {
2824
- getBandwidthEstimate: () => abr ? abr.getBandwidthEstimate() : NaN,
2822
+ getBandwidthEstimate: () => this.abrManager_ ?
2823
+ this.abrManager_.getBandwidthEstimate() : NaN,
2825
2824
  getBufferedInfo: () => this.getBufferedInfo(),
2826
2825
  getCurrentTime: () => this.video_ ? this.video_.currentTime : 0,
2827
- getManifest: () => this.manifest_,
2826
+ getVariantTracks: () => this.getVariantTracks(),
2828
2827
  getPlaybackRate: () => this.getPlaybackRate(),
2829
2828
  isLive: () => this.isLive(),
2830
2829
  };
@@ -3613,7 +3612,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3613
3612
  const height = fullImageHeight / rows;
3614
3613
  const totalImages = columns * rows;
3615
3614
  const segmentDuration = reference.trueEndTime - reference.startTime;
3616
- const thumbnailDuration = segmentDuration / totalImages;
3615
+ const thumbnailDuration =
3616
+ reference.getTileDuration() || (segmentDuration / totalImages);
3617
3617
  let thumbnailTime = reference.startTime;
3618
3618
  let positionX = 0;
3619
3619
  let positionY = 0;
@@ -3636,6 +3636,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3636
3636
  positionY = Math.floor(thumbnailPosition / columns) * height;
3637
3637
  }
3638
3638
  return {
3639
+ imageHeight: fullImageHeight,
3640
+ imageWidth: fullImageWidth,
3639
3641
  height: height,
3640
3642
  positionX: positionX,
3641
3643
  positionY: positionY,
@@ -4037,20 +4039,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4037
4039
  const LanguageUtils = shaka.util.LanguageUtils;
4038
4040
  const inputlanguage = LanguageUtils.normalize(language);
4039
4041
  const chaptersTracks = this.getChaptersTracks_();
4040
- const chaptersTrack = chaptersTracks
4041
- .find((t) => LanguageUtils.normalize(t.language) == inputlanguage);
4042
- if (!chaptersTrack || !chaptersTrack.cues) {
4042
+ const chaptersTracksWithLanguage = chaptersTracks
4043
+ .filter((t) => LanguageUtils.normalize(t.language) == inputlanguage);
4044
+ if (!chaptersTracksWithLanguage || !chaptersTracksWithLanguage.length) {
4043
4045
  return [];
4044
4046
  }
4045
4047
  const chapters = [];
4046
- for (const cue of chaptersTrack.cues) {
4047
- /** @type {shaka.extern.Chapter} */
4048
- const chapter = {
4049
- title: cue.text,
4050
- startTime: cue.startTime,
4051
- endTime: cue.endTime,
4052
- };
4053
- chapters.push(chapter);
4048
+ for (const chaptersTrack of chaptersTracksWithLanguage) {
4049
+ if (chaptersTrack && chaptersTrack.cues) {
4050
+ for (const cue of chaptersTrack.cues) {
4051
+ /** @type {shaka.extern.Chapter} */
4052
+ const chapter = {
4053
+ title: cue.text,
4054
+ startTime: cue.startTime,
4055
+ endTime: cue.endTime,
4056
+ };
4057
+ chapters.push(chapter);
4058
+ }
4059
+ }
4054
4060
  }
4055
4061
  return chapters;
4056
4062
  }
@@ -6530,7 +6536,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6530
6536
  * @define {string} A version number taken from git at compile time.
6531
6537
  * @export
6532
6538
  */
6533
- shaka.Player.version = 'v3.3.0-uncompiled';
6539
+ shaka.Player.version = 'v3.3.1-uncompiled';
6534
6540
 
6535
6541
  // Initialize the deprecation system using the version string we just set
6536
6542
  // on the player.
@@ -24,8 +24,6 @@ shaka.polyfill.MediaCapabilities = class {
24
24
  * @export
25
25
  */
26
26
  static install() {
27
- shaka.log.debug('MediaCapabilities: install');
28
-
29
27
  // Since MediaCapabilities is not fully supported on Chromecast yet, we
30
28
  // should always install polyfill for Chromecast.
31
29
  // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
@@ -40,21 +38,28 @@ shaka.polyfill.MediaCapabilities = class {
40
38
  // TODO: re-evaluate MediaCapabilities in the future versions of PS5
41
39
  // Browsers.
42
40
  if (!shaka.util.Platform.isChromecast() &&
43
- !shaka.util.Platform.isApple() &&
44
- !shaka.util.Platform.isPS5() &&
45
- navigator.mediaCapabilities) {
46
- shaka.log.debug(
41
+ !shaka.util.Platform.isApple() &&
42
+ !shaka.util.Platform.isPS5() &&
43
+ navigator.mediaCapabilities) {
44
+ shaka.log.info(
47
45
  'MediaCapabilities: Native mediaCapabilities support found.');
48
- } else {
49
- if (!navigator.mediaCapabilities) {
50
- navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
51
- }
52
- navigator.mediaCapabilities.decodingInfo =
53
- shaka.polyfill.MediaCapabilities.decodingInfo_;
46
+ return;
47
+ }
48
+
49
+ shaka.log.info('MediaCapabilities: install');
50
+
51
+ if (!navigator.mediaCapabilities) {
52
+ navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
54
53
  }
55
- // Workaround because in Apple browsers there is no way to overwrite
56
- // MediaCapabilities.
57
- window.shakaMediaCapabilities = navigator.mediaCapabilities;
54
+
55
+ // Keep the patched MediaCapabilities object from being garbage-collected in
56
+ // Safari.
57
+ // See https://github.com/google/shaka-player/issues/3696#issuecomment-1009472718
58
+ shaka.polyfill.MediaCapabilities.originalMcap =
59
+ navigator.mediaCapabilities;
60
+
61
+ navigator.mediaCapabilities.decodingInfo =
62
+ shaka.polyfill.MediaCapabilities.decodingInfo_;
58
63
  }
59
64
 
60
65
  /**
@@ -182,6 +187,16 @@ shaka.polyfill.MediaCapabilities = class {
182
187
  }
183
188
  };
184
189
 
190
+ /**
191
+ * A copy of the MediaCapabilities instance, to prevent Safari from
192
+ * garbage-collecting the polyfilled method on it. We make it public and export
193
+ * it to ensure that it is not stripped out by the compiler.
194
+ *
195
+ * @type {MediaCapabilities}
196
+ * @export
197
+ */
198
+ shaka.polyfill.MediaCapabilities.originalMcap = null;
199
+
185
200
  // Install at a lower priority than MediaSource polyfill, so that we have
186
201
  // MediaSource available first.
187
202
  shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);
package/lib/text/cue.js CHANGED
@@ -222,6 +222,12 @@ shaka.text.Cue = class {
222
222
  */
223
223
  this.nestedCues = [];
224
224
 
225
+ /**
226
+ * @override
227
+ * @exportInterface
228
+ */
229
+ this.isContainer = false;
230
+
225
231
  /**
226
232
  * @override
227
233
  * @exportInterface