shaka-player 4.11.13 → 4.12.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 (71) hide show
  1. package/README.md +47 -0
  2. package/dist/deps.js +8 -8
  3. package/dist/locales.js +35 -35
  4. package/dist/shaka-player.compiled.d.ts +36 -13
  5. package/dist/shaka-player.compiled.debug.d.ts +36 -13
  6. package/dist/shaka-player.compiled.debug.externs.js +46 -11
  7. package/dist/shaka-player.compiled.debug.js +543 -517
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +46 -11
  10. package/dist/shaka-player.compiled.js +1348 -1331
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +37 -14
  13. package/dist/shaka-player.ui.debug.d.ts +37 -14
  14. package/dist/shaka-player.ui.debug.externs.js +46 -11
  15. package/dist/shaka-player.ui.debug.js +629 -598
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +46 -11
  18. package/dist/shaka-player.ui.js +1624 -1603
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/ads.js +9 -0
  21. package/externs/shaka/manifest.js +26 -1
  22. package/externs/shaka/offline.js +8 -2
  23. package/externs/shaka/player.js +80 -28
  24. package/externs/shaka/text.js +9 -0
  25. package/lib/ads/ad_manager.js +6 -0
  26. package/lib/ads/ad_utils.js +1 -0
  27. package/lib/ads/client_side_ad.js +8 -0
  28. package/lib/ads/interstitial_ad.js +25 -4
  29. package/lib/ads/interstitial_ad_manager.js +67 -8
  30. package/lib/ads/media_tailor_ad.js +8 -0
  31. package/lib/ads/server_side_ad.js +8 -0
  32. package/lib/dash/content_protection.js +31 -0
  33. package/lib/dash/dash_parser.js +22 -12
  34. package/lib/hls/hls_parser.js +63 -49
  35. package/lib/media/adaptation_set.js +11 -17
  36. package/lib/media/adaptation_set_criteria.js +4 -9
  37. package/lib/media/drm_engine.js +3 -1
  38. package/lib/media/manifest_filterer.js +2 -1
  39. package/lib/media/media_source_engine.js +286 -20
  40. package/lib/media/playhead.js +3 -1
  41. package/lib/media/preload_manager.js +10 -7
  42. package/lib/media/segment_prefetch.js +32 -13
  43. package/lib/media/segment_reference.js +3 -3
  44. package/lib/media/streaming_engine.js +87 -261
  45. package/lib/mss/mss_parser.js +1 -0
  46. package/lib/offline/download_info.js +5 -1
  47. package/lib/offline/indexeddb/storage_mechanism.js +4 -3
  48. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  49. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  50. package/lib/offline/manifest_converter.js +4 -0
  51. package/lib/offline/storage.js +277 -5
  52. package/lib/player.js +224 -78
  53. package/lib/text/simple_text_displayer.js +7 -0
  54. package/lib/text/stub_text_displayer.js +7 -0
  55. package/lib/text/ui_text_displayer.js +12 -0
  56. package/lib/util/dom_utils.js +14 -0
  57. package/lib/util/error.js +5 -0
  58. package/lib/util/periods.js +2 -0
  59. package/lib/util/platform.js +15 -0
  60. package/lib/util/player_configuration.js +11 -4
  61. package/lib/util/stream_utils.js +20 -1
  62. package/package.json +1 -1
  63. package/ui/ad_statistics_button.js +12 -0
  64. package/ui/controls.js +31 -7
  65. package/ui/externs/ui.js +8 -1
  66. package/ui/fullscreen_button.js +9 -0
  67. package/ui/locales/be.json +49 -0
  68. package/ui/mute_button.js +42 -11
  69. package/ui/skip_ad_button.js +16 -11
  70. package/ui/statistics_button.js +12 -0
  71. package/ui/ui.js +10 -5
@@ -65,6 +65,7 @@ shaka.extern.AdCuePoint;
65
65
  * uri: string,
66
66
  * isSkippable: boolean,
67
67
  * skipOffset: ?number,
68
+ * skipFor: ?number,
68
69
  * canJump: boolean,
69
70
  * resumeOffset: ?number,
70
71
  * playoutLimit: ?number,
@@ -91,6 +92,9 @@ shaka.extern.AdCuePoint;
91
92
  * @property {?number} skipOffset
92
93
  * Time value that identifies when skip controls are made available to the
93
94
  * end user.
95
+ * @property {?number} skipFor
96
+ * The amount of time in seconds a skip button should be displayed for.
97
+ * Note that this value should be >= 0.
94
98
  * @property {boolean} canJump
95
99
  * Indicate if the interstitial is jumpable.
96
100
  * @property {?number} resumeOffset
