shaka-player 4.6.7 → 4.6.9

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +3 -1
  3. package/dist/locales.js +31 -31
  4. package/dist/shaka-player.compiled.d.ts +4 -0
  5. package/dist/shaka-player.compiled.debug.d.ts +4 -0
  6. package/dist/shaka-player.compiled.debug.externs.js +5 -0
  7. package/dist/shaka-player.compiled.debug.js +117 -114
  8. package/dist/shaka-player.compiled.debug.map +3 -3
  9. package/dist/shaka-player.compiled.externs.js +5 -0
  10. package/dist/shaka-player.compiled.js +677 -675
  11. package/dist/shaka-player.compiled.map +3 -3
  12. package/dist/shaka-player.ui.d.ts +4 -0
  13. package/dist/shaka-player.ui.debug.d.ts +4 -0
  14. package/dist/shaka-player.ui.debug.externs.js +5 -0
  15. package/dist/shaka-player.ui.debug.js +141 -136
  16. package/dist/shaka-player.ui.debug.map +3 -3
  17. package/dist/shaka-player.ui.externs.js +5 -0
  18. package/dist/shaka-player.ui.js +765 -763
  19. package/dist/shaka-player.ui.map +3 -3
  20. package/docs/tutorials/index.json +1 -0
  21. package/docs/tutorials/text-displayer.md +15 -0
  22. package/lib/ads/client_side_ad.js +4 -2
  23. package/lib/ads/client_side_ad_manager.js +5 -3
  24. package/lib/cast/cast_proxy.js +18 -0
  25. package/lib/cea/cea608_memory.js +1 -1
  26. package/lib/cea/cea_decoder.js +1 -1
  27. package/lib/hls/hls_parser.js +16 -0
  28. package/lib/hls/hls_utils.js +5 -5
  29. package/lib/media/media_source_engine.js +4 -4
  30. package/lib/media/play_rate_controller.js +1 -0
  31. package/lib/media/presentation_timeline.js +11 -0
  32. package/lib/offline/session_deleter.js +0 -8
  33. package/lib/player.js +41 -6
  34. package/lib/polyfill/patchedmediakeys_apple.js +0 -6
  35. package/lib/text/ttml_text_parser.js +5 -0
  36. package/lib/text/vtt_text_parser.js +5 -0
  37. package/lib/util/language_utils.js +7 -8
  38. package/package.json +1 -1
  39. package/test/cea/cea_decoder_unit.js +36 -0
  40. package/test/media/media_source_engine_unit.js +26 -3
  41. package/test/player_load_graph_integration.js +5 -0
  42. package/test/player_unit.js +17 -0
  43. package/test/text/ttml_text_parser_unit.js +16 -16
  44. package/ui/play_button.js +17 -0
  45. package/ui/playback_rate_selection.js +4 -0
  46. package/ui/small_play_button.js +2 -2
@@ -9,6 +9,7 @@
9
9
  { "license-wrapping": { "title": "License Wrapping" } },
10
10
  { "ui": { "title": "UI Library" } },
11
11
  { "ui-customization": { "title": "Configuring the UI" } },
12
+ { "text-displayer": { "title": "Configuring text displayer" } },
12
13
  { "errors": { "title": "Error Handling" } },
13
14
  { "a11y": { "title": "Creating accessible buttons" } },
14
15
  { "ad_monetization": { "title": "Serving ads with our IMA SDK integration" } },
