shaka-player 3.2.19 → 3.2.21
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 +4 -4
- package/.github/workflows/demo-version-index.yaml +6 -0
- package/.github/workflows/selenium-lab-tests.yaml +78 -17
- package/.github/workflows/update-screenshots.yaml +142 -0
- package/AUTHORS +2 -1
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTORS +4 -1
- package/demo/common/asset.js +15 -0
- package/demo/common/assets.js +4 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +19 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +14 -0
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +150 -148
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +150 -148
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +1 -1
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +19 -19
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +19 -19
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +13 -2
- package/dist/shaka-player.compiled.debug.d.ts +13 -2
- package/dist/shaka-player.compiled.debug.js +178 -176
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +294 -292
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +13 -2
- package/dist/shaka-player.ui.debug.d.ts +13 -2
- package/dist/shaka-player.ui.debug.js +117 -115
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +318 -316
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/errors.md +94 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +2 -0
- package/docs/tutorials/widevine-service-certs.md +45 -0
- package/externs/ima.js +9 -0
- package/externs/shaka/net.js +1 -0
- package/externs/shaka/player.js +13 -7
- package/lib/ads/client_side_ad_manager.js +8 -5
- package/lib/hls/hls_parser.js +2 -0
- package/lib/media/media_source_engine.js +9 -4
- package/lib/media/streaming_engine.js +8 -3
- package/lib/media/transmuxer.js +13 -7
- package/lib/media/video_wrapper.js +3 -3
- package/lib/net/http_fetch_plugin.js +10 -0
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/player.js +19 -2
- package/lib/polyfill/media_capabilities.js +15 -8
- package/lib/polyfill/mediasource.js +7 -2
- package/lib/text/mp4_vtt_parser.js +4 -0
- package/lib/text/text_engine.js +1 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +4 -5
- package/lib/util/manifest_parser_utils.js +1 -2
- package/lib/util/mime_utils.js +12 -1
- package/lib/util/stream_utils.js +28 -16
- package/maintained-branches.md +3 -4
- package/package.json +1 -1
- package/test/media/content_workarounds_unit.js +43 -0
- package/test/media/drm_engine_integration.js +4 -17
- package/test/media/streaming_engine_unit.js +6 -1
- package/test/net/networking_engine_unit.js +6 -1
- package/test/player_integration.js +39 -0
- package/test/player_unit.js +54 -0
- package/test/polyfill/media_capabilities_unit.js +20 -23
- package/test/test/assets/text-clip-alt.vtt +63 -0
- package/test/test/util/fake_drm_engine.js +3 -0
- package/test/test/util/simple_fakes.js +3 -0
- package/test/test/util/test_scheme.js +9 -14
- package/test/text/mp4_vtt_parser_unit.js +8 -0
- package/test/text/vtt_text_parser_unit.js +26 -2
- package/test/text/web_vtt_layout_integration.js +0 -3
- package/test/util/mime_utils_unit.js +10 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
3
|
+
The basics for handling native Error and {@link shaka.util.Error} thrown by Shaka.
|
|
4
|
+
|
|
5
|
+
## Listening To and Handling Errors
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
const handleError = (error) => {
|
|
9
|
+
if (error instanceof Error) {
|
|
10
|
+
// shaka crashed with an unhandled native error
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (error.severity === shaka.util.Error.Severity.CRITICAL) {
|
|
14
|
+
// handle fatal error, playback can not continue
|
|
15
|
+
} else {
|
|
16
|
+
// handle non-fatal error, playback can continue
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const player = new shaka.Player(video);
|
|
21
|
+
|
|
22
|
+
// handle errors that occur after load
|
|
23
|
+
player.addEventListener('error', handleError);
|
|
24
|
+
|
|
25
|
+
// there are two options for catching errors that occur during load
|
|
26
|
+
|
|
27
|
+
// it's possible to listen directly on the promise
|
|
28
|
+
player.load(url).catch(handleError);
|
|
29
|
+
|
|
30
|
+
// or to use async/await with a try/catch
|
|
31
|
+
try {
|
|
32
|
+
await player.load(url);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
handleError(e);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Custom Handling of Streaming Errors
|
|
39
|
+
|
|
40
|
+
The `streaming.failureCallback` property of {@link shaka.extern.PlayerConfiguration} can be used to add custom handling or error conversion of errors that occur during streaming.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
player.configure('streaming.failureCallback', (error) => {
|
|
44
|
+
if (error.severity === shaka.util.Error.Severity.CRITICAL) {
|
|
45
|
+
// custom handling of critical error
|
|
46
|
+
// e.g. player.retryStreaming();
|
|
47
|
+
} else {
|
|
48
|
+
// custom handling of recoverable error
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Custom Handling of Retries
|
|
54
|
+
|
|
55
|
+
When configuring retry parameters in Shaka there may be known error codes that should not be retried, or the need to break out of an infinite retry loop in a live context. Retries for failed network requests are not reported as `RECOVERABLE` errors.
|
|
56
|
+
|
|
57
|
+
For example, if a VOD manifest is missing, unlike when a LIVE manifest is missing, it can be expected to not show up and there is no need to retry.
|
|
58
|
+
|
|
59
|
+
This is how to convert a retry into a critical error, see {@link shaka.net.NetworkingEngine.RetryEvent} and {@link shaka.util.Error} to decipher how the event can be parsed:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const nwEngine = player.getNetworkingEngine();
|
|
63
|
+
|
|
64
|
+
const vodManifestNotFoundHandler = (event /* shaka.net.NetworkingEngine.RetryEvent */) => {
|
|
65
|
+
const code = event.error.code;
|
|
66
|
+
const data = event.error.data;
|
|
67
|
+
|
|
68
|
+
if (code === shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
69
|
+
if (
|
|
70
|
+
// each type of error has its own data structure (or none at all), tread with care
|
|
71
|
+
Array.isArray(data) &&
|
|
72
|
+
data[1] === 404 &&
|
|
73
|
+
data[4] === shaka.net.NetworkingEngine.RequestType.MANIFEST
|
|
74
|
+
) {
|
|
75
|
+
// Throwing inside a retry callback will immediately stop retries
|
|
76
|
+
throw error;
|
|
77
|
+
|
|
78
|
+
// A proprietary error code can also be thrown
|
|
79
|
+
// throw new shaka.util.Error(
|
|
80
|
+
// shaka.util.Error.Severity.CRITICAL,
|
|
81
|
+
// shaka.util.Error.Category.NETWORK,
|
|
82
|
+
// 'RECOGNIZABLE_ERROR_MESSAGE'
|
|
83
|
+
// );
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
nwEngine.addEventListener('retry', vodManifestNotFoundHandler);
|
|
89
|
+
|
|
90
|
+
player.addEventListener('load'() => {
|
|
91
|
+
nwEngine.removeEventListener('retry', vodManifestNotFoundHandler);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
```
|
|
@@ -25,14 +25,14 @@ is used by the browser to generate the license request. If you don't use the
|
|
|
25
25
|
default content ID derivation, you need to specify a custom init data transform:
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
|
-
player.configure('drm.initDataTransform', (initData, initDataType) => {
|
|
28
|
+
player.configure('drm.initDataTransform', (initData, initDataType, drmInfo) => {
|
|
29
29
|
if (initDataType != 'skd')
|
|
30
30
|
return initData;
|
|
31
31
|
|
|
32
32
|
// 'initData' is a buffer containing an 'skd://' URL as a UTF-8 string.
|
|
33
33
|
const skdUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
|
|
34
34
|
const contentId = getMyContentId(skdUri);
|
|
35
|
-
const cert =
|
|
35
|
+
const cert = drmInfo.serverCertificate;
|
|
36
36
|
return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
|
|
37
37
|
});
|
|
38
38
|
```
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
{ "license-wrapping": { "title": "License Wrapping" } },
|
|
10
10
|
{ "ui": { "title": "UI Library" } },
|
|
11
11
|
{ "ui-customization": { "title": "Configuring the UI" } },
|
|
12
|
+
{ "errors": { "title": "Error Handling" } },
|
|
12
13
|
{ "a11y": { "title": "Creating accessible buttons" } },
|
|
13
14
|
{ "ad_monetization": { "title": "Serving ads with our IMA SDK integration" } },
|
|
14
15
|
{ "plugins": { "title": "Plugins and Customizing the Build" } },
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
{ "architecture": { "title": "Architecture Diagrams" } },
|
|
17
18
|
{ "service-worker": { "title": "Service Worker Caching" } },
|
|
18
19
|
{ "offline": { "title": "Offline Storage and Playback" } },
|
|
20
|
+
{ "widevine-service-certs": { "title": "Widevine Service Certificates" } },
|
|
19
21
|
{ "fairplay": { "title": "FairPlay support" } },
|
|
20
22
|
{ "application-level-redirects": { "title": "Application-Level Redirects" } },
|
|
21
23
|
{ "selenium-grid-config": { "title": "Selenium Grid Config" } },
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Widevine Service Certificates
|
|
2
|
+
|
|
3
|
+
With Widevine, you can eliminate one round-trip to your license server or proxy
|
|
4
|
+
per session by preloading a Widevine service certificate. You can either provide
|
|
5
|
+
it directly or set a URI and let Shaka fetch it for you. Do this before calling
|
|
6
|
+
`player.load()`. The values will persist across multiple calls to `load()` on
|
|
7
|
+
the same `shaka.Player` instance.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// This is an example of loading the certificate from your site at runtime.
|
|
11
|
+
// You could also choose to bundle it into your JavaScript as a Uint8Array.
|
|
12
|
+
const req = await fetch('https://example.com/service.cert');
|
|
13
|
+
const cert = new Uint8Array(await req.arrayBuffer());
|
|
14
|
+
|
|
15
|
+
// This is the short form for configuration of a certificate:
|
|
16
|
+
player.configure('drm.advanced.com\\.widevine\\.alpha.serverCertificate',
|
|
17
|
+
cert);
|
|
18
|
+
|
|
19
|
+
// This is the long form:
|
|
20
|
+
player.configure({
|
|
21
|
+
drm: {
|
|
22
|
+
advanced: {
|
|
23
|
+
'com.widevine.alpha': {
|
|
24
|
+
'serverCertificate': cert,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// This is the short form for configuration of a certificate URI:
|
|
32
|
+
player.configure('drm.advanced.com\\.widevine\\.alpha.serverCertificateUri',
|
|
33
|
+
'https://example.com/service.cert');
|
|
34
|
+
|
|
35
|
+
// This is the long form:
|
|
36
|
+
player.configure({
|
|
37
|
+
drm: {
|
|
38
|
+
advanced: {
|
|
39
|
+
'com.widevine.alpha': {
|
|
40
|
+
'serverCertificateUri': 'https://example.com/service.cert',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
package/externs/ima.js
CHANGED
|
@@ -498,6 +498,15 @@ google.ima.dai.api.StreamRequest.prototype.streamActivityMonitorId;
|
|
|
498
498
|
google.ima.dai.api.StreamRequest.prototype.format;
|
|
499
499
|
|
|
500
500
|
|
|
501
|
+
/**
|
|
502
|
+
* @enum {string}
|
|
503
|
+
*/
|
|
504
|
+
google.ima.dai.api.StreamRequest.StreamFormat = {
|
|
505
|
+
DASH: 'dash',
|
|
506
|
+
HLS: 'hls',
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
|
|
501
510
|
/** @const */
|
|
502
511
|
google.ima.dai.api.VODStreamRequest =
|
|
503
512
|
class extends google.ima.dai.api.StreamRequest {};
|
package/externs/shaka/net.js
CHANGED
package/externs/shaka/player.js
CHANGED
|
@@ -571,9 +571,7 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
571
571
|
* clearKeys: !Object.<string, string>,
|
|
572
572
|
* delayLicenseRequestUntilPlayed: boolean,
|
|
573
573
|
* advanced: Object.<string, shaka.extern.AdvancedDrmConfiguration>,
|
|
574
|
-
* initDataTransform:
|
|
575
|
-
* ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
576
|
-
* undefined),
|
|
574
|
+
* initDataTransform:(shaka.extern.InitDataTransform|undefined),
|
|
577
575
|
* logLicenseExchange: boolean,
|
|
578
576
|
* updateExpirationTime: number,
|
|
579
577
|
* preferredKeySystems: !Array.<string>
|
|
@@ -597,10 +595,7 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
597
595
|
* <i>Optional.</i> <br>
|
|
598
596
|
* A dictionary which maps key system IDs to advanced DRM configuration for
|
|
599
597
|
* those key systems.
|
|
600
|
-
* @property
|
|
601
|
-
* {((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
602
|
-
* undefined)}
|
|
603
|
-
* initDataTransform
|
|
598
|
+
* @property {shaka.extern.InitDataTransform|undefined} initDataTransform
|
|
604
599
|
* <i>Optional.</i><br>
|
|
605
600
|
* If given, this function is called with the init data from the
|
|
606
601
|
* manifest/media and should return the (possibly transformed) init data to
|
|
@@ -622,6 +617,17 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
622
617
|
*/
|
|
623
618
|
shaka.extern.DrmConfiguration;
|
|
624
619
|
|
|
620
|
+
/**
|
|
621
|
+
* @typedef {function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array}
|
|
622
|
+
*
|
|
623
|
+
* @description
|
|
624
|
+
* A callback function to handle custom content ID signaling for FairPlay
|
|
625
|
+
* content.
|
|
626
|
+
*
|
|
627
|
+
* @exportDoc
|
|
628
|
+
*/
|
|
629
|
+
shaka.extern.InitDataTransform;
|
|
630
|
+
|
|
625
631
|
|
|
626
632
|
/**
|
|
627
633
|
* @typedef {{
|
|
@@ -79,9 +79,9 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
// Notify the SDK when the video has ended, so it can play post-roll ads.
|
|
82
|
-
this.video_
|
|
82
|
+
this.eventManager_.listen(this.video_, 'ended', () => {
|
|
83
83
|
this.adsLoader_.contentComplete();
|
|
84
|
-
};
|
|
84
|
+
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
@@ -212,9 +212,9 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
212
212
|
// seen the ads actually play until requestAds() is called.
|
|
213
213
|
// Note: We listen for a play event to avoid autoplay issues that might
|
|
214
214
|
// crash IMA.
|
|
215
|
-
this.
|
|
215
|
+
this.eventManager_.listenOnce(this.video_, 'play', () => {
|
|
216
216
|
this.imaAdsManager_.start();
|
|
217
|
-
}
|
|
217
|
+
});
|
|
218
218
|
} catch (adError) {
|
|
219
219
|
// If there was a problem with the VAST response,
|
|
220
220
|
// we we won't be getting an ad. Hide ad UI if we showed it already
|
|
@@ -401,6 +401,7 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
401
401
|
shaka.ads.AdManager.AD_STARTED, data));
|
|
402
402
|
this.adContainer_.setAttribute('ad-active', 'true');
|
|
403
403
|
this.video_.pause();
|
|
404
|
+
this.ad_.setVolume(this.video_.muted ? 0 : this.video_.volume);
|
|
404
405
|
}
|
|
405
406
|
|
|
406
407
|
/**
|
|
@@ -411,6 +412,8 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
411
412
|
this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
|
|
412
413
|
(new Map()).set('originalEvent', e)));
|
|
413
414
|
this.adContainer_.removeAttribute('ad-active');
|
|
414
|
-
this.video_.
|
|
415
|
+
if (!this.video_.ended) {
|
|
416
|
+
this.video_.play();
|
|
417
|
+
}
|
|
415
418
|
}
|
|
416
419
|
};
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -2925,6 +2925,7 @@ shaka.hls.HlsParser.AUDIO_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2925
2925
|
'cmfa': 'audio/mp4',
|
|
2926
2926
|
// MPEG2-TS also uses video/ for audio: https://bit.ly/TsMse
|
|
2927
2927
|
'ts': 'video/mp2t',
|
|
2928
|
+
'tsa': 'video/mp2t',
|
|
2928
2929
|
|
|
2929
2930
|
// Raw formats:
|
|
2930
2931
|
'aac': 'audio/aac',
|
|
@@ -2962,6 +2963,7 @@ shaka.hls.HlsParser.VIDEO_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2962
2963
|
'm4f': 'video/mp4',
|
|
2963
2964
|
'cmfv': 'video/mp4',
|
|
2964
2965
|
'ts': 'video/mp2t',
|
|
2966
|
+
'tsv': 'video/mp2t',
|
|
2965
2967
|
};
|
|
2966
2968
|
|
|
2967
2969
|
|
|
@@ -182,6 +182,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
182
182
|
'audio/mp4; codecs="ec-3"',
|
|
183
183
|
'audio/mp4; codecs="opus"',
|
|
184
184
|
'audio/mp4; codecs="flac"',
|
|
185
|
+
'audio/mp4; codecs="dtsc"', // DTS Digital Surround
|
|
186
|
+
'audio/mp4; codecs="dtse"', // DTS Express
|
|
187
|
+
'audio/mp4; codecs="dtsx"', // DTS:X
|
|
185
188
|
// WebM types
|
|
186
189
|
'video/webm; codecs="vp8"',
|
|
187
190
|
'video/webm; codecs="vp9"',
|
|
@@ -515,7 +518,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
515
518
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
516
519
|
// the video stream, so textEngine may not have been initialized.
|
|
517
520
|
if (!this.textEngine_) {
|
|
518
|
-
this.reinitText(
|
|
521
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE);
|
|
519
522
|
}
|
|
520
523
|
|
|
521
524
|
if (transmuxedData.metadata) {
|
|
@@ -542,7 +545,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
542
545
|
contentType, () => this.append_(contentType, transmuxedSegment));
|
|
543
546
|
} else if (hasClosedCaptions) {
|
|
544
547
|
if (!this.textEngine_) {
|
|
545
|
-
this.reinitText(
|
|
548
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE);
|
|
546
549
|
}
|
|
547
550
|
// If it is the init segment for closed captions, initialize the closed
|
|
548
551
|
// caption parser.
|
|
@@ -711,8 +714,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
711
714
|
async endOfStream(reason) {
|
|
712
715
|
await this.enqueueBlockingOperation_(() => {
|
|
713
716
|
// If endOfStream() has already been called on the media source,
|
|
714
|
-
// don't call it again.
|
|
715
|
-
|
|
717
|
+
// don't call it again. Also do not call if readyState is
|
|
718
|
+
// 'closed' (not attached to video element) since it is not a
|
|
719
|
+
// valid operation.
|
|
720
|
+
if (this.ended() || this.mediaSource_.readyState === 'closed') {
|
|
716
721
|
return;
|
|
717
722
|
}
|
|
718
723
|
// Tizen won't let us pass undefined, but it will let us omit the
|
|
@@ -77,6 +77,9 @@ shaka.media.StreamingEngine = class {
|
|
|
77
77
|
/** @private {?shaka.extern.Stream} */
|
|
78
78
|
this.currentTextStream_ = null;
|
|
79
79
|
|
|
80
|
+
/** @private {number} */
|
|
81
|
+
this.textStreamSequenceId_ = 0;
|
|
82
|
+
|
|
80
83
|
/**
|
|
81
84
|
* Maps a content type, e.g., 'audio', 'video', or 'text', to a MediaState.
|
|
82
85
|
*
|
|
@@ -220,6 +223,8 @@ shaka.media.StreamingEngine = class {
|
|
|
220
223
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
221
224
|
goog.asserts.assert(!this.mediaStates_.has(ContentType.TEXT),
|
|
222
225
|
'Should not call loadNewTextStream_ while streaming text!');
|
|
226
|
+
this.textStreamSequenceId_++;
|
|
227
|
+
const currentSequenceId = this.textStreamSequenceId_;
|
|
223
228
|
|
|
224
229
|
try {
|
|
225
230
|
// Clear MediaSource's buffered text, so that the new text stream will
|
|
@@ -241,7 +246,7 @@ shaka.media.StreamingEngine = class {
|
|
|
241
246
|
const streamText =
|
|
242
247
|
textDisplayer.isTextVisible() || this.config_.alwaysStreamText;
|
|
243
248
|
|
|
244
|
-
if (streamText) {
|
|
249
|
+
if (streamText && (this.textStreamSequenceId_ == currentSequenceId)) {
|
|
245
250
|
const state = this.createMediaState_(stream);
|
|
246
251
|
this.mediaStates_.set(ContentType.TEXT, state);
|
|
247
252
|
this.scheduleUpdate_(state, 0);
|
|
@@ -1477,10 +1482,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1477
1482
|
'waiting for another stream to recover...');
|
|
1478
1483
|
}
|
|
1479
1484
|
|
|
1480
|
-
// QuotaExceededError gets thrown if
|
|
1485
|
+
// QuotaExceededError gets thrown if eviction didn't help to make room
|
|
1481
1486
|
// for a segment. We want to wait for a while (4 seconds is just an
|
|
1482
1487
|
// arbitrary number) before updating to give the playhead a chance to
|
|
1483
|
-
// advance, so we don't
|
|
1488
|
+
// advance, so we don't immediately throw again.
|
|
1484
1489
|
this.scheduleUpdate_(mediaState, 4);
|
|
1485
1490
|
}
|
|
1486
1491
|
|
package/lib/media/transmuxer.js
CHANGED
|
@@ -29,9 +29,7 @@ shaka.media.Transmuxer = class {
|
|
|
29
29
|
this.muxjs_ = shaka.dependencies.muxjs();
|
|
30
30
|
|
|
31
31
|
/** @private {muxjs.mp4.Transmuxer} */
|
|
32
|
-
this.muxTransmuxer_ =
|
|
33
|
-
'keepOriginalTimestamps': true,
|
|
34
|
-
});
|
|
32
|
+
this.muxTransmuxer_ = null;
|
|
35
33
|
|
|
36
34
|
/** @private {shaka.util.PublicPromise} */
|
|
37
35
|
this.transmuxPromise_ = null;
|
|
@@ -48,16 +46,22 @@ shaka.media.Transmuxer = class {
|
|
|
48
46
|
/** @private {boolean} */
|
|
49
47
|
this.isTransmuxing_ = false;
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
if (this.muxjs_) {
|
|
50
|
+
this.muxTransmuxer_ = new this.muxjs_.mp4.Transmuxer({
|
|
51
|
+
'keepOriginalTimestamps': true,
|
|
52
|
+
});
|
|
53
|
+
this.muxTransmuxer_.on('data', (segment) => this.onTransmuxed_(segment));
|
|
54
|
+
this.muxTransmuxer_.on('done', () => this.onTransmuxDone_());
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
59
|
* @override
|
|
58
60
|
*/
|
|
59
61
|
destroy() {
|
|
60
|
-
this.muxTransmuxer_
|
|
62
|
+
if (this.muxTransmuxer_) {
|
|
63
|
+
this.muxTransmuxer_.dispose();
|
|
64
|
+
}
|
|
61
65
|
this.muxTransmuxer_ = null;
|
|
62
66
|
return Promise.resolve();
|
|
63
67
|
}
|
|
@@ -156,6 +160,8 @@ shaka.media.Transmuxer = class {
|
|
|
156
160
|
* metadata: !Array.<!Object>}>}
|
|
157
161
|
*/
|
|
158
162
|
transmux(data) {
|
|
163
|
+
goog.asserts.assert(this.muxTransmuxer_,
|
|
164
|
+
'mux.js should be available.');
|
|
159
165
|
goog.asserts.assert(!this.isTransmuxing_,
|
|
160
166
|
'No transmuxing should be in progress.');
|
|
161
167
|
this.isTransmuxing_ = true;
|
|
@@ -135,15 +135,15 @@ shaka.media.VideoWrapper = class {
|
|
|
135
135
|
// calling |Player.load|, meaning that |currentTime| is more meaningful than
|
|
136
136
|
// |startTime|.
|
|
137
137
|
//
|
|
138
|
-
// Seeking to the current time is a work around for Issue 1298
|
|
139
|
-
// do this, the video may get stuck and not play.
|
|
138
|
+
// Seeking to the current time is a work around for Issue 1298 and 4888.
|
|
139
|
+
// If we don't do this, the video may get stuck and not play.
|
|
140
140
|
//
|
|
141
141
|
// TODO: Need further investigation why it happens. Before and after
|
|
142
142
|
// setting the current time, video.readyState is 1, video.paused is true,
|
|
143
143
|
// and video.buffered's TimeRanges length is 0.
|
|
144
144
|
// See: https://github.com/shaka-project/shaka-player/issues/1298
|
|
145
145
|
this.mover_.moveTo(
|
|
146
|
-
this.video_.currentTime == 0 ?
|
|
146
|
+
(!this.video_.currentTime || this.video_.currentTime == 0) ?
|
|
147
147
|
startTime :
|
|
148
148
|
this.video_.currentTime);
|
|
149
149
|
}
|
|
@@ -227,6 +227,16 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
227
227
|
} else {
|
|
228
228
|
return false;
|
|
229
229
|
}
|
|
230
|
+
// Old fetch implementations hasn't body and ReadableStream implementation
|
|
231
|
+
// See: https://github.com/shaka-project/shaka-player/issues/5088
|
|
232
|
+
if (window.Response) {
|
|
233
|
+
const response = new Response('');
|
|
234
|
+
if (!response.body) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
230
240
|
return !!(window.fetch && window.AbortController);
|
|
231
241
|
}
|
|
232
242
|
};
|
|
@@ -299,6 +299,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
299
299
|
if (!responseAndGotProgress.gotProgress &&
|
|
300
300
|
this.onProgressUpdated_ &&
|
|
301
301
|
!response.fromCache &&
|
|
302
|
+
request.method != 'HEAD' &&
|
|
302
303
|
type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
|
|
303
304
|
this.onProgressUpdated_(response.timeMs, response.data.byteLength);
|
|
304
305
|
}
|
|
@@ -67,7 +67,7 @@ shaka.offline.indexeddb.StorageMechanism = class {
|
|
|
67
67
|
shaka.util.Error.Category.STORAGE,
|
|
68
68
|
shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT));
|
|
69
69
|
});
|
|
70
|
-
timeOutTimer.tickAfter(
|
|
70
|
+
timeOutTimer.tickAfter(5);
|
|
71
71
|
|
|
72
72
|
const open = window.indexedDB.open(name, version);
|
|
73
73
|
open.onsuccess = (event) => {
|
package/lib/player.js
CHANGED
|
@@ -1148,6 +1148,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1148
1148
|
*/
|
|
1149
1149
|
shouldUseSrcEquals_(payload) {
|
|
1150
1150
|
const Platform = shaka.util.Platform;
|
|
1151
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
1151
1152
|
|
|
1152
1153
|
// If we are using a platform that does not support media source, we will
|
|
1153
1154
|
// fall back to src= to handle all playback.
|
|
@@ -1202,7 +1203,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1202
1203
|
// version there.
|
|
1203
1204
|
|
|
1204
1205
|
// Native HLS can be preferred on any platform via this flag:
|
|
1205
|
-
if (
|
|
1206
|
+
if (MimeUtils.isHlsType(mimeType) &&
|
|
1207
|
+
this.config_.streaming.preferNativeHls) {
|
|
1206
1208
|
return true;
|
|
1207
1209
|
}
|
|
1208
1210
|
|
|
@@ -2297,6 +2299,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2297
2299
|
// goes wrong.
|
|
2298
2300
|
new shaka.util.Timer(resolve).tickAfter(1);
|
|
2299
2301
|
});
|
|
2302
|
+
} else if (textTracks.length > 0) {
|
|
2303
|
+
this.isTextVisible_ = true;
|
|
2300
2304
|
}
|
|
2301
2305
|
|
|
2302
2306
|
// If we have moved on to another piece of content while waiting for
|
|
@@ -3896,6 +3900,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3896
3900
|
firstVariantWithLabel.language, '', 0, label);
|
|
3897
3901
|
|
|
3898
3902
|
this.chooseVariantAndSwitch_();
|
|
3903
|
+
} else if (this.video_ && this.video_.audioTracks) {
|
|
3904
|
+
const audioTracks = Array.from(this.video_.audioTracks);
|
|
3905
|
+
|
|
3906
|
+
let trackMatch = null;
|
|
3907
|
+
|
|
3908
|
+
for (const audioTrack of audioTracks) {
|
|
3909
|
+
if (audioTrack.label == label) {
|
|
3910
|
+
trackMatch = audioTrack;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
if (trackMatch) {
|
|
3914
|
+
this.switchHtml5Track_(trackMatch);
|
|
3915
|
+
}
|
|
3899
3916
|
}
|
|
3900
3917
|
}
|
|
3901
3918
|
|
|
@@ -6459,7 +6476,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6459
6476
|
* @define {string} A version number taken from git at compile time.
|
|
6460
6477
|
* @export
|
|
6461
6478
|
*/
|
|
6462
|
-
shaka.Player.version = 'v3.2.
|
|
6479
|
+
shaka.Player.version = 'v3.2.21-uncompiled';
|
|
6463
6480
|
|
|
6464
6481
|
// Initialize the deprecation system using the version string we just set
|
|
6465
6482
|
// on the player.
|
|
@@ -8,7 +8,6 @@ goog.provide('shaka.polyfill.MediaCapabilities');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.log');
|
|
10
10
|
goog.require('shaka.polyfill');
|
|
11
|
-
goog.require('shaka.util.Error');
|
|
12
11
|
goog.require('shaka.util.Platform');
|
|
13
12
|
|
|
14
13
|
|
|
@@ -102,6 +101,19 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
102
101
|
if (shaka.util.Platform.isChromecast()) {
|
|
103
102
|
isSupported =
|
|
104
103
|
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
104
|
+
} else if (shaka.util.Platform.isTizen()) {
|
|
105
|
+
let extendedType = videoConfig.contentType;
|
|
106
|
+
if (videoConfig.width && videoConfig.height) {
|
|
107
|
+
extendedType += `; width=${videoConfig.width}`;
|
|
108
|
+
extendedType += `; height=${videoConfig.height}`;
|
|
109
|
+
}
|
|
110
|
+
if (videoConfig.framerate) {
|
|
111
|
+
extendedType += `; framerate=${videoConfig.framerate}`;
|
|
112
|
+
}
|
|
113
|
+
if (videoConfig.bitrate) {
|
|
114
|
+
extendedType += `; bitrate=${videoConfig.bitrate}`;
|
|
115
|
+
}
|
|
116
|
+
isSupported = MediaSource.isTypeSupported(extendedType);
|
|
105
117
|
} else {
|
|
106
118
|
isSupported = MediaSource.isTypeSupported(videoConfig.contentType);
|
|
107
119
|
}
|
|
@@ -212,13 +224,8 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
212
224
|
* @private
|
|
213
225
|
*/
|
|
214
226
|
static canCastDisplayType_(videoConfig) {
|
|
215
|
-
if (!(window.cast
|
|
216
|
-
|
|
217
|
-
throw new shaka.util.Error(
|
|
218
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
219
|
-
shaka.util.Error.Category.CAST,
|
|
220
|
-
shaka.util.Error.Code.CAST_API_UNAVAILABLE);
|
|
221
|
-
} else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
227
|
+
if (!(window.cast &&
|
|
228
|
+
cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
222
229
|
shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
|
|
223
230
|
'MediaSource.isTypeSupported() for type support.');
|
|
224
231
|
return MediaSource.isTypeSupported(videoConfig.contentType);
|
|
@@ -58,14 +58,19 @@ shaka.polyfill.MediaSource = class {
|
|
|
58
58
|
// Offsetting the end of the removal range seems to help.
|
|
59
59
|
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=177884
|
|
60
60
|
shaka.polyfill.MediaSource.patchRemovalRange_();
|
|
61
|
-
} else {
|
|
62
|
-
shaka.log.info('Patching Safari 13 MSE bugs.');
|
|
61
|
+
} else if (safariVersion <= 15) {
|
|
62
|
+
shaka.log.info('Patching Safari 13 & 14 & 15 MSE bugs.');
|
|
63
63
|
// Safari 13 does not correctly implement abort() on SourceBuffer.
|
|
64
64
|
// Calling abort() before appending a segment causes that segment to be
|
|
65
65
|
// incomplete in the buffer.
|
|
66
66
|
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
|
|
67
67
|
shaka.polyfill.MediaSource.stubAbort_();
|
|
68
68
|
}
|
|
69
|
+
} else if (shaka.util.Platform.isEdge()) {
|
|
70
|
+
shaka.log.info('Rejecting TS container.');
|
|
71
|
+
// TS content is broken on Edge in general.
|
|
72
|
+
// See https://github.com/shaka-project/shaka-player/issues/4955
|
|
73
|
+
shaka.polyfill.MediaSource.rejectTsContent_();
|
|
69
74
|
} else if (shaka.util.Platform.isTizen2() ||
|
|
70
75
|
shaka.util.Platform.isTizen3() ||
|
|
71
76
|
shaka.util.Platform.isTizen4()) {
|
package/lib/text/text_engine.js
CHANGED
|
@@ -134,6 +134,7 @@ shaka.text.TextEngine = class {
|
|
|
134
134
|
// into TextEngine and TextDisplayer.
|
|
135
135
|
if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
|
|
136
136
|
mimeType == shaka.util.MimeUtils.CEA708_CLOSED_CAPTION_MIMETYPE) {
|
|
137
|
+
this.parser_ = null;
|
|
137
138
|
return;
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -480,6 +480,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
480
480
|
wrapper = shaka.util.Dom.createHTMLElement('span');
|
|
481
481
|
wrapper.classList.add('shaka-text-wrapper');
|
|
482
482
|
wrapper.style.backgroundColor = cue.backgroundColor;
|
|
483
|
+
wrapper.style.lineHeight = 'normal';
|
|
483
484
|
cueElement.appendChild(wrapper);
|
|
484
485
|
}
|
|
485
486
|
|