@@ -297,6 +301,11 @@ shaka.extern.IAd = class {
297
301
  */
298
302
  isClientRendering() {}
299
303
 
304
+ /**
305
+ * @return {boolean}
306
+ */
307
+ isUsingAnotherMediaElement() {}
308
+
300
309
  /**
301
310
  * @return {number}
302
311
  */
@@ -387,7 +387,8 @@ shaka.extern.SegmentIndex = class {
387
387
  * mssPrivateData: (shaka.extern.MssPrivateData|undefined),
388
388
  * external: boolean,
389
389
  * fastSwitching: boolean,
390
- * fullMimeTypes: !Set.<string>
390
+ * fullMimeTypes: !Set.<string>,
391
+ * isAudioMuxedInVideo: boolean
391
392
  * }}
392
393
  *
393
394
  * @description
@@ -534,6 +535,8 @@ shaka.extern.SegmentIndex = class {
534
535
  * represents the types used in each period of the original manifest.
535
536
  * Meant for being used by compatibility checking, such as with
536
537
  * MediaSource.isTypeSupported.
538
+ * @property {boolean} isAudioMuxedInVideo
539
+ * Indicate if the audio of this stream is muxed in the video of other stream.
537
540
  *
538
541
  * @exportDoc
539
542
  */
@@ -562,3 +565,25 @@ shaka.extern.Stream;
562
565
  * @exportDoc
563
566
  */
564
567
  shaka.extern.MssPrivateData;
568
+
569
+
570
+ /**
571
+ * @typedef {{
572
+ * height: number,
573
+ * positionX: number,
574
+ * positionY: number,
575
+ * width: number
576
+ * }}
577
+ *
578
+ * @property {number} height
579
+ * The thumbnail height in px.
580
+ * @property {number} positionX
581
+ * The thumbnail left position in px.
582
+ * @property {number} positionY
583
+ * The thumbnail top position in px.
584
+ * @property {number} width
585
+ * The thumbnail width in px.
586
+ *
587
+ * @exportDoc
588
+ */
589
+ shaka.extern.ThumbnailSprite;
@@ -142,7 +142,8 @@ shaka.extern.ManifestDB;
142
142
  * closedCaptions: Map.<string, string>,
143
143
  * tilesLayout: (string|undefined),
144
144
  * external: boolean,
145
- * fastSwitching: boolean
145
+ * fastSwitching: boolean,
146
+ * isAudioMuxedInVideo: boolean
146
147
  * }}
147
148
  *
148
149
  * @property {number} id
@@ -218,6 +219,8 @@ shaka.extern.ManifestDB;
218
219
  * Eg: external text tracks.
219
220
  * @property {boolean} fastSwitching
220
221
  * Indicate if the stream should be used for fast switching.
222
+ * @property {boolean} isAudioMuxedInVideo
223
+ * Indicate if the audio of this stream is muxed in the video of other stream.
221
224
  */
222
225
  shaka.extern.StreamDB;
223
226
 
@@ -235,7 +238,8 @@ shaka.extern.StreamDB;
235
238
  * pendingInitSegmentRefId: (string|undefined),
236
239
  * dataKey: number,
237
240
  * mimeType: ?string,
238
- * codecs: ?string
241
+ * codecs: ?string,
242
+ * thumbnailSprite: ?shaka.extern.ThumbnailSprite
239
243
  * }}
240
244
  *
241
245
  * @property {?number} initSegmentKey
@@ -272,6 +276,8 @@ shaka.extern.StreamDB;
272
276
  * The mimeType of the segment.
273
277
  * @property {?string} codecs
274
278
  * The codecs of the segment.
279
+ * @property {?shaka.extern.ThumbnailSprite} thumbnailSprite
280
+ * The segment's thumbnail sprite.
275
281
  */
276
282
  shaka.extern.SegmentDB;
277
283
 
@@ -1074,7 +1074,6 @@ shaka.extern.xml.Node;
1074
1074
  * manifestPreprocessor: function(!Element),
1075
1075
  * manifestPreprocessorTXml: function(!shaka.extern.xml.Node),
1076
1076
  * sequenceMode: boolean,
1077
- * enableAudioGroups: boolean,
1078
1077
  * multiTypeVariantsAllowed: boolean,
1079
1078
  * useStreamOnceInPeriodFlattening: boolean,
1080
1079
  * updatePeriod: number,
@@ -1096,7 +1095,7 @@ shaka.extern.xml.Node;
1096
1095
  * @property {boolean} disableXlinkProcessing
1097
1096
  * If true, xlink-related processing will be disabled.
1098
1097
  * <br>
