shaka-player 4.2.3 → 4.2.5
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/AUTHORS +2 -0
- package/CHANGELOG.md +39 -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 +5 -4
- package/dist/locales.js +32 -32
- 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 +15 -1
- package/dist/shaka-player.compiled.debug.d.ts +15 -1
- package/dist/shaka-player.compiled.debug.externs.js +95 -80
- package/dist/shaka-player.compiled.debug.js +583 -573
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +95 -80
- package/dist/shaka-player.compiled.js +823 -820
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +15 -1
- package/dist/shaka-player.ui.debug.d.ts +15 -1
- package/dist/shaka-player.ui.debug.externs.js +95 -80
- package/dist/shaka-player.ui.debug.js +612 -602
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +95 -80
- package/dist/shaka-player.ui.js +963 -960
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/welcome.md +2 -2
- package/externs/mux.js +3 -0
- package/externs/shaka/player.js +2 -1
- 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/cea/cea708_window.js +2 -2
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +21 -4
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_engine.js +6 -0
- package/lib/media/playhead.js +23 -1
- package/lib/media/streaming_engine.js +3 -12
- package/lib/media/transmuxer.js +6 -0
- package/lib/player.js +24 -11
- 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/manifest_parser_utils.js +4 -0
- 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/roadmap.md +24 -6
- package/shaka-player.uncompiled.js +1 -0
- 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/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 +108 -1
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +13 -0
- package/test/media/streaming_engine_unit.js +36 -23
- package/test/player_unit.js +87 -0
- package/test/polyfill/media_capabilities_unit.js +261 -0
- package/test/test/util/simple_fakes.js +3 -0
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +3 -2
- 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 -37
- package/ui/settings_menu.js +11 -2
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/mux.js
CHANGED
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/cast/cast_proxy.js
CHANGED
|
@@ -705,7 +705,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
705
705
|
// If we are casting, but the first update has not come in yet, use local
|
|
706
706
|
// getters, but not local methods.
|
|
707
707
|
if (this.sender_.isCasting() && !this.sender_.hasRemoteProperties()) {
|
|
708
|
-
if (shaka.cast.CastUtils.PlayerGetterMethods[name]
|
|
708
|
+
if (shaka.cast.CastUtils.PlayerGetterMethods[name] ||
|
|
709
|
+
shaka.cast.CastUtils.LargePlayerGetterMethods[name]) {
|
|
709
710
|
const value = /** @type {Object} */(this.localPlayer_)[name];
|
|
710
711
|
goog.asserts.assert(typeof value == 'function',
|
|
711
712
|
'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
|
@@ -370,7 +370,8 @@ shaka.cast.CastSender = class {
|
|
|
370
370
|
return (...args) =>
|
|
371
371
|
this.remoteAsyncCall_(targetName, property, ...args);
|
|
372
372
|
}
|
|
373
|
-
if (CastUtils.PlayerGetterMethods[property]
|
|
373
|
+
if (CastUtils.PlayerGetterMethods[property] ||
|
|
374
|
+
CastUtils.LargePlayerGetterMethods[property]) {
|
|
374
375
|
return () => this.propertyGetter_(targetName, property);
|
|
375
376
|
}
|
|
376
377
|
}
|
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/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
|
@@ -831,13 +831,19 @@ shaka.hls.HlsParser = class {
|
|
|
831
831
|
shaka.util.ArrayUtils.remove(allCodecs, textCodecs);
|
|
832
832
|
}
|
|
833
833
|
if (audioGroupId) {
|
|
834
|
-
|
|
834
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
835
835
|
ContentType.AUDIO, allCodecs);
|
|
836
|
+
if (!codecs) {
|
|
837
|
+
codecs = this.config_.hls.defaultAudioCodec;
|
|
838
|
+
}
|
|
836
839
|
this.groupIdToCodecsMap_.set(audioGroupId, codecs);
|
|
837
840
|
}
|
|
838
841
|
if (videoGroupId) {
|
|
839
|
-
|
|
842
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
840
843
|
ContentType.VIDEO, allCodecs);
|
|
844
|
+
if (!codecs) {
|
|
845
|
+
codecs = this.config_.hls.defaultVideoCodec;
|
|
846
|
+
}
|
|
841
847
|
this.groupIdToCodecsMap_.set(videoGroupId, codecs);
|
|
842
848
|
}
|
|
843
849
|
}
|
|
@@ -1043,7 +1049,7 @@ shaka.hls.HlsParser = class {
|
|
|
1043
1049
|
shaka.log.debug('Guessing audio-only.');
|
|
1044
1050
|
type = ContentType.AUDIO;
|
|
1045
1051
|
ignoreStream = true;
|
|
1046
|
-
} else if (res.video.length) {
|
|
1052
|
+
} else if (res.video.length && !res.audio.length) {
|
|
1047
1053
|
// There are associated video streams. Assume this is audio.
|
|
1048
1054
|
shaka.log.debug('Guessing audio-only.');
|
|
1049
1055
|
type = ContentType.AUDIO;
|
|
@@ -1659,9 +1665,16 @@ shaka.hls.HlsParser = class {
|
|
|
1659
1665
|
/** @type {!Map.<number, number>} */
|
|
1660
1666
|
const mediaSequenceToStartTime = new Map();
|
|
1661
1667
|
|
|
1668
|
+
const prevLowLatencyMode = this.lowLatencyMode_;
|
|
1662
1669
|
const segments = this.createSegments_(verbatimMediaPlaylistUri, playlist,
|
|
1663
1670
|
type, mimeType, mediaSequenceToStartTime, mediaVariables, codecs);
|
|
1664
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
|
+
|
|
1665
1678
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1666
1679
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1667
1680
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -1790,8 +1803,11 @@ shaka.hls.HlsParser = class {
|
|
|
1790
1803
|
shaka.util.Error.Code.HLS_AES_128_INVALID_KEY_LENGTH);
|
|
1791
1804
|
}
|
|
1792
1805
|
|
|
1806
|
+
const algorithm = {
|
|
1807
|
+
name: 'AES-CBC',
|
|
1808
|
+
};
|
|
1793
1809
|
keyInfo.cryptoKey = await window.crypto.subtle.importKey(
|
|
1794
|
-
'raw', keyResponse.data,
|
|
1810
|
+
'raw', keyResponse.data, algorithm, true, ['decrypt']);
|
|
1795
1811
|
keyInfo.fetchKey = undefined; // No longer needed.
|
|
1796
1812
|
};
|
|
1797
1813
|
|
|
@@ -2872,6 +2888,7 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2872
2888
|
'm4f': 'application/mp4',
|
|
2873
2889
|
'cmft': 'application/mp4',
|
|
2874
2890
|
'vtt': 'text/vtt',
|
|
2891
|
+
'webvtt': 'txt/vtt',
|
|
2875
2892
|
'ttml': 'application/ttml+xml',
|
|
2876
2893
|
};
|
|
2877
2894
|
|
|
@@ -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,
|
|
@@ -545,6 +545,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
545
545
|
}
|
|
546
546
|
|
|
547
547
|
if (this.transmuxers_[contentType]) {
|
|
548
|
+
// When seeked we should reset the transmuxer captionstreams
|
|
549
|
+
// so it does not ignores the captions from previous segments
|
|
550
|
+
if (seeked) {
|
|
551
|
+
this.transmuxers_[contentType].resetCaptions();
|
|
552
|
+
}
|
|
553
|
+
|
|
548
554
|
const transmuxedData =
|
|
549
555
|
await this.transmuxers_[contentType].transmux(data);
|
|
550
556
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
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.
|
|
@@ -90,6 +98,14 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
90
98
|
|
|
91
99
|
/** @private {shaka.util.EventManager} */
|
|
92
100
|
this.eventManager_ = new shaka.util.EventManager();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @override */
|
|
104
|
+
ready() {
|
|
105
|
+
goog.asserts.assert(
|
|
106
|
+
this.mediaElement_ != null,
|
|
107
|
+
'Playhead should not be released before calling ready()',
|
|
108
|
+
);
|
|
93
109
|
|
|
94
110
|
// We listen for the loaded-data-event so that we know when we can
|
|
95
111
|
// interact with |currentTime|.
|
|
@@ -102,6 +118,7 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
102
118
|
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
|
|
103
119
|
this.started_ = true;
|
|
104
120
|
});
|
|
121
|
+
|
|
105
122
|
const currentTime = this.mediaElement_.currentTime;
|
|
106
123
|
// Using the currentTime allows using a negative number in Live HLS
|
|
107
124
|
const newTime = Math.max(0, currentTime + this.startTime_);
|
|
@@ -239,7 +256,12 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
239
256
|
/** @type {shaka.util.Timer} */
|
|
240
257
|
this.checkWindowTimer_ = new shaka.util.Timer(() => {
|
|
241
258
|
this.onPollWindow_();
|
|
242
|
-
})
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** @override */
|
|
263
|
+
ready() {
|
|
264
|
+
this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
|
|
243
265
|
}
|
|
244
266
|
|
|
245
267
|
/** @override */
|
|
@@ -1403,16 +1403,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1403
1403
|
this.mediaStates_.delete(ContentType.TEXT);
|
|
1404
1404
|
} else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
|
|
1405
1405
|
this.handleQuotaExceeded_(mediaState, error);
|
|
1406
|
-
} else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
|
|
1407
|
-
error.data && error.data[1] == 404) {
|
|
1408
|
-
// The segment could not be found, does not exist, or is not available.
|
|
1409
|
-
// In any case just try again.
|
|
1410
|
-
// The current segment is not available. Schedule another update to
|
|
1411
|
-
// fetch the segment again.
|
|
1412
|
-
shaka.log.v2(logPrefix, 'segment not available.');
|
|
1413
|
-
mediaState.performingUpdate = false;
|
|
1414
|
-
mediaState.updateTimer = null;
|
|
1415
|
-
this.scheduleUpdate_(mediaState, 1);
|
|
1416
1406
|
} else {
|
|
1417
1407
|
shaka.log.error(logPrefix, 'failed fetch and append: code=' +
|
|
1418
1408
|
error.code);
|
|
@@ -1455,9 +1445,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1455
1445
|
|
|
1456
1446
|
/**
|
|
1457
1447
|
* Clear per-stream error states and retry any failed streams.
|
|
1448
|
+
* @param {number} delaySeconds
|
|
1458
1449
|
* @return {boolean} False if unable to retry.
|
|
1459
1450
|
*/
|
|
1460
|
-
retry() {
|
|
1451
|
+
retry(delaySeconds) {
|
|
1461
1452
|
if (this.destroyer_.destroyed()) {
|
|
1462
1453
|
shaka.log.error('Unable to retry after StreamingEngine is destroyed!');
|
|
1463
1454
|
return false;
|
|
@@ -1474,7 +1465,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1474
1465
|
if (mediaState.hasError) {
|
|
1475
1466
|
shaka.log.info(logPrefix, 'Retrying after failure...');
|
|
1476
1467
|
mediaState.hasError = false;
|
|
1477
|
-
this.scheduleUpdate_(mediaState,
|
|
1468
|
+
this.scheduleUpdate_(mediaState, delaySeconds);
|
|
1478
1469
|
}
|
|
1479
1470
|
}
|
|
1480
1471
|
|
package/lib/media/transmuxer.js
CHANGED
|
@@ -182,6 +182,12 @@ shaka.media.Transmuxer = class {
|
|
|
182
182
|
return this.transmuxPromise_;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Reset captions from Transport stream to MP4, using the mux.js library.
|
|
187
|
+
*/
|
|
188
|
+
resetCaptions() {
|
|
189
|
+
this.muxTransmuxer_.resetCaptions();
|
|
190
|
+
}
|
|
185
191
|
|
|
186
192
|
/**
|
|
187
193
|
* Handles the 'data' event of the transmuxer.
|
package/lib/player.js
CHANGED
|
@@ -2062,6 +2062,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2062
2062
|
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
2063
2063
|
}
|
|
2064
2064
|
|
|
2065
|
+
this.playhead_.ready();
|
|
2066
|
+
|
|
2065
2067
|
// Decide if text should be shown automatically.
|
|
2066
2068
|
// similar to video/audio track, we would skip switch initial text track
|
|
2067
2069
|
// if user already pick text track (via selectTextTrack api)
|
|
@@ -2399,6 +2401,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2399
2401
|
HTMLMediaElement.HAVE_METADATA,
|
|
2400
2402
|
this.loadEventManager_,
|
|
2401
2403
|
() => {
|
|
2404
|
+
this.playhead_.ready();
|
|
2402
2405
|
fullyLoaded.resolve();
|
|
2403
2406
|
});
|
|
2404
2407
|
|
|
@@ -4841,12 +4844,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4841
4844
|
* If the player has loaded content, and streaming seen an error, but the
|
|
4842
4845
|
* could not resume streaming, this will return <code>false</code>.
|
|
4843
4846
|
*
|
|
4847
|
+
* @param {number=} retryDelaySeconds
|
|
4844
4848
|
* @return {boolean}
|
|
4845
4849
|
* @export
|
|
4846
4850
|
*/
|
|
4847
|
-
retryStreaming() {
|
|
4851
|
+
retryStreaming(retryDelaySeconds = 0.1) {
|
|
4848
4852
|
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
|
|
4849
|
-
this.streamingEngine_.retry() :
|
|
4853
|
+
this.streamingEngine_.retry(retryDelaySeconds) :
|
|
4850
4854
|
false;
|
|
4851
4855
|
}
|
|
4852
4856
|
|
|
@@ -4942,17 +4946,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4942
4946
|
* @private
|
|
4943
4947
|
*/
|
|
4944
4948
|
defaultStreamingFailureCallback_(error) {
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4949
|
+
// For live streams, we retry streaming automatically for certain errors.
|
|
4950
|
+
// For VOD streams, all streaming failures are fatal.
|
|
4951
|
+
if (!this.isLive()) {
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4950
4954
|
|
|
4951
|
-
|
|
4952
|
-
|
|
4955
|
+
let retryDelaySeconds = null;
|
|
4956
|
+
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
|
|
4957
|
+
error.code == shaka.util.Error.Code.HTTP_ERROR) {
|
|
4958
|
+
// These errors can be near-instant, so delay a bit before retrying.
|
|
4959
|
+
retryDelaySeconds = 1;
|
|
4960
|
+
} else if (error.code == shaka.util.Error.Code.TIMEOUT) {
|
|
4961
|
+
// We already waited for a timeout, so retry quickly.
|
|
4962
|
+
retryDelaySeconds = 0.1;
|
|
4963
|
+
}
|
|
4953
4964
|
|
|
4965
|
+
if (retryDelaySeconds != null) {
|
|
4966
|
+
error.severity = shaka.util.Error.Severity.RECOVERABLE;
|
|
4954
4967
|
shaka.log.warning('Live streaming error. Retrying automatically...');
|
|
4955
|
-
this.retryStreaming();
|
|
4968
|
+
this.retryStreaming(retryDelaySeconds);
|
|
4956
4969
|
}
|
|
4957
4970
|
}
|
|
4958
4971
|
|
|
@@ -6582,7 +6595,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6582
6595
|
* @define {string} A version number taken from git at compile time.
|
|
6583
6596
|
* @export
|
|
6584
6597
|
*/
|
|
6585
|
-
shaka.Player.version = 'v4.2.
|
|
6598
|
+
shaka.Player.version = 'v4.2.5-uncompiled';
|
|
6586
6599
|
|
|
6587
6600
|
// Initialize the deprecation system using the version string we just set
|
|
6588
6601
|
// 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);
|