@@ -0,0 +1,15 @@
1
+ # Configuring text displayer`
2
+
3
+ Shaka Player supports two types of Text Displayer.
4
+ 1) Using the browser's native API, Shaka Player creates a textTrack along
5
+ with the AUDIO or VIDEO Element.
6
+ 2) Rendering the subtitles in a container in the DOM.
7
+ 3) Providing a custom text displayer factory would allow a developer to make
8
+ their own custom text displayer that fits neither category.
9
+
10
+ By default, if not configured otherwise, the player will use type 1.
11
+ To configure type 2, you have to call `setVideoContainer` function before
12
+ making `load` call in the player.
13
+
14
+ Note: The UI calls setVideoContainer for you if the video's controls are
15
+ disabled, so this isn't always necessary when using the UI.
@@ -155,6 +155,7 @@ shaka.ads.ClientSideAd = class {
155
155
  * @export
156
156
  */
157
157
  setVolume(volume) {
158
+ this.video_.volume = volume;
158
159
  return this.manager_.setVolume(volume);
159
160
  }
160
161
 
@@ -197,13 +198,14 @@ shaka.ads.ClientSideAd = class {
197
198
  * @export
198
199
  */
199
200
  setMuted(muted) {
201
+ this.video_.muted = muted;
200
202
  // Emulate the "mute" functionality, where current, pre-mute
201
203
  // volume is saved and can be restored on unmute.
202
204
  if (muted) {
203
205
  this.volume_ = this.getVolume();
204
- this.setVolume(0);
206
+ this.manager_.setVolume(0);
205
207
  } else {
206
- this.setVolume(this.volume_);
208
+ this.manager_.setVolume(this.volume_);
207
209
  }
208
210
  }
209
211
 
@@ -234,10 +234,12 @@ shaka.ads.ClientSideAdManager = class {
234
234
  videoPlayHead.currentTime = this.video_.currentTime;
235
235
  });
236
236
  this.eventManager_.listen(this.video_, 'volumechange', () => {
237
+ if (!this.ad_) {
238
+ return;
239
+ }
240
+ this.ad_.setVolume(this.video_.volume);
237
241
  if (this.video_.muted) {
238
- this.imaAdsManager_.setVolume(0);
239
- } else {
240
- this.imaAdsManager_.setVolume(this.video_.volume);
242
+ this.ad_.setMuted(true);
241
243
  }
242
244
  });
243
245
  }
@@ -496,6 +496,16 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
496
496
  startTime = this.sender_.get('video', 'currentTime');
497
497
  }
498
498
 
499
+ let activeTextTrack;
500
+ const textTracks = this.sender_.get('player', 'getTextTracks')();
501
+
502
+ if (textTracks && textTracks.length) {
503
+ activeTextTrack = textTracks.find((t) => t.active);
504
+ }
505
+
506
+ const isTextTrackVisible =
507
+ this.sender_.get('player', 'isTextTrackVisible')();
508
+
499
509
  // Now load the manifest, if present.
500
510
  if (assetUri) {
501
511
  // Don't autoplay the content until we finish setting up initial state.
@@ -527,6 +537,14 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
527
537
  /** @type {Object} */(this.localPlayer_)[setter](value);
528
538
  }
529
539
 
540
+ this.localPlayer_.setTextTrackVisibility(isTextTrackVisible);
541
+ if (activeTextTrack) {
542
+ this.localPlayer_.selectTextLanguage(
543
+ activeTextTrack.language,
544
+ activeTextTrack.roles,
545
+ activeTextTrack.forced);
546
+ }
547
+
530
548
  // Restore the original autoplay setting.
531
549
  this.localVideo_.autoplay = autoplay;