1099
- * Defaults to <code>false</code>.
1098
+ * Defaults to <code>true</code>.
1100
1099
  * @property {boolean} xlinkFailGracefully
1101
1100
  * If true, xlink-related errors will result in a fallback to the tag's
1102
1101
  * existing contents. If false, xlink-related errors will be propagated
@@ -1155,11 +1154,6 @@ shaka.extern.xml.Node;
1155
1154
  * "sequence mode" (ignoring their internal timestamps).
1156
1155
  * <br>
1157
1156
  * Defaults to <code>false</code>.
1158
- * @property {boolean} enableAudioGroups
1159
- * If set, audio streams will be grouped and filtered by their parent
1160
- * adaptation set ID.
1161
- * <br>
1162
- * Defaults to <code>false</code>.
1163
1157
  * @property {boolean} multiTypeVariantsAllowed
1164
1158
  * If true, the manifest parser will create variants that have multiple
1165
1159
  * mimeTypes or codecs for video or for audio if there is no other choice.
@@ -1179,10 +1173,10 @@ shaka.extern.xml.Node;
1179
1173
  * <br>
1180
1174
  * Defaults to <code>false</code>.
1181
1175
  * @property {number} updatePeriod
1182
- * Override the minimumUpdatePeriod of the manifest. The value is in second
1183
- * if the value is greater than the minimumUpdatePeriod, it will update the
1184
- * manifest less frequently. if you update the value during for a dynamic
1185
- * manifest, it will directly trigger a new download of the manifest
1176
+ * Override the minimumUpdatePeriod of the manifest. The value is in seconds.
1177
+ * If the value is greater than the minimumUpdatePeriod, it will update the
1178
+ * manifest less frequently. If you update the value during for a dynamic
1179
+ * manifest, it will directly trigger a new download of the manifest.
1186
1180
  * <br>
1187
1181
  * Defaults to <code>-1</code>.
1188
1182
  * @property {boolean} enableFastSwitching
@@ -1208,7 +1202,8 @@ shaka.extern.DashManifestConfiguration;
1208
1202
  * ignoreManifestTimestampsInSegmentsMode: boolean,
1209
1203
  * disableCodecGuessing: boolean,
1210
1204
  * disableClosedCaptionsDetection: boolean,
1211
- * allowLowLatencyByteRangeOptimization: boolean
1205
+ * allowLowLatencyByteRangeOptimization: boolean,
1206
+ * updatePeriod: number
1212
1207
  * }}
1213
1208
  *
1214
1209
  * @property {boolean} ignoreTextStreamFailures
@@ -1294,6 +1289,14 @@ shaka.extern.DashManifestConfiguration;
1294
1289
  * https://www.akamai.com/blog/performance/-using-ll-hls-with-byte-range-addressing-to-achieve-interoperabi
1295
1290
  * <br>
1296
1291
  * Defaults to <code>true</code>.
1292
+ * @property {number} updatePeriod
1293
+ * Override the update period of the playlist. The value is in seconds.
1294
+ * If the value is less than 0, the period will be determined based on the
1295
+ * segment length. If the value is greater than 0, it will update the target
1296
+ * duration. If you update the value during the live, it will directly
1297
+ * trigger a new download of the manifest.
1298
+ * <br>
1299
+ * Defaults to <code>-1</code>.
1297
1300
  * @exportDoc
1298
1301
  */
1299
1302
  shaka.extern.HlsManifestConfiguration;
@@ -1337,6 +1340,7 @@ shaka.extern.MssManifestConfiguration;
1337
1340
  * disableVideo: boolean,
1338
1341
  * disableText: boolean,
1339
1342
  * disableThumbnails: boolean,
1343
+ * disableIFrames: boolean,
1340
1344
  * defaultPresentationDelay: number,
1341
1345
  * segmentRelativeVttTiming: boolean,
1342
1346
  * dash: shaka.extern.DashManifestConfiguration,
@@ -1371,6 +1375,10 @@ shaka.extern.MssManifestConfiguration;
1371
1375
  * If <code>true</code>, the image tracks are ignored.
1372
1376
  * <br>
1373
1377
  * Defaults to <code>false</code>.
1378
+ * @property {boolean} disableIFrames
1379
+ * If <code>true</code>, the I-Frames tracks are ignored.
1380
+ * <br>
1381
+ * Defaults to <code>false</code>.
1374
1382
  * @property {number} defaultPresentationDelay
1375
1383
  * For DASH, it's a default <code>presentationDelay</code> value if
1376
1384
  * <code>suggestedPresentationDelay</code> is missing in the MPEG DASH
@@ -1536,6 +1544,7 @@ shaka.extern.LiveSyncConfiguration;
1536
1544
  * gapJumpTimerTime: number,
