shaka-player 4.3.14 → 4.3.16
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/appspot.yaml +4 -4
- package/.github/workflows/build-and-test.yaml +23 -10
- package/.github/workflows/custom-actions/set-commit-status/action.yaml +29 -7
- package/.github/workflows/demo-version-index.yaml +3 -3
- package/.github/workflows/nightly-demo.yaml +4 -4
- package/.github/workflows/release-please.yaml +25 -24
- package/.github/workflows/report-incremental-coverage.yaml +1 -1
- package/.github/workflows/selenium-lab-tests.yaml +30 -18
- package/.github/workflows/shaka-bot-commands/lib.sh +37 -12
- package/.github/workflows/shaka-bot-commands/main.sh +4 -2
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/talk-to-shaka-bot.yaml +2 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/.github/workflows/update-screenshots.yaml +25 -38
- package/CHANGELOG.md +37 -0
- package/demo/demo.less +1 -5
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +0 -1
- package/dist/locales.js +32 -32
- package/dist/shaka-player.compiled.d.ts +6 -10
- package/dist/shaka-player.compiled.debug.d.ts +6 -10
- package/dist/shaka-player.compiled.debug.externs.js +13 -11
- package/dist/shaka-player.compiled.debug.js +222 -217
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +13 -11
- package/dist/shaka-player.compiled.js +855 -853
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +6 -10
- package/dist/shaka-player.ui.debug.d.ts +6 -10
- package/dist/shaka-player.ui.debug.externs.js +13 -11
- package/dist/shaka-player.ui.debug.js +171 -167
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +13 -11
- package/dist/shaka-player.ui.js +950 -948
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/faq.md +25 -0
- package/docs/tutorials/offline.md +3 -0
- package/docs/tutorials/ui.md +4 -4
- package/externs/shaka/player.js +32 -19
- package/lib/cast/cast_proxy.js +1 -3
- package/lib/cast/cast_sender.js +8 -11
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/dash/dash_parser.js +3 -0
- package/lib/dash/segment_template.js +5 -0
- package/lib/deprecate/deprecate.js +1 -1
- package/lib/hls/hls_parser.js +34 -17
- package/lib/media/drm_engine.js +17 -3
- package/lib/media/manifest_parser.js +2 -1
- package/lib/media/presentation_timeline.js +23 -0
- package/lib/media/segment_index.js +10 -0
- package/lib/media/streaming_engine.js +1 -13
- package/lib/player.js +19 -5
- package/lib/polyfill/mediasource.js +5 -36
- package/lib/text/ui_text_displayer.js +10 -1
- package/lib/text/vtt_text_parser.js +2 -1
- package/lib/util/error.js +1 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/stream_utils.js +8 -5
- package/package.json +2 -2
- package/test/cast/cast_proxy_unit.js +0 -46
- package/test/cast/cast_sender_unit.js +26 -26
- package/test/cea/cea608_memory_unit.js +3 -2
- package/test/hls/hls_live_unit.js +52 -0
- package/test/hls/hls_parser_unit.js +28 -0
- package/test/media/media_source_engine_integration.js +1 -1
- package/test/player_integration.js +9 -1
- package/test/player_load_graph_integration.js +13 -7
- package/test/player_src_equals_integration.js +2 -2
- package/test/player_unit.js +22 -4
- package/test/text/vtt_text_parser_unit.js +31 -0
- package/ui/less/containers.less +1 -0
- package/lib/polyfill/storage_estimate.js +0 -57
package/docs/tutorials/faq.md
CHANGED
|
@@ -263,6 +263,31 @@ so don't declare it using a ref(), and if you put your player instance into a
|
|
|
263
263
|
data() object, you can prefix the property name with "$" or "_" to make Vue not
|
|
264
264
|
proxy them.
|
|
265
265
|
|
|
266
|
+
<hr>
|
|
267
|
+
|
|
268
|
+
**Q:** My player keeps buffering after each chunk when streaming HLS live.
|
|
269
|
+
|
|
270
|
+
**A:** It is recommended to have more than 3 chunks in your chunklist. If this
|
|
271
|
+
is not possible, then you have to change the presentationDelay value. We offer
|
|
272
|
+
the following configuration:
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
player.configure('manifest.hls.liveSegmentsDelay', 1);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
<hr>
|
|
279
|
+
|
|
280
|
+
**Q:** Why can I not find the new version of Shaka Player on
|
|
281
|
+
ajax.googleapis.com?
|
|
282
|
+
|
|
283
|
+
**A:** When a new version of Shaka Player is released on GitHub, it has to
|
|
284
|
+
undergo a brief manual review process before it can be uploaded to Google's
|
|
285
|
+
servers. After approval, the upload process then usually takes several hours.
|
|
286
|
+
Therefore it is not unusual for a day or two to pass before a new version of
|
|
287
|
+
Shaka Player is hosted on Google's ajax.googleapis.com website. We ask for your
|
|
288
|
+
patience in these matters.
|
|
289
|
+
|
|
290
|
+
|
|
266
291
|
[386]: https://github.com/shaka-project/shaka-player/issues/386#issuecomment-227898001
|
|
267
292
|
[489]: https://github.com/shaka-project/shaka-player/issues/489#issuecomment-240466224
|
|
268
293
|
[743]: https://github.com/shaka-project/shaka-player/issues/743
|
|
@@ -339,6 +339,9 @@ you want.
|
|
|
339
339
|
At this point, the content is now stored offline and it's ready to be played.
|
|
340
340
|
Next we will add functionality to play offline content.
|
|
341
341
|
|
|
342
|
+
Note: If you call `storage.store` twice with the same manifestUri as input,
|
|
343
|
+
you'll download the same manifestUri twice.
|
|
344
|
+
|
|
342
345
|
## Playing Offline Content
|
|
343
346
|
|
|
344
347
|
Now that we have stored some content, we want to play it. To do this, resolve
|
package/docs/tutorials/ui.md
CHANGED
|
@@ -16,9 +16,9 @@ Set up controls with HTML data attributes:
|
|
|
16
16
|
<!DOCTYPE html>
|
|
17
17
|
<html>
|
|
18
18
|
<head>
|
|
19
|
-
<!-- Shaka Player
|
|
19
|
+
<!-- Shaka Player UI compiled library: -->
|
|
20
20
|
<script src="dist/shaka-player.ui.js"></script>
|
|
21
|
-
<!-- Shaka Player
|
|
21
|
+
<!-- Shaka Player UI compiled library default CSS: -->
|
|
22
22
|
<link rel="stylesheet" type="text/css" href="dist/controls.css">
|
|
23
23
|
<!-- Chromecast SDK (if you want Chromecast support for your app): -->
|
|
24
24
|
<script defer src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
|
|
@@ -52,7 +52,7 @@ async function init() {
|
|
|
52
52
|
const controls = ui.getControls();
|
|
53
53
|
const player = controls.getPlayer();
|
|
54
54
|
|
|
55
|
-
// Attach player and
|
|
55
|
+
// Attach player and UI to the window to make it easy to access in the JS console.
|
|
56
56
|
window.player = player;
|
|
57
57
|
window.ui = ui;
|
|
58
58
|
|
|
@@ -145,7 +145,7 @@ casting to an Android receiver app.
|
|
|
145
145
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
146
146
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
147
147
|
</div
|
|
148
|
-
|
|
148
|
+
```
|
|
149
149
|
|
|
150
150
|
#### Providing source(s) for auto load.
|
|
151
151
|
|
package/externs/shaka/player.js
CHANGED
|
@@ -96,57 +96,70 @@ shaka.extern.StateChange;
|
|
|
96
96
|
* again.
|
|
97
97
|
*
|
|
98
98
|
* @property {number} width
|
|
99
|
-
* The width of the current video track.
|
|
99
|
+
* The width of the current video track. If nothing is loaded or the content
|
|
100
|
+
* is audio-only, NaN.
|
|
100
101
|
* @property {number} height
|
|
101
|
-
* The height of the current video track.
|
|
102
|
+
* The height of the current video track. If nothing is loaded or the content
|
|
103
|
+
* is audio-only, NaN.
|
|
102
104
|
* @property {number} streamBandwidth
|
|
103
105
|
* The bandwidth required for the current streams (total, in bit/sec).
|
|
104
|
-
* It takes into account the playbackrate.
|
|
106
|
+
* It takes into account the playbackrate. If nothing is loaded, NaN.
|
|
105
107
|
*
|
|
106
108
|
* @property {number} decodedFrames
|
|
107
|
-
* The total number of frames decoded by the Player.
|
|
108
|
-
*
|
|
109
|
+
* The total number of frames decoded by the Player. If not reported by the
|
|
110
|
+
* browser, NaN.
|
|
109
111
|
* @property {number} droppedFrames
|
|
110
|
-
* The total number of frames dropped by the Player.
|
|
111
|
-
*
|
|
112
|
+
* The total number of frames dropped by the Player. If not reported by the
|
|
113
|
+
* browser, NaN.
|
|
112
114
|
* @property {number} corruptedFrames
|
|
113
|
-
* The total number of corrupted frames dropped by the browser.
|
|
114
|
-
*
|
|
115
|
+
* The total number of corrupted frames dropped by the browser. If not
|
|
116
|
+
* reported by the browser, NaN.
|
|
115
117
|
* @property {number} estimatedBandwidth
|
|
116
|
-
* The current estimated network bandwidth (in bit/sec).
|
|
118
|
+
* The current estimated network bandwidth (in bit/sec). If no estimate
|
|
119
|
+
* available, NaN.
|
|
117
120
|
*
|
|
118
121
|
* @property {number} gapsJumped
|
|
119
122
|
* The total number of playback gaps jumped by the GapJumpingController.
|
|
123
|
+
* If nothing is loaded, NaN.
|
|
120
124
|
* @property {number} stallsDetected
|
|
121
125
|
* The total number of playback stalls detected by the StallDetector.
|
|
126
|
+
* If nothing is loaded, NaN.
|
|
122
127
|
*
|
|
123
128
|
* @property {number} completionPercent
|
|
124
129
|
* This is the greatest completion percent that the user has experienced in
|
|
125
|
-
* playback.
|
|
126
|
-
*
|
|
130
|
+
* playback. Also known as the "high water mark". If nothing is loaded, or
|
|
131
|
+
* the stream is live (and therefore indefinite), NaN.
|
|
127
132
|
* @property {number} loadLatency
|
|
128
133
|
* This is the number of seconds it took for the video element to have enough
|
|
129
134
|
* data to begin playback. This is measured from the time load() is called to
|
|
130
135
|
* the time the <code>'loadeddata'</code> event is fired by the media element.
|
|
136
|
+
* If nothing is loaded, NaN.
|
|
131
137
|
* @property {number} manifestTimeSeconds
|
|
132
138
|
* The amount of time it took to download and parse the manifest.
|
|
139
|
+
* If nothing is loaded, NaN.
|
|
133
140
|
* @property {number} drmTimeSeconds
|
|
134
141
|
* The amount of time it took to download the first drm key, and load that key
|
|
135
|
-
* into the drm system.
|
|
142
|
+
* into the drm system. If nothing is loaded or DRM is not in use, NaN.
|
|
136
143
|
* @property {number} playTime
|
|
137
|
-
* The total time spent in a playing state in seconds.
|
|
144
|
+
* The total time spent in a playing state in seconds. If nothing is loaded,
|
|
145
|
+
* NaN.
|
|
138
146
|
* @property {number} pauseTime
|
|
139
|
-
* The total time spent in a paused state in seconds.
|
|
147
|
+
* The total time spent in a paused state in seconds. If nothing is loaded,
|
|
148
|
+
* NaN.
|
|
140
149
|
* @property {number} bufferingTime
|
|
141
|
-
* The total time spent in a buffering state in seconds.
|
|
150
|
+
* The total time spent in a buffering state in seconds. If nothing is
|
|
151
|
+
* loaded, NaN.
|
|
142
152
|
* @property {number} licenseTime
|
|
143
|
-
* The time spent on license requests during this session in seconds
|
|
153
|
+
* The time spent on license requests during this session in seconds. If DRM
|
|
154
|
+
* is not in use, NaN.
|
|
144
155
|
* @property {number} liveLatency
|
|
145
156
|
* The time between the capturing of a frame and the end user having it
|
|
146
|
-
* displayed on their screen.
|
|
157
|
+
* displayed on their screen. If nothing is loaded or the content is VOD,
|
|
158
|
+
* NaN.
|
|
147
159
|
*
|
|
148
160
|
* @property {number} maxSegmentDuration
|
|
149
|
-
* The presentation's max segment duration in seconds
|
|
161
|
+
* The presentation's max segment duration in seconds. If nothing is loaded,
|
|
162
|
+
* NaN.
|
|
150
163
|
*
|
|
151
164
|
* @property {!Array.<shaka.extern.TrackChoice>} switchHistory
|
|
152
165
|
* A history of the stream changes.
|
package/lib/cast/cast_proxy.js
CHANGED
|
@@ -188,12 +188,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
188
188
|
* @export
|
|
189
189
|
*/
|
|
190
190
|
async cast() {
|
|
191
|
-
const initState = this.getInitState_();
|
|
192
|
-
|
|
193
191
|
// TODO: transfer manually-selected tracks?
|
|
194
192
|
// TODO: transfer side-loaded text tracks?
|
|
195
193
|
|
|
196
|
-
await this.sender_.cast(
|
|
194
|
+
await this.sender_.cast();
|
|
197
195
|
if (!this.localPlayer_) {
|
|
198
196
|
// We've already been destroyed.
|
|
199
197
|
return;
|
package/lib/cast/cast_sender.js
CHANGED
|
@@ -261,12 +261,10 @@ shaka.cast.CastSender = class {
|
|
|
261
261
|
|
|
262
262
|
|
|
263
263
|
/**
|
|
264
|
-
* @param {shaka.cast.CastUtils.InitStateType} initState Video and player
|
|
265
|
-
* state to be sent to the receiver.
|
|
266
264
|
* @return {!Promise} Resolved when connected to a receiver. Rejected if the
|
|
267
265
|
* connection fails or is canceled by the user.
|
|
268
266
|
*/
|
|
269
|
-
async cast(
|
|
267
|
+
async cast() {
|
|
270
268
|
if (!this.apiReady_) {
|
|
271
269
|
throw new shaka.util.Error(
|
|
272
270
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
@@ -288,7 +286,7 @@ shaka.cast.CastSender = class {
|
|
|
288
286
|
|
|
289
287
|
this.castPromise_ = new shaka.util.PublicPromise();
|
|
290
288
|
chrome.cast.requestSession(
|
|
291
|
-
(session) => this.onSessionInitiated_(
|
|
289
|
+
(session) => this.onSessionInitiated_(session),
|
|
292
290
|
(error) => this.onConnectionError_(error));
|
|
293
291
|
await this.castPromise_;
|
|
294
292
|
}
|
|
@@ -303,10 +301,9 @@ shaka.cast.CastSender = class {
|
|
|
303
301
|
if (!this.isCasting_) {
|
|
304
302
|
return;
|
|
305
303
|
}
|
|
306
|
-
const initState = this.onInitStateRequired_();
|
|
307
304
|
|
|
308
305
|
chrome.cast.requestSession(
|
|
309
|
-
(session) => this.onSessionInitiated_(
|
|
306
|
+
(session) => this.onSessionInitiated_(session),
|
|
310
307
|
(error) => this.onConnectionError_(error));
|
|
311
308
|
}
|
|
312
309
|
|
|
@@ -401,12 +398,14 @@ shaka.cast.CastSender = class {
|
|
|
401
398
|
|
|
402
399
|
|
|
403
400
|
/**
|
|
404
|
-
* @param {shaka.cast.CastUtils.InitStateType} initState
|
|
405
401
|
* @param {chrome.cast.Session} session
|
|
406
402
|
* @private
|
|
407
403
|
*/
|
|
408
|
-
onSessionInitiated_(
|
|
404
|
+
onSessionInitiated_(session) {
|
|
409
405
|
shaka.log.debug('CastSender: onSessionInitiated');
|
|
406
|
+
|
|
407
|
+
const initState = this.onInitStateRequired_();
|
|
408
|
+
|
|
410
409
|
this.onSessionCreated_(session);
|
|
411
410
|
|
|
412
411
|
this.sendMessage_({
|
|
@@ -529,12 +528,10 @@ shaka.cast.CastSender = class {
|
|
|
529
528
|
onExistingSessionJoined_(session) {
|
|
530
529
|
shaka.log.debug('CastSender: onExistingSessionJoined');
|
|
531
530
|
|
|
532
|
-
const initState = this.onInitStateRequired_();
|
|
533
|
-
|
|
534
531
|
this.castPromise_ = new shaka.util.PublicPromise();
|
|
535
532
|
this.hasJoinedExistingSession_ = true;
|
|
536
533
|
|
|
537
|
-
this.onSessionInitiated_(
|
|
534
|
+
this.onSessionInitiated_(session);
|
|
538
535
|
}
|
|
539
536
|
|
|
540
537
|
|
package/lib/cea/cea608_memory.js
CHANGED
|
@@ -288,7 +288,7 @@ shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars = new Map([
|
|
|
288
288
|
*/
|
|
289
289
|
shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars = new Map([
|
|
290
290
|
[0x30, '®'], [0x31, '°'], [0x32, '½'], [0x33, '¿'], [0x34, '™'], [0x35, '¢'],
|
|
291
|
-
[0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, '
|
|
291
|
+
[0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, ' '], [0x3a, 'è'], [0x3b, 'â'],
|
|
292
292
|
[0x3c, 'ê'], [0x3d, 'î'], [0x3e, 'ô'], [0x3f, 'û'],
|
|
293
293
|
]);
|
|
294
294
|
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -521,6 +521,9 @@ shaka.dash.DashParser = class {
|
|
|
521
521
|
// best latency
|
|
522
522
|
if (this.lowLatencyMode_) {
|
|
523
523
|
minBufferTime = 0;
|
|
524
|
+
const presentationDelay = suggestedPresentationDelay != null ?
|
|
525
|
+
suggestedPresentationDelay : this.config_.defaultPresentationDelay;
|
|
526
|
+
presentationTimeline.setDelay(presentationDelay);
|
|
524
527
|
}
|
|
525
528
|
|
|
526
529
|
// These steps are not done on manifest update.
|
|
@@ -372,6 +372,11 @@ shaka.dash.SegmentTemplate = class {
|
|
|
372
372
|
Math.ceil(availablePeriodTimes[1] / segmentDuration) - 1,
|
|
373
373
|
];
|
|
374
374
|
|
|
375
|
+
// For Low Latency we can request the partial current position.
|
|
376
|
+
if (context.representation.availabilityTimeOffset) {
|
|
377
|
+
availablePeriodPositions[1]++;
|
|
378
|
+
}
|
|
379
|
+
|
|
375
380
|
// In absolute positions.
|
|
376
381
|
const availablePresentationPositions =
|
|
377
382
|
availablePeriodPositions.map((x) => x + startNumber);
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -479,7 +479,7 @@ shaka.hls.HlsParser = class {
|
|
|
479
479
|
if (goog.DEBUG) {
|
|
480
480
|
const firstSequenceStartTime =
|
|
481
481
|
mediaSequenceToStartTime.values().next().value;
|
|
482
|
-
|
|
482
|
+
shaka.log.warning(
|
|
483
483
|
firstSequenceStartTime == segment0.startTime,
|
|
484
484
|
'Sequence number map is not ordered as expected!');
|
|
485
485
|
}
|
|
@@ -513,16 +513,18 @@ shaka.hls.HlsParser = class {
|
|
|
513
513
|
// Drop any earlier references.
|
|
514
514
|
const numSegmentsToDrop =
|
|
515
515
|
this.minSequenceNumber_ - streamInfo.firstSequenceNumber;
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
516
|
+
if (numSegmentsToDrop > 0) {
|
|
517
|
+
segmentIndex.dropFirstReferences(numSegmentsToDrop);
|
|
518
|
+
|
|
519
|
+
// Now adjust timestamps back to begin at 0.
|
|
520
|
+
const segmentN = segmentIndex.earliestReference();
|
|
521
|
+
if (segmentN) {
|
|
522
|
+
const streamOffset = -segmentN.startTime;
|
|
523
|
+
// Modify all SegmentReferences equally.
|
|
524
|
+
streamInfo.stream.segmentIndex.offset(streamOffset);
|
|
525
|
+
// Update other parts of streamInfo the same way.
|
|
526
|
+
this.offsetStreamInfo_(streamInfo, streamOffset);
|
|
527
|
+
}
|
|
526
528
|
}
|
|
527
529
|
}
|
|
528
530
|
}
|
|
@@ -848,17 +850,17 @@ shaka.hls.HlsParser = class {
|
|
|
848
850
|
}
|
|
849
851
|
|
|
850
852
|
if (!this.presentationTimeline_.isStartTimeLocked()) {
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
853
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
854
|
+
if (!streamInfo.stream.segmentIndex) {
|
|
855
|
+
continue; // Not active.
|
|
856
|
+
}
|
|
855
857
|
if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
|
|
856
858
|
continue;
|
|
857
859
|
}
|
|
858
860
|
const firstReference = streamInfo.stream.segmentIndex.get(0);
|
|
859
861
|
if (firstReference && firstReference.syncTime) {
|
|
860
862
|
const syncTime = firstReference.syncTime;
|
|
861
|
-
this.presentationTimeline_.
|
|
863
|
+
this.presentationTimeline_.setInitialProgramDateTime(syncTime);
|
|
862
864
|
}
|
|
863
865
|
}
|
|
864
866
|
}
|
|
@@ -3382,7 +3384,22 @@ shaka.hls.HlsParser = class {
|
|
|
3382
3384
|
* @return {?shaka.extern.DrmInfo}
|
|
3383
3385
|
* @private
|
|
3384
3386
|
*/
|
|
3385
|
-
static identityDrmParser_(drmTag) {
|
|
3387
|
+
static identityDrmParser_(drmTag, mimeType) {
|
|
3388
|
+
if (mimeType == 'video/mp2t') {
|
|
3389
|
+
throw new shaka.util.Error(
|
|
3390
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
3391
|
+
shaka.util.Error.Category.MANIFEST,
|
|
3392
|
+
shaka.util.Error.Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
if (shaka.util.Platform.isMediaKeysPolyfilled()) {
|
|
3396
|
+
throw new shaka.util.Error(
|
|
3397
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
3398
|
+
shaka.util.Error.Category.MANIFEST,
|
|
3399
|
+
shaka.util.Error.Code
|
|
3400
|
+
.HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED);
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3386
3403
|
const method = drmTag.getRequiredAttrValue('METHOD');
|
|
3387
3404
|
const VALID_METHODS = ['SAMPLE-AES', 'SAMPLE-AES-CTR'];
|
|
3388
3405
|
if (!VALID_METHODS.includes(method)) {
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1425,6 +1425,9 @@ shaka.media.DrmEngine = class {
|
|
|
1425
1425
|
const req = this.playerInterface_.netEngine.request(requestType, request);
|
|
1426
1426
|
response = await req.promise;
|
|
1427
1427
|
} catch (error) {
|
|
1428
|
+
if (this.destroyer_.destroyed()) {
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1428
1431
|
// Request failed!
|
|
1429
1432
|
goog.asserts.assert(error instanceof shaka.util.Error,
|
|
1430
1433
|
'Wrong NetworkingEngine error type!');
|
|
@@ -1433,9 +1436,20 @@ shaka.media.DrmEngine = class {
|
|
|
1433
1436
|
shaka.util.Error.Category.DRM,
|
|
1434
1437
|
shaka.util.Error.Code.LICENSE_REQUEST_FAILED,
|
|
1435
1438
|
error);
|
|
1436
|
-
this.
|
|
1437
|
-
|
|
1438
|
-
metadata.updatePromise
|
|
1439
|
+
if (this.activeSessions_.size == 1) {
|
|
1440
|
+
this.onError_(shakaErr);
|
|
1441
|
+
if (metadata && metadata.updatePromise) {
|
|
1442
|
+
metadata.updatePromise.reject(shakaErr);
|
|
1443
|
+
}
|
|
1444
|
+
} else {
|
|
1445
|
+
if (metadata && metadata.updatePromise) {
|
|
1446
|
+
metadata.updatePromise.reject(shakaErr);
|
|
1447
|
+
}
|
|
1448
|
+
this.activeSessions_.delete(session);
|
|
1449
|
+
if (this.areAllSessionsLoaded_()) {
|
|
1450
|
+
this.allSessionsLoaded_.resolve();
|
|
1451
|
+
this.keyStatusTimer_.tickAfter(/* seconds= */ 0.1);
|
|
1452
|
+
}
|
|
1439
1453
|
}
|
|
1440
1454
|
return;
|
|
1441
1455
|
}
|
|
@@ -96,6 +96,9 @@ shaka.media.PresentationTimeline = class {
|
|
|
96
96
|
|
|
97
97
|
/** @private {boolean} */
|
|
98
98
|
this.startTimeLocked_ = false;
|
|
99
|
+
|
|
100
|
+
/** @private {?number} */
|
|
101
|
+
this.initialProgramDateTime_ = null;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
|
|
@@ -299,6 +302,26 @@ shaka.media.PresentationTimeline = class {
|
|
|
299
302
|
}
|
|
300
303
|
|
|
301
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Sets the initial program date time.
|
|
307
|
+
*
|
|
308
|
+
* @param {number} initialProgramDateTime
|
|
309
|
+
* @export
|
|
310
|
+
*/
|
|
311
|
+
setInitialProgramDateTime(initialProgramDateTime) {
|
|
312
|
+
this.initialProgramDateTime_ = initialProgramDateTime;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @return {?number} The initial program date time in seconds.
|
|
318
|
+
* @export
|
|
319
|
+
*/
|
|
320
|
+
getInitialProgramDateTime() {
|
|
321
|
+
return this.initialProgramDateTime_;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
302
325
|
/**
|
|
303
326
|
* Gives PresentationTimeline a Stream's minimum segment start time.
|
|
304
327
|
*
|
|
@@ -659,6 +659,16 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
659
659
|
this.indexes_ = [];
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
/**
|
|
663
|
+
* @override
|
|
664
|
+
* @export
|
|
665
|
+
*/
|
|
666
|
+
forEachTopLevelReference(fn) {
|
|
667
|
+
for (const index of this.indexes_) {
|
|
668
|
+
index.forEachTopLevelReference(fn);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
662
672
|
/**
|
|
663
673
|
* @override
|
|
664
674
|
* @export
|
|
@@ -855,7 +855,6 @@ shaka.media.StreamingEngine = class {
|
|
|
855
855
|
// The playhead might be seeking on startup, if a start time is set, so
|
|
856
856
|
// start "seeked" as true.
|
|
857
857
|
seeked: true,
|
|
858
|
-
needsResync: false,
|
|
859
858
|
recovering: false,
|
|
860
859
|
hasError: false,
|
|
861
860
|
operation: null,
|
|
@@ -1179,11 +1178,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1179
1178
|
shaka.log.v1(
|
|
1180
1179
|
logPrefix, 'looking up segment from new stream endTime:', time);
|
|
1181
1180
|
|
|
1182
|
-
// Using a new iterator means we need to resync the stream in sequence
|
|
1183
|
-
// mode. The buffered range might not align perfectly with the last
|
|
1184
|
-
// segment end time, so we may end up repeating a segment. Resyncing
|
|
1185
|
-
// makes this safe to do.
|
|
1186
|
-
mediaState.needsResync = true;
|
|
1187
1181
|
mediaState.segmentIterator =
|
|
1188
1182
|
mediaState.stream.segmentIndex.getIteratorForTime(time);
|
|
1189
1183
|
const ref = mediaState.segmentIterator &&
|
|
@@ -1707,9 +1701,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1707
1701
|
// Across discontinuity bounds, we should resync timestamps for
|
|
1708
1702
|
// sequence mode playbacks. The next segment appended should
|
|
1709
1703
|
// land at its theoretical timestamp from the segment index.
|
|
1710
|
-
if (reference.discontinuitySequence != lastDiscontinuitySequence
|
|
1711
|
-
mediaState.needsResync) {
|
|
1712
|
-
mediaState.needsResync = false;
|
|
1704
|
+
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
|
|
1713
1705
|
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1714
1706
|
mediaState.type, reference.startTime));
|
|
1715
1707
|
}
|
|
@@ -2348,7 +2340,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2348
2340
|
* clearBufferSafeMargin: number,
|
|
2349
2341
|
* clearingBuffer: boolean,
|
|
2350
2342
|
* seeked: boolean,
|
|
2351
|
-
* needsResync: boolean,
|
|
2352
2343
|
* adaptation: boolean,
|
|
2353
2344
|
* recovering: boolean,
|
|
2354
2345
|
* hasError: boolean,
|
|
@@ -2398,9 +2389,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2398
2389
|
* True indicates that the presentation just seeked.
|
|
2399
2390
|
* @property {boolean} adaptation
|
|
2400
2391
|
* True indicates that the presentation just automatically switched variants.
|
|
2401
|
-
* @property {boolean} needsResync
|
|
2402
|
-
* True indicates that the stream needs to be resynced in sequence mode,
|
|
2403
|
-
* regardless of discontinuity sequence.
|
|
2404
2392
|
* @property {boolean} recovering
|
|
2405
2393
|
* True indicates that the last segment was not appended because it could not
|
|
2406
2394
|
* fit in the buffer.
|
package/lib/player.js
CHANGED
|
@@ -2120,7 +2120,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2120
2120
|
this.abrManager_ = abrFactory();
|
|
2121
2121
|
if (typeof this.abrManager_.setMediaElement != 'function') {
|
|
2122
2122
|
shaka.Deprecate.deprecateFeature(5,
|
|
2123
|
-
'AbrManager',
|
|
2123
|
+
'AbrManager w/o setMediaElement',
|
|
2124
2124
|
'Please use an AbrManager with setMediaElement function.');
|
|
2125
2125
|
this.abrManager_.setMediaElement = () => {};
|
|
2126
2126
|
}
|
|
@@ -2574,6 +2574,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2574
2574
|
mediaElement.load();
|
|
2575
2575
|
}
|
|
2576
2576
|
|
|
2577
|
+
// In Safari using HLS won't load anything unless you call load()
|
|
2578
|
+
// explicitly, no matter the value of the preload attribute.
|
|
2579
|
+
// Note: this only happens when there are not autoplay.
|
|
2580
|
+
if (mediaElement.preload != 'none' && !mediaElement.autoplay &&
|
|
2581
|
+
shaka.util.MimeUtils.isHlsType(mimeType) &&
|
|
2582
|
+
shaka.util.Platform.safariVersion()) {
|
|
2583
|
+
mediaElement.load();
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2577
2586
|
// Set the load mode last so that we know that all our components are
|
|
2578
2587
|
// initialized.
|
|
2579
2588
|
this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS;
|
|
@@ -3748,12 +3757,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3748
3757
|
const currentTime = this.video_.currentTime;
|
|
3749
3758
|
const seekRange = this.seekRange();
|
|
3750
3759
|
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
3760
|
+
|
|
3761
|
+
// Cancel trick play if we hit the beginning or end of the seekable
|
|
3762
|
+
// (Sub-second accuracy not required here)
|
|
3751
3763
|
if (rate > 0) {
|
|
3752
|
-
if (currentTime >= seekRange.end) {
|
|
3764
|
+
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
3753
3765
|
this.cancelTrickPlay();
|
|
3754
3766
|
}
|
|
3755
3767
|
} else {
|
|
3756
|
-
if (currentTime <=
|
|
3768
|
+
if (Math.floor(currentTime) <=
|
|
3769
|
+
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
3757
3770
|
this.cancelTrickPlay();
|
|
3758
3771
|
}
|
|
3759
3772
|
}
|
|
@@ -4527,7 +4540,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4527
4540
|
|
|
4528
4541
|
if (this.manifest_) {
|
|
4529
4542
|
const timeline = this.manifest_.presentationTimeline;
|
|
4530
|
-
const startTime = timeline.
|
|
4543
|
+
const startTime = timeline.getInitialProgramDateTime() ||
|
|
4544
|
+
timeline.getPresentationStartTime();
|
|
4531
4545
|
return new Date(/* ms= */ (startTime + presentationTime) * 1000);
|
|
4532
4546
|
} else if (this.video_ && this.video_.getStartDate) {
|
|
4533
4547
|
// Apple's native HLS gives us getStartDate(), which is only available if
|
|
@@ -7032,7 +7046,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7032
7046
|
* @export
|
|
7033
7047
|
*/
|
|
7034
7048
|
// eslint-disable-next-line no-useless-concat
|
|
7035
|
-
shaka.Player.version = 'v4.3.
|
|
7049
|
+
shaka.Player.version = 'v4.3.16' + '-uncompiled'; // x-release-please-version
|
|
7036
7050
|
|
|
7037
7051
|
// Initialize the deprecation system using the version string we just set
|
|
7038
7052
|
// on the player.
|
|
@@ -32,11 +32,12 @@ shaka.polyfill.MediaSource = class {
|
|
|
32
32
|
|
|
33
33
|
if (!window.MediaSource) {
|
|
34
34
|
shaka.log.info('No MSE implementation available.');
|
|
35
|
-
} else if (
|
|
36
|
-
cast.__platform__.canDisplayType) {
|
|
35
|
+
} else if (shaka.util.Platform.isChromecast()) {
|
|
37
36
|
shaka.log.info('Patching Chromecast MSE bugs.');
|
|
38
|
-
// Chromecast
|
|
39
|
-
|
|
37
|
+
// Chromecast fails on some TS content, even though it is supposed to
|
|
38
|
+
// support it. Better to transmux.
|
|
39
|
+
// See https://github.com/shaka-project/shaka-player/issues/5278
|
|
40
|
+
shaka.polyfill.MediaSource.rejectContainer_('mp2t');
|
|
40
41
|
} else if (safariVersion) {
|
|
41
42
|
// NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
|
|
42
43
|
// Safari version.
|
|
@@ -161,38 +162,6 @@ shaka.polyfill.MediaSource = class {
|
|
|
161
162
|
};
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
/**
|
|
165
|
-
* Patch isTypeSupported() to chain to a private API on the Chromecast which
|
|
166
|
-
* can query for support of detailed content parameters.
|
|
167
|
-
*
|
|
168
|
-
* @private
|
|
169
|
-
*/
|
|
170
|
-
static patchCastIsTypeSupported_() {
|
|
171
|
-
const originalIsTypeSupported = MediaSource.isTypeSupported;
|
|
172
|
-
|
|
173
|
-
MediaSource.isTypeSupported = (mimeType) => {
|
|
174
|
-
// Parse the basic MIME type from its parameters.
|
|
175
|
-
const pieces = mimeType.split(/ *; */);
|
|
176
|
-
pieces.shift(); // Remove basic MIME type from pieces.
|
|
177
|
-
|
|
178
|
-
const hasCodecs = pieces.some((piece) => piece.startsWith('codecs='));
|
|
179
|
-
if (!hasCodecs) {
|
|
180
|
-
// Though the original reason for this special case was not documented,
|
|
181
|
-
// it is presumed to be because the platform won't accept a MIME type
|
|
182
|
-
// without codecs in canDisplayType. It is valid, however, in
|
|
183
|
-
// isTypeSupported.
|
|
184
|
-
return originalIsTypeSupported(mimeType);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Only canDisplayType can check extended MIME type parameters on this
|
|
188
|
-
// platform (such as frame rate, resolution, etc).
|
|
189
|
-
// In previous versions of this polyfill, the MIME type parameters were
|
|
190
|
-
// manipulated, filtered, or extended. This is no longer true, so we pass
|
|
191
|
-
// the full MIME type to the platform as we received it.
|
|
192
|
-
return cast.__platform__.canDisplayType(mimeType);
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
165
|
/**
|
|
197
166
|
* Patch isTypeSupported() to translate vp09 codec strings into vp9, to allow
|
|
198
167
|
* such content to play on older smart TVs.
|