Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(offline): improve the speed of offline downloads #4168

Merged
merged 3 commits into from Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/common/message_ids.js
Expand Up @@ -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',
Expand Down
4 changes: 3 additions & 1 deletion demo/config.js
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions demo/locales/en.json
Expand Up @@ -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.",
Expand Down
4 changes: 4 additions & 0 deletions demo/locales/source.json
Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions demo/main.js
Expand Up @@ -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));
Expand Down
9 changes: 8 additions & 1 deletion externs/shaka/player.js
Expand Up @@ -1075,7 +1075,8 @@ shaka.extern.CmcdConfiguration;
* function(shaka.extern.TrackList):!Promise<shaka.extern.TrackList>,
* downloadSizeCallback: function(number):!Promise<boolean>,
* progressCallback: function(shaka.extern.StoredContent,number),
* usePersistentLicense: boolean
* usePersistentLicense: boolean,
* numberOfParallelDownloads: number
* }}
*
* @property {function(shaka.extern.TrackList):!Promise<shaka.extern.TrackList>}
Expand All @@ -1098,6 +1099,12 @@ shaka.extern.CmcdConfiguration;
* license. A network will be required to retrieve a temporary license to
* view.
* Defaults to <code>true</code>.
* @property {number} numberOfParallelDownloads
* Number of parallel downloads. Putting a number other than the default can
* help reduce download time.
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
* Note: normally browsers limit to 5 request in parallel, so putting a
* number higher than this will not help it download faster.
* Defaults to <code>5</code>.
* @exportDoc
*/
shaka.extern.OfflineConfiguration;
Expand Down
12 changes: 10 additions & 2 deletions lib/offline/storage.js
Expand Up @@ -1324,7 +1324,14 @@ shaka.offline.Storage = class {
const startTime =
manifest.presentationTimeline.getSegmentAvailabilityStart();

const numberOfParallelDownloads = config.offline.numberOfParallelDownloads;
let groupId = 0;

shaka.offline.Storage.forEachSegment_(stream, startTime, (segment) => {
if (groupId >= numberOfParallelDownloads) {
groupId = 0;
}
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved

const pendingSegmentRefId =
shaka.offline.DownloadInfo.idForSegmentRef(segment);
let pendingInitSegmentRefId = undefined;
Expand All @@ -1337,7 +1344,7 @@ shaka.offline.Storage = class {
const segmentDownload = new shaka.offline.DownloadInfo(
segment,
estimateId,
stream.id,
groupId,
/* isInitSegment= */ false);
toDownload.set(pendingSegmentRefId, segmentDownload);
}
Expand All @@ -1352,7 +1359,7 @@ shaka.offline.Storage = class {
const initDownload = new shaka.offline.DownloadInfo(
segment.initSegmentReference,
estimateId,
stream.id,
groupId,
/* isInitSegment= */ true);
toDownload.set(pendingInitSegmentRefId, initDownload);
}
Expand All @@ -1372,6 +1379,7 @@ shaka.offline.Storage = class {
dataKey: 0,
};
streamDb.segments.push(segmentDB);
groupId++;
});

return streamDb;
Expand Down
2 changes: 2 additions & 0 deletions lib/util/player_configuration.js
Expand Up @@ -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 = {
Expand Down
5 changes: 1 addition & 4 deletions test/offline/storage_integration.js
Expand Up @@ -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);
});
Expand Down