1537
1545
  * durationBackoff: number,
1538
1546
  * safeSeekOffset: number,
1547
+ * safeSeekEndOffset: number,
1539
1548
  * stallEnabled: boolean,
1540
1549
  * stallThreshold: number,
1541
1550
  * stallSkip: number,
@@ -1548,10 +1557,8 @@ shaka.extern.LiveSyncConfiguration;
1548
1557
  * minBytesForProgressEvents: number,
1549
1558
  * preferNativeHls: boolean,
1550
1559
  * updateIntervalSeconds: number,
1551
- * dispatchAllEmsgBoxes: boolean,
1552
1560
  * observeQualityChanges: boolean,
1553
1561
  * maxDisabledTime: number,
1554
- * parsePrftBox: boolean,
1555
1562
  * segmentPrefetchLimit: number,
1556
1563
  * prefetchAudioLanguages: !Array<string>,
1557
1564
  * disableAudioPrefetch: boolean,
@@ -1653,6 +1660,13 @@ shaka.extern.LiveSyncConfiguration;
1653
1660
  * bandwidth scenarios.
1654
1661
  * <br>
1655
1662
  * Defaults to <code>5</code>.
1663
+ * @property {number} safeSeekEndOffset
1664
+ * The amount of seconds that should be added when repositioning the playhead
1665
+ * after falling out of the seakable end range. This is helpful for live
1666
+ * stream with a lot of GAP. This will reposition the playback in the past
1667
+ * and avoid to be block at the edge and buffer at the next GAP
1668
+ * <br>
1669
+ * Defaults to <code>0</code>.
1656
1670
  * @property {boolean} stallEnabled
1657
1671
  * When set to <code>true</code>, the stall detector logic will run. If the
1658
1672
  * playhead stops moving for <code>stallThreshold</code> seconds, the player
@@ -1724,10 +1738,6 @@ shaka.extern.LiveSyncConfiguration;
1724
1738
  * The minimum number of seconds to see if the manifest has changes.
1725
1739
  * <br>
1726
1740
  * Defaults to <code>1</code>.
1727
- * @property {boolean} dispatchAllEmsgBoxes
1728
- * If true, all emsg boxes are parsed and dispatched.
1729
- * <br>
1730
- * Defaults to <code>false</code>.
1731
1741
  * @property {boolean} observeQualityChanges
1732
1742
  * If true, monitor media quality changes and emit
1733
1743
  * <code>shaka.Player.MediaQualityChangedEvent</code>.
@@ -1739,14 +1749,6 @@ shaka.extern.LiveSyncConfiguration;
1739
1749
  * If all variants are disabled this way, NETWORK HTTP_ERROR will be thrown.
1740
1750
  * <br>
1741
1751
  * Defaults to <code>30</code>.
1742
- * @property {boolean} parsePrftBox
1743
- * If <code>true</code>, will raise a shaka.extern.ProducerReferenceTime
1744
- * player event (event name 'prft').
1745
- * The event will be raised only once per playback session as program
1746
- * start date will not change, and would save parsing the segment multiple
1747
- * times needlessly.
1748
- * <br>
1749
- * Defaults to <code>false</code>.
1750
1752
  * @property {number} segmentPrefetchLimit
1751
1753
  * The maximum number of segments for each active stream to be prefetched
1752
1754
  * ahead of playhead in parallel.
@@ -1843,7 +1845,8 @@ shaka.extern.StreamingConfiguration;
1843
1845
  * addExtraFeaturesToSourceBuffer: function(string): string,
1844
1846
  * forceTransmux: boolean,
1845
1847
  * insertFakeEncryptionInInit: boolean,
1846
- * modifyCueCallback: shaka.extern.TextParser.ModifyCueCallback
1848
+ * modifyCueCallback: shaka.extern.TextParser.ModifyCueCallback,
1849
+ * dispatchAllEmsgBoxes: boolean
1847
1850
  * }}
1848
1851
  *
1849
1852
  * @description
@@ -1882,6 +1885,10 @@ shaka.extern.StreamingConfiguration;
1882
1885
  * A callback called for each cue after it is parsed, but right before it
1883
1886
  * is appended to the presentation.
1884
1887
  * Gives a chance for client-side editing of cue text, cue timing, etc.
1888
+ * @property {boolean} dispatchAllEmsgBoxes
1889
+ * If true, all emsg boxes are parsed and dispatched.
1890
+ * <br>
1891
+ * Defaults to <code>false</code>.
1885
1892
  * @exportDoc
1886
1893
  */
1887
1894
  shaka.extern.MediaSourceConfiguration;
