shaka-player 4.3.12 → 4.3.14
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/question.md +7 -0
- package/.github/workflows/release-please.yaml +2 -27
- package/AUTHORS +5 -0
- package/CHANGELOG.md +108 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +6 -0
- package/README.md +2 -0
- package/demo/common/asset.js +5 -1
- package/demo/common/assets.js +16 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +2 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +1 -1
- package/dist/demo.compiled.debug.js +56 -54
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +56 -54
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +6 -6
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +24 -22
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +24 -22
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.externs.js +79 -49
- package/dist/shaka-player.compiled.debug.js +426 -410
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +79 -49
- package/dist/shaka-player.compiled.js +912 -901
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +38 -7
- package/dist/shaka-player.ui.debug.d.ts +38 -7
- package/dist/shaka-player.ui.debug.externs.js +80 -49
- package/dist/shaka-player.ui.debug.js +460 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +80 -49
- package/dist/shaka-player.ui.js +1060 -1049
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/offline.md +1 -1
- package/docs/tutorials/plugins.md +4 -0
- package/docs/tutorials/text-displayer.md +15 -0
- package/docs/tutorials/ui-customization.md +29 -0
- package/docs/tutorials/ui.md +4 -1
- package/externs/lcevc.js +6 -0
- package/externs/shaka/manifest_parser.js +8 -0
- package/externs/shaka/player.js +1 -1
- package/externs/shaka/text.js +3 -1
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +7 -2
- package/lib/ads/server_side_ad_manager.js +4 -0
- package/lib/cast/cast_proxy.js +18 -0
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/cea_decoder.js +11 -1
- package/lib/dash/dash_parser.js +79 -42
- package/lib/dash/mpd_utils.js +39 -27
- package/lib/hls/hls_parser.js +148 -49
- package/lib/hls/hls_utils.js +5 -5
- package/lib/media/content_workarounds.js +30 -0
- package/lib/media/drm_engine.js +16 -12
- package/lib/media/media_source_engine.js +4 -2
- package/lib/media/play_rate_controller.js +1 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_reference.js +16 -7
- package/lib/media/streaming_engine.js +64 -54
- package/lib/media/transmuxer.js +1 -1
- package/lib/media/video_wrapper.js +4 -3
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/session_deleter.js +0 -8
- package/lib/offline/storage.js +4 -1
- package/lib/player.js +79 -18
- package/lib/polyfill/media_capabilities.js +23 -3
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/simple_text_displayer.js +7 -6
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +33 -5
- package/lib/text/ui_text_displayer.js +81 -15
- package/lib/text/vtt_text_parser.js +13 -0
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/cmcd_manager.js +16 -1
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/manifest_parser_utils.js +20 -8
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +91 -36
- package/lib/util/platform.js +39 -2
- package/lib/util/player_configuration.js +5 -3
- package/lib/util/stream_utils.js +36 -28
- package/lib/util/ts_parser.js +3 -3
- package/lib/util/xml_utils.js +5 -0
- package/package.json +2 -2
- package/test/cea/cea_decoder_unit.js +40 -0
- package/test/dash/dash_parser_live_unit.js +5 -8
- package/test/dash/dash_parser_manifest_unit.js +5 -0
- package/test/dash/dash_parser_segment_base_unit.js +5 -0
- package/test/dash/dash_parser_segment_list_unit.js +21 -17
- package/test/dash/dash_parser_segment_template_unit.js +5 -0
- package/test/hls/hls_live_unit.js +33 -0
- package/test/hls/hls_parser_unit.js +52 -60
- package/test/media/buffering_observer_unit.js +13 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +30 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/transmuxer_integration.js +2 -2
- package/test/offline/storage_integration.js +3 -0
- package/test/player_integration.js +40 -40
- package/test/player_unit.js +31 -0
- package/test/polyfill/media_capabilities_unit.js +12 -6
- package/test/test/util/dash_parser_util.js +16 -7
- package/test/test/util/fake_drm_engine.js +7 -0
- package/test/test/util/layout_tests.js +2 -1
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/test_scheme.js +3 -0
- package/test/test/util/waiter.js +5 -0
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/simple_text_displayer_unit.js +1 -1
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +49 -18
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/text/vtt_text_parser_unit.js +2 -17
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/periods_unit.js +113 -0
- package/test/util/ts_parser_unit.js +1 -1
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/externs/ui.js +24 -0
- package/ui/language_utils.js +11 -5
- package/ui/play_button.js +20 -0
- package/ui/playback_rate_selection.js +4 -0
- package/ui/presentation_time.js +5 -5
- package/ui/small_play_button.js +2 -2
- package/ui/volume_bar.js +10 -6
- /package/.github/{config.yml → ISSUE_TEMPLATE/config.yml} +0 -0
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @fileoverview
|
|
9
|
-
* @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
goog.provide('shaka.media.StreamingEngine');
|
|
@@ -19,6 +18,8 @@ goog.require('shaka.media.SegmentIterator');
|
|
|
19
18
|
goog.require('shaka.media.SegmentReference');
|
|
20
19
|
goog.require('shaka.net.Backoff');
|
|
21
20
|
goog.require('shaka.net.NetworkingEngine');
|
|
21
|
+
goog.require('shaka.util.BufferUtils');
|
|
22
|
+
goog.require('shaka.util.CmcdManager');
|
|
22
23
|
goog.require('shaka.util.DelayedTick');
|
|
23
24
|
goog.require('shaka.util.Destroyer');
|
|
24
25
|
goog.require('shaka.util.Error');
|
|
@@ -27,6 +28,7 @@ goog.require('shaka.util.IDestroyable');
|
|
|
27
28
|
goog.require('shaka.util.Id3Utils');
|
|
28
29
|
goog.require('shaka.util.ManifestParserUtils');
|
|
29
30
|
goog.require('shaka.util.MimeUtils');
|
|
31
|
+
goog.require('shaka.util.Mp4BoxParsers');
|
|
30
32
|
goog.require('shaka.util.Mp4Parser');
|
|
31
33
|
goog.require('shaka.util.Networking');
|
|
32
34
|
|
|
@@ -1285,6 +1287,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1285
1287
|
let remaining = new Uint8Array(0);
|
|
1286
1288
|
let processingResult = false;
|
|
1287
1289
|
let callbackCalled = false;
|
|
1290
|
+
let streamDataCallbackError;
|
|
1288
1291
|
const streamDataCallback = async (data) => {
|
|
1289
1292
|
if (processingResult) {
|
|
1290
1293
|
// If the fallback result processing was triggered, don't also
|
|
@@ -1297,35 +1300,44 @@ shaka.media.StreamingEngine = class {
|
|
|
1297
1300
|
if (this.fatalError_) {
|
|
1298
1301
|
return;
|
|
1299
1302
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1303
|
+
try {
|
|
1304
|
+
// Append the data with complete boxes.
|
|
1305
|
+
// Every time streamDataCallback gets called, append the new data
|
|
1306
|
+
// to the remaining data.
|
|
1307
|
+
// Find the last fully completed Mdat box, and slice the data into
|
|
1308
|
+
// two parts: the first part with completed Mdat boxes, and the
|
|
1309
|
+
// second part with an incomplete box.
|
|
1310
|
+
// Append the first part, and save the second part as remaining
|
|
1311
|
+
// data, and handle it with the next streamDataCallback call.
|
|
1312
|
+
remaining = this.concatArray_(remaining, data);
|
|
1313
|
+
let sawMDAT = false;
|
|
1314
|
+
let offset = 0;
|
|
1315
|
+
new shaka.util.Mp4Parser()
|
|
1316
|
+
.box('mdat', (box) => {
|
|
1317
|
+
offset = box.size + box.start;
|
|
1318
|
+
sawMDAT = true;
|
|
1319
|
+
})
|
|
1320
|
+
.parse(remaining, /* partialOkay= */ false,
|
|
1321
|
+
/* isChunkedData= */ true);
|
|
1322
|
+
if (sawMDAT) {
|
|
1323
|
+
const dataToAppend = remaining.subarray(0, offset);
|
|
1324
|
+
remaining = remaining.subarray(offset);
|
|
1325
|
+
await this.append_(
|
|
1326
|
+
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1317
1327
|
/* isChunkedData= */ true);
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
await this.append_(
|
|
1322
|
-
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1323
|
-
/* isChunkedData= */ true);
|
|
1328
|
+
}
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
streamDataCallbackError = error;
|
|
1324
1331
|
}
|
|
1325
1332
|
};
|
|
1326
1333
|
|
|
1327
1334
|
const result =
|
|
1328
1335
|
await this.fetch_(mediaState, reference, streamDataCallback);
|
|
1336
|
+
|
|
1337
|
+
if (streamDataCallbackError) {
|
|
1338
|
+
throw streamDataCallbackError;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1329
1341
|
if (!callbackCalled) {
|
|
1330
1342
|
// In some environments, we might be forced to use network plugins
|
|
1331
1343
|
// that don't support streamDataCallback. In those cases, as a
|
|
@@ -1357,16 +1369,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1357
1369
|
'ReadableStream is not supported by the browser.');
|
|
1358
1370
|
}
|
|
1359
1371
|
const fetchSegment = this.fetch_(mediaState, reference);
|
|
1360
|
-
|
|
1372
|
+
const result = await fetchSegment;
|
|
1361
1373
|
this.destroyer_.ensureNotDestroyed();
|
|
1362
1374
|
if (this.fatalError_) {
|
|
1363
1375
|
return;
|
|
1364
1376
|
}
|
|
1365
|
-
if (reference.hlsAes128Key) {
|
|
1366
|
-
goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
|
|
1367
|
-
result = await this.aes128Decrypt_(result, reference, iter);
|
|
1368
|
-
}
|
|
1369
|
-
this.destroyer_.ensureNotDestroyed();
|
|
1370
1377
|
|
|
1371
1378
|
// If the text stream gets switched between fetch_() and append_(), the
|
|
1372
1379
|
// new text parser is initialized, but the new init segment is not
|
|
@@ -1450,13 +1457,13 @@ shaka.media.StreamingEngine = class {
|
|
|
1450
1457
|
|
|
1451
1458
|
/**
|
|
1452
1459
|
* @param {!BufferSource} rawResult
|
|
1453
|
-
* @param {
|
|
1460
|
+
* @param {shaka.extern.HlsAes128Key} aes128Key
|
|
1454
1461
|
* @param {!shaka.media.SegmentIterator} iter
|
|
1455
1462
|
* @return {!Promise.<!BufferSource>} finalResult
|
|
1456
1463
|
* @private
|
|
1457
1464
|
*/
|
|
1458
|
-
async aes128Decrypt_(rawResult,
|
|
1459
|
-
const key =
|
|
1465
|
+
async aes128Decrypt_(rawResult, aes128Key, iter) {
|
|
1466
|
+
const key = aes128Key;
|
|
1460
1467
|
if (!key.cryptoKey) {
|
|
1461
1468
|
goog.asserts.assert(key.fetchKey, 'If AES-128 cryptoKey was not ' +
|
|
1462
1469
|
'preloaded, fetchKey function should be provided');
|
|
@@ -1693,10 +1700,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1693
1700
|
}
|
|
1694
1701
|
}
|
|
1695
1702
|
|
|
1696
|
-
const lastDiscontinuitySequence =
|
|
1697
|
-
mediaState.lastSegmentReference ?
|
|
1698
|
-
mediaState.lastSegmentReference.discontinuitySequence : null;
|
|
1699
1703
|
if (this.manifest_.sequenceMode) {
|
|
1704
|
+
const lastDiscontinuitySequence =
|
|
1705
|
+
mediaState.lastSegmentReference ?
|
|
1706
|
+
mediaState.lastSegmentReference.discontinuitySequence : null;
|
|
1700
1707
|
// Across discontinuity bounds, we should resync timestamps for
|
|
1701
1708
|
// sequence mode playbacks. The next segment appended should
|
|
1702
1709
|
// land at its theoretical timestamp from the segment index.
|
|
@@ -1706,18 +1713,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1706
1713
|
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1707
1714
|
mediaState.type, reference.startTime));
|
|
1708
1715
|
}
|
|
1709
|
-
} else {
|
|
1710
|
-
// In segments mode, we need to resync to set the timestampOffset
|
|
1711
|
-
// to the start of the current discontinuity sequence. This is
|
|
1712
|
-
// because individual discontinuity sequences may have internal
|
|
1713
|
-
// timestamps that overlap, so we adjust the timestampOffset to avoid
|
|
1714
|
-
// having the SourceBuffer get overwritten.
|
|
1715
|
-
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
|
|
1716
|
-
operations.push(
|
|
1717
|
-
this.playerInterface_.mediaSourceEngine.resync(
|
|
1718
|
-
mediaState.type,
|
|
1719
|
-
reference.timestampOffset));
|
|
1720
|
-
}
|
|
1721
1716
|
}
|
|
1722
1717
|
|
|
1723
1718
|
await Promise.all(operations);
|
|
@@ -2021,8 +2016,8 @@ shaka.media.StreamingEngine = class {
|
|
|
2021
2016
|
const bufferedBehind = presentationTime - startTime;
|
|
2022
2017
|
|
|
2023
2018
|
const overflow = bufferedBehind - bufferBehind;
|
|
2024
|
-
// See: https://github.com/shaka-project/shaka-player/issues/
|
|
2025
|
-
if (overflow <=
|
|
2019
|
+
// See: https://github.com/shaka-project/shaka-player/issues/6240
|
|
2020
|
+
if (overflow <= 1) {
|
|
2026
2021
|
shaka.log.v2(logPrefix,
|
|
2027
2022
|
'buffer behind okay:',
|
|
2028
2023
|
'presentationTime=' + presentationTime,
|
|
@@ -2073,7 +2068,6 @@ shaka.media.StreamingEngine = class {
|
|
|
2073
2068
|
*
|
|
2074
2069
|
* @return {!Promise.<BufferSource>}
|
|
2075
2070
|
* @private
|
|
2076
|
-
* @suppress {strictMissingProperties}
|
|
2077
2071
|
*/
|
|
2078
2072
|
async fetch_(mediaState, reference, streamDataCallback) {
|
|
2079
2073
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
@@ -2088,12 +2082,17 @@ shaka.media.StreamingEngine = class {
|
|
|
2088
2082
|
shaka.log.v2('fetching: reference=', reference);
|
|
2089
2083
|
|
|
2090
2084
|
const stream = mediaState.stream;
|
|
2085
|
+
const isInit = reference instanceof shaka.media.InitSegmentReference;
|
|
2086
|
+
const mediaReference =
|
|
2087
|
+
/** @type {!shaka.media.SegmentReference} */(reference);
|
|
2088
|
+
const duration = isInit ? 0 :
|
|
2089
|
+
mediaReference.endTime - mediaReference.startTime;
|
|
2091
2090
|
this.playerInterface_.modifySegmentRequest(
|
|
2092
2091
|
request,
|
|
2093
2092
|
{
|
|
2094
2093
|
type: stream.type,
|
|
2095
|
-
init:
|
|
2096
|
-
duration
|
|
2094
|
+
init: isInit,
|
|
2095
|
+
duration,
|
|
2097
2096
|
mimeType: stream.mimeType,
|
|
2098
2097
|
codecs: stream.codecs,
|
|
2099
2098
|
bandwidth: stream.bandwidth,
|
|
@@ -2104,7 +2103,18 @@ shaka.media.StreamingEngine = class {
|
|
|
2104
2103
|
mediaState.operation = op;
|
|
2105
2104
|
const response = await op.promise;
|
|
2106
2105
|
mediaState.operation = null;
|
|
2107
|
-
|
|
2106
|
+
|
|
2107
|
+
let result = response.data;
|
|
2108
|
+
if (reference.hlsAes128Key) {
|
|
2109
|
+
let iter = mediaState.segmentIterator;
|
|
2110
|
+
const time = isInit ? null : mediaReference.startTime;
|
|
2111
|
+
if (!iter && time != null) {
|
|
2112
|
+
iter = mediaState.stream.segmentIndex.getIteratorForTime(time);
|
|
2113
|
+
}
|
|
2114
|
+
goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
|
|
2115
|
+
result = await this.aes128Decrypt_(result, reference.hlsAes128Key, iter);
|
|
2116
|
+
}
|
|
2117
|
+
return result;
|
|
2108
2118
|
}
|
|
2109
2119
|
|
|
2110
2120
|
|
package/lib/media/transmuxer.js
CHANGED
|
@@ -175,8 +175,8 @@ shaka.media.VideoWrapper = class {
|
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* A class used to move the playhead away from its current time. Sometimes,
|
|
178
|
-
* Edge ignores re-seeks. After changing the current time, check every
|
|
179
|
-
* retrying if the change was not accepted.
|
|
178
|
+
* legacy Edge ignores re-seeks. After changing the current time, check every
|
|
179
|
+
* 100ms, retrying if the change was not accepted.
|
|
180
180
|
*
|
|
181
181
|
* Delay stats over 100 runs of a re-seeking integration test:
|
|
182
182
|
* Edge - 0ms - 2%
|
|
@@ -186,7 +186,8 @@ shaka.media.VideoWrapper = class {
|
|
|
186
186
|
* Edge - 400ms - 2%
|
|
187
187
|
* Chrome - 0ms - 100%
|
|
188
188
|
*
|
|
189
|
-
*
|
|
189
|
+
* Unfortunately, legacy Edge is not receiving updates anymore, but it still
|
|
190
|
+
* must be supported as it is used for web browsers on XBox.
|
|
190
191
|
*
|
|
191
192
|
* @implements {shaka.util.IReleasable}
|
|
192
193
|
* @final
|
|
@@ -71,14 +71,6 @@ shaka.offline.SessionDeleter = class {
|
|
|
71
71
|
return [];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
try {
|
|
75
|
-
await drmEngine.setServerCertificate();
|
|
76
|
-
} catch (e) {
|
|
77
|
-
shaka.log.warning('Error setting server certificate', e);
|
|
78
|
-
await drmEngine.destroy();
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
74
|
/** @type {!Array.<string>} */
|
|
83
75
|
const sessionIds = [];
|
|
84
76
|
await Promise.all(bucket.sessionIds.map(async (sessionId) => {
|
package/lib/offline/storage.js
CHANGED
|
@@ -396,6 +396,10 @@ shaka.offline.Storage = class {
|
|
|
396
396
|
downloader, config, activeHandle.cell, manifest, drmEngine);
|
|
397
397
|
this.ensureNotDestroyed_();
|
|
398
398
|
|
|
399
|
+
this.setManifestDrmFields_(manifest, manifestDB, drmEngine, config);
|
|
400
|
+
await activeHandle.cell.updateManifest(manifestId, manifestDB);
|
|
401
|
+
this.ensureNotDestroyed_();
|
|
402
|
+
|
|
399
403
|
const offlineUri = shaka.offline.OfflineUri.manifest(
|
|
400
404
|
activeHandle.path.mechanism, activeHandle.path.cell, manifestId);
|
|
401
405
|
|
|
@@ -1231,7 +1235,6 @@ shaka.offline.Storage = class {
|
|
|
1231
1235
|
drmEngine.configure(config.drm);
|
|
1232
1236
|
await drmEngine.initForStorage(
|
|
1233
1237
|
manifest.variants, config.offline.usePersistentLicense);
|
|
1234
|
-
await drmEngine.setServerCertificate();
|
|
1235
1238
|
await drmEngine.createOrLoad();
|
|
1236
1239
|
|
|
1237
1240
|
return drmEngine;
|
package/lib/player.js
CHANGED
|
@@ -479,6 +479,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
479
479
|
*/
|
|
480
480
|
this.loadEventManager_ = new shaka.util.EventManager();
|
|
481
481
|
|
|
482
|
+
/**
|
|
483
|
+
* For listeners scoped to the lifetime of the loaded content.
|
|
484
|
+
* @private {shaka.util.EventManager}
|
|
485
|
+
*/
|
|
486
|
+
this.trickPlayEventManager_ = new shaka.util.EventManager();
|
|
487
|
+
|
|
482
488
|
/** @private {shaka.net.NetworkingEngine} */
|
|
483
489
|
this.networkingEngine_ = null;
|
|
484
490
|
|
|
@@ -897,6 +903,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
897
903
|
this.loadEventManager_.release();
|
|
898
904
|
this.loadEventManager_ = null;
|
|
899
905
|
}
|
|
906
|
+
if (this.trickPlayEventManager_) {
|
|
907
|
+
this.trickPlayEventManager_.release();
|
|
908
|
+
this.trickPlayEventManager_ = null;
|
|
909
|
+
}
|
|
900
910
|
|
|
901
911
|
this.abrManagerFactory_ = null;
|
|
902
912
|
this.config_ = null;
|
|
@@ -1555,6 +1565,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1555
1565
|
// be if there was an error and we, by chance, did not have a media element.
|
|
1556
1566
|
if (has.mediaElement) {
|
|
1557
1567
|
this.loadEventManager_.removeAll();
|
|
1568
|
+
this.trickPlayEventManager_.removeAll();
|
|
1558
1569
|
}
|
|
1559
1570
|
|
|
1560
1571
|
// Stop the variant checker timer
|
|
@@ -2012,8 +2023,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2012
2023
|
|
|
2013
2024
|
this.drmEngine_.configure(this.config_.drm);
|
|
2014
2025
|
|
|
2026
|
+
const tracksChanged = shaka.util.StreamUtils.applyRestrictions(
|
|
2027
|
+
this.manifest_.variants, this.config_.restrictions, this.maxHwRes_);
|
|
2028
|
+
if (tracksChanged && this.streamingEngine_) {
|
|
2029
|
+
this.onTracksChanged_();
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
const playableVariants = shaka.util.StreamUtils.getPlayableVariants(
|
|
2033
|
+
this.manifest_.variants);
|
|
2034
|
+
|
|
2015
2035
|
await this.drmEngine_.initForPlayback(
|
|
2016
|
-
|
|
2036
|
+
playableVariants,
|
|
2017
2037
|
this.manifest_.offlineSessionIds);
|
|
2018
2038
|
|
|
2019
2039
|
await this.drmEngine_.attach(has.mediaElement);
|
|
@@ -2192,6 +2212,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2192
2212
|
await Promise.all(createSegmentIndexPromises);
|
|
2193
2213
|
}
|
|
2194
2214
|
|
|
2215
|
+
if (this.parser_ && this.parser_.onInitialVariantChosen) {
|
|
2216
|
+
this.parser_.onInitialVariantChosen(toLazyLoad);
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2195
2219
|
shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
|
|
2196
2220
|
this.config_.playRangeStart,
|
|
2197
2221
|
this.config_.playRangeEnd);
|
|
@@ -3705,6 +3729,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3705
3729
|
shaka.log.alwaysWarn('A trick play rate of 0 is unsupported!');
|
|
3706
3730
|
return;
|
|
3707
3731
|
}
|
|
3732
|
+
this.trickPlayEventManager_.removeAll();
|
|
3708
3733
|
|
|
3709
3734
|
if (this.video_.paused) {
|
|
3710
3735
|
// Our fast forward is implemented with playbackRate and needs the video
|
|
@@ -3718,6 +3743,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3718
3743
|
this.abrManager_.playbackRateChanged(rate);
|
|
3719
3744
|
this.streamingEngine_.setTrickPlay(Math.abs(rate) > 1);
|
|
3720
3745
|
}
|
|
3746
|
+
if (this.isLive()) {
|
|
3747
|
+
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
3748
|
+
const currentTime = this.video_.currentTime;
|
|
3749
|
+
const seekRange = this.seekRange();
|
|
3750
|
+
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
3751
|
+
if (rate > 0) {
|
|
3752
|
+
if (currentTime >= seekRange.end) {
|
|
3753
|
+
this.cancelTrickPlay();
|
|
3754
|
+
}
|
|
3755
|
+
} else {
|
|
3756
|
+
if (currentTime <= (seekRange.start + safeSeekOffset)) {
|
|
3757
|
+
this.cancelTrickPlay();
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3721
3762
|
}
|
|
3722
3763
|
|
|
3723
3764
|
/**
|
|
@@ -3737,6 +3778,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3737
3778
|
this.abrManager_.playbackRateChanged(defaultPlaybackRate);
|
|
3738
3779
|
this.streamingEngine_.setTrickPlay(false);
|
|
3739
3780
|
}
|
|
3781
|
+
this.trickPlayEventManager_.removeAll();
|
|
3740
3782
|
}
|
|
3741
3783
|
|
|
3742
3784
|
/**
|
|
@@ -4635,7 +4677,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4635
4677
|
|
|
4636
4678
|
if (this.isLive()) {
|
|
4637
4679
|
const now = this.getPresentationStartTimeAsDate().valueOf() +
|
|
4638
|
-
|
|
4680
|
+
element.currentTime * 1000;
|
|
4639
4681
|
const latency = (Date.now() - now) / 1000;
|
|
4640
4682
|
this.stats_.setLiveLatency(latency);
|
|
4641
4683
|
}
|
|
@@ -4689,6 +4731,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4689
4731
|
shaka.util.Error.Code.CONTENT_NOT_LOADED);
|
|
4690
4732
|
}
|
|
4691
4733
|
|
|
4734
|
+
if (kind != 'subtitles' && kind != 'captions') {
|
|
4735
|
+
shaka.log.alwaysWarn(
|
|
4736
|
+
'Using a kind value different of `subtitles` or `captions` can ' +
|
|
4737
|
+
'cause unwanted issues.');
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4692
4740
|
if (!mimeType) {
|
|
4693
4741
|
mimeType = await this.getTextMimetype_(uri);
|
|
4694
4742
|
}
|
|
@@ -4707,9 +4755,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4707
4755
|
}
|
|
4708
4756
|
await this.addSrcTrackElement_(uri, language, kind, mimeType, label || '',
|
|
4709
4757
|
adCuePoints);
|
|
4758
|
+
|
|
4759
|
+
const LanguageUtils = shaka.util.LanguageUtils;
|
|
4760
|
+
const languageNormalized = LanguageUtils.normalize(language);
|
|
4761
|
+
|
|
4710
4762
|
const textTracks = this.getTextTracks();
|
|
4711
4763
|
const srcTrack = textTracks.find((t) => {
|
|
4712
|
-
return t.language ==
|
|
4764
|
+
return LanguageUtils.normalize(t.language) == languageNormalized &&
|
|
4713
4765
|
t.label == (label || '') &&
|
|
4714
4766
|
t.kind == kind;
|
|
4715
4767
|
});
|
|
@@ -4871,16 +4923,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4871
4923
|
vttOffset: 0,
|
|
4872
4924
|
};
|
|
4873
4925
|
const data = shaka.util.BufferUtils.toUint8(buffer);
|
|
4874
|
-
const cues = TextParser.parseMedia(data, time);
|
|
4926
|
+
const cues = TextParser.parseMedia(data, time, uri);
|
|
4875
4927
|
|
|
4876
4928
|
const references = [];
|
|
4877
4929
|
for (const cue of cues) {
|
|
4878
|
-
|
|
4879
|
-
|
|
4930
|
+
let uris = null;
|
|
4931
|
+
const getUris = () => {
|
|
4932
|
+
if (uris == null) {
|
|
4933
|
+
uris = shaka.util.ManifestParserUtils.resolveUris(
|
|
4934
|
+
[uri], [cue.payload]);
|
|
4935
|
+
}
|
|
4936
|
+
return uris || [];
|
|
4937
|
+
};
|
|
4880
4938
|
const reference = new shaka.media.SegmentReference(
|
|
4881
4939
|
cue.startTime,
|
|
4882
4940
|
cue.endTime,
|
|
4883
|
-
|
|
4941
|
+
getUris,
|
|
4884
4942
|
/* startByte= */ 0,
|
|
4885
4943
|
/* endByte= */ null,
|
|
4886
4944
|
/* initSegmentReference= */ null,
|
|
@@ -4888,8 +4946,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4888
4946
|
/* appendWindowStart= */ 0,
|
|
4889
4947
|
/* appendWindowEnd= */ Infinity,
|
|
4890
4948
|
);
|
|
4891
|
-
if (
|
|
4892
|
-
const spriteInfo =
|
|
4949
|
+
if (cue.payload.includes('#xywh')) {
|
|
4950
|
+
const spriteInfo = cue.payload.split('#xywh=')[1].split(',');
|
|
4893
4951
|
if (spriteInfo.length === 4) {
|
|
4894
4952
|
reference.setThumbnailSprite({
|
|
4895
4953
|
height: parseInt(spriteInfo[3], 10),
|
|
@@ -5129,7 +5187,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5129
5187
|
vttOffset: 0,
|
|
5130
5188
|
};
|
|
5131
5189
|
const data = shaka.util.BufferUtils.toUint8(buffer);
|
|
5132
|
-
const cues = obj.parseMedia(data, time);
|
|
5190
|
+
const cues = obj.parseMedia(data, time, /* uri= */ null);
|
|
5133
5191
|
return shaka.text.WebVttGenerator.convert(cues, adCuePoints);
|
|
5134
5192
|
}
|
|
5135
5193
|
throw new shaka.util.Error(
|
|
@@ -5248,7 +5306,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5248
5306
|
return new shaka.text.UITextDisplayer(
|
|
5249
5307
|
this.video_, this.videoContainer_);
|
|
5250
5308
|
} else {
|
|
5251
|
-
return new shaka.text.SimpleTextDisplayer(
|
|
5309
|
+
return new shaka.text.SimpleTextDisplayer(
|
|
5310
|
+
this.video_, shaka.Player.TextTrackLabel);
|
|
5252
5311
|
}
|
|
5253
5312
|
};
|
|
5254
5313
|
return config;
|
|
@@ -6263,6 +6322,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6263
6322
|
const restrictedStatuses = shaka.Player.restrictedStatuses_;
|
|
6264
6323
|
let tracksChanged = false;
|
|
6265
6324
|
|
|
6325
|
+
goog.asserts.assert(this.drmEngine_, 'drmEngine should be non-null here.');
|
|
6326
|
+
|
|
6266
6327
|
// Only filter tracks for keys if we have some key statuses to look at.
|
|
6267
6328
|
if (keyIds.length) {
|
|
6268
6329
|
for (const variant of this.manifest_.variants) {
|
|
@@ -6278,8 +6339,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6278
6339
|
|
|
6279
6340
|
for (const keyId of stream.keyIds) {
|
|
6280
6341
|
const keyStatus = keyStatusMap[isGlobalStatus ? '00' : keyId];
|
|
6281
|
-
|
|
6282
|
-
|
|
6342
|
+
if (keyStatus || this.drmEngine_.hasManifestInitData()) {
|
|
6343
|
+
variant.allowedByKeySystem = variant.allowedByKeySystem &&
|
|
6344
|
+
!!keyStatus && !restrictedStatuses.includes(keyStatus);
|
|
6345
|
+
}
|
|
6283
6346
|
}
|
|
6284
6347
|
}
|
|
6285
6348
|
|
|
@@ -6291,6 +6354,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6291
6354
|
} // if (keyIds.size)
|
|
6292
6355
|
|
|
6293
6356
|
if (tracksChanged) {
|
|
6357
|
+
this.onTracksChanged_();
|
|
6294
6358
|
const variantsUpdated = this.updateAbrManagerVariants_();
|
|
6295
6359
|
if (!variantsUpdated) {
|
|
6296
6360
|
return;
|
|
@@ -6302,10 +6366,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6302
6366
|
shaka.log.debug('Choosing new streams after key status changed');
|
|
6303
6367
|
this.chooseVariantAndSwitch_();
|
|
6304
6368
|
}
|
|
6305
|
-
|
|
6306
|
-
if (tracksChanged) {
|
|
6307
|
-
this.onTracksChanged_();
|
|
6308
|
-
}
|
|
6309
6369
|
}
|
|
6310
6370
|
|
|
6311
6371
|
/**
|
|
@@ -6971,7 +7031,8 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6971
7031
|
* @define {string} A version number taken from git at compile time.
|
|
6972
7032
|
* @export
|
|
6973
7033
|
*/
|
|
6974
|
-
|
|
7034
|
+
// eslint-disable-next-line no-useless-concat
|
|
7035
|
+
shaka.Player.version = 'v4.3.14' + '-uncompiled'; // x-release-please-version
|
|
6975
7036
|
|
|
6976
7037
|
// Initialize the deprecation system using the version string we just set
|
|
6977
7038
|
// on the player.
|
|
@@ -141,8 +141,15 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
if (audioConfig) {
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
let isSupported;
|
|
145
|
+
if (shaka.util.Platform.isChromecast() &&
|
|
146
|
+
audioConfig.spatialRendering) {
|
|
147
|
+
const extendedType =
|
|
148
|
+
audioConfig.contentType + '; spatialRendering=true';
|
|
149
|
+
isSupported = Capabilities.isTypeSupported(extendedType);
|
|
150
|
+
} else {
|
|
151
|
+
isSupported = Capabilities.isTypeSupported(audioConfig.contentType);
|
|
152
|
+
}
|
|
146
153
|
if (!isSupported) {
|
|
147
154
|
return res;
|
|
148
155
|
}
|
|
@@ -184,10 +191,23 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
184
191
|
const videoCapabilities = [];
|
|
185
192
|
|
|
186
193
|
if (mediaCapkeySystemConfig.audio) {
|
|
187
|
-
|
|
194
|
+
let capability = {
|
|
188
195
|
robustness: mediaCapkeySystemConfig.audio.robustness || '',
|
|
189
196
|
contentType: mediaDecodingConfig.audio.contentType,
|
|
190
197
|
};
|
|
198
|
+
|
|
199
|
+
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
200
|
+
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
201
|
+
// See https://github.com/shaka-project/shaka-player/issues/2989 for
|
|
202
|
+
// details.
|
|
203
|
+
if (shaka.util.Platform.isTizen() &&
|
|
204
|
+
mediaDecodingConfig.audio.contentType.includes('codecs="ac-3"')) {
|
|
205
|
+
capability = {
|
|
206
|
+
robustness: capability.robustness,
|
|
207
|
+
contentType: 'audio/mp4; codecs="ec-3"',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
191
211
|
audioCapabilities.push(capability);
|
|
192
212
|
}
|
|
193
213
|
|
|
@@ -67,7 +67,7 @@ shaka.text.Mp4TtmlParser = class {
|
|
|
67
67
|
* @override
|
|
68
68
|
* @export
|
|
69
69
|
*/
|
|
70
|
-
parseMedia(data, time) {
|
|
70
|
+
parseMedia(data, time, uri) {
|
|
71
71
|
const Mp4Parser = shaka.util.Mp4Parser;
|
|
72
72
|
|
|
73
73
|
let sawMDAT = false;
|
|
@@ -78,7 +78,7 @@ shaka.text.Mp4TtmlParser = class {
|
|
|
78
78
|
sawMDAT = true;
|
|
79
79
|
// Join this to any previous payload, in case the mp4 has multiple
|
|
80
80
|
// mdats.
|
|
81
|
-
payload = payload.concat(this.parser_.parseMedia(data, time));
|
|
81
|
+
payload = payload.concat(this.parser_.parseMedia(data, time, uri));
|
|
82
82
|
}));
|
|
83
83
|
parser.parse(data, /* partialOkay= */ false);
|
|
84
84
|
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @fileoverview
|
|
9
|
-
* @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
goog.provide('shaka.text.SimpleTextDisplayer');
|
|
@@ -24,8 +23,11 @@ goog.require('shaka.text.Utils');
|
|
|
24
23
|
* @export
|
|
25
24
|
*/
|
|
26
25
|
shaka.text.SimpleTextDisplayer = class {
|
|
27
|
-
/**
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* @param {HTMLMediaElement} video
|
|
28
|
+
* @param {string} label
|
|
29
|
+
*/
|
|
30
|
+
constructor(video, label) {
|
|
29
31
|
/** @private {TextTrack} */
|
|
30
32
|
this.textTrack_ = null;
|
|
31
33
|
|
|
@@ -39,7 +41,7 @@ shaka.text.SimpleTextDisplayer = class {
|
|
|
39
41
|
// element.
|
|
40
42
|
track.mode = 'disabled';
|
|
41
43
|
|
|
42
|
-
if (track.label ==
|
|
44
|
+
if (track.label == label) {
|
|
43
45
|
this.textTrack_ = track;
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -49,8 +51,7 @@ shaka.text.SimpleTextDisplayer = class {
|
|
|
49
51
|
// kind to 'subtitles' or 'captions' when creating the TextTrack object.
|
|
50
52
|
// The individual text tracks from the manifest will still have their own
|
|
51
53
|
// kinds which can be displayed in the app's UI.
|
|
52
|
-
this.textTrack_ = video.addTextTrack(
|
|
53
|
-
'subtitles', shaka.Player.TextTrackLabel);
|
|
54
|
+
this.textTrack_ = video.addTextTrack('subtitles', label);
|
|
54
55
|
}
|
|
55
56
|
this.textTrack_.mode = 'hidden';
|
|
56
57
|
}
|
|
@@ -47,7 +47,7 @@ shaka.text.SrtTextParser = class {
|
|
|
47
47
|
* @override
|
|
48
48
|
* @export
|
|
49
49
|
*/
|
|
50
|
-
parseMedia(data, time) {
|
|
50
|
+
parseMedia(data, time, uri) {
|
|
51
51
|
const SrtTextParser = shaka.text.SrtTextParser;
|
|
52
52
|
const BufferUtils = shaka.util.BufferUtils;
|
|
53
53
|
const StringUtils = shaka.util.StringUtils;
|
|
@@ -59,7 +59,7 @@ shaka.text.SrtTextParser = class {
|
|
|
59
59
|
|
|
60
60
|
const newData = BufferUtils.toUint8(StringUtils.toUTF8(vvtText));
|
|
61
61
|
|
|
62
|
-
return this.parser_.parseMedia(newData, time);
|
|
62
|
+
return this.parser_.parseMedia(newData, time, uri);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|