shaka-player 4.2.4 → 4.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build-and-test.yaml +13 -0
- package/AUTHORS +2 -0
- package/CHANGELOG.md +35 -0
- package/CONTRIBUTORS +2 -0
- package/demo/index.html +1 -1
- package/dist/demo.compiled.debug.js +64 -64
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +64 -64
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +3 -2
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +12 -12
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +12 -12
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.externs.js +93 -79
- package/dist/shaka-player.compiled.debug.js +585 -577
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +93 -79
- package/dist/shaka-player.compiled.js +816 -814
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +10 -0
- package/dist/shaka-player.ui.debug.d.ts +10 -0
- package/dist/shaka-player.ui.debug.externs.js +93 -79
- package/dist/shaka-player.ui.debug.js +615 -606
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +93 -79
- package/dist/shaka-player.ui.js +964 -962
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/player.js +2 -1
- package/lib/ads/ad_manager.js +2 -2
- package/lib/cea/cea708_window.js +2 -2
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +24 -6
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_engine.js +16 -0
- package/lib/media/segment_reference.js +3 -0
- package/lib/media/streaming_engine.js +33 -10
- package/lib/player.js +6 -2
- package/lib/polyfill/abort_controller.js +143 -0
- package/lib/polyfill/media_capabilities.js +59 -9
- package/lib/text/ttml_text_parser.js +32 -7
- package/lib/text/ui_text_displayer.js +22 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +10 -3
- package/lib/util/stream_utils.js +30 -10
- package/package.json +1 -1
- package/shaka-player.uncompiled.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +1 -0
- package/test/hls/hls_parser_unit.js +1 -0
- package/test/media/drm_engine_unit.js +11 -1
- package/test/media/media_source_engine_integration.js +78 -2
- package/test/player_integration.js +10 -3
- package/test/polyfill/media_capabilities_unit.js +261 -0
- package/test/test/boot.js +29 -11
- package/test/test/util/manifest_parser_util.js +4 -3
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +5 -4
- package/test/test/util/util.js +3 -2
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/ttml_text_parser_unit.js +119 -86
- package/test/text/ui_text_displayer_unit.js +76 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/stream_utils_unit.js +18 -0
- package/test/util/xml_utils_unit.js +6 -1
- package/ui/controls.js +49 -36
|
@@ -136,7 +136,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
const cue = TtmlTextParser.parseCue_(
|
|
139
|
-
body, time
|
|
139
|
+
body, time, rateInfo, metadataElements, styles,
|
|
140
140
|
regionElements, cueRegions, whitespaceTrim,
|
|
141
141
|
cellResolutionInfo, /* parentCueElement= */ null,
|
|
142
142
|
/* isContent= */ false);
|
|
@@ -157,7 +157,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
157
157
|
* Parses a TTML node into a Cue.
|
|
158
158
|
*
|
|
159
159
|
* @param {!Node} cueNode
|
|
160
|
-
* @param {
|
|
160
|
+
* @param {shaka.extern.TextParser.TimeContext} timeContext
|
|
161
161
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
162
162
|
* @param {!Array.<!Element>} metadataElements
|
|
163
163
|
* @param {!Array.<!Element>} styles
|
|
@@ -171,7 +171,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
171
171
|
* @private
|
|
172
172
|
*/
|
|
173
173
|
static parseCue_(
|
|
174
|
-
cueNode,
|
|
174
|
+
cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
|
|
175
175
|
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
|
|
176
176
|
/** @type {Element} */
|
|
177
177
|
let cueElement;
|
|
@@ -236,7 +236,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
236
236
|
for (const childNode of cueElement.childNodes) {
|
|
237
237
|
const nestedCue = shaka.text.TtmlTextParser.parseCue_(
|
|
238
238
|
childNode,
|
|
239
|
-
|
|
239
|
+
timeContext,
|
|
240
240
|
rateInfo,
|
|
241
241
|
metadataElements,
|
|
242
242
|
styles,
|
|
@@ -293,15 +293,20 @@ shaka.text.TtmlTextParser = class {
|
|
|
293
293
|
if (start == null) {
|
|
294
294
|
start = 0;
|
|
295
295
|
}
|
|
296
|
-
start +=
|
|
296
|
+
start += timeContext.periodStart;
|
|
297
297
|
|
|
298
298
|
// If end is null, that means the duration is effectively infinite.
|
|
299
299
|
if (end == null) {
|
|
300
300
|
end = Infinity;
|
|
301
301
|
} else {
|
|
302
|
-
end +=
|
|
302
|
+
end += timeContext.periodStart;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
// Clip times to segment boundaries.
|
|
306
|
+
// https://github.com/shaka-project/shaka-player/issues/4631
|
|
307
|
+
start = Math.max(start, timeContext.segmentStart);
|
|
308
|
+
end = Math.min(end, timeContext.segmentEnd);
|
|
309
|
+
|
|
305
310
|
if (!hasTimeAttributes && nestedCues.length > 0) {
|
|
306
311
|
// If no time is defined for this cue, base the timing information on
|
|
307
312
|
// the time of the nested cues. In the case of multiple nested cues with
|
|
@@ -569,8 +574,28 @@ shaka.text.TtmlTextParser = class {
|
|
|
569
574
|
|
|
570
575
|
const fontFamily = TtmlTextParser.getStyleAttribute_(
|
|
571
576
|
cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
|
|
577
|
+
// See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
|
|
572
578
|
if (fontFamily) {
|
|
573
|
-
|
|
579
|
+
switch (fontFamily) {
|
|
580
|
+
case 'monospaceSerif':
|
|
581
|
+
cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
|
|
582
|
+
break;
|
|
583
|
+
case 'proportionalSansSerif':
|
|
584
|
+
cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
|
|
585
|
+
break;
|
|
586
|
+
case 'sansSerif':
|
|
587
|
+
cue.fontFamily = 'sans-serif';
|
|
588
|
+
break;
|
|
589
|
+
case 'monospaceSansSerif':
|
|
590
|
+
cue.fontFamily = 'Consolas,monospace';
|
|
591
|
+
break;
|
|
592
|
+
case 'proportionalSerif':
|
|
593
|
+
cue.fontFamily = 'serif';
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
cue.fontFamily = fontFamily;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
574
599
|
}
|
|
575
600
|
|
|
576
601
|
const fontWeight = TtmlTextParser.getStyleAttribute_(
|
|
@@ -199,6 +199,19 @@ shaka.text.UITextDisplayer = class {
|
|
|
199
199
|
this.isTextVisible_ = on;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
isElementUnderTextContainer_(elemToCheck) {
|
|
206
|
+
while (elemToCheck != null) {
|
|
207
|
+
if (elemToCheck == this.textContainer_) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
elemToCheck = elemToCheck.parentElement;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
/**
|
|
203
216
|
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
204
217
|
* @param {!HTMLElement} container
|
|
@@ -260,6 +273,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
260
273
|
cueRegistry = this.currentCuesMap_.get(cue);
|
|
261
274
|
wrapper = cueRegistry.wrapper;
|
|
262
275
|
updateDOM = true;
|
|
276
|
+
} else if (!this.isElementUnderTextContainer_(wrapper)) {
|
|
277
|
+
// We found that the wrapper needs to be in the DOM
|
|
278
|
+
updateDOM = true;
|
|
263
279
|
}
|
|
264
280
|
}
|
|
265
281
|
|
|
@@ -276,6 +292,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
276
292
|
const topCue = parents.pop();
|
|
277
293
|
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
278
294
|
}
|
|
295
|
+
|
|
279
296
|
if (updateDOM) {
|
|
280
297
|
for (const element of toUproot) {
|
|
281
298
|
// NOTE: Because we uproot shared region elements, too, we might hit an
|
|
@@ -578,7 +595,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
578
595
|
// (for vertical growing left) is aligned at the line.
|
|
579
596
|
// TODO: Implement line alignment with line number.
|
|
580
597
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
581
|
-
if (cue.line) {
|
|
598
|
+
if (cue.line != null) {
|
|
582
599
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
583
600
|
// When setting absolute positioning, you need to set x/y/width/height
|
|
584
601
|
// so the element is positioned correctly. Set these as default and
|
|
@@ -591,21 +608,21 @@ shaka.text.UITextDisplayer = class {
|
|
|
591
608
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
592
609
|
style.top = cue.line + '%';
|
|
593
610
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
594
|
-
style.bottom = cue.line + '%';
|
|
611
|
+
style.bottom = (100 - cue.line) + '%';
|
|
595
612
|
}
|
|
596
613
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
597
614
|
style.height = '100%';
|
|
598
615
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
599
616
|
style.left = cue.line + '%';
|
|
600
617
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
601
|
-
style.right = cue.line + '%';
|
|
618
|
+
style.right = (100 - cue.line) + '%';
|
|
602
619
|
}
|
|
603
620
|
} else {
|
|
604
621
|
style.height = '100%';
|
|
605
622
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
606
623
|
style.right = cue.line + '%';
|
|
607
624
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
608
|
-
style.left = cue.line + '%';
|
|
625
|
+
style.left = (100 - cue.line) + '%';
|
|
609
626
|
}
|
|
610
627
|
}
|
|
611
628
|
}
|
|
@@ -615,7 +632,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
615
632
|
|
|
616
633
|
// The position defines the indent of the text container in the
|
|
617
634
|
// direction defined by the writing direction.
|
|
618
|
-
if (cue.position) {
|
|
635
|
+
if (cue.position != null) {
|
|
619
636
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
620
637
|
style.paddingLeft = cue.position;
|
|
621
638
|
} else {
|
package/lib/util/platform.js
CHANGED
|
@@ -174,7 +174,7 @@ shaka.util.Platform = class {
|
|
|
174
174
|
* @return {?number} A major version number or null if not Chromium-based.
|
|
175
175
|
*/
|
|
176
176
|
static chromeVersion() {
|
|
177
|
-
if (!shaka.util.Platform.
|
|
177
|
+
if (!shaka.util.Platform.isChrome()) {
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -213,6 +213,7 @@ shaka.util.Platform = class {
|
|
|
213
213
|
return !!navigator.vendor && navigator.vendor.includes('Apple') &&
|
|
214
214
|
!shaka.util.Platform.isTizen() &&
|
|
215
215
|
!shaka.util.Platform.isEOS() &&
|
|
216
|
+
!shaka.util.Platform.isVirginMedia() &&
|
|
216
217
|
!shaka.util.Platform.isPS4() &&
|
|
217
218
|
!shaka.util.Platform.isAmazonFireTV();
|
|
218
219
|
}
|
|
@@ -237,6 +238,13 @@ shaka.util.Platform = class {
|
|
|
237
238
|
return shaka.util.Platform.userAgentContains_('PlayStation 4');
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Check if the current platform is Virgin Media device.
|
|
243
|
+
*/
|
|
244
|
+
static isVirginMedia() {
|
|
245
|
+
return shaka.util.Platform.userAgentContains_('VirginMedia');
|
|
246
|
+
}
|
|
247
|
+
|
|
240
248
|
/**
|
|
241
249
|
* Check if the current platform is Amazon Fire TV.
|
|
242
250
|
* https://developer.amazon.com/docs/fire-tv/identify-amazon-fire-tv-devices.html
|
|
@@ -11,6 +11,7 @@ goog.require('shaka.abr.SimpleAbrManager');
|
|
|
11
11
|
goog.require('shaka.log');
|
|
12
12
|
goog.require('shaka.net.NetworkingEngine');
|
|
13
13
|
goog.require('shaka.util.ConfigUtils');
|
|
14
|
+
goog.require('shaka.util.FairPlayUtils');
|
|
14
15
|
goog.require('shaka.util.LanguageUtils');
|
|
15
16
|
goog.require('shaka.util.ManifestParserUtils');
|
|
16
17
|
goog.require('shaka.util.Platform');
|
|
@@ -67,9 +68,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
67
68
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
68
69
|
delayLicenseRequestUntilPlayed: false,
|
|
69
70
|
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const keySystem = drmInfo.keySystem;
|
|
72
|
+
if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
|
|
73
|
+
const cert = drmInfo.serverCertificate;
|
|
74
|
+
const contentId =
|
|
75
|
+
shaka.util.FairPlayUtils.defaultGetContentId(initData);
|
|
76
|
+
initData = shaka.util.FairPlayUtils.initDataTransform(
|
|
77
|
+
initData, contentId, cert);
|
|
78
|
+
}
|
|
79
|
+
return initData;
|
|
73
80
|
},
|
|
74
81
|
logLicenseExchange: false,
|
|
75
82
|
updateExpirationTime: 1,
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -438,15 +438,20 @@ shaka.util.StreamUtils = class {
|
|
|
438
438
|
const video = variant.video;
|
|
439
439
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
440
440
|
if (video) {
|
|
441
|
-
let videoCodecs =
|
|
441
|
+
let videoCodecs =
|
|
442
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
442
443
|
// For multiplexed streams. Here we must check the audio of the
|
|
443
444
|
// stream to see if it is compatible.
|
|
444
445
|
if (video.codecs.includes(',')) {
|
|
445
446
|
const allCodecs = video.codecs.split(',');
|
|
446
447
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
447
448
|
ContentType.VIDEO, allCodecs);
|
|
448
|
-
|
|
449
|
+
videoCodecs =
|
|
450
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
451
|
+
let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
449
452
|
ContentType.AUDIO, allCodecs);
|
|
453
|
+
audioCodecs =
|
|
454
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
450
455
|
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
451
456
|
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
452
457
|
if (!MediaSource.isTypeSupported(audioFullType)) {
|
|
@@ -566,7 +571,8 @@ shaka.util.StreamUtils = class {
|
|
|
566
571
|
const allCodecs = video.codecs.split(',');
|
|
567
572
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
568
573
|
ContentType.VIDEO, allCodecs);
|
|
569
|
-
videoCodecs =
|
|
574
|
+
videoCodecs =
|
|
575
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
570
576
|
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
571
577
|
ContentType.AUDIO, allCodecs);
|
|
572
578
|
|
|
@@ -580,7 +586,7 @@ shaka.util.StreamUtils = class {
|
|
|
580
586
|
spatialRendering: false,
|
|
581
587
|
};
|
|
582
588
|
}
|
|
583
|
-
videoCodecs = shaka.util.StreamUtils.
|
|
589
|
+
videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
584
590
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
585
591
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
586
592
|
// VideoConfiguration
|
|
@@ -749,15 +755,29 @@ shaka.util.StreamUtils = class {
|
|
|
749
755
|
|
|
750
756
|
|
|
751
757
|
/**
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
* mediaCapabilities enabled.
|
|
755
|
-
*
|
|
758
|
+
* Generates the correct video codec for MediaDecodingConfiguration and
|
|
759
|
+
* for MediaSource.isTypeSupported.
|
|
756
760
|
* @param {string} codec
|
|
757
761
|
* @return {string}
|
|
762
|
+
* @private
|
|
758
763
|
*/
|
|
759
|
-
static
|
|
760
|
-
if (codec
|
|
764
|
+
static getCorrectVideoCodecs_(codec) {
|
|
765
|
+
if (codec.includes('avc1')) {
|
|
766
|
+
// Convert avc1 codec string from RFC-4281 to RFC-6381 for
|
|
767
|
+
// MediaSource.isTypeSupported
|
|
768
|
+
// Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
|
|
769
|
+
const avcdata = codec.split('.');
|
|
770
|
+
if (avcdata.length == 3) {
|
|
771
|
+
let result = avcdata.shift() + '.';
|
|
772
|
+
result += parseInt(avcdata.shift(), 10).toString(16);
|
|
773
|
+
result +=
|
|
774
|
+
('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
|
|
775
|
+
return result;
|
|
776
|
+
}
|
|
777
|
+
} else if (codec == 'vp9') {
|
|
778
|
+
// MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
|
|
779
|
+
// vp9 codec strings into 'vp09...', to allow such content to play with
|
|
780
|
+
// mediaCapabilities enabled.
|
|
761
781
|
// This means profile 0, level 4.1, 8-bit color. This supports 1080p @
|
|
762
782
|
// 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
|
|
763
783
|
//
|
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@ goog.require('shaka.offline.OfflineManifestParser');
|
|
|
32
32
|
goog.require('shaka.offline.OfflineScheme');
|
|
33
33
|
goog.require('shaka.offline.Storage');
|
|
34
34
|
goog.require('shaka.offline.indexeddb.StorageMechanism');
|
|
35
|
+
goog.require('shaka.polyfill.AbortController');
|
|
35
36
|
goog.require('shaka.polyfill.Aria');
|
|
36
37
|
goog.require('shaka.polyfill.EncryptionScheme');
|
|
37
38
|
goog.require('shaka.polyfill.Fullscreen');
|
|
@@ -2106,6 +2106,7 @@ describe('DashParser Manifest', () => {
|
|
|
2106
2106
|
/** @type {shaka.extern.Manifest} */
|
|
2107
2107
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2108
2108
|
expect(manifest.imageStreams.length).toBe(1);
|
|
2109
|
+
expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
|
|
2109
2110
|
const imageStream = manifest.imageStreams[0];
|
|
2110
2111
|
expect(imageStream.width).toBe(1024);
|
|
2111
2112
|
expect(imageStream.height).toBe(1152);
|
|
@@ -120,11 +120,17 @@ describe('DrmEngine', () => {
|
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
drmEngine = new shaka.media.DrmEngine(playerInterface);
|
|
123
|
+
|
|
123
124
|
config = shaka.util.PlayerConfiguration.createDefault().drm;
|
|
124
125
|
config.servers = {
|
|
125
126
|
'drm.abc': 'http://abc.drm/license',
|
|
126
127
|
'drm.def': 'http://def.drm/license',
|
|
127
128
|
};
|
|
129
|
+
// Some platforms, such as Xbox, default to parseInbandPssh: true, which
|
|
130
|
+
// ignores encrypted events. So set it explicitly to false, and let
|
|
131
|
+
// individual tests set it to true where relevant.
|
|
132
|
+
config.parseInbandPsshEnabled = false;
|
|
133
|
+
|
|
128
134
|
drmEngine.configure(config);
|
|
129
135
|
});
|
|
130
136
|
|
|
@@ -2548,7 +2554,11 @@ describe('DrmEngine', () => {
|
|
|
2548
2554
|
*/
|
|
2549
2555
|
async function sendEncryptedEvent(
|
|
2550
2556
|
initDataType = 'cenc', initData = new Uint8Array(1), keyId = null) {
|
|
2551
|
-
|
|
2557
|
+
// For some platforms, such as Xbox, where parseInbandPssh defaults to
|
|
2558
|
+
// true, this listener may never be set.
|
|
2559
|
+
if (mockVideo.on['encrypted']) {
|
|
2560
|
+
mockVideo.on['encrypted']({initDataType, initData, keyId});
|
|
2561
|
+
}
|
|
2552
2562
|
|
|
2553
2563
|
await Util.shortDelay();
|
|
2554
2564
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
describe('MediaSourceEngine', () => {
|
|
8
8
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
9
|
+
const Cue = shaka.text.Cue;
|
|
10
|
+
const Util = shaka.test.Util;
|
|
9
11
|
const presentationDuration = 840;
|
|
10
12
|
|
|
11
13
|
/** @type {!HTMLVideoElement} */
|
|
@@ -18,6 +20,64 @@ describe('MediaSourceEngine', () => {
|
|
|
18
20
|
let metadata;
|
|
19
21
|
// TODO: add text streams to MSE integration tests
|
|
20
22
|
|
|
23
|
+
const mp4CeaCue0 = jasmine.objectContaining({
|
|
24
|
+
startTime: Util.closeTo(0.067, 0.001),
|
|
25
|
+
endTime: Util.closeTo(1, 0.001),
|
|
26
|
+
textAlign: Cue.textAlign.CENTER,
|
|
27
|
+
nestedCues: [
|
|
28
|
+
jasmine.objectContaining({
|
|
29
|
+
startTime: Util.closeTo(0.067, 0.001),
|
|
30
|
+
endTime: Util.closeTo(1, 0.001),
|
|
31
|
+
payload: 'eng:⠀00:00:00:00',
|
|
32
|
+
textAlign: Cue.textAlign.CENTER,
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const tsCeaCue0 = jasmine.objectContaining({
|
|
38
|
+
startTime: Util.closeTo(0.767, 0.001),
|
|
39
|
+
endTime: Util.closeTo(4.972, 0.001),
|
|
40
|
+
textAlign: Cue.textAlign.CENTER,
|
|
41
|
+
payload: 'These are 608 captions\n(top left)',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const tsCeaCue1 = jasmine.objectContaining({
|
|
45
|
+
startTime: Util.closeTo(5.305, 0.001),
|
|
46
|
+
endTime: Util.closeTo(11.979, 0.001),
|
|
47
|
+
textAlign: Cue.textAlign.CENTER,
|
|
48
|
+
payload: 'These are 608 captions\n(middle)',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const tsCeaCue2 = jasmine.objectContaining({
|
|
52
|
+
startTime: Util.closeTo(12.312, 0.001),
|
|
53
|
+
endTime: Util.closeTo(19.319, 0.001),
|
|
54
|
+
textAlign: Cue.textAlign.CENTER,
|
|
55
|
+
payload: 'These are 608 captions\n(bottom left)',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// The same segments as above, but offset by 40 seconds (yes, 40), which is
|
|
59
|
+
// also 2 segments.
|
|
60
|
+
const tsCeaCue3 = jasmine.objectContaining({
|
|
61
|
+
startTime: Util.closeTo(40.767, 0.001),
|
|
62
|
+
endTime: Util.closeTo(44.972, 0.001),
|
|
63
|
+
textAlign: Cue.textAlign.CENTER,
|
|
64
|
+
payload: 'These are 608 captions\n(top left)',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const tsCeaCue4 = jasmine.objectContaining({
|
|
68
|
+
startTime: Util.closeTo(45.305, 0.001),
|
|
69
|
+
endTime: Util.closeTo(51.979, 0.001),
|
|
70
|
+
textAlign: Cue.textAlign.CENTER,
|
|
71
|
+
payload: 'These are 608 captions\n(middle)',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const tsCeaCue5 = jasmine.objectContaining({
|
|
75
|
+
startTime: Util.closeTo(52.312, 0.001),
|
|
76
|
+
endTime: Util.closeTo(59.319, 0.001),
|
|
77
|
+
textAlign: Cue.textAlign.CENTER,
|
|
78
|
+
payload: 'These are 608 captions\n(bottom left)',
|
|
79
|
+
});
|
|
80
|
+
|
|
21
81
|
/**
|
|
22
82
|
* We use a fake text displayer so that we can check if CEA text is being
|
|
23
83
|
* passed through the system correctly.
|
|
@@ -387,9 +447,14 @@ describe('MediaSourceEngine', () => {
|
|
|
387
447
|
// platforms with native TS support.
|
|
388
448
|
await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
|
|
389
449
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
450
|
+
|
|
390
451
|
await append(ContentType.VIDEO, 0);
|
|
391
452
|
|
|
392
453
|
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
454
|
+
|
|
455
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
|
|
456
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
|
|
457
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
|
|
393
458
|
});
|
|
394
459
|
|
|
395
460
|
it('extracts CEA-708 captions from previous segment from hls', async () => {
|
|
@@ -406,9 +471,19 @@ describe('MediaSourceEngine', () => {
|
|
|
406
471
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
407
472
|
|
|
408
473
|
await append(ContentType.VIDEO, 2);
|
|
474
|
+
|
|
475
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
476
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue3]);
|
|
477
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue4]);
|
|
478
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue5]);
|
|
479
|
+
|
|
480
|
+
textDisplayer.appendSpy.calls.reset();
|
|
409
481
|
await appendWithSeek(ContentType.VIDEO, 0);
|
|
410
482
|
|
|
411
|
-
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(
|
|
483
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
484
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
|
|
485
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
|
|
486
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
|
|
412
487
|
});
|
|
413
488
|
|
|
414
489
|
it('buffers partial TS video segments in sequence mode', async () => {
|
|
@@ -461,6 +536,7 @@ describe('MediaSourceEngine', () => {
|
|
|
461
536
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
462
537
|
await appendWithClosedCaptions(ContentType.VIDEO, 0);
|
|
463
538
|
|
|
464
|
-
expect(textDisplayer.appendSpy).
|
|
539
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(1);
|
|
540
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([mp4CeaCue0]);
|
|
465
541
|
});
|
|
466
542
|
});
|
|
@@ -898,7 +898,7 @@ describe('Player', () => {
|
|
|
898
898
|
expect(getBufferedBehind()).toBe(20); // Buffered to start still.
|
|
899
899
|
video.currentTime = 50;
|
|
900
900
|
await waitUntilBuffered(30);
|
|
901
|
-
expect(getBufferedBehind()).toBeLessThan(
|
|
901
|
+
expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
|
|
902
902
|
|
|
903
903
|
player.configure('streaming.bufferBehind', 10);
|
|
904
904
|
// We only evict content when we append a segment, so increase the
|
|
@@ -925,7 +925,7 @@ describe('Player', () => {
|
|
|
925
925
|
}
|
|
926
926
|
|
|
927
927
|
async function waitUntilBuffered(amount) {
|
|
928
|
-
for (let i = 0; i <
|
|
928
|
+
for (let i = 0; i < 50; i++) {
|
|
929
929
|
// We buffer from an internal segment, so this shouldn't take long to
|
|
930
930
|
// buffer.
|
|
931
931
|
await Util.delay(0.1); // eslint-disable-line no-await-in-loop
|
|
@@ -933,7 +933,14 @@ describe('Player', () => {
|
|
|
933
933
|
return;
|
|
934
934
|
}
|
|
935
935
|
}
|
|
936
|
-
|
|
936
|
+
|
|
937
|
+
const ranges =
|
|
938
|
+
shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
|
|
939
|
+
const currentTime = video.currentTime;
|
|
940
|
+
const target = currentTime + amount;
|
|
941
|
+
|
|
942
|
+
throw new Error('Timeout waiting to buffer! ' +
|
|
943
|
+
JSON.stringify({ranges, currentTime, target}));
|
|
937
944
|
}
|
|
938
945
|
}); // describe('buffering')
|
|
939
946
|
|