shaka-player 4.0.0 → 4.1.0
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/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +4 -4
- package/build/misspellings.txt +1 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/demo.less +0 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +4 -4
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +4 -4
- package/dist/demo.compiled.map +2 -2
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +3 -3
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +2 -2
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +2 -2
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +15 -8
- package/dist/shaka-player.compiled.debug.d.ts +15 -8
- package/dist/shaka-player.compiled.debug.externs.js +11 -2
- package/dist/shaka-player.compiled.debug.js +266 -253
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +11 -2
- package/dist/shaka-player.compiled.js +792 -784
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +16 -9
- package/dist/shaka-player.ui.debug.d.ts +16 -9
- package/dist/shaka-player.ui.debug.externs.js +11 -2
- package/dist/shaka-player.ui.debug.js +299 -282
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +11 -2
- package/dist/shaka-player.ui.js +948 -941
- package/dist/shaka-player.ui.map +3 -3
- package/docs/design/chromecast.md +3 -1
- package/docs/tutorials/drm-config.md +16 -0
- package/docs/tutorials/ui.md +26 -0
- package/docs/tutorials/upgrade.md +4 -2
- package/externs/chromecast.js +9 -2
- package/externs/shaka/manifest.js +7 -0
- package/externs/shaka/player.js +32 -2
- package/externs/shaka/text.js +7 -0
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- package/lib/cast/cast_proxy.js +19 -8
- package/lib/cast/cast_receiver.js +3 -3
- package/lib/cast/cast_sender.js +12 -2
- package/lib/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +46 -15
- package/lib/media/drm_engine.js +36 -8
- package/lib/media/gap_jumping_controller.js +23 -1
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/playhead.js +47 -4
- package/lib/media/stall_detector.js +19 -2
- package/lib/media/streaming_engine.js +9 -3
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/player.js +339 -175
- package/lib/text/cue.js +6 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +11 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +13 -0
- package/lib/util/fake_event.js +39 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +62 -6
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +16 -14
- package/lib/util/stats.js +28 -1
- package/lib/util/stream_utils.js +11 -2
- package/package.json +5 -2
- package/test/cast/cast_proxy_unit.js +3 -1
- package/test/cast/cast_sender_unit.js +21 -6
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +79 -7
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/playhead_unit.js +114 -25
- package/test/media/stall_detector_unit.js +20 -2
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +1 -0
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +9 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +240 -2
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/simple_fakes.js +45 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/text/vtt_text_parser_unit.js +68 -5
- package/test/ui/ui_unit.js +18 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +108 -0
- package/ui/controls.js +12 -8
- package/ui/externs/ui.js +10 -1
- package/ui/less/containers.less +1 -1
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +1 -0
- package/ui/resolution_selection.js +1 -1
- package/ui/statistics_button.js +10 -0
- package/ui/ui.js +12 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
package/lib/text/cue.js
CHANGED
|
@@ -411,6 +411,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
411
411
|
style.paddingRight =
|
|
412
412
|
shaka.text.UITextDisplayer.convertLengthValue_(
|
|
413
413
|
cue.linePadding, cue, this.videoContainer_);
|
|
414
|
+
style.textShadow = cue.textShadow;
|
|
414
415
|
|
|
415
416
|
if (cue.backgroundImage) {
|
|
416
417
|
style.backgroundImage = 'url(\'' + cue.backgroundImage + '\')';
|
|
@@ -68,13 +68,15 @@ shaka.text.VttTextParser = class {
|
|
|
68
68
|
// to the beginning of each segment.
|
|
69
69
|
// NOTE: "periodStart" is the timestamp offset applied via TextEngine.
|
|
70
70
|
// It is no longer closely tied to periods, but the name stuck around.
|
|
71
|
+
// NOTE: This offset and the flag choosing its meaning have no effect on
|
|
72
|
+
// HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
|
|
71
73
|
let offset = time.vttOffset;
|
|
72
74
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
if (blocks[0].includes('X-TIMESTAMP-MAP') &&
|
|
75
|
+
// Only use 'X-TIMESTAMP-MAP' in sequence mode, as that is currently
|
|
76
|
+
// shorthand for HLS. Note that an offset based on the first video
|
|
77
|
+
// timestamp has already been extracted, and appears in periodStart.
|
|
78
|
+
// The relative offset from X-TIMESTAMP-MAP will be added to that for HLS.
|
|
79
|
+
if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
|
|
78
80
|
// https://bit.ly/2K92l7y
|
|
79
81
|
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
80
82
|
// the rest of the media.
|
|
@@ -109,8 +111,6 @@ shaka.text.VttTextParser = class {
|
|
|
109
111
|
mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
// Apple-encoded HLS content uses absolute timestamps, so assume the
|
|
113
|
-
// presence of the map tag means the content uses absolute timestamps.
|
|
114
114
|
offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -293,6 +293,10 @@ shaka.text.VttTextParser = class {
|
|
|
293
293
|
validStyle = true;
|
|
294
294
|
cue.opacity = parseFloat(value);
|
|
295
295
|
break;
|
|
296
|
+
case 'text-shadow':
|
|
297
|
+
validStyle = true;
|
|
298
|
+
cue.textShadow = value;
|
|
299
|
+
break;
|
|
296
300
|
case 'white-space':
|
|
297
301
|
validStyle = true;
|
|
298
302
|
cue.wrapLine = value != 'noWrap';
|
package/lib/util/config_utils.js
CHANGED
|
@@ -127,4 +127,29 @@ shaka.util.ConfigUtils = class {
|
|
|
127
127
|
last[fieldName.substring(nameStart).replace(/\\\./g, '.')] = value;
|
|
128
128
|
return configObject;
|
|
129
129
|
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Reference the input parameters so the compiler doesn't remove them from
|
|
133
|
+
* the calling function. Return whatever value is specified.
|
|
134
|
+
*
|
|
135
|
+
* This allows an empty or default implementation of a config callback that
|
|
136
|
+
* still bears the complete function signature even in compiled mode.
|
|
137
|
+
*
|
|
138
|
+
* The caller should look something like this:
|
|
139
|
+
*
|
|
140
|
+
* const callback = (a, b, c, d) => {
|
|
141
|
+
* return referenceParametersAndReturn(
|
|
142
|
+
[a, b, c, d],
|
|
143
|
+
a); // Can be anything, doesn't need to be one of the parameters
|
|
144
|
+
* };
|
|
145
|
+
*
|
|
146
|
+
* @param {!Array.<?>} parameters
|
|
147
|
+
* @param {T} returnValue
|
|
148
|
+
* @return {T}
|
|
149
|
+
* @template T
|
|
150
|
+
* @noinline
|
|
151
|
+
*/
|
|
152
|
+
static referenceParametersAndReturn(parameters, returnValue) {
|
|
153
|
+
return parameters && returnValue;
|
|
154
|
+
}
|
|
130
155
|
};
|
package/lib/util/error.js
CHANGED
|
@@ -267,6 +267,12 @@ shaka.util.Error.Code = {
|
|
|
267
267
|
*/
|
|
268
268
|
'ATTEMPTS_EXHAUSTED': 1010,
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
* The segment is missing.
|
|
272
|
+
* <br> error.data[0] is the URI.
|
|
273
|
+
*/
|
|
274
|
+
'SEGMENT_MISSING': 1011,
|
|
275
|
+
|
|
270
276
|
|
|
271
277
|
/** The text parser failed to parse a text stream due to an invalid header. */
|
|
272
278
|
'INVALID_TEXT_HEADER': 2000,
|
|
@@ -345,6 +351,13 @@ shaka.util.Error.Code = {
|
|
|
345
351
|
*/
|
|
346
352
|
'MISSING_TEXT_PLUGIN': 2014,
|
|
347
353
|
|
|
354
|
+
/**
|
|
355
|
+
* The chapters track failed to load. The browser does not provide any
|
|
356
|
+
* information in this case to identify why it failed, but there may be
|
|
357
|
+
* details in the JavaScript console.
|
|
358
|
+
*/
|
|
359
|
+
'CHAPTERS_TRACK_FAILED': 2015,
|
|
360
|
+
|
|
348
361
|
/**
|
|
349
362
|
* Some component tried to read past the end of a buffer. The segment index,
|
|
350
363
|
* init segment, or PSSH may be malformed.
|
package/lib/util/fake_event.js
CHANGED
|
@@ -144,3 +144,42 @@ shaka.util.FakeEvent = class {
|
|
|
144
144
|
*/
|
|
145
145
|
stopPropagation() {}
|
|
146
146
|
};
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* An internal enum that contains the string values of all of the player events.
|
|
151
|
+
* This exists primarily to act as an implicit list of events, for tests.
|
|
152
|
+
*
|
|
153
|
+
* @enum {string}
|
|
154
|
+
*/
|
|
155
|
+
shaka.util.FakeEvent.EventName = {
|
|
156
|
+
AbrStatusChanged: 'abrstatuschanged',
|
|
157
|
+
Adaptation: 'adaptation',
|
|
158
|
+
Buffering: 'buffering',
|
|
159
|
+
DownloadFailed: 'downloadfailed',
|
|
160
|
+
DownloadHeadersReceived: 'downloadheadersreceived',
|
|
161
|
+
DrmSessionUpdate: 'drmsessionupdate',
|
|
162
|
+
Emsg: 'emsg',
|
|
163
|
+
Error: 'error',
|
|
164
|
+
ExpirationUpdated: 'expirationupdated',
|
|
165
|
+
GapJumped: 'gapjumped',
|
|
166
|
+
Loaded: 'loaded',
|
|
167
|
+
Loading: 'loading',
|
|
168
|
+
ManifestParsed: 'manifestparsed',
|
|
169
|
+
MediaQualityChanged: 'mediaqualitychanged',
|
|
170
|
+
Metadata: 'metadata',
|
|
171
|
+
OnStateChange: 'onstatechange',
|
|
172
|
+
OnStateIdle: 'onstateidle',
|
|
173
|
+
RateChange: 'ratechange',
|
|
174
|
+
SessionDataEvent: 'sessiondata',
|
|
175
|
+
StallDetected: 'stalldetected',
|
|
176
|
+
Streaming: 'streaming',
|
|
177
|
+
TextChanged: 'textchanged',
|
|
178
|
+
TextTrackVisibility: 'texttrackvisibility',
|
|
179
|
+
TimelineRegionAdded: 'timelineregionadded',
|
|
180
|
+
TimelineRegionEnter: 'timelineregionenter',
|
|
181
|
+
TimelineRegionExit: 'timelineregionexit',
|
|
182
|
+
TracksChanged: 'trackschanged',
|
|
183
|
+
Unloading: 'unloading',
|
|
184
|
+
VariantChanged: 'variantchanged',
|
|
185
|
+
};
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.MimeUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.dependencies');
|
|
10
10
|
goog.require('shaka.media.Transmuxer');
|
|
11
|
+
goog.require('shaka.util.ManifestParserUtils');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @summary A set of utility functions for dealing with MIME types.
|
|
@@ -34,6 +35,8 @@ shaka.util.MimeUtils = class {
|
|
|
34
35
|
/**
|
|
35
36
|
* Takes a MIME type and a codecs string and produces the full MIME
|
|
36
37
|
* type. If it's a transport stream, convert its codecs to MP4 codecs.
|
|
38
|
+
* Otherwise for multiplexed content, convert the video MIME types to
|
|
39
|
+
* their audio equivalents if the content type is audio.
|
|
37
40
|
*
|
|
38
41
|
* @param {string} mimeType
|
|
39
42
|
* @param {string} codecs
|
|
@@ -42,13 +45,19 @@ shaka.util.MimeUtils = class {
|
|
|
42
45
|
*/
|
|
43
46
|
static getFullOrConvertedType(mimeType, codecs, contentType) {
|
|
44
47
|
const fullMimeType = shaka.util.MimeUtils.getFullType(mimeType, codecs);
|
|
48
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
45
49
|
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
if (shaka.media.Transmuxer.isTsContainer(fullMimeType)) {
|
|
51
|
+
if (shaka.dependencies.muxjs()) {
|
|
52
|
+
return shaka.media.Transmuxer.convertTsCodecs(
|
|
53
|
+
contentType, fullMimeType);
|
|
54
|
+
}
|
|
55
|
+
} else if (contentType == ContentType.AUDIO) {
|
|
56
|
+
// video/mp2t is the correct mime type for TS audio, so only replace the
|
|
57
|
+
// word "video" with "audio" for non-TS audio content.
|
|
58
|
+
return fullMimeType.replace('video', 'audio');
|
|
49
59
|
}
|
|
50
|
-
|
|
51
|
-
return shaka.media.Transmuxer.convertTsCodecs(contentType, fullMimeType);
|
|
60
|
+
return fullMimeType;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
|
|
@@ -96,6 +105,53 @@ shaka.util.MimeUtils = class {
|
|
|
96
105
|
return codecs.split(',');
|
|
97
106
|
}
|
|
98
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Get the normalized codec from a codec string,
|
|
110
|
+
* independently of their container.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} codecString
|
|
113
|
+
* @return {string}
|
|
114
|
+
*/
|
|
115
|
+
static getNormalizedCodec(codecString) {
|
|
116
|
+
const parts =
|
|
117
|
+
shaka.util.MimeUtils.getCodecParts_(codecString);
|
|
118
|
+
const base = parts[0];
|
|
119
|
+
const profile = parts[1].toLowerCase();
|
|
120
|
+
switch (true) {
|
|
121
|
+
case base === 'mp4a' && profile === '69':
|
|
122
|
+
case base === 'mp4a' && profile === '6b':
|
|
123
|
+
return 'mp3';
|
|
124
|
+
case base === 'mp4a' && profile === '66':
|
|
125
|
+
case base === 'mp4a' && profile === '67':
|
|
126
|
+
case base === 'mp4a' && profile === '68':
|
|
127
|
+
case base === 'mp4a' && profile === '40.2':
|
|
128
|
+
case base === 'mp4a' && profile === '40.02':
|
|
129
|
+
case base === 'mp4a' && profile === '40.5':
|
|
130
|
+
case base === 'mp4a' && profile === '40.05':
|
|
131
|
+
case base === 'mp4a' && profile === '40.29':
|
|
132
|
+
case base === 'mp4a' && profile === '40.42': // Extended HE-AAC
|
|
133
|
+
return 'aac';
|
|
134
|
+
case base === 'mp4a' && profile === 'a5':
|
|
135
|
+
return 'ac-3'; // Dolby Digital
|
|
136
|
+
case base === 'mp4a' && profile === 'a6':
|
|
137
|
+
return 'ec-3'; // Dolby Digital Plus
|
|
138
|
+
case base === 'mp4a' && profile === 'b2':
|
|
139
|
+
return 'dtsx'; // DTS:X
|
|
140
|
+
case base === 'mp4a' && profile === 'a9':
|
|
141
|
+
return 'dtsc'; // DTS Digital Surround
|
|
142
|
+
case base === 'avc1':
|
|
143
|
+
case base === 'avc3':
|
|
144
|
+
return 'avc'; // H264
|
|
145
|
+
case base === 'hvc1':
|
|
146
|
+
case base === 'hev1':
|
|
147
|
+
return 'hevc'; // H265
|
|
148
|
+
case base === 'dvh1':
|
|
149
|
+
case base === 'dvhe':
|
|
150
|
+
return 'dovi'; // Dolby Vision
|
|
151
|
+
}
|
|
152
|
+
return base;
|
|
153
|
+
}
|
|
154
|
+
|
|
99
155
|
/**
|
|
100
156
|
* Get the base codec from a codec string.
|
|
101
157
|
*
|
|
@@ -152,7 +208,7 @@ shaka.util.MimeUtils = class {
|
|
|
152
208
|
|
|
153
209
|
const base = parts[0];
|
|
154
210
|
|
|
155
|
-
parts.
|
|
211
|
+
parts.shift();
|
|
156
212
|
const profile = parts.join('.');
|
|
157
213
|
|
|
158
214
|
// Make sure that we always return a "base" and "profile".
|
package/lib/util/periods.js
CHANGED
|
@@ -573,8 +573,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
573
573
|
// TODO(#1528): Consider changing this when we support codec switching.
|
|
574
574
|
const hasCodec = outputStreams.some((s) => {
|
|
575
575
|
return s.mimeType == stream.mimeType &&
|
|
576
|
-
shaka.util.MimeUtils.
|
|
577
|
-
shaka.util.MimeUtils.
|
|
576
|
+
shaka.util.MimeUtils.getNormalizedCodec(s.codecs) ==
|
|
577
|
+
shaka.util.MimeUtils.getNormalizedCodec(stream.codecs);
|
|
578
578
|
});
|
|
579
579
|
if (!hasCodec) {
|
|
580
580
|
continue;
|
|
@@ -1050,10 +1050,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
1050
1050
|
* @private
|
|
1051
1051
|
*/
|
|
1052
1052
|
static areAVStreamsCompatible_(outputStream, candidate) {
|
|
1053
|
-
const
|
|
1053
|
+
const getCodec = (codecs) =>
|
|
1054
|
+
shaka.util.MimeUtils.getNormalizedCodec(codecs);
|
|
1054
1055
|
// Check MIME type and codecs, which should always be the same.
|
|
1055
1056
|
if (candidate.mimeType != outputStream.mimeType ||
|
|
1056
|
-
|
|
1057
|
+
getCodec(candidate.codecs) != getCodec(outputStream.codecs)) {
|
|
1057
1058
|
return false;
|
|
1058
1059
|
}
|
|
1059
1060
|
|
|
@@ -16,14 +16,6 @@ goog.require('shaka.util.ManifestParserUtils');
|
|
|
16
16
|
goog.require('shaka.util.Platform');
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
// TODO(vaage): Many times in our configs, we need to create an empty
|
|
20
|
-
// implementation of a method, but to avoid closure from removing unused
|
|
21
|
-
// parameters (and breaking our merge config code) we need to use each
|
|
22
|
-
// parameter. Is there a better solution to this problem than what we are
|
|
23
|
-
// doing now?
|
|
24
|
-
//
|
|
25
|
-
// NOTE: Chrome App Content Security Policy prohibits usage of new Function()
|
|
26
|
-
|
|
27
19
|
/**
|
|
28
20
|
* @final
|
|
29
21
|
* @export
|
|
@@ -74,9 +66,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
74
66
|
clearKeys: {}, // key is arbitrary key system ID, value must be string
|
|
75
67
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
76
68
|
delayLicenseRequestUntilPlayed: false,
|
|
69
|
+
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
70
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
71
|
+
[initData, initDataType, drmInfo],
|
|
72
|
+
initData);
|
|
73
|
+
},
|
|
77
74
|
logLicenseExchange: false,
|
|
78
75
|
updateExpirationTime: 1,
|
|
79
76
|
preferredKeySystems: [],
|
|
77
|
+
keySystemsMapping: {},
|
|
80
78
|
};
|
|
81
79
|
|
|
82
80
|
const manifest = {
|
|
@@ -113,11 +111,10 @@ shaka.util.PlayerConfiguration = class {
|
|
|
113
111
|
'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb':
|
|
114
112
|
'com.adobe.primetime',
|
|
115
113
|
},
|
|
116
|
-
// Need some operation in the callback or else closure may remove calls
|
|
117
|
-
// to the function as it would be a no-op. The operation can't just be
|
|
118
|
-
// a log message, because those are stripped in the compiled build.
|
|
119
114
|
manifestPreprocessor: (element) => {
|
|
120
|
-
return
|
|
115
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
116
|
+
[element],
|
|
117
|
+
element);
|
|
121
118
|
},
|
|
122
119
|
},
|
|
123
120
|
hls: {
|
|
@@ -138,7 +135,9 @@ shaka.util.PlayerConfiguration = class {
|
|
|
138
135
|
// log message, because those are stripped in the compiled build.
|
|
139
136
|
failureCallback: (error) => {
|
|
140
137
|
shaka.log.error('Unhandled streaming error', error);
|
|
141
|
-
return
|
|
138
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
139
|
+
[error],
|
|
140
|
+
undefined);
|
|
142
141
|
},
|
|
143
142
|
// When low latency streaming is enabled, rebufferingGoal will default to
|
|
144
143
|
// 0.01 if not specified.
|
|
@@ -171,6 +170,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
171
170
|
updateIntervalSeconds: 1,
|
|
172
171
|
dispatchAllEmsgBoxes: false,
|
|
173
172
|
observeQualityChanges: false,
|
|
173
|
+
maxDisabledTime: 30,
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
// Some browsers will stop earlier than others before a gap (e.g., Edge
|
|
@@ -214,7 +214,9 @@ shaka.util.PlayerConfiguration = class {
|
|
|
214
214
|
// to the function as it would be a no-op. The operation can't just be a
|
|
215
215
|
// log message, because those are stripped in the compiled build.
|
|
216
216
|
progressCallback: (content, progress) => {
|
|
217
|
-
return
|
|
217
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
218
|
+
[content, progress],
|
|
219
|
+
undefined);
|
|
218
220
|
},
|
|
219
221
|
|
|
220
222
|
// By default we use persistent licenses as forces errors to surface if
|
package/lib/util/stats.js
CHANGED
|
@@ -31,6 +31,11 @@ shaka.util.Stats = class {
|
|
|
31
31
|
/** @private {number} */
|
|
32
32
|
this.totalCorruptedFrames_ = NaN;
|
|
33
33
|
|
|
34
|
+
/** @private {number} */
|
|
35
|
+
this.totalStallsDetected_ = NaN;
|
|
36
|
+
/** @private {number} */
|
|
37
|
+
this.totalGapsJumped_ = NaN;
|
|
38
|
+
|
|
34
39
|
/** @private {number} */
|
|
35
40
|
this.completionPercent_ = NaN;
|
|
36
41
|
|
|
@@ -76,7 +81,6 @@ shaka.util.Stats = class {
|
|
|
76
81
|
this.totalDecodedFrames_ = decoded;
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
|
|
80
84
|
/**
|
|
81
85
|
* Update corrupted frames. This will replace the previous values.
|
|
82
86
|
*
|
|
@@ -86,6 +90,25 @@ shaka.util.Stats = class {
|
|
|
86
90
|
this.totalCorruptedFrames_ = corrupted;
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Update number of stalls detected. This will replace the previous value.
|
|
95
|
+
*
|
|
96
|
+
* @param {number} stallsDetected
|
|
97
|
+
*/
|
|
98
|
+
setStallsDetected(stallsDetected) {
|
|
99
|
+
this.totalStallsDetected_ = stallsDetected;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Update number of playback gaps jumped over. This will replace the previous
|
|
104
|
+
* value.
|
|
105
|
+
*
|
|
106
|
+
* @param {number} gapsJumped
|
|
107
|
+
*/
|
|
108
|
+
setGapsJumped(gapsJumped) {
|
|
109
|
+
this.totalGapsJumped_ = gapsJumped;
|
|
110
|
+
}
|
|
111
|
+
|
|
89
112
|
/**
|
|
90
113
|
* Set the width and height of the video we are currently playing.
|
|
91
114
|
*
|
|
@@ -208,6 +231,8 @@ shaka.util.Stats = class {
|
|
|
208
231
|
decodedFrames: this.totalDecodedFrames_,
|
|
209
232
|
droppedFrames: this.totalDroppedFrames_,
|
|
210
233
|
corruptedFrames: this.totalCorruptedFrames_,
|
|
234
|
+
stallsDetected: this.totalStallsDetected_,
|
|
235
|
+
gapsJumped: this.totalGapsJumped_,
|
|
211
236
|
estimatedBandwidth: this.bandwidthEstimate_,
|
|
212
237
|
completionPercent: this.completionPercent_,
|
|
213
238
|
loadLatency: this.loadLatencySeconds_,
|
|
@@ -238,6 +263,8 @@ shaka.util.Stats = class {
|
|
|
238
263
|
decodedFrames: NaN,
|
|
239
264
|
droppedFrames: NaN,
|
|
240
265
|
corruptedFrames: NaN,
|
|
266
|
+
stallsDetected: NaN,
|
|
267
|
+
gapsJumped: NaN,
|
|
241
268
|
estimatedBandwidth: NaN,
|
|
242
269
|
completionPercent: NaN,
|
|
243
270
|
loadLatency: NaN,
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -283,12 +283,14 @@ shaka.util.StreamUtils = class {
|
|
|
283
283
|
// both be considered the same codec: avc1.42c01e, avc1.4d401f
|
|
284
284
|
let baseVideoCodec = '';
|
|
285
285
|
if (variant.video) {
|
|
286
|
-
baseVideoCodec =
|
|
286
|
+
baseVideoCodec =
|
|
287
|
+
shaka.util.MimeUtils.getNormalizedCodec(variant.video.codecs);
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
let baseAudioCodec = '';
|
|
290
291
|
if (variant.audio) {
|
|
291
|
-
baseAudioCodec =
|
|
292
|
+
baseAudioCodec =
|
|
293
|
+
shaka.util.MimeUtils.getNormalizedCodec(variant.audio.codecs);
|
|
292
294
|
}
|
|
293
295
|
|
|
294
296
|
return baseVideoCodec + '-' + baseAudioCodec;
|
|
@@ -327,6 +329,13 @@ shaka.util.StreamUtils = class {
|
|
|
327
329
|
|
|
328
330
|
const video = variant.video;
|
|
329
331
|
|
|
332
|
+
if (variant.disabledUntilTime != 0) {
|
|
333
|
+
if (variant.disabledUntilTime > Date.now() / 1000) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
variant.disabledUntilTime = 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
330
339
|
// |video.width| and |video.height| can be undefined, which breaks
|
|
331
340
|
// the math, so make sure they are there first.
|
|
332
341
|
if (video && video.width && video.height) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
|
|
51
51
|
"less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
|
|
52
52
|
"material-design-lite": "^1.3.0",
|
|
53
|
-
"mux.js": "^
|
|
53
|
+
"mux.js": "^6.1.0",
|
|
54
54
|
"open-sans-fonts": "^1.6.2",
|
|
55
55
|
"postcss-less": "^6.0.0",
|
|
56
56
|
"pwacompat": "^2.0.17",
|
|
@@ -97,5 +97,8 @@
|
|
|
97
97
|
},
|
|
98
98
|
"dependencies": {
|
|
99
99
|
"eme-encryption-scheme-polyfill": "^2.0.3"
|
|
100
|
+
},
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=14"
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -11,6 +11,7 @@ describe('CastProxy', () => {
|
|
|
11
11
|
|
|
12
12
|
const originalCastSender = shaka.cast.CastSender;
|
|
13
13
|
const fakeAppId = 'fake app ID';
|
|
14
|
+
const fakeAndroidReceiverCompatible = false;
|
|
14
15
|
|
|
15
16
|
let mockPlayer;
|
|
16
17
|
let mockSender;
|
|
@@ -31,7 +32,8 @@ describe('CastProxy', () => {
|
|
|
31
32
|
mockPlayer = createMockPlayer();
|
|
32
33
|
mockSender = null;
|
|
33
34
|
|
|
34
|
-
proxy = new CastProxy(mockVideo, mockPlayer, fakeAppId
|
|
35
|
+
proxy = new CastProxy(mockVideo, mockPlayer, fakeAppId,
|
|
36
|
+
fakeAndroidReceiverCompatible);
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
afterEach(async () => {
|
|
@@ -13,6 +13,7 @@ describe('CastSender', () => {
|
|
|
13
13
|
const originalStatusDelay = shaka.cast.CastSender.STATUS_DELAY;
|
|
14
14
|
|
|
15
15
|
const fakeAppId = 'asdf';
|
|
16
|
+
const fakeAndroidReceiverCompatible = false;
|
|
16
17
|
const fakeInitState = {
|
|
17
18
|
manifest: null,
|
|
18
19
|
player: null,
|
|
@@ -52,7 +53,8 @@ describe('CastSender', () => {
|
|
|
52
53
|
sender = new CastSender(
|
|
53
54
|
fakeAppId, Util.spyFunc(onStatusChanged),
|
|
54
55
|
Util.spyFunc(onFirstCastStateUpdate), Util.spyFunc(onRemoteEvent),
|
|
55
|
-
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired)
|
|
56
|
+
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired),
|
|
57
|
+
fakeAndroidReceiverCompatible);
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
afterEach(async () => {
|
|
@@ -87,7 +89,12 @@ describe('CastSender', () => {
|
|
|
87
89
|
expect(sender.apiReady()).toBe(true);
|
|
88
90
|
expect(sender.hasReceivers()).toBe(false);
|
|
89
91
|
expect(onStatusChanged).toHaveBeenCalled();
|
|
90
|
-
expect(mockCastApi.SessionRequest).toHaveBeenCalledWith(
|
|
92
|
+
expect(mockCastApi.SessionRequest).toHaveBeenCalledWith(
|
|
93
|
+
fakeAppId,
|
|
94
|
+
/* capabilities= */ [],
|
|
95
|
+
/* timeout= */ null,
|
|
96
|
+
fakeAndroidReceiverCompatible,
|
|
97
|
+
/* credentialsData= */ null);
|
|
91
98
|
expect(mockCastApi.initialize).toHaveBeenCalled();
|
|
92
99
|
});
|
|
93
100
|
|
|
@@ -97,7 +104,12 @@ describe('CastSender', () => {
|
|
|
97
104
|
expect(sender.apiReady()).toBe(true);
|
|
98
105
|
expect(sender.hasReceivers()).toBe(false);
|
|
99
106
|
expect(onStatusChanged).toHaveBeenCalled();
|
|
100
|
-
expect(mockCastApi.SessionRequest).toHaveBeenCalledWith(
|
|
107
|
+
expect(mockCastApi.SessionRequest).toHaveBeenCalledWith(
|
|
108
|
+
fakeAppId,
|
|
109
|
+
/* capabilities= */ [],
|
|
110
|
+
/* timeout= */ null,
|
|
111
|
+
fakeAndroidReceiverCompatible,
|
|
112
|
+
/* credentialsData= */ null);
|
|
101
113
|
expect(mockCastApi.initialize).toHaveBeenCalled();
|
|
102
114
|
});
|
|
103
115
|
});
|
|
@@ -122,7 +134,8 @@ describe('CastSender', () => {
|
|
|
122
134
|
sender = new CastSender(
|
|
123
135
|
fakeAppId, Util.spyFunc(onStatusChanged),
|
|
124
136
|
Util.spyFunc(onFirstCastStateUpdate), Util.spyFunc(onRemoteEvent),
|
|
125
|
-
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired)
|
|
137
|
+
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired),
|
|
138
|
+
/* androidReceiverCompatible= */ false);
|
|
126
139
|
sender.init();
|
|
127
140
|
// You get an initial call to onStatusChanged when it initializes.
|
|
128
141
|
expect(onStatusChanged).toHaveBeenCalledTimes(3);
|
|
@@ -256,7 +269,8 @@ describe('CastSender', () => {
|
|
|
256
269
|
sender = new CastSender(
|
|
257
270
|
fakeAppId, Util.spyFunc(onStatusChanged),
|
|
258
271
|
Util.spyFunc(onFirstCastStateUpdate), Util.spyFunc(onRemoteEvent),
|
|
259
|
-
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired)
|
|
272
|
+
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired),
|
|
273
|
+
/* androidReceiverCompatible= */ false);
|
|
260
274
|
sender.init();
|
|
261
275
|
|
|
262
276
|
// The sender should automatically rejoin the session, without needing
|
|
@@ -288,7 +302,8 @@ describe('CastSender', () => {
|
|
|
288
302
|
sender = new CastSender(
|
|
289
303
|
fakeAppId, Util.spyFunc(onStatusChanged),
|
|
290
304
|
Util.spyFunc(onFirstCastStateUpdate), Util.spyFunc(onRemoteEvent),
|
|
291
|
-
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired)
|
|
305
|
+
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired),
|
|
306
|
+
/* androidReceiverCompatible= */ false);
|
|
292
307
|
sender.init();
|
|
293
308
|
|
|
294
309
|
expect(sender.isCasting()).toBe(false);
|
package/test/demo/demo_unit.js
CHANGED
|
@@ -94,7 +94,8 @@ describe('Demo', () => {
|
|
|
94
94
|
.add('playRangeStart')
|
|
95
95
|
.add('playRangeEnd')
|
|
96
96
|
.add('manifest.dash.keySystemsByURI')
|
|
97
|
-
.add('manifest.hls.mediaPlaylistFullMimeType')
|
|
97
|
+
.add('manifest.hls.mediaPlaylistFullMimeType')
|
|
98
|
+
.add('drm.keySystemsMapping');
|
|
98
99
|
|
|
99
100
|
/**
|
|
100
101
|
* @param {!Object} section
|