diff --git a/externs/shaka/offline.js b/externs/shaka/offline.js index 1c8c22f459..3021280f24 100644 --- a/externs/shaka/offline.js +++ b/externs/shaka/offline.js @@ -98,7 +98,6 @@ shakaExtern.StoredContent; /** * @typedef {{ - * key: number, * originalManifestUri: string, * duration: number, * size: number, @@ -109,8 +108,6 @@ shakaExtern.StoredContent; * appMetadata: Object * }} * - * @property {number} key - * The key that uniquely identifies the manifest. * @property {string} originalManifestUri * The URI that the manifest was originally loaded from. * @property {number} duration @@ -223,12 +220,9 @@ shakaExtern.SegmentDB; /** * @typedef {{ - * key: number, * data: ArrayBuffer * }} * - * @property {number} key - * A key that uniquely describes the segment. * @property {ArrayBuffer} data * The data contents of the segment. */ diff --git a/lib/offline/db_engine.js b/lib/offline/db_engine.js index f348dbeea5..72fb4bfd84 100644 --- a/lib/offline/db_engine.js +++ b/lib/offline/db_engine.js @@ -209,9 +209,22 @@ shaka.offline.DBEngine.prototype.forEachManifest = function(each) { /** @override */ -shaka.offline.DBEngine.prototype.insertManifest = function(value) { +shaka.offline.DBEngine.prototype.addManifest = function(value) { + /** @type {number} */ + var key = this.nextManifestId_++; + return this.insert_( shaka.offline.DBEngine.Store_.MANIFEST, + key, + value); +}; + + +/** @override */ +shaka.offline.DBEngine.prototype.updateManifest = function(key, value) { + return this.insert_( + shaka.offline.DBEngine.Store_.MANIFEST, + key, value); }; @@ -226,12 +239,6 @@ shaka.offline.DBEngine.prototype.removeManifests = }; -/** @override */ -shaka.offline.DBEngine.prototype.reserveManifestId = function() { - return this.nextManifestId_++; -}; - - /** @override */ shaka.offline.DBEngine.prototype.getSegment = function(key) { return this.get_( @@ -249,9 +256,13 @@ shaka.offline.DBEngine.prototype.forEachSegment = function(each) { /** @override */ -shaka.offline.DBEngine.prototype.insertSegment = function(value) { +shaka.offline.DBEngine.prototype.addSegment = function(value) { + /** @type {number} */ + var key = this.nextSegmentId_++; + return this.insert_( shaka.offline.DBEngine.Store_.SEGMENT, + key, value); }; @@ -266,12 +277,6 @@ shaka.offline.DBEngine.prototype.removeSegments = }; -/** @override */ -shaka.offline.DBEngine.prototype.reserveSegmentId = function() { - return this.nextSegmentId_++; -}; - - /** * @param {shaka.offline.DBEngine.Store_} store * @param {number} key @@ -295,7 +300,7 @@ shaka.offline.DBEngine.prototype.get_ = function(store, key) { /** * @param {shaka.offline.DBEngine.Store_} store - * @param {function(T)} each + * @param {function(number, T)} each * @return {!Promise} * @template T * @private @@ -313,7 +318,7 @@ shaka.offline.DBEngine.prototype.forEach_ = function(store, each) { request.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { - each(cursor.value); + each(cursor.key, cursor.value); cursor.continue(); } }; @@ -323,20 +328,24 @@ shaka.offline.DBEngine.prototype.forEach_ = function(store, each) { /** * @param {shaka.offline.DBEngine.Store_} store + * @param {number} key * @param {T} value - * @return {!Promise} + * @return {!Promise} * @template T * @private */ -shaka.offline.DBEngine.prototype.insert_ = function(store, value) { +shaka.offline.DBEngine.prototype.insert_ = function(store, key, value) { /** @const */ var DBEngine = shaka.offline.DBEngine; /** @const */ var READ_WRITE = DBEngine.Mode_.READ_WRITE; + // TODO (vaage) : Replace this with auto key. + value['key'] = key; + return this.createTransaction_(store, READ_WRITE, function(store) { store.put(value); - }); + }).then(function() { return key; }); }; diff --git a/lib/offline/download_manager.js b/lib/offline/download_manager.js index 13e895a087..871b6377b0 100644 --- a/lib/offline/download_manager.js +++ b/lib/offline/download_manager.js @@ -49,7 +49,7 @@ shaka.offline.DownloadManager = function( * download(). This is used to cleanup in destroy(). * @private {!Array.} */ - this.storedSegments_ = []; + this.storedSegmentIds_ = []; /** @private {shaka.offline.IStorageEngine} */ this.storageEngine_ = storageEngine; @@ -91,7 +91,7 @@ shaka.offline.DownloadManager.prototype.followProgress = function(callback) { * startByte: number, * endByte: ?number, * bandwidthSize: number, - * segmentId: number + * onStore: function(number) * }} * * @property {!Array.} uris @@ -102,8 +102,8 @@ shaka.offline.DownloadManager.prototype.followProgress = function(callback) { * 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 {number} segmentId - * The key that the segment should be saved under in storage. + * @property {function(number)} onStore + * A callback for when a segment as been added to the storage. */ shaka.offline.DownloadManager.Segment; @@ -111,7 +111,7 @@ shaka.offline.DownloadManager.Segment; /** @override */ shaka.offline.DownloadManager.prototype.destroy = function() { var storage = this.storageEngine_; - var segments = this.storedSegments_; + var segments = this.storedSegmentIds_; var p = this.promise_ || Promise.resolve(); // Don't try to remove segments if there are none. That may trigger an error @@ -123,7 +123,7 @@ shaka.offline.DownloadManager.prototype.destroy = function() { // Don't destroy() storageEngine since it is owned by Storage. this.segments_ = {}; - this.storedSegments_ = []; + this.storedSegmentIds_ = []; this.storageEngine_ = null; this.netEngine_ = null; this.retryParams_ = null; @@ -139,19 +139,19 @@ shaka.offline.DownloadManager.prototype.destroy = function() { * @param {string} type * @param {!shaka.media.SegmentReference|!shaka.media.InitSegmentReference} ref * @param {number} bandwidthSize - * @param {number} segmentId - * The data to store in the database with the data. The |data| field of this - * object will contain the downloaded data. + * @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( - type, ref, bandwidthSize, segmentId) { + type, ref, bandwidthSize, onStore) { this.segments_[type] = this.segments_[type] || []; this.segments_[type].push({ uris: ref.getUris(), startByte: ref.startByte, endByte: ref.endByte, bandwidthSize: bandwidthSize, - segmentId: segmentId + onStore: onStore }); }; @@ -161,7 +161,7 @@ shaka.offline.DownloadManager.prototype.addSegment = function( * manifest object. * * @param {shakaExtern.ManifestDB} manifest - * @return {!Promise} + * @return {!Promise} */ shaka.offline.DownloadManager.prototype.downloadAndStore = function(manifest) { var MapUtils = shaka.util.MapUtils; @@ -196,9 +196,10 @@ shaka.offline.DownloadManager.prototype.downloadAndStore = function(manifest) { this.segments_ = {}; this.promise_ = Promise.all(async).then(function() { - return this.storageEngine_.insertManifest(manifest); - }.bind(this)).then(function() { - this.storedSegments_ = []; + return this.storageEngine_.addManifest(manifest); + }.bind(this)).then(function(id) { + this.storedSegmentIds_ = []; + return id; }.bind(this)); return this.promise_; }; @@ -233,14 +234,14 @@ shaka.offline.DownloadManager.prototype.downloadSegment_ = function(segment) { } byteCount = response.data.byteLength; - this.storedSegments_.push(segment.segmentId); - - return this.storageEngine_.insertSegment({ - key: segment.segmentId, + /** @type {shakaExtern.SegmentDataDB} */ + var segmentDb = { data: response.data - }); + }; + + return this.storageEngine_.addSegment(segmentDb); }.bind(this)) - .then(function() { + .then(function(id) { if (!this.manifest_) { return Promise.reject(new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, @@ -251,6 +252,9 @@ shaka.offline.DownloadManager.prototype.downloadSegment_ = function(segment) { this.manifest_.size += byteCount; this.markAsDone_(segment); + this.storedSegmentIds_.push(id); + segment.onStore(id); + this.updateProgress_(); }.bind(this)); }; @@ -296,7 +300,10 @@ shaka.offline.DownloadManager.prototype.updateProgress_ = function() { 0 : (this.downloadActual_ / this.downloadExpected_); + /** @type {number} */ + var size = this.manifest_.size; + this.progressListeners_.forEach(function(listener) { - listener(progress, this.manifest_.size); - }.bind(this)); + listener(progress, size); + }); }; diff --git a/lib/offline/i_storage_engine.js b/lib/offline/i_storage_engine.js index 2b89555a92..750efae81c 100644 --- a/lib/offline/i_storage_engine.js +++ b/lib/offline/i_storage_engine.js @@ -41,19 +41,29 @@ shaka.offline.IStorageEngine.prototype.getManifest; /** * Iterate over all the manifests in storage. * - * @param {function(shakaExtern.ManifestDB)} each + * @param {function(number, shakaExtern.ManifestDB)} each * @return {!Promise} */ shaka.offline.IStorageEngine.prototype.forEachManifest; /** - * Insert or update a manifest in storage. + * Add a manifest to storage. * * @param {shakaExtern.ManifestDB} value + * @return {!Promise.} + */ +shaka.offline.IStorageEngine.prototype.addManifest; + + +/** + * Update a manifest already in storage. + * + * @param {number} key + * @param {shakaExtern.ManifestDB} value * @return {!Promise} */ -shaka.offline.IStorageEngine.prototype.insertManifest; +shaka.offline.IStorageEngine.prototype.updateManifest; /** @@ -69,14 +79,6 @@ shaka.offline.IStorageEngine.prototype.insertManifest; shaka.offline.IStorageEngine.prototype.removeManifests; -/** - * Reserve an id for a new manifest. - * - * @return {number} - */ -shaka.offline.IStorageEngine.prototype.reserveManifestId; - - /** * Get a single segment from storage using the key associated * to the segment. @@ -90,19 +92,19 @@ shaka.offline.IStorageEngine.prototype.getSegment; /** * Iterate over all the segments in storage. * - * @param {function(shakaExtern.SegmentDataDB)} each + * @param {function(number, shakaExtern.SegmentDataDB)} each * @return {!Promise} */ shaka.offline.IStorageEngine.prototype.forEachSegment; /** - * Insert or update a segment in storage. + * Add a segment to storage. * * @param {shakaExtern.SegmentDataDB} value - * @return {!Promise} + * @return {!Promise.} */ -shaka.offline.IStorageEngine.prototype.insertSegment; +shaka.offline.IStorageEngine.prototype.addSegment; /** @@ -116,11 +118,3 @@ shaka.offline.IStorageEngine.prototype.insertSegment; * @return {!Promise} */ shaka.offline.IStorageEngine.prototype.removeSegments; - - -/** - * Reserve an id for a new segment. - * - * @return {number} - */ -shaka.offline.IStorageEngine.prototype.reserveSegmentId; diff --git a/lib/offline/offline_manifest_parser.js b/lib/offline/offline_manifest_parser.js index 24f91eaa8f..08657a226f 100644 --- a/lib/offline/offline_manifest_parser.js +++ b/lib/offline/offline_manifest_parser.js @@ -129,7 +129,7 @@ shaka.offline.OfflineManifestParser.prototype.onExpirationUpdated = function( manifest.expiration > expiration) { shaka.log.debug('Updating expiration for stored content'); manifest.expiration = expiration; - return storageEngine.insertManifest(manifest); + return storageEngine.updateManifest(manifestId, manifest); } }) .catch(function(error) { diff --git a/lib/offline/storage.js b/lib/offline/storage.js index 49651fa729..ed52e5300f 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -253,24 +253,19 @@ shaka.offline.Storage.prototype.downloadAndStoreManifest_ = function( 0, // start with a size of 0 appMetadata); - /** @type {number} */ - var manifestId = this.storageEngine_.reserveManifestId(); - /** @type {shakaExtern.ManifestDB} */ var manifestDb = this.createOfflineManifest_( - manifestUri, - manifestId, - appMetadata); + manifestUri, appMetadata); return this.downloadManager_.downloadAndStore(manifestDb) - .then(function() { + .then(function(id) { /** @const */ var OfflineScheme = shaka.offline.OfflineScheme; /** @const */ var OfflineUtils = shaka.offline.OfflineUtils; /** @type {string} */ - var uri = OfflineScheme.manifestIdToUri(manifestId); + var uri = OfflineScheme.manifestIdToUri(id); return OfflineUtils.createStoredContentFromManifestDB(uri, manifestDb); }); }; @@ -411,9 +406,9 @@ shaka.offline.Storage.prototype.list = function() { return this.initIfNeeded_() .then(function() { this.checkDestroyed_(); - return this.storageEngine_.forEachManifest(function(manifestDB) { + return this.storageEngine_.forEachManifest(function(id, manifestDB) { var content = OfflineUtils.createStoredContentFromManifestDB( - shaka.offline.OfflineScheme.manifestIdToUri(manifestDB.key), + shaka.offline.OfflineScheme.manifestIdToUri(id), manifestDB); storedContents.push(content); }); @@ -792,16 +787,16 @@ shaka.offline.Storage.prototype.createSegmentIndex_ = function(manifest) { * createPeriod_. * * @param {string} originalManifestUri - * @param {number} manifestId * @param {!Object} metadata * @return {shakaExtern.ManifestDB} * @private */ shaka.offline.Storage.prototype.createOfflineManifest_ = function( - originalManifestUri, manifestId, metadata) { + originalManifestUri, metadata) { var periods = this.manifest_.periods.map(this.createPeriod_.bind(this)); var drmInfo = this.drmEngine_.getDrmInfo(); var sessions = this.drmEngine_.getSessionIds(); + if (drmInfo) { if (!sessions.length) { throw new shaka.util.Error( @@ -813,7 +808,6 @@ shaka.offline.Storage.prototype.createOfflineManifest_ = function( } return { - key: manifestId, originalManifestUri: originalManifestUri, duration: this.manifest_.presentationTimeline.getDuration(), size: 0, @@ -983,23 +977,20 @@ shaka.offline.Storage.prototype.createStream_ = function( /** @type {number} */ var bandwidthSize = duration * estimatedStreamBandwidth / 8; - /** @type {number} */ - var id = this.storageEngine_.reserveSegmentId(); - - /** @type {shakaExtern.SegmentDB} */ - var segmentDb = { - startTime: startTime, - endTime: endTime, - uri: OfflineScheme.segmentIdToUri(id) - }; - - streamDb.segments.push(segmentDb); - this.downloadManager_.addSegment( stream.type, segment, bandwidthSize, - id); + function(id) { + /** @type {shakaExtern.SegmentDB} */ + var segmentDb = { + startTime: startTime, + endTime: endTime, + uri: OfflineScheme.segmentIdToUri(id) + }; + + streamDb.segments.push(segmentDb); + }.bind(this)); }.bind(this)); var initSegment = stream.initSegmentReference; @@ -1007,16 +998,13 @@ shaka.offline.Storage.prototype.createStream_ = function( /** @const {number} */ var noBandwidth = 0; - /** @type {number} */ - var id = this.storageEngine_.reserveSegmentId(); - - streamDb.initSegmentUri = OfflineScheme.segmentIdToUri(id); - this.downloadManager_.addSegment( stream.contentType, initSegment, noBandwidth, - id); + function(id) { + streamDb.initSegmentUri = OfflineScheme.segmentIdToUri(id); + }); } return streamDb; diff --git a/test/offline/db_engine_unit.js b/test/offline/db_engine_unit.js index d74e6e1f2e..7475680da6 100644 --- a/test/offline/db_engine_unit.js +++ b/test/offline/db_engine_unit.js @@ -48,17 +48,14 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { }); it('stores and retrieves a manifest', checkAndRun(function(done) { - /** @type {number} */ - var id = db.reserveManifestId(); - /** @type {shakaExtern.ManifestDB} */ - var original = createManifest(id); + var original = createManifest('original manifest'); Promise.resolve() .then(function() { - return db.insertManifest(original); + return db.addManifest(original); }) - .then(function() { + .then(function(id) { return db.getManifest(id); }) .then(function(copy) { @@ -68,17 +65,13 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { })); it('stores and retrieves many manifest', checkAndRun(function(done) { - /** @type {!Array} */ - var ids = [ - db.reserveManifestId(), - db.reserveManifestId(), - db.reserveManifestId() - ]; - /** @type {!Array} */ - var originals = ids.map(function(id) { - return createManifest(id); - }); + var originals = [ + createManifest('original manifest 1'), + createManifest('original manifest 2'), + createManifest('original manifest 3'), + createManifest('original manifest 4') + ]; /** @type {!Array} */ var copies = []; @@ -86,11 +79,11 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { Promise.resolve() .then(function() { return Promise.all(originals.map(function(original) { - return db.insertManifest(original); + return db.addManifest(original); })); }) .then(function() { - return db.forEachManifest(function(manifest) { + return db.forEachManifest(function(id, manifest) { copies.push(manifest); }); }) @@ -103,17 +96,18 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { })); it('stores and remove a manifest', checkAndRun(function(done) { - /** @type {number} */ - var id = db.reserveManifestId(); - /** @type {shakaExtern.ManifestDB} */ - var original = createManifest(id); + var original = createManifest('original manifest'); + + /** @type {number} */ + var id; Promise.resolve() .then(function() { - return db.insertManifest(original); + return db.addManifest(original); }) - .then(function() { + .then(function(newId) { + id = newId; return db.getManifest(id); }) .then(function(value) { @@ -130,17 +124,14 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { })); it('stores and retrieves a segment', checkAndRun(function(done) { - /** @type {number} */ - var id = db.reserveSegmentId(); - /** @type {shakaExtern.SegmentDataDB} */ - var original = createSegment(id); + var original = createSegment([0, 1, 2]); Promise.resolve() .then(function() { - return db.insertSegment(original); + return db.addSegment(original); }) - .then(function() { + .then(function(id) { return db.getSegment(id); }) .then(function(copy) { @@ -150,17 +141,13 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { })); it('stores and retrieves many segments', checkAndRun(function(done) { - /** @type {!Array} */ - var ids = [ - db.reserveSegmentId(), - db.reserveSegmentId(), - db.reserveSegmentId() - ]; - /** @type {!Array} */ - var originals = ids.map(function(id) { - return createSegment(id); - }); + var originals = [ + createSegment([0]), + createSegment([1, 2]), + createSegment([3, 4, 5]), + createSegment([6, 7, 8, 9]) + ]; /** @type {!Array} */ var copies = []; @@ -168,11 +155,11 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { Promise.resolve() .then(function() { return Promise.all(originals.map(function(original) { - return db.insertSegment(original); + return db.addSegment(original); })); }) .then(function() { - return db.forEachSegment(function(segment) { + return db.forEachSegment(function(id, segment) { copies.push(segment); }); }) @@ -185,17 +172,18 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { })); it('stores and remove a segment', checkAndRun(function(done) { - /** @type {number} */ - var id = db.reserveSegmentId(); - /** @type {shakaExtern.SegmentDataDB} */ - var original = createSegment(id); + var original = createSegment([0, 1, 2]); + + /** @type {number} */ + var id; Promise.resolve() .then(function() { - return db.insertSegment(original); + return db.addSegment(original); }) - .then(function() { + .then(function(newId) { + id = newId; return db.getSegment(id); }) .then(function(value) { @@ -213,15 +201,12 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { // TODO : Remove this test once we drop support for DB Engine Version 1 it('fills missing variant ids for old manifests', checkAndRun(function(done) { - /** @type {number} */ - var id = db.reserveManifestId(); - // Create a manifest with four streams. Two video and two audio. When db // engine recreates the variant ids, it should pair them together into // four variants. /** @type {shakaExtern.ManifestDB} */ - var originalManifest = createManifest(id); + var originalManifest = createManifest('original manifest'); originalManifest.periods.push({ startTime: 0, streams: [ @@ -240,9 +225,9 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { Promise.resolve() .then(function() { - return db.insertManifest(originalManifest); + return db.addManifest(originalManifest); }) - .then(function() { + .then(function(id) { return db.getManifest(id); }) .then(function(manifest) { @@ -287,17 +272,16 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { /** - * @param {number} id + * @param {string} originalUri * @return {shakaExtern.ManifestDB} */ - function createManifest(id) { + function createManifest(originalUri) { return { appMetadata: null, drmInfo: null, duration: 90, expiration: Infinity, - key: id, - originalManifestUri: '', + originalManifestUri: originalUri, periods: [], sessionIds: [], size: 1024 @@ -334,16 +318,15 @@ describe('DBEngine', /** @suppress {accessControls} */ function() { /** - * @param {number} id + * @param {!Array.} data * @return {shakaExtern.SegmentDataDB} */ - function createSegment(id) { + function createSegment(data) { + /** @type {Int32Array} */ + var array = new Int32Array(data); + return { - data: null, - key: id, - manifestKey: 0, - segmentNumber: 0, - streamNumber: 0 + data: array.buffer }; } }); diff --git a/test/offline/offline_manifest_parser_unit.js b/test/offline/offline_manifest_parser_unit.js index 578167e230..e6c05048bf 100644 --- a/test/offline/offline_manifest_parser_unit.js +++ b/test/offline/offline_manifest_parser_unit.js @@ -23,11 +23,6 @@ describe('OfflineManifestParser', function() { var playerInterface = /** @type {shakaExtern.ManifestParser.PlayerInterface} */ (null); - /** @const {number} */ - var manifestId = 10; - /** @const {string} */ - var manifestUri = shaka.offline.OfflineScheme.manifestIdToUri(manifestId); - /** @type {!shaka.offline.IStorageEngine} */ var fakeStorageEngine; /** @type {!shaka.offline.OfflineManifestParser} */ @@ -50,13 +45,12 @@ describe('OfflineManifestParser', function() { mockSEFactory.resetAll(); }); - it('will query DBEngine for the manifest', function(done) { + it('will query storage engine for the manifest', function(done) { new shaka.test.ManifestDBBuilder(fakeStorageEngine) - .onManifest(function(manifest) { - manifest.key = manifestId; - }) - .build().then(function(manifest) { - return parser.start(manifestUri, playerInterface); + .build().then(function(id) { + /** @const {string} */ + var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); + return parser.start(uri, playerInterface); }).catch(fail).then(done); }); @@ -99,10 +93,8 @@ describe('OfflineManifestParser', function() { /** @const {string} */ var sessionId = 'abc'; - /** @const {number} */ - var id = 101; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); + /** @type {number} */ + var id; /** @type {number} */ var expiration = 256; @@ -110,10 +102,14 @@ describe('OfflineManifestParser', function() { beforeEach(function(done) { new shaka.test.ManifestDBBuilder(fakeStorageEngine) .onManifest(function(manifest) { - manifest.key = id; manifest.sessionIds = [sessionId]; }) - .build().then(function(manifest) { + .build().then(function(newId) { + // Save the id so that we can use it later to fetch the manifest. + id = newId; + + /** @const {string} */ + var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); return parser.start(uri, playerInterface); }).catch(fail).then(done); }); @@ -152,18 +148,14 @@ describe('OfflineManifestParser', function() { }); it('converts non-Period members correctly', function(done) { - /** @const {number} */ - var id = 101; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); - new shaka.test.ManifestDBBuilder(fakeStorageEngine) .onManifest(function(manifest) { - manifest.key = id; manifest.sessionIds = ['abc', '123']; manifest.duration = 60; }) - .build().then(function(manifest) { + .build().then(function(id) { + /** @const {string} */ + var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); return parser.start(uri, playerInterface); }).then(function(manifest) { expect(manifest).toBeTruthy(); @@ -181,11 +173,6 @@ describe('OfflineManifestParser', function() { }); it('will accept DrmInfo', function(done) { - /** @const {number} */ - var id = 101; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); - var drmInfo = { keySystem: 'com.example.drm', licenseServerUri: 'https://example.com/drm', @@ -204,11 +191,12 @@ describe('OfflineManifestParser', function() { new shaka.test.ManifestDBBuilder(fakeStorageEngine) .onManifest(function(manifest) { - manifest.key = id; manifest.drmInfo = drmInfo; }) .period() - .build().then(function() { + .build().then(function(id) { + /** @const {string} */ + var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); return parser.start(uri, playerInterface); }).then(function(manifest) { expect(manifest).toBeTruthy(); @@ -219,24 +207,20 @@ describe('OfflineManifestParser', function() { }); it('will call reconstructPeriod for each Period', function(done) { - /** @const {number} */ - var id = 101; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); - var spy = jasmine.createSpy('reconstructPeriod'); shaka.offline.OfflineUtils.reconstructPeriod = shaka.test.Util.spyFunc(spy); new shaka.test.ManifestDBBuilder(fakeStorageEngine) .onManifest(function(manifest) { - manifest.key = id; manifest.sessionId = ['abc', '123']; }) .period() .period() .period() - .build().then(function() { + .build().then(function(id) { + /** @const {string} */ + var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); return parser.start(uri, playerInterface); }) .then(function(manifest) { diff --git a/test/offline/offline_scheme_unit.js b/test/offline/offline_scheme_unit.js index 65a47b5b48..9eb1b7548c 100644 --- a/test/offline/offline_scheme_unit.js +++ b/test/offline/offline_scheme_unit.js @@ -119,15 +119,12 @@ describe('OfflineScheme', function() { }); it('will return special content-type header for manifests', function(done) { - /** @const {number} */ - var id = 789; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.manifestIdToUri(id); + /** @type {string} */ + var uri; Promise.resolve() .then(function() { - return fakeStorageEngine.insertManifest({ - key: id, + return fakeStorageEngine.addManifest({ originalManifestUri: '', duration: 0, size: 0, @@ -138,7 +135,8 @@ describe('OfflineScheme', function() { appMetadata: {} }); }) - .then(function() { + .then(function(id) { + uri = shaka.offline.OfflineScheme.manifestIdToUri(id); return OfflineScheme(uri, request); }) .then(function(response) { @@ -152,22 +150,20 @@ describe('OfflineScheme', function() { }); it('will get segment data from storage engine', function(done) { - /** @const {number} */ - var id = 789; - /** @const {string} */ - var uri = shaka.offline.OfflineScheme.segmentIdToUri(id); - /** @const {!Uint8Array} */ var originalData = new Uint8Array([0, 1, 2, 3]); + /** @type {string} */ + var uri; + Promise.resolve() .then(function() { - return fakeStorageEngine.insertSegment({ - key: id, + return fakeStorageEngine.addSegment({ data: originalData.buffer }); }) - .then(function() { + .then(function(id) { + uri = shaka.offline.OfflineScheme.segmentIdToUri(id); return OfflineScheme(uri, request); }) .then(function(response) { diff --git a/test/offline/storage_unit.js b/test/offline/storage_unit.js index d3207133ba..cd9d3d52f0 100644 --- a/test/offline/storage_unit.js +++ b/test/offline/storage_unit.js @@ -507,9 +507,31 @@ describe('Storage', function() { describe('segments', function() { it('stores media segments', function(done) { + // The IDs and their order may change in a refactor. The constant + // values here can be updated to match the behavior without changing + // the rest of the test." + + /** @const {number} */ + var id1 = 0; + /** @const {number} */ + var id2 = 1; + /** @const {number} */ + var id3 = 2; + /** @const {number} */ + var id4 = 3; + /** @const {number} */ + var id5 = 4; + /** @const {number} */ + var id6 = 5; + + /** @const {number} */ + var fakeDataLength1 = 5; + /** @const {number} */ + var fakeDataLength2 = 7; + netEngine.setResponseMap({ - 'fake:0': new ArrayBuffer(5), - 'fake:1': new ArrayBuffer(7) + 'fake:0': new ArrayBuffer(fakeDataLength1), + 'fake:1': new ArrayBuffer(fakeDataLength2) }); stream1Index.merge([ @@ -523,6 +545,17 @@ describe('Storage', function() { new SegmentReference(0, 0, 1, makeUris('fake:0'), 0, null) ]); + var makeSegment = function(startTime, endTime, id) { + /** @type {string} */ + var uri = Scheme.segmentIdToUri(id); + + return { + startTime: startTime, + endTime: endTime, + uri: uri + }; + }; + storage.store(fakeManifestUri) .then(function(manifest) { expect(manifest).toBeTruthy(); @@ -535,31 +568,23 @@ describe('Storage', function() { var stream1 = manifest.periods[0].streams[0]; expect(stream1.initSegmentUri).toBe(null); expect(stream1.segments.length).toBe(5); - expect(stream1.segments[0]).toEqual({ - startTime: 0, - endTime: 1, - uri: Scheme.segmentIdToUri(0) - }); - expect(stream1.segments[3]).toEqual({ - startTime: 3, - endTime: 4, - uri: Scheme.segmentIdToUri(3) - }); + expect(stream1.segments).toContain(makeSegment(0, 1, id1)); + expect(stream1.segments).toContain(makeSegment(1, 2, id3)); + expect(stream1.segments).toContain(makeSegment(2, 3, id4)); + expect(stream1.segments).toContain(makeSegment(3, 4, id5)); + expect(stream1.segments).toContain(makeSegment(4, 5, id6)); var stream2 = manifest.periods[0].streams[1]; expect(stream2.initSegmentUri).toBe(null); expect(stream2.segments.length).toBe(1); - expect(stream2.segments[0]).toEqual({ - startTime: 0, - endTime: 1, - uri: Scheme.segmentIdToUri(5) - }); - return fakeStorageEngine.getSegment(3); + expect(stream2.segments).toContain(makeSegment(0, 1, id2)); + + return fakeStorageEngine.getSegment(id4); }) .then(function(segment) { expect(segment).toBeTruthy(); expect(segment.data).toBeTruthy(); - expect(segment.data.byteLength).toBe(5); + expect(segment.data.byteLength).toBe(fakeDataLength2); }) .catch(fail) .then(done); @@ -895,9 +920,9 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 4); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { expectDatabaseCount(0, 0); }).catch(fail).then(done); @@ -916,9 +941,9 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 5); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { expectDatabaseCount(0, 0); }).catch(fail).then(done); @@ -941,9 +966,9 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 8); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { expectDatabaseCount(0, 0); }).catch(fail).then(done); @@ -967,9 +992,9 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 8); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { expectDatabaseCount(0, 0); }).catch(fail).then(done); @@ -989,9 +1014,9 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 4); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { expectDatabaseCount(0, 0); }).catch(fail).then(done); @@ -1002,7 +1027,9 @@ describe('Storage', function() { fakeStorageEngine, 'Need storage engine for this test.'); - var manifest1; + var manifestId1; + var manifestId2; + var manifest2; Promise.all([ @@ -1022,14 +1049,17 @@ describe('Storage', function() { .segment(4, 6) .segment(6, 8) .build() - ]).then(function(manifests) { - manifest1 = manifests[0]; - manifest2 = manifests[1]; + ]).then(function(manifestsIds) { + manifestId1 = manifestsIds[0]; + manifestId2 = manifestsIds[1]; expectDatabaseCount(2, 8); - return removeManifest(manifest1.key); + return removeManifest(manifestId1); }).then(function() { expectDatabaseCount(1, 4); + return fakeStorageEngine.getManifest(manifestId2); + }).then(function(manifest) { + manifest2 = manifest; return loadSegmentsForStream(manifest2.periods[0].streams[0]); }).then(function(segments) { // Make sure all the segments for the second manifest are still @@ -1060,9 +1090,9 @@ describe('Storage', function() { segment.uri = Scheme.segmentIdToUri(1253); }) .build() - .then(function(manifest) { + .then(function(manifestId) { expectDatabaseCount(1, 4); - return removeManifest(manifest.key); + return removeManifest(manifestId); }).then(function() { // The segment that was changed above was not deleted. expectDatabaseCount(0, 1); diff --git a/test/test/util/manifest_db_builder.js b/test/test/util/manifest_db_builder.js index 409ad597e9..ee183a5515 100644 --- a/test/test/util/manifest_db_builder.js +++ b/test/test/util/manifest_db_builder.js @@ -31,10 +31,8 @@ shaka.test.ManifestDBBuilder = function(storageEngine) { /** @private {!shaka.offline.IStorageEngine} */ this.storageEngine_ = storageEngine; - /** @type {number} */ - var manifestId = this.storageEngine_.reserveManifestId(); /** @private {shakaExtern.ManifestDB} */ - this.manifest_ = shaka.test.ManifestDBBuilder.emptyManifest_(manifestId); + this.manifest_ = shaka.test.ManifestDBBuilder.emptyManifest_(); /** @private {?shakaExtern.PeriodDB} */ this.currentPeriod_ = null; @@ -43,14 +41,14 @@ shaka.test.ManifestDBBuilder = function(storageEngine) { /** @private {number} */ this.nextStreamId_ = 0; - /** @private {Array} */ - this.storageActions_ = []; + /** @private {!Promise} */ + this.deferredActions_ = Promise.resolve(); }; /** - * Mark the end of building the manifest and return it for use. - * @return {!Promise} + * Mark the end of building the manifest and return the id it was stored under. + * @return {!Promise} */ shaka.test.ManifestDBBuilder.prototype.build = function() { /** @type {shakaExtern.ManifestDB} */ @@ -59,17 +57,11 @@ shaka.test.ManifestDBBuilder.prototype.build = function() { /** @type {!shaka.offline.IStorageEngine} */ var storageEngine = this.storageEngine_; - shaka.log.info(this.storageActions_.length, ' actions'); - - return Promise.all(this.storageActions_) - .then(function() { - // TODO (vaage) : Calculate the duration and size of the manifest before - // writing the manifest to storage. - return storageEngine.insertManifest(manifest); - }) - .then(function() { - return manifest; - }); + return this.deferredActions_.then(function() { + // TODO (vaage) : Calculate the duration and size of the manifest before + // writing the manifest to storage. + return storageEngine.addManifest(manifest); + }); }; @@ -155,11 +147,19 @@ shaka.test.ManifestDBBuilder.prototype.stream = function() { * @return {!shaka.test.ManifestDBBuilder} */ shaka.test.ManifestDBBuilder.prototype.onStream = function(func) { - /** @type {?shakaExtern.StreamDB} */ + goog.asserts.assert( + this.currentStream_, + 'Must have current stream when using onStream.'); + + /** @type {shakaExtern.StreamDB} */ var stream = this.currentStream_; - goog.asserts.assert(stream, 'Must have current stream when using onStream.'); - shaka.log.info(stream); - func(stream); + + // Need to defer this function as the segments would not have been + // added to it yet. + this.deferredActions_ = this.deferredActions_.then(function() { + func(stream); + }); + return this; }; @@ -171,21 +171,29 @@ shaka.test.ManifestDBBuilder.prototype.onStream = function(func) { * @return {!shaka.test.ManifestDBBuilder} */ shaka.test.ManifestDBBuilder.prototype.initSegment = function() { + goog.asserts.assert( + this.currentStream_, + 'Must have a currewnt stream to add a segment.'); + /** @const */ var Scheme = shaka.offline.OfflineScheme; /** @type {!shaka.offline.IStorageEngine} */ var storageEngine = this.storageEngine_; - /** @type {number} */ - var id = storageEngine.reserveSegmentId(); - /** @type {string} */ - var uri = Scheme.segmentIdToUri(id); + /** @type {shakaExtern.SegmentDataDB} */ + var segmentData = shaka.test.ManifestDBBuilder.emptySegment_(); - this.currentStream_.initSegmentUri = uri; + /** @type {shakaExtern.StreamDB} */ + var currentStream = this.currentStream_; - this.storageActions_.push(storageEngine.insertSegment( - shaka.test.ManifestDBBuilder.emptySegment_(id))); + this.deferredActions_ = this.deferredActions_.then(function() { + return storageEngine.addSegment(segmentData); + }).then(function(id) { + /** @type {string} */ + var uri = Scheme.segmentIdToUri(id); + currentStream.initSegmentUri = uri; + }); return this; }; @@ -199,7 +207,12 @@ shaka.test.ManifestDBBuilder.prototype.initSegment = function() { * @return {!shaka.test.ManifestDBBuilder} */ shaka.test.ManifestDBBuilder.prototype.segment = function(start, end) { - goog.asserts.assert(start < end, 'Start should always be less than end'); + goog.asserts.assert( + this.currentStream_, + 'Must have a current stream to add a segment.'); + goog.asserts.assert( + start < end, + 'Start should always be less than end'); /** @const */ var Scheme = shaka.offline.OfflineScheme; @@ -207,22 +220,27 @@ shaka.test.ManifestDBBuilder.prototype.segment = function(start, end) { /** @type {!shaka.offline.IStorageEngine} */ var storageEngine = this.storageEngine_; - /** @type {number} */ - var id = storageEngine.reserveSegmentId(); - /** @type {string} */ - var uri = Scheme.segmentIdToUri(id); + /** @type {shakaExtern.SegmentDataDB} */ + var segmentData = shaka.test.ManifestDBBuilder.emptySegment_(); - /** @type {shakaExtern.SegmentDB} */ - var segment = { - uri: uri, - startTime: start, - endTime: end - }; + /** @type {shakaExtern.StreamDB} */ + var currentStream = this.currentStream_; + + this.deferredActions_ = this.deferredActions_.then(function() { + return storageEngine.addSegment(segmentData); + }).then(function(id) { + /** @type {string} */ + var uri = Scheme.segmentIdToUri(id); - this.currentStream_.segments.push(segment); + /** @type {shakaExtern.SegmentDB} */ + var segment = { + uri: uri, + startTime: start, + endTime: end + }; - this.storageActions_.push(storageEngine.insertSegment( - shaka.test.ManifestDBBuilder.emptySegment_(id))); + currentStream.segments.push(segment); + }); return this; }; @@ -230,14 +248,12 @@ shaka.test.ManifestDBBuilder.prototype.segment = function(start, end) { /** * Create an empty manifest as the starting point for all manifests. - * @param {number} id * @return {shakaExtern.ManifestDB} * @private */ -shaka.test.ManifestDBBuilder.emptyManifest_ = function(id) { +shaka.test.ManifestDBBuilder.emptyManifest_ = function() { /** @type {shakaExtern.ManifestDB} */ var manifest = { - key: id, originalManifestUri: '', duration: 10, // TODO(vaage) : calculate this from the segments size: 10, @@ -255,18 +271,13 @@ shaka.test.ManifestDBBuilder.emptyManifest_ = function(id) { /** * Create an empty segment that can be inserted into storage. The data in this * segment is meaningless. - * @param {number} id * @return {shakaExtern.SegmentDataDB} * @private */ -shaka.test.ManifestDBBuilder.emptySegment_ = function(id) { +shaka.test.ManifestDBBuilder.emptySegment_ = function() { /** @type {shakaExtern.SegmentDataDB} */ var segment = { - data: null, - key: id, - manifestKey: 0, - segmentNumber: 0, - streamNumber: 0 + data: null }; return segment; diff --git a/test/test/util/memory_storage_engine.js b/test/test/util/memory_storage_engine.js index fece6c1f5e..ed46ffc2b9 100644 --- a/test/test/util/memory_storage_engine.js +++ b/test/test/util/memory_storage_engine.js @@ -55,18 +55,31 @@ shaka.test.MemoryStorageEngine.prototype.getManifest = function(key) { /** @override */ shaka.test.MemoryStorageEngine.prototype.forEachManifest = function(each) { - shaka.util.MapUtils.forEach(this.manifests_, function(key, value) { - return each(value); - }); + shaka.util.MapUtils.forEach(this.manifests_, each); return Promise.resolve(); }; /** @override */ -shaka.test.MemoryStorageEngine.prototype.insertManifest = - function(manifest) { - this.manifests_[manifest.key] = manifest; - return Promise.resolve(); +shaka.test.MemoryStorageEngine.prototype.addManifest = function(manifest) { + /** @type {number} */ + var key = this.nextManifestId_++; + + this.manifests_[key] = manifest; + return Promise.resolve(key); +}; + + +/** @override */ +shaka.test.MemoryStorageEngine.prototype.updateManifest = function( + key, manifest) { + + if (this.manifests_[key]) { + this.manifests_[key] = manifest; + return Promise.resolve(key); + } else { + return Promise.reject(); + } }; @@ -82,12 +95,6 @@ shaka.test.MemoryStorageEngine.prototype.removeManifests = }; -/** @override */ -shaka.test.MemoryStorageEngine.prototype.reserveManifestId = function() { - return this.nextManifestId_++; -}; - - /** @override */ shaka.test.MemoryStorageEngine.prototype.getSegment = function(key) { var segment = this.segments_[key]; @@ -97,15 +104,16 @@ shaka.test.MemoryStorageEngine.prototype.getSegment = function(key) { /** @override */ shaka.test.MemoryStorageEngine.prototype.forEachSegment = function(each) { - shaka.util.MapUtils.forEach(this.segments_, function(key, value) { - return each(value); - }); + shaka.util.MapUtils.forEach(this.segments_, each); return Promise.resolve(); }; /** @override */ -shaka.test.MemoryStorageEngine.prototype.insertSegment = function(segment) { +shaka.test.MemoryStorageEngine.prototype.addSegment = function(segment) { + /** @type {number} */ + var key = this.nextSegmentId_++; + // Clone the segment, so the caller can wipe its version. var clonedData = null; if (segment.data) { @@ -113,11 +121,11 @@ shaka.test.MemoryStorageEngine.prototype.insertSegment = function(segment) { (new Uint8Array(clonedData)).set(new Uint8Array(segment.data)); } - this.segments_[segment.key] = { - key: segment.key, + this.segments_[key] = { data: clonedData }; - return Promise.resolve(); + + return Promise.resolve(key); }; @@ -133,12 +141,6 @@ shaka.test.MemoryStorageEngine.prototype.removeSegments = }; -/** @override */ -shaka.test.MemoryStorageEngine.prototype.reserveSegmentId = function() { - return this.nextSegmentId_++; -}; - - /** * @param {!Object} group * @param {!Array} keys