shaka-player 4.3.13 → 4.3.14
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +7 -0
- package/.github/workflows/release-please.yaml +2 -27
- package/AUTHORS +5 -0
- package/CHANGELOG.md +87 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +6 -0
- package/README.md +2 -0
- package/demo/common/asset.js +0 -1
- package/demo/common/assets.js +0 -1
- package/demo/custom.js +2 -0
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.debug.map +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/demo.compiled.map +1 -1
- package/dist/deps.js +6 -6
- package/dist/locales.js +29 -29
- package/dist/receiver.compiled.debug.map +1 -1
- package/dist/receiver.compiled.map +1 -1
- package/dist/shaka-player.compiled.d.ts +25 -5
- package/dist/shaka-player.compiled.debug.d.ts +25 -5
- package/dist/shaka-player.compiled.debug.externs.js +73 -45
- package/dist/shaka-player.compiled.debug.js +414 -396
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +73 -45
- package/dist/shaka-player.compiled.js +912 -903
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +37 -6
- package/dist/shaka-player.ui.debug.d.ts +37 -6
- package/dist/shaka-player.ui.debug.externs.js +74 -45
- package/dist/shaka-player.ui.debug.js +438 -418
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +74 -45
- package/dist/shaka-player.ui.js +1060 -1051
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/plugins.md +4 -0
- package/docs/tutorials/text-displayer.md +15 -0
- package/docs/tutorials/ui-customization.md +29 -0
- package/docs/tutorials/ui.md +4 -1
- package/externs/lcevc.js +6 -0
- package/externs/shaka/manifest_parser.js +8 -0
- package/externs/shaka/player.js +1 -1
- package/externs/shaka/text.js +3 -1
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +7 -2
- package/lib/ads/server_side_ad_manager.js +4 -0
- package/lib/cast/cast_proxy.js +18 -0
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/cea_decoder.js +11 -1
- package/lib/dash/dash_parser.js +75 -41
- package/lib/dash/mpd_utils.js +39 -27
- package/lib/hls/hls_parser.js +90 -27
- package/lib/hls/hls_utils.js +5 -5
- package/lib/media/content_workarounds.js +30 -0
- package/lib/media/drm_engine.js +16 -12
- package/lib/media/media_source_engine.js +4 -2
- package/lib/media/play_rate_controller.js +1 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_reference.js +5 -2
- package/lib/media/streaming_engine.js +50 -30
- package/lib/media/transmuxer.js +1 -1
- package/lib/media/video_wrapper.js +4 -3
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/session_deleter.js +0 -8
- package/lib/offline/storage.js +4 -1
- package/lib/player.js +79 -18
- package/lib/polyfill/media_capabilities.js +23 -3
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/simple_text_displayer.js +7 -6
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +33 -5
- package/lib/text/ui_text_displayer.js +81 -15
- package/lib/text/vtt_text_parser.js +15 -2
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/cmcd_manager.js +10 -0
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/manifest_parser_utils.js +14 -8
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +91 -36
- package/lib/util/platform.js +39 -2
- package/lib/util/player_configuration.js +5 -3
- package/lib/util/stream_utils.js +36 -28
- package/lib/util/xml_utils.js +5 -0
- package/package.json +2 -2
- package/test/cea/cea_decoder_unit.js +40 -0
- package/test/dash/dash_parser_live_unit.js +5 -8
- package/test/hls/hls_parser_unit.js +50 -3
- package/test/media/buffering_observer_unit.js +13 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +30 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/transmuxer_integration.js +2 -2
- package/test/offline/storage_integration.js +3 -0
- package/test/player_integration.js +40 -40
- package/test/player_unit.js +31 -0
- package/test/polyfill/media_capabilities_unit.js +12 -6
- package/test/test/util/fake_drm_engine.js +7 -0
- package/test/test/util/layout_tests.js +2 -1
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/test_scheme.js +3 -0
- package/test/test/util/waiter.js +5 -0
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/simple_text_displayer_unit.js +1 -1
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +49 -18
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/periods_unit.js +113 -0
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/externs/ui.js +24 -0
- package/ui/language_utils.js +11 -5
- package/ui/play_button.js +20 -0
- package/ui/playback_rate_selection.js +4 -0
- package/ui/presentation_time.js +5 -5
- package/ui/small_play_button.js +2 -2
- package/ui/volume_bar.js +10 -6
- /package/.github/{config.yml → ISSUE_TEMPLATE/config.yml} +0 -0
|
@@ -403,8 +403,6 @@ shaka.text.VttTextParser = class {
|
|
|
403
403
|
cue = new shaka.text.Cue(start, end, '');
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
VttTextParser.parseCueStyles(payload, cue, styles);
|
|
407
|
-
|
|
408
406
|
// Parse optional settings.
|
|
409
407
|
parser.skipWhitespace();
|
|
410
408
|
let word = parser.readWord();
|
|
@@ -418,6 +416,8 @@ shaka.text.VttTextParser = class {
|
|
|
418
416
|
word = parser.readWord();
|
|
419
417
|
}
|
|
420
418
|
|
|
419
|
+
VttTextParser.parseCueStyles(payload, cue, styles);
|
|
420
|
+
|
|
421
421
|
if (id != null) {
|
|
422
422
|
cue.id = id;
|
|
423
423
|
}
|
|
@@ -433,6 +433,11 @@ shaka.text.VttTextParser = class {
|
|
|
433
433
|
*/
|
|
434
434
|
static parseCueStyles(payload, rootCue, styles) {
|
|
435
435
|
const VttTextParser = shaka.text.VttTextParser;
|
|
436
|
+
// Optimization for unstyled payloads.
|
|
437
|
+
if (!payload.includes('<')) {
|
|
438
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
436
441
|
if (styles.size === 0) {
|
|
437
442
|
VttTextParser.addDefaultTextColor_(styles);
|
|
438
443
|
}
|
|
@@ -688,6 +693,14 @@ shaka.text.VttTextParser = class {
|
|
|
688
693
|
static generateCueFromElement_(element, rootCue, cues, styles) {
|
|
689
694
|
const VttTextParser = shaka.text.VttTextParser;
|
|
690
695
|
const nestedCue = rootCue.clone();
|
|
696
|
+
// We don't want propagate some properties.
|
|
697
|
+
nestedCue.nestedCues = [];
|
|
698
|
+
nestedCue.payload = '';
|
|
699
|
+
// We don't want propagate some position settings
|
|
700
|
+
nestedCue.line = null;
|
|
701
|
+
nestedCue.region = new shaka.text.CueRegion();
|
|
702
|
+
nestedCue.position = null;
|
|
703
|
+
nestedCue.size = 0;
|
|
691
704
|
if (element.nodeType === Node.ELEMENT_NODE && element.nodeName) {
|
|
692
705
|
const bold = shaka.text.Cue.fontWeight.BOLD;
|
|
693
706
|
const italic = shaka.text.Cue.fontStyle.ITALIC;
|
package/lib/util/buffer_utils.js
CHANGED
|
@@ -110,6 +110,23 @@ shaka.util.BufferUtils = class {
|
|
|
110
110
|
return shaka.util.BufferUtils.view_(data, offset, length, Uint8Array);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new Uint16Array view on the same buffer. This clamps the values
|
|
115
|
+
* to be within the same view (i.e. you can't use this to move past the end
|
|
116
|
+
* of the view, even if the underlying buffer is larger). However, you can
|
|
117
|
+
* pass a negative offset to access the data before the view.
|
|
118
|
+
*
|
|
119
|
+
* @param {BufferSource} data
|
|
120
|
+
* @param {number=} offset The offset from the beginning of this data's view
|
|
121
|
+
* to start the new view at.
|
|
122
|
+
* @param {number=} length The byte length of the new view.
|
|
123
|
+
* @return {!Uint16Array}
|
|
124
|
+
* @export
|
|
125
|
+
*/
|
|
126
|
+
static toUint16(data, offset = 0, length = Infinity) {
|
|
127
|
+
return shaka.util.BufferUtils.view_(data, offset, length, Uint16Array);
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
/**
|
|
114
131
|
* Creates a DataView over the given buffer.
|
|
115
132
|
*
|
|
@@ -135,15 +152,20 @@ shaka.util.BufferUtils = class {
|
|
|
135
152
|
*/
|
|
136
153
|
static view_(data, offset, length, Type) {
|
|
137
154
|
const buffer = shaka.util.BufferUtils.unsafeGetArrayBuffer_(data);
|
|
155
|
+
let bytesPerElement = 1;
|
|
156
|
+
if ('BYTES_PER_ELEMENT' in Type) {
|
|
157
|
+
bytesPerElement = Type.BYTES_PER_ELEMENT;
|
|
158
|
+
}
|
|
138
159
|
// Absolute end of the |data| view within |buffer|.
|
|
139
160
|
/** @suppress {strictMissingProperties} */
|
|
140
|
-
const dataEnd = (data.byteOffset || 0) + data.byteLength
|
|
161
|
+
const dataEnd = ((data.byteOffset || 0) + data.byteLength) /
|
|
162
|
+
bytesPerElement;
|
|
141
163
|
// Absolute start of the result within |buffer|.
|
|
142
164
|
/** @suppress {strictMissingProperties} */
|
|
143
|
-
const rawStart = (data.byteOffset || 0) + offset;
|
|
144
|
-
const start = Math.max(0, Math.min(rawStart, dataEnd));
|
|
165
|
+
const rawStart = ((data.byteOffset || 0) + offset) / bytesPerElement;
|
|
166
|
+
const start = Math.floor(Math.max(0, Math.min(rawStart, dataEnd)));
|
|
145
167
|
// Absolute end of the result within |buffer|.
|
|
146
|
-
const end = Math.min(start + Math.max(length, 0), dataEnd);
|
|
168
|
+
const end = Math.floor(Math.min(start + Math.max(length, 0), dataEnd));
|
|
147
169
|
return new Type(buffer, start, end - start);
|
|
148
170
|
}
|
|
149
171
|
};
|
package/lib/util/cmcd_manager.js
CHANGED
|
@@ -332,6 +332,15 @@ shaka.util.CmcdManager = class {
|
|
|
332
332
|
*/
|
|
333
333
|
getObjectTypeFromMimeType_(mimeType) {
|
|
334
334
|
switch (mimeType.toLowerCase()) {
|
|
335
|
+
case 'audio/mp4':
|
|
336
|
+
case 'audio/webm':
|
|
337
|
+
case 'audio/ogg':
|
|
338
|
+
case 'audio/mpeg':
|
|
339
|
+
case 'audio/aac':
|
|
340
|
+
case 'audio/flac':
|
|
341
|
+
case 'audio/wav':
|
|
342
|
+
return shaka.util.CmcdManager.ObjectType.AUDIO;
|
|
343
|
+
|
|
335
344
|
case 'video/webm':
|
|
336
345
|
case 'video/mp4':
|
|
337
346
|
case 'video/mpeg':
|
|
@@ -342,6 +351,7 @@ shaka.util.CmcdManager = class {
|
|
|
342
351
|
case 'application/vnd.apple.mpegurl':
|
|
343
352
|
case 'application/dash+xml':
|
|
344
353
|
case 'video/vnd.mpeg.dash.mpd':
|
|
354
|
+
case 'application/vnd.ms-sstr+xml':
|
|
345
355
|
return shaka.util.CmcdManager.ObjectType.MANIFEST;
|
|
346
356
|
|
|
347
357
|
default:
|
|
@@ -192,7 +192,7 @@ shaka.util.FairPlayUtils = class {
|
|
|
192
192
|
const stringToArray = (string) => {
|
|
193
193
|
// 2 bytes for each char
|
|
194
194
|
const buffer = new ArrayBuffer(string.length * 2);
|
|
195
|
-
const array =
|
|
195
|
+
const array = shaka.util.BufferUtils.toUint16(buffer);
|
|
196
196
|
for (let i = 0, strLen = string.length; i < strLen; i++) {
|
|
197
197
|
array[i] = string.charCodeAt(i);
|
|
198
198
|
}
|
|
@@ -8,7 +8,6 @@ goog.provide('shaka.util.ManifestParserUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.Uri');
|
|
10
10
|
goog.require('shaka.util.Error');
|
|
11
|
-
goog.require('shaka.util.Functional');
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -24,7 +23,6 @@ shaka.util.ManifestParserUtils = class {
|
|
|
24
23
|
* @return {!Array.<string>}
|
|
25
24
|
*/
|
|
26
25
|
static resolveUris(baseUris, relativeUris) {
|
|
27
|
-
const Functional = shaka.util.Functional;
|
|
28
26
|
if (relativeUris.length == 0) {
|
|
29
27
|
return baseUris;
|
|
30
28
|
}
|
|
@@ -36,12 +34,18 @@ shaka.util.ManifestParserUtils = class {
|
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
const relativeAsGoog = relativeUris.map((uri) => new goog.Uri(uri));
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
|
|
38
|
+
// For each base URI, this code resolves it with every relative URI.
|
|
39
|
+
// The result is a single array containing all the resolved URIs.
|
|
40
|
+
const resolvedUris = [];
|
|
41
|
+
for (const baseStr of baseUris) {
|
|
42
|
+
const base = new goog.Uri(baseStr);
|
|
43
|
+
for (const relative of relativeAsGoog) {
|
|
44
|
+
resolvedUris.push(base.resolve(relative).toString());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return resolvedUris;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
|
|
@@ -172,6 +176,8 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
172
176
|
/^avc/,
|
|
173
177
|
/^hev/,
|
|
174
178
|
/^hvc/,
|
|
179
|
+
/^vvc/,
|
|
180
|
+
/^vvi/,
|
|
175
181
|
/^vp0?[89]/,
|
|
176
182
|
/^av01/,
|
|
177
183
|
/^dvh/,
|
package/lib/util/object_utils.js
CHANGED
|
@@ -19,7 +19,7 @@ shaka.util.ObjectUtils = class {
|
|
|
19
19
|
* @return {T}
|
|
20
20
|
*/
|
|
21
21
|
static cloneObject(arg) {
|
|
22
|
-
const seenObjects = new
|
|
22
|
+
const seenObjects = new WeakSet();
|
|
23
23
|
// This recursively clones the value |val|, using the captured variable
|
|
24
24
|
// |seenObjects| to track the objects we have already cloned.
|
|
25
25
|
/** @suppress {strictMissingProperties} */
|
package/lib/util/periods.js
CHANGED
|
@@ -90,6 +90,37 @@ shaka.util.PeriodCombiner = class {
|
|
|
90
90
|
return this.imageStreams_;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Returns an object that contains arrays of streams by type
|
|
95
|
+
* @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
|
|
96
|
+
* @return {{
|
|
97
|
+
* audioStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
98
|
+
* videoStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
99
|
+
* textStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
100
|
+
* imageStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>
|
|
101
|
+
* }}
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
getStreamsPerPeriod_(periods) {
|
|
105
|
+
const audioStreamsPerPeriod = [];
|
|
106
|
+
const videoStreamsPerPeriod = [];
|
|
107
|
+
const textStreamsPerPeriod = [];
|
|
108
|
+
const imageStreamsPerPeriod = [];
|
|
109
|
+
|
|
110
|
+
for (const period of periods) {
|
|
111
|
+
audioStreamsPerPeriod.push(period.audioStreams);
|
|
112
|
+
videoStreamsPerPeriod.push(period.videoStreams);
|
|
113
|
+
textStreamsPerPeriod.push(period.textStreams);
|
|
114
|
+
imageStreamsPerPeriod.push(period.imageStreams);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
audioStreamsPerPeriod,
|
|
118
|
+
videoStreamsPerPeriod,
|
|
119
|
+
textStreamsPerPeriod,
|
|
120
|
+
imageStreamsPerPeriod,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
/**
|
|
94
125
|
* @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
|
|
95
126
|
* @param {boolean} isDynamic
|
|
@@ -136,14 +167,12 @@ shaka.util.PeriodCombiner = class {
|
|
|
136
167
|
return;
|
|
137
168
|
}
|
|
138
169
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const imageStreamsPerPeriod = periods.map(
|
|
146
|
-
(period) => period.imageStreams);
|
|
170
|
+
const {
|
|
171
|
+
audioStreamsPerPeriod,
|
|
172
|
+
videoStreamsPerPeriod,
|
|
173
|
+
textStreamsPerPeriod,
|
|
174
|
+
imageStreamsPerPeriod,
|
|
175
|
+
} = this.getStreamsPerPeriod_(periods);
|
|
147
176
|
|
|
148
177
|
// It's okay to have a period with no text or images, but our algorithm
|
|
149
178
|
// fails on any period without matching streams. So we add dummy streams
|
|
@@ -159,33 +188,32 @@ shaka.util.PeriodCombiner = class {
|
|
|
159
188
|
ContentType.IMAGE));
|
|
160
189
|
}
|
|
161
190
|
|
|
162
|
-
await
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
shaka.util.PeriodCombiner.concatenateStreams_);
|
|
191
|
+
await Promise.all([
|
|
192
|
+
shaka.util.PeriodCombiner.combine_(
|
|
193
|
+
this.audioStreams_,
|
|
194
|
+
audioStreamsPerPeriod,
|
|
195
|
+
firstNewPeriodIndex,
|
|
196
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
197
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
198
|
+
shaka.util.PeriodCombiner.combine_(
|
|
199
|
+
this.videoStreams_,
|
|
200
|
+
videoStreamsPerPeriod,
|
|
201
|
+
firstNewPeriodIndex,
|
|
202
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
203
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
204
|
+
shaka.util.PeriodCombiner.combine_(
|
|
205
|
+
this.textStreams_,
|
|
206
|
+
textStreamsPerPeriod,
|
|
207
|
+
firstNewPeriodIndex,
|
|
208
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
209
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
210
|
+
shaka.util.PeriodCombiner.combine_(
|
|
211
|
+
this.imageStreams_,
|
|
212
|
+
imageStreamsPerPeriod,
|
|
213
|
+
firstNewPeriodIndex,
|
|
214
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
215
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
216
|
+
]);
|
|
189
217
|
}
|
|
190
218
|
|
|
191
219
|
// Create variants for all audio/video combinations.
|
|
@@ -1102,6 +1130,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
1102
1130
|
return true;
|
|
1103
1131
|
}
|
|
1104
1132
|
|
|
1133
|
+
// Forced subtitles should be treated as unique streams
|
|
1134
|
+
if (outputStream.forced !== candidate.forced) {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1105
1138
|
const languageRelatedness = LanguageUtils.relatedness(
|
|
1106
1139
|
outputStream.language, candidate.language);
|
|
1107
1140
|
|
|
@@ -1180,7 +1213,18 @@ shaka.util.PeriodCombiner = class {
|
|
|
1180
1213
|
return false;
|
|
1181
1214
|
}
|
|
1182
1215
|
|
|
1183
|
-
// If language-based differences haven't decided this, look at
|
|
1216
|
+
// If language-based differences haven't decided this, look at labels.
|
|
1217
|
+
// If available options differ, look does any matches with output stream.
|
|
1218
|
+
if (best.label !== candidate.label) {
|
|
1219
|
+
if (outputStream.label === best.label) {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
if (outputStream.label === candidate.label) {
|
|
1223
|
+
return true;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// If label-based differences haven't decided this, look at roles. If
|
|
1184
1228
|
// the candidate has more roles in common with the output, upgrade to the
|
|
1185
1229
|
// candidate.
|
|
1186
1230
|
if (outputStream.roles.length) {
|
|
@@ -1383,6 +1427,17 @@ shaka.util.PeriodCombiner = class {
|
|
|
1383
1427
|
return false;
|
|
1384
1428
|
}
|
|
1385
1429
|
|
|
1430
|
+
// If language-based differences haven't decided this, look at labels.
|
|
1431
|
+
// If available options differ, look does any matches with output stream.
|
|
1432
|
+
if (best.label !== candidate.label) {
|
|
1433
|
+
if (outputStream.label === best.label) {
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
if (outputStream.label === candidate.label) {
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1386
1441
|
// If the candidate has more roles in common with the output, upgrade to the
|
|
1387
1442
|
// candidate.
|
|
1388
1443
|
if (outputStream.roles.length) {
|
package/lib/util/platform.js
CHANGED
|
@@ -199,6 +199,15 @@ shaka.util.Platform = class {
|
|
|
199
199
|
!shaka.util.Platform.isEdge();
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Check if the current platform is Firefox.
|
|
204
|
+
*
|
|
205
|
+
* @return {boolean}
|
|
206
|
+
*/
|
|
207
|
+
static isFirefox() {
|
|
208
|
+
return shaka.util.Platform.userAgentContains_('Firefox');
|
|
209
|
+
}
|
|
210
|
+
|
|
202
211
|
/**
|
|
203
212
|
* Check if the current platform is from Apple.
|
|
204
213
|
*
|
|
@@ -213,10 +222,12 @@ shaka.util.Platform = class {
|
|
|
213
222
|
return !!navigator.vendor && navigator.vendor.includes('Apple') &&
|
|
214
223
|
!shaka.util.Platform.isTizen() &&
|
|
215
224
|
!shaka.util.Platform.isEOS() &&
|
|
225
|
+
!shaka.util.Platform.isAPL() &&
|
|
216
226
|
!shaka.util.Platform.isVirginMedia() &&
|
|
217
227
|
!shaka.util.Platform.isOrange() &&
|
|
218
228
|
!shaka.util.Platform.isPS4() &&
|
|
219
|
-
!shaka.util.Platform.isAmazonFireTV()
|
|
229
|
+
!shaka.util.Platform.isAmazonFireTV() &&
|
|
230
|
+
!shaka.util.Platform.isWPE();
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
/**
|
|
@@ -271,6 +282,14 @@ shaka.util.Platform = class {
|
|
|
271
282
|
return shaka.util.Platform.userAgentContains_('AFT');
|
|
272
283
|
}
|
|
273
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Check if the current platform is Comcast X1.
|
|
287
|
+
* @return {boolean}
|
|
288
|
+
*/
|
|
289
|
+
static isWPE() {
|
|
290
|
+
return shaka.util.Platform.userAgentContains_('WPE');
|
|
291
|
+
}
|
|
292
|
+
|
|
274
293
|
/**
|
|
275
294
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
276
295
|
*
|
|
@@ -327,6 +346,15 @@ shaka.util.Platform = class {
|
|
|
327
346
|
return shaka.util.Platform.userAgentContains_('PC=EOS');
|
|
328
347
|
}
|
|
329
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Check if the current platform is an APL set-top box.
|
|
351
|
+
*
|
|
352
|
+
* @return {boolean}
|
|
353
|
+
*/
|
|
354
|
+
static isAPL() {
|
|
355
|
+
return shaka.util.Platform.userAgentContains_('PC=APL');
|
|
356
|
+
}
|
|
357
|
+
|
|
330
358
|
/**
|
|
331
359
|
* Guesses if the platform is a mobile one (iOS or Android).
|
|
332
360
|
*
|
|
@@ -368,6 +396,15 @@ shaka.util.Platform = class {
|
|
|
368
396
|
return navigator.platform.toLowerCase().includes('mac');
|
|
369
397
|
}
|
|
370
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Return true if the platform is a Android, regardless of the browser.
|
|
401
|
+
*
|
|
402
|
+
* @return {boolean}
|
|
403
|
+
*/
|
|
404
|
+
static isAndroid() {
|
|
405
|
+
return shaka.util.Platform.userAgentContains_('Android');
|
|
406
|
+
}
|
|
407
|
+
|
|
371
408
|
/**
|
|
372
409
|
* Check if the user agent contains a key. This is the best way we know of
|
|
373
410
|
* right now to detect platforms. If there is a better way, please send a
|
|
@@ -429,7 +466,7 @@ shaka.util.Platform = class {
|
|
|
429
466
|
*/
|
|
430
467
|
static requiresEncryptionInfoInAllInitSegments() {
|
|
431
468
|
const Platform = shaka.util.Platform;
|
|
432
|
-
return Platform.isTizen() || Platform.isXboxOne();
|
|
469
|
+
return Platform.isTizen() || Platform.isXboxOne() || Platform.isOrange();
|
|
433
470
|
}
|
|
434
471
|
|
|
435
472
|
/**
|
|
@@ -189,13 +189,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
189
189
|
parsePrftBox: false,
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
-
// WebOS, Tizen, and
|
|
193
|
-
// slowly to seeking.
|
|
192
|
+
// WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
|
|
193
|
+
// that respond slowly to seeking.
|
|
194
|
+
// Therefore we should not seek when we detect a stall
|
|
194
195
|
// on one of these platforms. Instead, default stallSkip to 0 to force the
|
|
195
196
|
// stall detector to pause and play instead.
|
|
196
197
|
if (shaka.util.Platform.isWebOS() ||
|
|
197
198
|
shaka.util.Platform.isTizen() ||
|
|
198
|
-
shaka.util.Platform.isChromecast()
|
|
199
|
+
shaka.util.Platform.isChromecast() ||
|
|
200
|
+
shaka.util.Platform.isHisense()) {
|
|
199
201
|
streaming.stallSkip = 0;
|
|
200
202
|
}
|
|
201
203
|
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -434,19 +434,36 @@ shaka.util.StreamUtils = class {
|
|
|
434
434
|
|
|
435
435
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
436
436
|
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
437
|
+
|
|
438
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
439
|
+
const Capabilities = shaka.media.Capabilities;
|
|
440
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
441
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
442
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
443
|
+
const isXboxOne = shaka.util.Platform.isXboxOne();
|
|
444
|
+
|
|
445
|
+
const isFirefoxAndroid = shaka.util.Platform.isFirefox() &&
|
|
446
|
+
shaka.util.Platform.isAndroid();
|
|
447
|
+
|
|
437
448
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
438
449
|
// See: https://github.com/shaka-project/shaka-player/issues/3860
|
|
439
450
|
const video = variant.video;
|
|
451
|
+
const videoWidth = (video && video.width) || 0;
|
|
452
|
+
const videoHeight = (video && video.height) || 0;
|
|
440
453
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
454
|
+
// See: https://github.com/shaka-project/shaka-player/issues/3380
|
|
455
|
+
// Note: it makes sense to drop early
|
|
456
|
+
if (isXboxOne && video &&
|
|
457
|
+
(videoWidth > 1920 || videoHeight > 1080) &&
|
|
458
|
+
(video.codecs.includes('avc1.') || video.codecs.includes('avc3.'))
|
|
459
|
+
) {
|
|
460
|
+
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
461
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
446
464
|
|
|
447
465
|
if (video) {
|
|
448
466
|
let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
449
|
-
|
|
450
467
|
// For multiplexed streams. Here we must check the audio of the
|
|
451
468
|
// stream to see if it is compatible.
|
|
452
469
|
if (video.codecs.includes(',')) {
|
|
@@ -455,7 +472,6 @@ shaka.util.StreamUtils = class {
|
|
|
455
472
|
videoCodecs = ManifestParserUtils.guessCodecs(
|
|
456
473
|
ContentType.VIDEO, allCodecs);
|
|
457
474
|
videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
458
|
-
|
|
459
475
|
let audioCodecs = ManifestParserUtils.guessCodecs(
|
|
460
476
|
ContentType.AUDIO, allCodecs);
|
|
461
477
|
audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
@@ -484,6 +500,17 @@ shaka.util.StreamUtils = class {
|
|
|
484
500
|
|
|
485
501
|
const audio = variant.audio;
|
|
486
502
|
|
|
503
|
+
// See: https://github.com/shaka-project/shaka-player/issues/6111
|
|
504
|
+
// It seems that Firefox Android reports that it supports
|
|
505
|
+
// Opus + Widevine, but it is not actually supported.
|
|
506
|
+
// It makes sense to drop early.
|
|
507
|
+
if (isFirefoxAndroid && audio && audio.encrypted &&
|
|
508
|
+
audio.codecs.toLowerCase().includes('opus')) {
|
|
509
|
+
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
510
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
487
514
|
if (audio) {
|
|
488
515
|
const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
489
516
|
const fullType = MimeUtils.getFullOrConvertedType(
|
|
@@ -497,17 +524,6 @@ shaka.util.StreamUtils = class {
|
|
|
497
524
|
audio.codecs = codecs;
|
|
498
525
|
}
|
|
499
526
|
|
|
500
|
-
// See: https://github.com/shaka-project/shaka-player/issues/3380
|
|
501
|
-
if (shaka.util.Platform.isXboxOne() && video &&
|
|
502
|
-
((video.width && video.width > 1920) ||
|
|
503
|
-
(video.height && video.height > 1080)) &&
|
|
504
|
-
(video.codecs.includes('avc1.') ||
|
|
505
|
-
video.codecs.includes('avc3.'))) {
|
|
506
|
-
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
507
|
-
StreamUtils.getVariantSummaryString_(variant));
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
527
|
const supported = variant.decodingInfos.some((decodingInfo) => {
|
|
512
528
|
return decodingInfo.supported;
|
|
513
529
|
});
|
|
@@ -790,7 +806,7 @@ shaka.util.StreamUtils = class {
|
|
|
790
806
|
// currently don't support 'fLaC', while 'flac' is supported by most
|
|
791
807
|
// major browsers.
|
|
792
808
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
|
793
|
-
if (codecs === 'fLaC') {
|
|
809
|
+
if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
|
|
794
810
|
return 'flac';
|
|
795
811
|
}
|
|
796
812
|
|
|
@@ -799,15 +815,7 @@ shaka.util.StreamUtils = class {
|
|
|
799
815
|
return 'opus';
|
|
800
816
|
}
|
|
801
817
|
|
|
802
|
-
|
|
803
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
804
|
-
// See https://github.com/shaka-project/shaka-player/issues/2989 for
|
|
805
|
-
// details.
|
|
806
|
-
if (shaka.util.Platform.isTizen()) {
|
|
807
|
-
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
808
|
-
} else {
|
|
809
|
-
return codecs;
|
|
810
|
-
}
|
|
818
|
+
return codecs;
|
|
811
819
|
}
|
|
812
820
|
|
|
813
821
|
|
package/lib/util/xml_utils.js
CHANGED
|
@@ -384,6 +384,11 @@ shaka.util.XmlUtils = class {
|
|
|
384
384
|
return null;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
// Cobalt browser doesn't support document.createNodeIterator.
|
|
388
|
+
if (!('createNodeIterator' in document)) {
|
|
389
|
+
return rootElem;
|
|
390
|
+
}
|
|
391
|
+
|
|
387
392
|
// SECURITY: Verify that the document does not contain elements from the
|
|
388
393
|
// HTML or SVG namespaces, which could trigger script execution and XSS.
|
|
389
394
|
const iterator = document.createNodeIterator(
|
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.3.
|
|
4
|
+
"version": "4.3.14",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"karma-coverage": "^2.2.0",
|
|
43
43
|
"karma-jasmine": "^4.0.1",
|
|
44
44
|
"karma-jasmine-ajax": "^0.1.13",
|
|
45
|
-
"karma-local-wd-launcher": "^1.6.
|
|
45
|
+
"karma-local-wd-launcher": "^1.6.7",
|
|
46
46
|
"karma-opera-launcher": "^1.0.0",
|
|
47
47
|
"karma-sourcemap-loader": "^0.3.8",
|
|
48
48
|
"karma-spec-reporter": "^0.0.34",
|
|
@@ -44,6 +44,39 @@ describe('CeaDecoder', () => {
|
|
|
44
44
|
decoder.clear();
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
it('painton captions on CC4', () => {
|
|
48
|
+
const controlCount = 0x03;
|
|
49
|
+
const captionData = 0xc0 | controlCount;
|
|
50
|
+
const paintonCaptionCC4Packet = new Uint8Array([
|
|
51
|
+
...atscCaptionInitBytes, captionData, /* padding= */ 0xff,
|
|
52
|
+
0xfd, 0x9d, 0x29, // Paint-on mode (RDC control code).
|
|
53
|
+
0xfd, 0xf4, 0xe5, // t, e
|
|
54
|
+
0xfd, 0x73, 0xf4, // s, t
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const startTimeCaption1 = 1;
|
|
58
|
+
const startTimeCaption2 = 2;
|
|
59
|
+
const expectedText = 'test';
|
|
60
|
+
|
|
61
|
+
const topLevelCue = new shaka.text.Cue(startTimeCaption1,
|
|
62
|
+
startTimeCaption2, '');
|
|
63
|
+
topLevelCue.nestedCues = [
|
|
64
|
+
CeaUtils.createDefaultCue(
|
|
65
|
+
startTimeCaption1, startTimeCaption2, expectedText),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const expectedCaptions = [{
|
|
69
|
+
stream: 'CC4',
|
|
70
|
+
cue: topLevelCue,
|
|
71
|
+
}];
|
|
72
|
+
|
|
73
|
+
decoder.extract(paintonCaptionCC4Packet, startTimeCaption1);
|
|
74
|
+
decoder.extract(eraseDisplayedMemory, startTimeCaption2);
|
|
75
|
+
const captions = decoder.decode();
|
|
76
|
+
|
|
77
|
+
expect(captions).toEqual(expectedCaptions);
|
|
78
|
+
});
|
|
79
|
+
|
|
47
80
|
it('green and underlined popon caption data on CC3', () => {
|
|
48
81
|
const controlCount = 0x08;
|
|
49
82
|
const captionData = 0xc0 | controlCount;
|
|
@@ -486,6 +519,13 @@ describe('CeaDecoder', () => {
|
|
|
486
519
|
|
|
487
520
|
expect(decoder.reset).toHaveBeenCalledTimes(1);
|
|
488
521
|
});
|
|
522
|
+
|
|
523
|
+
it('does not attempt to extract SEI packet that is too short', () => {
|
|
524
|
+
const badData = new Uint8Array([0xb5]);
|
|
525
|
+
decoder.extract(badData, 0);
|
|
526
|
+
const captions = decoder.decode();
|
|
527
|
+
expect(captions.length).toBe(0);
|
|
528
|
+
});
|
|
489
529
|
});
|
|
490
530
|
|
|
491
531
|
describe('decodes CEA-708', () => {
|