@@ -1891,7 +1898,9 @@ shaka.extern.MediaSourceConfiguration;
1891
1898
  * @typedef {{
1892
1899
  * customPlayheadTracker: boolean,
1893
1900
  * skipPlayDetection: boolean,
1894
- * supportsMultipleMediaElements: boolean
1901
+ * supportsMultipleMediaElements: boolean,
1902
+ * disableHLSInterstitial: boolean,
1903
+ * disableDASHInterstitial: boolean
1895
1904
  * }}
1896
1905
  *
1897
1906
  * @description
@@ -1918,6 +1927,14 @@ shaka.extern.MediaSourceConfiguration;
1918
1927
  * Defaults to <code>true</code> except on Tizen, WebOS, Chromecast,
1919
1928
  * Hisense, PlayStation 4, PlayStation5, Xbox whose default value is
1920
1929
  * <code>false</code>.
1930
+ * @property {boolean} disableHLSInterstitial
1931
+ * If this is true, we ignore HLS interstitial events.
1932
+ * <br>
1933
+ * Defaults to <code>false</code>.
1934
+ * @property {boolean} disableDASHInterstitial
1935
+ * If this is true, we ignore DASH interstitial events.
1936
+ * <br>
1937
+ * Defaults to <code>false</code>.
1921
1938
  *
1922
1939
  * @exportDoc
1923
1940
  */
@@ -2278,6 +2295,7 @@ shaka.extern.TextDisplayerConfiguration;
2278
2295
  * cmsd: shaka.extern.CmsdConfiguration,
2279
2296
  * lcevc: shaka.extern.LcevcConfiguration,
2280
2297
  * offline: shaka.extern.OfflineConfiguration,
2298
+ * ignoreHardwareResolution: boolean,
2281
2299
  * preferredAudioLanguage: string,
2282
2300
  * preferredAudioLabel: string,
2283
2301
  * preferredTextLanguage: string,
@@ -2285,6 +2303,7 @@ shaka.extern.TextDisplayerConfiguration;
2285
2303
  * preferredTextRole: string,
2286
2304
  * preferredVideoCodecs: !Array.<string>,
2287
2305
  * preferredAudioCodecs: !Array.<string>,
2306
+ * preferredTextFormats: !Array.<string>,
2288
2307
  * preferredAudioChannelCount: number,
2289
2308
  * preferredVideoHdrLevel: string,
2290
2309
  * preferredVideoLayout: string,
@@ -2327,6 +2346,11 @@ shaka.extern.TextDisplayerConfiguration;
2327
2346
  * (Low Complexity Enhancement Video Codec)
2328
2347
  * @property {shaka.extern.OfflineConfiguration} offline
2329
2348
  * Offline configuration and settings.
2349
+ * @property {boolean} ignoreHardwareResolution
2350
+ * Do not detect the hardware resolution. For some niche cases where content
2351
+ * is only available at resolutions beyond the device's native resolution,
2352
+ * and you are confident it can be decoded and downscaled, this flag can
2353
+ * allow playback when it would otherwise fail.
2330
2354
  * @property {string} preferredAudioLanguage
2331
2355
  * The preferred language to use for audio tracks. If not given it will use
2332
2356
  * the <code>'main'</code> track.
@@ -2364,6 +2388,10 @@ shaka.extern.TextDisplayerConfiguration;
2364
2388
  * The list of preferred audio codecs, in order of highest to lowest priority.
2365
2389
  * <br>
2366
2390
  * Defaults to <code>[]</code>.
2391
+ * @property {!Array.<string>} preferredTextFormats
2392
+ * The list of preferred text formats, in order of highest to lowest priority.
2393
+ * <br>
2394
+ * Defaults to <code>[]</code>.
2367
2395
  * @property {number} preferredAudioChannelCount
2368
2396
  * The preferred number of audio channels.
2369
2397
  * <br>
@@ -2508,3 +2536,27 @@ shaka.extern.Thumbnail;
2508
2536
  * @exportDoc
2509
2537
  */
2510
2538
  shaka.extern.Chapter;
