shaka-player 3.3.4 → 3.3.7

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 (80) hide show
  1. package/.github/workflows/build-and-test.yaml +0 -1
  2. package/.github/workflows/release-please.yaml +4 -3
  3. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  4. package/AUTHORS +1 -0
  5. package/CHANGELOG.md +38 -0
  6. package/CONTRIBUTORS +1 -0
  7. package/README.md +4 -4
  8. package/build/README.md +2 -2
  9. package/build/generateTsDefs.py +0 -2
  10. package/build/test.py +8 -2
  11. package/build/types/core +0 -1
  12. package/demo/demo.less +0 -1
  13. package/demo/main.js +3 -3
  14. package/dist/controls.css +3 -3
  15. package/dist/controls.css.map +1 -1
  16. package/dist/demo.compiled.debug.js +2 -2
  17. package/dist/demo.compiled.debug.map +2 -2
  18. package/dist/demo.compiled.js +2 -2
  19. package/dist/demo.compiled.map +2 -2
  20. package/dist/demo.css +1 -1
  21. package/dist/demo.css.map +1 -1
  22. package/dist/deps.js +2 -3
  23. package/dist/locales.js +31 -31
  24. package/dist/shaka-player.compiled.d.ts +1 -2
  25. package/dist/shaka-player.compiled.debug.d.ts +1 -2
  26. package/dist/shaka-player.compiled.debug.externs.js +1 -0
  27. package/dist/shaka-player.compiled.debug.js +153 -151
  28. package/dist/shaka-player.compiled.debug.map +4 -4
  29. package/dist/shaka-player.compiled.externs.js +1 -0
  30. package/dist/shaka-player.compiled.js +845 -843
  31. package/dist/shaka-player.compiled.map +4 -4
  32. package/dist/shaka-player.ui.d.ts +1 -2
  33. package/dist/shaka-player.ui.debug.d.ts +1 -2
  34. package/dist/shaka-player.ui.debug.externs.js +1 -0
  35. package/dist/shaka-player.ui.debug.js +225 -223
  36. package/dist/shaka-player.ui.debug.map +4 -4
  37. package/dist/shaka-player.ui.externs.js +1 -0
  38. package/dist/shaka-player.ui.js +953 -951
  39. package/dist/shaka-player.ui.map +4 -4
  40. package/karma.conf.js +4 -0
  41. package/lib/abr/simple_abr_manager.js +2 -1
  42. package/lib/dash/dash_parser.js +2 -3
  43. package/lib/hls/hls_parser.js +37 -14
  44. package/lib/media/drm_engine.js +3 -0
  45. package/lib/media/media_source_engine.js +7 -1
  46. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  47. package/lib/offline/storage.js +92 -91
  48. package/lib/player.js +165 -107
  49. package/lib/util/config_utils.js +25 -0
  50. package/lib/util/error.js +7 -0
  51. package/lib/util/manifest_parser_utils.js +1 -1
  52. package/lib/util/mime_utils.js +62 -6
  53. package/lib/util/periods.js +5 -4
  54. package/lib/util/player_configuration.js +9 -14
  55. package/lib/util/stream_utils.js +5 -2
  56. package/package.json +2 -2
  57. package/test/cast/cast_utils_unit.js +1 -0
  58. package/test/dash/dash_parser_manifest_unit.js +0 -24
  59. package/test/hls/hls_live_unit.js +86 -7
  60. package/test/hls/manifest_text_parser_unit.js +19 -0
  61. package/test/media/media_source_engine_integration.js +1 -0
  62. package/test/media/media_source_engine_unit.js +4 -4
  63. package/test/media/streaming_engine_integration.js +2 -0
  64. package/test/offline/indexeddb_storage_unit.js +1 -1
  65. package/test/offline/storage_compatibility_unit.js +1 -1
  66. package/test/player_external.js +7 -1
  67. package/test/player_integration.js +6 -10
  68. package/test/player_src_equals_integration.js +81 -83
  69. package/test/player_unit.js +98 -1
  70. package/test/test/boot.js +9 -0
  71. package/test/test/externs/filters.js +4 -0
  72. package/test/test/util/simple_fakes.js +31 -2
  73. package/test/util/mime_utils_unit.js +54 -0
  74. package/test/util/periods_unit.js +79 -0
  75. package/ui/less/containers.less +1 -1
  76. package/ui/less/general.less +1 -1
  77. package/ui/less/range_elements.less +1 -0
  78. package/.github/pull_request_template.md +0 -10
  79. package/docs/tutorials/gsoc-ideas-list.md +0 -3
  80. package/lib/util/mutex.js +0 -52
