shaka-player 4.11.11 → 4.11.13

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 (41) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/dist/deps.js +1 -1
  4. package/dist/locales.js +37 -37
  5. package/dist/shaka-player.compiled.d.ts +1 -1
  6. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  7. package/dist/shaka-player.compiled.debug.externs.js +1 -1
  8. package/dist/shaka-player.compiled.debug.js +243 -242
  9. package/dist/shaka-player.compiled.debug.map +3 -3
  10. package/dist/shaka-player.compiled.externs.js +1 -1
  11. package/dist/shaka-player.compiled.js +915 -913
  12. package/dist/shaka-player.compiled.map +3 -3
  13. package/dist/shaka-player.ui.d.ts +1 -1
  14. package/dist/shaka-player.ui.debug.d.ts +1 -1
  15. package/dist/shaka-player.ui.debug.externs.js +1 -1
  16. package/dist/shaka-player.ui.debug.js +353 -351
  17. package/dist/shaka-player.ui.debug.map +3 -3
  18. package/dist/shaka-player.ui.externs.js +1 -1
  19. package/dist/shaka-player.ui.js +1232 -1229
  20. package/dist/shaka-player.ui.map +3 -3
  21. package/lib/ads/interstitial_ad_manager.js +83 -29
  22. package/lib/dash/segment_template.js +6 -2
  23. package/lib/media/adaptation_set_criteria.js +22 -47
  24. package/lib/media/media_source_engine.js +9 -8
  25. package/lib/media/preload_manager.js +2 -1
  26. package/lib/media/streaming_engine.js +1 -2
  27. package/lib/net/networking_engine.js +3 -3
  28. package/lib/player.js +57 -20
  29. package/lib/polyfill/media_capabilities.js +2 -1
  30. package/lib/polyfill/mediasource.js +12 -0
  31. package/lib/text/cue.js +1 -0
  32. package/lib/text/ui_text_displayer.js +8 -0
  33. package/lib/util/error.js +1 -1
  34. package/lib/util/mp4_generator.js +1 -1
  35. package/lib/util/platform.js +3 -3
  36. package/package.json +1 -1
  37. package/ui/controls.js +17 -7
  38. package/ui/language_utils.js +3 -1
  39. package/ui/playback_rate_selection.js +6 -2
  40. package/ui/remote_button.js +42 -17
  41. package/ui/seek_bar.js +3 -0
@@ -39,6 +39,9 @@ shaka.ads.InterstitialAdManager = class {
39
39
  * @param {function(!shaka.util.FakeEvent)} onEvent
40
40
  */
41
41
  constructor(adContainer, basePlayer, baseVideo, onEvent) {
42
+ /** @private {?shaka.extern.AdsConfiguration} */
43
+ this.config_ = null;
44
+
42
45
  /** @private {HTMLElement} */
43
46
  this.adContainer_ = adContainer;
44
47
 
@@ -93,10 +96,24 @@ shaka.ads.InterstitialAdManager = class {
93
96
  this.lastPlayedAd_ = null;
94
97
 
95
98
  this.eventManager_.listen(this.baseVideo_, 'timeupdate', () => {
96
- if (this.playingAd_ || this.basePlayer_.isRemotePlayback()) {
99
+ if (this.playingAd_ || this.lastTime_ ||
100
+ this.basePlayer_.isRemotePlayback()) {
101
+ return;
102
+ }
103
+ this.lastTime_ = this.baseVideo_.currentTime;
104
+ const currentInterstitial = this.getCurrentInterstitial_(
105
+ /* needPreRoll= */ true, /* numberToSkip= */ 0, this.lastPlayedAd_);
106
+ if (currentInterstitial) {
107
+ this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
108
+ /* adPosition= */ 1, /* initialTime= */ Date.now());
109
+ }
110
+ });
111
+
112
+ const checkForInterstitials = () => {
113
+ if (this.playingAd_ || !this.lastTime_ ||
114
+ this.basePlayer_.isRemotePlayback()) {
97
115
  return;
98
116
  }
99
- const needPreRoll = this.lastTime_ == null || this.lastTime_ == 0;
100
117
  this.lastTime_ = this.baseVideo_.currentTime;
101
118
  // Remove last played add when the new time is before to the ad time.
102
119
  if (this.lastPlayedAd_ &&
@@ -105,12 +122,27 @@ shaka.ads.InterstitialAdManager = class {
105
122
  this.lastPlayedAd_ = null;
106
123
  }
107
124
  const currentInterstitial = this.getCurrentInterstitial_(
108
- needPreRoll, /* numberToSkip= */ 0, this.lastPlayedAd_);
125
+ /* needPreRoll= */ false, /* numberToSkip= */ 0, this.lastPlayedAd_);
109
126
  if (currentInterstitial) {
110
127
  this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
111
128
  /* adPosition= */ 1, /* initialTime= */ Date.now());
112
129
  }
113
- });
130
+ };
131
+
132
+ /** @private {shaka.util.Timer} */
133
+ this.timeUpdateTimer_ = new shaka.util.Timer(checkForInterstitials);
134
+
135
+ if ('requestVideoFrameCallback' in this.baseVideo_ &&
136
+ !shaka.util.Platform.isSmartTV()) {
137
+ const baseVideo = /** @type {!HTMLVideoElement} */ (this.baseVideo_);
138
+ const videoFrameCallback = () => {
139
+ checkForInterstitials();
140
+ baseVideo.requestVideoFrameCallback(videoFrameCallback);
141
+ };
142
+ baseVideo.requestVideoFrameCallback(videoFrameCallback);
143
+ } else {
144
+ this.timeUpdateTimer_.tickEvery(/* seconds= */ 0.025);
145
+ }
114
146
 
