shaka-player 4.2.4 → 4.2.6
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 +13 -0
- package/AUTHORS +2 -0
- package/CHANGELOG.md +35 -0
- package/CONTRIBUTORS +2 -0
- package/demo/index.html +1 -1
- package/dist/demo.compiled.debug.js +64 -64
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +64 -64
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +3 -2
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +12 -12
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +12 -12
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.externs.js +93 -79
- package/dist/shaka-player.compiled.debug.js +585 -577
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +93 -79
- package/dist/shaka-player.compiled.js +816 -814
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +10 -0
- package/dist/shaka-player.ui.debug.d.ts +10 -0
- package/dist/shaka-player.ui.debug.externs.js +93 -79
- package/dist/shaka-player.ui.debug.js +615 -606
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +93 -79
- package/dist/shaka-player.ui.js +964 -962
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/player.js +2 -1
- package/lib/ads/ad_manager.js +2 -2
- package/lib/cea/cea708_window.js +2 -2
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +24 -6
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_engine.js +16 -0
- package/lib/media/segment_reference.js +3 -0
- package/lib/media/streaming_engine.js +33 -10
- package/lib/player.js +6 -2
- package/lib/polyfill/abort_controller.js +143 -0
- package/lib/polyfill/media_capabilities.js +59 -9
- package/lib/text/ttml_text_parser.js +32 -7
- package/lib/text/ui_text_displayer.js +22 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +10 -3
- package/lib/util/stream_utils.js +30 -10
- package/package.json +1 -1
- package/shaka-player.uncompiled.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +1 -0
- package/test/hls/hls_parser_unit.js +1 -0
- package/test/media/drm_engine_unit.js +11 -1
- package/test/media/media_source_engine_integration.js +78 -2
- package/test/player_integration.js +10 -3
- package/test/polyfill/media_capabilities_unit.js +261 -0
- package/test/test/boot.js +29 -11
- package/test/test/util/manifest_parser_util.js +4 -3
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +5 -4
- package/test/test/util/util.js +3 -2
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/ttml_text_parser_unit.js +119 -86
- package/test/text/ui_text_displayer_unit.js +76 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/stream_utils_unit.js +18 -0
- package/test/util/xml_utils_unit.js +6 -1
- package/ui/controls.js +49 -36
package/docs/tutorials/ui.md
CHANGED
|
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
|
|
|
30
30
|
The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
|
|
31
31
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
32
32
|
<div data-shaka-player-container style="max-width:40em"
|
|
33
|
-
data-shaka-player-cast-receiver-id="
|
|
33
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
34
34
|
<!-- The data-shaka-player tag will make the UI library use this video element.
|
|
35
35
|
If no video is provided, the UI will automatically make one inside the container div. -->
|
|
36
36
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
|
|
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
|
|
|
111
111
|
<!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
|
|
112
112
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
113
113
|
<div data-shaka-player-container style="max-width:40em"
|
|
114
|
-
data-shaka-player-cast-receiver-id="
|
|
114
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
115
115
|
</div>
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -139,7 +139,7 @@ casting to an Android receiver app.
|
|
|
139
139
|
|
|
140
140
|
```html
|
|
141
141
|
<div data-shaka-player-container style="max-width:40em"
|
|
142
|
-
data-shaka-player-cast-receiver-id="
|
|
142
|
+
data-shaka-player-cast-receiver-id="A67E4E9A"
|
|
143
143
|
data-shaka-player-cast-android-receiver-compatible="true">
|
|
144
144
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
145
145
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
|
|
|
154
154
|
|
|
155
155
|
```html
|
|
156
156
|
<div data-shaka-player-container style="max-width:40em"
|
|
157
|
-
data-shaka-player-cast-receiver-id="
|
|
157
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
158
158
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
159
159
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
160
160
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
@@ -165,7 +165,7 @@ or
|
|
|
165
165
|
|
|
166
166
|
```html
|
|
167
167
|
<div data-shaka-player-container style="max-width:40em"
|
|
168
|
-
data-shaka-player-cast-receiver-id="
|
|
168
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
169
169
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
170
170
|
<!-- The manifest url in the src attribute will be auto loaded -->
|
|
171
171
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -178,7 +178,7 @@ call to the first one fails.
|
|
|
178
178
|
|
|
179
179
|
```html
|
|
180
180
|
<div data-shaka-player-container style="max-width:40em"
|
|
181
|
-
data-shaka-player-cast-receiver-id="
|
|
181
|
+
data-shaka-player-cast-receiver-id="A67E4E9A">
|
|
182
182
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
183
183
|
<!-- Try this first -->
|
|
184
184
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -222,7 +222,7 @@ const video = controls.getVideo();
|
|
|
222
222
|
// Receiver Compatability.
|
|
223
223
|
ui.configure({
|
|
224
224
|
// Set the castReceiverAppId
|
|
225
|
-
'castReceiverAppId': '
|
|
225
|
+
'castReceiverAppId': 'A67E4E9A',
|
|
226
226
|
// Enable casting to native Android Apps (e.g. Android TV Apps)
|
|
227
227
|
'castAndroidReceiverCompatible': true,
|
|
228
228
|
});
|
|
@@ -29,8 +29,8 @@ You can build Shaka on Linux, Windows, or Mac.
|
|
|
29
29
|
To get the sources and compile the library, you will need:
|
|
30
30
|
* {@link https://git-scm.com/downloads Git v1.9+}
|
|
31
31
|
* {@link https://www.python.org/downloads/ Python v2.7 or v3.5+}
|
|
32
|
-
* {@link https://java.com/en/download/ Java Runtime Environment
|
|
33
|
-
* {@link https://nodejs.org/en/download/ NodeJS
|
|
32
|
+
* {@link https://java.com/en/download/ Java Runtime Environment v14+}
|
|
33
|
+
* {@link https://nodejs.org/en/download/ NodeJS v14+}
|
|
34
34
|
* A local web server, such as {@link https://httpd.apache.org/ Apache}
|
|
35
35
|
* _NOTE: A local web server is required because browsers place restrictions
|
|
36
36
|
on applications from file:/// URLs._
|
package/externs/shaka/player.js
CHANGED
|
@@ -131,7 +131,8 @@ shaka.extern.StateChange;
|
|
|
131
131
|
* @property {number} manifestTimeSeconds
|
|
132
132
|
* The amount of time it took to download and parse the manifest.
|
|
133
133
|
* @property {number} drmTimeSeconds
|
|
134
|
-
* The amount of time it took to download the first drm key
|
|
134
|
+
* The amount of time it took to download the first drm key, and load that key
|
|
135
|
+
* into the drm system.
|
|
135
136
|
* @property {number} playTime
|
|
136
137
|
* The total time spent in a playing state in seconds.
|
|
137
138
|
* @property {number} pauseTime
|
package/lib/ads/ad_manager.js
CHANGED
|
@@ -19,8 +19,8 @@ goog.require('shaka.util.IReleasable');
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* @event shaka.ads.AdManager.
|
|
23
|
-
* @description Fired when
|
|
22
|
+
* @event shaka.ads.AdManager.AdsLoadedEvent
|
|
23
|
+
* @description Fired when a sequence of ads has been loaded.
|
|
24
24
|
* @property {string} type
|
|
25
25
|
* 'ads-loaded'
|
|
26
26
|
* @property {number} loadTime
|
package/lib/cea/cea708_window.js
CHANGED
|
@@ -420,7 +420,7 @@ shaka.cea.Cea708Window.TextJustification = {
|
|
|
420
420
|
shaka.cea.Cea708Window.MAX_COLS = 42;
|
|
421
421
|
|
|
422
422
|
/**
|
|
423
|
-
* Maximum of
|
|
423
|
+
* Maximum of 16 rows that can be indexed from 0 to 15.
|
|
424
424
|
* @private @const {number}
|
|
425
425
|
*/
|
|
426
|
-
shaka.cea.Cea708Window.MAX_ROWS =
|
|
426
|
+
shaka.cea.Cea708Window.MAX_ROWS = 16;
|
|
@@ -67,7 +67,7 @@ shaka.dash.SegmentTemplate = class {
|
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
69
|
} else if (info.segmentDuration) {
|
|
70
|
-
if (!isUpdate) {
|
|
70
|
+
if (!isUpdate && context.adaptationSet.contentType !== 'image') {
|
|
71
71
|
context.presentationTimeline.notifyMaxSegmentDuration(
|
|
72
72
|
info.segmentDuration);
|
|
73
73
|
context.presentationTimeline.notifyMinSegmentStartTime(
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -1049,7 +1049,7 @@ shaka.hls.HlsParser = class {
|
|
|
1049
1049
|
shaka.log.debug('Guessing audio-only.');
|
|
1050
1050
|
type = ContentType.AUDIO;
|
|
1051
1051
|
ignoreStream = true;
|
|
1052
|
-
} else if (res.video.length) {
|
|
1052
|
+
} else if (res.video.length && !res.audio.length) {
|
|
1053
1053
|
// There are associated video streams. Assume this is audio.
|
|
1054
1054
|
shaka.log.debug('Guessing audio-only.');
|
|
1055
1055
|
type = ContentType.AUDIO;
|
|
@@ -1665,9 +1665,16 @@ shaka.hls.HlsParser = class {
|
|
|
1665
1665
|
/** @type {!Map.<number, number>} */
|
|
1666
1666
|
const mediaSequenceToStartTime = new Map();
|
|
1667
1667
|
|
|
1668
|
+
const prevLowLatencyMode = this.lowLatencyMode_;
|
|
1668
1669
|
const segments = this.createSegments_(verbatimMediaPlaylistUri, playlist,
|
|
1669
1670
|
type, mimeType, mediaSequenceToStartTime, mediaVariables, codecs);
|
|
1670
1671
|
|
|
1672
|
+
// This happens when autoLowLatencyMode is true, so we need set the
|
|
1673
|
+
// correct lowLatencyPresentationDelay_
|
|
1674
|
+
if (prevLowLatencyMode != this.lowLatencyMode_) {
|
|
1675
|
+
this.determinePresentationType_(playlist);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1671
1678
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1672
1679
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1673
1680
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -1796,8 +1803,11 @@ shaka.hls.HlsParser = class {
|
|
|
1796
1803
|
shaka.util.Error.Code.HLS_AES_128_INVALID_KEY_LENGTH);
|
|
1797
1804
|
}
|
|
1798
1805
|
|
|
1806
|
+
const algorithm = {
|
|
1807
|
+
name: 'AES-CBC',
|
|
1808
|
+
};
|
|
1799
1809
|
keyInfo.cryptoKey = await window.crypto.subtle.importKey(
|
|
1800
|
-
'raw', keyResponse.data,
|
|
1810
|
+
'raw', keyResponse.data, algorithm, true, ['decrypt']);
|
|
1801
1811
|
keyInfo.fetchKey = undefined; // No longer needed.
|
|
1802
1812
|
};
|
|
1803
1813
|
|
|
@@ -2233,12 +2243,12 @@ shaka.hls.HlsParser = class {
|
|
|
2233
2243
|
/** @type {shaka.extern.HlsAes128Key|undefined} */
|
|
2234
2244
|
let hlsAes128Key = undefined;
|
|
2235
2245
|
|
|
2236
|
-
|
|
2237
|
-
|
|
2246
|
+
let discontinuitySequence = shaka.hls.Utils.getFirstTagWithNameAsNumber(
|
|
2247
|
+
playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE', 0);
|
|
2238
2248
|
const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
|
|
2239
2249
|
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
|
|
2240
|
-
const skipTag = shaka.hls.Utils.getFirstTagWithName(
|
|
2241
|
-
'EXT-X-SKIP');
|
|
2250
|
+
const skipTag = shaka.hls.Utils.getFirstTagWithName(
|
|
2251
|
+
playlist.tags, 'EXT-X-SKIP');
|
|
2242
2252
|
const skippedSegments =
|
|
2243
2253
|
skipTag ? Number(skipTag.getAttributeValue('SKIPPED-SEGMENTS')) : 0;
|
|
2244
2254
|
let position = mediaSequenceNumber + skippedSegments;
|
|
@@ -2261,6 +2271,12 @@ shaka.hls.HlsParser = class {
|
|
|
2261
2271
|
(i == 0) ? firstStartTime : previousReference.endTime;
|
|
2262
2272
|
position = mediaSequenceNumber + skippedSegments + i;
|
|
2263
2273
|
|
|
2274
|
+
const discontinuityTag = shaka.hls.Utils.getFirstTagWithName(
|
|
2275
|
+
playlist.tags, 'EXT-X-DISCONTINUITY');
|
|
2276
|
+
if (discontinuityTag) {
|
|
2277
|
+
discontinuitySequence++;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2264
2280
|
// Apply new AES-128 tags as you see them, keeping a running total.
|
|
2265
2281
|
for (const drmTag of item.tags) {
|
|
2266
2282
|
if (drmTag.name == 'EXT-X-KEY' &&
|
|
@@ -2298,6 +2314,8 @@ shaka.hls.HlsParser = class {
|
|
|
2298
2314
|
previousReference = reference;
|
|
2299
2315
|
|
|
2300
2316
|
if (reference) {
|
|
2317
|
+
reference.discontinuitySequence = discontinuitySequence;
|
|
2318
|
+
|
|
2301
2319
|
if (this.config_.hls.ignoreManifestProgramDateTime &&
|
|
2302
2320
|
this.minSequenceNumber_ != null &&
|
|
2303
2321
|
position < this.minSequenceNumber_) {
|
|
@@ -69,6 +69,18 @@ shaka.media.ContentWorkarounds = class {
|
|
|
69
69
|
})
|
|
70
70
|
.fullBox('encv', onEncryptionMetadataBox)
|
|
71
71
|
.fullBox('enca', onEncryptionMetadataBox)
|
|
72
|
+
.fullBox('hev1', (box) => {
|
|
73
|
+
boxesToModify.push({
|
|
74
|
+
box,
|
|
75
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
76
|
+
});
|
|
77
|
+
})
|
|
78
|
+
.fullBox('hvc1', (box) => {
|
|
79
|
+
boxesToModify.push({
|
|
80
|
+
box,
|
|
81
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
82
|
+
});
|
|
83
|
+
})
|
|
72
84
|
.fullBox('avc1', (box) => {
|
|
73
85
|
boxesToModify.push({
|
|
74
86
|
box,
|
|
@@ -856,6 +856,22 @@ shaka.media.MediaSourceEngine = class {
|
|
|
856
856
|
]);
|
|
857
857
|
}
|
|
858
858
|
|
|
859
|
+
/**
|
|
860
|
+
* Adjust timestamp offset to maintain AV sync across discontinuities.
|
|
861
|
+
* Only used in sequence mode.
|
|
862
|
+
*
|
|
863
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
864
|
+
* @param {number} timestampOffset
|
|
865
|
+
* @return {!Promise}
|
|
866
|
+
*/
|
|
867
|
+
async resync(contentType, timestampOffset) {
|
|
868
|
+
goog.asserts.assert(this.sequenceMode_,
|
|
869
|
+
'resyncAudio only used with sequence mode!');
|
|
870
|
+
await this.enqueueOperation_(
|
|
871
|
+
contentType,
|
|
872
|
+
() => this.setTimestampOffset_(contentType, timestampOffset));
|
|
873
|
+
}
|
|
874
|
+
|
|
859
875
|
/**
|
|
860
876
|
* @param {string=} reason Valid reasons are 'network' and 'decode'.
|
|
861
877
|
* @return {!Promise}
|
|
@@ -618,6 +618,11 @@ shaka.media.StreamingEngine = class {
|
|
|
618
618
|
* within the presentation timeline.
|
|
619
619
|
*/
|
|
620
620
|
seeked() {
|
|
621
|
+
if (!this.playerInterface_) {
|
|
622
|
+
// Already destroyed.
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
621
626
|
const presentationTime = this.playerInterface_.getPresentationTime();
|
|
622
627
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
623
628
|
const newTimeIsBuffered = (type) => {
|
|
@@ -675,7 +680,7 @@ shaka.media.StreamingEngine = class {
|
|
|
675
680
|
|
|
676
681
|
/**
|
|
677
682
|
* Clear the buffer for a given stream. Unlike clearBuffer_, this will handle
|
|
678
|
-
* cases where a MediaState is performing an update. After this runs,
|
|
683
|
+
* cases where a MediaState is performing an update. After this runs, the
|
|
679
684
|
* MediaState will have a pending update.
|
|
680
685
|
* @param {!shaka.media.StreamingEngine.MediaState_} mediaState
|
|
681
686
|
* @private
|
|
@@ -791,9 +796,9 @@ shaka.media.StreamingEngine = class {
|
|
|
791
796
|
for (const type of streamsByType.keys()) {
|
|
792
797
|
const stream = streamsByType.get(type);
|
|
793
798
|
if (!this.mediaStates_.has(type)) {
|
|
794
|
-
const
|
|
795
|
-
this.mediaStates_.set(type,
|
|
796
|
-
this.scheduleUpdate_(
|
|
799
|
+
const mediaState = this.createMediaState_(stream);
|
|
800
|
+
this.mediaStates_.set(type, mediaState);
|
|
801
|
+
this.scheduleUpdate_(mediaState, 0);
|
|
797
802
|
}
|
|
798
803
|
}
|
|
799
804
|
}
|
|
@@ -905,7 +910,7 @@ shaka.media.StreamingEngine = class {
|
|
|
905
910
|
'mediastate.stream should not have segmentIndex yet.');
|
|
906
911
|
thisStream.closeSegmentIndex();
|
|
907
912
|
}
|
|
908
|
-
if (mediaState.
|
|
913
|
+
if (!mediaState.performingUpdate && !mediaState.updateTimer) {
|
|
909
914
|
this.scheduleUpdate_(mediaState, 0);
|
|
910
915
|
}
|
|
911
916
|
return;
|
|
@@ -1389,7 +1394,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1389
1394
|
// If the network slows down, abort the current fetch request and start
|
|
1390
1395
|
// a new one, and ignore the error message.
|
|
1391
1396
|
mediaState.performingUpdate = false;
|
|
1392
|
-
mediaState
|
|
1397
|
+
this.cancelUpdate_(mediaState);
|
|
1393
1398
|
this.scheduleUpdate_(mediaState, 0);
|
|
1394
1399
|
} else if (mediaState.type == ContentType.TEXT &&
|
|
1395
1400
|
this.config_.ignoreTextStreamFailures) {
|
|
@@ -1462,7 +1467,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1462
1467
|
|
|
1463
1468
|
for (const mediaState of this.mediaStates_.values()) {
|
|
1464
1469
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
1465
|
-
if
|
|
1470
|
+
// Only schedule an update if it has an error, but it's not mid-update
|
|
1471
|
+
// and there is not already an update scheduled.
|
|
1472
|
+
if (mediaState.hasError && !mediaState.performingUpdate &&
|
|
1473
|
+
!mediaState.updateTimer) {
|
|
1466
1474
|
shaka.log.info(logPrefix, 'Retrying after failure...');
|
|
1467
1475
|
mediaState.hasError = false;
|
|
1468
1476
|
this.scheduleUpdate_(mediaState, delaySeconds);
|
|
@@ -1645,6 +1653,19 @@ shaka.media.StreamingEngine = class {
|
|
|
1645
1653
|
}
|
|
1646
1654
|
}
|
|
1647
1655
|
|
|
1656
|
+
if (this.manifest_.sequenceMode) {
|
|
1657
|
+
// Across discontinuity bounds, we should resync timestamps for
|
|
1658
|
+
// sequence mode playbacks. The next segment appended should
|
|
1659
|
+
// land at its theoretical timestamp from the segment index.
|
|
1660
|
+
const lastDiscontinuitySequence =
|
|
1661
|
+
mediaState.lastSegmentReference ?
|
|
1662
|
+
mediaState.lastSegmentReference.discontinuitySequence : null;
|
|
1663
|
+
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
|
|
1664
|
+
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1665
|
+
mediaState.type, reference.startTime));
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1648
1669
|
await Promise.all(operations);
|
|
1649
1670
|
}
|
|
1650
1671
|
|
|
@@ -1678,8 +1699,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1678
1699
|
|
|
1679
1700
|
await this.evict_(mediaState, presentationTime);
|
|
1680
1701
|
this.destroyer_.ensureNotDestroyed();
|
|
1681
|
-
shaka.log.v1(logPrefix, 'appending media segment at',
|
|
1682
|
-
(reference.syncTime == null ? 'unknown' : reference.syncTime));
|
|
1683
1702
|
|
|
1684
1703
|
// 'seeked' or 'adaptation' triggered logic applies only to this
|
|
1685
1704
|
// appendBuffer() call.
|
|
@@ -1956,7 +1975,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1956
1975
|
shaka.log.debug(logPrefix, 'cleared buffer');
|
|
1957
1976
|
mediaState.clearingBuffer = false;
|
|
1958
1977
|
mediaState.endOfStream = false;
|
|
1959
|
-
|
|
1978
|
+
// Since the clear operation was async, check to make sure we're not doing
|
|
1979
|
+
// another update and we don't have one scheduled yet.
|
|
1980
|
+
if (!mediaState.performingUpdate && !mediaState.updateTimer) {
|
|
1981
|
+
this.scheduleUpdate_(mediaState, 0);
|
|
1982
|
+
}
|
|
1960
1983
|
}
|
|
1961
1984
|
|
|
1962
1985
|
|
package/lib/player.js
CHANGED
|
@@ -4140,6 +4140,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4140
4140
|
return [];
|
|
4141
4141
|
}
|
|
4142
4142
|
const chapters = [];
|
|
4143
|
+
const uniqueChapters = new Set();
|
|
4143
4144
|
for (const chaptersTrack of chaptersTracksWithLanguage) {
|
|
4144
4145
|
if (chaptersTrack && chaptersTrack.cues) {
|
|
4145
4146
|
for (const cue of chaptersTrack.cues) {
|
|
@@ -4154,7 +4155,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4154
4155
|
startTime: cue.startTime,
|
|
4155
4156
|
endTime: cue.endTime,
|
|
4156
4157
|
};
|
|
4157
|
-
|
|
4158
|
+
if (!uniqueChapters.has(id)) {
|
|
4159
|
+
chapters.push(chapter);
|
|
4160
|
+
uniqueChapters.add(id);
|
|
4161
|
+
}
|
|
4158
4162
|
}
|
|
4159
4163
|
}
|
|
4160
4164
|
}
|
|
@@ -6595,7 +6599,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6595
6599
|
* @define {string} A version number taken from git at compile time.
|
|
6596
6600
|
* @export
|
|
6597
6601
|
*/
|
|
6598
|
-
shaka.Player.version = 'v4.2.
|
|
6602
|
+
shaka.Player.version = 'v4.2.6-uncompiled';
|
|
6599
6603
|
|
|
6600
6604
|
// Initialize the deprecation system using the version string we just set
|
|
6601
6605
|
// on the player.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.polyfill.AbortController');
|
|
8
|
+
|
|
9
|
+
goog.require('shaka.polyfill');
|
|
10
|
+
goog.require('shaka.util.FakeEvent');
|
|
11
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @summary A polyfill for systems that do not implement AbortController.
|
|
15
|
+
* This is used both with the fetch API for HTTP requests and inside the HLS
|
|
16
|
+
* parser.
|
|
17
|
+
* @export
|
|
18
|
+
* @extends AbortController
|
|
19
|
+
*/
|
|
20
|
+
shaka.polyfill.AbortController = class {
|
|
21
|
+
/**
|
|
22
|
+
* Install the polyfill if needed.
|
|
23
|
+
* @export
|
|
24
|
+
*/
|
|
25
|
+
static install() {
|
|
26
|
+
if (window.AbortController) {
|
|
27
|
+
// Not needed.
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
window.AbortController = shaka.polyfill.AbortController;
|
|
32
|
+
window.AbortSignal = shaka.polyfill.AbortController.AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** */
|
|
36
|
+
constructor() {
|
|
37
|
+
/** @private {!shaka.polyfill.AbortController.AbortSignal} */
|
|
38
|
+
this.signal_ = new shaka.polyfill.AbortController.AbortSignal();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
* @suppress {const|duplicate} Since the original is defined as "const", we
|
|
44
|
+
* need this suppression to override it.
|
|
45
|
+
*/
|
|
46
|
+
get signal() {
|
|
47
|
+
return this.signal_;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {*=} reason
|
|
52
|
+
* @override
|
|
53
|
+
*/
|
|
54
|
+
abort(reason) {
|
|
55
|
+
this.signal_.doAbort_(reason);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @summary A polyfill for AbortSignal, part of the AbortController API.
|
|
61
|
+
* @implements {AbortSignal}
|
|
62
|
+
*/
|
|
63
|
+
shaka.polyfill.AbortController.AbortSignal =
|
|
64
|
+
class extends shaka.util.FakeEventTarget {
|
|
65
|
+
/** */
|
|
66
|
+
constructor() {
|
|
67
|
+
super();
|
|
68
|
+
|
|
69
|
+
/** @private {boolean} */
|
|
70
|
+
this.aborted_ = false;
|
|
71
|
+
|
|
72
|
+
/** @private {*} */
|
|
73
|
+
this.reason_ = undefined;
|
|
74
|
+
|
|
75
|
+
/** @type {?function(!Event)} */
|
|
76
|
+
this.onabort = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @override */
|
|
80
|
+
get aborted() {
|
|
81
|
+
return this.aborted_;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @return {*} */
|
|
85
|
+
get reason() {
|
|
86
|
+
return this.reason_;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {*} reason
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
doAbort_(reason) {
|
|
94
|
+
if (this.aborted_) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.aborted_ = true;
|
|
99
|
+
this.reason_ = reason;
|
|
100
|
+
if (this.reason_ === undefined) {
|
|
101
|
+
// This is equivalent to a native implementation.
|
|
102
|
+
this.reason_ = new DOMException(
|
|
103
|
+
'signal is aborted without reason', 'AbortError');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// According to MDN:
|
|
107
|
+
// "Event type - A generic Event with no added properties."
|
|
108
|
+
const event = new shaka.util.FakeEvent('abort');
|
|
109
|
+
if (this.onabort) {
|
|
110
|
+
this.onabort(event);
|
|
111
|
+
}
|
|
112
|
+
this.dispatchEvent(event);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {*=} reason
|
|
118
|
+
* @return {!AbortSignal}
|
|
119
|
+
*/
|
|
120
|
+
static abort(reason) {
|
|
121
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
122
|
+
signal.doAbort_(reason);
|
|
123
|
+
return signal;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {number} timeMs
|
|
128
|
+
* @return {!AbortSignal}
|
|
129
|
+
*/
|
|
130
|
+
static timeout(timeMs) {
|
|
131
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
132
|
+
|
|
133
|
+
window.setTimeout(() => {
|
|
134
|
+
// This is equivalent to a native implementation.
|
|
135
|
+
signal.doAbort_(new DOMException('signal timed out', 'TimeoutError'));
|
|
136
|
+
}, timeMs);
|
|
137
|
+
|
|
138
|
+
return signal;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
shaka.polyfill.register(shaka.polyfill.AbortController.install);
|
|
@@ -8,6 +8,7 @@ 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');
|
|
11
12
|
goog.require('shaka.util.Platform');
|
|
12
13
|
|
|
13
14
|
|
|
@@ -96,37 +97,48 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
96
97
|
return res;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
const videoConfig = mediaDecodingConfig['video'];
|
|
101
|
+
const audioConfig = mediaDecodingConfig['audio'];
|
|
102
|
+
|
|
99
103
|
if (mediaDecodingConfig.type == 'media-source') {
|
|
100
104
|
if (!shaka.util.Platform.supportsMediaSource()) {
|
|
101
105
|
return res;
|
|
102
106
|
}
|
|
103
107
|
// Use 'MediaSource.isTypeSupported' to check if the stream is supported.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
// Cast platforms will additionally check canDisplayType(), which
|
|
109
|
+
// accepts extended MIME type parameters.
|
|
110
|
+
// See: https://github.com/shaka-project/shaka-player/issues/4726
|
|
111
|
+
if (videoConfig) {
|
|
112
|
+
let isSupported;
|
|
113
|
+
if (shaka.util.Platform.isChromecast()) {
|
|
114
|
+
isSupported =
|
|
115
|
+
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
116
|
+
} else {
|
|
117
|
+
isSupported = MediaSource.isTypeSupported(videoConfig.contentType);
|
|
118
|
+
}
|
|
107
119
|
if (!isSupported) {
|
|
108
120
|
return res;
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
|
|
112
|
-
if (
|
|
113
|
-
const contentType =
|
|
124
|
+
if (audioConfig) {
|
|
125
|
+
const contentType = audioConfig.contentType;
|
|
114
126
|
const isSupported = MediaSource.isTypeSupported(contentType);
|
|
115
127
|
if (!isSupported) {
|
|
116
128
|
return res;
|
|
117
129
|
}
|
|
118
130
|
}
|
|
119
131
|
} else if (mediaDecodingConfig.type == 'file') {
|
|
120
|
-
if (
|
|
121
|
-
const contentType =
|
|
132
|
+
if (videoConfig) {
|
|
133
|
+
const contentType = videoConfig.contentType;
|
|
122
134
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
123
135
|
if (!isSupported) {
|
|
124
136
|
return res;
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
if (
|
|
129
|
-
const contentType =
|
|
140
|
+
if (audioConfig) {
|
|
141
|
+
const contentType = audioConfig.contentType;
|
|
130
142
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
131
143
|
if (!isSupported) {
|
|
132
144
|
return res;
|
|
@@ -201,6 +213,44 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
201
213
|
|
|
202
214
|
return res;
|
|
203
215
|
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Checks if the given media parameters of the video or audio streams are
|
|
219
|
+
* supported by the Cast platform.
|
|
220
|
+
* @param {!VideoConfiguration} videoConfig The 'video' field of the
|
|
221
|
+
* MediaDecodingConfiguration.
|
|
222
|
+
* @return {boolean} `true` when the stream can be displayed on a Cast device.
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
static canCastDisplayType_(videoConfig) {
|
|
226
|
+
if (!(window.cast)) {
|
|
227
|
+
shaka.log.error('Expected cast namespace to be available!');
|
|
228
|
+
throw new shaka.util.Error(
|
|
229
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
230
|
+
shaka.util.Error.Category.CAST,
|
|
231
|
+
shaka.util.Error.Code.CAST_API_UNAVAILABLE);
|
|
232
|
+
} else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
233
|
+
shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
|
|
234
|
+
'MediaSource.isTypeSupported() for type support.');
|
|
235
|
+
return MediaSource.isTypeSupported(videoConfig.contentType);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let displayType = videoConfig.contentType;
|
|
239
|
+
if (videoConfig.width && videoConfig.height) {
|
|
240
|
+
displayType +=
|
|
241
|
+
`; width=${videoConfig.width}; height=${videoConfig.height}`;
|
|
242
|
+
}
|
|
243
|
+
if (videoConfig.framerate) {
|
|
244
|
+
displayType += `; framerate=${videoConfig.framerate}`;
|
|
245
|
+
}
|
|
246
|
+
if (videoConfig.transferFunction === 'pq') {
|
|
247
|
+
// A "PQ" transfer function indicates this is an HDR-capable stream;
|
|
248
|
+
// "smpte2084" is the published standard. We need to inform the platform
|
|
249
|
+
// this query is specifically for HDR.
|
|
250
|
+
displayType += '; eotf=smpte2084';
|
|
251
|
+
}
|
|
252
|
+
return cast.__platform__.canDisplayType(displayType);
|
|
253
|
+
}
|
|
204
254
|
};
|
|
205
255
|
|
|
206
256
|
/**
|