From 73f6de3e01ae4ed3b86302add7ee16c86c3b9b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Thu, 28 Apr 2022 20:30:52 +0200 Subject: [PATCH] feat(offline): improve the speed of offline downloads (#4168) Add the possibility to configure the number of downloads in parallel per stream. If you put a higher value the download time can decrease. Related to https://github.com/shaka-project/shaka-player/issues/4166 Co-authored-by: Joey Parrish --- demo/common/message_ids.js | 1 + demo/config.js | 4 +++- demo/locales/en.json | 1 + demo/locales/source.json | 4 ++++ demo/main.js | 3 +++ externs/shaka/player.js | 8 +++++++- lib/offline/storage.js | 8 ++++++-- lib/util/player_configuration.js | 2 ++ test/offline/storage_integration.js | 5 +---- 9 files changed, 28 insertions(+), 8 deletions(-) diff --git a/demo/common/message_ids.js b/demo/common/message_ids.js index 76b4eab01c..069d6620b2 100644 --- a/demo/common/message_ids.js +++ b/demo/common/message_ids.js @@ -223,6 +223,7 @@ shakaDemo.MessageIds = { NUMBER_INTEGER_WARNING: 'DEMO_NUMBER_INTEGER_WARNING', NUMBER_NONZERO_DECIMAL_WARNING: 'DEMO_NUMBER_NONZERO_DECIMAL_WARNING', NUMBER_NONZERO_INTEGER_WARNING: 'DEMO_NUMBER_NONZERO_INTEGER_WARNING', + NUMBER_OF_PARALLEL_DOWNLOADS: 'DEMO_NUMBER_OF_PARALLEL_DOWNLOADS', OFFLINE_SECTION_HEADER: 'DEMO_OFFLINE_SECTION_HEADER', PREFER_FORCED_SUBS: 'DEMO_PREFER_FORCED_SUBS', PREFER_NATIVE_HLS: 'DEMO_PREFER_NATIVE_HLS', diff --git a/demo/config.js b/demo/config.js index b44071df05..a68a04d885 100644 --- a/demo/config.js +++ b/demo/config.js @@ -341,7 +341,9 @@ shakaDemo.Config = class { const docLink = this.resolveExternLink_('.OfflineConfiguration'); this.addSection_(MessageIds.OFFLINE_SECTION_HEADER, docLink) .addBoolInput_(MessageIds.USE_PERSISTENT_LICENSES, - 'offline.usePersistentLicense'); + 'offline.usePersistentLicense') + .addNumberInput_(MessageIds.NUMBER_OF_PARALLEL_DOWNLOADS, + 'offline.numberOfParallelDownloads'); } /** @private */ diff --git a/demo/locales/en.json b/demo/locales/en.json index 080c9457ac..adcb726bd6 100644 --- a/demo/locales/en.json +++ b/demo/locales/en.json @@ -159,6 +159,7 @@ "DEMO_NUMBER_INTEGER_WARNING": "Must be a positive integer.", "DEMO_NUMBER_NONZERO_DECIMAL_WARNING": "Must be a positive, nonzero number.", "DEMO_NUMBER_NONZERO_INTEGER_WARNING": "Must be a positive, nonzero integer.", + "DEMO_NUMBER_OF_PARALLEL_DOWNLOADS": "Number of parallel downloads", "DEMO_OBSERVE_QUALITY_CHANGES": "Observe media quality changes", "DEMO_OFFLINE": "Downloadable", "DEMO_OFFLINE_SEARCH": "Filters for assets that can be stored offline.", diff --git a/demo/locales/source.json b/demo/locales/source.json index 753888df71..500be98513 100644 --- a/demo/locales/source.json +++ b/demo/locales/source.json @@ -639,6 +639,10 @@ "description": "A warning on number inputs, telling the user what the expected input format is.", "message": "Must be a positive, nonzero integer." }, + "DEMO_NUMBER_OF_PARALLEL_DOWNLOADS": { + "description": "The name of a configuration value.", + "message": "Number of parallel downloads." + }, "DEMO_OBSERVE_QUALITY_CHANGES": { "description": "The name of a configuration value.", "message": "Observe media quality changes" diff --git a/demo/main.js b/demo/main.js index c3d3b26092..95a24da1ba 100644 --- a/demo/main.js +++ b/demo/main.js @@ -582,7 +582,10 @@ shakaDemo.Main = class { }; asset.storedProgress = 0; this.dispatchEventWithName_('shaka-main-offline-progress'); + const start = Date.now(); const stored = await storage.store(asset.manifestUri, metadata).promise; + const end = Date.now(); + console.log('Download time:', end - start); asset.storedContent = stored; } catch (error) { this.onError_(/** @type {!shaka.util.Error} */ (error)); diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 886fffb5f8..ca73947a39 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -1075,7 +1075,8 @@ shaka.extern.CmcdConfiguration; * function(shaka.extern.TrackList):!Promise, * downloadSizeCallback: function(number):!Promise, * progressCallback: function(shaka.extern.StoredContent,number), - * usePersistentLicense: boolean + * usePersistentLicense: boolean, + * numberOfParallelDownloads: number * }} * * @property {function(shaka.extern.TrackList):!Promise} @@ -1098,6 +1099,11 @@ shaka.extern.CmcdConfiguration; * license. A network will be required to retrieve a temporary license to * view. * Defaults to true. + * @property {number} numberOfParallelDownloads + * Number of parallel downloads. + * Note: normally browsers limit to 5 request in parallel, so putting a + * number higher than this will not help it download faster. + * Defaults to 5. * @exportDoc */ shaka.extern.OfflineConfiguration; diff --git a/lib/offline/storage.js b/lib/offline/storage.js index 04eb8eca93..1b6c0f7690 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -1324,6 +1324,9 @@ shaka.offline.Storage = class { const startTime = manifest.presentationTimeline.getSegmentAvailabilityStart(); + const numberOfParallelDownloads = config.offline.numberOfParallelDownloads; + let groupId = 0; + shaka.offline.Storage.forEachSegment_(stream, startTime, (segment) => { const pendingSegmentRefId = shaka.offline.DownloadInfo.idForSegmentRef(segment); @@ -1337,7 +1340,7 @@ shaka.offline.Storage = class { const segmentDownload = new shaka.offline.DownloadInfo( segment, estimateId, - stream.id, + groupId, /* isInitSegment= */ false); toDownload.set(pendingSegmentRefId, segmentDownload); } @@ -1352,7 +1355,7 @@ shaka.offline.Storage = class { const initDownload = new shaka.offline.DownloadInfo( segment.initSegmentReference, estimateId, - stream.id, + groupId, /* isInitSegment= */ true); toDownload.set(pendingInitSegmentRefId, initDownload); } @@ -1372,6 +1375,7 @@ shaka.offline.Storage = class { dataKey: 0, }; streamDb.segments.push(segmentDB); + groupId = (groupId + 1) % numberOfParallelDownloads; }); return streamDb; diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 6a6eda9c4a..e9367ec2df 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -222,6 +222,8 @@ shaka.util.PlayerConfiguration = class { // unexpected behaviours when someone tries to plays downloaded content // without a persistent license. usePersistentLicense: true, + + numberOfParallelDownloads: 5, }; const abr = { diff --git a/test/offline/storage_integration.js b/test/offline/storage_integration.js index ee5b39ecd2..6b7ee341f8 100644 --- a/test/offline/storage_integration.js +++ b/test/offline/storage_integration.js @@ -1110,10 +1110,7 @@ filterDescribe('Storage', storageSupport, () => { aRequestIsStarted.resolve(); await promise; - // All downloads for a given stream are in the same "download group", - // and will be downloaded sequentially. Thus, we expect only the first - // download to be aborted. - expect(abortCheck()).toBe(i == 0 ? true : false); + expect(abortCheck()).toBe(true); return new ArrayBuffer(16); });