shaka-player 4.5.0 → 4.6.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/AUTHORS +2 -0
- package/CHANGELOG.md +98 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +2 -0
- package/README.md +38 -32
- package/demo/common/asset.js +11 -17
- package/demo/common/assets.js +117 -11
- package/demo/config.js +28 -1
- package/demo/custom.js +52 -38
- package/demo/main.js +11 -3
- package/demo/search.js +7 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +133 -124
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +133 -124
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +20 -18
- package/dist/locales.js +35 -31
- package/dist/receiver.compiled.debug.js +58 -50
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +58 -50
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.externs.js +162 -77
- package/dist/shaka-player.compiled.debug.js +786 -736
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +162 -77
- package/dist/shaka-player.compiled.js +1154 -1121
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +491 -437
- package/dist/shaka-player.ui.debug.d.ts +491 -437
- package/dist/shaka-player.ui.debug.externs.js +167 -77
- package/dist/shaka-player.ui.debug.js +877 -811
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +167 -77
- package/dist/shaka-player.ui.js +1362 -1318
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/basic-usage.md +2 -1
- package/docs/tutorials/errors.md +2 -1
- package/docs/tutorials/offline.md +10 -8
- package/docs/tutorials/plugins.md +5 -0
- package/docs/tutorials/ui-customization.md +13 -0
- package/docs/tutorials/ui.md +2 -1
- package/docs/tutorials/upgrade.md +3 -0
- package/externs/managedmediasource.js +24 -0
- package/externs/shaka/abr_manager.js +6 -2
- package/externs/shaka/cea.js +7 -0
- package/externs/shaka/codecs.js +5 -3
- package/externs/shaka/manifest.js +21 -5
- package/externs/shaka/manifest_parser.js +17 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/offline.js +9 -2
- package/externs/shaka/player.js +53 -7
- package/externs/shaka/text.js +3 -1
- package/externs/shaka/transmuxer.js +1 -1
- package/lib/abr/simple_abr_manager.js +25 -8
- package/lib/ads/media_tailor_ad_manager.js +14 -3
- package/lib/cast/cast_receiver.js +1 -1
- package/lib/cea/cea_decoder.js +28 -0
- package/lib/cea/dummy_caption_decoder.js +5 -0
- package/lib/dash/dash_parser.js +249 -83
- package/lib/dash/mpd_utils.js +52 -31
- package/lib/dash/segment_base.js +4 -5
- package/lib/dash/segment_list.js +5 -4
- package/lib/dash/segment_template.js +83 -26
- package/lib/hls/hls_classes.js +28 -43
- package/lib/hls/hls_parser.js +761 -649
- package/lib/hls/hls_utils.js +64 -5
- package/lib/hls/manifest_text_parser.js +7 -11
- package/lib/media/adaptation_set.js +20 -13
- package/lib/media/adaptation_set_criteria.js +70 -18
- package/lib/media/closed_caption_parser.js +13 -0
- package/lib/media/content_workarounds.js +6 -0
- package/lib/media/drm_engine.js +8 -13
- package/lib/media/manifest_parser.js +8 -113
- package/lib/media/media_source_capabilities.js +10 -3
- package/lib/media/media_source_engine.js +75 -21
- package/lib/media/play_rate_controller.js +8 -38
- package/lib/media/presentation_timeline.js +7 -1
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_index.js +5 -5
- package/lib/media/segment_prefetch.js +92 -24
- package/lib/media/segment_reference.js +67 -1
- package/lib/media/segment_utils.js +343 -0
- package/lib/media/stall_detector.js +0 -3
- package/lib/media/streaming_engine.js +41 -21
- package/lib/mss/mss_parser.js +18 -2
- package/lib/net/networking_engine.js +66 -12
- package/lib/net/networking_utils.js +95 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
- package/lib/offline/manifest_converter.js +2 -0
- package/lib/offline/offline_manifest_parser.js +10 -0
- package/lib/offline/storage.js +11 -7
- package/lib/player.js +993 -1502
- package/lib/polyfill/mediasource.js +22 -1
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/stub_text_displayer.js +51 -0
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +28 -5
- package/lib/text/ui_text_displayer.js +4 -0
- package/lib/text/vtt_text_parser.js +7 -2
- package/lib/transmuxer/aac_transmuxer.js +1 -1
- package/lib/transmuxer/ac3_transmuxer.js +1 -1
- package/lib/transmuxer/ec3_transmuxer.js +1 -1
- package/lib/transmuxer/h264.js +5 -4
- package/lib/transmuxer/h265.js +5 -4
- package/lib/transmuxer/mp3_transmuxer.js +1 -1
- package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
- package/lib/transmuxer/mss_transmuxer.js +1 -1
- package/lib/transmuxer/ts_transmuxer.js +33 -13
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/content_steering_manager.js +358 -0
- package/lib/util/error.js +2 -6
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/fake_event.js +5 -1
- package/lib/util/id3_utils.js +50 -1
- package/lib/util/manifest_parser_utils.js +12 -8
- package/lib/util/mp4_generator.js +2 -4
- package/lib/util/mutex.js +59 -0
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +154 -179
- package/lib/util/platform.js +4 -3
- package/lib/util/player_configuration.js +35 -6
- package/lib/util/stream_utils.js +105 -98
- package/lib/util/ts_parser.js +124 -73
- package/lib/util/uint8array_utils.js +5 -1
- package/lib/util/xml_utils.js +5 -0
- package/maintained-branches.md +2 -2
- package/package.json +1 -1
- package/roadmap.md +15 -5
- package/shaka-player.uncompiled.js +0 -1
- package/test/abr/simple_abr_manager_unit.js +33 -33
- package/test/ads/ad_manager_unit.js +3 -2
- package/test/cast/cast_receiver_integration.js +3 -4
- package/test/cast/cast_utils_unit.js +2 -0
- package/test/cea/cea_decoder_unit.js +7 -0
- package/test/codec_switching/codec_switching_integration.js +2 -1
- package/test/dash/dash_parser_integration.js +2 -1
- package/test/dash/dash_parser_live_unit.js +0 -19
- package/test/dash/dash_parser_manifest_unit.js +494 -2
- package/test/dash/mpd_utils_unit.js +61 -30
- package/test/hls/hls_live_unit.js +12 -3
- package/test/hls/hls_parser_unit.js +377 -27
- package/test/hls/manifest_text_parser_unit.js +32 -99
- package/test/media/adaptation_set_criteria_unit.js +172 -21
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/play_rate_controller_unit.js +0 -48
- package/test/media/segment_index_unit.js +2 -1
- package/test/mss/mss_player_integration.js +3 -2
- package/test/net/networking_engine_unit.js +3 -3
- package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
- package/test/offline/manifest_convert_unit.js +9 -0
- package/test/offline/offline_integration.js +2 -1
- package/test/offline/storage_integration.js +21 -9
- package/test/player_external.js +4 -2
- package/test/player_integration.js +15 -4
- package/test/player_load_graph_integration.js +47 -151
- package/test/player_src_equals_external.js +1 -0
- package/test/player_src_equals_integration.js +71 -4
- package/test/player_unit.js +80 -27
- package/test/test/util/fake_media_source_engine.js +5 -0
- package/test/test/util/fake_networking_engine.js +9 -1
- package/test/test/util/fake_segment_prefetch.js +3 -0
- package/test/test/util/manifest_generator.js +4 -0
- package/test/test/util/manifest_parser_util.js +14 -2
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/streaming_engine_util.js +3 -0
- package/test/test/util/test_scheme.js +6 -0
- package/test/test/util/ui_utils.js +4 -3
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_displayer_layout_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +33 -2
- package/test/transmuxer/transmuxer_engine_integration.js +9 -0
- package/test/transmuxer/transmuxer_integration.js +33 -3
- package/test/ui/ad_ui_unit.js +2 -2
- package/test/ui/ui_customization_unit.js +17 -16
- package/test/ui/ui_integration.js +30 -20
- package/test/ui/ui_unit.js +25 -19
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/content_steering_manager_unit.js +427 -0
- package/test/util/id3_utils_unit.js +52 -0
- package/test/util/stream_utils_unit.js +17 -113
- package/test/util/ts_parser_unit.js +3 -3
- package/third_party/closure-uri/uri.js +13 -1
- package/ui/audio_language_selection.js +9 -2
- package/ui/externs/ui.js +17 -2
- package/ui/language_utils.js +43 -8
- package/ui/less/range_elements.less +33 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/es-419.json +1 -0
- package/ui/locales/es.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +10 -0
- package/ui/remote_button.js +3 -1
- package/ui/resolution_selection.js +47 -10
- package/ui/seek_bar.js +296 -3
- package/ui/text_selection.js +2 -1
- package/ui/ui.js +6 -2
- package/lib/routing/node.js +0 -23
- package/lib/routing/payload.js +0 -47
- package/lib/routing/walker.js +0 -587
- package/test/routing/walker_unit.js +0 -505
|
@@ -1054,6 +1054,14 @@ shaka.media.StreamingEngine = class {
|
|
|
1054
1054
|
this.playerInterface_.mediaSourceEngine.clearSelectedClosedCaptionId();
|
|
1055
1055
|
}
|
|
1056
1056
|
|
|
1057
|
+
if (!this.playerInterface_.mediaSourceEngine.isStreamingAllowed() &&
|
|
1058
|
+
mediaState.type != ContentType.TEXT) {
|
|
1059
|
+
// It is not allowed to add segments yet, so we schedule an update to
|
|
1060
|
+
// check again later. So any prediction we make now could be terribly
|
|
1061
|
+
// invalid soon.
|
|
1062
|
+
return this.config_.updateIntervalSeconds / 2;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1057
1065
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
1058
1066
|
|
|
1059
1067
|
// Compute how far we've buffered ahead of the playhead.
|
|
@@ -1163,6 +1171,12 @@ shaka.media.StreamingEngine = class {
|
|
|
1163
1171
|
}
|
|
1164
1172
|
|
|
1165
1173
|
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1174
|
+
const initSegmentReference = reference.initSegmentReference;
|
|
1175
|
+
if (initSegmentReference && (!mediaState.lastSegmentReference ||
|
|
1176
|
+
!shaka.media.InitSegmentReference.equal(
|
|
1177
|
+
initSegmentReference, mediaState.lastInitSegmentReference))) {
|
|
1178
|
+
mediaState.segmentPrefetch.prefetchInitSegment(initSegmentReference);
|
|
1179
|
+
}
|
|
1166
1180
|
mediaState.segmentPrefetch.prefetchSegments(reference);
|
|
1167
1181
|
}
|
|
1168
1182
|
|
|
@@ -1373,6 +1387,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1373
1387
|
await this.append_(
|
|
1374
1388
|
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1375
1389
|
/* isChunkedData= */ true);
|
|
1390
|
+
|
|
1391
|
+
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1392
|
+
mediaState.segmentPrefetch.prefetchSegments(
|
|
1393
|
+
reference, /* skipFirst= */ true);
|
|
1394
|
+
}
|
|
1376
1395
|
}
|
|
1377
1396
|
};
|
|
1378
1397
|
|
|
@@ -1403,6 +1422,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1403
1422
|
await this.append_(
|
|
1404
1423
|
mediaState, presentationTime, stream, reference, result);
|
|
1405
1424
|
}
|
|
1425
|
+
|
|
1426
|
+
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1427
|
+
mediaState.segmentPrefetch.prefetchSegments(
|
|
1428
|
+
reference, /* skipFirst= */ true);
|
|
1429
|
+
}
|
|
1406
1430
|
} else {
|
|
1407
1431
|
if (this.config_.lowLatencyMode && !isReadableStreamSupported) {
|
|
1408
1432
|
shaka.log.warning('Low latency streaming mode is enabled, but ' +
|
|
@@ -1499,11 +1523,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1499
1523
|
/**
|
|
1500
1524
|
* @param {!BufferSource} rawResult
|
|
1501
1525
|
* @param {shaka.extern.aes128Key} aes128Key
|
|
1502
|
-
* @param {
|
|
1526
|
+
* @param {number} position
|
|
1503
1527
|
* @return {!Promise.<!BufferSource>} finalResult
|
|
1504
1528
|
* @private
|
|
1505
1529
|
*/
|
|
1506
|
-
async aes128Decrypt_(rawResult, aes128Key,
|
|
1530
|
+
async aes128Decrypt_(rawResult, aes128Key, position) {
|
|
1507
1531
|
const key = aes128Key;
|
|
1508
1532
|
if (!key.cryptoKey) {
|
|
1509
1533
|
goog.asserts.assert(key.fetchKey, 'If AES-128 cryptoKey was not ' +
|
|
@@ -1514,7 +1538,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1514
1538
|
let iv = key.iv;
|
|
1515
1539
|
if (!iv) {
|
|
1516
1540
|
iv = shaka.util.BufferUtils.toUint8(new ArrayBuffer(16));
|
|
1517
|
-
let sequence = key.firstMediaSequenceNumber +
|
|
1541
|
+
let sequence = key.firstMediaSequenceNumber + position;
|
|
1518
1542
|
for (let i = iv.byteLength - 1; i >= 0; i--) {
|
|
1519
1543
|
iv[i] = sequence & 0xff;
|
|
1520
1544
|
sequence >>= 8;
|
|
@@ -1764,8 +1788,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1764
1788
|
shaka.log.v1(logPrefix, 'fetching init segment');
|
|
1765
1789
|
|
|
1766
1790
|
const fetchInit =
|
|
1767
|
-
this.fetch_(mediaState, reference.initSegmentReference
|
|
1768
|
-
/* streamDataCallback= */ undefined, /* isInit= */ true);
|
|
1791
|
+
this.fetch_(mediaState, reference.initSegmentReference);
|
|
1769
1792
|
const append = async () => {
|
|
1770
1793
|
try {
|
|
1771
1794
|
const initSegment = await fetchInit;
|
|
@@ -2169,13 +2192,12 @@ shaka.media.StreamingEngine = class {
|
|
|
2169
2192
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2170
2193
|
* reference
|
|
2171
2194
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2172
|
-
* @param {boolean=} isInit
|
|
2173
2195
|
*
|
|
2174
2196
|
* @return {!Promise.<BufferSource>}
|
|
2175
2197
|
* @private
|
|
2176
2198
|
* @suppress {strictMissingProperties}
|
|
2177
2199
|
*/
|
|
2178
|
-
async fetch_(mediaState, reference, streamDataCallback
|
|
2200
|
+
async fetch_(mediaState, reference, streamDataCallback) {
|
|
2179
2201
|
if (reference instanceof shaka.media.InitSegmentReference) {
|
|
2180
2202
|
const segmentData = reference.getSegmentData();
|
|
2181
2203
|
if (segmentData) {
|
|
@@ -2183,17 +2205,18 @@ shaka.media.StreamingEngine = class {
|
|
|
2183
2205
|
}
|
|
2184
2206
|
}
|
|
2185
2207
|
let op = null;
|
|
2186
|
-
if (
|
|
2187
|
-
mediaState.segmentPrefetch &&
|
|
2188
|
-
reference instanceof shaka.media.SegmentReference
|
|
2189
|
-
) {
|
|
2208
|
+
if (mediaState.segmentPrefetch) {
|
|
2190
2209
|
op = mediaState.segmentPrefetch.getPrefetchedSegment(
|
|
2191
2210
|
reference, streamDataCallback);
|
|
2192
2211
|
}
|
|
2193
2212
|
if (!op) {
|
|
2194
2213
|
op = this.dispatchFetch_(
|
|
2195
|
-
reference, mediaState.stream, streamDataCallback
|
|
2196
|
-
|
|
2214
|
+
reference, mediaState.stream, streamDataCallback);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
let position = 0;
|
|
2218
|
+
if (mediaState.segmentIterator) {
|
|
2219
|
+
position = mediaState.segmentIterator.currentPosition();
|
|
2197
2220
|
}
|
|
2198
2221
|
|
|
2199
2222
|
mediaState.operation = op;
|
|
@@ -2201,9 +2224,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2201
2224
|
mediaState.operation = null;
|
|
2202
2225
|
let result = response.data;
|
|
2203
2226
|
if (reference.aes128Key) {
|
|
2204
|
-
|
|
2205
|
-
goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
|
|
2206
|
-
result = await this.aes128Decrypt_(result, reference.aes128Key, iter);
|
|
2227
|
+
result = await this.aes128Decrypt_(result, reference.aes128Key, position);
|
|
2207
2228
|
}
|
|
2208
2229
|
return result;
|
|
2209
2230
|
}
|
|
@@ -2215,18 +2236,17 @@ shaka.media.StreamingEngine = class {
|
|
|
2215
2236
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2216
2237
|
* reference
|
|
2217
2238
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2218
|
-
* @param {boolean=} isInit
|
|
2219
2239
|
*
|
|
2220
2240
|
* @return {!shaka.net.NetworkingEngine.PendingRequest}
|
|
2221
2241
|
* @private
|
|
2222
2242
|
*/
|
|
2223
|
-
dispatchFetch_(reference, stream, streamDataCallback
|
|
2243
|
+
dispatchFetch_(reference, stream, streamDataCallback) {
|
|
2224
2244
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
2225
|
-
const type = isInit ?
|
|
2226
|
-
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT :
|
|
2227
|
-
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
|
|
2228
2245
|
const segment = reference instanceof shaka.media.SegmentReference ?
|
|
2229
2246
|
reference : undefined;
|
|
2247
|
+
const type = segment ?
|
|
2248
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT :
|
|
2249
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
|
|
2230
2250
|
const request = shaka.util.Networking.createSegmentRequest(
|
|
2231
2251
|
reference.getUris(),
|
|
2232
2252
|
reference.startByte,
|
package/lib/mss/mss_parser.js
CHANGED
|
@@ -213,6 +213,22 @@ shaka.mss.MssParser = class {
|
|
|
213
213
|
// No-op
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
/**
|
|
217
|
+
* @override
|
|
218
|
+
* @exportInterface
|
|
219
|
+
*/
|
|
220
|
+
onInitialVariantChosen(variant) {
|
|
221
|
+
// No-op
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @override
|
|
226
|
+
* @exportInterface
|
|
227
|
+
*/
|
|
228
|
+
banLocation(uri) {
|
|
229
|
+
// No-op
|
|
230
|
+
}
|
|
231
|
+
|
|
216
232
|
/**
|
|
217
233
|
* Makes a network request for the manifest and parses the resulting data.
|
|
218
234
|
*
|
|
@@ -529,6 +545,7 @@ shaka.mss.MssParser = class {
|
|
|
529
545
|
spatialAudio: false,
|
|
530
546
|
closedCaptions: null,
|
|
531
547
|
hdr: undefined,
|
|
548
|
+
videoLayout: undefined,
|
|
532
549
|
tilesLayout: undefined,
|
|
533
550
|
matchedStreams: [],
|
|
534
551
|
mssPrivateData: {
|
|
@@ -538,6 +555,7 @@ shaka.mss.MssParser = class {
|
|
|
538
555
|
},
|
|
539
556
|
accessibilityPurpose: null,
|
|
540
557
|
external: false,
|
|
558
|
+
fastSwitching: false,
|
|
541
559
|
};
|
|
542
560
|
|
|
543
561
|
// This is specifically for text tracks.
|
|
@@ -1077,7 +1095,5 @@ shaka.mss.MssParser.Context;
|
|
|
1077
1095
|
*/
|
|
1078
1096
|
shaka.mss.MssParser.TimeRange;
|
|
1079
1097
|
|
|
1080
|
-
shaka.media.ManifestParser.registerParserByExtension(
|
|
1081
|
-
'ism', () => new shaka.mss.MssParser());
|
|
1082
1098
|
shaka.media.ManifestParser.registerParserByMime(
|
|
1083
1099
|
'application/vnd.ms-sstr+xml', () => new shaka.mss.MssParser());
|
|
@@ -46,18 +46,21 @@ goog.require('shaka.util.Timer');
|
|
|
46
46
|
*/
|
|
47
47
|
shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
48
48
|
/**
|
|
49
|
-
* @param {function(number, number)=} onProgressUpdated Called when
|
|
50
|
-
* event is triggered. Passed the duration, in milliseconds,
|
|
51
|
-
* request took,
|
|
49
|
+
* @param {function(number, number, boolean)=} onProgressUpdated Called when
|
|
50
|
+
* a progress event is triggered. Passed the duration, in milliseconds,
|
|
51
|
+
* that the request took, the number of bytes transferred, and the boolean
|
|
52
|
+
* of whether the switching is allowed.
|
|
52
53
|
* @param {shaka.net.NetworkingEngine.OnHeadersReceived=} onHeadersReceived
|
|
53
54
|
* Called when the headers are received for a download.
|
|
54
55
|
* @param {shaka.net.NetworkingEngine.OnDownloadFailed=} onDownloadFailed
|
|
55
56
|
* Called when a download fails, for any reason.
|
|
56
57
|
* @param {shaka.net.NetworkingEngine.OnRequest=} onRequest
|
|
57
58
|
* Called when a request is made
|
|
59
|
+
* @param {shaka.net.NetworkingEngine.OnRetry=} onRetry
|
|
60
|
+
* Called when a request retry is made
|
|
58
61
|
*/
|
|
59
62
|
constructor(onProgressUpdated, onHeadersReceived, onDownloadFailed,
|
|
60
|
-
onRequest) {
|
|
63
|
+
onRequest, onRetry) {
|
|
61
64
|
super();
|
|
62
65
|
|
|
63
66
|
/** @private {boolean} */
|
|
@@ -72,7 +75,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
72
75
|
/** @private {!Set.<shaka.extern.ResponseFilter>} */
|
|
73
76
|
this.responseFilters_ = new Set();
|
|
74
77
|
|
|
75
|
-
/** @private {?function(number, number)} */
|
|
78
|
+
/** @private {?function(number, number, boolean)} */
|
|
76
79
|
this.onProgressUpdated_ = onProgressUpdated || null;
|
|
77
80
|
|
|
78
81
|
/** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */
|
|
@@ -84,6 +87,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
84
87
|
/** @private {?shaka.net.NetworkingEngine.OnRequest} */
|
|
85
88
|
this.onRequest_ = onRequest || null;
|
|
86
89
|
|
|
90
|
+
/** @private {?shaka.net.NetworkingEngine.OnRetry} */
|
|
91
|
+
this.onRetry_ = onRetry || null;
|
|
92
|
+
|
|
87
93
|
/** @private {boolean} */
|
|
88
94
|
this.forceHTTPS_ = false;
|
|
89
95
|
}
|
|
@@ -295,7 +301,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
295
301
|
// Apply the registered filters to the request.
|
|
296
302
|
const requestFilterOperation = this.filterRequest_(type, request, context);
|
|
297
303
|
const requestOperation = requestFilterOperation.chain(
|
|
298
|
-
() => this.makeRequestWithRetry_(type, request,
|
|
304
|
+
() => this.makeRequestWithRetry_(type, request, context,
|
|
305
|
+
numBytesRemainingObj));
|
|
299
306
|
const responseFilterOperation = requestOperation.chain(
|
|
300
307
|
(responseAndGotProgress) =>
|
|
301
308
|
this.filterResponse_(type, responseAndGotProgress, context));
|
|
@@ -322,7 +329,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
322
329
|
!response.fromCache &&
|
|
323
330
|
request.method != 'HEAD' &&
|
|
324
331
|
type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
|
|
325
|
-
this.
|
|
332
|
+
const allowSwitch = this.allowSwitch_(context);
|
|
333
|
+
this.onProgressUpdated_(
|
|
334
|
+
response.timeMs, response.data.byteLength, allowSwitch);
|
|
326
335
|
}
|
|
327
336
|
return response;
|
|
328
337
|
}, (e) => {
|
|
@@ -399,18 +408,19 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
399
408
|
/**
|
|
400
409
|
* @param {shaka.net.NetworkingEngine.RequestType} type
|
|
401
410
|
* @param {shaka.extern.Request} request
|
|
411
|
+
* @param {(shaka.extern.RequestContext|undefined)} context
|
|
402
412
|
* @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
|
|
403
413
|
* numBytesRemainingObj
|
|
404
414
|
* @return {!shaka.extern.IAbortableOperation.<
|
|
405
415
|
* shaka.net.NetworkingEngine.ResponseAndGotProgress>}
|
|
406
416
|
* @private
|
|
407
417
|
*/
|
|
408
|
-
makeRequestWithRetry_(type, request, numBytesRemainingObj) {
|
|
418
|
+
makeRequestWithRetry_(type, request, context, numBytesRemainingObj) {
|
|
409
419
|
const backoff = new shaka.net.Backoff(
|
|
410
420
|
request.retryParameters, /* autoReset= */ false);
|
|
411
421
|
const index = 0;
|
|
412
422
|
return this.send_(
|
|
413
|
-
type, request, backoff, index, /* lastError= */ null,
|
|
423
|
+
type, request, context, backoff, index, /* lastError= */ null,
|
|
414
424
|
numBytesRemainingObj);
|
|
415
425
|
}
|
|
416
426
|
|
|
@@ -419,6 +429,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
419
429
|
*
|
|
420
430
|
* @param {shaka.net.NetworkingEngine.RequestType} type
|
|
421
431
|
* @param {shaka.extern.Request} request
|
|
432
|
+
* @param {(shaka.extern.RequestContext|undefined)} context
|
|
422
433
|
* @param {!shaka.net.Backoff} backoff
|
|
423
434
|
* @param {number} index
|
|
424
435
|
* @param {?shaka.util.Error} lastError
|
|
@@ -428,11 +439,18 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
428
439
|
* shaka.net.NetworkingEngine.ResponseAndGotProgress>}
|
|
429
440
|
* @private
|
|
430
441
|
*/
|
|
431
|
-
send_(type, request, backoff, index, lastError,
|
|
442
|
+
send_(type, request, context, backoff, index, lastError,
|
|
443
|
+
numBytesRemainingObj) {
|
|
432
444
|
if (this.forceHTTPS_) {
|
|
433
445
|
request.uris[index] = request.uris[index].replace('http://', 'https://');
|
|
434
446
|
}
|
|
435
447
|
|
|
448
|
+
if (index > 0 && this.onRetry_) {
|
|
449
|
+
const newUri = request.uris[index];
|
|
450
|
+
const oldUri = request.uris[index - 1];
|
|
451
|
+
this.onRetry_(type, context, newUri, oldUri);
|
|
452
|
+
}
|
|
453
|
+
|
|
436
454
|
const uri = new goog.Uri(request.uris[index]);
|
|
437
455
|
let scheme = uri.getScheme();
|
|
438
456
|
// Whether it got a progress event.
|
|
@@ -501,7 +519,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
501
519
|
stallTimer.tickAfter(stallTimeoutMs / 1000);
|
|
502
520
|
}
|
|
503
521
|
if (this.onProgressUpdated_ && type == segment) {
|
|
504
|
-
this.
|
|
522
|
+
const allowSwitch = this.allowSwitch_(context);
|
|
523
|
+
this.onProgressUpdated_(time, bytes, allowSwitch);
|
|
505
524
|
gotProgress = true;
|
|
506
525
|
numBytesRemainingObj.setBytes(numBytesRemaining);
|
|
507
526
|
}
|
|
@@ -610,7 +629,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
610
629
|
// Move to the next URI.
|
|
611
630
|
index = (index + 1) % request.uris.length;
|
|
612
631
|
return this.send_(
|
|
613
|
-
type, request, backoff, index, error,
|
|
632
|
+
type, request, context, backoff, index, error,
|
|
633
|
+
numBytesRemainingObj);
|
|
614
634
|
}
|
|
615
635
|
}
|
|
616
636
|
|
|
@@ -670,6 +690,24 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
670
690
|
});
|
|
671
691
|
}
|
|
672
692
|
|
|
693
|
+
/**
|
|
694
|
+
* @param {(shaka.extern.RequestContext|undefined)} context
|
|
695
|
+
* @return {boolean}
|
|
696
|
+
* @private
|
|
697
|
+
*/
|
|
698
|
+
allowSwitch_(context) {
|
|
699
|
+
if (context) {
|
|
700
|
+
const segment = context.segment;
|
|
701
|
+
const stream = context.stream;
|
|
702
|
+
if (segment && stream && stream.fastSwitching) {
|
|
703
|
+
if (segment.isPartial() && !segment.isLastPartial()) {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
|
|
673
711
|
/**
|
|
674
712
|
* This is here only for testability. We can't mock location in our tests on
|
|
675
713
|
* all browsers, so instead we mock this.
|
|
@@ -770,6 +808,7 @@ shaka.net.NetworkingEngine.RequestType = {
|
|
|
770
808
|
'SERVER_CERTIFICATE': 5,
|
|
771
809
|
'KEY': 6,
|
|
772
810
|
'ADS': 7,
|
|
811
|
+
'CONTENT_STEERING': 8,
|
|
773
812
|
};
|
|
774
813
|
|
|
775
814
|
/**
|
|
@@ -887,3 +926,18 @@ shaka.net.NetworkingEngine.OnDownloadFailed;
|
|
|
887
926
|
* @export
|
|
888
927
|
*/
|
|
889
928
|
shaka.net.NetworkingEngine.OnRequest;
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* @typedef {function(
|
|
933
|
+
* !shaka.net.NetworkingEngine.RequestType,
|
|
934
|
+
* (shaka.extern.RequestContext|undefined),
|
|
935
|
+
* string,
|
|
936
|
+
* string)}
|
|
937
|
+
*
|
|
938
|
+
* @description
|
|
939
|
+
* A callback function called on every request retry. The first string is the
|
|
940
|
+
* new URI and the second string is the old URI.
|
|
941
|
+
* @export
|
|
942
|
+
*/
|
|
943
|
+
shaka.net.NetworkingEngine.OnRetry;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.net.NetworkingUtils');
|
|
8
|
+
|
|
9
|
+
goog.require('goog.Uri');
|
|
10
|
+
goog.require('shaka.net.NetworkingEngine');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @summary Networking utility functions.
|
|
15
|
+
*/
|
|
16
|
+
shaka.net.NetworkingUtils = class {
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} uri
|
|
19
|
+
* @param {!shaka.net.NetworkingEngine} netEngine
|
|
20
|
+
* @param {shaka.extern.RetryParameters} retryParams
|
|
21
|
+
* @return {!Promise.<string>}
|
|
22
|
+
*/
|
|
23
|
+
static async getMimeType(uri, netEngine, retryParams) {
|
|
24
|
+
const extension = shaka.net.NetworkingUtils.getExtension_(uri);
|
|
25
|
+
let mimeType =
|
|
26
|
+
shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
27
|
+
if (mimeType) {
|
|
28
|
+
return mimeType;
|
|
29
|
+
}
|
|
30
|
+
const type = shaka.net.NetworkingEngine.RequestType.MANIFEST;
|
|
31
|
+
|
|
32
|
+
const request = shaka.net.NetworkingEngine.makeRequest([uri], retryParams);
|
|
33
|
+
request.method = 'HEAD';
|
|
34
|
+
|
|
35
|
+
const response = await netEngine.request(type, request).promise;
|
|
36
|
+
|
|
37
|
+
// https://bit.ly/2K9s9kf says this header should always be available,
|
|
38
|
+
// but just to be safe:
|
|
39
|
+
mimeType = response.headers['content-type'];
|
|
40
|
+
return mimeType ? mimeType.toLowerCase().split(';').shift() : '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} uri
|
|
46
|
+
* @return {string}
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
static getExtension_(uri) {
|
|
50
|
+
const uriObj = new goog.Uri(uri);
|
|
51
|
+
const uriPieces = uriObj.getPath().split('/');
|
|
52
|
+
const uriFilename = uriPieces.pop();
|
|
53
|
+
const filenamePieces = uriFilename.split('.');
|
|
54
|
+
|
|
55
|
+
// Only one piece means there is no extension.
|
|
56
|
+
if (filenamePieces.length == 1) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return filenamePieces.pop().toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @const {!Object.<string, string>}
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = {
|
|
69
|
+
'mp4': 'video/mp4',
|
|
70
|
+
'm4v': 'video/mp4',
|
|
71
|
+
'm4a': 'audio/mp4',
|
|
72
|
+
'webm': 'video/webm',
|
|
73
|
+
'weba': 'audio/webm',
|
|
74
|
+
'mkv': 'video/webm', // Chromium browsers supports it.
|
|
75
|
+
'ts': 'video/mp2t',
|
|
76
|
+
'ogv': 'video/ogg',
|
|
77
|
+
'ogg': 'audio/ogg',
|
|
78
|
+
'mpg': 'video/mpeg',
|
|
79
|
+
'mpeg': 'video/mpeg',
|
|
80
|
+
'm3u8': 'application/x-mpegurl',
|
|
81
|
+
'mpd': 'application/dash+xml',
|
|
82
|
+
'ism': 'application/vnd.ms-sstr+xml',
|
|
83
|
+
'mp3': 'audio/mpeg',
|
|
84
|
+
'aac': 'audio/aac',
|
|
85
|
+
'flac': 'audio/flac',
|
|
86
|
+
'wav': 'audio/wav',
|
|
87
|
+
'sbv': 'text/x-subviewer',
|
|
88
|
+
'srt': 'text/srt',
|
|
89
|
+
'vtt': 'text/vtt',
|
|
90
|
+
'webvtt': 'text/vtt',
|
|
91
|
+
'ttml': 'application/ttml+xml',
|
|
92
|
+
'lrc': 'application/x-subtitle-lrc',
|
|
93
|
+
'ssa': 'text/x-ssa',
|
|
94
|
+
'ass': 'text/x-ssa',
|
|
95
|
+
};
|
|
@@ -158,6 +158,7 @@ shaka.offline.indexeddb.V1StorageCell = class
|
|
|
158
158
|
frameRate: old.frameRate,
|
|
159
159
|
pixelAspectRatio: undefined,
|
|
160
160
|
hdr: undefined,
|
|
161
|
+
videoLayout: undefined,
|
|
161
162
|
kind: old.kind,
|
|
162
163
|
language: old.language,
|
|
163
164
|
originalLanguage: old.language || null,
|
|
@@ -179,6 +180,7 @@ shaka.offline.indexeddb.V1StorageCell = class
|
|
|
179
180
|
closedCaptions: null,
|
|
180
181
|
tilesLayout: undefined,
|
|
181
182
|
external: false,
|
|
183
|
+
fastSwitching: false,
|
|
182
184
|
};
|
|
183
185
|
}
|
|
184
186
|
|
|
@@ -107,6 +107,7 @@ shaka.offline.indexeddb.V2StorageCell = class
|
|
|
107
107
|
frameRate: old.frameRate,
|
|
108
108
|
pixelAspectRatio: old.pixelAspectRatio,
|
|
109
109
|
hdr: undefined,
|
|
110
|
+
videoLayout: undefined,
|
|
110
111
|
kind: old.kind,
|
|
111
112
|
language: old.language,
|
|
112
113
|
originalLanguage: old.language || null,
|
|
@@ -128,6 +129,7 @@ shaka.offline.indexeddb.V2StorageCell = class
|
|
|
128
129
|
closedCaptions: null,
|
|
129
130
|
tilesLayout: undefined,
|
|
130
131
|
external: false,
|
|
132
|
+
fastSwitching: false,
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
|
|
@@ -190,6 +190,7 @@ shaka.offline.ManifestConverter = class {
|
|
|
190
190
|
frameRate: streamDB.frameRate,
|
|
191
191
|
pixelAspectRatio: streamDB.pixelAspectRatio,
|
|
192
192
|
hdr: streamDB.hdr,
|
|
193
|
+
videoLayout: streamDB.videoLayout,
|
|
193
194
|
kind: streamDB.kind,
|
|
194
195
|
encrypted: streamDB.encrypted,
|
|
195
196
|
drmInfos: [],
|
|
@@ -210,6 +211,7 @@ shaka.offline.ManifestConverter = class {
|
|
|
210
211
|
tilesLayout: streamDB.tilesLayout,
|
|
211
212
|
accessibilityPurpose: null,
|
|
212
213
|
external: streamDB.external,
|
|
214
|
+
fastSwitching: streamDB.fastSwitching,
|
|
213
215
|
};
|
|
214
216
|
|
|
215
217
|
return stream;
|
|
@@ -112,6 +112,16 @@ shaka.offline.OfflineManifestParser = class {
|
|
|
112
112
|
await muxer.destroy();
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
/** @override */
|
|
117
|
+
onInitialVariantChosen(variant) {
|
|
118
|
+
// No-op
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** @override */
|
|
122
|
+
banLocation(uri) {
|
|
123
|
+
// No-op
|
|
124
|
+
}
|
|
115
125
|
};
|
|
116
126
|
|
|
117
127
|
|
package/lib/offline/storage.js
CHANGED
|
@@ -12,6 +12,7 @@ goog.require('shaka.log');
|
|
|
12
12
|
goog.require('shaka.media.DrmEngine');
|
|
13
13
|
goog.require('shaka.media.ManifestParser');
|
|
14
14
|
goog.require('shaka.net.NetworkingEngine');
|
|
15
|
+
goog.require('shaka.net.NetworkingUtils');
|
|
15
16
|
goog.require('shaka.offline.DownloadInfo');
|
|
16
17
|
goog.require('shaka.offline.DownloadManager');
|
|
17
18
|
goog.require('shaka.offline.OfflineUri');
|
|
@@ -271,10 +272,13 @@ shaka.offline.Storage = class {
|
|
|
271
272
|
goog.asserts.assert(
|
|
272
273
|
this.networkingEngine_, 'Should not call |store| after |destroy|');
|
|
273
274
|
|
|
274
|
-
|
|
275
|
+
if (!mimeType) {
|
|
276
|
+
mimeType = await shaka.net.NetworkingUtils.getMimeType(
|
|
277
|
+
uri, this.networkingEngine_, config.manifest.retryParameters);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const factory = shaka.media.ManifestParser.getFactory(
|
|
275
281
|
uri,
|
|
276
|
-
this.networkingEngine_,
|
|
277
|
-
config.manifest.retryParameters,
|
|
278
282
|
mimeType || null);
|
|
279
283
|
|
|
280
284
|
return factory();
|
|
@@ -656,20 +660,19 @@ shaka.offline.Storage = class {
|
|
|
656
660
|
// Filter the manifest based on what we know MediaCapabilities will be able
|
|
657
661
|
// to play later (no point storing something we can't play).
|
|
658
662
|
await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
|
|
659
|
-
manifest, config.offline.usePersistentLicense);
|
|
663
|
+
drmEngine, manifest, config.offline.usePersistentLicense);
|
|
660
664
|
|
|
661
665
|
// Gather all tracks.
|
|
662
666
|
const allTracks = [];
|
|
663
667
|
|
|
664
668
|
// Choose the codec that has the lowest average bandwidth.
|
|
665
|
-
const preferredAudioChannelCount = config.preferredAudioChannelCount;
|
|
666
669
|
const preferredDecodingAttributes = config.preferredDecodingAttributes;
|
|
667
670
|
const preferredVideoCodecs = config.preferredVideoCodecs;
|
|
668
671
|
const preferredAudioCodecs = config.preferredAudioCodecs;
|
|
669
672
|
|
|
670
673
|
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
|
|
671
674
|
manifest, preferredVideoCodecs, preferredAudioCodecs,
|
|
672
|
-
|
|
675
|
+
preferredDecodingAttributes);
|
|
673
676
|
|
|
674
677
|
for (const variant of manifest.variants) {
|
|
675
678
|
goog.asserts.assert(
|
|
@@ -1230,7 +1233,6 @@ shaka.offline.Storage = class {
|
|
|
1230
1233
|
drmEngine.configure(config.drm);
|
|
1231
1234
|
await drmEngine.initForStorage(
|
|
1232
1235
|
manifest.variants, config.offline.usePersistentLicense);
|
|
1233
|
-
await drmEngine.setServerCertificate();
|
|
1234
1236
|
await drmEngine.createOrLoad();
|
|
1235
1237
|
|
|
1236
1238
|
return drmEngine;
|
|
@@ -1309,6 +1311,7 @@ shaka.offline.Storage = class {
|
|
|
1309
1311
|
frameRate: stream.frameRate,
|
|
1310
1312
|
pixelAspectRatio: stream.pixelAspectRatio,
|
|
1311
1313
|
hdr: stream.hdr,
|
|
1314
|
+
videoLayout: stream.videoLayout,
|
|
1312
1315
|
kind: stream.kind,
|
|
1313
1316
|
language: stream.language,
|
|
1314
1317
|
originalLanguage: stream.originalLanguage,
|
|
@@ -1327,6 +1330,7 @@ shaka.offline.Storage = class {
|
|
|
1327
1330
|
closedCaptions: stream.closedCaptions,
|
|
1328
1331
|
tilesLayout: stream.tilesLayout,
|
|
1329
1332
|
external: stream.external,
|
|
1333
|
+
fastSwitching: stream.fastSwitching,
|
|
1330
1334
|
};
|
|
1331
1335
|
|
|
1332
1336
|
const startTime =
|