shaka-player 4.3.6 → 4.3.8

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 (66) hide show
  1. package/.github/workflows/appspot.yaml +39 -0
  2. package/.github/workflows/demo-version-index.yaml +3 -3
  3. package/CHANGELOG.md +46 -0
  4. package/CONTRIBUTORS +1 -0
  5. package/README.md +3 -0
  6. package/demo/custom.js +1 -1
  7. package/demo/demo.less +1 -0
  8. package/dist/demo.compiled.debug.js +2 -2
  9. package/dist/demo.compiled.debug.map +2 -2
  10. package/dist/demo.compiled.js +2 -2
  11. package/dist/demo.compiled.map +2 -2
  12. package/dist/demo.css +1 -1
  13. package/dist/demo.css.map +1 -1
  14. package/dist/deps.js +1 -1
  15. package/dist/locales.js +33 -33
  16. package/dist/shaka-player.compiled.d.ts +30 -0
  17. package/dist/shaka-player.compiled.debug.d.ts +30 -0
  18. package/dist/shaka-player.compiled.debug.externs.js +9 -0
  19. package/dist/shaka-player.compiled.debug.js +263 -256
  20. package/dist/shaka-player.compiled.debug.map +3 -3
  21. package/dist/shaka-player.compiled.externs.js +9 -0
  22. package/dist/shaka-player.compiled.js +901 -895
  23. package/dist/shaka-player.compiled.map +4 -4
  24. package/dist/shaka-player.ui.d.ts +30 -0
  25. package/dist/shaka-player.ui.debug.d.ts +30 -0
  26. package/dist/shaka-player.ui.debug.externs.js +9 -0
  27. package/dist/shaka-player.ui.debug.js +268 -262
  28. package/dist/shaka-player.ui.debug.map +3 -3
  29. package/dist/shaka-player.ui.externs.js +9 -0
  30. package/dist/shaka-player.ui.js +926 -920
  31. package/dist/shaka-player.ui.map +4 -4
  32. package/docs/tutorials/drm-config.md +9 -6
  33. package/docs/tutorials/welcome.md +1 -1
  34. package/externs/ima.js +34 -0
  35. package/lib/abr/simple_abr_manager.js +1 -1
  36. package/lib/cea/mp4_cea_parser.js +29 -20
  37. package/lib/dash/dash_parser.js +18 -3
  38. package/lib/hls/hls_parser.js +66 -6
  39. package/lib/hls/manifest_text_parser.js +10 -1
  40. package/lib/media/drm_engine.js +23 -14
  41. package/lib/media/gap_jumping_controller.js +24 -2
  42. package/lib/media/media_source_engine.js +10 -6
  43. package/lib/media/playhead.js +11 -0
  44. package/lib/media/region_observer.js +19 -1
  45. package/lib/media/segment_reference.js +22 -0
  46. package/lib/media/streaming_engine.js +48 -23
  47. package/lib/media/video_wrapper.js +6 -1
  48. package/lib/player.js +16 -5
  49. package/lib/polyfill/abort_controller.js +7 -0
  50. package/lib/text/vtt_text_parser.js +2 -1
  51. package/lib/util/manifest_parser_utils.js +1 -1
  52. package/lib/util/stream_utils.js +1 -0
  53. package/package.json +9 -4
  54. package/test/cea/mp4_cea_parser_unit.js +45 -3
  55. package/test/dash/dash_parser_manifest_unit.js +120 -0
  56. package/test/hls/hls_live_unit.js +3 -1
  57. package/test/hls/hls_parser_unit.js +144 -0
  58. package/test/media/media_source_engine_unit.js +30 -0
  59. package/test/media/region_observer_unit.js +2 -1
  60. package/test/media/streaming_engine_unit.js +26 -2
  61. package/test/test/assets/h265-cea-init.mp4 +0 -0
  62. package/test/test/assets/h265-cea-segment.mp4 +0 -0
  63. package/test/test/assets/multiple-trun-init.mp4 +0 -0
  64. package/test/test/assets/multiple-trun-segment.mp4 +0 -0
  65. package/test/test/util/layout_tests.js +4 -3
  66. package/ui/resolution_selection.js +7 -1