2539
+
2540
+
2541
+ /**
2542
+ * @typedef {{
2543
+ * uri: string,
2544
+ * language: string,
2545
+ * kind: string,
2546
+ * mime: string,
2547
+ * codecs: (string|undefined)
2548
+ * }}
2549
+ *
2550
+ * @property {string} uri
2551
+ * The URI of the text.
2552
+ * @property {string} language
2553
+ * The language of the text (e.g. 'en').
2554
+ * @property {string} kind
2555
+ * The kind of text (e.g. 'subtitles').
2556
+ * @property {?string} mime
2557
+ * The MIME type of the text (e.g. 'text/vtt')
2558
+ * @property {?string} codecs
2559
+ * The codecs string, if needed to refine the MIME type.
2560
+ * @exportDoc
2561
+ */
2562
+ shaka.extern.ExtraText;
@@ -183,6 +183,15 @@ shaka.extern.TextDisplayer = class {
183
183
  * @exportDoc
184
184
  */
185
185
  setTextVisibility(on) {}
186
+
187
+ /**
188
+ * Set the current language.
189
+ *
190
+ * @param {string} language
191
+ *
192
+ * @exportDoc
193
+ */
194
+ setTextLanguage(language) {}
186
195
  };
187
196
 
188
197
 
@@ -824,6 +824,9 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
824
824
  * @export
825
825
  */
