shaka-player 3.3.11 → 3.3.13
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/workflows/build-and-test.yaml +11 -6
- package/.github/workflows/compute-incremental-coverage.py +14 -3
- package/.github/workflows/demo-version-index.yaml +9 -1
- package/.github/workflows/nightly-demo.yaml +1 -1
- package/.github/workflows/release-please.yaml +4 -4
- package/.github/workflows/report-incremental-coverage.yaml +1 -1
- package/.github/workflows/selenium-lab-tests.yaml +8 -6
- package/CHANGELOG.md +26 -0
- package/dist/deps.js +5 -5
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.externs.js +3 -1
- package/dist/shaka-player.compiled.debug.js +104 -101
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +3 -1
- package/dist/shaka-player.compiled.js +725 -722
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +2 -1
- package/dist/shaka-player.ui.debug.d.ts +2 -1
- package/dist/shaka-player.ui.debug.externs.js +3 -1
- package/dist/shaka-player.ui.debug.js +122 -119
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +3 -1
- package/dist/shaka-player.ui.js +843 -840
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/welcome.md +2 -2
- package/lib/ads/client_side_ad_manager.js +5 -2
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +16 -6
- package/lib/hls/hls_parser.js +9 -2
- package/lib/media/drm_engine.js +4 -2
- package/lib/media/playhead.js +23 -1
- package/lib/media/streaming_engine.js +3 -12
- package/lib/offline/indexeddb/storage_mechanism.js +24 -0
- package/lib/player.js +24 -11
- package/lib/polyfill/media_capabilities.js +1 -0
- package/lib/text/ttml_text_parser.js +69 -93
- package/lib/util/error.js +7 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/periods.js +9 -9
- package/lib/util/xml_utils.js +45 -6
- package/package.json +1 -1
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +13 -0
- package/test/media/streaming_engine_unit.js +23 -8
- package/test/offline/storage_integration.js +30 -0
- package/test/player_unit.js +87 -0
- package/test/test/util/simple_fakes.js +3 -0
- package/test/util/periods_unit.js +149 -11
- package/test/util/xml_utils_unit.js +64 -0
- package/ui/controls.js +4 -1
- package/ui/settings_menu.js +11 -2
|
@@ -66,8 +66,8 @@ python build/all.py
|
|
|
66
66
|
|
|
67
67
|
Alternatively you can use a docker container:
|
|
68
68
|
```sh
|
|
69
|
-
|
|
70
|
-
docker
|
|
69
|
+
docker build -t shaka-player-build build/docker
|
|
70
|
+
docker run -v $(pwd):/usr/src --user $(id -u):$(id -g) shaka-player-build
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
The output is:
|
|
@@ -15,7 +15,6 @@ goog.require('goog.asserts');
|
|
|
15
15
|
goog.require('shaka.ads.ClientSideAd');
|
|
16
16
|
goog.require('shaka.util.IReleasable');
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
/**
|
|
20
19
|
* A class responsible for client-side ad interactions.
|
|
21
20
|
* @implements {shaka.util.IReleasable}
|
|
@@ -211,7 +210,11 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
211
210
|
// TODO (ismena): Need a better inderstanding of what this does.
|
|
212
211
|
// The docs say it's called to 'start playing the ads,' but I haven't
|
|
213
212
|
// seen the ads actually play until requestAds() is called.
|
|
214
|
-
|
|
213
|
+
// Note: We listen for a play event to avoid autoplay issues that might
|
|
214
|
+
// crash IMA.
|
|
215
|
+
this.video_.addEventListener('play', () => {
|
|
216
|
+
this.imaAdsManager_.start();
|
|
217
|
+
}, {once: true});
|
|
215
218
|
} catch (adError) {
|
|
216
219
|
// If there was a problem with the VAST response,
|
|
217
220
|
// we we won't be getting an ad. Hide ad UI if we showed it already
|
package/lib/cast/cast_proxy.js
CHANGED
|
@@ -694,7 +694,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
694
694
|
// If we are casting, but the first update has not come in yet, use local
|
|
695
695
|
// getters, but not local methods.
|
|
696
696
|
if (this.sender_.isCasting() && !this.sender_.hasRemoteProperties()) {
|
|
697
|
-
if (shaka.cast.CastUtils.PlayerGetterMethods[name]
|
|
697
|
+
if (shaka.cast.CastUtils.PlayerGetterMethods[name] ||
|
|
698
|
+
shaka.cast.CastUtils.LargePlayerGetterMethods[name]) {
|
|
698
699
|
const value = /** @type {Object} */(this.localPlayer_)[name];
|
|
699
700
|
goog.asserts.assert(typeof value == 'function',
|
|
700
701
|
'only methods on Player');
|
|
@@ -518,6 +518,25 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
|
|
518
518
|
update['video']['muted'] = systemVolume.muted;
|
|
519
519
|
}
|
|
520
520
|
|
|
521
|
+
this.sendMessage_({
|
|
522
|
+
'type': 'update',
|
|
523
|
+
'update': update,
|
|
524
|
+
}, this.shakaBus_);
|
|
525
|
+
|
|
526
|
+
// Getters with large outputs each get sent in their own update message.
|
|
527
|
+
for (const name in shaka.cast.CastUtils.LargePlayerGetterMethods) {
|
|
528
|
+
const frequency = shaka.cast.CastUtils.LargePlayerGetterMethods[name];
|
|
529
|
+
if (this.updateNumber_ % frequency == 0) {
|
|
530
|
+
const update = {'player': {}};
|
|
531
|
+
update['player'][name] = /** @type {Object} */ (this.player_)[name]();
|
|
532
|
+
|
|
533
|
+
this.sendMessage_({
|
|
534
|
+
'type': 'update',
|
|
535
|
+
'update': update,
|
|
536
|
+
}, this.shakaBus_);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
521
540
|
// Only start progressing the update number once data is loaded,
|
|
522
541
|
// just in case any of the "rarely changing" properties with less frequent
|
|
523
542
|
// update messages changes significantly during the loading process.
|
|
@@ -525,11 +544,6 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
|
|
525
544
|
this.updateNumber_ += 1;
|
|
526
545
|
}
|
|
527
546
|
|
|
528
|
-
this.sendMessage_({
|
|
529
|
-
'type': 'update',
|
|
530
|
-
'update': update,
|
|
531
|
-
}, this.shakaBus_);
|
|
532
|
-
|
|
533
547
|
this.maybeSendMediaInfoMessage_();
|
|
534
548
|
}
|
|
535
549
|
|
package/lib/cast/cast_sender.js
CHANGED
|
@@ -360,7 +360,8 @@ shaka.cast.CastSender = class {
|
|
|
360
360
|
return (...args) =>
|
|
361
361
|
this.remoteAsyncCall_(targetName, property, ...args);
|
|
362
362
|
}
|
|
363
|
-
if (CastUtils.PlayerGetterMethods[property]
|
|
363
|
+
if (CastUtils.PlayerGetterMethods[property] ||
|
|
364
|
+
CastUtils.LargePlayerGetterMethods[property]) {
|
|
364
365
|
return () => this.propertyGetter_(targetName, property);
|
|
365
366
|
}
|
|
366
367
|
}
|
package/lib/cast/cast_utils.js
CHANGED
|
@@ -307,9 +307,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
|
|
|
307
307
|
'getAudioLanguagesAndRoles': 4,
|
|
308
308
|
'getBufferFullness': 1,
|
|
309
309
|
'getBufferedInfo': 2,
|
|
310
|
-
// NOTE: The 'getSharedConfiguration' property is not proxied as it would
|
|
311
|
-
// not be possible to share a reference.
|
|
312
|
-
'getConfiguration': 4,
|
|
313
310
|
'getExpiration': 2,
|
|
314
311
|
'getKeyStatuses': 2,
|
|
315
312
|
// NOTE: The 'getManifest' property is not proxied, as it is very large.
|
|
@@ -318,9 +315,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
|
|
|
318
315
|
'getPlaybackRate': 2,
|
|
319
316
|
'getTextLanguages': 4,
|
|
320
317
|
'getTextLanguagesAndRoles': 4,
|
|
321
|
-
'getTextTracks': 2,
|
|
322
|
-
'getStats': 5,
|
|
323
|
-
'getVariantTracks': 2,
|
|
324
318
|
'getImageTracks': 2,
|
|
325
319
|
'getThumbnails': 2,
|
|
326
320
|
'isAudioOnly': 10,
|
|
@@ -334,6 +328,22 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
|
|
|
334
328
|
};
|
|
335
329
|
|
|
336
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Player getter methods with data large enough to be sent in their own update
|
|
333
|
+
* messages, to reduce the size of each message. The format of this is
|
|
334
|
+
* identical to PlayerGetterMethods.
|
|
335
|
+
* @const {!Object.<string, number>}
|
|
336
|
+
*/
|
|
337
|
+
shaka.cast.CastUtils.LargePlayerGetterMethods = {
|
|
338
|
+
// NOTE: The 'getSharedConfiguration' property is not proxied as it would
|
|
339
|
+
// not be possible to share a reference.
|
|
340
|
+
'getConfiguration': 4,
|
|
341
|
+
'getStats': 5,
|
|
342
|
+
'getTextTracks': 2,
|
|
343
|
+
'getVariantTracks': 2,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
|
|
337
347
|
/**
|
|
338
348
|
* Player getter methods that are proxied while casting, but only when casting
|
|
339
349
|
* a livestream.
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -639,13 +639,19 @@ shaka.hls.HlsParser = class {
|
|
|
639
639
|
shaka.util.ArrayUtils.remove(allCodecs, textCodecs);
|
|
640
640
|
}
|
|
641
641
|
if (audioGroupId) {
|
|
642
|
-
|
|
642
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
643
643
|
ContentType.AUDIO, allCodecs);
|
|
644
|
+
if (!codecs) {
|
|
645
|
+
codecs = this.config_.hls.defaultAudioCodec;
|
|
646
|
+
}
|
|
644
647
|
this.groupIdToCodecsMap_.set(audioGroupId, codecs);
|
|
645
648
|
}
|
|
646
649
|
if (videoGroupId) {
|
|
647
|
-
|
|
650
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
648
651
|
ContentType.VIDEO, allCodecs);
|
|
652
|
+
if (!codecs) {
|
|
653
|
+
codecs = this.config_.hls.defaultVideoCodec;
|
|
654
|
+
}
|
|
649
655
|
this.groupIdToCodecsMap_.set(videoGroupId, codecs);
|
|
650
656
|
}
|
|
651
657
|
}
|
|
@@ -3028,6 +3034,7 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
3028
3034
|
'm4f': 'application/mp4',
|
|
3029
3035
|
'cmft': 'application/mp4',
|
|
3030
3036
|
'vtt': 'text/vtt',
|
|
3037
|
+
'webvtt': 'txt/vtt',
|
|
3031
3038
|
'ttml': 'application/ttml+xml',
|
|
3032
3039
|
};
|
|
3033
3040
|
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -27,6 +27,7 @@ goog.require('shaka.util.StreamUtils');
|
|
|
27
27
|
goog.require('shaka.util.StringUtils');
|
|
28
28
|
goog.require('shaka.util.Timer');
|
|
29
29
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
30
|
+
goog.require('shaka.util.XmlUtils');
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
/** @implements {shaka.util.IDestroyable} */
|
|
@@ -1439,7 +1440,8 @@ shaka.media.DrmEngine = class {
|
|
|
1439
1440
|
return;
|
|
1440
1441
|
}
|
|
1441
1442
|
shaka.log.debug('Unwrapping PlayReady request.');
|
|
1442
|
-
const dom =
|
|
1443
|
+
const dom = shaka.util.XmlUtils.parseXmlString(xml, 'PlayReadyKeyMessage');
|
|
1444
|
+
goog.asserts.assert(dom, 'Failed to parse PlayReady XML!');
|
|
1443
1445
|
|
|
1444
1446
|
// Set request headers.
|
|
1445
1447
|
const headers = dom.getElementsByTagName('HttpHeader');
|
|
@@ -1523,7 +1525,7 @@ shaka.media.DrmEngine = class {
|
|
|
1523
1525
|
hasExpiredKeys = true;
|
|
1524
1526
|
}
|
|
1525
1527
|
|
|
1526
|
-
const keyIdHex = shaka.util.Uint8ArrayUtils.toHex(keyId);
|
|
1528
|
+
const keyIdHex = shaka.util.Uint8ArrayUtils.toHex(keyId).slice(0, 32);
|
|
1527
1529
|
|
|
1528
1530
|
this.keyStatusByKeyId_.set(keyIdHex, status);
|
|
1529
1531
|
});
|
package/lib/media/playhead.js
CHANGED
|
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
|
|
|
33
33
|
* @interface
|
|
34
34
|
*/
|
|
35
35
|
shaka.media.Playhead = class {
|
|
36
|
+
/**
|
|
37
|
+
* Called when the Player is ready to begin playback. Anything that depends
|
|
38
|
+
* on setStartTime() should be done here, not in the constructor.
|
|
39
|
+
*
|
|
40
|
+
* @see https://github.com/shaka-project/shaka-player/issues/4244
|
|
41
|
+
*/
|
|
42
|
+
ready() {}
|
|
43
|
+
|
|
36
44
|
/**
|
|
37
45
|
* Set the start time. If the content has already started playback, this will
|
|
38
46
|
* be ignored.
|
|
@@ -76,6 +84,14 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
76
84
|
|
|
77
85
|
/** @private {shaka.util.EventManager} */
|
|
78
86
|
this.eventManager_ = new shaka.util.EventManager();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @override */
|
|
90
|
+
ready() {
|
|
91
|
+
goog.asserts.assert(
|
|
92
|
+
this.mediaElement_ != null,
|
|
93
|
+
'Playhead should not be released before calling ready()',
|
|
94
|
+
);
|
|
79
95
|
|
|
80
96
|
// We listen for the loaded-data-event so that we know when we can
|
|
81
97
|
// interact with |currentTime|.
|
|
@@ -88,6 +104,7 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
88
104
|
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
|
|
89
105
|
this.started_ = true;
|
|
90
106
|
});
|
|
107
|
+
|
|
91
108
|
const currentTime = this.mediaElement_.currentTime;
|
|
92
109
|
// Using the currentTime allows using a negative number in Live HLS
|
|
93
110
|
const newTime = Math.max(0, currentTime + this.startTime_);
|
|
@@ -211,7 +228,12 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
211
228
|
/** @type {shaka.util.Timer} */
|
|
212
229
|
this.checkWindowTimer_ = new shaka.util.Timer(() => {
|
|
213
230
|
this.onPollWindow_();
|
|
214
|
-
})
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** @override */
|
|
235
|
+
ready() {
|
|
236
|
+
this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
|
|
215
237
|
}
|
|
216
238
|
|
|
217
239
|
/** @override */
|
|
@@ -1366,16 +1366,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1366
1366
|
this.mediaStates_.delete(ContentType.TEXT);
|
|
1367
1367
|
} else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
|
|
1368
1368
|
this.handleQuotaExceeded_(mediaState, error);
|
|
1369
|
-
} else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
|
|
1370
|
-
error.data && error.data[1] == 404) {
|
|
1371
|
-
// The segment could not be found, does not exist, or is not available.
|
|
1372
|
-
// In any case just try again.
|
|
1373
|
-
// The current segment is not available. Schedule another update to
|
|
1374
|
-
// fetch the segment again.
|
|
1375
|
-
shaka.log.v2(logPrefix, 'segment not available.');
|
|
1376
|
-
mediaState.performingUpdate = false;
|
|
1377
|
-
mediaState.updateTimer = null;
|
|
1378
|
-
this.scheduleUpdate_(mediaState, 1);
|
|
1379
1369
|
} else {
|
|
1380
1370
|
shaka.log.error(logPrefix, 'failed fetch and append: code=' +
|
|
1381
1371
|
error.code);
|
|
@@ -1390,9 +1380,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1390
1380
|
|
|
1391
1381
|
/**
|
|
1392
1382
|
* Clear per-stream error states and retry any failed streams.
|
|
1383
|
+
* @param {number} delaySeconds
|
|
1393
1384
|
* @return {boolean} False if unable to retry.
|
|
1394
1385
|
*/
|
|
1395
|
-
retry() {
|
|
1386
|
+
retry(delaySeconds) {
|
|
1396
1387
|
if (this.destroyer_.destroyed()) {
|
|
1397
1388
|
shaka.log.error('Unable to retry after StreamingEngine is destroyed!');
|
|
1398
1389
|
return false;
|
|
@@ -1409,7 +1400,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1409
1400
|
if (mediaState.hasError) {
|
|
1410
1401
|
shaka.log.info(logPrefix, 'Retrying after failure...');
|
|
1411
1402
|
mediaState.hasError = false;
|
|
1412
|
-
this.scheduleUpdate_(mediaState,
|
|
1403
|
+
this.scheduleUpdate_(mediaState, delaySeconds);
|
|
1413
1404
|
}
|
|
1414
1405
|
}
|
|
1415
1406
|
|
|
@@ -16,6 +16,7 @@ goog.require('shaka.offline.indexeddb.V5StorageCell');
|
|
|
16
16
|
goog.require('shaka.util.Error');
|
|
17
17
|
goog.require('shaka.util.PublicPromise');
|
|
18
18
|
goog.require('shaka.util.Platform');
|
|
19
|
+
goog.require('shaka.util.Timer');
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -55,8 +56,25 @@ shaka.offline.indexeddb.StorageMechanism = class {
|
|
|
55
56
|
const version = shaka.offline.indexeddb.StorageMechanism.VERSION;
|
|
56
57
|
|
|
57
58
|
const p = new shaka.util.PublicPromise();
|
|
59
|
+
|
|
60
|
+
// Add a timeout mechanism, for the (rare?) case where no callbacks are
|
|
61
|
+
// called at all, so that this method doesn't hang forever.
|
|
62
|
+
let timedOut = false;
|
|
63
|
+
const timeOutTimer = new shaka.util.Timer(() => {
|
|
64
|
+
timedOut = true;
|
|
65
|
+
p.reject(new shaka.util.Error(
|
|
66
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
67
|
+
shaka.util.Error.Category.STORAGE,
|
|
68
|
+
shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT));
|
|
69
|
+
});
|
|
70
|
+
timeOutTimer.tickAfter(2);
|
|
71
|
+
|
|
58
72
|
const open = window.indexedDB.open(name, version);
|
|
59
73
|
open.onsuccess = (event) => {
|
|
74
|
+
if (timedOut) {
|
|
75
|
+
// Too late, we have already given up on opening the storage mechanism.
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
60
78
|
const db = open.result;
|
|
61
79
|
this.db_ = db;
|
|
62
80
|
this.v1_ = shaka.offline.indexeddb.StorageMechanism.createV1_(db);
|
|
@@ -68,6 +86,7 @@ shaka.offline.indexeddb.StorageMechanism = class {
|
|
|
68
86
|
this.v5_ = shaka.offline.indexeddb.StorageMechanism.createV5_(db);
|
|
69
87
|
this.sessions_ =
|
|
70
88
|
shaka.offline.indexeddb.StorageMechanism.createEmeSessionCell_(db);
|
|
89
|
+
timeOutTimer.stop();
|
|
71
90
|
p.resolve();
|
|
72
91
|
};
|
|
73
92
|
open.onupgradeneeded = (event) => {
|
|
@@ -75,11 +94,16 @@ shaka.offline.indexeddb.StorageMechanism = class {
|
|
|
75
94
|
this.createStores_(open.result);
|
|
76
95
|
};
|
|
77
96
|
open.onerror = (event) => {
|
|
97
|
+
if (timedOut) {
|
|
98
|
+
// Too late, we have already given up on opening the storage mechanism.
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
78
101
|
p.reject(new shaka.util.Error(
|
|
79
102
|
shaka.util.Error.Severity.CRITICAL,
|
|
80
103
|
shaka.util.Error.Category.STORAGE,
|
|
81
104
|
shaka.util.Error.Code.INDEXED_DB_ERROR,
|
|
82
105
|
open.error));
|
|
106
|
+
timeOutTimer.stop();
|
|
83
107
|
|
|
84
108
|
// Firefox will raise an error on the main thread unless we stop it here.
|
|
85
109
|
event.preventDefault();
|
package/lib/player.js
CHANGED
|
@@ -2040,6 +2040,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2040
2040
|
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
2041
2041
|
}
|
|
2042
2042
|
|
|
2043
|
+
this.playhead_.ready();
|
|
2044
|
+
|
|
2043
2045
|
// Decide if text should be shown automatically.
|
|
2044
2046
|
// similar to video/audio track, we would skip switch initial text track
|
|
2045
2047
|
// if user already pick text track (via selectTextTrack api)
|
|
@@ -2374,6 +2376,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2374
2376
|
HTMLMediaElement.HAVE_METADATA,
|
|
2375
2377
|
this.loadEventManager_,
|
|
2376
2378
|
() => {
|
|
2379
|
+
this.playhead_.ready();
|
|
2377
2380
|
fullyLoaded.resolve();
|
|
2378
2381
|
});
|
|
2379
2382
|
|
|
@@ -4958,12 +4961,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4958
4961
|
* If the player has loaded content, and streaming seen an error, but the
|
|
4959
4962
|
* could not resume streaming, this will return <code>false</code>.
|
|
4960
4963
|
*
|
|
4964
|
+
* @param {number=} retryDelaySeconds
|
|
4961
4965
|
* @return {boolean}
|
|
4962
4966
|
* @export
|
|
4963
4967
|
*/
|
|
4964
|
-
retryStreaming() {
|
|
4968
|
+
retryStreaming(retryDelaySeconds = 0.1) {
|
|
4965
4969
|
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
|
|
4966
|
-
this.streamingEngine_.retry() :
|
|
4970
|
+
this.streamingEngine_.retry(retryDelaySeconds) :
|
|
4967
4971
|
false;
|
|
4968
4972
|
}
|
|
4969
4973
|
|
|
@@ -5059,17 +5063,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5059
5063
|
* @private
|
|
5060
5064
|
*/
|
|
5061
5065
|
defaultStreamingFailureCallback_(error) {
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5066
|
+
// For live streams, we retry streaming automatically for certain errors.
|
|
5067
|
+
// For VOD streams, all streaming failures are fatal.
|
|
5068
|
+
if (!this.isLive()) {
|
|
5069
|
+
return;
|
|
5070
|
+
}
|
|
5067
5071
|
|
|
5068
|
-
|
|
5069
|
-
|
|
5072
|
+
let retryDelaySeconds = null;
|
|
5073
|
+
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
|
|
5074
|
+
error.code == shaka.util.Error.Code.HTTP_ERROR) {
|
|
5075
|
+
// These errors can be near-instant, so delay a bit before retrying.
|
|
5076
|
+
retryDelaySeconds = 1;
|
|
5077
|
+
} else if (error.code == shaka.util.Error.Code.TIMEOUT) {
|
|
5078
|
+
// We already waited for a timeout, so retry quickly.
|
|
5079
|
+
retryDelaySeconds = 0.1;
|
|
5080
|
+
}
|
|
5070
5081
|
|
|
5082
|
+
if (retryDelaySeconds != null) {
|
|
5083
|
+
error.severity = shaka.util.Error.Severity.RECOVERABLE;
|
|
5071
5084
|
shaka.log.warning('Live streaming error. Retrying automatically...');
|
|
5072
|
-
this.retryStreaming();
|
|
5085
|
+
this.retryStreaming(retryDelaySeconds);
|
|
5073
5086
|
}
|
|
5074
5087
|
}
|
|
5075
5088
|
|
|
@@ -6635,7 +6648,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6635
6648
|
* @define {string} A version number taken from git at compile time.
|
|
6636
6649
|
* @export
|
|
6637
6650
|
*/
|
|
6638
|
-
shaka.Player.version = 'v3.3.
|
|
6651
|
+
shaka.Player.version = 'v3.3.13-uncompiled';
|
|
6639
6652
|
|
|
6640
6653
|
// Initialize the deprecation system using the version string we just set
|
|
6641
6654
|
// on the player.
|
|
@@ -43,6 +43,7 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
43
43
|
!shaka.util.Platform.isPS5() &&
|
|
44
44
|
!shaka.util.Platform.isWebOS() &&
|
|
45
45
|
!shaka.util.Platform.isTizen() &&
|
|
46
|
+
!shaka.util.Platform.isEOS() &&
|
|
46
47
|
navigator.mediaCapabilities) {
|
|
47
48
|
shaka.log.info(
|
|
48
49
|
'MediaCapabilities: Native mediaCapabilities support found.');
|
|
@@ -41,8 +41,6 @@ shaka.text.TtmlTextParser = class {
|
|
|
41
41
|
const ttsNs = TtmlTextParser.styleNs_;
|
|
42
42
|
const str = shaka.util.StringUtils.fromUTF8(data);
|
|
43
43
|
const cues = [];
|
|
44
|
-
const parser = new DOMParser();
|
|
45
|
-
let xml = null;
|
|
46
44
|
|
|
47
45
|
// dont try to parse empty string as
|
|
48
46
|
// DOMParser will not throw error but return an errored xml
|
|
@@ -50,9 +48,8 @@ shaka.text.TtmlTextParser = class {
|
|
|
50
48
|
return cues;
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} catch (exception) {
|
|
51
|
+
const tt = XmlUtils.parseXmlString(str, 'tt');
|
|
52
|
+
if (!tt) {
|
|
56
53
|
throw new shaka.util.Error(
|
|
57
54
|
shaka.util.Error.Severity.CRITICAL,
|
|
58
55
|
shaka.util.Error.Category.TEXT,
|
|
@@ -60,110 +57,89 @@ shaka.text.TtmlTextParser = class {
|
|
|
60
57
|
'Failed to parse TTML.');
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
68
|
-
shaka.util.Error.Category.TEXT,
|
|
69
|
-
shaka.util.Error.Code.INVALID_XML,
|
|
70
|
-
parserError.textContent);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const tt = xml.getElementsByTagName('tt')[0];
|
|
74
|
-
// TTML should always have tt element.
|
|
75
|
-
if (!tt) {
|
|
76
|
-
throw new shaka.util.Error(
|
|
77
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
78
|
-
shaka.util.Error.Category.TEXT,
|
|
79
|
-
shaka.util.Error.Code.INVALID_XML,
|
|
80
|
-
'TTML does not contain <tt> tag.');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const body = tt.getElementsByTagName('body')[0];
|
|
84
|
-
if (!body) {
|
|
85
|
-
return [];
|
|
86
|
-
}
|
|
60
|
+
const body = tt.getElementsByTagName('body')[0];
|
|
61
|
+
if (!body) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
87
64
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
// Get the framerate, subFrameRate and frameRateMultiplier if applicable.
|
|
66
|
+
const frameRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRate');
|
|
67
|
+
const subFrameRate = XmlUtils.getAttributeNSList(
|
|
68
|
+
tt, ttpNs, 'subFrameRate');
|
|
69
|
+
const frameRateMultiplier =
|
|
70
|
+
XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
|
|
71
|
+
const tickRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'tickRate');
|
|
95
72
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
73
|
+
const cellResolution = XmlUtils.getAttributeNSList(
|
|
74
|
+
tt, ttpNs, 'cellResolution');
|
|
75
|
+
const spaceStyle = tt.getAttribute('xml:space') || 'default';
|
|
76
|
+
const extent = XmlUtils.getAttributeNSList(tt, ttsNs, 'extent');
|
|
100
77
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
78
|
+
if (spaceStyle != 'default' && spaceStyle != 'preserve') {
|
|
79
|
+
throw new shaka.util.Error(
|
|
80
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
81
|
+
shaka.util.Error.Category.TEXT,
|
|
82
|
+
shaka.util.Error.Code.INVALID_XML,
|
|
83
|
+
'Invalid xml:space value: ' + spaceStyle);
|
|
84
|
+
}
|
|
85
|
+
const whitespaceTrim = spaceStyle == 'default';
|
|
109
86
|
|
|
110
|
-
|
|
111
|
-
|
|
87
|
+
const rateInfo = new TtmlTextParser.RateInfo_(
|
|
88
|
+
frameRate, subFrameRate, frameRateMultiplier, tickRate);
|
|
112
89
|
|
|
113
|
-
|
|
114
|
-
|
|
90
|
+
const cellResolutionInfo =
|
|
91
|
+
TtmlTextParser.getCellResolution_(cellResolution);
|
|
115
92
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
93
|
+
const metadata = tt.getElementsByTagName('metadata')[0];
|
|
94
|
+
const metadataElements = metadata ? XmlUtils.getChildren(metadata) : [];
|
|
95
|
+
const styles = Array.from(tt.getElementsByTagName('style'));
|
|
96
|
+
const regionElements = Array.from(tt.getElementsByTagName('region'));
|
|
120
97
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
98
|
+
const cueRegions = [];
|
|
99
|
+
for (const region of regionElements) {
|
|
100
|
+
const cueRegion =
|
|
101
|
+
TtmlTextParser.parseCueRegion_(region, styles, extent);
|
|
102
|
+
if (cueRegion) {
|
|
103
|
+
cueRegions.push(cueRegion);
|
|
128
104
|
}
|
|
105
|
+
}
|
|
129
106
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
107
|
+
// A <body> element should only contain <div> elements, not <p> or <span>
|
|
108
|
+
// elements. We used to allow this, but it is non-compliant, and the
|
|
109
|
+
// loose nature of our previous parser made it difficult to implement TTML
|
|
110
|
+
// nesting more fully.
|
|
111
|
+
if (XmlUtils.findChildren(body, 'p').length) {
|
|
112
|
+
throw new shaka.util.Error(
|
|
113
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
114
|
+
shaka.util.Error.Category.TEXT,
|
|
115
|
+
shaka.util.Error.Code.INVALID_TEXT_CUE,
|
|
116
|
+
'<p> can only be inside <div> in TTML');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const div of XmlUtils.findChildren(body, 'div')) {
|
|
120
|
+
// A <div> element should only contain <p>, not <span>.
|
|
121
|
+
if (XmlUtils.findChildren(div, 'span').length) {
|
|
135
122
|
throw new shaka.util.Error(
|
|
136
123
|
shaka.util.Error.Severity.CRITICAL,
|
|
137
124
|
shaka.util.Error.Category.TEXT,
|
|
138
125
|
shaka.util.Error.Code.INVALID_TEXT_CUE,
|
|
139
|
-
'<
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
for (const div of XmlUtils.findChildren(body, 'div')) {
|
|
143
|
-
// A <div> element should only contain <p>, not <span>.
|
|
144
|
-
if (XmlUtils.findChildren(div, 'span').length) {
|
|
145
|
-
throw new shaka.util.Error(
|
|
146
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
147
|
-
shaka.util.Error.Category.TEXT,
|
|
148
|
-
shaka.util.Error.Code.INVALID_TEXT_CUE,
|
|
149
|
-
'<span> can only be inside <p> in TTML');
|
|
150
|
-
}
|
|
126
|
+
'<span> can only be inside <p> in TTML');
|
|
151
127
|
}
|
|
128
|
+
}
|
|
152
129
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
cues.push(cue);
|
|
130
|
+
const cue = TtmlTextParser.parseCue_(
|
|
131
|
+
body, time.periodStart, rateInfo, metadataElements, styles,
|
|
132
|
+
regionElements, cueRegions, whitespaceTrim,
|
|
133
|
+
cellResolutionInfo, /* parentCueElement= */ null,
|
|
134
|
+
/* isContent= */ false);
|
|
135
|
+
if (cue) {
|
|
136
|
+
// According to the TTML spec, backgrounds default to transparent.
|
|
137
|
+
// So default the background of the top-level element to transparent.
|
|
138
|
+
// Nested elements may override that background color already.
|
|
139
|
+
if (!cue.backgroundColor) {
|
|
140
|
+
cue.backgroundColor = 'transparent';
|
|
166
141
|
}
|
|
142
|
+
cues.push(cue);
|
|
167
143
|
}
|
|
168
144
|
|
|
169
145
|
return cues;
|
package/lib/util/error.js
CHANGED
|
@@ -999,6 +999,13 @@ shaka.util.Error.Code = {
|
|
|
999
999
|
*/
|
|
1000
1000
|
'MODIFY_OPERATION_NOT_SUPPORTED': 9016,
|
|
1001
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* When attempting to open an indexedDB instance, nothing happened for long
|
|
1004
|
+
* enough for us to time out. This keeps the storage mechanism from hanging
|
|
1005
|
+
* indefinitely, if neither the success nor error callbacks are called.
|
|
1006
|
+
*/
|
|
1007
|
+
'INDEXED_DB_INIT_TIMED_OUT': 9017,
|
|
1008
|
+
|
|
1002
1009
|
/**
|
|
1003
1010
|
* CS IMA SDK, required for ad insertion, has not been included on the page.
|
|
1004
1011
|
*/
|
|
@@ -168,6 +168,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
168
168
|
/^hvc/,
|
|
169
169
|
/^vp0?[89]/,
|
|
170
170
|
/^av01/,
|
|
171
|
+
/^dvhe/,
|
|
171
172
|
];
|
|
172
173
|
|
|
173
174
|
|
|
@@ -183,6 +184,9 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
|
|
|
183
184
|
/^flac$/,
|
|
184
185
|
/^mp4a/,
|
|
185
186
|
/^[ae]c-3$/,
|
|
187
|
+
/^ac-4$/,
|
|
188
|
+
/^dts[cx]$/, // DTS Surround Sound
|
|
189
|
+
/^[du]dts$/, // DTS Surround Sound
|
|
186
190
|
];
|
|
187
191
|
|
|
188
192
|
|