shaka-player 4.1.6 → 4.1.7
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 +1 -0
- package/CHANGELOG.md +15 -0
- package/CONTRIBUTORS +1 -0
- package/dist/deps.js +2 -2
- package/dist/locales.js +29 -29
- package/dist/shaka-player.compiled.d.ts +5 -1
- package/dist/shaka-player.compiled.debug.d.ts +5 -1
- package/dist/shaka-player.compiled.debug.externs.js +2 -1
- package/dist/shaka-player.compiled.debug.js +82 -81
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +2 -1
- package/dist/shaka-player.compiled.js +516 -513
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +5 -1
- package/dist/shaka-player.ui.debug.d.ts +5 -1
- package/dist/shaka-player.ui.debug.externs.js +2 -1
- package/dist/shaka-player.ui.debug.js +98 -97
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +2 -1
- package/dist/shaka-player.ui.js +656 -653
- package/dist/shaka-player.ui.map +3 -3
- package/externs/mux.js +3 -0
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +16 -6
- package/lib/hls/hls_parser.js +9 -2
- package/lib/media/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/util/manifest_parser_utils.js +4 -0
- package/package.json +1 -1
- package/roadmap.md +24 -6
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/media/media_source_engine_integration.js +31 -0
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +13 -0
- package/test/media/streaming_engine_unit.js +36 -23
- package/test/player_unit.js +87 -0
- package/test/test/util/simple_fakes.js +3 -0
- package/ui/controls.js +4 -1
- package/ui/settings_menu.js +11 -2
package/externs/mux.js
CHANGED
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/hls/hls_parser.js
CHANGED
|
@@ -846,13 +846,19 @@ shaka.hls.HlsParser = class {
|
|
|
846
846
|
shaka.util.ArrayUtils.remove(allCodecs, textCodecs);
|
|
847
847
|
}
|
|
848
848
|
if (audioGroupId) {
|
|
849
|
-
|
|
849
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
850
850
|
ContentType.AUDIO, allCodecs);
|
|
851
|
+
if (!codecs) {
|
|
852
|
+
codecs = this.config_.hls.defaultAudioCodec;
|
|
853
|
+
}
|
|
851
854
|
this.groupIdToCodecsMap_.set(audioGroupId, codecs);
|
|
852
855
|
}
|
|
853
856
|
if (videoGroupId) {
|
|
854
|
-
|
|
857
|
+
let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
855
858
|
ContentType.VIDEO, allCodecs);
|
|
859
|
+
if (!codecs) {
|
|
860
|
+
codecs = this.config_.hls.defaultVideoCodec;
|
|
861
|
+
}
|
|
856
862
|
this.groupIdToCodecsMap_.set(videoGroupId, codecs);
|
|
857
863
|
}
|
|
858
864
|
}
|
|
@@ -2830,6 +2836,7 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2830
2836
|
'm4f': 'application/mp4',
|
|
2831
2837
|
'cmft': 'application/mp4',
|
|
2832
2838
|
'vtt': 'text/vtt',
|
|
2839
|
+
'webvtt': 'txt/vtt',
|
|
2833
2840
|
'ttml': 'application/ttml+xml',
|
|
2834
2841
|
};
|
|
2835
2842
|
|
|
@@ -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 */
|
|
@@ -1395,16 +1395,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1395
1395
|
this.mediaStates_.delete(ContentType.TEXT);
|
|
1396
1396
|
} else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
|
|
1397
1397
|
this.handleQuotaExceeded_(mediaState, error);
|
|
1398
|
-
} else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
|
|
1399
|
-
error.data && error.data[1] == 404) {
|
|
1400
|
-
// The segment could not be found, does not exist, or is not available.
|
|
1401
|
-
// In any case just try again.
|
|
1402
|
-
// The current segment is not available. Schedule another update to
|
|
1403
|
-
// fetch the segment again.
|
|
1404
|
-
shaka.log.v2(logPrefix, 'segment not available.');
|
|
1405
|
-
mediaState.performingUpdate = false;
|
|
1406
|
-
mediaState.updateTimer = null;
|
|
1407
|
-
this.scheduleUpdate_(mediaState, 1);
|
|
1408
1398
|
} else {
|
|
1409
1399
|
shaka.log.error(logPrefix, 'failed fetch and append: code=' +
|
|
1410
1400
|
error.code);
|
|
@@ -1419,9 +1409,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1419
1409
|
|
|
1420
1410
|
/**
|
|
1421
1411
|
* Clear per-stream error states and retry any failed streams.
|
|
1412
|
+
* @param {number} delaySeconds
|
|
1422
1413
|
* @return {boolean} False if unable to retry.
|
|
1423
1414
|
*/
|
|
1424
|
-
retry() {
|
|
1415
|
+
retry(delaySeconds) {
|
|
1425
1416
|
if (this.destroyer_.destroyed()) {
|
|
1426
1417
|
shaka.log.error('Unable to retry after StreamingEngine is destroyed!');
|
|
1427
1418
|
return false;
|
|
@@ -1438,7 +1429,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1438
1429
|
if (mediaState.hasError) {
|
|
1439
1430
|
shaka.log.info(logPrefix, 'Retrying after failure...');
|
|
1440
1431
|
mediaState.hasError = false;
|
|
1441
|
-
this.scheduleUpdate_(mediaState,
|
|
1432
|
+
this.scheduleUpdate_(mediaState, delaySeconds);
|
|
1442
1433
|
}
|
|
1443
1434
|
}
|
|
1444
1435
|
|
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
|
@@ -2055,6 +2055,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2055
2055
|
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
2056
2056
|
}
|
|
2057
2057
|
|
|
2058
|
+
this.playhead_.ready();
|
|
2059
|
+
|
|
2058
2060
|
// Decide if text should be shown automatically.
|
|
2059
2061
|
// similar to video/audio track, we would skip switch initial text track
|
|
2060
2062
|
// if user already pick text track (via selectTextTrack api)
|
|
@@ -2392,6 +2394,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2392
2394
|
HTMLMediaElement.HAVE_METADATA,
|
|
2393
2395
|
this.loadEventManager_,
|
|
2394
2396
|
() => {
|
|
2397
|
+
this.playhead_.ready();
|
|
2395
2398
|
fullyLoaded.resolve();
|
|
2396
2399
|
});
|
|
2397
2400
|
|
|
@@ -4834,12 +4837,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4834
4837
|
* If the player has loaded content, and streaming seen an error, but the
|
|
4835
4838
|
* could not resume streaming, this will return <code>false</code>.
|
|
4836
4839
|
*
|
|
4840
|
+
* @param {number=} retryDelaySeconds
|
|
4837
4841
|
* @return {boolean}
|
|
4838
4842
|
* @export
|
|
4839
4843
|
*/
|
|
4840
|
-
retryStreaming() {
|
|
4844
|
+
retryStreaming(retryDelaySeconds = 0.1) {
|
|
4841
4845
|
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
|
|
4842
|
-
this.streamingEngine_.retry() :
|
|
4846
|
+
this.streamingEngine_.retry(retryDelaySeconds) :
|
|
4843
4847
|
false;
|
|
4844
4848
|
}
|
|
4845
4849
|
|
|
@@ -4935,17 +4939,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4935
4939
|
* @private
|
|
4936
4940
|
*/
|
|
4937
4941
|
defaultStreamingFailureCallback_(error) {
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4942
|
+
// For live streams, we retry streaming automatically for certain errors.
|
|
4943
|
+
// For VOD streams, all streaming failures are fatal.
|
|
4944
|
+
if (!this.isLive()) {
|
|
4945
|
+
return;
|
|
4946
|
+
}
|
|
4943
4947
|
|
|
4944
|
-
|
|
4945
|
-
|
|
4948
|
+
let retryDelaySeconds = null;
|
|
4949
|
+
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
|
|
4950
|
+
error.code == shaka.util.Error.Code.HTTP_ERROR) {
|
|
4951
|
+
// These errors can be near-instant, so delay a bit before retrying.
|
|
4952
|
+
retryDelaySeconds = 1;
|
|
4953
|
+
} else if (error.code == shaka.util.Error.Code.TIMEOUT) {
|
|
4954
|
+
// We already waited for a timeout, so retry quickly.
|
|
4955
|
+
retryDelaySeconds = 0.1;
|
|
4956
|
+
}
|
|
4946
4957
|
|
|
4958
|
+
if (retryDelaySeconds != null) {
|
|
4959
|
+
error.severity = shaka.util.Error.Severity.RECOVERABLE;
|
|
4947
4960
|
shaka.log.warning('Live streaming error. Retrying automatically...');
|
|
4948
|
-
this.retryStreaming();
|
|
4961
|
+
this.retryStreaming(retryDelaySeconds);
|
|
4949
4962
|
}
|
|
4950
4963
|
}
|
|
4951
4964
|
|
|
@@ -6575,7 +6588,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6575
6588
|
* @define {string} A version number taken from git at compile time.
|
|
6576
6589
|
* @export
|
|
6577
6590
|
*/
|
|
6578
|
-
shaka.Player.version = 'v4.1.
|
|
6591
|
+
shaka.Player.version = 'v4.1.7-uncompiled';
|
|
6579
6592
|
|
|
6580
6593
|
// Initialize the deprecation system using the version string we just set
|
|
6581
6594
|
// on the player.
|
|
@@ -168,6 +168,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
168
168
|
/^hvc/,
|
|
169
169
|
/^vp0?[89]/,
|
|
170
170
|
/^av01/,
|
|
171
|
+
/^dvhe/,
|
|
171
172
|
];
|
|
172
173
|
|
|
173
174
|
|
|
@@ -183,6 +184,9 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
|
|
|
183
184
|
/^flac$/,
|
|
184
185
|
/^mp4a/,
|
|
185
186
|
/^[ae]c-3$/,
|
|
187
|
+
/^ac-4$/,
|
|
188
|
+
/^dts[cx]$/, // DTS Surround Sound
|
|
189
|
+
/^[du]dts$/, // DTS Surround Sound
|
|
186
190
|
];
|
|
187
191
|
|
|
188
192
|
|
package/package.json
CHANGED
package/roadmap.md
CHANGED
|
@@ -9,28 +9,46 @@ The goals of future milestones are fluid until we begin that development cycle,
|
|
|
9
9
|
so the exact milestone for future features is not pre-determined.
|
|
10
10
|
|
|
11
11
|
Priority features up next:
|
|
12
|
-
-
|
|
13
|
-
https://github.com/shaka-project/shaka-player/
|
|
12
|
+
- HLS improvements
|
|
13
|
+
https://github.com/shaka-project/shaka-player/projects/6
|
|
14
14
|
|
|
15
15
|
Candidate features for future release cycles:
|
|
16
|
+
- Preload API
|
|
17
|
+
https://github.com/shaka-project/shaka-player/issues/880
|
|
16
18
|
- Official TypeScript defs, generated from source
|
|
17
19
|
https://github.com/shaka-project/shaka-player/issues/1030
|
|
18
20
|
- Codec-switching
|
|
19
21
|
https://github.com/shaka-project/shaka-player/issues/1528
|
|
20
22
|
- Background fetch for offline storage
|
|
21
23
|
https://github.com/shaka-project/shaka-player/issues/879
|
|
24
|
+
- Smaller, more modular binary
|
|
25
|
+
|
|
26
|
+
v4.3 - 2022 Q4 / 2023 Q1
|
|
22
27
|
- Support ID3 tags in mp4
|
|
23
28
|
https://github.com/shaka-project/shaka-player/issues/3351
|
|
24
|
-
-
|
|
29
|
+
- Lazy-load HLS playlists on adaptation
|
|
30
|
+
https://github.com/shaka-project/shaka-player/issues/1936
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
=====
|
|
33
|
+
|
|
34
|
+
v4.2 - 2022 Q3
|
|
35
|
+
- Automatic ABR quality restrictions based on size
|
|
36
|
+
https://github.com/shaka-project/shaka-player/issues/2333
|
|
37
|
+
- HLS support for AES-128 encryption
|
|
38
|
+
https://github.com/shaka-project/shaka-player/issues/850
|
|
39
|
+
|
|
40
|
+
v4.1 - 2022 Q2
|
|
41
|
+
- HLS support for EXT-X-GAP
|
|
42
|
+
https://github.com/shaka-project/shaka-player/issues/1308
|
|
43
|
+
- Temporarily disable the active variant after `HTTP_ERROR`
|
|
44
|
+
https://github.com/shaka-project/shaka-player/issues/1542
|
|
45
|
+
|
|
46
|
+
v4.0 - 2022 Q2
|
|
27
47
|
- MSE support for FairPlay
|
|
28
48
|
https://github.com/shaka-project/shaka-player/issues/3346
|
|
29
49
|
- Support containerless formats
|
|
30
50
|
https://github.com/shaka-project/shaka-player/issues/2337
|
|
31
51
|
|
|
32
|
-
=====
|
|
33
|
-
|
|
34
52
|
v3.3 - 2022 Q1
|
|
35
53
|
- Common Media Client Data (CMCD) logging support
|
|
36
54
|
https://github.com/shaka-project/shaka-player/issues/3619
|
|
@@ -34,6 +34,8 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
34
34
|
|
|
35
35
|
/** @type {shaka.util.PublicPromise} */
|
|
36
36
|
let messageWaitPromise;
|
|
37
|
+
/** @type {Array.<string>} */
|
|
38
|
+
let pendingMessages = null;
|
|
37
39
|
|
|
38
40
|
/** @type {!Array.<function()>} */
|
|
39
41
|
let toRestore;
|
|
@@ -91,6 +93,9 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
91
93
|
toRestore = [];
|
|
92
94
|
pendingWaitWrapperCalls = 0;
|
|
93
95
|
|
|
96
|
+
messageWaitPromise = null;
|
|
97
|
+
pendingMessages = null;
|
|
98
|
+
|
|
94
99
|
fakeInitState = {
|
|
95
100
|
player: {
|
|
96
101
|
configure: {},
|
|
@@ -118,6 +123,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
118
123
|
player = null;
|
|
119
124
|
video = null;
|
|
120
125
|
receiver = null;
|
|
126
|
+
|
|
127
|
+
if (messageWaitPromise) {
|
|
128
|
+
messageWaitPromise.resolve([]);
|
|
129
|
+
}
|
|
130
|
+
messageWaitPromise = null;
|
|
131
|
+
pendingMessages = null;
|
|
121
132
|
});
|
|
122
133
|
|
|
123
134
|
afterAll(() => {
|
|
@@ -128,6 +139,66 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
128
139
|
}
|
|
129
140
|
});
|
|
130
141
|
|
|
142
|
+
describe('without drm', () => {
|
|
143
|
+
it('sends reasonably-sized updates', async () => {
|
|
144
|
+
// Use an unencrypted asset.
|
|
145
|
+
fakeInitState.manifest = 'test:sintel';
|
|
146
|
+
|
|
147
|
+
const p = waitForLoadedData();
|
|
148
|
+
|
|
149
|
+
// Start the process of loading by sending a fake init message.
|
|
150
|
+
fakeConnectedSenders(1);
|
|
151
|
+
fakeIncomingMessage({
|
|
152
|
+
type: 'init',
|
|
153
|
+
initState: fakeInitState,
|
|
154
|
+
appData: {},
|
|
155
|
+
}, mockShakaMessageBus);
|
|
156
|
+
|
|
157
|
+
await p;
|
|
158
|
+
// Wait for an update message.
|
|
159
|
+
const messages = await waitForUpdateMessages();
|
|
160
|
+
for (const message of messages) {
|
|
161
|
+
// Check that the update message is of a reasonable size. From previous
|
|
162
|
+
// testing we found that the socket would silently reject data that got
|
|
163
|
+
// too big. 6KB is safely below the limit.
|
|
164
|
+
expect(message.length).toBeLessThan(6000);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('has reasonable average message size', async () => {
|
|
169
|
+
// Use an unencrypted asset.
|
|
170
|
+
fakeInitState.manifest = 'test:sintel';
|
|
171
|
+
|
|
172
|
+
const p = waitForLoadedData();
|
|
173
|
+
|
|
174
|
+
// Start the process of loading by sending a fake init message.
|
|
175
|
+
fakeConnectedSenders(1);
|
|
176
|
+
fakeIncomingMessage({
|
|
177
|
+
type: 'init',
|
|
178
|
+
initState: fakeInitState,
|
|
179
|
+
appData: {},
|
|
180
|
+
}, mockShakaMessageBus);
|
|
181
|
+
|
|
182
|
+
await p;
|
|
183
|
+
|
|
184
|
+
// Collect messages over 50 update cycles, and average their length.
|
|
185
|
+
// Not all properties are passed along on every update message, so
|
|
186
|
+
// the average length is expected to be lower than the length of the first
|
|
187
|
+
// update message.
|
|
188
|
+
let totalLength = 0;
|
|
189
|
+
let totalMessages = 0;
|
|
190
|
+
for (let i = 0; i < 50; i++) {
|
|
191
|
+
// eslint-disable-next-line no-await-in-loop
|
|
192
|
+
const messages = await waitForUpdateMessages();
|
|
193
|
+
for (const message of messages) {
|
|
194
|
+
totalLength += message.length;
|
|
195
|
+
totalMessages += 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
expect(totalLength / totalMessages).toBeLessThan(2000);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
131
202
|
filterDescribe('with drm', () => support['com.widevine.alpha'], () => {
|
|
132
203
|
drmIt('sends reasonably-sized updates', async () => {
|
|
133
204
|
// Use an encrypted asset, to make sure DRM info doesn't balloon the size.
|
|
@@ -150,11 +221,13 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
150
221
|
|
|
151
222
|
await p;
|
|
152
223
|
// Wait for an update message.
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
224
|
+
const messages = await waitForUpdateMessages();
|
|
225
|
+
for (const message of messages) {
|
|
226
|
+
// Check that the update message is of a reasonable size. From previous
|
|
227
|
+
// testing we found that the socket would silently reject data that got
|
|
228
|
+
// too big. 6KB is safely below the limit.
|
|
229
|
+
expect(message.length).toBeLessThan(6000);
|
|
230
|
+
}
|
|
158
231
|
});
|
|
159
232
|
|
|
160
233
|
drmIt('has reasonable average message size', async () => {
|
|
@@ -177,17 +250,22 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
177
250
|
}, mockShakaMessageBus);
|
|
178
251
|
|
|
179
252
|
await p;
|
|
180
|
-
|
|
253
|
+
|
|
254
|
+
// Collect messages over 50 update cycles, and average their length.
|
|
181
255
|
// Not all properties are passed along on every update message, so
|
|
182
256
|
// the average length is expected to be lower than the length of the first
|
|
183
257
|
// update message.
|
|
184
258
|
let totalLength = 0;
|
|
259
|
+
let totalMessages = 0;
|
|
185
260
|
for (let i = 0; i < 50; i++) {
|
|
186
261
|
// eslint-disable-next-line no-await-in-loop
|
|
187
|
-
const
|
|
188
|
-
|
|
262
|
+
const messages = await waitForUpdateMessages();
|
|
263
|
+
for (const message of messages) {
|
|
264
|
+
totalLength += message.length;
|
|
265
|
+
totalMessages += 1;
|
|
266
|
+
}
|
|
189
267
|
}
|
|
190
|
-
expect(totalLength /
|
|
268
|
+
expect(totalLength / totalMessages).toBeLessThan(2000);
|
|
191
269
|
});
|
|
192
270
|
});
|
|
193
271
|
|
|
@@ -227,12 +305,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
227
305
|
expect(pendingWaitWrapperCalls).toBe(0);
|
|
228
306
|
|
|
229
307
|
// Wait for a final update message before proceeding.
|
|
230
|
-
await
|
|
308
|
+
await waitForUpdateMessages();
|
|
231
309
|
});
|
|
232
310
|
|
|
233
311
|
/**
|
|
234
312
|
* Creates a wrapper around a method on a given prototype, which makes it
|
|
235
|
-
* wait on
|
|
313
|
+
* wait on waitForUpdateMessages before returning, and registers that wrapper
|
|
236
314
|
* to be uninstalled afterwards.
|
|
237
315
|
* The replaced method is expected to be a method that returns a promise.
|
|
238
316
|
* @param {!Object} prototype
|
|
@@ -249,7 +327,7 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
249
327
|
'Waiting for update message before calling ' +
|
|
250
328
|
name + '.' + methodName + '...');
|
|
251
329
|
const originalArguments = Array.from(arguments);
|
|
252
|
-
await
|
|
330
|
+
await waitForUpdateMessages();
|
|
253
331
|
// eslint-disable-next-line no-restricted-syntax
|
|
254
332
|
return original.apply(this, originalArguments);
|
|
255
333
|
};
|
|
@@ -265,7 +343,8 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
265
343
|
});
|
|
266
344
|
}
|
|
267
345
|
|
|
268
|
-
function
|
|
346
|
+
function waitForUpdateMessages() {
|
|
347
|
+
pendingMessages = [];
|
|
269
348
|
messageWaitPromise = new shaka.util.PublicPromise();
|
|
270
349
|
return messageWaitPromise;
|
|
271
350
|
}
|
|
@@ -316,10 +395,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
316
395
|
bus.messages.push(CastUtils.deserialize(message));
|
|
317
396
|
// Check to see if it's an update message.
|
|
318
397
|
const parsed = CastUtils.deserialize(message);
|
|
319
|
-
if (parsed.type == 'update' &&
|
|
398
|
+
if (parsed.type == 'update' && pendingMessages) {
|
|
320
399
|
shaka.log.debug('Received update message. Proceeding...');
|
|
321
|
-
|
|
322
|
-
|
|
400
|
+
// The code waiting on this Promise will get an array of all of the
|
|
401
|
+
// messages processed within this tick.
|
|
402
|
+
pendingMessages.push(message);
|
|
403
|
+
messageWaitPromise.resolve(pendingMessages);
|
|
323
404
|
}
|
|
324
405
|
});
|
|
325
406
|
const channel = {
|
|
@@ -245,18 +245,20 @@ filterDescribe('CastReceiver', castReceiverSupport, () => {
|
|
|
245
245
|
const fakeEvent = {type: 'timeupdate'};
|
|
246
246
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
247
247
|
|
|
248
|
-
// There are now "update" and "event" messages, in that
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
248
|
+
// There are now some number of "update" and "event" messages, in that
|
|
249
|
+
// order.
|
|
250
|
+
expect(mockShakaMessageBus.messages).toContain({
|
|
251
|
+
type: 'update',
|
|
252
|
+
update: jasmine.any(Object),
|
|
253
|
+
});
|
|
254
|
+
expect(mockShakaMessageBus.messages).toContain({
|
|
255
|
+
type: 'event',
|
|
256
|
+
targetName: 'video',
|
|
257
|
+
event: jasmine.objectContaining(fakeEvent),
|
|
258
|
+
});
|
|
259
|
+
const eventIndex = mockShakaMessageBus.messages.findIndex(
|
|
260
|
+
(message) => message.type == 'event');
|
|
261
|
+
expect(eventIndex).toBe(mockShakaMessageBus.messages.length - 1);
|
|
260
262
|
});
|
|
261
263
|
});
|
|
262
264
|
|
|
@@ -1105,6 +1107,9 @@ filterDescribe('CastReceiver', castReceiverSupport, () => {
|
|
|
1105
1107
|
for (const name in CastUtils.PlayerGetterMethods) {
|
|
1106
1108
|
player[name] = jasmine.createSpy(name);
|
|
1107
1109
|
}
|
|
1110
|
+
for (const name in CastUtils.LargePlayerGetterMethods) {
|
|
1111
|
+
player[name] = jasmine.createSpy(name);
|
|
1112
|
+
}
|
|
1108
1113
|
for (const name in CastUtils.PlayerGetterMethodsThatRequireLive) {
|
|
1109
1114
|
player[name] = jasmine.createSpy(name);
|
|
1110
1115
|
}
|
|
@@ -34,6 +34,7 @@ describe('CastUtils', () => {
|
|
|
34
34
|
const castMembers = CastUtils.PlayerVoidMethods
|
|
35
35
|
.concat(CastUtils.PlayerPromiseMethods)
|
|
36
36
|
.concat(Object.keys(CastUtils.PlayerGetterMethods))
|
|
37
|
+
.concat(Object.keys(CastUtils.LargePlayerGetterMethods))
|
|
37
38
|
.concat(Object.keys(CastUtils.PlayerGetterMethodsThatRequireLive));
|
|
38
39
|
// eslint-disable-next-line no-restricted-syntax
|
|
39
40
|
const allPlayerMembers = Object.getOwnPropertyNames(shaka.Player.prototype);
|