shaka-player 4.9.21 → 4.9.23

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.
@@ -58,14 +58,11 @@ shaka.polyfill.AbortController = class {
58
58
 
59
59
  /**
60
60
  * @summary A polyfill for AbortSignal, part of the AbortController API.
61
- * @implements {AbortSignal}
61
+ * @extends AbortSignal
62
62
  */
63
- shaka.polyfill.AbortController.AbortSignal =
64
- class extends shaka.util.FakeEventTarget {
63
+ shaka.polyfill.AbortController.AbortSignal = class {
65
64
  /** */
66
65
  constructor() {
67
- super();
68
-
69
66
  /** @private {boolean} */
70
67
  this.aborted_ = false;
71
68
 
@@ -74,6 +71,24 @@ class extends shaka.util.FakeEventTarget {
74
71
 
75
72
  /** @type {?function(!Event)} */
76
73
  this.onabort = null;
74
+
75
+ /** @private {!shaka.util.FakeEventTarget} */
76
+ this.eventTarget_ = new shaka.util.FakeEventTarget();
77
+ }
78
+
79
+ /** @override */
80
+ addEventListener(type, listener, options) {
81
+ return this.eventTarget_.addEventListener(type, listener, options);
82
+ }
83
+
84
+ /** @override */
85
+ removeEventListener(type, listener, options) {
86
+ return this.eventTarget_.removeEventListener(type, listener, options);
87
+ }
88
+
89
+ /** @override */
90
+ dispatchEvent(event) {
91
+ return this.eventTarget_.dispatchEvent(event);
77
92
  }
78
93
 
79
94
  /** @override */
@@ -81,12 +96,12 @@ class extends shaka.util.FakeEventTarget {
81
96
  return this.aborted_;
82
97
  }
83
98
 
84
- /** @return {*} */
99
+ /** @override */
85
100
  get reason() {
86
101
  return this.reason_;
87
102
  }
88
103
 
89
- /** @return {void} */
104
+ /** @override */
90
105
  throwIfAborted() {
91
106
  if (this.aborted_) {
92
107
  throw this.reason_;
@@ -119,21 +134,14 @@ class extends shaka.util.FakeEventTarget {
119
134
  this.dispatchEvent(event);
120
135
  }
121
136
 
122
-
123
- /**
124
- * @param {*=} reason
125
- * @return {!AbortSignal}
126
- */
137
+ /** @override */
127
138
  static abort(reason) {
128
139
  const signal = new shaka.polyfill.AbortController.AbortSignal();
129
140
  signal.doAbort_(reason);
130
141
  return signal;
131
142
  }
132
143
 
133
- /**
134
- * @param {number} timeMs
135
- * @return {!AbortSignal}
136
- */
144
+ /** @override */
137
145
  static timeout(timeMs) {
138
146
  const signal = new shaka.polyfill.AbortController.AbortSignal();
139
147
 
@@ -471,8 +471,8 @@ shaka.text.UITextDisplayer = class {
471
471
  const linesUnit = shaka.text.CueRegion.units.LINES;
472
472
  const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
473
473
  const pixelUnit = shaka.text.CueRegion.units.PX;
474
- const heightUnit = region.heightUnits == percentageUnit ? '%' : 'px';
475
- const widthUnit = region.widthUnits == percentageUnit ? '%' : 'px';
474
+ let heightUnit = region.heightUnits == percentageUnit ? '%' : 'px';
475
+ let widthUnit = region.widthUnits == percentageUnit ? '%' : 'px';
476
476
  const viewportAnchorUnit =
477
477
  region.viewportAnchorUnits == percentageUnit ? '%' : 'px';
478
478
 
@@ -481,16 +481,19 @@ shaka.text.UITextDisplayer = class {
481
481
 
482
482
  regionElement.style.position = 'absolute';
483
483
 
484
+ let regionHeight = region.height;
485
+ let regionWidth = region.width;
486
+
484
487
  if (region.heightUnits === linesUnit) {
485
- regionElement.style.height = region.height * lineHeightMultiple + '%';
486
- } else {
487
- regionElement.style.height = region.height + heightUnit;
488
+ regionHeight = region.height * lineHeightMultiple;
489
+ heightUnit = '%';
488
490
  }
489
491
  if (region.widthUnits === linesUnit) {
490
- regionElement.style.width = region.width * lineWidthMultiple + '%';
491
- } else {
492
- regionElement.style.width = region.width + widthUnit;
492
+ regionWidth = region.width * lineWidthMultiple;
493
+ widthUnit = '%';
493
494
  }
495
+ regionElement.style.height = regionHeight + heightUnit;
496
+ regionElement.style.width = regionWidth + widthUnit;
494
497
 
495
498
  if (region.viewportAnchorUnits === linesUnit) {
496
499
  // from https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#positioning-in-cea-708
@@ -498,15 +501,15 @@ shaka.text.UITextDisplayer = class {
498
501
  const windowWidth = this.aspectRatio_ === 4/3 ? 160 : 210;
499
502
  let left = region.viewportAnchorX / windowWidth * 100;
500
503
  // adjust top and left values based on the region anchor and window size
501
- top -= region.regionAnchorY * region.height * lineHeightMultiple / 100;
502
- left -= region.regionAnchorX * region.width * lineWidthMultiple / 100;
504
+ top -= region.regionAnchorY * regionHeight / 100;
505
+ left -= region.regionAnchorX * regionWidth / 100;
503
506
  regionElement.style.top = top + '%';
504
507
  regionElement.style.left = left + '%';
505
508
  } else {
506
509
  regionElement.style.top = region.viewportAnchorY -
507
- region.regionAnchorY * region.height / 100 + viewportAnchorUnit;
510
+ region.regionAnchorY * regionHeight / 100 + viewportAnchorUnit;
508
511
  regionElement.style.left = region.viewportAnchorX -
509
- region.regionAnchorX * region.width / 100 + viewportAnchorUnit;
512
+ region.regionAnchorX * regionWidth / 100 + viewportAnchorUnit;
510
513
  }
511
514
  if (region.heightUnits !== pixelUnit &&
512
515
  region.widthUnits !== pixelUnit &&
@@ -839,7 +842,7 @@ shaka.text.UITextDisplayer = class {
839
842
  if (cue.position !== null) {
840
843
  style.position = 'absolute';
841
844
  if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
842
- style.right = cue.position + '%';
845
+ style.right = (100 - cue.position) + '%';
843
846
  style.width = 'auto';
844
847
  } else {
845
848
  style.bottom = cue.position + '%';
@@ -160,9 +160,17 @@ shaka.text.VttTextParser = class {
160
160
  shaka.util.Error.Code.INVALID_TEXT_HEADER);
161
161
  }
162
162
 
163
- const mpegTime = Number(mpegTimeMatch[1]);
163
+ let mpegTime = Number(mpegTimeMatch[1]);
164
164
  const mpegTimescale = shaka.text.VttTextParser.MPEG_TIMESCALE_;
165
165
 
166
+ const rolloverSeconds =
167
+ shaka.text.VttTextParser.TS_ROLLOVER_ / mpegTimescale;
168
+ let segmentStart = time.segmentStart - time.periodStart;
169
+ while (segmentStart >= rolloverSeconds) {
170
+ segmentStart -= rolloverSeconds;
171
+ mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
172
+ }
173
+
166
174
  return time.periodStart + mpegTime / mpegTimescale - cueTime;
167
175
  }
168
176
 
@@ -1007,6 +1015,13 @@ shaka.text.VttTextParser = class {
1007
1015
  */
1008
1016
  shaka.text.VttTextParser.MPEG_TIMESCALE_ = 90000;
1009
1017
 
1018
+ /**
1019
+ * At this value, timestamps roll over in TS content.
1020
+ * @const {number}
1021
+ * @private
1022
+ */
1023
+ shaka.text.VttTextParser.TS_ROLLOVER_ = 0x200000000;
1024
+
1010
1025
  /**
1011
1026
  * @const
1012
1027
  * @private {!RegExp}
@@ -749,10 +749,21 @@ shaka.util.Platform = class {
749
749
  maxResolution.width = 1920;
750
750
  maxResolution.height = 1080;
751
751
  try {
752
+ let winRT = undefined;
753
+
754
+ // Try to access to WinRT for WebView, if it's not defined,
755
+ // try to access to WinRT for WebView2, if it's not defined either,
756
+ // let it throw.
757
+ if (typeof Windows !== 'undefined') {
758
+ winRT = Windows;
759
+ } else {
760
+ winRT = chrome.webview.hostObjects.sync.Windows;
761
+ }
762
+
752
763
  const protectionCapabilities =
753
- new Windows.Media.Protection.ProtectionCapabilities();
764
+ new winRT.Media.Protection.ProtectionCapabilities();
754
765
  const protectionResult =
755
- Windows.Media.Protection.ProtectionCapabilityResult;
766
+ winRT.Media.Protection.ProtectionCapabilityResult;
756
767
  // isTypeSupported may return "maybe", which means the operation is not
757
768
  // completed. This means we need to retry
758
769
  // https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
@@ -374,6 +374,9 @@ shaka.util.StreamUtils = class {
374
374
  goog.asserts.assert(navigator.mediaCapabilities,
375
375
  'MediaCapabilities should be valid.');
376
376
 
377
+ if (shaka.util.Platform.isXboxOne()) {
378
+ shaka.util.StreamUtils.overrideDolbyVisionCodecs(manifest.variants);
379
+ }
377
380
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
378
381
  manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
379
382
  preferredKeySystems);
@@ -400,6 +403,40 @@ shaka.util.StreamUtils = class {
400
403
  }
401
404
 
402
405
 
406
+ /**
407
+ * Maps Dolby Vision codecs to H.264 and H.265 equivalents as a workaround
408
+ * to make Dolby Vision playback work on some platforms.
409
+ *
410
+ * Mapping is done according to the relevant Dolby documentation found here:
411
+ * https://professionalsupport.dolby.com/s/article/How-to-signal-Dolby-Vision-in-MPEG-DASH?language=en_US
412
+ * @param {!Array<!shaka.extern.Variant>} variants
413
+ */
414
+ static overrideDolbyVisionCodecs(variants) {
415
+ /** @type {!Object<string, string>} */
416
+ const codecMap = {
417
+ 'dvav': 'avc3',
418
+ 'dva1': 'avc1',
419
+ 'dvhe': 'hev1',
420
+ 'dvh1': 'hvc1',
421
+ };
422
+ /** @type {!Set<!shaka.extern.Stream>} */
423
+ const streams = new Set();
424
+ for (const variant of variants) {
425
+ if (variant.video) {
426
+ streams.add(variant.video);
427
+ }
428
+ }
429
+ for (const video of streams) {
430
+ for (const dvCodec of Object.keys(codecMap)) {
431
+ if (video.codecs.includes(dvCodec)) {
432
+ video.codecs = video.codecs.replace(dvCodec, codecMap[dvCodec]);
433
+ break;
434
+ }
435
+ }
436
+ }
437
+ }
438
+
439
+
403
440
  /**
404
441
  * @param {!shaka.extern.Variant} variant
405
442
  * @param {?string} keySystem
@@ -1345,6 +1382,13 @@ shaka.util.StreamUtils = class {
1345
1382
  track.hdr = video.hdr || null;
1346
1383
  track.colorGamut = video.colorGamut || null;
1347
1384
  track.videoLayout = video.videoLayout || null;
1385
+
1386
+ if (videoCodec.includes(',')) {
1387
+ track.channelsCount = video.channelsCount;
1388
+ track.audioSamplingRate = video.audioSamplingRate;
1389
+ track.spatialAudio = video.spatialAudio;
1390
+ track.originalLanguage = video.originalLanguage;
1391
+ }
1348
1392
  }
1349
1393
 
1350
1394
  if (audio) {
package/lib/util/tXml.js CHANGED
@@ -753,11 +753,18 @@ shaka.util.TXml = class {
753
753
  // We only want the id attribute in which case
754
754
  // /'(.*?)'/ will suffice to get it.
755
755
  const idAttr = path.match(/(@id='(.*?)')/);
756
+ const tAttr = path.match(/(@t='(\d+)')/);
757
+ const numberIndex = path.match(/(@n='(\d+)')/);
756
758
  const position = path.match(/\[(\d+)\]/);
757
759
  returnPaths.push({
758
760
  name: nodeName[0],
759
761
  id: idAttr ?
760
762
  idAttr[0].match(/'(.*?)'/)[0].replace(/'/gm, '') : null,
763
+ t: tAttr ?
764
+ Number(tAttr[0].match(/'(.*?)'/)[0].replace(/'/gm, '')) : null,
765
+ n: numberIndex ?
766
+ Number(numberIndex[0].match(/'(.*?)'/)[0].replace(/'/gm, '')):
767
+ null,
761
768
  // position is counted from 1, so make it readable for devs
762
769
  position: position ? Number(position[1]) - 1 : null,
763
770
  attribute: path.split('/@')[1] || null,
@@ -787,6 +794,14 @@ shaka.util.TXml = class {
787
794
  const position = patchNode.attributes['pos'] || null;
788
795
 
789
796
  let index = lastNode.position;
797
+ if (index == null) {
798
+ if (lastNode.t !== null) {
799
+ index = TXml.nodePositionByAttribute_(nodes, 't', lastNode.t);
800
+ }
801
+ if (lastNode.n !== null) {
802
+ index = TXml.nodePositionByAttribute_(nodes, 'n', lastNode.n);
803
+ }
804
+ }
790
805
  if (index === null) {
791
806
  index = position === 'prepend' ? 0 : nodes.length;
792
807
  } else if (position === 'prepend') {
@@ -798,7 +813,7 @@ shaka.util.TXml = class {
798
813
  const attribute = lastNode.attribute;
799
814
 
800
815
  // Modify attribute
801
- if (attribute) {
816
+ if (attribute && nodes[index]) {
802
817
  TXml.modifyNodeAttribute(nodes[index], action, attribute,
803
818
  TXml.getContents(patchNode) || '');
804
819
  // Rearrange nodes
@@ -814,6 +829,28 @@ shaka.util.TXml = class {
814
829
  }
815
830
 
816
831
 
832
+ /**
833
+ * Search the node index by the t attribute
834
+ * and return the index. if not found return null
835
+ * @param {!Array<shaka.extern.xml.Node>} nodes
836
+ * @param {!string} attribute
837
+ * @param {!number} value
838
+ * @private
839
+ */
840
+ static nodePositionByAttribute_(nodes, attribute, value) {
841
+ let index = 0;
842
+ for (const node of nodes) {
843
+ const attrs = node.attributes;
844
+ const val = Number(attrs[attribute]);
845
+ if (val === value) {
846
+ return index;
847
+ }
848
+ index++;
849
+ }
850
+ return null;
851
+ }
852
+
853
+
817
854
  /**
818
855
  * @param {!shaka.extern.xml.Node} node
819
856
  * @param {string} action
@@ -875,6 +912,8 @@ shaka.util.TXml.knownNameSpaces_ = new Map([]);
875
912
  * @typedef {{
876
913
  * name: string,
877
914
  * id: ?string,
915
+ * t: ?number,
916
+ * n: ?number,
878
917
  * position: ?number,
879
918
  * attribute: ?string
880
919
  * }}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.9.21",
4
+ "version": "4.9.23",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -31,12 +31,12 @@
31
31
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
32
32
  "esprima": "^4.0.1",
33
33
  "fontfaceonload": "^1.0.2",
34
- "google-closure-compiler-java": "^20221102.0.0",
35
- "google-closure-deps": "^20221102.0.0",
34
+ "google-closure-compiler-java": "^20240317.0.0",
35
+ "google-closure-deps": "^20230802.0.0",
36
36
  "google-closure-library": "^20221102.0.0",
37
37
  "htmlhint": "^1.1.3",
38
38
  "jasmine-ajax": "^4.0.0",
39
- "jimp": "^0.16.1",
39
+ "jimp": "^0.22.12",
40
40
  "js-yaml": "^4.1.0",
41
41
  "jsdoc": "github:joeyparrish/jsdoc#2ca85bb6",
42
42
  "karma": "github:joeyparrish/karma#shaka-fixes",
@@ -50,7 +50,7 @@
50
50
  "karma-webdriver-launcher": "^1.0.8",
51
51
  "lcevc_dec.js": "^1.1.3",
52
52
  "less": "^4.2.0",
53
- "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
53
+ "less-plugin-clean-css": "^1.6.0",
54
54
  "material-design-lite": "^1.3.0",
55
55
  "open-sans-fonts": "^1.6.2",
56
56
  "postcss-less": "^6.0.0",
@@ -83,7 +83,10 @@ shaka.ui.PresentationTimeTracker = class extends shaka.ui.Element {
83
83
  const seekRangeSize = seekRange.end - seekRange.start;
84
84
  const Utils = shaka.ui.Utils;
85
85
 
86
- if (this.player.isLive()) {
86
+ if (!isFinite(seekRangeSize)) {
87
+ this.setValue_(this.localization.resolve(shaka.ui.Locales.Ids.LIVE));
88
+ this.currentTime_.disabled = true;
89
+ } else if (this.player.isLive()) {
87
90
  // The amount of time we are behind the live edge.
88
91
  const behindLive = Math.floor(seekRange.end - displayTime);
89
92
  displayTime = Math.max(0, behindLive);
@@ -259,9 +259,17 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
259
259
  if (height == 2160) {
260
260
  text = '4K';
261
261
  }
262
- const frameRate = track.frameRate;
263
- if (frameRate && (frameRate >= 50 || frameRate <= 20)) {
264
- text += Math.round(track.frameRate);
262
+ const frameRates = new Set();
263
+ for (const item of tracks) {
264
+ if (item.frameRate) {
265
+ frameRates.add(Math.round(item.frameRate));
266
+ }
267
+ }
268
+ if (frameRates.size > 1) {
269
+ const frameRate = track.frameRate;
270
+ if (frameRate && (frameRate >= 50 || frameRate <= 20)) {
271
+ text += Math.round(track.frameRate);
272
+ }
265
273
  }
266
274
  if (track.hdr == 'PQ' || track.hdr == 'HLG') {
267
275
  text += ' (HDR)';
package/ui/seek_bar.js CHANGED
@@ -470,7 +470,8 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
470
470
  const seekRangeSize = seekRange.end - seekRange.start;
471
471
 
472
472
  if (this.player.isLive() &&
473
- seekRangeSize < shaka.ui.Constants.MIN_SEEK_WINDOW_TO_SHOW_SEEKBAR) {
473
+ (seekRangeSize < shaka.ui.Constants.MIN_SEEK_WINDOW_TO_SHOW_SEEKBAR ||
474
+ !isFinite(seekRangeSize))) {
474
475
  return false;
475
476
  }
476
477