115
147
  /** @private {shaka.util.Timer} */
116
148
  this.pollTimer_ = new shaka.util.Timer(async () => {
@@ -168,11 +200,27 @@ shaka.ads.InterstitialAdManager = class {
168
200
  * @param {shaka.extern.AdsConfiguration} config
169
201
  */
170
202
  configure(config) {
171
- if (!this.adContainer_ ||
172
- this.usingBaseVideo_ != config.supportsMultipleMediaElements) {
203
+ this.config_ = config;
204
+ this.determineIfUsingBaseVideo_();
205
+ }
206
+
207
+ /**
208
+ * @private
209
+ */
210
+ determineIfUsingBaseVideo_() {
211
+ if (!this.adContainer_ || !this.config_ || this.playingAd_) {
173
212
  return;
174
213
  }
175
- this.usingBaseVideo_ = !config.supportsMultipleMediaElements;
214
+ let supportsMultipleMediaElements =
215
+ this.config_.supportsMultipleMediaElements;
216
+ const video = /** @type {HTMLVideoElement} */(this.baseVideo_);
217
+ if (video.webkitSupportsFullscreen && video.webkitDisplayingFullscreen) {
218
+ supportsMultipleMediaElements = false;
219
+ }
220
+ if (this.usingBaseVideo_ != supportsMultipleMediaElements) {
221
+ return;
222
+ }
223
+ this.usingBaseVideo_ = !supportsMultipleMediaElements;
176
224
  if (this.usingBaseVideo_) {
177
225
  this.video_ = this.baseVideo_;
178
226
  if (this.adVideo_) {
@@ -219,6 +267,10 @@ shaka.ads.InterstitialAdManager = class {
219
267
  if (this.adContainer_) {
220
268
  shaka.util.Dom.removeAllChildren(this.adContainer_);
221
269
  }
270
+ if (this.timeUpdateTimer_) {
271
+ this.timeUpdateTimer_.stop();
272
+ this.timeUpdateTimer_ = null;
273
+ }
222
274
  if (this.pollTimer_) {
223
275
  this.pollTimer_.stop();
224
276
  this.pollTimer_ = null;
@@ -231,13 +283,6 @@ shaka.ads.InterstitialAdManager = class {
231
283
  * @param {shaka.extern.HLSInterstitial} hlsInterstitial
232
284
  */
233
285
  async addMetadata(hlsInterstitial) {
234
- if (this.basePlayer_.getLoadMode() == shaka.Player.LoadMode.SRC_EQUALS &&
235
- this.usingBaseVideo_) {
236
- shaka.log.alwaysWarn(
237
- 'Unsupported HLS interstitial when using single media element',
238
- hlsInterstitial);
239
- return;
240
- }
241
286
  this.updatePlayerConfig_();
242
287
  const adInterstitials = await this.getInterstitialsInfo_(hlsInterstitial);
243
288
  if (adInterstitials.length) {
@@ -277,7 +322,7 @@ shaka.ads.InterstitialAdManager = class {
277
322
  const interstitial = {
278
323
  id: null,
279
324
  startTime: region.startTime,
280
- endTime: isReplace ? region.endTime : null,
325
+ endTime: region.endTime,
281
326
  uri: alternativeMPDUri,
282
327
  isSkippable: false,
283
328
  skipOffset: null,
@@ -336,16 +381,9 @@ shaka.ads.InterstitialAdManager = class {
336
381
  * @param {!Array.<shaka.extern.AdInterstitial>} interstitials
337
382
  */
338
383
  addInterstitials(interstitials) {
339
- if (this.basePlayer_.getLoadMode() == shaka.Player.LoadMode.SRC_EQUALS &&
340
- this.usingBaseVideo_) {
341
- shaka.log.alwaysWarn(
342
- 'Unsupported interstitial when using single media element',
343
- interstitials);
344
- return;
345
- }
346
384
  let cuepointsChanged = false;
347
385
  for (const interstitial of interstitials) {
348
- const interstitialId = JSON.stringify(interstitial);
386
+ const interstitialId = interstitial.id || JSON.stringify(interstitial);
349
387
  if (this.interstitialIds_.has(interstitialId)) {
350
388
  continue;
351
389
  }
@@ -423,8 +461,7 @@ shaka.ads.InterstitialAdManager = class {
423
461
  this.lastTime_ - roundDecimals(interstitial.startTime);
424
462
  if (difference > 0 &&
425
463
  (difference <= 1 || !interstitial.canJump)) {
426
- if (difference > 1 && lastPlayedAd &&
427
- !lastPlayedAd.pre && !lastPlayedAd.post &&
464
+ if (lastPlayedAd && !lastPlayedAd.pre && !lastPlayedAd.post &&
428
465
  lastPlayedAd.startTime >= interstitial.startTime) {
429
466
  isValid = false;
430
467
  } else {
@@ -455,10 +492,14 @@ shaka.ads.InterstitialAdManager = class {
455
492
  */
456
493
  async setupAd_(interstitial, sequenceLength, adPosition, initialTime,
457
494
  oncePlayed = 0) {
495
+ this.determineIfUsingBaseVideo_();
458
496
  goog.asserts.assert(this.video_, 'Must have video');
459
497
 
460
498
  this.lastPlayedAd_ = interstitial;
461
499
 
500
+ shaka.log.info('Starting interstitial',
501
+ interstitial.startTime, 'at', this.lastTime_);
502
+
462
503
  const startTime = Date.now();
463
504
 
464
505
  if (!this.video_.parentElement && this.adContainer_) {
@@ -578,7 +619,10 @@ shaka.ads.InterstitialAdManager = class {
578
619
  if (!this.baseVideo_.ended) {
579
620
  this.baseVideo_.play();
580
621
  }
622
+ } else {
623
+ this.cuepointsChanged_();
581
624
  }
625
+ this.determineIfUsingBaseVideo_();
582
626
  }
583
627
  };
584
628
  const error = async (e) => {
@@ -750,6 +794,16 @@ shaka.ads.InterstitialAdManager = class {
750
794
  if (hlsInterstitialId) {
751
795
  id = /** @type {string} */(hlsInterstitialId.data);
752
796
  }
797
+ const startTime = id == null ?
798
+ Math.floor(hlsInterstitial.startTime * 10) / 10:
799
+ hlsInterstitial.startTime;
800
+ let endTime = hlsInterstitial.endTime;
801
+ if (hlsInterstitial.endTime && hlsInterstitial.endTime != Infinity &&
802
+ typeof(hlsInterstitial.endTime) == 'number') {
803
+ endTime = id == null ?
804
+ Math.floor(hlsInterstitial.endTime * 10) / 10:
805
+ hlsInterstitial.endTime;
806
+ }
753
807
  const restrict = hlsInterstitial.values.find((v) => v.key == 'X-RESTRICT');
754
808
  let isSkippable = true;
755
809
  let canJump = true;
@@ -804,8 +858,8 @@ shaka.ads.InterstitialAdManager = class {
804
858
  }
805
859
  interstitialsAd.push({
806
860
  id,
807
- startTime: hlsInterstitial.startTime,
808
- endTime: hlsInterstitial.endTime,
861
+ startTime,
862
+ endTime,
809
863
  uri,
810
864
  isSkippable,
811
865
  skipOffset: isSkippable ? 0 : null,
@@ -836,8 +890,8 @@ shaka.ads.InterstitialAdManager = class {
836
890
  if (asset['URI']) {
837
891
  interstitialsAd.push({
838
892
  id,
839
- startTime: hlsInterstitial.startTime,
840
- endTime: hlsInterstitial.endTime,
893
+ startTime,
894
+ endTime,
841
895
  uri: asset['URI'],
842
896
  isSkippable,
843
897
  skipOffset: isSkippable ? 0 : null,
@@ -85,7 +85,9 @@ shaka.dash.SegmentTemplate = class {
85
85
  },
86
86
  };
87
87
  } else if (info.segmentDuration) {
88
- if (!isUpdate && context.adaptationSet.contentType !== 'image') {
88
+ if (!isUpdate &&
89
+ context.adaptationSet.contentType !== 'image' &&
90
+ context.adaptationSet.contentType !== 'text') {
89
91
  const periodStart = context.periodInfo.start;
90
92
  const periodId = context.period.id;
91
93
  const initialPeriodDuration = context.periodInfo.duration;
@@ -172,7 +174,9 @@ shaka.dash.SegmentTemplate = class {
172
174
  tsi.evict(availabilityStart);
173
175
  }
174
176
 
175
- if (info.timeline && context.adaptationSet.contentType !== 'image') {
177
+ if (info.timeline &&
178
+ context.adaptationSet.contentType !== 'image' &&
179
+ context.adaptationSet.contentType !== 'text') {
176
180
  const timeline = info.timeline;
177
181
  context.presentationTimeline.notifyTimeRange(
178
182
  timeline,
@@ -41,60 +41,36 @@ shaka.media.AdaptationSetCriteria = class {
41
41
  shaka.media.ExampleBasedCriteria = class {
42
42
  /**
43
43
  * @param {shaka.extern.Variant} example
44
- * @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
45
- * @param {boolean=} enableAudioGroups
44
+ * @param {shaka.config.CodecSwitchingStrategy} codecSwitchingStrategy
45
+ * @param {boolean} enableAudioGroups
46
46
  */
47
- constructor(example,
48
- codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
49
- enableAudioGroups = false) {
50
- /** @private {shaka.extern.Variant} */
51
- this.example_ = example;
52
- /** @private {shaka.config.CodecSwitchingStrategy} */
53
- this.codecSwitchingStrategy_ = codecSwitchingStrategy;
54
- /** @private {boolean} */
55
- this.enableAudioGroups_ = enableAudioGroups;
56
-
57
-
47
+ constructor(example, codecSwitchingStrategy, enableAudioGroups) {
58
48
  // We can't know if role and label are really important, so we don't use
59
49
  // role and label for this.
60
50
  const role = '';
61
51
  const audioLabel = '';
62
52
  const videoLabel = '';
63
- const hdrLevel = '';
64
- const spatialAudio = false;
65
- const videoLayout = '';
53
+ const hdrLevel = example.video && example.video.hdr ?
54
+ example.video.hdr : '';
55
+ const spatialAudio = example.audio && example.audio.spatialAudio ?
56
+ example.audio.spatialAudio : false;
57
+ const videoLayout = example.video && example.video.videoLayout ?
58
+ example.video.videoLayout : '';
66
59
  const channelCount = example.audio && example.audio.channelsCount ?
67
- example.audio.channelsCount :
68
- 0;
60
+ example.audio.channelsCount : 0;
61
+ const audioCodec = example.audio && example.audio.codecs ?
62
+ example.audio.codecs : '';
69
63
 
70
64
  /** @private {!shaka.media.AdaptationSetCriteria} */
71
- this.fallback_ = new shaka.media.PreferenceBasedCriteria(
65
+ this.preferenceBasedCriteria_ = new shaka.media.PreferenceBasedCriteria(
72
66
  example.language, role, channelCount, hdrLevel, spatialAudio,
73
67
  videoLayout, audioLabel, videoLabel,
74
- codecSwitchingStrategy, enableAudioGroups);
68
+ codecSwitchingStrategy, enableAudioGroups, audioCodec);
75
69
  }
76
70
 
77
71
  /** @override */
78
72
  create(variants) {
79
- const supportsSmoothCodecTransitions = this.codecSwitchingStrategy_ ==
80
- shaka.config.CodecSwitchingStrategy.SMOOTH &&
81
- shaka.media.Capabilities.isChangeTypeSupported();
82
- // We can't assume that the example is in |variants| because it could
83
- // actually be from another period.
84
- const shortList = variants.filter((variant) => {
85
- return shaka.media.AdaptationSet.areAdaptable(this.example_, variant,
86
- !supportsSmoothCodecTransitions, this.enableAudioGroups_);
87
- });
88
-
89
- if (shortList.length) {
90
- // Use the first item in the short list as the root. It should not matter
91
- // which element we use as all items in the short list should already be
92
- // compatible.
93
- return new shaka.media.AdaptationSet(shortList[0], shortList,
94
- !supportsSmoothCodecTransitions, this.enableAudioGroups_);
95
- } else {
96
- return this.fallback_.create(variants);
97
- }
73
+ return this.preferenceBasedCriteria_.create(variants);
98
74
  }
99
75
  };
100
76
 
@@ -111,16 +87,15 @@ shaka.media.PreferenceBasedCriteria = class {
111
87
  * @param {string} hdrLevel
112
88
  * @param {boolean} spatialAudio
113
89
  * @param {string} videoLayout
114
- * @param {string=} audioLabel
115
- * @param {string=} videoLabel
116
- * @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
117
- * @param {boolean=} enableAudioGroups
118
- * @param {string=} audioCodec
90
+ * @param {string} audioLabel
91
+ * @param {string} videoLabel
92
+ * @param {shaka.config.CodecSwitchingStrategy} codecSwitchingStrategy
93
+ * @param {boolean} enableAudioGroups
94
+ * @param {string} audioCodec
119
95
  */
120
96
  constructor(language, role, channelCount, hdrLevel, spatialAudio,
121
- videoLayout, audioLabel = '', videoLabel = '',
122
- codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
123
- enableAudioGroups = false, audioCodec = '') {
97
+ videoLayout, audioLabel, videoLabel, codecSwitchingStrategy,
98
+ enableAudioGroups, audioCodec) {
124
99
  /** @private {string} */
125
100
  this.language_ = language;
126
101
  /** @private {string} */
@@ -149,8 +149,9 @@ shaka.media.MediaSourceEngine = class {
149
149
  /** @private {?number} */
150
150
  this.lastDuration_ = null;
151
151
 
152
- /** @private {?shaka.util.TsParser} */
153
- this.tsParser_ = null;
152
+ /** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,
153
+ !shaka.util.TsParser>} */
154
+ this.tsParsers_ = {};
154
155
 
155
156
  /** @private {?number} */
156
157
  this.firstVideoTimestamp_ = null;
@@ -438,7 +439,7 @@ shaka.media.MediaSourceEngine = class {
438
439
  // This object is owned by Player
439
440
  this.lcevcDec_ = null;
440
441
 
441
- this.tsParser_ = null;
442
+ this.tsParsers_ = {};
442
443
  this.playerInterface_ = null;
443
444
  }
444
445
 
@@ -490,7 +491,7 @@ shaka.media.MediaSourceEngine = class {
490
491
  this.manifestType_ == shaka.media.ManifestParser.HLS &&
491
492
  !this.ignoreManifestTimestampsInSegmentsMode_;
492
493
 
493
- this.tsParser_ = null;
494
+ this.tsParsers_ = {};
494
495
 
495
496
  for (const contentType of streamsByType.keys()) {
496
497
  const stream = streamsByType.get(contentType);
@@ -841,12 +842,12 @@ shaka.media.MediaSourceEngine = class {
841
842
  }
842
843
  } else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
843
844
  shaka.util.TsParser.probe(uint8ArrayData)) {
844
- if (!this.tsParser_) {
845
- this.tsParser_ = new shaka.util.TsParser();
845
+ if (!this.tsParsers_[contentType]) {
846
+ this.tsParsers_[contentType] = new shaka.util.TsParser();
846
847
  } else {
847
- this.tsParser_.clearData();
848
+ this.tsParsers_[contentType].clearData();
848
849
  }
849
- const tsParser = this.tsParser_.parse(uint8ArrayData);
850
+ const tsParser = this.tsParsers_[contentType].parse(uint8ArrayData);
850
851
  const startTime = tsParser.getStartTime(contentType);
851
852
  if (startTime != null) {
852
853
  timestamp = startTime;
@@ -630,7 +630,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
630
630
  this.config_.preferredAudioLabel,
631
631
  this.config_.preferredVideoLabel,
632
632
  this.config_.mediaSource.codecSwitchingStrategy,
633
- this.config_.manifest.dash.enableAudioGroups);
633
+ this.config_.manifest.dash.enableAudioGroups,
634
+ /* audioCodec= */ '');
634
635
  }
635
636
 
636
637
  // Make the ABR manager.
@@ -1966,8 +1966,7 @@ shaka.media.StreamingEngine = class {
1966
1966
  const MimeUtils = shaka.util.MimeUtils;
1967
1967
  const StreamingEngine = shaka.media.StreamingEngine;
1968
1968
  const logPrefix = StreamingEngine.logPrefix_(mediaState);
1969
- const nullLastReferences = mediaState.lastSegmentReference == null &&
1970
- mediaState.lastInitSegmentReference == null;
1969
+ const nullLastReferences = mediaState.lastSegmentReference == null;
1971
1970
 
1972
1971
  /** @type {!Array.<!Promise>} */
1973
1972
  const operations = [];
@@ -576,12 +576,12 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
576
576
  }
577
577
  };
578
578
  const headersReceived = (headers) => {
579
- if (this.onHeadersReceived_) {
580
- this.onHeadersReceived_(headers, request, type);
581
- }
582
579
  headersReceivedCalled = true;
583
580
  request.timeToFirstByte = Date.now() -
584
581
  /** @type {number} */ (request.requestStartTime);
582
+ if (this.onHeadersReceived_) {
583
+ this.onHeadersReceived_(headers, request, type);
584
+ }
585
585
  };
586
586
  request.requestStartTime = Date.now();
587
587
 
package/lib/player.js CHANGED
@@ -786,7 +786,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
786
786
  this.config_.preferredAudioLabel,
787
787
  this.config_.preferredVideoLabel,
788
788
  this.config_.mediaSource.codecSwitchingStrategy,
789
- this.config_.manifest.dash.enableAudioGroups);
789
+ this.config_.manifest.dash.enableAudioGroups,
790
+ /* audioCodec= */ '');
790
791
 
791
792
  /** @private {string} */
792
793
  this.currentTextLanguage_ = this.config_.preferredTextLanguage;
@@ -2929,22 +2930,47 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2929
2930
 
2930
2931
  if (mediaElement.textTracks) {
2931
2932
  this.createTextDisplayer_();
2932
- this.loadEventManager_.listen(
2933
- mediaElement, 'enterpictureinpicture', () => {
2934
- const track = this.getFilteredTextTracks_()
2935
- .find((t) => t.mode !== 'disabled');
2936
- if (track) {
2937
- track.mode = 'showing';
2938
- }
2939
- });
2940
- this.loadEventManager_.listen(
2941
- mediaElement, 'leavepictureinpicture', () => {
2942
- const track = this.getFilteredTextTracks_()
2943
- .find((t) => t.mode !== 'disabled');
2944
- if (track) {
2945
- track.mode = 'hidden';
2946
- }
2947
- });
2933
+ const setShowingMode = () => {
2934
+ const track = this.getFilteredTextTracks_()
2935
+ .find((t) => t.mode !== 'disabled');
2936
+ if (track) {
2937
+ track.mode = 'showing';
2938
+ }
2939
+ };
2940
+ const setHiddenMode = () => {
2941
+ const track = this.getFilteredTextTracks_()
2942
+ .find((t) => t.mode !== 'disabled');
2943
+ if (track) {
2944
+ track.mode = 'hidden';
2945
+ }
2946
+ };
2947
+ this.loadEventManager_.listen(mediaElement, 'enterpictureinpicture',
2948
+ () => setShowingMode());
2949
+ this.loadEventManager_.listen(mediaElement, 'leavepictureinpicture',
2950
+ () => setHiddenMode());
2951
+ if (mediaElement.remote) {
2952
+ this.loadEventManager_.listen(mediaElement.remote, 'connect',
2953
+ () => setHiddenMode());
2954
+ this.loadEventManager_.listen(mediaElement.remote, 'connecting',
2955
+ () => setHiddenMode());
2956
+ this.loadEventManager_.listen(mediaElement.remote, 'disconnect',
2957
+ () => setHiddenMode());
2958
+ } else if ('webkitCurrentPlaybackTargetIsWireless' in mediaElement) {
2959
+ this.loadEventManager_.listen(mediaElement,
2960
+ 'webkitcurrentplaybacktargetiswirelesschanged',
2961
+ () => setHiddenMode());
2962
+ }
2963
+ const video = /** @type {HTMLVideoElement} */(mediaElement);
2964
+ if (video.webkitSupportsFullscreen) {
2965
+ this.loadEventManager_.listen(video, 'webkitpresentationmodechanged',
2966
+ () => {
2967
+ if (video.webkitPresentationMode != 'inline') {
2968
+ setShowingMode();
2969
+ } else {
2970
+ setHiddenMode();
2971
+ }
2972
+ });
2973
+ }
2948
2974
  }
2949
2975
  // Add all media element listeners.
2950
2976
  this.addBasicMediaListeners_(mediaElement, startTimeOfLoad);
@@ -5271,7 +5297,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5271
5297
  label,
5272
5298
  /* videoLabel= */ '',
5273
5299
  this.config_.mediaSource.codecSwitchingStrategy,
5274
- this.config_.manifest.dash.enableAudioGroups);
5300
+ this.config_.manifest.dash.enableAudioGroups,
5301
+ /* audioCodec= */ '');
5275
5302
 
5276
5303
  this.chooseVariantAndSwitch_(clearBuffer, safeMargin);
5277
5304
  } else if (this.video_ && this.video_.audioTracks) {
@@ -6352,7 +6379,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6352
6379
  // Because this.video_ may not be set when the config is built, the default
6353
6380
  // TextDisplay factory must capture a reference to "this".
6354
6381
  config.textDisplayFactory = () => {
6355
- if (this.videoContainer_) {
6382
+ // On iOS where the Fullscreen API is not available we prefer
6383
+ // SimpleTextDisplayer because it works with the Fullscreen API of the
6384
+ // video element itself.
6385
+ const Platform = shaka.util.Platform;
6386
+ if (this.videoContainer_ &&
6387
+ (!Platform.safariVersion() || document.fullscreenEnabled)) {
6356
6388
  const latestConfig = this.getConfiguration();
6357
6389
  return new shaka.text.UITextDisplayer(
6358
6390
  this.video_, this.videoContainer_, latestConfig.textDisplayer);
@@ -6707,6 +6739,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6707
6739
  return;
6708
6740
  }
6709
6741
 
6742
+ // We don't want to block the user from pausing the stream.
6743
+ if (this.video_.paused) {
6744
+ return;
6745
+ }
6746
+
6710
6747
  let targetLatency;
6711
6748
  let maxLatency;
6712
6749
  let maxPlaybackRate;
@@ -8086,7 +8123,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8086
8123
  * @export
8087
8124
  */
8088
8125
  // eslint-disable-next-line no-useless-concat, max-len
8089
- shaka.Player.version = 'v4.11.11' + '-uncompiled'; // x-release-please-version
8126
+ shaka.Player.version = 'v4.11.13' + '-uncompiled'; // x-release-please-version
8090
8127
 
8091
8128
  // Initialize the deprecation system using the version string we just set
8092
8129
  // on the player.
@@ -61,7 +61,8 @@ shaka.polyfill.MediaCapabilities = class {
61
61
  shaka.util.Platform.isWebOS() ||
62
62
  shaka.util.Platform.isTizen() ||
63
63
  shaka.util.Platform.isEOS() ||
64
- shaka.util.Platform.isHisense()) {
64
+ shaka.util.Platform.isHisense() ||
65
+ shaka.util.Platform.isComcastX1()) {
65
66
  canUseNativeMCap = false;
66
67
  }
67
68
  if (canUseNativeMCap && navigator.mediaCapabilities) {
@@ -87,6 +87,18 @@ shaka.polyfill.MediaSource = class {
87
87
  // the player from trying to play opus on Tizen, we will override media
88
88
  // source to always reject opus content.
89
89
  shaka.polyfill.MediaSource.rejectCodec_('opus');
90
+ } else if (shaka.util.Platform.isComcastX1()) {
91
+ // XOne look to be like safari 8 which do not correctly
92
+ // implement abort() on SourceBuffer.
93
+ // Calling abort() before appending a segment causes that segment to be
94
+ // incomplete in the buffer.
95
+ // Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
96
+ shaka.polyfill.MediaSource.stubAbort_();
97
+ // If you remove up to a keyframe, Webkit 601.x.x incorrectly will also
98
+ // remove that keyframe and the content up to the next.
99
+ // Offsetting the end of the removal range seems to help.
100
+ // Bug filed: https://bugs.webkit.org/show_bug.cgi?id=177884
101
+ shaka.polyfill.MediaSource.patchRemovalRange_();
90
102
  } else {
91
103
  shaka.log.info('Using native MSE as-is.');
92
104
  }
package/lib/text/cue.js CHANGED
@@ -632,6 +632,7 @@ shaka.text.Cue = class {
632
632
  nestedCue.region = new shaka.text.CueRegion();
633
633
  nestedCue.position = null;
634
634
  nestedCue.size = 0;
635
+ nestedCue.textAlign = shaka.text.Cue.textAlign.CENTER;
635
636
 
636
637
  if (TXml.isNode(element)) {
637
638
  const bold = shaka.text.Cue.fontWeight.BOLD;
@@ -9,6 +9,7 @@ goog.provide('shaka.text.UITextDisplayer');
9
9
 
10
10
  goog.require('goog.asserts');
11
11
  goog.require('shaka.Deprecate');
12
+ goog.require('shaka.log');
12
13
  goog.require('shaka.text.Cue');
13
14
  goog.require('shaka.text.CueRegion');
14
15
  goog.require('shaka.text.Utils');
@@ -35,6 +36,12 @@ shaka.text.UITextDisplayer = class {
35
36
  constructor(video, videoContainer, config) {
36
37
  goog.asserts.assert(videoContainer, 'videoContainer should be valid.');
37
38
 
39
+ if (!document.fullscreenEnabled) {
40
+ shaka.log.alwaysWarn('Using UITextDisplayer in a browser without ' +
41
+ 'Fullscreen API support causes subtitles to not be rendered in ' +
42
+ 'fullscreen');
43
+ }
44
+
38
45
  /** @private {boolean} */
39
46
  this.isTextVisible_ = false;
40
47
 
@@ -248,6 +255,7 @@ shaka.text.UITextDisplayer = class {
248
255
  */
249
256
  setTextVisibility(on) {
250
257
  this.isTextVisible_ = on;
258
+ this.updateCaptions_(/* forceUpdate= */ true);
251
259
  }
252
260
 
253
261
  /**
package/lib/util/error.js CHANGED
@@ -1158,5 +1158,5 @@ shaka.util.Error.Code = {
1158
1158
  /**
1159
1159
  * The VAST contained invalid XML markup.
1160
1160
  */
1161
- 'VAST_INVALID_XML': 4001,
1161
+ 'VAST_INVALID_XML': 10007,
1162
1162
  };
@@ -765,7 +765,7 @@ shaka.util.Mp4Generator = class {
765
765
  0x00, 0x00, 0x00, 0x01, // default_sample_description_index
766
766
  0x00, 0x00, 0x00, 0x00, // default_sample_duration
767
767
  0x00, 0x00, 0x00, 0x00, // default_sample_size
768
- 0x00, 0x01, 0x00, 0x01, // default_sample_flags
768
+ 0x00, 0x00, 0x00, 0x00, // default_sample_flags
769
769
  ]);
770
770
  return Mp4Generator.box('trex', bytes);
771
771
  }
@@ -300,7 +300,7 @@ shaka.util.Platform = class {
300
300
  !shaka.util.Platform.isOrange() &&
301
301
  !shaka.util.Platform.isPS4() &&
302
302
  !shaka.util.Platform.isAmazonFireTV() &&
303
- !shaka.util.Platform.isWPE() &&
303
+ !shaka.util.Platform.isComcastX1() &&
304
304
  !shaka.util.Platform.isZenterio() &&
305
305
  !shaka.util.Platform.isSkyQ();
306
306
  }
@@ -368,7 +368,7 @@ shaka.util.Platform = class {
368
368
  * Check if the current platform is Comcast X1.
369
369
  * @return {boolean}
370
370
  */
371
- static isWPE() {
371
+ static isComcastX1() {
372
372
  return shaka.util.Platform.userAgentContains_('WPE');
373
373
  }
374
374
 
@@ -536,7 +536,7 @@ shaka.util.Platform = class {
536
536
  Platform.isPS5() || Platform.isAmazonFireTV() ||
537
537
  Platform.isEOS() || Platform.isAPL() ||
538
538
  Platform.isVirginMedia() || Platform.isOrange() ||
539
- Platform.isWPE() || Platform.isChromecast() ||
539
+ Platform.isComcastX1() || Platform.isChromecast() ||
540
540
  Platform.isHisense() || Platform.isZenterio()) {
541
541
  return true;
542
542
  }