shaka-player 3.1.4 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/AUTHORS +6 -5
- package/CHANGELOG.md +363 -0
- package/CONTRIBUTORS +4 -2
- package/README.md +4 -4
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +2 -0
- package/build/types/ui +2 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +28 -1
- package/demo/common/message_ids.js +15 -2
- package/demo/config.js +35 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +14 -1
- package/demo/locales/source.json +56 -4
- package/demo/main.js +2 -1
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +24 -21
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +24 -21
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +33 -25
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +14 -12
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +14 -12
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.externs.js +1005 -695
- package/dist/shaka-player.compiled.debug.js +720 -640
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1005 -695
- package/dist/shaka-player.compiled.js +850 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +274 -21
- package/dist/shaka-player.ui.debug.d.ts +274 -21
- package/dist/shaka-player.ui.debug.externs.js +766 -429
- package/dist/shaka-player.ui.debug.js +863 -765
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +766 -429
- package/dist/shaka-player.ui.js +1042 -996
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +29 -1
- package/externs/shaka/manifest.js +18 -2
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +36 -2
- package/externs/shaka/player.js +182 -13
- package/externs/shaka/text.js +24 -10
- package/externs/texttrack.js +3 -0
- package/externs/webcrypto.js +18 -0
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +14 -6
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +7 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +53 -14
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +210 -30
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +9 -1
- package/lib/media/segment_reference.js +37 -5
- package/lib/media/streaming_engine.js +48 -10
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +82 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +14 -7
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +404 -297
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +622 -153
- package/lib/polyfill/aria.js +82 -0
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/cue.js +12 -0
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/ttml_text_parser.js +25 -2
- package/lib/text/ui_text_displayer.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/cmcd_manager.js +826 -0
- package/lib/util/error.js +12 -0
- package/lib/util/language_utils.js +12 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +31 -1
- package/lib/util/stream_utils.js +140 -85
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +4 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +101 -1
- package/test/dash/dash_parser_manifest_unit.js +78 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +252 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- package/test/media/quality_observer_unit.js +139 -0
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +2 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +117 -23
- package/test/player_integration.js +151 -0
- package/test/player_src_equals_integration.js +52 -0
- package/test/player_unit.js +11 -33
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_ad_manager.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +5 -2
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/test/util/test_scheme.js +14 -1
- package/test/text/ttml_text_parser_unit.js +47 -0
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +277 -0
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +2 -1
- package/ui/audio_language_selection.js +9 -5
- package/ui/cast_button.js +5 -6
- package/ui/constants.js +0 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +41 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +16 -8
- package/ui/fullscreen_button.js +2 -3
- package/ui/language_utils.js +1 -1
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/loop_button.js +6 -3
- package/ui/mute_button.js +3 -4
- package/ui/overflow_menu.js +7 -28
- package/ui/pip_button.js +10 -7
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +11 -15
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +11 -7
- package/ui/rewind_button.js +17 -9
- package/ui/seek_bar.js +6 -6
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/spacer.js +1 -1
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +19 -15
- package/ui/ui.js +30 -0
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +2 -4
package/lib/offline/storage.js
CHANGED
|
@@ -13,6 +13,7 @@ goog.require('shaka.log');
|
|
|
13
13
|
goog.require('shaka.media.DrmEngine');
|
|
14
14
|
goog.require('shaka.media.ManifestParser');
|
|
15
15
|
goog.require('shaka.net.NetworkingEngine');
|
|
16
|
+
goog.require('shaka.offline.DownloadInfo');
|
|
16
17
|
goog.require('shaka.offline.DownloadManager');
|
|
17
18
|
goog.require('shaka.offline.OfflineUri');
|
|
18
19
|
goog.require('shaka.offline.SessionDeleter');
|
|
@@ -28,11 +29,10 @@ goog.require('shaka.util.Functional');
|
|
|
28
29
|
goog.require('shaka.util.IDestroyable');
|
|
29
30
|
goog.require('shaka.util.Iterables');
|
|
30
31
|
goog.require('shaka.util.MimeUtils');
|
|
31
|
-
goog.require('shaka.util.
|
|
32
|
+
goog.require('shaka.util.Mutex');
|
|
32
33
|
goog.require('shaka.util.Platform');
|
|
33
34
|
goog.require('shaka.util.PlayerConfiguration');
|
|
34
35
|
goog.require('shaka.util.StreamUtils');
|
|
35
|
-
goog.requireType('shaka.media.InitSegmentReference');
|
|
36
36
|
goog.requireType('shaka.media.SegmentReference');
|
|
37
37
|
goog.requireType('shaka.offline.StorageCellHandle');
|
|
38
38
|
|
|
@@ -95,14 +95,6 @@ shaka.offline.Storage = class {
|
|
|
95
95
|
this.networkingEngine_ = new shaka.net.NetworkingEngine();
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/**
|
|
99
|
-
* A list of segment ids for all the segments that were added during the
|
|
100
|
-
* current store. If the store fails or is aborted, these need to be
|
|
101
|
-
* removed from storage.
|
|
102
|
-
* @private {!Array.<number>}
|
|
103
|
-
*/
|
|
104
|
-
this.segmentsFromStore_ = [];
|
|
105
|
-
|
|
106
98
|
/**
|
|
107
99
|
* A list of open operations that are being performed by this instance of
|
|
108
100
|
* |shaka.offline.Storage|.
|
|
@@ -285,7 +277,7 @@ shaka.offline.Storage = class {
|
|
|
285
277
|
store(uri, appMetadata, mimeType) {
|
|
286
278
|
goog.asserts.assert(
|
|
287
279
|
this.networkingEngine_,
|
|
288
|
-
'Cannot call |
|
|
280
|
+
'Cannot call |store| after calling |destroy|.');
|
|
289
281
|
|
|
290
282
|
// Get a copy of the current config.
|
|
291
283
|
const config = this.getConfiguration();
|
|
@@ -371,6 +363,8 @@ shaka.offline.Storage = class {
|
|
|
371
363
|
const muxer = new shaka.offline.StorageMuxer();
|
|
372
364
|
/** @type {?shaka.offline.StorageCellHandle} */
|
|
373
365
|
let activeHandle = null;
|
|
366
|
+
/** @type {?number} */
|
|
367
|
+
let manifestId = null;
|
|
374
368
|
|
|
375
369
|
// This will be used to store any errors from drm engine. Whenever drm
|
|
376
370
|
// engine is passed to another function to do work, we should check if this
|
|
@@ -398,6 +392,7 @@ shaka.offline.Storage = class {
|
|
|
398
392
|
uri);
|
|
399
393
|
}
|
|
400
394
|
|
|
395
|
+
// Create the DRM engine, and load the keys in the manifest.
|
|
401
396
|
drmEngine = await this.createDrmEngine(
|
|
402
397
|
manifest,
|
|
403
398
|
(e) => { drmError = drmError || e; },
|
|
@@ -422,38 +417,38 @@ shaka.offline.Storage = class {
|
|
|
422
417
|
this.ensureNotDestroyed_();
|
|
423
418
|
|
|
424
419
|
goog.asserts.assert(drmEngine, 'drmEngine should be non-null here.');
|
|
420
|
+
const {manifestDB, toDownload} = this.makeManifestDB_(
|
|
421
|
+
drmEngine, manifest, uri, appMetadata, config, downloader);
|
|
425
422
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
423
|
+
// Store the empty manifest, before downloading the segments.
|
|
424
|
+
const ids = await activeHandle.cell.addManifests([manifestDB]);
|
|
425
|
+
this.ensureNotDestroyed_();
|
|
426
|
+
manifestId = ids[0];
|
|
427
|
+
|
|
428
|
+
goog.asserts.assert(drmEngine, 'drmEngine should be non-null here.');
|
|
429
429
|
this.ensureNotDestroyed_();
|
|
430
430
|
if (drmError) {
|
|
431
431
|
throw drmError;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
|
|
434
|
+
await this.downloadSegments_(toDownload, manifestId, manifestDB,
|
|
435
|
+
downloader, config, activeHandle.cell, manifest, drmEngine);
|
|
435
436
|
this.ensureNotDestroyed_();
|
|
436
437
|
|
|
437
438
|
const offlineUri = shaka.offline.OfflineUri.manifest(
|
|
438
|
-
activeHandle.path.mechanism, activeHandle.path.cell,
|
|
439
|
+
activeHandle.path.mechanism, activeHandle.path.cell, manifestId);
|
|
439
440
|
|
|
440
441
|
return shaka.offline.StoredContentUtils.fromManifestDB(
|
|
441
442
|
offlineUri, manifestDB);
|
|
442
443
|
} catch (e) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// then we won't have an active cell to remove the segments from.
|
|
446
|
-
if (activeHandle) {
|
|
447
|
-
await activeHandle.cell.removeSegments(
|
|
448
|
-
this.segmentsFromStore_, () => {});
|
|
444
|
+
if (manifestId != null) {
|
|
445
|
+
await shaka.offline.Storage.cleanStoredManifest(manifestId);
|
|
449
446
|
}
|
|
450
447
|
|
|
451
448
|
// If we already had an error, ignore this error to avoid hiding
|
|
452
449
|
// the original error.
|
|
453
450
|
throw drmError || e;
|
|
454
451
|
} finally {
|
|
455
|
-
this.segmentsFromStore_ = [];
|
|
456
|
-
|
|
457
452
|
await muxer.destroy();
|
|
458
453
|
|
|
459
454
|
if (parser) {
|
|
@@ -466,6 +461,215 @@ shaka.offline.Storage = class {
|
|
|
466
461
|
}
|
|
467
462
|
}
|
|
468
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Download and then store the contents of each segment.
|
|
466
|
+
* The promise this returns will wait for local downloads.
|
|
467
|
+
*
|
|
468
|
+
* @param {!Array.<!shaka.offline.DownloadInfo>} toDownload
|
|
469
|
+
* @param {number} manifestId
|
|
470
|
+
* @param {shaka.extern.ManifestDB} manifestDB
|
|
471
|
+
* @param {!shaka.offline.DownloadManager} downloader
|
|
472
|
+
* @param {shaka.extern.PlayerConfiguration} config
|
|
473
|
+
* @param {shaka.extern.StorageCell} storage
|
|
474
|
+
* @param {shaka.extern.Manifest} manifest
|
|
475
|
+
* @param {!shaka.media.DrmEngine} drmEngine
|
|
476
|
+
* @return {!Promise}
|
|
477
|
+
* @private
|
|
478
|
+
*/
|
|
479
|
+
async downloadSegments_(
|
|
480
|
+
toDownload, manifestId, manifestDB, downloader, config, storage,
|
|
481
|
+
manifest, drmEngine) {
|
|
482
|
+
/**
|
|
483
|
+
* @param {!Array.<!shaka.offline.DownloadInfo>} toDownload
|
|
484
|
+
* @param {boolean} updateDRM
|
|
485
|
+
*/
|
|
486
|
+
const download = async (toDownload, updateDRM) => {
|
|
487
|
+
const throwIfAbortedFn = () => {
|
|
488
|
+
this.ensureNotDestroyed_();
|
|
489
|
+
};
|
|
490
|
+
for (const download of toDownload) {
|
|
491
|
+
/** @param {?BufferSource} data */
|
|
492
|
+
let data;
|
|
493
|
+
const request = download.makeSegmentRequest(config);
|
|
494
|
+
const estimateId = download.estimateId;
|
|
495
|
+
const isInitSegment = download.isInitSegment;
|
|
496
|
+
const onDownloaded = (d) => {
|
|
497
|
+
data = d;
|
|
498
|
+
return Promise.resolve();
|
|
499
|
+
};
|
|
500
|
+
downloader.queue(download.groupId,
|
|
501
|
+
request, estimateId, isInitSegment, onDownloaded);
|
|
502
|
+
downloader.queueWork(download.groupId, async () => {
|
|
503
|
+
goog.asserts.assert(data, 'We should have loaded data by now');
|
|
504
|
+
goog.asserts.assert(data instanceof ArrayBuffer,
|
|
505
|
+
'The data should be an ArrayBuffer');
|
|
506
|
+
|
|
507
|
+
const ref = /** @type {!shaka.media.SegmentReference} */ (
|
|
508
|
+
download.ref);
|
|
509
|
+
manifestDB = (await shaka.offline.Storage.assignStreamToManifest(
|
|
510
|
+
manifestId, ref, {data}, throwIfAbortedFn)) || manifestDB;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
await downloader.waitToFinish();
|
|
514
|
+
|
|
515
|
+
if (updateDRM) {
|
|
516
|
+
// Re-store the manifest, to attach session IDs.
|
|
517
|
+
// These were (maybe) discovered inside the downloader; we can only add
|
|
518
|
+
// them now, at the end, since the manifestDB is in flux during the
|
|
519
|
+
// process of downloading and storing, and assignStreamToManifest does
|
|
520
|
+
// not know about the DRM engine.
|
|
521
|
+
this.ensureNotDestroyed_();
|
|
522
|
+
this.setManifestDrmFields_(manifest, manifestDB, drmEngine, config);
|
|
523
|
+
await storage.updateManifest(manifestId, manifestDB);
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const usingBgFetch = false; // TODO: Get.
|
|
528
|
+
|
|
529
|
+
if (this.getManifestIsEncrypted_(manifest) && usingBgFetch &&
|
|
530
|
+
!this.getManifestIncludesInitData_(manifest)) {
|
|
531
|
+
// Background fetch can't make DRM sessions, so if we have to get the
|
|
532
|
+
// init data from the init segments, download those first before anything
|
|
533
|
+
// else.
|
|
534
|
+
await download(toDownload.filter((info) => info.isInitSegment), true);
|
|
535
|
+
this.ensureNotDestroyed_();
|
|
536
|
+
toDownload = toDownload.filter((info) => !info.isInitSegment);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (!usingBgFetch) {
|
|
540
|
+
await download(toDownload, false);
|
|
541
|
+
this.ensureNotDestroyed_();
|
|
542
|
+
|
|
543
|
+
goog.asserts.assert(
|
|
544
|
+
!manifestDB.isIncomplete, 'The manifest should be complete by now');
|
|
545
|
+
} else {
|
|
546
|
+
// TODO: Send the request to the service worker. Don't await the result.
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Removes all of the contents for a given manifest, statelessly.
|
|
552
|
+
*
|
|
553
|
+
* @param {number} manifestId
|
|
554
|
+
* @return {!Promise}
|
|
555
|
+
*/
|
|
556
|
+
static async cleanStoredManifest(manifestId) {
|
|
557
|
+
const muxer = new shaka.offline.StorageMuxer();
|
|
558
|
+
await muxer.init();
|
|
559
|
+
const activeHandle = await muxer.getActive();
|
|
560
|
+
const uri = shaka.offline.OfflineUri.manifest(
|
|
561
|
+
activeHandle.path.mechanism,
|
|
562
|
+
activeHandle.path.cell,
|
|
563
|
+
manifestId);
|
|
564
|
+
await muxer.destroy();
|
|
565
|
+
const storage = new shaka.offline.Storage();
|
|
566
|
+
await storage.remove(uri.toString());
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Load the given manifest, modifies it by assigning the given data to the
|
|
571
|
+
* segments corresponding to "ref", then re-stores the manifest.
|
|
572
|
+
* The parts of this function that modify the manifest are protected by a
|
|
573
|
+
* mutex, to prevent race conditions; specifically, it prevents two parallel
|
|
574
|
+
* instances of this method from both loading the manifest into memory at the
|
|
575
|
+
* same time, which would result in the slower/later call overwriting the
|
|
576
|
+
* changes of the other.
|
|
577
|
+
*
|
|
578
|
+
* @param {number} manifestId
|
|
579
|
+
* @param {!shaka.media.SegmentReference} ref
|
|
580
|
+
* @param {shaka.extern.SegmentDataDB} data
|
|
581
|
+
* @param {function()} throwIfAbortedFn A function that should throw if the
|
|
582
|
+
* download has been aborted.
|
|
583
|
+
* @return {!Promise.<?shaka.extern.ManifestDB>}
|
|
584
|
+
*/
|
|
585
|
+
static async assignStreamToManifest(manifestId, ref, data, throwIfAbortedFn) {
|
|
586
|
+
/** @type {shaka.offline.StorageMuxer} */
|
|
587
|
+
const muxer = new shaka.offline.StorageMuxer();
|
|
588
|
+
|
|
589
|
+
const idForRef = shaka.offline.DownloadInfo.idForSegmentRef(ref);
|
|
590
|
+
let manifestUpdated = false;
|
|
591
|
+
let dataKey;
|
|
592
|
+
let activeHandle;
|
|
593
|
+
/** @type {!shaka.extern.ManifestDB} */
|
|
594
|
+
let manifestDB;
|
|
595
|
+
|
|
596
|
+
let mutexId = 0;
|
|
597
|
+
|
|
598
|
+
try {
|
|
599
|
+
await muxer.init();
|
|
600
|
+
activeHandle = await muxer.getActive();
|
|
601
|
+
|
|
602
|
+
// Store the data.
|
|
603
|
+
const dataKeys = await activeHandle.cell.addSegments([data]);
|
|
604
|
+
dataKey = dataKeys[0];
|
|
605
|
+
throwIfAbortedFn();
|
|
606
|
+
|
|
607
|
+
// Acquire the mutex before accessing the manifest, since there could be
|
|
608
|
+
// multiple instances of this method running at once.
|
|
609
|
+
mutexId = await shaka.offline.Storage.mutex_.acquire();
|
|
610
|
+
throwIfAbortedFn();
|
|
611
|
+
|
|
612
|
+
// Load the manifest.
|
|
613
|
+
const manifests = await activeHandle.cell.getManifests([manifestId]);
|
|
614
|
+
throwIfAbortedFn();
|
|
615
|
+
manifestDB = manifests[0];
|
|
616
|
+
|
|
617
|
+
// Assign the stored data to the manifest.
|
|
618
|
+
let complete = true;
|
|
619
|
+
for (const stream of manifestDB.streams) {
|
|
620
|
+
for (const segment of stream.segments) {
|
|
621
|
+
if (segment.pendingSegmentRefId == idForRef) {
|
|
622
|
+
segment.dataKey = dataKey;
|
|
623
|
+
// Now that the segment has been associated with the appropriate
|
|
624
|
+
// dataKey, the pendingSegmentRefId is no longer necessary.
|
|
625
|
+
segment.pendingSegmentRefId = undefined;
|
|
626
|
+
}
|
|
627
|
+
if (segment.pendingInitSegmentRefId == idForRef) {
|
|
628
|
+
segment.initSegmentKey = dataKey;
|
|
629
|
+
// Now that the init segment has been associated with the
|
|
630
|
+
// appropriate initSegmentKey, the pendingInitSegmentRefId is no
|
|
631
|
+
// longer necessary.
|
|
632
|
+
segment.pendingInitSegmentRefId = undefined;
|
|
633
|
+
}
|
|
634
|
+
if (segment.pendingSegmentRefId) {
|
|
635
|
+
complete = false;
|
|
636
|
+
}
|
|
637
|
+
if (segment.pendingInitSegmentRefId) {
|
|
638
|
+
complete = false;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Update the size of the manifest.
|
|
644
|
+
manifestDB.size += data.data.byteLength;
|
|
645
|
+
|
|
646
|
+
// Mark the manifest as complete, if all segments are downloaded.
|
|
647
|
+
if (complete) {
|
|
648
|
+
manifestDB.isIncomplete = false;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Re-store the manifest.
|
|
652
|
+
await activeHandle.cell.updateManifest(manifestId, manifestDB);
|
|
653
|
+
manifestUpdated = true;
|
|
654
|
+
throwIfAbortedFn();
|
|
655
|
+
} catch (e) {
|
|
656
|
+
await shaka.offline.Storage.cleanStoredManifest(manifestId);
|
|
657
|
+
|
|
658
|
+
if (activeHandle && !manifestUpdated && dataKey) {
|
|
659
|
+
// The cleanStoredManifest method will not "see" any segments that have
|
|
660
|
+
// been downloaded but not assigned to the manifest yet. So un-store
|
|
661
|
+
// them separately.
|
|
662
|
+
await activeHandle.cell.removeSegments([dataKey], (key) => {});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
throw e;
|
|
666
|
+
} finally {
|
|
667
|
+
await muxer.destroy();
|
|
668
|
+
shaka.offline.Storage.mutex_.release(mutexId);
|
|
669
|
+
}
|
|
670
|
+
return manifestDB;
|
|
671
|
+
}
|
|
672
|
+
|
|
469
673
|
/**
|
|
470
674
|
* Filter |manifest| such that it will only contain the variants and text
|
|
471
675
|
* streams that we want to store and can actually play.
|
|
@@ -484,25 +688,23 @@ shaka.offline.Storage = class {
|
|
|
484
688
|
const maxHwRes = {width: Infinity, height: Infinity};
|
|
485
689
|
StreamUtils.filterByRestrictions(manifest, config.restrictions, maxHwRes);
|
|
486
690
|
|
|
487
|
-
// Filter the manifest based on what we know
|
|
488
|
-
// play later (no point storing something we can't play).
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
manifest, config.offline.usePersistentLicense);
|
|
492
|
-
} else {
|
|
493
|
-
StreamUtils.filterManifestByMediaSource(manifest);
|
|
494
|
-
// Filter the manifest based on what we know our drm system will support
|
|
495
|
-
// playing later.
|
|
496
|
-
StreamUtils.filterManifestByDrm(manifest, drmEngine);
|
|
497
|
-
}
|
|
691
|
+
// Filter the manifest based on what we know MediaCapabilities will be able
|
|
692
|
+
// to play later (no point storing something we can't play).
|
|
693
|
+
StreamUtils.filterManifestByMediaCapabilities(
|
|
694
|
+
manifest, config.offline.usePersistentLicense);
|
|
498
695
|
|
|
499
696
|
// Gather all tracks.
|
|
500
697
|
const allTracks = [];
|
|
501
698
|
|
|
502
699
|
// Choose the codec that has the lowest average bandwidth.
|
|
503
700
|
const preferredAudioChannelCount = config.preferredAudioChannelCount;
|
|
701
|
+
const preferredDecodingAttributes = config.preferredDecodingAttributes;
|
|
702
|
+
const preferredVideoCodecs = config.preferredVideoCodecs;
|
|
703
|
+
const preferredAudioCodecs = config.preferredAudioCodecs;
|
|
704
|
+
|
|
504
705
|
StreamUtils.chooseCodecsAndFilterManifest(
|
|
505
|
-
manifest,
|
|
706
|
+
manifest, preferredVideoCodecs, preferredAudioCodecs,
|
|
707
|
+
preferredAudioChannelCount, preferredDecodingAttributes);
|
|
506
708
|
|
|
507
709
|
for (const variant of manifest.variants) {
|
|
508
710
|
goog.asserts.assert(
|
|
@@ -586,19 +788,21 @@ shaka.offline.Storage = class {
|
|
|
586
788
|
|
|
587
789
|
/**
|
|
588
790
|
* Create a download manager and download the manifest.
|
|
791
|
+
* This also sets up download infos for each segment to be downloaded.
|
|
589
792
|
*
|
|
590
|
-
* @param {shaka.extern.StorageCell} storage
|
|
591
793
|
* @param {!shaka.media.DrmEngine} drmEngine
|
|
592
794
|
* @param {shaka.extern.Manifest} manifest
|
|
593
795
|
* @param {string} uri
|
|
594
796
|
* @param {!Object} metadata
|
|
595
797
|
* @param {shaka.extern.PlayerConfiguration} config
|
|
596
798
|
* @param {!shaka.offline.DownloadManager} downloader
|
|
597
|
-
* @return {
|
|
799
|
+
* @return {{
|
|
800
|
+
* manifestDB: shaka.extern.ManifestDB,
|
|
801
|
+
* toDownload: !Array.<!shaka.offline.DownloadInfo>
|
|
802
|
+
* }}
|
|
598
803
|
* @private
|
|
599
804
|
*/
|
|
600
|
-
|
|
601
|
-
storage, drmEngine, manifest, uri, metadata, config, downloader) {
|
|
805
|
+
makeManifestDB_(drmEngine, manifest, uri, metadata, config, downloader) {
|
|
602
806
|
const pendingContent = shaka.offline.StoredContentUtils.fromManifest(
|
|
603
807
|
uri, manifest, /* size= */ 0, metadata);
|
|
604
808
|
// In https://github.com/google/shaka-player/issues/2652, we found that this
|
|
@@ -621,12 +825,73 @@ shaka.offline.Storage = class {
|
|
|
621
825
|
};
|
|
622
826
|
downloader.setCallbacks(onProgress, onInitData);
|
|
623
827
|
|
|
624
|
-
const
|
|
828
|
+
const needsInitData = this.getManifestIsEncrypted_(manifest) &&
|
|
829
|
+
!this.getManifestIncludesInitData_(manifest);
|
|
830
|
+
|
|
831
|
+
let currentSystemId = null;
|
|
832
|
+
if (needsInitData) {
|
|
833
|
+
const drmInfo = drmEngine.getDrmInfo();
|
|
834
|
+
currentSystemId =
|
|
835
|
+
shaka.offline.Storage.defaultSystemIds_.get(drmInfo.keySystem);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Make the estimator, which is used to make the download registries.
|
|
839
|
+
const estimator = new shaka.offline.StreamBandwidthEstimator();
|
|
840
|
+
for (const stream of manifest.textStreams) {
|
|
841
|
+
estimator.addText(stream);
|
|
842
|
+
}
|
|
843
|
+
for (const stream of manifest.imageStreams) {
|
|
844
|
+
estimator.addImage(stream);
|
|
845
|
+
}
|
|
846
|
+
for (const variant of manifest.variants) {
|
|
847
|
+
estimator.addVariant(variant);
|
|
848
|
+
}
|
|
849
|
+
const {streams, toDownload} = this.createStreams_(
|
|
850
|
+
downloader, estimator, drmEngine, manifest, config);
|
|
851
|
+
|
|
852
|
+
const drmInfo = drmEngine.getDrmInfo();
|
|
853
|
+
const usePersistentLicense = config.offline.usePersistentLicense;
|
|
854
|
+
if (drmInfo && usePersistentLicense) {
|
|
855
|
+
// Don't store init data, since we have stored sessions.
|
|
856
|
+
drmInfo.initData = [];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const manifestDB = {
|
|
860
|
+
creationTime: Date.now(),
|
|
861
|
+
originalManifestUri: uri,
|
|
862
|
+
duration: manifest.presentationTimeline.getDuration(),
|
|
863
|
+
size: 0,
|
|
864
|
+
expiration: drmEngine.getExpiration(),
|
|
865
|
+
streams,
|
|
866
|
+
sessionIds: usePersistentLicense ? drmEngine.getSessionIds() : [],
|
|
867
|
+
drmInfo,
|
|
868
|
+
appMetadata: metadata,
|
|
869
|
+
isIncomplete: true,
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
return {manifestDB, toDownload};
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* @param {shaka.extern.Manifest} manifest
|
|
877
|
+
* @return {boolean}
|
|
878
|
+
* @private
|
|
879
|
+
*/
|
|
880
|
+
getManifestIsEncrypted_(manifest) {
|
|
881
|
+
return manifest.variants.some((variant) => {
|
|
625
882
|
const videoEncrypted = variant.video && variant.video.encrypted;
|
|
626
883
|
const audioEncrypted = variant.audio && variant.audio.encrypted;
|
|
627
884
|
return videoEncrypted || audioEncrypted;
|
|
628
885
|
});
|
|
629
|
-
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* @param {shaka.extern.Manifest} manifest
|
|
890
|
+
* @return {boolean}
|
|
891
|
+
* @private
|
|
892
|
+
*/
|
|
893
|
+
getManifestIncludesInitData_(manifest) {
|
|
894
|
+
return manifest.variants.some((variant) => {
|
|
630
895
|
const videoDrmInfos = variant.video ? variant.video.drmInfos : [];
|
|
631
896
|
const audioDrmInfos = variant.audio ? variant.audio.drmInfos : [];
|
|
632
897
|
const drmInfos = videoDrmInfos.concat(audioDrmInfos);
|
|
@@ -634,36 +899,28 @@ shaka.offline.Storage = class {
|
|
|
634
899
|
return drmInfos.initData && drmInfos.initData.length;
|
|
635
900
|
});
|
|
636
901
|
});
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
let currentSystemId = null;
|
|
640
|
-
if (needsInitData) {
|
|
641
|
-
const drmInfo = drmEngine.getDrmInfo();
|
|
642
|
-
currentSystemId =
|
|
643
|
-
shaka.offline.Storage.defaultSystemIds_.get(drmInfo.keySystem);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
try {
|
|
647
|
-
const manifestDB = this.createOfflineManifest_(
|
|
648
|
-
downloader, storage, drmEngine, manifest, uri, metadata, config);
|
|
902
|
+
}
|
|
649
903
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
904
|
+
/**
|
|
905
|
+
* @param {shaka.extern.Manifest} manifest
|
|
906
|
+
* @param {shaka.extern.ManifestDB} manifestDB
|
|
907
|
+
* @param {!shaka.media.DrmEngine} drmEngine
|
|
908
|
+
* @param {shaka.extern.PlayerConfiguration} config
|
|
909
|
+
* @private
|
|
910
|
+
*/
|
|
911
|
+
setManifestDrmFields_(manifest, manifestDB, drmEngine, config) {
|
|
912
|
+
manifestDB.expiration = drmEngine.getExpiration();
|
|
655
913
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
660
|
-
shaka.util.Error.Category.STORAGE,
|
|
661
|
-
shaka.util.Error.Code.NO_INIT_DATA_FOR_OFFLINE);
|
|
662
|
-
}
|
|
914
|
+
const sessions = drmEngine.getSessionIds();
|
|
915
|
+
manifestDB.sessionIds = config.offline.usePersistentLicense ?
|
|
916
|
+
sessions : [];
|
|
663
917
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
918
|
+
if (this.getManifestIsEncrypted_(manifest) &&
|
|
919
|
+
config.offline.usePersistentLicense && !sessions.length) {
|
|
920
|
+
throw new shaka.util.Error(
|
|
921
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
922
|
+
shaka.util.Error.Category.STORAGE,
|
|
923
|
+
shaka.util.Error.Code.NO_INIT_DATA_FOR_OFFLINE);
|
|
667
924
|
}
|
|
668
925
|
}
|
|
669
926
|
|
|
@@ -766,7 +1023,7 @@ shaka.offline.Storage = class {
|
|
|
766
1023
|
* @return {!Promise}
|
|
767
1024
|
* @private
|
|
768
1025
|
*/
|
|
769
|
-
removeFromStorage_(
|
|
1026
|
+
removeFromStorage_(storage, uri, manifest) {
|
|
770
1027
|
/** @type {!Array.<number>} */
|
|
771
1028
|
const segmentIds = shaka.offline.Storage.getAllSegmentIds_(manifest);
|
|
772
1029
|
|
|
@@ -923,6 +1180,10 @@ shaka.offline.Storage = class {
|
|
|
923
1180
|
const playerInterface = {
|
|
924
1181
|
networkingEngine: networkingEngine,
|
|
925
1182
|
|
|
1183
|
+
// No need to apply CMCD data for offline requests
|
|
1184
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
1185
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
1186
|
+
|
|
926
1187
|
// Don't bother filtering now. We will do that later when we have all the
|
|
927
1188
|
// information we need to filter.
|
|
928
1189
|
filter: () => Promise.resolve(),
|
|
@@ -1001,8 +1262,7 @@ shaka.offline.Storage = class {
|
|
|
1001
1262
|
|
|
1002
1263
|
drmEngine.configure(config.drm);
|
|
1003
1264
|
await drmEngine.initForStorage(
|
|
1004
|
-
manifest.variants, config.offline.usePersistentLicense
|
|
1005
|
-
config.useMediaCapabilities);
|
|
1265
|
+
manifest.variants, config.offline.usePersistentLicense);
|
|
1006
1266
|
await drmEngine.setServerCertificate();
|
|
1007
1267
|
await drmEngine.createOrLoad();
|
|
1008
1268
|
|
|
@@ -1010,100 +1270,23 @@ shaka.offline.Storage = class {
|
|
|
1010
1270
|
}
|
|
1011
1271
|
|
|
1012
1272
|
/**
|
|
1013
|
-
*
|
|
1014
|
-
* the segments yet, only adds them to the download manager through
|
|
1015
|
-
* createStreams_.
|
|
1016
|
-
*
|
|
1017
|
-
* @param {!shaka.offline.DownloadManager} downloader
|
|
1018
|
-
* @param {shaka.extern.StorageCell} storage
|
|
1019
|
-
* @param {!shaka.media.DrmEngine} drmEngine
|
|
1020
|
-
* @param {shaka.extern.Manifest} manifest
|
|
1021
|
-
* @param {string} originalManifestUri
|
|
1022
|
-
* @param {!Object} metadata
|
|
1023
|
-
* @param {shaka.extern.PlayerConfiguration} config
|
|
1024
|
-
* @return {shaka.extern.ManifestDB}
|
|
1025
|
-
* @private
|
|
1026
|
-
*/
|
|
1027
|
-
createOfflineManifest_(
|
|
1028
|
-
downloader, storage, drmEngine, manifest, originalManifestUri, metadata,
|
|
1029
|
-
config) {
|
|
1030
|
-
const estimator = new shaka.offline.StreamBandwidthEstimator();
|
|
1031
|
-
|
|
1032
|
-
const streams = this.createStreams_(
|
|
1033
|
-
downloader, storage, estimator, drmEngine, manifest, config);
|
|
1034
|
-
|
|
1035
|
-
const usePersistentLicense = config.offline.usePersistentLicense;
|
|
1036
|
-
const drmInfo = drmEngine.getDrmInfo();
|
|
1037
|
-
|
|
1038
|
-
if (drmInfo && usePersistentLicense) {
|
|
1039
|
-
// Don't store init data, since we have stored sessions.
|
|
1040
|
-
drmInfo.initData = [];
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
return {
|
|
1044
|
-
creationTime: Date.now(),
|
|
1045
|
-
originalManifestUri: originalManifestUri,
|
|
1046
|
-
duration: manifest.presentationTimeline.getDuration(),
|
|
1047
|
-
size: 0,
|
|
1048
|
-
expiration: drmEngine.getExpiration(),
|
|
1049
|
-
streams: streams,
|
|
1050
|
-
sessionIds: usePersistentLicense ? drmEngine.getSessionIds() : [],
|
|
1051
|
-
drmInfo: drmInfo,
|
|
1052
|
-
appMetadata: metadata,
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* Converts manifest Streams to database Streams. This will use the current
|
|
1058
|
-
* configuration to get the tracks to use, then it will search each segment
|
|
1059
|
-
* index and add all the segments to the download manager through
|
|
1060
|
-
* createStream_.
|
|
1273
|
+
* Converts manifest Streams to database Streams.
|
|
1061
1274
|
*
|
|
1062
1275
|
* @param {!shaka.offline.DownloadManager} downloader
|
|
1063
|
-
* @param {shaka.extern.StorageCell} storage
|
|
1064
1276
|
* @param {shaka.offline.StreamBandwidthEstimator} estimator
|
|
1065
1277
|
* @param {!shaka.media.DrmEngine} drmEngine
|
|
1066
1278
|
* @param {shaka.extern.Manifest} manifest
|
|
1067
1279
|
* @param {shaka.extern.PlayerConfiguration} config
|
|
1068
|
-
* @return {
|
|
1280
|
+
* @return {{
|
|
1281
|
+
* streams: !Array.<shaka.extern.StreamDB>,
|
|
1282
|
+
* toDownload: !Array.<!shaka.offline.DownloadInfo>
|
|
1283
|
+
* }}
|
|
1069
1284
|
* @private
|
|
1070
1285
|
*/
|
|
1071
|
-
createStreams_(downloader,
|
|
1072
|
-
//
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
estimator.addVariant(variant);
|
|
1076
|
-
}
|
|
1077
|
-
for (const text of manifest.textStreams) {
|
|
1078
|
-
estimator.addText(text);
|
|
1079
|
-
}
|
|
1080
|
-
for (const image of manifest.imageStreams) {
|
|
1081
|
-
estimator.addImage(image);
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
// TODO(joeyparrish): Break out stack-based state and method params into a
|
|
1085
|
-
// separate class to clean up. See:
|
|
1086
|
-
// https://github.com/google/shaka-player/issues/2781#issuecomment-678438039
|
|
1087
|
-
|
|
1088
|
-
/**
|
|
1089
|
-
* A cache mapping init segment references to Promises to their DB key.
|
|
1090
|
-
*
|
|
1091
|
-
* @type {!Map.<shaka.media.InitSegmentReference, !Promise.<?number>>}
|
|
1092
|
-
*/
|
|
1093
|
-
const initSegmentDbKeyCache = new Map();
|
|
1094
|
-
|
|
1095
|
-
// A null init segment reference always maps to a null DB key.
|
|
1096
|
-
initSegmentDbKeyCache.set(
|
|
1097
|
-
null, /** @type {!Promise.<?number>} */(Promise.resolve(null)));
|
|
1098
|
-
|
|
1099
|
-
/**
|
|
1100
|
-
* A cache mapping equivalent segment references to Promises to their DB
|
|
1101
|
-
* key. The key in this map is a string of the form
|
|
1102
|
-
* "<URI>-<startByte>-<endByte>".
|
|
1103
|
-
*
|
|
1104
|
-
* @type {!Map.<string, !Promise.<number>>}
|
|
1105
|
-
*/
|
|
1106
|
-
const segmentDbKeyCache = new Map();
|
|
1286
|
+
createStreams_(downloader, estimator, drmEngine, manifest, config) {
|
|
1287
|
+
// Download infos are stored based on their refId, to dedup them.
|
|
1288
|
+
/** @type {!Map.<string, !shaka.offline.DownloadInfo>} */
|
|
1289
|
+
const toDownload = new Map();
|
|
1107
1290
|
|
|
1108
1291
|
// Find the streams we want to download and create a stream db instance
|
|
1109
1292
|
// for each of them.
|
|
@@ -1113,8 +1296,7 @@ shaka.offline.Storage = class {
|
|
|
1113
1296
|
|
|
1114
1297
|
for (const stream of streamSet) {
|
|
1115
1298
|
const streamDB = this.createStream_(
|
|
1116
|
-
downloader,
|
|
1117
|
-
initSegmentDbKeyCache, segmentDbKeyCache);
|
|
1299
|
+
downloader, estimator, manifest, stream, config, toDownload);
|
|
1118
1300
|
streamDBs.set(stream.id, streamDB);
|
|
1119
1301
|
}
|
|
1120
1302
|
|
|
@@ -1128,27 +1310,26 @@ shaka.offline.Storage = class {
|
|
|
1128
1310
|
}
|
|
1129
1311
|
}
|
|
1130
1312
|
|
|
1131
|
-
return
|
|
1313
|
+
return {
|
|
1314
|
+
streams: Array.from(streamDBs.values()),
|
|
1315
|
+
toDownload: Array.from(toDownload.values()),
|
|
1316
|
+
};
|
|
1132
1317
|
}
|
|
1133
1318
|
|
|
1134
1319
|
/**
|
|
1135
1320
|
* Converts a manifest stream to a database stream. This will search the
|
|
1136
|
-
* segment index and add all the segments to the download
|
|
1321
|
+
* segment index and add all the segments to the download infos.
|
|
1137
1322
|
*
|
|
1138
1323
|
* @param {!shaka.offline.DownloadManager} downloader
|
|
1139
|
-
* @param {shaka.extern.StorageCell} storage
|
|
1140
1324
|
* @param {shaka.offline.StreamBandwidthEstimator} estimator
|
|
1141
1325
|
* @param {shaka.extern.Manifest} manifest
|
|
1142
1326
|
* @param {shaka.extern.Stream} stream
|
|
1143
1327
|
* @param {shaka.extern.PlayerConfiguration} config
|
|
1144
|
-
* @param {!Map.<shaka.
|
|
1145
|
-
* initSegmentDbKeyCache
|
|
1146
|
-
* @param {!Map.<string, !Promise.<number>>} segmentDbKeyCache
|
|
1328
|
+
* @param {!Map.<string, !shaka.offline.DownloadInfo>} toDownload
|
|
1147
1329
|
* @return {shaka.extern.StreamDB}
|
|
1148
1330
|
* @private
|
|
1149
1331
|
*/
|
|
1150
|
-
createStream_(downloader,
|
|
1151
|
-
initSegmentDbKeyCache, segmentDbKeyCache) {
|
|
1332
|
+
createStream_(downloader, estimator, manifest, stream, config, toDownload) {
|
|
1152
1333
|
/** @type {shaka.extern.StreamDB} */
|
|
1153
1334
|
const streamDb = {
|
|
1154
1335
|
id: stream.id,
|
|
@@ -1178,137 +1359,60 @@ shaka.offline.Storage = class {
|
|
|
1178
1359
|
tilesLayout: stream.tilesLayout,
|
|
1179
1360
|
};
|
|
1180
1361
|
|
|
1181
|
-
// Download each stream in parallel.
|
|
1182
|
-
const downloadGroup = stream.id;
|
|
1183
|
-
|
|
1184
1362
|
const startTime =
|
|
1185
1363
|
manifest.presentationTimeline.getSegmentAvailabilityStart();
|
|
1186
1364
|
|
|
1187
1365
|
shaka.offline.Storage.forEachSegment_(stream, startTime, (segment) => {
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
appendWindowStart: segment.appendWindowStart,
|
|
1205
|
-
appendWindowEnd: segment.appendWindowEnd,
|
|
1206
|
-
timestampOffset: segment.timestampOffset,
|
|
1207
|
-
dataKey,
|
|
1208
|
-
});
|
|
1209
|
-
});
|
|
1210
|
-
});
|
|
1211
|
-
|
|
1212
|
-
return streamDb;
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
/**
|
|
1216
|
-
* Get a Promise to the DB key for a given init segment reference.
|
|
1217
|
-
*
|
|
1218
|
-
* The return values will be cached so that multiple calls with the same init
|
|
1219
|
-
* segment reference will only trigger one request.
|
|
1220
|
-
*
|
|
1221
|
-
* @param {!shaka.offline.DownloadManager} downloader
|
|
1222
|
-
* @param {number} downloadGroup
|
|
1223
|
-
* @param {number} streamId
|
|
1224
|
-
* @param {shaka.extern.StorageCell} storage
|
|
1225
|
-
* @param {shaka.offline.StreamBandwidthEstimator} estimator
|
|
1226
|
-
* @param {shaka.media.InitSegmentReference} initSegmentReference
|
|
1227
|
-
* @param {shaka.extern.PlayerConfiguration} config
|
|
1228
|
-
* @param {!Map.<shaka.media.InitSegmentReference, !Promise.<?number>>}
|
|
1229
|
-
* initSegmentDbKeyCache
|
|
1230
|
-
* @return {!Promise.<?number>}
|
|
1231
|
-
* @private
|
|
1232
|
-
*/
|
|
1233
|
-
getInitSegmentDbKey_(
|
|
1234
|
-
downloader, downloadGroup, streamId, storage, estimator,
|
|
1235
|
-
initSegmentReference, config, initSegmentDbKeyCache) {
|
|
1236
|
-
if (initSegmentDbKeyCache.has(initSegmentReference)) {
|
|
1237
|
-
return initSegmentDbKeyCache.get(initSegmentReference);
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
const request = shaka.util.Networking.createSegmentRequest(
|
|
1241
|
-
initSegmentReference.getUris(),
|
|
1242
|
-
initSegmentReference.startByte,
|
|
1243
|
-
initSegmentReference.endByte,
|
|
1244
|
-
config.streaming.retryParameters);
|
|
1245
|
-
|
|
1246
|
-
const promise = downloader.queue(
|
|
1247
|
-
downloadGroup,
|
|
1248
|
-
request,
|
|
1249
|
-
estimator.getInitSegmentEstimate(streamId),
|
|
1250
|
-
/* isInitSegment= */ true,
|
|
1251
|
-
async (data) => {
|
|
1252
|
-
/** @type {!Array.<number>} */
|
|
1253
|
-
const ids = await storage.addSegments([{data: data}]);
|
|
1254
|
-
this.segmentsFromStore_.push(ids[0]);
|
|
1255
|
-
return ids[0];
|
|
1256
|
-
});
|
|
1257
|
-
|
|
1258
|
-
initSegmentDbKeyCache.set(initSegmentReference, promise);
|
|
1259
|
-
return promise;
|
|
1260
|
-
}
|
|
1366
|
+
const pendingSegmentRefId =
|
|
1367
|
+
shaka.offline.DownloadInfo.idForSegmentRef(segment);
|
|
1368
|
+
let pendingInitSegmentRefId = undefined;
|
|
1369
|
+
|
|
1370
|
+
// Set up the download for the segment, which will be downloaded later,
|
|
1371
|
+
// perhaps in a service worker.
|
|
1372
|
+
if (!toDownload.has(pendingSegmentRefId)) {
|
|
1373
|
+
const estimateId = downloader.addDownloadEstimate(
|
|
1374
|
+
estimator.getSegmentEstimate(stream.id, segment));
|
|
1375
|
+
const segmentDownload = new shaka.offline.DownloadInfo(
|
|
1376
|
+
segment,
|
|
1377
|
+
estimateId,
|
|
1378
|
+
stream.id,
|
|
1379
|
+
/* isInitSegment= */ false);
|
|
1380
|
+
toDownload.set(pendingSegmentRefId, segmentDownload);
|
|
1381
|
+
}
|
|
1261
1382
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
* @private
|
|
1278
|
-
*/
|
|
1279
|
-
getSegmentDbKey_(
|
|
1280
|
-
downloader, downloadGroup, streamId, storage, estimator,
|
|
1281
|
-
segmentReference, config, segmentDbKeyCache) {
|
|
1282
|
-
const mapKey = [
|
|
1283
|
-
segmentReference.getUris()[0],
|
|
1284
|
-
segmentReference.startByte,
|
|
1285
|
-
segmentReference.endByte,
|
|
1286
|
-
].join('-');
|
|
1287
|
-
|
|
1288
|
-
if (segmentDbKeyCache.has(mapKey)) {
|
|
1289
|
-
return segmentDbKeyCache.get(mapKey);
|
|
1290
|
-
}
|
|
1383
|
+
// Set up the download for the init segment, similarly, if there is one.
|
|
1384
|
+
if (segment.initSegmentReference) {
|
|
1385
|
+
pendingInitSegmentRefId = shaka.offline.DownloadInfo.idForSegmentRef(
|
|
1386
|
+
segment.initSegmentReference);
|
|
1387
|
+
if (!toDownload.has(pendingInitSegmentRefId)) {
|
|
1388
|
+
const estimateId = downloader.addDownloadEstimate(
|
|
1389
|
+
estimator.getInitSegmentEstimate(stream.id));
|
|
1390
|
+
const initDownload = new shaka.offline.DownloadInfo(
|
|
1391
|
+
segment.initSegmentReference,
|
|
1392
|
+
estimateId,
|
|
1393
|
+
stream.id,
|
|
1394
|
+
/* isInitSegment= */ true);
|
|
1395
|
+
toDownload.set(pendingInitSegmentRefId, initDownload);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1291
1398
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
return ids[0];
|
|
1308
|
-
});
|
|
1399
|
+
/** @type {!shaka.extern.SegmentDB} */
|
|
1400
|
+
const segmentDB = {
|
|
1401
|
+
pendingInitSegmentRefId,
|
|
1402
|
+
initSegmentKey: pendingInitSegmentRefId ? 0 : null,
|
|
1403
|
+
startTime: segment.startTime,
|
|
1404
|
+
endTime: segment.endTime,
|
|
1405
|
+
appendWindowStart: segment.appendWindowStart,
|
|
1406
|
+
appendWindowEnd: segment.appendWindowEnd,
|
|
1407
|
+
timestampOffset: segment.timestampOffset,
|
|
1408
|
+
tilesLayout: segment.tilesLayout,
|
|
1409
|
+
pendingSegmentRefId,
|
|
1410
|
+
dataKey: 0,
|
|
1411
|
+
};
|
|
1412
|
+
streamDb.segments.push(segmentDB);
|
|
1413
|
+
});
|
|
1309
1414
|
|
|
1310
|
-
|
|
1311
|
-
return promise;
|
|
1415
|
+
return streamDb;
|
|
1312
1416
|
}
|
|
1313
1417
|
|
|
1314
1418
|
/**
|
|
@@ -1410,21 +1514,21 @@ shaka.offline.Storage = class {
|
|
|
1410
1514
|
* @private
|
|
1411
1515
|
*/
|
|
1412
1516
|
static getAllSegmentIds_(manifest) {
|
|
1413
|
-
/** @type {!
|
|
1414
|
-
const ids =
|
|
1517
|
+
/** @type {!Set.<number>} */
|
|
1518
|
+
const ids = new Set();
|
|
1415
1519
|
|
|
1416
1520
|
// Get every segment for every stream in the manifest.
|
|
1417
1521
|
for (const stream of manifest.streams) {
|
|
1418
1522
|
for (const segment of stream.segments) {
|
|
1419
1523
|
if (segment.initSegmentKey != null) {
|
|
1420
|
-
ids.
|
|
1524
|
+
ids.add(segment.initSegmentKey);
|
|
1421
1525
|
}
|
|
1422
1526
|
|
|
1423
|
-
ids.
|
|
1527
|
+
ids.add(segment.dataKey);
|
|
1424
1528
|
}
|
|
1425
1529
|
}
|
|
1426
1530
|
|
|
1427
|
-
return ids;
|
|
1531
|
+
return Array.from(ids);
|
|
1428
1532
|
}
|
|
1429
1533
|
|
|
1430
1534
|
/**
|
|
@@ -1559,6 +1663,9 @@ shaka.offline.Storage = class {
|
|
|
1559
1663
|
}
|
|
1560
1664
|
};
|
|
1561
1665
|
|
|
1666
|
+
/** @private {!shaka.util.Mutex} */
|
|
1667
|
+
shaka.offline.Storage.mutex_ = new shaka.util.Mutex();
|
|
1668
|
+
|
|
1562
1669
|
shaka.offline.Storage.defaultSystemIds_ = new Map()
|
|
1563
1670
|
.set('org.w3.clearkey', '1077efecc0b24d02ace33c1e52e2fb4b')
|
|
1564
1671
|
.set('com.widevine.alpha', 'edef8ba979d64acea3c827dcd51d21ed')
|