package/karma.conf.js CHANGED
@@ -363,6 +363,8 @@ module.exports = (config) => {
363
363
  // If testing custom assets, we don't serve other unit or integration tests.
364
364
  // External asset tests are the basis for custom asset testing, so this file
365
365
  // is automatically included.
366
+ clientArgs.testFiles.push('demo/common/asset.js');
367
+ clientArgs.testFiles.push('demo/common/assets.js');
366
368
  clientArgs.testFiles.push('test/player_external.js');
367
369
  } else {
368
370
  // In a normal test run, we serve unit tests.
@@ -374,6 +376,8 @@ module.exports = (config) => {
374
376
  }
375
377
  if (settings.external) {
376
378
  // If --external is present, we serve external asset tests.
379
+ clientArgs.testFiles.push('demo/common/asset.js');
380
+ clientArgs.testFiles.push('demo/common/assets.js');
377
381
  clientArgs.testFiles.push('test/**/*_external.js');
378
382
  }
379
383
  }
@@ -219,8 +219,9 @@ shaka.abr.SimpleAbrManager = class {
219
219
  * @export
220
220
  */
221
221
  getBandwidthEstimate() {
222
+ const defaultBandwidthEstimate = this.getDefaultBandwidth_();
222
223
  return this.bandwidthEstimator_.getBandwidthEstimate(
223
- this.config_.defaultBandwidthEstimate);
224
+ defaultBandwidthEstimate);
224
225
  }
225
226
 
226
227
 
@@ -764,8 +764,8 @@ shaka.dash.DashParser = class {
764
764
  // currently support that. Just choose one.
765
765
  // TODO: https://github.com/shaka-project/shaka-player/issues/1528
766
766
  stream.trickModeVideo = trickModeSet.streams.find((trickStream) =>
767
- shaka.util.MimeUtils.getCodecBase(stream.codecs) ==
768
- shaka.util.MimeUtils.getCodecBase(trickStream.codecs));
767
+ shaka.util.MimeUtils.getNormalizedCodec(stream.codecs) ==
768
+ shaka.util.MimeUtils.getNormalizedCodec(trickStream.codecs));
769
769
  }
770
770
  }
771
771
  }
@@ -1228,7 +1228,6 @@ shaka.dash.DashParser = class {
1228
1228
  });
1229
1229
  let spatialAudio = false;
1230
1230
  if (hasJoc) {
1231
- context.representation.mimeType = 'audio/eac3-joc';
1232
1231
  spatialAudio = true;
1233
1232
  }
1234
1233
 