532
550
  if (assetUri) {
@@ -79,7 +79,7 @@ shaka.cea.Cea608Memory = class {
79
79
  * @return {?shaka.extern.ICaptionDecoder.ClosedCaption}
80
80
  */
81
81
  forceEmit(startTime, endTime) {
82
- const stream = `CC${(this.fieldNum_<< 1) | this.channelNum_ +1}`;
82
+ const stream = `CC${((this.fieldNum_<< 1) | this.channelNum_) + 1}`;
83
83
  const topLevelCue = new shaka.text.Cue(
84
84
  startTime, endTime, /* payload= */ '');
85
85
  topLevelCue.lineInterpretation =
@@ -283,7 +283,7 @@ shaka.cea.CeaDecoder = class {
283
283
  // Get the correct stream for this caption packet (CC1, ..., CC4)
284
284
  const selectedChannel = fieldNum ?
285
285
  this.currentField2Channel_ : this.currentField1Channel_;
286
- const selectedMode = `CC${(fieldNum << 1) | selectedChannel + 1}`;
286
+ const selectedMode = `CC${((fieldNum << 1) | selectedChannel) + 1}`;
287
287
  const selectedStream = this.cea608ModeToStream_.get(selectedMode);
288
288
 
289
289
  // Check for bad frames (bad pairs). This can be two 0xff, two 0x00, or any
@@ -979,6 +979,22 @@ shaka.hls.HlsParser = class {
979
979
  this.presentationTimeline_.setSegmentAvailabilityDuration(
980
980
  segmentAvailabilityDuration);
981
981
  }
982
+
983
+ if (!this.presentationTimeline_.isStartTimeLocked()) {
984
+ const streamInfos = Array.from(this.uriToStreamInfosMap_.values());
985
+ const activeStreamInfos =
986
+ streamInfos.filter((s) => s.stream.segmentIndex);
987
+ for (const streamInfo of activeStreamInfos) {
988
+ if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
989
+ continue;
990
+ }
991
+ const firstReference = streamInfo.stream.segmentIndex.get(0);
992
+ if (firstReference && firstReference.syncTime) {
993
+ const syncTime = firstReference.syncTime;
994
+ this.presentationTimeline_.setPresentationStartTime(syncTime);
995
+ }
996
+ }
997
+ }
982
998
  } else {
983
999
  // Use the minimum duration as the presentation duration.
984
1000
  this.presentationTimeline_.setDuration(this.getMinDuration_());
@@ -47,12 +47,12 @@ shaka.hls.Utils = class {
47
47
  * @return {?shaka.hls.Tag}
48
48
  */
49
49
  static getFirstTagWithName(tags, name) {
50
- const tagsWithName = shaka.hls.Utils.filterTagsByName(tags, name);
51
- if (!tagsWithName.length) {
52
- return null;
50
+ for (const tag of tags) {
51
+ if (tag.name === name) {
52
+ return tag;
53
+ }
53
54
  }
54
-
55
- return tagsWithName[0];
55
+ return null;
56
56
  }
57
57
 
58
58
  /**
@@ -165,9 +165,6 @@ shaka.media.MediaSourceEngine = class {
165
165
 
166
166
  const mediaSource = new ManagedMediaSource();
167
167
 
168
- this.eventManager_.listenOnce(
169
- mediaSource, 'sourceopen', () => this.onSourceOpen_(p));
170
-
171
168
  this.eventManager_.listen(
172
169
  mediaSource, 'startstreaming', () => {
173
170
  this.streamingAllowed_ = true;
@@ -178,6 +175,9 @@ shaka.media.MediaSourceEngine = class {
178
175
  this.streamingAllowed_ = false;
179
176
  });
180
177
 
178
+ this.eventManager_.listenOnce(
179
+ mediaSource, 'sourceopen', () => this.onSourceOpen_(p));
180
+
181
181
  // Correctly set when playback has begun.
182
182
  this.eventManager_.listenOnce(this.video_, 'playing', () => {
183
183
  this.playbackHasBegun_ = true;
@@ -1793,7 +1793,7 @@ shaka.media.MediaSourceEngine = class {
1793
1793
 
1794
1794
  this.destroyer_.ensureNotDestroyed();
1795
1795
 
1796
- this.eventManager_.listenOnce(this.video_, 'canplay', () => {
1796
+ this.eventManager_.listenOnce(this.video_, 'canplaythrough', () => {
1797
1797
  this.destroyer_.ensureNotDestroyed();
1798
1798
 
1799
1799
  this.video_.autoplay = previousAutoPlayState;
@@ -41,6 +41,7 @@ shaka.media.PlayRateController = class {
41
41
 
42
42
  /** @override */
43
43
  release() {
44
+ this.set(this.getDefaultRate());
44
45
  if (this.timer_) {
45
46
  this.timer_.stop();
46
47
  this.timer_ = null;
@@ -328,6 +328,17 @@ shaka.media.PresentationTimeline = class {
328
328
  }
329
329
 
330
330
 
331
+ /**
332
+ * Returns if the presentation timeline's start time is locked.
333
+ *
334
+ * @return {boolean}
335
+ * @export
336
+ */
337
+ isStartTimeLocked() {
338
+ return this.startTimeLocked_;
339
+ }
340
+
341
+
331
342
  /**
332
343
  * Gives PresentationTimeline a Stream's minimum segment start time.
333
344
  *
@@ -71,14 +71,6 @@ shaka.offline.SessionDeleter = class {
71
71
  return [];
72
72
  }
73
73
 
74
- try {
75
- await drmEngine.setServerCertificate();
76
- } catch (e) {
77
- shaka.log.warning('Error setting server certificate', e);
78
- await drmEngine.destroy();
79
- return [];
80
- }
81
-
82
74
  /** @type {!Array.<string>} */
83
75
  const sessionIds = [];
84
76
  await Promise.all(bucket.sessionIds.map(async (sessionId) => {
package/lib/player.js CHANGED
@@ -549,6 +549,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
549
549
  */
550
550
  this.loadEventManager_ = new shaka.util.EventManager();
551
551
 
552
+ /**
553
+ * For listeners scoped to the lifetime of the loaded content.
554
+ * @private {shaka.util.EventManager}
555
+ */
556
+ this.trickPlayEventManager_ = new shaka.util.EventManager();
557
+
552
558
  /** @private {shaka.net.NetworkingEngine} */
553
559
  this.networkingEngine_ = null;
554
560
 
@@ -840,6 +846,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
840
846
  this.loadEventManager_.release();
841
847
  this.loadEventManager_ = null;
842
848
  }
849
+ if (this.trickPlayEventManager_) {
850
+ this.trickPlayEventManager_.release();
851
+ this.trickPlayEventManager_ = null;
852
+ }
843
853
 
844
854
  this.abrManagerFactory_ = null;
845
855
  this.config_ = null;
@@ -1157,6 +1167,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1157
1167
  // element.
1158
1168
  if (this.video_) {
1159
1169
  this.loadEventManager_.removeAll();
1170
+ this.trickPlayEventManager_.removeAll();
1160
1171
  }
1161
1172
 
1162
1173
  // Stop the variant checker timer
@@ -3504,6 +3515,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3504
3515
  shaka.log.alwaysWarn('A trick play rate of 0 is unsupported!');
3505
3516
  return;
3506
3517
  }
3518
+ this.trickPlayEventManager_.removeAll();
3507
3519
 
3508
3520
  if (this.video_.paused) {
3509
3521
  // Our fast forward is implemented with playbackRate and needs the video
@@ -3517,6 +3529,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3517
3529
  this.abrManager_.playbackRateChanged(rate);
3518
3530
  this.streamingEngine_.setTrickPlay(Math.abs(rate) > 1);
3519
3531
  }
3532
+ if (this.isLive()) {
3533
+ this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
3534
+ const currentTime = this.video_.currentTime;
3535
+ const seekRange = this.seekRange();
3536
+ const safeSeekOffset = this.config_.streaming.safeSeekOffset;
3537
+ if (rate > 0) {
3538
+ if (currentTime >= seekRange.end) {
3539
+ this.cancelTrickPlay();
3540
+ }
3541
+ } else {
3542
+ if (currentTime <= (seekRange.start + safeSeekOffset)) {
3543
+ this.cancelTrickPlay();
3544
+ }
3545
+ }
3546
+ });
3547
+ }
3520
3548
  }
3521
3549
 
3522
3550
  /**
@@ -3536,6 +3564,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3536
3564
  this.abrManager_.playbackRateChanged(defaultPlaybackRate);
3537
3565
  this.streamingEngine_.setTrickPlay(false);
3538
3566
  }
3567
+ this.trickPlayEventManager_.removeAll();
3539
3568
  }
3540
3569
 
3541
3570
  /**
@@ -4768,12 +4797,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4768
4797
 
4769
4798
  const references = [];
4770
4799
  for (const cue of cues) {
4771
- const imageUri = shaka.util.ManifestParserUtils.resolveUris(
4772
- [uri], [cue.payload])[0];
4800
+ let uris = null;
4801
+ const getUris = () => {
4802
+ if (uris == null) {
4803
+ uris = shaka.util.ManifestParserUtils.resolveUris(
4804
+ [uri], [cue.payload]);
4805
+ }
4806
+ return uris || [];
4807
+ };
4773
4808
  const reference = new shaka.media.SegmentReference(
4774
4809
  cue.startTime,
4775
4810
  cue.endTime,
4776
- () => [imageUri],
4811
+ getUris,
4777
4812
  /* startByte= */ 0,
4778
4813
  /* endByte= */ null,
4779
4814
  /* initSegmentReference= */ null,
@@ -4781,8 +4816,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4781
4816
  /* appendWindowStart= */ 0,
4782
4817
  /* appendWindowEnd= */ Infinity,
4783
4818
  );
4784
- if (imageUri.includes('#xywh')) {
4785
- const spriteInfo = imageUri.split('#xywh=')[1].split(',');
4819
+ if (cue.payload.includes('#xywh')) {
4820
+ const spriteInfo = cue.payload.split('#xywh=')[1].split(',');
4786
4821
  if (spriteInfo.length === 4) {
4787
4822
  reference.setThumbnailSprite({
4788
4823
  height: parseInt(spriteInfo[3], 10),
@@ -6779,7 +6814,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6779
6814
  * @export
6780
6815
  */
6781
6816
  // eslint-disable-next-line no-useless-concat
6782
- shaka.Player.version = 'v4.6.7' + '-uncompiled'; // x-release-please-version
6817
+ shaka.Player.version = 'v4.6.9' + '-uncompiled'; // x-release-please-version
6783
6818
 
6784
6819
  // Initialize the deprecation system using the version string we just set
6785
6820
  // on the player.
@@ -791,9 +791,3 @@ shaka.polyfill.PatchedMediaKeysApple.MediaKeyStatusMap = class {
791
791
  goog.asserts.assert(false, 'Not used! Provided only for the compiler.');
792
792
  }
793
793
  };
794
-
795
- if (!navigator.requestMediaKeySystemAccess ||
796
- // eslint-disable-next-line no-restricted-syntax
797
- !MediaKeySystemAccess.prototype.getConfiguration) {
798
- shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysApple.install);
799
- }
@@ -491,6 +491,11 @@ shaka.text.TtmlTextParser = class {
491
491
  region.viewportAnchorX = region.viewportAnchorX * 100 /
492
492
  globalWidth;
493
493
  }
494
+ } else if (!extent) {
495
+ region.width = 100 - region.viewportAnchorX;
496
+ region.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
497
+ region.height = 100 - region.viewportAnchorY;
498
+ region.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
494
499
  }
495
500
 
496
501
  region.viewportAnchorUnits = percentage || globalWidth != null ?
@@ -451,6 +451,11 @@ shaka.text.VttTextParser = class {
451
451
  */
452
452
  static parseCueStyles(payload, rootCue, styles) {
453
453
  const VttTextParser = shaka.text.VttTextParser;
454
+ // Optimization for unstyled payloads.
455
+ if (!payload.includes('<')) {
456
+ rootCue.payload = VttTextParser.htmlUnescape_(payload);
457
+ return;
458
+ }
454
459
  if (styles.size === 0) {
455
460
  VttTextParser.addDefaultTextColor_(styles);
456
461
  }
@@ -192,25 +192,24 @@ shaka.util.LanguageUtils = class {
192
192
  const LanguageUtils = shaka.util.LanguageUtils;
193
193
 
194
194
  const privateusePrefix = 'x-';
195
- const components = locale.split(`-${privateusePrefix}`);
196
- const languageAndRegion = components[0].split('-');
195
+ const [languageRegion = '', privateuseSuffix = ''] =
196
+ locale.split(`-${privateusePrefix}`);
197
+ const [languageCode = '', regionCode = ''] = languageRegion.split('-');
197
198
 
198
199
  // We are only going to use the language, the region and the "privateuse" part (as per https://datatracker.ietf.org/doc/html/rfc5646).
199
200
  // Anything else is thrown away.
200
- let language = languageAndRegion[0] || '';
201
- let region = languageAndRegion[1] || '';
202
- const privateuse = components[1] ?
203
- `${privateusePrefix}${components[1]}` : '';
201
+ const privateuse = privateuseSuffix ?
202
+ `${privateusePrefix}${privateuseSuffix}` : '';
204
203
 
205
204
  // Convert the language to lower case. It is standard for the language code
206
205
  // to be in lower case, but it will also make the map look-up easier.
207
- language = language.toLowerCase();
206
+ let language = languageCode.toLowerCase();
208
207
  language = LanguageUtils.isoMap_.get(language) || language;
209
208
 
210
209
  // Convert the region to upper case. It is standard for the region to be in
211
210
  // upper case. If there is no upper code, then it will be an empty string
212
211
  // and this will be a no-op.
213
- region = region.toUpperCase();
212
+ const region = regionCode.toUpperCase();
214
213
 
215
214
  return `${region ? `${language}-${region}` : language}${
216
215
  privateuse ? `-${privateuse}` : ''}`;
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.6.7",
4
+ "version": "4.6.9",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -44,6 +44,42 @@ 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.line = 10;
64
+ topLevelCue.lineInterpretation =
65
+ shaka.text.Cue.lineInterpretation.PERCENTAGE;
66
+ topLevelCue.nestedCues = [
67
+ CeaUtils.createDefaultCue(
68
+ startTimeCaption1, startTimeCaption2, expectedText),
69
+ ];
70
+
71
+ const expectedCaptions = [{
72
+ stream: 'CC4',
73
+ cue: topLevelCue,
74
+ }];
75
+
76
+ decoder.extract(paintonCaptionCC4Packet, startTimeCaption1);
77
+ decoder.extract(eraseDisplayedMemory, startTimeCaption2);
78
+ const captions = decoder.decode();
79
+
80
+ expect(captions).toEqual(expectedCaptions);
81
+ });
82
+
47
83
  it('green and underlined popon caption data on CC3', () => {
48
84
  const controlCount = 0x08;
49
85
  const captionData = 0xc0 | controlCount;
@@ -38,6 +38,10 @@ describe('MediaSourceEngine', () => {
38
38
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
39
39
 
40
40
  const originalIsTypeSupported = window.MediaSource.isTypeSupported;
41
+ let originalIsTypeSupportedManaged;
42
+ if (window.ManagedMediaSource) {
43
+ originalIsTypeSupportedManaged = window.ManagedMediaSource.isTypeSupported;
44
+ }
41
45
  const originalTextEngine = shaka.text.TextEngine;
42
46
  const originalCreateMediaSource =
43
47
  // eslint-disable-next-line no-restricted-syntax
@@ -98,10 +102,20 @@ describe('MediaSourceEngine', () => {
98
102
  const type = mimeType.split('/')[0];
99
103
  return type == 'video' || type == 'audio';
100
104
  };
105
+ if (window.ManagedMediaSource) {
106
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => {
107
+ const type = mimeType.split('/')[0];
108
+ return type == 'video' || type == 'audio';
109
+ };
110
+ }
101
111
  });
102
112
 
103
113
  afterAll(() => {
104
114
  window.MediaSource.isTypeSupported = originalIsTypeSupported;
115
+ if (window.ManagedMediaSource) {
116
+ window.ManagedMediaSource.isTypeSupported =
117
+ originalIsTypeSupportedManaged;
118
+ }
105
119
  shaka.transmuxer.TransmuxerEngine.findTransmuxer =
106
120
  originalFindTransmuxer;
107
121
  shaka.transmuxer.TransmuxerEngine.convertCodecs =
@@ -206,6 +220,7 @@ describe('MediaSourceEngine', () => {
206
220
  shaka.media.MediaSourceEngine.createObjectURL;
207
221
  const originalRevokeObjectURL = window.URL.revokeObjectURL;
208
222
  const originalMediaSource = window.MediaSource;
223
+ const originalManagedMediaSource = window.ManagedMediaSource;
209
224
  /** @type {jasmine.Spy} */
210
225
  let createObjectURLSpy;
211
226
  /** @type {jasmine.Spy} */
@@ -237,6 +252,9 @@ describe('MediaSourceEngine', () => {
237
252
  return mockMediaSource;
238
253
  });
239
254
  window.MediaSource = Util.spyFunc(mediaSourceSpy);
255
+ if (window.ManagedMediaSource) {
256
+ window.ManagedMediaSource = Util.spyFunc(mediaSourceSpy);
257
+ }
240
258
 
241
259
  await mediaSourceEngine.destroy();
242
260
  });
@@ -244,6 +262,7 @@ describe('MediaSourceEngine', () => {
244
262
  afterAll(() => {
245
263
  shaka.media.MediaSourceEngine.createObjectURL = originalCreateObjectURL;
246
264
  window.MediaSource = originalMediaSource;
265
+ window.ManagedMediaSource = originalManagedMediaSource;
247
266
  window.URL.revokeObjectURL = originalRevokeObjectURL;
248
267
  });
249
268
 
@@ -270,7 +289,11 @@ describe('MediaSourceEngine', () => {
270
289
  video,
271
290
  new shaka.test.FakeTextDisplayer());
272
291
 
273
- expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(1);
292
+ if (window.ManagedMediaSource) {
293
+ expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(3);
294
+ } else {
295
+ expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(1);
296
+ }
274
297
  expect(mockMediaSource.addEventListener.calls.mostRecent().args[0])
275
298
  .toBe('sourceopen');
276
299
  expect(typeof onSourceOpenListener).toBe(typeof Function);
@@ -1223,7 +1246,7 @@ describe('MediaSourceEngine', () => {
1223
1246
  const callback = addListenOnceSpy.calls.argsFor(0)[2];
1224
1247
  callback();
1225
1248
  expect(initSourceBufferSpy).toHaveBeenCalled();
1226
- expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
1249
+ expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplaythrough');
1227
1250
  expect(video.autoplay).toBe(true);
1228
1251
  expect(playSpy).not.toHaveBeenCalled();
1229
1252
  } finally {
@@ -1290,7 +1313,7 @@ describe('MediaSourceEngine', () => {
1290
1313
  const callback = addListenOnceSpy.calls.argsFor(0)[2];
1291
1314
  callback();
1292
1315
  expect(initSourceBufferSpy).toHaveBeenCalled();
1293
- expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
1316
+ expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplaythrough');
1294
1317
  expect(video.autoplay).toBe(false);
1295
1318
  expect(playSpy).toHaveBeenCalled();
1296
1319
  } finally {
@@ -394,12 +394,16 @@ describe('Player Load Graph', () => {
394
394
  // that the player will behave as expected when media source is missing.
395
395
  describe('without media source', () => {
396
396
  let mediaSource;
397
+ let managedMediaSource;
397
398
 
398
399
  beforeEach(async () => {
399
400
  // Remove our media source support. In order to remove it, we need to set
400
401
  // it via [] notation or else closure will stop us.
401
402
  mediaSource = window.MediaSource;
402
403
  window['MediaSource'] = undefined;
404
+ managedMediaSource = window.ManagedMediaSource;
405
+ window['ManagedMediaSource'] = undefined;
406
+
403
407
 
404
408
  createPlayer();
405
409
  await shaka.test.Util.delay(/* seconds= */ 0.25);
@@ -409,6 +413,7 @@ describe('Player Load Graph', () => {
409
413
  // Restore our media source support to what it was before. If we did not
410
414
  // have support before, this will do nothing.
411
415
  window['MediaSource'] = mediaSource;
416
+ window['ManagedMediaSource'] = managedMediaSource;
412
417
  });
413
418
 
414
419
  it('attaching ignores init media source flag', async () => {
@@ -12,6 +12,10 @@ describe('Player', () => {
12
12
  const originalLogWarn = shaka.log.warning;
13
13
  const originalLogAlwaysWarn = shaka.log.alwaysWarn;
14
14
  const originalIsTypeSupported = window.MediaSource.isTypeSupported;
15
+ let originalIsTypeSupportedManaged;
16
+ if (window.ManagedMediaSource) {
17
+ originalIsTypeSupportedManaged = window.ManagedMediaSource.isTypeSupported;
18
+ }
15
19
  const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
16
20
 
17
21
  const fakeManifestUri = 'fake-manifest-uri';
@@ -67,6 +71,12 @@ describe('Player', () => {
67
71
  const type = mimeType.split('/')[0];
68
72
  return type == 'video' || type == 'audio';
69
73
  };
74
+ if (window.ManagedMediaSource) {
75
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => {
76
+ const type = mimeType.split('/')[0];
77
+ return type == 'video' || type == 'audio';
78
+ };
79
+ }
70
80
 
71
81
  // Since this is not an integration test, we don't want MediaCapabilities to
72
82
  // fail assertions based on browser support for types. Pretend that all
@@ -178,6 +188,10 @@ describe('Player', () => {
178
188
  shaka.log.warning = originalLogWarn;
179
189
  shaka.log.alwaysWarn = originalLogAlwaysWarn;
180
190
  window.MediaSource.isTypeSupported = originalIsTypeSupported;
191
+ if (window.ManagedMediaSource) {
192
+ window.ManagedMediaSource.isTypeSupported =
193
+ originalIsTypeSupportedManaged;
194
+ }
181
195
  shaka.media.ManifestParser.unregisterParserByMime(fakeMimeType);
182
196
  navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
183
197
  onError.calls.reset();
@@ -2900,6 +2914,9 @@ describe('Player', () => {
2900
2914
  describe('unplayable content', () => {
2901
2915
  it('throws CONTENT_UNSUPPORTED_BY_BROWSER', async () => {
2902
2916
  window.MediaSource.isTypeSupported = (mimeType) => false;
2917
+ if (window.ManagedMediaSource) {
2918
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => false;
2919
+ }
2903
2920
 
2904
2921
  navigator.mediaCapabilities.decodingInfo = async (config) => {
2905
2922
  await Promise.resolve();