shaka-player 4.2.10 → 4.2.11

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 (35) hide show
  1. package/.github/workflows/demo-version-index.yaml +3 -3
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +3 -0
  4. package/demo/demo.less +1 -0
  5. package/dist/demo.css +1 -1
  6. package/dist/demo.css.map +1 -1
  7. package/dist/locales.js +30 -30
  8. package/dist/shaka-player.compiled.d.ts +8 -0
  9. package/dist/shaka-player.compiled.debug.d.ts +8 -0
  10. package/dist/shaka-player.compiled.debug.externs.js +9 -0
  11. package/dist/shaka-player.compiled.debug.js +40 -38
  12. package/dist/shaka-player.compiled.debug.map +3 -3
  13. package/dist/shaka-player.compiled.externs.js +9 -0
  14. package/dist/shaka-player.compiled.js +425 -424
  15. package/dist/shaka-player.compiled.map +3 -3
  16. package/dist/shaka-player.ui.d.ts +8 -0
  17. package/dist/shaka-player.ui.debug.d.ts +8 -0
  18. package/dist/shaka-player.ui.debug.externs.js +9 -0
  19. package/dist/shaka-player.ui.debug.js +166 -164
  20. package/dist/shaka-player.ui.debug.map +3 -3
  21. package/dist/shaka-player.ui.externs.js +9 -0
  22. package/dist/shaka-player.ui.js +234 -233
  23. package/dist/shaka-player.ui.map +3 -3
  24. package/docs/tutorials/welcome.md +1 -1
  25. package/lib/hls/hls_parser.js +6 -0
  26. package/lib/hls/manifest_text_parser.js +10 -1
  27. package/lib/media/drm_engine.js +9 -6
  28. package/lib/media/media_source_engine.js +6 -4
  29. package/lib/media/segment_reference.js +22 -0
  30. package/lib/media/streaming_engine.js +9 -4
  31. package/lib/player.js +5 -1
  32. package/package.json +9 -3
  33. package/test/hls/hls_live_unit.js +3 -1
  34. package/test/media/media_source_engine_unit.js +31 -0
  35. package/test/test/util/layout_tests.js +4 -3
@@ -29,7 +29,7 @@ You can build Shaka on Linux, Windows, or Mac.
29
29
  To get the sources and compile the library, you will need:
30
30
  * {@link https://git-scm.com/downloads Git v1.9+}
31
31
  * {@link https://www.python.org/downloads/ Python v2.7 or v3.5+}
32
- * {@link https://java.com/en/download/ Java Runtime Environment v14+}
32
+ * {@link https://learn.microsoft.com/en-us/java/openjdk/download Java Runtime Environment v14+}
33
33
  * {@link https://nodejs.org/en/download/ NodeJS v14+}
34
34
  * A local web server, such as {@link https://httpd.apache.org/ Apache}
35
35
  * _NOTE: A local web server is required because browsers place restrictions
@@ -2131,6 +2131,9 @@ shaka.hls.HlsParser = class {
2131
2131
  /* tileDuration= */ null,
2132
2132
  /* syncTime= */ null,
2133
2133
  partialStatus);
2134
+ if (item.name == 'EXT-X-PRELOAD-HINT') {
2135
+ partial.markAsPreload();
2136
+ }
2134
2137
  partialSegmentRefs.push(partial);
2135
2138
  } // for-loop of hlsSegment.partialSegments
2136
2139
  }
@@ -2451,6 +2454,9 @@ shaka.hls.HlsParser = class {
2451
2454
  * @private
2452
2455
  */
2453
2456
  variableSubstitution_(uri, variables) {
2457
+ if (!variables.size) {
2458
+ return uri;
2459
+ }
2454
2460
  let newUri = String(uri).replace(/%7B/g, '{').replace(/%7D/g, '}');
2455
2461
 
2456
2462
  const uriVariables = newUri.match(/{\$\w*}/g);
@@ -158,7 +158,15 @@ shaka.hls.ManifestTextParser = class {
158
158
  partialSegmentTags.push(tag);
159
159
  } else if (tag.name == 'EXT-X-PRELOAD-HINT') {
160
160
  if (tag.getAttributeValue('TYPE') == 'PART') {
161
- partialSegmentTags.push(tag);
161
+ // Note: BYTERANGE-START without BYTERANGE-LENGTH is being
162
+ // ignored.
163
+ if (tag.getAttributeValue('BYTERANGE-START') != null) {
164
+ if (tag.getAttributeValue('BYTERANGE-LENGTH') != null) {
165
+ partialSegmentTags.push(tag);
166
+ }
167
+ } else {
168
+ partialSegmentTags.push(tag);
169
+ }
162
170
  } else if (tag.getAttributeValue('TYPE') == 'MAP') {
163
171
  // Rename the Preload Hint tag to be a Map tag.
164
172
  tag.setName('EXT-X-MAP');
@@ -300,6 +308,7 @@ shaka.hls.ManifestTextParser.MEDIA_PLAYLIST_TAGS = [
300
308
  'EXT-X-ENDLIST',
301
309
  'EXT-X-SERVER-CONTROL',
302
310
  'EXT-X-SKIP',
311
+ 'EXT-X-PART-INF',
303
312
  ];
304
313
 
305
314
 
@@ -133,6 +133,9 @@ shaka.media.DrmEngine = class {
133
133
 
134
134
  /** @private {Promise} */
135
135
  this.mediaKeysAttached_ = null;
136
+
137
+ /** @private {?shaka.extern.InitDataOverride} */
138
+ this.manifestInitData_ = null;
136
139
  }
137
140
 
138
141
  /** @override */
@@ -480,10 +483,10 @@ shaka.media.DrmEngine = class {
480
483
  () => this.closeOpenSessions_());
481
484
  }
482
485
 
483
- const manifestInitData = this.currentDrmInfo_ ?
484
- this.currentDrmInfo_.initData.find(
486
+ this.manifestInitData_ = this.currentDrmInfo_ ?
487
+ (this.currentDrmInfo_.initData.find(
485
488
  (initDataOverride) => initDataOverride.initData.length > 0,
486
- ) : null;
489
+ ) || null) : null;
487
490
 
488
491
  /**
489
492
  * We can attach media keys before the playback actually begins when:
@@ -491,7 +494,7 @@ shaka.media.DrmEngine = class {
491
494
  * - Some initData already has been generated (through the manifest)
492
495
  * - In case of an offline session
493
496
  */
494
- if (manifestInitData ||
497
+ if (this.manifestInitData_ ||
495
498
  this.currentDrmInfo_.keySystem !== 'com.apple.fps' ||
496
499
  this.offlineSessionIds_.length) {
497
500
  await this.attachMediaKeys_();
@@ -504,7 +507,7 @@ shaka.media.DrmEngine = class {
504
507
  // Also suppress 'encrypted' events when parsing in-band ppsh
505
508
  // from media segments because that serves the same purpose as the
506
509
  // 'encrypted' events.
507
- if (!manifestInitData && !this.offlineSessionIds_.length &&
510
+ if (!this.manifestInitData_ && !this.offlineSessionIds_.length &&
508
511
  !this.config_.parseInbandPsshEnabled) {
509
512
  this.eventManager_.listen(
510
513
  this.video_, 'encrypted', (e) => this.onEncryptedEvent_(e));
@@ -2353,7 +2356,7 @@ shaka.media.DrmEngine = class {
2353
2356
  * @return {!Promise<void>}
2354
2357
  */
2355
2358
  parseInbandPssh(contentType, mediaSegment) {
2356
- if (!this.config_.parseInbandPsshEnabled) {
2359
+ if (!this.config_.parseInbandPsshEnabled || this.manifestInitData_) {
2357
2360
  return Promise.resolve();
2358
2361
  }
2359
2362
 
@@ -99,15 +99,15 @@ shaka.media.MediaSourceEngine = class {
99
99
  /** @private {!shaka.util.PublicPromise} */
100
100
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
101
101
 
102
+ /** @private {string} */
103
+ this.url_ = '';
104
+
102
105
  /** @private {MediaSource} */
103
106
  this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
104
107
 
105
108
  /** @type {!shaka.util.Destroyer} */
106
109
  this.destroyer_ = new shaka.util.Destroyer(() => this.doDestroy_());
107
110
 
108
- /** @private {string} */
109
- this.url_ = '';
110
-
111
111
  /** @private {boolean} */
112
112
  this.sequenceMode_ = false;
113
113
 
@@ -144,6 +144,8 @@ shaka.media.MediaSourceEngine = class {
144
144
  * @private
145
145
  */
146
146
  onSourceOpen_(p) {
147
+ goog.asserts.assert(this.url_, 'Must have object URL');
148
+
147
149
  // Release the object URL that was previously created, to prevent memory
148
150
  // leak.
149
151
  // createObjectURL creates a strong reference to the MediaSource object
@@ -710,7 +712,7 @@ shaka.media.MediaSourceEngine = class {
710
712
  this.append_(contentType, data);
711
713
  });
712
714
 
713
- if (goog.DEBUG && reference) {
715
+ if (goog.DEBUG && reference && !reference.isPreload()) {
714
716
  const bufferedAfter = this.getBuffered_(contentType);
715
717
  const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
716
718
  bufferedBefore, bufferedAfter);
@@ -238,6 +238,9 @@ shaka.media.SegmentReference = class {
238
238
  /** @type {shaka.media.SegmentReference.Status} */
239
239
  this.status = status;
240
240
 
241
+ /** @type {boolean} */
242
+ this.preload = false;
243
+
241
244
  /** @type {?shaka.extern.HlsAes128Key} */
242
245
  this.hlsAes128Key = hlsAes128Key;
243
246
 
@@ -358,6 +361,25 @@ shaka.media.SegmentReference = class {
358
361
  this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
359
362
  }
360
363
 
364
+ /**
365
+ * Mark the reference as preload.
366
+ *
367
+ * @export
368
+ */
369
+ markAsPreload() {
370
+ this.preload = true;
371
+ }
372
+
373
+ /**
374
+ * Returns true if the segment is preloaded.
375
+ *
376
+ * @return {boolean}
377
+ * @export
378
+ */
379
+ isPreload() {
380
+ return this.preload;
381
+ }
382
+
361
383
  /**
362
384
  * Offset the segment reference by a fixed amount.
363
385
  *
@@ -640,10 +640,15 @@ shaka.media.StreamingEngine = class {
640
640
  const mediaState = this.mediaStates_.get(type);
641
641
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
642
642
 
643
- // Always clear the iterator since we need to start streaming from the
644
- // new time. This also happens in clearBuffer_, but if we don't clear,
645
- // we still want to reset the iterator.
646
- mediaState.segmentIterator = null;
643
+ let segment = null;
644
+ if (mediaState.segmentIterator) {
645
+ segment = mediaState.segmentIterator.current();
646
+ }
647
+ // Only reset the iterator if we seek outside the current segment.
648
+ if (!segment || segment.startTime > presentationTime ||
649
+ segment.endTime < presentationTime) {
650
+ mediaState.segmentIterator = null;
651
+ }
647
652
 
648
653
  if (!newTimeIsBuffered(type)) {
649
654
  const bufferEnd =
package/lib/player.js CHANGED
@@ -5686,6 +5686,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5686
5686
  error.category != shaka.util.Error.Category.NETWORK) {
5687
5687
  return false;
5688
5688
  }
5689
+ if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
5690
+ return false;
5691
+ }
5692
+ goog.asserts.assert(this.manifest_, 'Should have manifest!');
5689
5693
 
5690
5694
  if (!navigator.onLine) {
5691
5695
  // Don't restrict variants if we're completely offline, or else we end up
@@ -6626,7 +6630,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6626
6630
  * @define {string} A version number taken from git at compile time.
6627
6631
  * @export
6628
6632
  */
6629
- shaka.Player.version = 'v4.2.10-uncompiled';
6633
+ shaka.Player.version = 'v4.2.11-uncompiled';
6630
6634
 
6631
6635
  // Initialize the deprecation system using the version string we just set
6632
6636
  // on the player.
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.2.10",
4
+ "version": "4.2.11",
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",
@@ -66,6 +67,7 @@
66
67
  "demoDeps": [
67
68
  "awesomplete/awesomplete.css",
68
69
  "awesomplete/awesomplete.min.js",
70
+ "codem-isoboxer/dist/iso_boxer.min.js",
69
71
  "dialog-polyfill/dialog-polyfill.css",
70
72
  "dialog-polyfill/dist/dialog-polyfill.js",
71
73
  "eme-encryption-scheme-polyfill/index.js",
@@ -91,12 +93,16 @@
91
93
  },
92
94
  "license": "Apache-2.0",
93
95
  "scripts": {
96
+ "prepack": "clean-package",
97
+ "postpack": "clean-package restore",
94
98
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
95
99
  },
96
100
  "dependencies": {
97
101
  "eme-encryption-scheme-polyfill": "^2.1.1"
98
102
  },
99
- "engines": {
100
- "node": ">=14"
103
+ "clean-package": {
104
+ "remove": [
105
+ "engines"
106
+ ]
101
107
  }
102
108
  }
@@ -560,7 +560,8 @@ describe('HlsParser live', () => {
560
560
  // partialRef3
561
561
  '#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
562
562
  // preloadRef
563
- '#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
563
+ '#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210,',
564
+ 'BYTERANGE-LENGTH=210\n',
564
565
  ].join('');
565
566
 
566
567
  const partialRef = makeReference(
@@ -583,6 +584,7 @@ describe('HlsParser live', () => {
583
584
  const preloadRef = makeReference(
584
585
  'test:/partial.mp4', 6, 7.5, /* syncTime= */ null,
585
586
  /* baseUri= */ '', /* startByte= */ 210, /* endByte= */ null);
587
+ preloadRef.markAsPreload();
586
588
 
587
589
  // ref2 is not fully published yet, so it doesn't have a segment uri.
588
590
  const ref2 = makeReference(
@@ -165,9 +165,12 @@ describe('MediaSourceEngine', () => {
165
165
  describe('constructor', () => {
166
166
  const originalCreateObjectURL =
167
167
  shaka.media.MediaSourceEngine.createObjectURL;
168
+ const originalRevokeObjectURL = window.URL.revokeObjectURL;
168
169
  const originalMediaSource = window.MediaSource;
169
170
  /** @type {jasmine.Spy} */
170
171
  let createObjectURLSpy;
172
+ /** @type {jasmine.Spy} */
173
+ let revokeObjectURLSpy;
171
174
 
172
175
  beforeEach(async () => {
173
176
  // Mock out MediaSource so we can test the production version of
@@ -182,6 +185,9 @@ describe('MediaSourceEngine', () => {
182
185
  shaka.media.MediaSourceEngine.createObjectURL =
183
186
  Util.spyFunc(createObjectURLSpy);
184
187
 
188
+ revokeObjectURLSpy = jasmine.createSpy('revokeObjectURL');
189
+ window.URL.revokeObjectURL = Util.spyFunc(revokeObjectURLSpy);
190
+
185
191
  const mediaSourceSpy = jasmine.createSpy('MediaSource');
186
192
  // Because this is a fake constructor, it must be callable with "new".
187
193
  // This will cause jasmine to invoke the callback with "new" as well, so
@@ -199,6 +205,7 @@ describe('MediaSourceEngine', () => {
199
205
  afterAll(() => {
200
206
  shaka.media.MediaSourceEngine.createObjectURL = originalCreateObjectURL;
201
207
  window.MediaSource = originalMediaSource;
208
+ window.URL.revokeObjectURL = originalRevokeObjectURL;
202
209
  });
203
210
 
204
211
  it('creates a MediaSource object and sets video.src', () => {
@@ -211,6 +218,30 @@ describe('MediaSourceEngine', () => {
211
218
  expect(createObjectURLSpy).toHaveBeenCalled();
212
219
  expect(mockVideo.src).toBe('blob:foo');
213
220
  });
221
+
222
+ it('revokes object URL after MediaSource opens', () => {
223
+ let onSourceOpenListener;
224
+
225
+ mockMediaSource.addEventListener.and.callFake((event, callback, _) => {
226
+ if (event == 'sourceopen') {
227
+ onSourceOpenListener = callback;
228
+ }
229
+ });
230
+
231
+ mediaSourceEngine = new shaka.media.MediaSourceEngine(
232
+ video,
233
+ new shaka.test.FakeClosedCaptionParser(),
234
+ new shaka.test.FakeTextDisplayer());
235
+
236
+ expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(1);
237
+ expect(mockMediaSource.addEventListener.calls.mostRecent().args[0])
238
+ .toBe('sourceopen');
239
+ expect(typeof onSourceOpenListener).toBe(typeof Function);
240
+
241
+ onSourceOpenListener();
242
+
243
+ expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:foo');
244
+ });
214
245
  });
215
246
 
216
247
  describe('init', () => {
@@ -43,9 +43,10 @@ shaka.test.LayoutTests = class {
43
43
  });
44
44
  });
45
45
 
46
- // Wait one extra tick to make sure the font rendering on the page has been
47
- // updated. Without this, we saw some rare test flake in Firefox on Mac.
48
- await shaka.test.Util.shortDelay();
46
+ // Wait one extra second to make sure the font rendering on the page has
47
+ // been updated. Without this, we saw some rare test flake in Firefox on
48
+ // Mac.
49
+ await shaka.test.Util.delay(1);
49
50
  }
50
51
 
51
52
  /**