@@ -109,15 +109,15 @@ shaka.media.MediaSourceEngine = class {
109
109
  /** @private {!shaka.util.PublicPromise} */
110
110
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
111
111
 
112
+ /** @private {string} */
113
+ this.url_ = '';
114
+
112
115
  /** @private {MediaSource} */
113
116
  this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
114
117
 
115
118
  /** @type {!shaka.util.Destroyer} */
116
119
  this.destroyer_ = new shaka.util.Destroyer(() => this.doDestroy_());
117
120
 
118
- /** @private {string} */
119
- this.url_ = '';
120
-
121
121
  /** @private {boolean} */
122
122
  this.sequenceMode_ = false;
123
123
 
@@ -154,6 +154,8 @@ shaka.media.MediaSourceEngine = class {
154
154
  * @private
155
155
  */
156
156
  onSourceOpen_(p) {
157
+ goog.asserts.assert(this.url_, 'Must have object URL');
158
+
157
159
  // Release the object URL that was previously created, to prevent memory
158
160
  // leak.
159
161
  // createObjectURL creates a strong reference to the MediaSource object
@@ -580,11 +582,13 @@ shaka.media.MediaSourceEngine = class {
580
582
  * @param {boolean=} seeked True if we just seeked
581
583
  * @param {boolean=} adaptation True if we just automatically switched active
582
584
  * variant(s).
585
+ * @param {boolean=} isChunkedData True if we add to the buffer from the
586
+ * partial read of the segment.
583
587
  * @return {!Promise}
584
588
  */
585
589
  async appendBuffer(
586
590
  contentType, data, reference, hasClosedCaptions, seeked = false,
587
- adaptation = false) {
591
+ adaptation = false, isChunkedData = false) {
588
592
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
589
593
 
590
594
  if (contentType == ContentType.TEXT) {
@@ -785,13 +789,13 @@ shaka.media.MediaSourceEngine = class {
785
789
  let bufferedBefore = null;
786
790
 
787
791
  await this.enqueueOperation_(contentType, () => {
788
- if (goog.DEBUG && reference) {
792
+ if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
789
793
  bufferedBefore = this.getBuffered_(contentType);
790
794
  }
791
795
  this.append_(contentType, data);
792
796
  });
793
797
 
794
- if (goog.DEBUG && reference) {
798
+ if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
795
799
  const bufferedAfter = this.getBuffered_(contentType);
796
800
  const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
797
801
  bufferedBefore, bufferedAfter);
@@ -251,6 +251,7 @@ shaka.media.MediaSourcePlayhead = class {
251
251
  this.videoWrapper_ = new shaka.media.VideoWrapper(
252
252
  mediaElement,
253
253
  () => this.onSeeking_(),
254
+ (realStartTime) => this.onStarted_(realStartTime),
254
255
  this.getStartTime_(startTime));
255
256
 
256
257
  /** @type {shaka.util.Timer} */
@@ -390,6 +391,16 @@ shaka.media.MediaSourcePlayhead = class {
390
391
  }
391
392
  }
392
393
 
394
+ /**
395
+ * Called when the video element has started up and is listening for new seeks
396
+ *
397
+ * @param {number} startTime
398
+ * @private
399
+ */
400
+ onStarted_(startTime) {
401
+ this.gapController_.onStarted(startTime);
402
+ }
403
+
393
404
  /**
394
405
  * Handles when a seek happens on the video.
395
406
  *
@@ -27,13 +27,24 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
27
27
  * destroy the timeline.
28
28
  *
29
29
  * @param {!shaka.media.RegionTimeline} timeline
30
+ * @param {boolean} startsPastZero
30
31
  */
31
- constructor(timeline) {
32
+ constructor(timeline, startsPastZero) {
32
33
  super();
33
34
 
34
35
  /** @private {shaka.media.RegionTimeline} */
35
36
  this.timeline_ = timeline;
36
37
 
38
+ /**
39
+ * Whether the asset is expected to start at a time beyond 0 seconds.
40
+ * For example, if the asset is a live stream.
41
+ * If true, we will not start polling for regions until the playhead has
42
+ * moved past 0 seconds, to avoid bad behaviors where the current time is
43
+ * briefly 0 before we have enough data to play.
44
+ * @private {boolean}
45
+ */
46
+ this.startsPastZero_ = startsPastZero;
47
+
37
48
  /**
38
49
  * A mapping between a region and where we previously were relative to it.
39
50
  * When the value here differs from what we calculate, it means we moved and
@@ -121,6 +132,13 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
121
132
  /** @override */
122
133
  poll(positionInSeconds, wasSeeking) {
123
134
  const RegionObserver = shaka.media.RegionObserver;
135
+ if (this.startsPastZero_ && positionInSeconds == 0) {
136
+ // Don't start checking regions until the timeline has begun moving.
137
+ return;
138
+ }
139
+ // Now that we have seen the playhead go past 0, it's okay if it goes
140
+ // back there (e.g. seeking back to the start).
141
+ this.startsPastZero_ = false;
124
142
 
125
143
  for (const region of this.timeline_.regions()) {
126
144
  const previousPosition = this.oldPosition_.get(region);
@@ -243,6 +243,9 @@ shaka.media.SegmentReference = class {
243
243
  /** @type {shaka.media.SegmentReference.Status} */
244
244
  this.status = status;
245
245
 
246
+ /** @type {boolean} */
247
+ this.preload = false;
248
+
246
249
  /** @type {?shaka.extern.HlsAes128Key} */
247
250
  this.hlsAes128Key = hlsAes128Key;
248
251
 
@@ -366,6 +369,25 @@ shaka.media.SegmentReference = class {
366
369
  this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
367
370
  }
368
371
 
372
+ /**
373
+ * Mark the reference as preload.
374
+ *
375
+ * @export
376
+ */
377
+ markAsPreload() {
378
+ this.preload = true;
379
+ }
380
+
381
+ /**
382
+ * Returns true if the segment is preloaded.
383
+ *
384
+ * @return {boolean}
385
+ * @export
386
+ */
387
+ isPreload() {
388
+ return this.preload;
389
+ }
390
+
369
391
  /**
370
392
  * Set the segment's thumbnail sprite.
371
393
  *
@@ -651,10 +651,15 @@ shaka.media.StreamingEngine = class {
651
651
  const mediaState = this.mediaStates_.get(type);
652
652
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
653
653
 
654
- // Always clear the iterator since we need to start streaming from the
655
- // new time. This also happens in clearBuffer_, but if we don't clear,
656
- // we still want to reset the iterator.
657
- mediaState.segmentIterator = null;
654
+ let segment = null;
655
+ if (mediaState.segmentIterator) {
656
+ segment = mediaState.segmentIterator.current();
657
+ }
658
+ // Only reset the iterator if we seek outside the current segment.
659
+ if (!segment || segment.startTime > presentationTime ||
660
+ segment.endTime < presentationTime) {
661
+ mediaState.segmentIterator = null;
662
+ }
658
663
 
659
664
  if (!newTimeIsBuffered(type)) {
660
665
  const bufferEnd =
@@ -1314,7 +1319,8 @@ shaka.media.StreamingEngine = class {
1314
1319
  const dataToAppend = remaining.subarray(0, offset);
1315
1320
  remaining = remaining.subarray(offset);
1316
1321
  await this.append_(
1317
- mediaState, presentationTime, stream, reference, dataToAppend);
1322
+ mediaState, presentationTime, stream, reference, dataToAppend,
1323
+ /* isChunkedData= */ true);
1318
1324
  }
1319
1325
  };
1320
1326
 
@@ -1726,10 +1732,12 @@ shaka.media.StreamingEngine = class {
1726
1732
  * @param {shaka.extern.Stream} stream
1727
1733
  * @param {!shaka.media.SegmentReference} reference
1728
1734
  * @param {BufferSource} segment
1735
+ * @param {boolean=} isChunkedData
1729
1736
  * @return {!Promise}
1730
1737
  * @private
1731
1738
  */
1732
- async append_(mediaState, presentationTime, stream, reference, segment) {
1739
+ async append_(mediaState, presentationTime, stream, reference, segment,
1740
+ isChunkedData = false) {
1733
1741
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
1734
1742
 
1735
1743
  const hasClosedCaptions = stream.closedCaptions &&
@@ -1783,7 +1791,8 @@ shaka.media.StreamingEngine = class {
1783
1791
  reference,
1784
1792
  hasClosedCaptions,
1785
1793
  seeked,
1786
- adaptation);
1794
+ adaptation,
1795
+ isChunkedData);
1787
1796
  this.destroyer_.ensureNotDestroyed();
1788
1797
  shaka.log.v2(logPrefix, 'appended media segment');
1789
1798
  }
@@ -1847,7 +1856,6 @@ shaka.media.StreamingEngine = class {
1847
1856
  const messageData = box.reader.readBytes(
1848
1857
  box.reader.getLength() - box.reader.getPosition());
1849
1858
 
1850
-
1851
1859
  // See DASH sec. 5.10.3.3.1
1852
1860
  // If a DASH client detects an event message box with a scheme that is not
1853
1861
  // defined in MPD, the client is expected to ignore it.
@@ -1857,22 +1865,8 @@ shaka.media.StreamingEngine = class {
1857
1865
  // A special scheme in DASH used to signal manifest updates.
1858
1866
  if (schemeId == 'urn:mpeg:dash:event:2012') {
1859
1867
  this.playerInterface_.onManifestUpdate();
1860
- } else if (schemeId == 'https://aomedia.org/emsg/ID3') {
1861
- // See https://aomediacodec.github.io/id3-emsg/
1862
- const frames = shaka.util.Id3Utils.getID3Frames(messageData);
1863
- if (frames.length && reference) {
1864
- /** @private {shaka.extern.ID3Metadata} */
1865
- const metadata = {
1866
- cueTime: reference.startTime,
1867
- data: messageData,
1868
- frames: frames,
1869
- dts: reference.startTime,
1870
- pts: reference.startTime,
1871
- };
1872
- this.playerInterface_.onMetadata(
1873
- [metadata], /* offset= */ 0, reference.endTime);
1874
- }
1875
1868
  } else {
1869
+ // All other schemes are dispatched as a general 'emsg' event.
1876
1870
  /** @type {shaka.extern.EmsgInfo} */
1877
1871
  const emsg = {
1878
1872
  startTime: startTime,
@@ -1890,7 +1884,38 @@ shaka.media.StreamingEngine = class {
1890
1884
  const eventName = shaka.util.FakeEvent.EventName.Emsg;
1891
1885
  const data = (new Map()).set('detail', emsg);
1892
1886
  const event = new shaka.util.FakeEvent(eventName, data);
1887
+ // A user can call preventDefault() on a cancelable event.
1888
+ event.cancelable = true;
1889
+
1893
1890
  this.playerInterface_.onEvent(event);
1891
+
1892
+ if (event.defaultPrevented) {
1893
+ // If the caller uses preventDefault() on the 'emsg' event, don't
1894
+ // process any further, and don't generate an ID3 'metadata' event
1895
+ // for the same data.
1896
+ return;
1897
+ }
1898
+
1899
+ // Additionally, ID3 events generate a 'metadata' event. This is a
1900
+ // pre-parsed version of the metadata blob already dispatched in the
1901
+ // 'emsg' event.
1902
+ if (schemeId == 'https://aomedia.org/emsg/ID3' ||
1903
+ schemeId == 'https://developer.apple.com/streaming/emsg-id3') {
1904
+ // See https://aomediacodec.github.io/id3-emsg/
1905
+ const frames = shaka.util.Id3Utils.getID3Frames(messageData);
1906
+ if (frames.length && reference) {
1907
+ /** @private {shaka.extern.ID3Metadata} */
1908
+ const metadata = {
1909
+ cueTime: reference.startTime,
1910
+ data: messageData,
1911
+ frames: frames,
1912
+ dts: reference.startTime,
1913
+ pts: reference.startTime,
1914
+ };
1915
+ this.playerInterface_.onMetadata(
1916
+ [metadata], /* offset= */ 0, reference.endTime);
1917
+ }
1918
+ }
1894
1919
  }
1895
1920
  }
1896
1921
  }
@@ -27,15 +27,19 @@ shaka.media.VideoWrapper = class {
27
27
  /**
28
28
  * @param {!HTMLMediaElement} video
29
29
  * @param {function()} onSeek Called when the video seeks.
30
+ * @param {function(number)} onStarted Called when the video has started.
30
31
  * @param {number} startTime The time to start at.
31
32
  */
32
- constructor(video, onSeek, startTime) {
33
+ constructor(video, onSeek, onStarted, startTime) {
33
34
  /** @private {HTMLMediaElement} */
34
35
  this.video_ = video;
35
36
 
36
37
  /** @private {function()} */
37
38
  this.onSeek_ = onSeek;
38
39
 
40
+ /** @private {function(number)} */
41
+ this.onStarted_ = onStarted;
42
+
39
43
  /** @private {number} */
40
44
  this.startTime_ = startTime;
41
45
 
@@ -165,6 +169,7 @@ shaka.media.VideoWrapper = class {
165
169
  this.started_ = true;
166
170
 
167
171
  this.eventManager_.listen(this.video_, 'seeking', () => this.onSeek_());
172
+ this.onStarted_(this.video_.currentTime);
168
173
  }
169
174
  };
170
175
 
package/lib/player.js CHANGED
@@ -99,7 +99,9 @@ goog.requireType('shaka.routing.Payload');
99
99
 
100
100
  /**
101
101
  * @event shaka.Player.EmsgEvent
102
- * @description Fired when a non-typical emsg is found in a segment.
102
+ * @description Fired when an emsg box is found in a segment.
103
+ * If the application calls preventDefault() on this event, further parsing
104
+ * will not happen, and no 'metadata' event will be raised for ID3 payloads.
103
105
  * @property {string} type
104
106
  * 'emsg'
105
107
  * @property {shaka.extern.EmsgInfo} detail
@@ -2191,7 +2193,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2191
2193
  }
2192
2194
 
2193
2195
  this.playhead_ = this.createPlayhead(has.startTime);
2194
- this.playheadObservers_ = this.createPlayheadObserversForMSE_();
2196
+ this.playheadObservers_ =
2197
+ this.createPlayheadObserversForMSE_(has.startTimeOfLoad);
2195
2198
 
2196
2199
  // We need to start the buffer management code near the end because it will
2197
2200
  // set the initial buffering state and that depends on other components
@@ -2918,17 +2921,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2918
2921
  * Create the observers for MSE playback. These observers are responsible for
2919
2922
  * notifying the app and player of specific events during MSE playback.
2920
2923
  *
2924
+ * @param {number} startTimeOfLoad
2921
2925
  * @return {!shaka.media.PlayheadObserverManager}
2922
2926
  * @private
2923
2927
  */
2924
- createPlayheadObserversForMSE_() {
2928
+ createPlayheadObserversForMSE_(startTimeOfLoad) {
2925
2929
  goog.asserts.assert(this.manifest_, 'Must have manifest');
2926
2930
  goog.asserts.assert(this.regionTimeline_, 'Must have region timeline');
2927
2931
  goog.asserts.assert(this.video_, 'Must have video element');
2928
2932
 
2933
+ const startsPastZero = this.isLive() || startTimeOfLoad > 0;
2934
+
2929
2935
  // Create the region observer. This will allow us to notify the app when we
2930
2936
  // move in and out of timeline regions.
2931
- const regionObserver = new shaka.media.RegionObserver(this.regionTimeline_);
2937
+ const regionObserver = new shaka.media.RegionObserver(
2938
+ this.regionTimeline_, startsPastZero);
2932
2939
 
2933
2940
  regionObserver.addEventListener('enter', (event) => {
2934
2941
  /** @type {shaka.extern.TimelineRegionInfo} */
@@ -6066,6 +6073,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6066
6073
  error.category != shaka.util.Error.Category.NETWORK) {
6067
6074
  return false;
6068
6075
  }
6076
+ if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
6077
+ return false;
6078
+ }
6079
+ goog.asserts.assert(this.manifest_, 'Should have manifest!');
6069
6080
 
6070
6081
  if (!navigator.onLine) {
6071
6082
  // Don't restrict variants if we're completely offline, or else we end up
@@ -7005,7 +7016,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7005
7016
  * @define {string} A version number taken from git at compile time.
7006
7017
  * @export
7007
7018
  */
7008
- shaka.Player.version = 'v4.3.6-uncompiled';
7019
+ shaka.Player.version = 'v4.3.8-uncompiled';
7009
7020
 
7010
7021
  // Initialize the deprecation system using the version string we just set
7011
7022
  // on the player.
@@ -86,6 +86,13 @@ class extends shaka.util.FakeEventTarget {
86
86
  return this.reason_;
87
87
  }
88
88
 
89
+ /** @return {void} */
90
+ throwIfAborted() {
91
+ if (this.aborted_) {
92
+ throw this.reason_;
93
+ }
94
+ }
95
+
89
96
  /**
90
97
  * @param {*} reason
91
98
  * @private
@@ -785,7 +785,8 @@ shaka.text.VttTextParser = class {
785
785
  } else if ((results = /^size:([\d.]+)%$/.exec(word))) {
786
786
  cue.size = Number(results[1]);
787
787
  } else if ((results =
788
- /^position:([\d.]+)%(?:,(line-left|line-right|center|start|end))?$/
788
+ // eslint-disable-next-line max-len
789
+ /^position:([\d.]+)%(?:,(line-left|line-right|middle|center|start|end))?$/
789
790
  .exec(word))) {
790
791
  cue.position = Number(results[1]);
791
792
  if (results[2]) {
@@ -168,7 +168,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
168
168
  /^hvc/,
169
169
  /^vp0?[89]/,
170
170
  /^av01/,
171
- /^dvhe/,
171
+ /^dvh/,
172
172
  ];
173
173
 
174
174
 
@@ -1068,6 +1068,7 @@ shaka.util.StreamUtils = class {
1068
1068
  track.frameRate = video.frameRate || null;
1069
1069
  track.pixelAspectRatio = video.pixelAspectRatio || null;
1070
1070
  track.videoBandwidth = video.bandwidth || null;
1071
+ track.hdr = video.hdr || null;
1071
1072
  }
1072
1073
 
1073
1074
  if (audio) {
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.3.6",
4
+ "version": "4.3.8",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -18,6 +18,7 @@
18
18
  "awesomplete": "^1.1.5",
19
19
  "babel-plugin-istanbul": "^6.1.1",
20
20
  "cajon": "^0.4.4",
21
+ "clean-package": "^2.2.0",
21
22
  "code-prettify": "^0.1.0",
22
23
  "color-themes-for-google-code-prettify": "^2.0.4",
23
24
  "core-js": "^3.21.1",
@@ -41,7 +42,7 @@
41
42
  "karma-coverage": "^2.2.0",
42
43
  "karma-jasmine": "^4.0.1",
43
44
  "karma-jasmine-ajax": "^0.1.13",
44
- "karma-local-wd-launcher": "^1.6.1",
45
+ "karma-local-wd-launcher": "^1.6.2",
45
46
  "karma-opera-launcher": "^1.0.0",
46
47
  "karma-sourcemap-loader": "^0.3.8",
47
48
  "karma-spec-reporter": "^0.0.34",
@@ -94,12 +95,16 @@
94
95
  },
95
96
  "license": "Apache-2.0",
96
97
  "scripts": {
98
+ "prepack": "clean-package",
99
+ "postpack": "clean-package restore",
97
100
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
98
101
  },
99
102
  "dependencies": {
100
103
  "eme-encryption-scheme-polyfill": "^2.1.1"
101
104
  },
102
- "engines": {
103
- "node": ">=14"
105
+ "clean-package": {
106
+ "remove": [
107
+ "engines"
108
+ ]
104
109
  }
105
110
  }
@@ -7,20 +7,43 @@
7
7
  describe('Mp4CeaParser', () => {
8
8
  const ceaInitSegmentUri = '/base/test/test/assets/cea-init.mp4';
9
9
  const ceaSegmentUri = '/base/test/test/assets/cea-segment.mp4';
10
+ const h265ceaInitSegmentUri = '/base/test/test/assets/h265-cea-init.mp4';
11
+ const h265ceaSegmentUri = '/base/test/test/assets/h265-cea-segment.mp4';
12
+ const multipleTrunInitSegmentUri =
13
+ '/base/test/test/assets/multiple-trun-init.mp4';
14
+ const multipleTrunSegmentUri =
15
+ '/base/test/test/assets/multiple-trun-segment.mp4';
10
16
  const Util = shaka.test.Util;
11
17
 
12
18
  /** @type {!ArrayBuffer} */
13
19
  let ceaInitSegment;
14
20
  /** @type {!ArrayBuffer} */
15
21
  let ceaSegment;
22
+ /** @type {!ArrayBuffer} */
23
+ let h265ceaInitSegment;
24
+ /** @type {!ArrayBuffer} */
25
+ let h265ceaSegment;
26
+ /** @type {!ArrayBuffer} */
27
+ let multipleTrunInitSegment;
28
+ /** @type {!ArrayBuffer} */
29
+ let multipleTrunSegment;
16
30
 
17
31
  beforeAll(async () => {
18
- const responses = await Promise.all([
32
+ [
33
+ ceaInitSegment,
34
+ ceaSegment,
35
+ h265ceaInitSegment,
36
+ h265ceaSegment,
37
+ multipleTrunInitSegment,
38
+ multipleTrunSegment,
39
+ ] = await Promise.all([
19
40
  shaka.test.Util.fetch(ceaInitSegmentUri),
20
41
  shaka.test.Util.fetch(ceaSegmentUri),
42
+ shaka.test.Util.fetch(h265ceaInitSegmentUri),
43
+ shaka.test.Util.fetch(h265ceaSegmentUri),
44
+ shaka.test.Util.fetch(multipleTrunInitSegmentUri),
45
+ shaka.test.Util.fetch(multipleTrunSegmentUri),
21
46
  ]);
22
- ceaInitSegment = responses[0];
23
- ceaSegment = responses[1];
24
47
  });
25
48
 
26
49
  /**
@@ -70,6 +93,25 @@ describe('Mp4CeaParser', () => {
70
93
  .toEqual(expectedCea708Packet);
71
94
  });
72
95
 
96
+ it('parses cea data from an h265 mp4 stream', () => {
97
+ const ceaParser = new shaka.cea.Mp4CeaParser();
98
+
99
+ ceaParser.init(h265ceaInitSegment);
100
+ const ceaPackets = ceaParser.parse(h265ceaSegment);
101
+ expect(ceaPackets).toBeDefined();
102
+ expect(ceaPackets.length).toBe(60);
103
+ });
104
+
105
+ it('parses cea data from a segment with multiple trun boxes', () => {
106
+ const ceaParser = new shaka.cea.Mp4CeaParser();
107
+
108
+ ceaParser.init(multipleTrunInitSegment);
109
+ const ceaPackets = ceaParser.parse(multipleTrunSegment);
110
+ // The first trun box references samples with 48 CEA packets.
111
+ // The second trun box references samples with 132 more, for a total of 180.
112
+ expect(ceaPackets.length).toBe(180);
113
+ });
114
+
73
115
  it('parses an invalid init segment', () => {
74
116
  const cea708Parser = new shaka.cea.Mp4CeaParser();
75
117
 
@@ -2023,6 +2023,66 @@ describe('DashParser Manifest', () => {
2023
2023
  expect(stream.hdr).toBe('PQ');
2024
2024
  });
2025
2025
 
2026
+ it('supports SupplementalProperty MatrixCoefficients', async () => {
2027
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2028
+ const scheme = cicpScheme('MatrixCoefficients');
2029
+ const manifestText = [
2030
+ '<MPD minBufferTime="PT75S">',
2031
+ ' <Period id="1" duration="PT30S">',
2032
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2033
+ ` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
2034
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2035
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2036
+ ' <SegmentBase indexRange="100-200" />',
2037
+ ' </Representation>',
2038
+ ' </AdaptationSet>',
2039
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2040
+ ' <Representation id="audio-en">',
2041
+ ' <BaseURL>a-en.mp4</BaseURL>',
2042
+ ' <SegmentBase indexRange="100-200" />',
2043
+ ' </Representation>',
2044
+ ' </AdaptationSet>',
2045
+ ' </Period>',
2046
+ '</MPD>',
2047
+ ].join('\n');
2048
+
2049
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2050
+
2051
+ /** @type {shaka.extern.Manifest} */
2052
+ const manifest = await parser.start('dummy://foo', playerInterface);
2053
+ expect(manifest.variants.length).toBe(1);
2054
+ });
2055
+
2056
+ it('supports SupplementalProperty ColourPrimaries', async () => {
2057
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2058
+ const scheme = cicpScheme('ColourPrimaries');
2059
+ const manifestText = [
2060
+ '<MPD minBufferTime="PT75S">',
2061
+ ' <Period id="1" duration="PT30S">',
2062
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2063
+ ` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
2064
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2065
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2066
+ ' <SegmentBase indexRange="100-200" />',
2067
+ ' </Representation>',
2068
+ ' </AdaptationSet>',
2069
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2070
+ ' <Representation id="audio-en">',
2071
+ ' <BaseURL>a-en.mp4</BaseURL>',
2072
+ ' <SegmentBase indexRange="100-200" />',
2073
+ ' </Representation>',
2074
+ ' </AdaptationSet>',
2075
+ ' </Period>',
2076
+ '</MPD>',
2077
+ ].join('\n');
2078
+
2079
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2080
+
2081
+ /** @type {shaka.extern.Manifest} */
2082
+ const manifest = await parser.start('dummy://foo', playerInterface);
2083
+ expect(manifest.variants.length).toBe(1);
2084
+ });
2085
+
2026
2086
  it('supports HDR signaling via EssentialProperty', async () => {
2027
2087
  // (DASH-IF IOP v4.3 6.2.5.1.)
2028
2088
  const hdrScheme = cicpScheme('TransferCharacteristics');
@@ -2056,6 +2116,66 @@ describe('DashParser Manifest', () => {
2056
2116
  expect(stream.hdr).toBe('HLG');
2057
2117
  });
2058
2118
 
2119
+ it('supports EssentialProperty MatrixCoefficients', async () => {
2120
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2121
+ const hdrScheme = cicpScheme('MatrixCoefficients');
2122
+ const manifestText = [
2123
+ '<MPD minBufferTime="PT75S">',
2124
+ ' <Period id="1" duration="PT30S">',
2125
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2126
+ ` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
2127
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2128
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2129
+ ' <SegmentBase indexRange="100-200" />',
2130
+ ' </Representation>',
2131
+ ' </AdaptationSet>',
2132
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2133
+ ' <Representation id="audio-en">',
2134
+ ' <BaseURL>a-en.mp4</BaseURL>',
2135
+ ' <SegmentBase indexRange="100-200" />',
2136
+ ' </Representation>',
2137
+ ' </AdaptationSet>',
2138
+ ' </Period>',
2139
+ '</MPD>',
2140
+ ].join('\n');
2141
+
2142
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2143
+
2144
+ /** @type {shaka.extern.Manifest} */
2145
+ const manifest = await parser.start('dummy://foo', playerInterface);
2146
+ expect(manifest.variants.length).toBe(1);
2147
+ });
2148
+
2149
+ it('supports EssentialProperty ColourPrimaries', async () => {
2150
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2151
+ const hdrScheme = cicpScheme('ColourPrimaries');
2152
+ const manifestText = [
2153
+ '<MPD minBufferTime="PT75S">',
2154
+ ' <Period id="1" duration="PT30S">',
2155
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2156
+ ` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
2157
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2158
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2159
+ ' <SegmentBase indexRange="100-200" />',
2160
+ ' </Representation>',
2161
+ ' </AdaptationSet>',
2162
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2163
+ ' <Representation id="audio-en">',
2164
+ ' <BaseURL>a-en.mp4</BaseURL>',
2165
+ ' <SegmentBase indexRange="100-200" />',
2166
+ ' </Representation>',
2167
+ ' </AdaptationSet>',
2168
+ ' </Period>',
2169
+ '</MPD>',
2170
+ ].join('\n');
2171
+
2172
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2173
+
2174
+ /** @type {shaka.extern.Manifest} */
2175
+ const manifest = await parser.start('dummy://foo', playerInterface);
2176
+ expect(manifest.variants.length).toBe(1);
2177
+ });
2178
+
2059
2179
  it('supports SDR signalling via EssentialProperty', async () => {
2060
2180
  // (DASH-IF IOP v4.3 6.2.5.1.)
2061
2181
  const scheme = cicpScheme('TransferCharacteristics');