shaka-player 4.5.0 → 4.6.1
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 +109 -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 +34 -30
- 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 +789 -738
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +162 -77
- package/dist/shaka-player.compiled.js +1147 -1113
- 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 +857 -791
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +167 -77
- package/dist/shaka-player.ui.js +1359 -1314
- 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 +251 -84
- 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 +92 -29
- package/lib/hls/hls_classes.js +28 -43
- package/lib/hls/hls_parser.js +770 -652
- 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 +72 -44
- 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 +998 -1503
- 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 +417 -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
|
|
|
@@ -1337,6 +1351,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1337
1351
|
let remaining = new Uint8Array(0);
|
|
1338
1352
|
let processingResult = false;
|
|
1339
1353
|
let callbackCalled = false;
|
|
1354
|
+
let streamDataCallbackError;
|
|
1340
1355
|
const streamDataCallback = async (data) => {
|
|
1341
1356
|
if (processingResult) {
|
|
1342
1357
|
// If the fallback result processing was triggered, don't also
|
|
@@ -1349,35 +1364,47 @@ shaka.media.StreamingEngine = class {
|
|
|
1349
1364
|
if (this.fatalError_) {
|
|
1350
1365
|
return;
|
|
1351
1366
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1367
|
+
try {
|
|
1368
|
+
// Append the data with complete boxes.
|
|
1369
|
+
// Every time streamDataCallback gets called, append the new data
|
|
1370
|
+
// to the remaining data.
|
|
1371
|
+
// Find the last fully completed Mdat box, and slice the data into
|
|
1372
|
+
// two parts: the first part with completed Mdat boxes, and the
|
|
1373
|
+
// second part with an incomplete box.
|
|
1374
|
+
// Append the first part, and save the second part as remaining
|
|
1375
|
+
// data, and handle it with the next streamDataCallback call.
|
|
1376
|
+
remaining = this.concatArray_(remaining, data);
|
|
1377
|
+
let sawMDAT = false;
|
|
1378
|
+
let offset = 0;
|
|
1379
|
+
new shaka.util.Mp4Parser()
|
|
1380
|
+
.box('mdat', (box) => {
|
|
1381
|
+
offset = box.size + box.start;
|
|
1382
|
+
sawMDAT = true;
|
|
1383
|
+
})
|
|
1384
|
+
.parse(remaining, /* partialOkay= */ false,
|
|
1385
|
+
/* isChunkedData= */ true);
|
|
1386
|
+
if (sawMDAT) {
|
|
1387
|
+
const dataToAppend = remaining.subarray(0, offset);
|
|
1388
|
+
remaining = remaining.subarray(offset);
|
|
1389
|
+
await this.append_(
|
|
1390
|
+
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1369
1391
|
/* isChunkedData= */ true);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1392
|
+
|
|
1393
|
+
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1394
|
+
mediaState.segmentPrefetch.prefetchSegments(
|
|
1395
|
+
reference, /* skipFirst= */ true);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
streamDataCallbackError = error;
|
|
1376
1400
|
}
|
|
1377
1401
|
};
|
|
1378
1402
|
|
|
1379
1403
|
const result =
|
|
1380
1404
|
await this.fetch_(mediaState, reference, streamDataCallback);
|
|
1405
|
+
if (streamDataCallbackError) {
|
|
1406
|
+
throw streamDataCallbackError;
|
|
1407
|
+
}
|
|
1381
1408
|
if (!callbackCalled) {
|
|
1382
1409
|
// In some environments, we might be forced to use network plugins
|
|
1383
1410
|
// that don't support streamDataCallback. In those cases, as a
|
|
@@ -1403,6 +1430,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1403
1430
|
await this.append_(
|
|
1404
1431
|
mediaState, presentationTime, stream, reference, result);
|
|
1405
1432
|
}
|
|
1433
|
+
|
|
1434
|
+
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1435
|
+
mediaState.segmentPrefetch.prefetchSegments(
|
|
1436
|
+
reference, /* skipFirst= */ true);
|
|
1437
|
+
}
|
|
1406
1438
|
} else {
|
|
1407
1439
|
if (this.config_.lowLatencyMode && !isReadableStreamSupported) {
|
|
1408
1440
|
shaka.log.warning('Low latency streaming mode is enabled, but ' +
|
|
@@ -1499,11 +1531,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1499
1531
|
/**
|
|
1500
1532
|
* @param {!BufferSource} rawResult
|
|
1501
1533
|
* @param {shaka.extern.aes128Key} aes128Key
|
|
1502
|
-
* @param {
|
|
1534
|
+
* @param {number} position
|
|
1503
1535
|
* @return {!Promise.<!BufferSource>} finalResult
|
|
1504
1536
|
* @private
|
|
1505
1537
|
*/
|
|
1506
|
-
async aes128Decrypt_(rawResult, aes128Key,
|
|
1538
|
+
async aes128Decrypt_(rawResult, aes128Key, position) {
|
|
1507
1539
|
const key = aes128Key;
|
|
1508
1540
|
if (!key.cryptoKey) {
|
|
1509
1541
|
goog.asserts.assert(key.fetchKey, 'If AES-128 cryptoKey was not ' +
|
|
@@ -1514,7 +1546,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1514
1546
|
let iv = key.iv;
|
|
1515
1547
|
if (!iv) {
|
|
1516
1548
|
iv = shaka.util.BufferUtils.toUint8(new ArrayBuffer(16));
|
|
1517
|
-
let sequence = key.firstMediaSequenceNumber +
|
|
1549
|
+
let sequence = key.firstMediaSequenceNumber + position;
|
|
1518
1550
|
for (let i = iv.byteLength - 1; i >= 0; i--) {
|
|
1519
1551
|
iv[i] = sequence & 0xff;
|
|
1520
1552
|
sequence >>= 8;
|
|
@@ -1764,8 +1796,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1764
1796
|
shaka.log.v1(logPrefix, 'fetching init segment');
|
|
1765
1797
|
|
|
1766
1798
|
const fetchInit =
|
|
1767
|
-
this.fetch_(mediaState, reference.initSegmentReference
|
|
1768
|
-
/* streamDataCallback= */ undefined, /* isInit= */ true);
|
|
1799
|
+
this.fetch_(mediaState, reference.initSegmentReference);
|
|
1769
1800
|
const append = async () => {
|
|
1770
1801
|
try {
|
|
1771
1802
|
const initSegment = await fetchInit;
|
|
@@ -2169,13 +2200,12 @@ shaka.media.StreamingEngine = class {
|
|
|
2169
2200
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2170
2201
|
* reference
|
|
2171
2202
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2172
|
-
* @param {boolean=} isInit
|
|
2173
2203
|
*
|
|
2174
2204
|
* @return {!Promise.<BufferSource>}
|
|
2175
2205
|
* @private
|
|
2176
2206
|
* @suppress {strictMissingProperties}
|
|
2177
2207
|
*/
|
|
2178
|
-
async fetch_(mediaState, reference, streamDataCallback
|
|
2208
|
+
async fetch_(mediaState, reference, streamDataCallback) {
|
|
2179
2209
|
if (reference instanceof shaka.media.InitSegmentReference) {
|
|
2180
2210
|
const segmentData = reference.getSegmentData();
|
|
2181
2211
|
if (segmentData) {
|
|
@@ -2183,17 +2213,18 @@ shaka.media.StreamingEngine = class {
|
|
|
2183
2213
|
}
|
|
2184
2214
|
}
|
|
2185
2215
|
let op = null;
|
|
2186
|
-
if (
|
|
2187
|
-
mediaState.segmentPrefetch &&
|
|
2188
|
-
reference instanceof shaka.media.SegmentReference
|
|
2189
|
-
) {
|
|
2216
|
+
if (mediaState.segmentPrefetch) {
|
|
2190
2217
|
op = mediaState.segmentPrefetch.getPrefetchedSegment(
|
|
2191
2218
|
reference, streamDataCallback);
|
|
2192
2219
|
}
|
|
2193
2220
|
if (!op) {
|
|
2194
2221
|
op = this.dispatchFetch_(
|
|
2195
|
-
reference, mediaState.stream, streamDataCallback
|
|
2196
|
-
|
|
2222
|
+
reference, mediaState.stream, streamDataCallback);
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
let position = 0;
|
|
2226
|
+
if (mediaState.segmentIterator) {
|
|
2227
|
+
position = mediaState.segmentIterator.currentPosition();
|
|
2197
2228
|
}
|
|
2198
2229
|
|
|
2199
2230
|
mediaState.operation = op;
|
|
@@ -2201,9 +2232,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2201
2232
|
mediaState.operation = null;
|
|
2202
2233
|
let result = response.data;
|
|
2203
2234
|
if (reference.aes128Key) {
|
|
2204
|
-
|
|
2205
|
-
goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
|
|
2206
|
-
result = await this.aes128Decrypt_(result, reference.aes128Key, iter);
|
|
2235
|
+
result = await this.aes128Decrypt_(result, reference.aes128Key, position);
|
|
2207
2236
|
}
|
|
2208
2237
|
return result;
|
|
2209
2238
|
}
|
|
@@ -2215,18 +2244,17 @@ shaka.media.StreamingEngine = class {
|
|
|
2215
2244
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2216
2245
|
* reference
|
|
2217
2246
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2218
|
-
* @param {boolean=} isInit
|
|
2219
2247
|
*
|
|
2220
2248
|
* @return {!shaka.net.NetworkingEngine.PendingRequest}
|
|
2221
2249
|
* @private
|
|
2222
2250
|
*/
|
|
2223
|
-
dispatchFetch_(reference, stream, streamDataCallback
|
|
2251
|
+
dispatchFetch_(reference, stream, streamDataCallback) {
|
|
2224
2252
|
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
2253
|
const segment = reference instanceof shaka.media.SegmentReference ?
|
|
2229
2254
|
reference : undefined;
|
|
2255
|
+
const type = segment ?
|
|
2256
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT :
|
|
2257
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
|
|
2230
2258
|
const request = shaka.util.Networking.createSegmentRequest(
|
|
2231
2259
|
reference.getUris(),
|
|
2232
2260
|
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
|
|