@@ -1739,8 +1739,8 @@ shaka.hls.HlsParser = class {
1739
1739
  * @param {number} timestampOffset
1740
1740
  * @param {!Map.<string, string>} variables
1741
1741
  * @param {string} absoluteMediaPlaylistUri
1742
- * @return {!shaka.media.SegmentReference}
1743
1742
  * @param {string} type
1743
+ * @return {shaka.media.SegmentReference}
1744
1744
  * @private
1745
1745
  */
1746
1746
  createSegmentReference_(
@@ -1756,17 +1756,42 @@ shaka.hls.HlsParser = class {
1756
1756
  let startByte = 0;
1757
1757
  let endByte = null;
1758
1758
 
1759
+ if (!extinfTag) {
1760
+ if (hlsSegment.partialSegments.length == 0) {
1761
+ // EXTINF tag must be available if the segment has no partial segments.
1762
+ throw new shaka.util.Error(
1763
+ shaka.util.Error.Severity.CRITICAL,
1764
+ shaka.util.Error.Category.MANIFEST,
1765
+ shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
1766
+ } else if (!this.lowLatencyMode_) {
1767
+ // Without EXTINF and without low-latency mode, partial segments get
1768
+ // ignored.
1769
+ return null;
1770
+ }
1771
+ }
1772
+
1759
1773
  // Create SegmentReferences for the partial segments.
1760
1774
  const partialSegmentRefs = [];
1761
- if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
1775
+ if (this.lowLatencyMode_) {
1762
1776
  for (let i = 0; i < hlsSegment.partialSegments.length; i++) {
1763
1777
  const item = hlsSegment.partialSegments[i];
1764
1778
  const pPreviousReference = i == 0 ?
1765
1779
  previousReference : partialSegmentRefs[partialSegmentRefs.length - 1];
1766
1780
  const pStartTime = (i == 0) ? startTime : pPreviousReference.endTime;
1767
- const pDuration = Number(item.getAttributeValue('DURATION'));
1768
- // A preload hinted partial segment doesn't have duration information,
1769
- // so its startTime and endTime are the same.
1781
+
1782
+ // If DURATION is missing from this partial segment, use the target
1783
+ // partial duration from the top of the playlist, which is a required
1784
+ // attribute for content with partial segments.
1785
+ const pDuration = Number(item.getAttributeValue('DURATION')) ||
1786
+ this.partialTargetDuration_;
1787
+
1788
+ // If for some reason we have neither an explicit duration, nor a target
1789
+ // partial duration, we should SKIP this partial segment to avoid
1790
+ // duplicating content in the presentation timeline.
1791
+ if (!pDuration) {
1792
+ continue;
1793
+ }
1794
+
1770
1795
  const pEndTime = pStartTime + pDuration;
1771
1796
 
1772
1797
  let pStartByte = 0;
@@ -1799,14 +1824,6 @@ shaka.hls.HlsParser = class {
1799
1824
  /* appendWindowEnd= */ Infinity);
1800
1825
  partialSegmentRefs.push(partial);
1801
1826
  } // for-loop of hlsSegment.partialSegments
1802
- } else {
1803
- // EXTINF tag must be available if the segment has no partial segments.
1804
- if (!extinfTag) {
1805
- throw new shaka.util.Error(
1806
- shaka.util.Error.Severity.CRITICAL,
1807
- shaka.util.Error.Category.MANIFEST,
1808
- shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
1809
- }
1810
1827
  }
1811
1828
 
1812
1829
  // If the segment has EXTINF tag, set the segment's end time, start byte
@@ -1825,6 +1842,7 @@ shaka.hls.HlsParser = class {
1825
1842
  } else {
1826
1843
  endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
1827
1844
  }
1845
+
1828
1846
  // If the segment has EXT-X-BYTERANGE tag, set the start byte and end byte
1829
1847
  // base on the byterange information. If segment has no EXT-X-BYTERANGE tag
1830
1848
  // and has partial segments, set the start byte and end byte base on the
@@ -2054,7 +2072,9 @@ shaka.hls.HlsParser = class {
2054
2072
  playlist.absoluteUri,
2055
2073
  type);
2056
2074
 
2057
- references.push(reference);
2075
+ if (reference) {
2076
+ references.push(reference);
2077
+ }
2058
2078
  } else if (!this.lowLatencyMode_) {
2059
2079
  // If a segment has no extinfTag, it must contain partial segments.
2060
2080
  shaka.log.alwaysWarn('Low-latency HLS live stream detected, but ' +
@@ -2233,6 +2253,9 @@ shaka.hls.HlsParser = class {
2233
2253
  variables,
2234
2254
  /* absoluteMediaPlaylistUri= */ '',
2235
2255
  type);
2256
+ goog.asserts.assert(
2257
+ segmentRef != null, 'Segment reference should not be null!');
2258
+
2236
2259
  // If we are updating the manifest, we can usually skip fetching the segment
2237
2260
  // by examining the references we already have. This won't be possible if
2238
2261
  // there was some kind of lag or delay updating the manifest on the server,
@@ -1372,6 +1372,9 @@ shaka.media.DrmEngine = class {
1372
1372
  }
1373
1373
  return;
1374
1374
  }
1375
+ if (this.destroyer_.destroyed()) {
1376
+ return;
1377
+ }
1375
1378
 
1376
1379
  const updateEvent = new shaka.util.FakeEvent('drmsessionupdate');
1377
1380
  this.playerInterface_.onEvent(updateEvent);
@@ -63,6 +63,11 @@ shaka.media.MediaSourceEngine = class {
63
63
  string>} */
64
64
  this.sourceBufferTypes_ = {};
65
65
 
66
+
67
+ /** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,
68
+ boolean>} */
69
+ this.expectedEncryption_ = {};
70
+
66
71
  /** @private {shaka.text.TextEngine} */
67
72
  this.textEngine_ = null;
68
73
 
@@ -345,6 +350,7 @@ shaka.media.MediaSourceEngine = class {
345
350
  this.sourceBuffers_[contentType] = sourceBuffer;
346
351
  this.sourceBufferTypes_[contentType] = mimeType;
347
352
  this.queues_[contentType] = [];
353
+ this.expectedEncryption_[contentType] = !!stream.drmInfos.length;
348
354
  }
349
355
  }
350
356
  }
@@ -1091,7 +1097,7 @@ shaka.media.MediaSourceEngine = class {
1091
1097
  */
1092
1098
  workAroundBrokenPlatforms_(segment, startTime, contentType) {
1093
1099
  const isInitSegment = startTime == null;
1094
- const encryptionExpected = this.video_.mediaKeys;
1100
+ const encryptionExpected = this.expectedEncryption_[contentType];
1095
1101
 
1096
1102
  // If:
1097
1103
  // 1. this is an init segment,
@@ -368,8 +368,10 @@ shaka.offline.indexeddb.StorageMechanism.SESSION_ID_STORE = 'session-ids';
368
368
  shaka.offline.StorageMuxer.register(
369
369
  'idb',
370
370
  () => {
371
- // Offline storage is not supported on the Chromecast platform.
372
- if (shaka.util.Platform.isChromecast()) {
371
+ // Offline storage is not supported on the Chromecast or Xbox One
372
+ // platforms.
373
+ if (shaka.util.Platform.isChromecast() ||
374
+ shaka.util.Platform.isXboxOne()) {
373
375
  return null;
374
376
  }
375
377
  // Offline storage requires the IndexedDB API.
@@ -29,7 +29,6 @@ goog.require('shaka.util.Functional');
29
29
  goog.require('shaka.util.IDestroyable');
30
30
  goog.require('shaka.util.Iterables');
31
31
  goog.require('shaka.util.MimeUtils');
32
- goog.require('shaka.util.Mutex');
33
32
  goog.require('shaka.util.Platform');
34
33
  goog.require('shaka.util.PlayerConfiguration');
35
34
  goog.require('shaka.util.StreamUtils');
@@ -210,7 +209,7 @@ shaka.offline.Storage = class {
210
209
  }
211
210
 
212
211
  goog.asserts.assert(
213
- this.config_, 'Cannot reconfigure stroage after calling destroy.');
212
+ this.config_, 'Cannot reconfigure storage after calling destroy.');
214
213
  return shaka.util.PlayerConfiguration.mergeConfigObjects(
215
214
  /* destination= */ this.config_, /* updates= */ config );
216
215
  }
@@ -479,36 +478,34 @@ shaka.offline.Storage = class {
479
478
  async downloadSegments_(
480
479
  toDownload, manifestId, manifestDB, downloader, config, storage,
481
480
  manifest, drmEngine) {
481
+ let pendingManifestUpdates = {};
482
+ let pendingDataSize = 0;
483
+
482
484
  /**
483
485
  * @param {!Array.<!shaka.offline.DownloadInfo>} toDownload
484
486
  * @param {boolean} updateDRM
485
487
  */
486
488
  const download = async (toDownload, updateDRM) => {
487
- const throwIfAbortedFn = () => {
488
- this.ensureNotDestroyed_();
489
- };
490
489
  for (const download of toDownload) {
491
- /** @param {?BufferSource} data */
492
- let data;
493
490
  const request = download.makeSegmentRequest(config);
494
491
  const estimateId = download.estimateId;
495
492
  const isInitSegment = download.isInitSegment;
496
- const onDownloaded = (d) => {
497
- data = d;
498
- return Promise.resolve();
499
- };
500
- downloader.queue(download.groupId,
501
- request, estimateId, isInitSegment, onDownloaded);
502
- downloader.queueWork(download.groupId, async () => {
503
- goog.asserts.assert(data, 'We should have loaded data by now');
504
- goog.asserts.assert(data instanceof ArrayBuffer,
505
- 'The data should be an ArrayBuffer');
506
493
 
494
+ const onDownloaded = async (data) => {
495
+ // Store the data.
496
+ const dataKeys = await storage.addSegments([{data}]);
497
+ this.ensureNotDestroyed_();
498
+
499
+ // Store the necessary update to the manifest, to be processed later.
507
500
  const ref = /** @type {!shaka.media.SegmentReference} */ (
508
501
  download.ref);
509
- manifestDB = (await shaka.offline.Storage.assignStreamToManifest(
510
- manifestId, ref, {data}, throwIfAbortedFn)) || manifestDB;
511
- });
502
+ const id = shaka.offline.DownloadInfo.idForSegmentRef(ref);
503
+ pendingManifestUpdates[id] = dataKeys[0];
504
+ pendingDataSize += data.byteLength;
505
+ };
506
+
507
+ downloader.queue(download.groupId,
508
+ request, estimateId, isInitSegment, onDownloaded);
512
509
  }
513
510
  await downloader.waitToFinish();
514
511
 
@@ -516,8 +513,8 @@ shaka.offline.Storage = class {
516
513
  // Re-store the manifest, to attach session IDs.
517
514
  // These were (maybe) discovered inside the downloader; we can only add
518
515
  // them now, at the end, since the manifestDB is in flux during the
519
- // process of downloading and storing, and assignStreamToManifest does
520
- // not know about the DRM engine.
516
+ // process of downloading and storing, and assignSegmentsToManifest
517
+ // does not know about the DRM engine.
521
518
  this.ensureNotDestroyed_();
522
519
  this.setManifestDrmFields_(manifest, manifestDB, drmEngine, config);
523
520
  await storage.updateManifest(manifestId, manifestDB);
@@ -526,24 +523,54 @@ shaka.offline.Storage = class {
526
523
 
527
524
  const usingBgFetch = false; // TODO: Get.
528
525
 
529
- if (this.getManifestIsEncrypted_(manifest) && usingBgFetch &&
530
- !this.getManifestIncludesInitData_(manifest)) {
531
- // Background fetch can't make DRM sessions, so if we have to get the
532
- // init data from the init segments, download those first before anything
533
- // else.
534
- await download(toDownload.filter((info) => info.isInitSegment), true);
535
- this.ensureNotDestroyed_();
536
- toDownload = toDownload.filter((info) => !info.isInitSegment);
537
- }
526
+ try {
527
+ if (this.getManifestIsEncrypted_(manifest) && usingBgFetch &&
528
+ !this.getManifestIncludesInitData_(manifest)) {
529
+ // Background fetch can't make DRM sessions, so if we have to get the
530
+ // init data from the init segments, download those first before
531
+ // anything else.
532
+ await download(toDownload.filter((info) => info.isInitSegment), true);
533
+ this.ensureNotDestroyed_();
534
+ toDownload = toDownload.filter((info) => !info.isInitSegment);
538
535
 
539
- if (!usingBgFetch) {
540
- await download(toDownload, false);
541
- this.ensureNotDestroyed_();
536
+ // Copy these and reset them now, before calling await.
537
+ const manifestUpdates = pendingManifestUpdates;
538
+ const dataSize = pendingDataSize;
539
+ pendingManifestUpdates = {};
540
+ pendingDataSize = 0;
542
541
 
543
- goog.asserts.assert(
544
- !manifestDB.isIncomplete, 'The manifest should be complete by now');
545
- } else {
546
- // TODO: Send the request to the service worker. Don't await the result.
542
+ await shaka.offline.Storage.assignSegmentsToManifest(
543
+ storage, manifestId, manifestDB, manifestUpdates, dataSize,
544
+ () => this.ensureNotDestroyed_());
545
+ this.ensureNotDestroyed_();
546
+ }
547
+
548
+ if (!usingBgFetch) {
549
+ await download(toDownload, false);
550
+ this.ensureNotDestroyed_();
551
+
552
+ // Copy these and reset them now, before calling await.
553
+ const manifestUpdates = pendingManifestUpdates;
554
+ const dataSize = pendingDataSize;
555
+ pendingManifestUpdates = {};
556
+ pendingDataSize = 0;
557
+
558
+ await shaka.offline.Storage.assignSegmentsToManifest(
559
+ storage, manifestId, manifestDB, manifestUpdates, dataSize,
560
+ () => this.ensureNotDestroyed_());
561
+ this.ensureNotDestroyed_();
562
+
563
+ goog.asserts.assert(
564
+ !manifestDB.isIncomplete, 'The manifest should be complete by now');
565
+ } else {
566
+ // TODO: Send the request to the service worker. Don't await the result.
567
+ }
568
+ } catch (error) {
569
+ const dataKeys = Object.values(pendingManifestUpdates);
570
+ // Remove these pending segments that are not yet linked to the manifest.
571
+ await storage.removeSegments(dataKeys, (key) => {});
572
+
573
+ throw error;
547
574
  }
548
575
  }
549
576
 
@@ -567,70 +594,50 @@ shaka.offline.Storage = class {
567
594
  }
568
595
 
569
596
  /**
570
- * Load the given manifest, modifies it by assigning the given data to the
571
- * segments corresponding to "ref", then re-stores the manifest.
572
- * The parts of this function that modify the manifest are protected by a
573
- * mutex, to prevent race conditions; specifically, it prevents two parallel
574
- * instances of this method from both loading the manifest into memory at the
575
- * same time, which would result in the slower/later call overwriting the
576
- * changes of the other.
597
+ * Updates the given manifest, assigns database keys to segments, then stores
598
+ * the updated manifest.
577
599
  *
600
+ * It is up to the caller to ensure that this method is not called
601
+ * concurrently on the same manifest.
602
+ *
603
+ * @param {shaka.extern.StorageCell} storage
578
604
  * @param {number} manifestId
579
- * @param {!shaka.media.SegmentReference} ref
580
- * @param {shaka.extern.SegmentDataDB} data
605
+ * @param {!shaka.extern.ManifestDB} manifestDB
606
+ * @param {!Object.<string, number>} manifestUpdates
607
+ * @param {number} dataSizeUpdate
581
608
  * @param {function()} throwIfAbortedFn A function that should throw if the
582
609
  * download has been aborted.
583
- * @return {!Promise.<?shaka.extern.ManifestDB>}
610
+ * @return {!Promise}
584
611
  */
585
- static async assignStreamToManifest(manifestId, ref, data, throwIfAbortedFn) {
586
- /** @type {shaka.offline.StorageMuxer} */
587
- const muxer = new shaka.offline.StorageMuxer();
588
-
589
- const idForRef = shaka.offline.DownloadInfo.idForSegmentRef(ref);
612
+ static async assignSegmentsToManifest(
613
+ storage, manifestId, manifestDB, manifestUpdates, dataSizeUpdate,
614
+ throwIfAbortedFn) {
590
615
  let manifestUpdated = false;
591
- let dataKey;
592
- let activeHandle;
593
- /** @type {!shaka.extern.ManifestDB} */
594
- let manifestDB;
595
-
596
- let mutexId = 0;
597
616
 
598
617
  try {
599
- await muxer.init();
600
- activeHandle = await muxer.getActive();
601
-
602
- // Store the data.
603
- const dataKeys = await activeHandle.cell.addSegments([data]);
604
- dataKey = dataKeys[0];
605
- throwIfAbortedFn();
606
-
607
- // Acquire the mutex before accessing the manifest, since there could be
608
- // multiple instances of this method running at once.
609
- mutexId = await shaka.offline.Storage.mutex_.acquire();
610
- throwIfAbortedFn();
611
-
612
- // Load the manifest.
613
- const manifests = await activeHandle.cell.getManifests([manifestId]);
614
- throwIfAbortedFn();
615
- manifestDB = manifests[0];
616
-
617
618
  // Assign the stored data to the manifest.
618
619
  let complete = true;
619
620
  for (const stream of manifestDB.streams) {
620
621
  for (const segment of stream.segments) {
621
- if (segment.pendingSegmentRefId == idForRef) {
622
+ let dataKey = segment.pendingSegmentRefId ?
623
+ manifestUpdates[segment.pendingSegmentRefId] : null;
624
+ if (dataKey != null) {
622
625
  segment.dataKey = dataKey;
623
626
  // Now that the segment has been associated with the appropriate
624
627
  // dataKey, the pendingSegmentRefId is no longer necessary.
625
628
  segment.pendingSegmentRefId = undefined;
626
629
  }
627
- if (segment.pendingInitSegmentRefId == idForRef) {
630
+
631
+ dataKey = segment.pendingInitSegmentRefId ?
632
+ manifestUpdates[segment.pendingInitSegmentRefId] : null;
633
+ if (dataKey != null) {
628
634
  segment.initSegmentKey = dataKey;
629
635
  // Now that the init segment has been associated with the
630
636
  // appropriate initSegmentKey, the pendingInitSegmentRefId is no
631
637
  // longer necessary.
632
638
  segment.pendingInitSegmentRefId = undefined;
633
639
  }
640
+
634
641
  if (segment.pendingSegmentRefId) {
635
642
  complete = false;
636
643
  }
@@ -641,33 +648,30 @@ shaka.offline.Storage = class {
641
648
  }
642
649
 
643
650
  // Update the size of the manifest.
644
- manifestDB.size += data.data.byteLength;
651
+ manifestDB.size += dataSizeUpdate;
645
652
 
646
653
  // Mark the manifest as complete, if all segments are downloaded.
647
654
  if (complete) {
648
655
  manifestDB.isIncomplete = false;
649
656
  }
650
657
 
651
- // Re-store the manifest.
652
- await activeHandle.cell.updateManifest(manifestId, manifestDB);
658
+ // Update the manifest.
659
+ await storage.updateManifest(manifestId, manifestDB);
653
660
  manifestUpdated = true;
654
661
  throwIfAbortedFn();
655
662
  } catch (e) {
656
663
  await shaka.offline.Storage.cleanStoredManifest(manifestId);
657
664
 
658
- if (activeHandle && !manifestUpdated && dataKey) {
665
+ if (!manifestUpdated) {
666
+ const dataKeys = Object.values(manifestUpdates);
659
667
  // The cleanStoredManifest method will not "see" any segments that have
660
668
  // been downloaded but not assigned to the manifest yet. So un-store
661
669
  // them separately.
662
- await activeHandle.cell.removeSegments([dataKey], (key) => {});
670
+ await storage.removeSegments(dataKeys, (key) => {});
663
671
  }
664
672
 
665
673
  throw e;
666
- } finally {
667
- await muxer.destroy();
668
- shaka.offline.Storage.mutex_.release(mutexId);
669
674
  }
670
- return manifestDB;
671
675
  }
672
676
 
673
677
  /**
@@ -1662,9 +1666,6 @@ shaka.offline.Storage = class {
1662
1666
  }
1663
1667
  };
1664
1668
 
1665
- /** @private {!shaka.util.Mutex} */
1666
- shaka.offline.Storage.mutex_ = new shaka.util.Mutex();
1667
-
1668
1669
  shaka.offline.Storage.defaultSystemIds_ = new Map()
1669
1670
  .set('org.w3.clearkey', '1077efecc0b24d02ace33c1e52e2fb4b')
1670
1671
  .set('com.widevine.alpha', 'edef8ba979d64acea3c827dcd51d21ed')