diff --git a/lib/media/preload_manager.js b/lib/media/preload_manager.js index 9c4e214798..08cdc4accd 100644 --- a/lib/media/preload_manager.js +++ b/lib/media/preload_manager.js @@ -607,9 +607,10 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget { * Performs a final filtering of the manifest, and chooses the initial * variant. * + * @return {!Promise} * @private */ - chooseInitialVariantInner_() { + async chooseInitialVariantInner_() { goog.asserts.assert( this.manifest_, 'The manifest should already be parsed.'); @@ -655,13 +656,15 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget { this.abrManager_.setVariants(Array.from(adaptationSet.values())); const variant = this.abrManager_.chooseVariant(); if (variant) { + const promises = []; this.prefetchedVariant_ = variant; if (variant.video) { - this.makePrefetchForStream_(variant.video, isLive); + promises.push(this.prefetchStream_(variant.video, isLive)); } if (variant.audio) { - this.makePrefetchForStream_(variant.audio, isLive); + promises.push(this.prefetchStream_(variant.audio, isLive)); } + await Promise.all(promises); } } } @@ -672,7 +675,7 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget { * @return {!Promise} * @private */ - async makePrefetchForStream_(stream, isLive) { + async prefetchStream_(stream, isLive) { // Use the prefetch limit from the config if this is set, otherwise use 2. const prefetchLimit = this.config_.streaming.segmentPrefetchLimit || 2; const prefetch = new shaka.media.SegmentPrefetch( @@ -700,13 +703,14 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget { if (isLive) { // Preload only the init segment for Live if (prefetchSegment.initSegmentReference) { - prefetch.prefetchInitSegment(prefetchSegment.initSegmentReference); + await prefetch.prefetchInitSegment( + prefetchSegment.initSegmentReference); } } else { // Preload a segment, too... either the first segment, or the segment // that corresponds with this.startTime_, as appropriate. // Note: this method also preload the init segment - prefetch.prefetchSegmentsByTime(prefetchSegment.startTime); + await prefetch.prefetchSegmentsByTime(prefetchSegment.startTime); } } } diff --git a/lib/media/segment_prefetch.js b/lib/media/segment_prefetch.js index 03ec5a8f33..b7ea6ceadc 100644 --- a/lib/media/segment_prefetch.js +++ b/lib/media/segment_prefetch.js @@ -12,6 +12,7 @@ goog.require('shaka.media.InitSegmentReference'); goog.require('shaka.media.SegmentIterator'); goog.require('shaka.media.SegmentReference'); goog.require('shaka.net.NetworkingEngine'); +goog.require('shaka.util.Error'); goog.require('shaka.util.Uint8ArrayUtils'); @@ -74,6 +75,7 @@ shaka.media.SegmentPrefetch = class { * * @param {number} currTime * @param {boolean=} skipFirst + * @return {!Promise} * @public */ prefetchSegmentsByTime(currTime, skipFirst = false) { @@ -83,7 +85,7 @@ shaka.media.SegmentPrefetch = class { const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_); if (!this.stream_.segmentIndex) { shaka.log.debug(logPrefix, 'missing segmentIndex'); - return; + return Promise.resolve(); } if (!this.iterator_) { this.iterator_ = this.stream_.segmentIndex.getIteratorForTime( @@ -91,11 +93,12 @@ shaka.media.SegmentPrefetch = class { } if (!this.iterator_) { shaka.log.debug(logPrefix, 'missing iterator'); - return; + return Promise.resolve(); } if (skipFirst) { this.iterator_.next(); } + const promises = []; while (this.segmentPrefetchMap_.size < this.prefetchLimit_) { const reference = this.iterator_.next().value; if (!reference) { @@ -115,22 +118,26 @@ shaka.media.SegmentPrefetch = class { prefetchAllowed = false; } if (prefetchAllowed && reference.initSegmentReference) { - this.prefetchInitSegment(reference.initSegmentReference); + promises.push(this.prefetchInitSegment( + reference.initSegmentReference)); } if (prefetchAllowed && !this.segmentPrefetchMap_.has(reference)) { const segmentPrefetchOperation = new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_); - segmentPrefetchOperation.dispatchFetch(reference, this.stream_); + promises.push(segmentPrefetchOperation.dispatchFetch( + reference, this.stream_)); this.segmentPrefetchMap_.set(reference, segmentPrefetchOperation); } } this.clearInitSegments_(); + return Promise.all(promises); } /** * Fetch init segment. * * @param {!shaka.media.InitSegmentReference} initSegmentReference + * @return {!Promise} */ prefetchInitSegment(initSegmentReference) { goog.asserts.assert(this.prefetchLimit_ > 0, @@ -139,11 +146,11 @@ shaka.media.SegmentPrefetch = class { const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_); if (!this.stream_.segmentIndex) { shaka.log.debug(logPrefix, 'missing segmentIndex'); - return; + return Promise.resolve(); } if (initSegmentReference.getSegmentData()) { - return; + return Promise.resolve(); } // init segments are ignored from the prefetch limit @@ -152,14 +159,16 @@ shaka.media.SegmentPrefetch = class { return shaka.media.InitSegmentReference.equal( reference, initSegmentReference); }); - if (!someReference) { - const segmentPrefetchOperation = - new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_); - segmentPrefetchOperation.dispatchFetch( - initSegmentReference, this.stream_); - this.initSegmentPrefetchMap_.set( - initSegmentReference, segmentPrefetchOperation); + if (someReference) { + return Promise.resolve(); } + const segmentPrefetchOperation = new shaka.media.SegmentPrefetchOperation( + this.fetchDispatcher_); + const promise = segmentPrefetchOperation.dispatchFetch( + initSegmentReference, this.stream_); + this.initSegmentPrefetchMap_.set( + initSegmentReference, segmentPrefetchOperation); + return promise; } /** @@ -415,6 +424,7 @@ shaka.media.SegmentPrefetchOperation = class { * @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)} * reference * @param {!shaka.extern.Stream} stream + * @return {!Promise} * @public */ dispatchFetch(reference, stream) { @@ -433,6 +443,15 @@ shaka.media.SegmentPrefetchOperation = class { buffered = new Uint8Array(0); } }); + return this.operation_.promise.catch((e) => { + // Ignore OPERATION_ABORTED errors. + if (e instanceof shaka.util.Error && + e.code == shaka.util.Error.Code.OPERATION_ABORTED) { + return Promise.resolve(); + } + // Continue to surface other errors. + return Promise.reject(e); + }); } /** diff --git a/test/test/util/fake_segment_prefetch.js b/test/test/util/fake_segment_prefetch.js index cd057f6aa3..115284c307 100644 --- a/test/test/util/fake_segment_prefetch.js +++ b/test/test/util/fake_segment_prefetch.js @@ -63,6 +63,7 @@ shaka.test.FakeSegmentPrefetch = class { this.prefetchPosTime_ = reference.startTime; reference = iterator.next().value; } + return Promise.resolve(); } /** @override */