826
826
  onHLSInterstitialMetadata(basePlayer, baseVideo, interstitial) {
827
+ if (this.config_ && this.config_.disableHLSInterstitial) {
828
+ return;
829
+ }
827
830
  if (!this.interstitialAdManager_) {
828
831
  this.initInterstitial(/* adContainer= */ null, basePlayer, baseVideo);
829
832
  }
@@ -837,6 +840,9 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
837
840
  * @export
838
841
  */
839
842
  onDASHInterstitialMetadata(basePlayer, baseVideo, region) {
843
+ if (this.config_ && this.config_.disableDASHInterstitial) {
844
+ return;
845
+ }
840
846
  if (region.schemeIdUri != 'urn:mpeg:dash:event:alternativeMPD:2022') {
841
847
  return;
842
848
  }
@@ -82,6 +82,7 @@ shaka.ads.Utils = class {
82
82
  uri: adUrl,
83
83
  isSkippable: skipOffset != null,
84
84
  skipOffset,
85
+ skipFor: null,
85
86
  canJump: false,
86
87
  resumeOffset: 0,
87
88
  playoutLimit: null,
@@ -66,6 +66,14 @@ shaka.ads.ClientSideAd = class {
66
66
  return true;
67
67
  }
68
68
 
69
+ /**
70
+ * @override
71
+ * @export
72
+ */
73
+ isUsingAnotherMediaElement() {
74
+ return true;
75
+ }
76
+
69
77
  /**
70
78
  * @override
71
79
  * @export
@@ -16,12 +16,14 @@ shaka.ads.InterstitialAd = class {
16
16
  * @param {HTMLMediaElement} video
17
17
  * @param {boolean} isSkippable
18
18
  * @param {?number} skipOffset
19
+ * @param {?number} skipFor
19
20
  * @param {function()} onSkip
20
21
  * @param {number} sequenceLength
21
22
  * @param {number} adPosition
23
+ * @param {boolean} isUsingAnotherMediaElement
22
24
  */
23
- constructor(video, isSkippable, skipOffset, onSkip,
24
- sequenceLength, adPosition) {
25
+ constructor(video, isSkippable, skipOffset, skipFor, onSkip,
26
+ sequenceLength, adPosition, isUsingAnotherMediaElement) {
25
27
  /** @private {HTMLMediaElement} */
26
28
  this.video_ = video;
27
29
 
@@ -29,7 +31,10 @@ shaka.ads.InterstitialAd = class {
29
31
  this.isSkippable_ = isSkippable;
30
32
 
31
33
  /** @private {?number} */
32
- this.skipOffset_ = skipOffset;
34
+ this.skipOffset_ = isSkippable ? skipOffset || 0 : skipOffset;
35
+
36
+ /** @private {?number} */
37
+ this.skipFor_ = skipFor;
33
38
 
34
39
  /** @private {function()} */
35
40
  this.onSkip_ = onSkip;
@@ -39,6 +44,9 @@ shaka.ads.InterstitialAd = class {
39
44
 
40
45
  /** @private {number} */
41
46
  this.adPosition_ = adPosition;
47
+
48
+ /** @private {boolean} */
49
+ this.isUsingAnotherMediaElement_ = isUsingAnotherMediaElement;
42
50
  }
43
51
 
44
52
  /**
@@ -57,6 +65,14 @@ shaka.ads.InterstitialAd = class {
57
65
  return true;
58
66
  }
59
67
 
68
+ /**
69
+ * @override
70
+ * @export
71
+ */
72
+ isUsingAnotherMediaElement() {
73
+ return this.isUsingAnotherMediaElement_;
74
+ }
75
+
60
76
  /**
61
77
  * @override
62
78
  * @export
@@ -102,6 +118,11 @@ shaka.ads.InterstitialAd = class {
102
118
  * @export
103
119
  */
104
120
  isSkippable() {
121
+ if (this.isSkippable_ && this.skipFor_ != null) {
122
+ const position = this.getDuration() - this.getRemainingTime();
123
+ const maxTime = this.skipOffset_ + this.skipFor_;
124
+ return position < maxTime;
125
+ }
105
126
  return this.isSkippable_;
106
127
  }
107
128
 
@@ -110,7 +131,7 @@ shaka.ads.InterstitialAd = class {
110
131
  * @export
111
132
  */
112
133
  getTimeUntilSkippable() {
113
- if (this.isSkippable_) {
134
+ if (this.isSkippable()) {
114
135
  const canSkipIn =
115
136
  this.getRemainingTime() + this.skipOffset_ - this.getDuration();
116
137
  return Math.max(canSkipIn, 0);
@@ -319,6 +319,8 @@ shaka.ads.InterstitialAdManager = class {
319
319
  shaka.log.warning('Unsupported MPD alternate', region);
320
320
  return;
321
321
  }
322
+
323
+ /** @type {!shaka.extern.AdInterstitial} */
322
324
  const interstitial = {
323
325
  id: null,
324
326
  startTime: region.startTime,
@@ -326,6 +328,7 @@ shaka.ads.InterstitialAdManager = class {
326
328
  uri: alternativeMPDUri,
327
329
  isSkippable: false,
328
330
  skipOffset: null,
331
+ skipFor: null,
329
332
  canJump: true,
330
333
  resumeOffset: isInsert ? 0 : null,
331
334
  playoutLimit: null,
@@ -654,7 +657,8 @@ shaka.ads.InterstitialAdManager = class {
654
657
 
655
658
  const ad = new shaka.ads.InterstitialAd(this.video_,
656
659
  interstitial.isSkippable, interstitial.skipOffset,
657
- onSkip, sequenceLength, adPosition);
660
+ interstitial.skipFor, onSkip, sequenceLength, adPosition,
661
+ !this.usingBaseVideo_);
658
662
  if (!this.usingBaseVideo_) {
659
663
  ad.setMuted(this.baseVideo_.muted);
660
664
  ad.setVolume(this.baseVideo_.volume);
@@ -664,7 +668,8 @@ shaka.ads.InterstitialAdManager = class {
664
668
  new shaka.util.FakeEvent(shaka.ads.Utils.AD_STARTED,
665
669
  (new Map()).set('ad', ad)));
666
670
 
667
- if (ad.canSkipNow()) {
671
+ let prevCanSkipNow = ad.canSkipNow();
672
+ if (prevCanSkipNow) {
668
673
  this.onEvent_(new shaka.util.FakeEvent(
669
674
  shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
670
675
  }
@@ -675,12 +680,13 @@ shaka.ads.InterstitialAdManager = class {
675
680
  if (!duration) {
676
681
  return;
677
682
  }
678
- if (interstitial.isSkippable && interstitial.skipOffset &&
679
- ad.canSkipNow() && ad.getRemainingTime() > 0 &&
680
- ad.getDuration() > 0) {
683
+ const currentCanSkipNow = ad.canSkipNow();
684
+ if (prevCanSkipNow != currentCanSkipNow &&
685
+ ad.getRemainingTime() > 0 && ad.getDuration() > 0) {
681
686
  this.onEvent_(
682
687
  new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
683
688
  }
689
+ prevCanSkipNow = currentCanSkipNow;
684
690
  const currentPercent = 100 * this.video_.currentTime / duration;
685
691
  if (currentPercent >= 25 && !eventsSent.has('firstquartile')) {
686
692
  updateBaseVideoTime();
@@ -812,6 +818,26 @@ shaka.ads.InterstitialAdManager = class {
812
818
  isSkippable = !data.includes('SKIP');
813
819
  canJump = !data.includes('JUMP');
814
820
  }
821
+ let skipOffset = isSkippable ? 0 : null;
822
+ const enableSkipAfter =
823
+ hlsInterstitial.values.find((v) => v.key == 'X-ENABLE-SKIP-AFTER');
824
+ if (enableSkipAfter) {
825
+ const enableSkipAfterString = /** @type {string} */(enableSkipAfter.data);
826
+ skipOffset = parseFloat(enableSkipAfterString);
827
+ if (isNaN(skipOffset)) {
828
+ skipOffset = isSkippable ? 0 : null;
829
+ }
830
+ }
831
+ let skipFor = null;
832
+ const enableSkipFor =
833
+ hlsInterstitial.values.find((v) => v.key == 'X-ENABLE-SKIP-FOR');
834
+ if (enableSkipFor) {
835
+ const enableSkipForString = /** @type {string} */(enableSkipFor.data);
836
+ skipFor = parseFloat(enableSkipForString);
837
+ if (isNaN(skipOffset)) {
838
+ skipFor = null;
839
+ }
840
+ }
815
841
  let resumeOffset = null;
816
842
  const resume =
817
843
  hlsInterstitial.values.find((v) => v.key == 'X-RESUME-OFFSET');
@@ -862,7 +888,8 @@ shaka.ads.InterstitialAdManager = class {
862
888
  endTime,
863
889
  uri,
864
890
  isSkippable,
865
- skipOffset: isSkippable ? 0 : null,
891
+ skipOffset,
892
+ skipFor,
866
893
  canJump,
867
894
  resumeOffset,
868
895
  playoutLimit,
@@ -886,6 +913,23 @@ shaka.ads.InterstitialAdManager = class {
886
913
  const dataAsJson =
887
914
  /** @type {!shaka.ads.InterstitialAdManager.AssetsList} */ (
888
915
  JSON.parse(data));
916
+ const skipControl = dataAsJson['SKIP-CONTROL'];
917
+ if (skipControl) {
918
+ const enableSkipAfterValue = skipControl['ENABLE-SKIP-AFTER'];
919
+ if (enableSkipAfterValue instanceof Number) {
920
+ skipOffset = parseFloat(enableSkipAfterValue);
921
+ if (isNaN(enableSkipAfterValue)) {
922
+ skipOffset = isSkippable ? 0 : null;
923
+ }
924
+ }
925
+ const enableSkipForValue = skipControl['X-ENABLE-SKIP-FOR'];
926
+ if (enableSkipForValue instanceof Number) {
927
+ skipFor = parseFloat(enableSkipForValue);
928
+ if (isNaN(enableSkipForValue)) {
929
+ skipFor = null;
930
+ }
931
+ }
932
+ }
889
933
  for (const asset of dataAsJson['ASSETS']) {
890
934
  if (asset['URI']) {
891
935
  interstitialsAd.push({
@@ -894,7 +938,8 @@ shaka.ads.InterstitialAdManager = class {
894
938
  endTime,
895
939
  uri: asset['URI'],
896
940
  isSkippable,
897
- skipOffset: isSkippable ? 0 : null,
941
+ skipOffset,
942
+ skipFor,
898
943
  canJump,
899
944
  resumeOffset,
900
945
  playoutLimit,
@@ -983,10 +1028,12 @@ shaka.ads.InterstitialAdManager = class {
983
1028
 
984
1029
  /**
985
1030
  * @typedef {{
986
- * ASSETS: !Array.<shaka.ads.InterstitialAdManager.Asset>
1031
+ * ASSETS: !Array.<shaka.ads.InterstitialAdManager.Asset>,
1032
+ * SKIP-CONTROL: ?shaka.ads.InterstitialAdManager.SkipControl
987
1033
  * }}
988
1034
  *
989
1035
  * @property {!Array.<shaka.ads.InterstitialAdManager.Asset>} ASSETS
1036
+ * @property {shaka.ads.InterstitialAdManager.SkipControl} SKIP-CONTROL
990
1037
  */
991
1038
  shaka.ads.InterstitialAdManager.AssetsList;
992
1039
 
@@ -999,3 +1046,15 @@ shaka.ads.InterstitialAdManager.AssetsList;
999
1046
  * @property {string} URI
1000
1047
  */
1001
1048
  shaka.ads.InterstitialAdManager.Asset;
1049
+
1050
+
1051
+ /**
1052
+ * @typedef {{
1053
+ * ENABLE-SKIP-AFTER: number,
1054
+ * ENABLE-SKIP-FOR: number
1055
+ * }}
1056
+ *
1057
+ * @property {number} ENABLE-SKIP-AFTER
1058
+ * @property {number} ENABLE-SKIP-FOR
1059
+ */
1060
+ shaka.ads.InterstitialAdManager.SkipControl;
@@ -59,6 +59,14 @@ shaka.ads.MediaTailorAd = class {
59
59
  return false;
60
60
  }
61
61
 
62
+ /**
63
+ * @override
64
+ * @export
65
+ */
66
+ isUsingAnotherMediaElement() {
67
+ return false;
68
+ }
69
+
62
70
  /**
63
71
  * @override
64
72
  * @export
@@ -51,6 +51,14 @@ shaka.ads.ServerSideAd = class {
51
51
  return false;
52
52
  }
53
53
 
54
+ /**
55
+ * @override
56
+ * @export
57
+ */
58
+ isUsingAnotherMediaElement() {
59
+ return false;
60
+ }
61
+
54
62
  /**
55
63
  * @override
56
64
  * @export