diff --git a/lib/offline/download_manager.js b/lib/offline/download_manager.js index b2de6b8b6..0a21e74b9 100644 --- a/lib/offline/download_manager.js +++ b/lib/offline/download_manager.js @@ -25,26 +25,39 @@ goog.require('shaka.util.IDestroyable'); goog.require('shaka.util.MapUtils'); +/** + * @typedef {{ + * request: shakaExtern.Request, + * estimatedSize: number, + * onStore: function(number) + * }} + * + * @property {shakaExtern.Request} + * The network request that will give us the bytes we want to download. + * @property {number} estimatedSize + * The size of the segment as estimated by the bandwidth and segment duration. + * @property {function(number)} onStore + * A callback for when a segment has been added to the storage. + */ +shaka.offline.DownloadRequest; + /** * This manages downloading segments and notifying the app of progress. * * @param {shaka.offline.IStorageEngine} storageEngine * @param {!shaka.net.NetworkingEngine} netEngine - * @param {shakaExtern.RetryParameters} retryParams * * @struct * @constructor * @implements {shaka.util.IDestroyable} */ -shaka.offline.DownloadManager = function( - storageEngine, netEngine, retryParams) { +shaka.offline.DownloadManager = function(storageEngine, netEngine) { /** * We sill store download requests in groups. The groups will be downloaded in * parallel but the segments in each group will be done serially. * - * @private {!Object.< - * number, !Array.>} + * @private {!Object.>} */ this.groups_ = {}; @@ -61,9 +74,6 @@ shaka.offline.DownloadManager = function( /** @private {shaka.net.NetworkingEngine} */ this.netEngine_ = netEngine; - /** @private {?shakaExtern.RetryParameters} */ - this.retryParams_ = retryParams; - /** @private {?shakaExtern.ManifestDB} */ this.manifest_ = null; @@ -92,29 +102,6 @@ shaka.offline.DownloadManager.prototype.followProgress = function(callback) { }; -/** - * @typedef {{ - * uris: !Array., - * startByte: number, - * endByte: ?number, - * bandwidthSize: number, - * onStore: function(number) - * }} - * - * @property {!Array.} uris - * The URIs to download for the segment. - * @property {number} startByte - * The byte index the segment starts at. - * @property {?number} endByte - * The byte index the segment ends at, if present. - * @property {number} bandwidthSize - * The size of the segment as estimated by the bandwidth and segment duration. - * @property {function(number)} onStore - * A callback for when a segment has been added to the storage. - */ -shaka.offline.DownloadManager.Segment; - - /** @override */ shaka.offline.DownloadManager.prototype.destroy = function() { let storage = this.storageEngine_; @@ -135,7 +122,6 @@ shaka.offline.DownloadManager.prototype.destroy = function() { this.storedSegmentIds_ = []; this.storageEngine_ = null; this.netEngine_ = null; - this.retryParams_ = null; this.manifest_ = null; this.promise_ = Promise.resolve(); @@ -144,24 +130,22 @@ shaka.offline.DownloadManager.prototype.destroy = function() { /** - * Adds a segment to the list to be downloaded. + * Add a request to be downloaded as part of a group. * * @param {number} group The group to add this segment to. If the group does * not exists, a new group will be created. - * @param {!shaka.media.SegmentReference|!shaka.media.InitSegmentReference} ref - * @param {number} bandwidthSize + * @param {shakaExtern.Request} request + * @param {number} estimatedSize * @param {function(number)} onStore * A callback for when the segment has been saved to storage. The parameter * will be the id the segment was saved under. */ -shaka.offline.DownloadManager.prototype.addSegment = function( - group, ref, bandwidthSize, onStore) { +shaka.offline.DownloadManager.prototype.queue = function( + group, request, estimatedSize, onStore) { this.groups_[group] = this.groups_[group] || []; this.groups_[group].push({ - uris: ref.getUris(), - startByte: ref.startByte, - endByte: ref.endByte, - bandwidthSize: bandwidthSize, + request: request, + estimatedSize: estimatedSize, onStore: onStore }); }; @@ -185,7 +169,7 @@ shaka.offline.DownloadManager.prototype.downloadAndStore = function(manifest) { this.groups_ = {}; // Clear the map to create a clean slate. groups.forEach((segments) => { - segments.forEach((segment) => this.markAsPending_(segment)); + segments.forEach((segment) => this.markAsPending_(segment.estimatedSize)); }); /** @type {!Promise.} */ @@ -210,7 +194,7 @@ shaka.offline.DownloadManager.prototype.downloadAndStore = function(manifest) { /** - * @param {!Array.} group + * @param {!Array.} group * @return {!Promise} * @private */ @@ -229,7 +213,7 @@ shaka.offline.DownloadManager.prototype.downloadGroup_ = function(group) { /** - * @param {shaka.offline.DownloadManager.Segment} segment + * @param {shaka.offline.DownloadRequest} segment * @return {!Promise} * @private */ @@ -238,14 +222,11 @@ shaka.offline.DownloadManager.prototype.downloadSegment_ = function(segment) { this.checkDestroyed_(); let type = shaka.net.NetworkingEngine.RequestType.SEGMENT; - let request = this.createRequest_(segment); - return this.netEngine_.request(type, request).promise; + return this.netEngine_.request(type, segment.request).promise; }).then((response) => { this.checkDestroyed_(); - // Update progress for the download (won't include the "store" part). - this.manifest_.size += response.data.byteLength; - this.markAsDone_(segment); + this.markAsDone_(segment.estimatedSize, response.data.byteLength); this.updateProgress_(); return this.storageEngine_.addSegment({ @@ -260,29 +241,6 @@ shaka.offline.DownloadManager.prototype.downloadSegment_ = function(segment) { }; -/** - * @param {!shaka.offline.DownloadManager.Segment} segment - * @return {shakaExtern.Request} - * @private - */ -shaka.offline.DownloadManager.prototype.createRequest_ = function(segment) { - goog.asserts.assert( - this.retryParams_, - 'DownloadManager must not be destroyed'); - - let request = shaka.net.NetworkingEngine.makeRequest( - segment.uris, - this.retryParams_); - - if (segment.startByte != 0 || segment.endByte != null) { - let end = segment.endByte == null ? '' : segment.endByte; - request.headers['Range'] = 'bytes=' + segment.startByte + '-' + end; - } - - return request; -}; - - /** * Check if the download manager has been destroyed. If so, throw an error to * kill the promise chain. @@ -299,30 +257,23 @@ shaka.offline.DownloadManager.prototype.checkDestroyed_ = function() { /** - * @param {!shaka.offline.DownloadManager.Segment} segment + * @param {number} estimate * @private */ -shaka.offline.DownloadManager.prototype.markAsPending_ = function(segment) { - /** @type {number} */ - let estimatedSize = segment.endByte == null ? - segment.bandwidthSize : - (segment.endByte - segment.startByte + 1); - - this.downloadExpected_ += estimatedSize; +shaka.offline.DownloadManager.prototype.markAsPending_ = function(estimate) { + this.downloadExpected_ += estimate; }; /** - * @param {!shaka.offline.DownloadManager.Segment} segment + * @param {number} estimate The estimated number of bytes we need to download. + * @param {number} actual The actual number of bytes we downloaded. * @private */ -shaka.offline.DownloadManager.prototype.markAsDone_ = function(segment) { - /** @type {number} */ - let estimatedSize = segment.endByte == null ? - segment.bandwidthSize : - (segment.endByte - segment.startByte + 1); - - this.downloadActual_ += estimatedSize; +shaka.offline.DownloadManager.prototype.markAsDone_ = function( + estimate, actual) { + this.downloadActual_ += estimate; + this.manifest_.size += actual; }; diff --git a/lib/offline/storage.js b/lib/offline/storage.js index a4a979b48..0b7c21ffd 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -22,6 +22,7 @@ goog.require('shaka.Player'); goog.require('shaka.log'); goog.require('shaka.media.DrmEngine'); goog.require('shaka.media.ManifestParser'); +goog.require('shaka.net.NetworkingEngine'); goog.require('shaka.offline.DownloadManager'); goog.require('shaka.offline.IStorageEngine'); goog.require('shaka.offline.ManifestConverter'); @@ -642,8 +643,6 @@ shaka.offline.Storage.prototype.initIfNeeded_ = function() { goog.asserts.assert(this.player_, 'Player must be initialized'); /** @type {shaka.net.NetworkingEngine} */ let netEngine = this.getNetEngine_(); - /** @type {shakaExtern.RetryParameters} */ - let retryParams = this.player_.getConfiguration().streaming.retryParameters; return shaka.offline.StorageEngineFactory.createStorageEngine() .then(function(storageEngine) { @@ -655,8 +654,7 @@ shaka.offline.Storage.prototype.initIfNeeded_ = function() { this.downloadManager_ = new shaka.offline.DownloadManager( storageEngine, - netEngine, - retryParams); + netEngine); this.downloadManager_.followProgress(function(progress, size) { /** @type {?shakaExtern.StoredContent} */ let content = this.pendingContent_; @@ -956,13 +954,17 @@ shaka.offline.Storage.prototype.createStream_ = function( let endTime = segment.endTime; /** @type {number} */ let duration = endTime - startTime; - /** @type {number} */ - let bandwidthSize = duration * estimatedStreamBandwidth / 8; - this.downloadManager_.addSegment( + /** @type {number} */ + let estimatedSize = segment.endByte ? + (segment.endByte - segment.startByte) : + (duration * estimatedStreamBandwidth / 8); + + let request = this.createRequest_(segment); + this.downloadManager_.queue( downloadGroup, - segment, - bandwidthSize, + request, + estimatedSize, function(id) { /** @type {shakaExtern.SegmentDB} */ let segmentDb = { @@ -978,10 +980,11 @@ shaka.offline.Storage.prototype.createStream_ = function( let initSegment = stream.initSegmentReference; if (initSegment) { const noBandwidth = 0; + let request = this.createRequest_(initSegment); - this.downloadManager_.addSegment( + this.downloadManager_.queue( downloadGroup, - initSegment, + request, noBandwidth, function(id) { streamDb.initSegmentKey = id; @@ -1036,6 +1039,26 @@ shaka.offline.Storage.prototype.getNetEngine_ = function() { }; +/** + * @param {!shaka.media.SegmentReference| + * !shaka.media.InitSegmentReference} segment + * @return {shakaExtern.Request} + * @private + */ +shaka.offline.Storage.prototype.createRequest_ = function(segment) { + let retryParams = this.player_.getConfiguration().streaming.retryParameters; + let request = shaka.net.NetworkingEngine.makeRequest( + segment.getUris(), retryParams); + + if (segment.startByte != 0 || segment.endByte != null) { + let end = segment.endByte == null ? '' : segment.endByte; + request.headers['Range'] = 'bytes=' + segment.startByte + '-' + end; + } + + return request; +}; + + /** * @param {shakaExtern.ManifestDB} manifest * @return {!Array.} diff --git a/test/offline/storage_unit.js b/test/offline/storage_unit.js index 8bd710d0e..737daac11 100644 --- a/test/offline/storage_unit.js +++ b/test/offline/storage_unit.js @@ -483,15 +483,15 @@ describe('Storage', function() { switch (progress.calls.count()) { case 1: - expect(percent).toBeCloseTo(54 / 101); + expect(percent).toBeCloseTo(0.53); expect(storedContent.size).toBe(54); break; case 2: - expect(percent).toBeCloseTo(64 / 101); + expect(percent).toBeCloseTo(0.64); expect(storedContent.size).toBe(67); break; case 3: - expect(percent).toBeCloseTo(84 / 101); + expect(percent).toBeCloseTo(0.84); expect(storedContent.size).toBe(133); break; default: