shaka-player 4.11.13 → 4.12.1

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 (71) hide show
  1. package/README.md +47 -0
  2. package/dist/deps.js +8 -8
  3. package/dist/locales.js +35 -35
  4. package/dist/shaka-player.compiled.d.ts +36 -13
  5. package/dist/shaka-player.compiled.debug.d.ts +36 -13
  6. package/dist/shaka-player.compiled.debug.externs.js +46 -11
  7. package/dist/shaka-player.compiled.debug.js +543 -517
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +46 -11
  10. package/dist/shaka-player.compiled.js +1348 -1331
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +37 -14
  13. package/dist/shaka-player.ui.debug.d.ts +37 -14
  14. package/dist/shaka-player.ui.debug.externs.js +46 -11
  15. package/dist/shaka-player.ui.debug.js +629 -598
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +46 -11
  18. package/dist/shaka-player.ui.js +1624 -1603
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/ads.js +9 -0
  21. package/externs/shaka/manifest.js +26 -1
  22. package/externs/shaka/offline.js +8 -2
  23. package/externs/shaka/player.js +80 -28
  24. package/externs/shaka/text.js +9 -0
  25. package/lib/ads/ad_manager.js +6 -0
  26. package/lib/ads/ad_utils.js +1 -0
  27. package/lib/ads/client_side_ad.js +8 -0
  28. package/lib/ads/interstitial_ad.js +25 -4
  29. package/lib/ads/interstitial_ad_manager.js +67 -8
  30. package/lib/ads/media_tailor_ad.js +8 -0
  31. package/lib/ads/server_side_ad.js +8 -0
  32. package/lib/dash/content_protection.js +31 -0
  33. package/lib/dash/dash_parser.js +22 -12
  34. package/lib/hls/hls_parser.js +63 -49
  35. package/lib/media/adaptation_set.js +11 -17
  36. package/lib/media/adaptation_set_criteria.js +4 -9
  37. package/lib/media/drm_engine.js +3 -1
  38. package/lib/media/manifest_filterer.js +2 -1
  39. package/lib/media/media_source_engine.js +286 -20
  40. package/lib/media/playhead.js +3 -1
  41. package/lib/media/preload_manager.js +10 -7
  42. package/lib/media/segment_prefetch.js +32 -13
  43. package/lib/media/segment_reference.js +3 -3
  44. package/lib/media/streaming_engine.js +87 -261
  45. package/lib/mss/mss_parser.js +1 -0
  46. package/lib/offline/download_info.js +5 -1
  47. package/lib/offline/indexeddb/storage_mechanism.js +4 -3
  48. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  49. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  50. package/lib/offline/manifest_converter.js +4 -0
  51. package/lib/offline/storage.js +277 -5
  52. package/lib/player.js +224 -78
  53. package/lib/text/simple_text_displayer.js +7 -0
  54. package/lib/text/stub_text_displayer.js +7 -0
  55. package/lib/text/ui_text_displayer.js +12 -0
  56. package/lib/util/dom_utils.js +14 -0
  57. package/lib/util/error.js +5 -0
  58. package/lib/util/periods.js +2 -0
  59. package/lib/util/platform.js +15 -0
  60. package/lib/util/player_configuration.js +11 -4
  61. package/lib/util/stream_utils.js +20 -1
  62. package/package.json +1 -1
  63. package/ui/ad_statistics_button.js +12 -0
  64. package/ui/controls.js +31 -7
  65. package/ui/externs/ui.js +8 -1
  66. package/ui/fullscreen_button.js +9 -0
  67. package/ui/locales/be.json +49 -0
  68. package/ui/mute_button.js +42 -11
  69. package/ui/skip_ad_button.js +16 -11
  70. package/ui/statistics_button.js +12 -0
  71. package/ui/ui.js +10 -5
package/lib/player.js CHANGED
@@ -556,6 +556,18 @@ goog.requireType('shaka.media.PresentationTimeline');
556
556
  */
557
557
 
558
558
 
559
+ /**
560
+ * @event shaka.Player.ProducerReferenceTimeEvent
561
+ * @description Fired when the content includes ProducerReferenceTime (PRFT)
562
+ * info.
563
+ * @property {string} type
564
+ * 'prft'
565
+ * @property {shaka.extern.ProducerReferenceTime} detail
566
+ * An object which contains the content of the PRFT box.
567
+ * @exportDoc
568
+ */
569
+
570
+
559
571
  /**
560
572
  * @summary The main player object for Shaka Player.
561
573
  *
@@ -786,7 +798,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
786
798
  this.config_.preferredAudioLabel,
787
799
  this.config_.preferredVideoLabel,
788
800
  this.config_.mediaSource.codecSwitchingStrategy,
789
- this.config_.manifest.dash.enableAudioGroups,
790
801
  /* audioCodec= */ '');
791
802
 
792
803
  /** @private {string} */
@@ -1447,16 +1458,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1447
1458
  if (this.video_) {
1448
1459
  // Remove all track nodes
1449
1460
  shaka.util.Dom.removeAllChildren(this.video_);
1450
- }
1451
1461
 
1452
- // In order to unload a media element, we need to remove the src attribute
1453
- // and then load again. When we destroy media source engine, this will be
1454
- // done for us, but for src=, we need to do it here.
1455
- //
1456
- // DrmEngine requires this to be done before we destroy DrmEngine itself.
1457
- if (this.video_ && this.video_.src) {
1458
- this.video_.removeAttribute('src');
1459
- this.video_.load();
1462
+ // In order to unload a media element, we need to remove the src
1463
+ // attribute and then load again. When we destroy media source engine,
1464
+ // this will be done for us, but for src=, we need to do it here.
1465
+ //
1466
+ // DrmEngine requires this to be done before we destroy DrmEngine
1467
+ // itself.
1468
+ if (this.video_.src) {
1469
+ this.video_.removeAttribute('src');
1470
+ this.video_.load();
1471
+ }
1460
1472
  }
1461
1473
 
1462
1474
  if (this.drmEngine_) {
@@ -1780,6 +1792,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1780
1792
  startTimeOfLoad, prefetchedVariant, segmentPrefetchById);
1781
1793
  }, 'loadInner_');
1782
1794
  preloadManager.stopQueuingLatePhaseQueuedOperations();
1795
+
1796
+ if (this.mimeType_ && shaka.util.Platform.isSafari() &&
1797
+ shaka.util.MimeUtils.isHlsType(this.mimeType_)) {
1798
+ this.mediaSourceEngine_.addSecondarySource(
1799
+ this.assetUri_, this.mimeType_);
1800
+ }
1783
1801
  }
1784
1802
  this.dispatchEvent(shaka.Player.makeEvent_(
1785
1803
  shaka.util.FakeEvent.EventName.Loaded));
@@ -2059,7 +2077,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2059
2077
  // Avoid having to detect the resolution again if it has already been
2060
2078
  // detected or set
2061
2079
  if (this.maxHwRes_.width == Infinity &&
2062
- this.maxHwRes_.height == Infinity) {
2080
+ this.maxHwRes_.height == Infinity &&
2081
+ !this.config_.ignoreHardwareResolution) {
2063
2082
  const maxResolution =
2064
2083
  await shaka.util.Platform.detectMaxHardwareResolution();
2065
2084
  this.maxHwRes_.width = maxResolution.width;
@@ -2395,6 +2414,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2395
2414
 
2396
2415
  this.makeStateChangeEvent_('media-source');
2397
2416
 
2417
+ // Remove children if we had any, i.e. from previously used src= mode.
2418
+ this.video_.removeAttribute('src');
2419
+ shaka.util.Dom.removeAllChildren(this.video_);
2420
+
2398
2421
  this.createTextDisplayer_();
2399
2422
  goog.asserts.assert(this.textDisplayer_,
2400
2423
  'Text displayer should be created already');
@@ -2406,6 +2429,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2406
2429
  onMetadata: (metadata, offset, endTime) => {
2407
2430
  this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
2408
2431
  },
2432
+ onEvent: (event) => this.dispatchEvent(event),
2433
+ onManifestUpdate: () => this.onManifestUpdate_(),
2409
2434
  },
2410
2435
  this.lcevcDec_);
2411
2436
  mediaSourceEngine.configure(this.config_.mediaSource);
@@ -2690,6 +2715,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2690
2715
  // text.
2691
2716
  if (initialTextStream && this.shouldStreamText_()) {
2692
2717
  this.streamingEngine_.switchTextStream(initialTextStream);
2718
+ this.setTextDisplayerLanguage_();
2693
2719
  }
2694
2720
  }
2695
2721
 
@@ -2866,6 +2892,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2866
2892
  external: false,
2867
2893
  fastSwitching: false,
2868
2894
  fullMimeTypes: new Set(),
2895
+ isAudioMuxedInVideo: false,
2869
2896
  };
2870
2897
  stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2871
2898
  stream.mimeType, stream.codecs));
@@ -2991,12 +3018,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2991
3018
  playbackUri += ',' + this.config_.playRangeEnd;
2992
3019
  }
2993
3020
  }
2994
- mediaElement.src = playbackUri;
2995
- // If ManagedMediaSource exists, we've disabled remote playback on our own.
2996
- // Reenable it so AirPlay can be used via RemotePlayback API.
2997
- if (this.mediaSourceEngine_ && window.ManagedMediaSource) {
2998
- mediaElement.disableRemotePlayback = false;
3021
+
3022
+ if (this.mediaSourceEngine_ ) {
3023
+ await this.mediaSourceEngine_.destroy();
3024
+ this.mediaSourceEngine_ = null;
2999
3025
  }
3026
+ shaka.util.Dom.removeAllChildren(mediaElement);
3027
+
3028
+ mediaElement.src = playbackUri;
3000
3029
 
3001
3030
  // Tizen 3 / WebOS won't load anything unless you call load() explicitly,
3002
3031
  // no matter the value of the preload attribute. This is harmful on some
@@ -3740,7 +3769,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3740
3769
  netEngine: this.networkingEngine_,
3741
3770
  onError: (error) => this.onError_(error),
3742
3771
  onEvent: (event) => this.dispatchEvent(event),
3743
- onManifestUpdate: () => this.onManifestUpdate_(),
3744
3772
  onSegmentAppended: (reference, stream) => {
3745
3773
  this.onSegmentAppended_(
3746
3774
  reference.startTime, reference.endTime, stream.type,
@@ -3755,9 +3783,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3755
3783
  beforeAppendSegment: (contentType, segment) => {
3756
3784
  return this.drmEngine_.parseInbandPssh(contentType, segment);
3757
3785
  },
3758
- onMetadata: (metadata, offset, endTime) => {
3759
- this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
3760
- },
3761
3786
  disableStream: (stream, time) => this.disableStream(stream, time),
3762
3787
  };
3763
3788
 
@@ -3970,6 +3995,33 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3970
3995
  delete config['manifest']['hls']['useSafariBehaviorForLive'];
3971
3996
  }
3972
3997
 
3998
+ // Deprecate 'streaming.parsePrftBox' configuration.
3999
+ if (config['streaming'] && 'parsePrftBox' in config['streaming']) {
4000
+ shaka.Deprecate.deprecateFeature(5,
4001
+ 'streaming.parsePrftBox configuration',
4002
+ 'Now fired without needing a configuration.');
4003
+ delete config['streaming']['parsePrftBox'];
4004
+ }
4005
+
4006
+ // Deprecate 'manifest.dash.enableAudioGroups' configuration.
4007
+ if (config['manifest'] && config['manifest']['dash'] &&
4008
+ 'enableAudioGroups' in config['manifest']['dash']) {
4009
+ shaka.Deprecate.deprecateFeature(5,
4010
+ 'manifest.dash.enableAudioGroups configuration',
4011
+ 'It is now enabled by default and cannot be disabled.');
4012
+ delete config['manifest']['dash']['enableAudioGroups'];
4013
+ }
4014
+
4015
+ // Deprecate 'streaming.dispatchAllEmsgBoxes' configuration.
4016
+ if (config['streaming'] && 'dispatchAllEmsgBoxes' in config['streaming']) {
4017
+ shaka.Deprecate.deprecateFeature(5,
4018
+ 'streaming.dispatchAllEmsgBoxes configuration',
4019
+ 'Please Use mediaSource.dispatchAllEmsgBoxes instead.');
4020
+ config['mediaSource']['dispatchAllEmsgBoxes'] =
4021
+ config['streaming']['dispatchAllEmsgBoxes'];
4022
+ delete config['streaming']['dispatchAllEmsgBoxes'];
4023
+ }
4024
+
3973
4025
  // If lowLatencyMode is enabled, and inaccurateManifestTolerance and
3974
4026
  // rebufferingGoal and segmentPrefetchLimit and baseDelay and
3975
4027
  // autoCorrectDrift and maxDisabledTime are not specified, set
@@ -4107,6 +4159,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4107
4159
  'Text displayer w/ configure',
4108
4160
  'Text displayer should have a "configure" method!');
4109
4161
  }
4162
+ if (!this.textDisplayer_.setTextLanguage) {
4163
+ shaka.Deprecate.deprecateFeature(5,
4164
+ 'Text displayer w/ setTextLanguage',
4165
+ 'Text displayer should have a "setTextLanguage" method!');
4166
+ }
4110
4167
  this.textDisplayer_.setTextVisibility(oldDisplayer.isTextVisible());
4111
4168
  oldDisplayer.destroy();
4112
4169
  if (this.mediaSourceEngine_) {
@@ -4946,12 +5003,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4946
5003
  * @export
4947
5004
  */
4948
5005
  selectTextTrack(track) {
4949
- if (this.manifest_ && this.streamingEngine_&& !this.isRemotePlayback()) {
5006
+ const selectMediaSourceMode = () => {
4950
5007
  const stream = this.manifest_.textStreams.find(
4951
5008
  (stream) => stream.id == track.id);
4952
5009
 
4953
5010
  if (!stream) {
4954
- shaka.log.error('No stream with id', track.id);
5011
+ if (!this.isRemotePlayback()) {
5012
+ shaka.log.error('No stream with id', track.id);
5013
+ }
4955
5014
  return;
4956
5015
  }
4957
5016
 
@@ -4964,30 +5023,48 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4964
5023
  this.addTextStreamToSwitchHistory_(stream, /* fromAdaptation= */ false);
4965
5024
  this.streamingEngine_.switchTextStream(stream);
4966
5025
  this.onTextChanged_();
5026
+ this.setTextDisplayerLanguage_();
4967
5027
 
4968
5028
  // Workaround for
4969
5029
  // https://github.com/shaka-project/shaka-player/issues/1299
4970
5030
  // When track is selected, back-propagate the language to
4971
5031
  // currentTextLanguage_.
4972
5032
  this.currentTextLanguage_ = stream.language;
4973
- } else if (this.video_ && this.video_.src && this.video_.textTracks) {
4974
- const textTracks = this.getFilteredTextTracks_();
4975
- const oldTrack = textTracks.find((textTrack) =>
4976
- textTrack.mode !== 'disabled');
4977
- const newTrack = textTracks.find((textTrack) =>
4978
- shaka.util.StreamUtils.html5TrackId(textTrack) === track.id);
4979
- if (oldTrack !== newTrack) {
4980
- if (oldTrack) {
4981
- oldTrack.mode = 'disabled';
4982
- this.loadEventManager_.unlisten(oldTrack, 'cuechange');
4983
- this.textDisplayer_.remove(0, Infinity);
5033
+ };
5034
+ const selectSrcEqualsMode = () => {
5035
+ if (this.video_ && this.video_.textTracks) {
5036
+ const textTracks = this.getFilteredTextTracks_();
5037
+ const oldTrack = textTracks.find((textTrack) =>
5038
+ textTrack.mode !== 'disabled');
5039
+ const newTrack = textTracks.find((textTrack) =>
5040
+ shaka.util.StreamUtils.html5TrackId(textTrack) === track.id);
5041
+ if (!newTrack) {
5042
+ shaka.log.error('No track with id', track.id);
5043
+ return;
4984
5044
  }
4985
- if (newTrack) {
4986
- this.enableNativeTrack_(newTrack);
5045
+ if (oldTrack !== newTrack) {
5046
+ if (oldTrack) {
5047
+ oldTrack.mode = 'disabled';
5048
+ this.loadEventManager_.unlisten(oldTrack, 'cuechange');
5049
+ this.textDisplayer_.remove(0, Infinity);
5050
+ }
5051
+ if (newTrack) {
5052
+ this.enableNativeTrack_(newTrack);
5053
+ }
4987
5054
  }
5055
+ this.onTextChanged_();
5056
+ this.setTextDisplayerLanguage_();
5057
+ }
5058
+ };
5059
+ if (this.manifest_ && this.playhead_) {
5060
+ selectMediaSourceMode();
5061
+ // When using MSE + remote we need to set tracks for both MSE and native
5062
+ // apis so that synchronization is maintained.
5063
+ if (!this.isRemotePlayback()) {
5064
+ return;
4988
5065
  }
4989
- this.onTextChanged_();
4990
5066
  }
5067
+ selectSrcEqualsMode();
4991
5068
  }
4992
5069
 
4993
5070
  /**
@@ -5037,11 +5114,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5037
5114
  * @export
5038
5115
  */
5039
5116
  selectVariantTrack(track, clearBuffer = false, safeMargin = 0) {
5040
- if (this.manifest_ && this.streamingEngine_&& !this.isRemotePlayback()) {
5117
+ const selectMediaSourceMode = () => {
5041
5118
  const variant = this.manifest_.variants.find(
5042
5119
  (variant) => variant.id == track.id);
5043
5120
  if (!variant) {
5044
- shaka.log.error('No variant with id', track.id);
5121
+ if (!this.isRemotePlayback()) {
5122
+ shaka.log.error('No variant with id', track.id);
5123
+ }
5045
5124
  return;
5046
5125
  }
5047
5126
 
@@ -5065,8 +5144,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5065
5144
  'calling selectVariantTrack().');
5066
5145
  }
5067
5146
 
5068
- this.switchVariant_(
5069
- variant, /* fromAdaptation= */ false, clearBuffer, safeMargin);
5147
+ if (this.isRemotePlayback()) {
5148
+ this.switchVariant_(
5149
+ variant, /* fromAdaptation= */ false,
5150
+ /* clearBuffer= */ false, /* safeMargin= */ 0);
5151
+ } else {
5152
+ this.switchVariant_(
5153
+ variant, /* fromAdaptation= */ false,
5154
+ clearBuffer || false, safeMargin || 0);
5155
+ }
5070
5156
 
5071
5157
  // Workaround for
5072
5158
  // https://github.com/shaka-project/shaka-player/issues/1299
@@ -5074,23 +5160,34 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5074
5160
  // currentAudioLanguage_.
5075
5161
  this.currentAdaptationSetCriteria_ = new shaka.media.ExampleBasedCriteria(
5076
5162
  variant,
5077
- this.config_.mediaSource.codecSwitchingStrategy,
5078
- this.config_.manifest.dash.enableAudioGroups);
5163
+ this.config_.mediaSource.codecSwitchingStrategy);
5079
5164
 
5080
5165
  // Update AbrManager variants to match these new settings.
5081
5166
  this.updateAbrManagerVariants_();
5082
- } else if (this.video_ && this.video_.audioTracks) {
5083
- // Safari's native HLS won't let you choose an explicit variant, though
5084
- // you can choose audio languages this way.
5085
- const audioTracks = Array.from(this.video_.audioTracks);
5086
- for (const audioTrack of audioTracks) {
5087
- if (shaka.util.StreamUtils.html5TrackId(audioTrack) == track.id) {
5088
- // This will reset the "enabled" of other tracks to false.
5089
- this.switchHtml5Track_(audioTrack);
5090
- return;
5167
+ };
5168
+ const selectSrcEqualsMode = () => {
5169
+ if (this.video_ && this.video_.audioTracks) {
5170
+ // Safari's native HLS won't let you choose an explicit variant, though
5171
+ // you can choose audio languages this way.
5172
+ const audioTracks = Array.from(this.video_.audioTracks);
5173
+ for (const audioTrack of audioTracks) {
5174
+ if (shaka.util.StreamUtils.html5TrackId(audioTrack) == track.id) {
5175
+ // This will reset the "enabled" of other tracks to false.
5176
+ this.switchHtml5Track_(audioTrack);
5177
+ return;
5178
+ }
5091
5179
  }
5092
5180
  }
5181
+ };
5182
+ if (this.manifest_ && this.playhead_) {
5183
+ selectMediaSourceMode();
5184
+ // When using MSE + remote we need to set tracks for both MSE and native
5185
+ // apis so that synchronization is maintained.
5186
+ if (!this.isRemotePlayback()) {
5187
+ return;
5188
+ }
5093
5189
  }
5190
+ selectSrcEqualsMode();
5094
5191
  }
5095
5192
 
5096
5193
  /**
@@ -5151,20 +5248,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5151
5248
  */
5152
5249
  selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0,
5153
5250
  codec = '') {
5154
- if (this.manifest_ && this.playhead_&& !this.isRemotePlayback()) {
5251
+ const selectMediaSourceMode = () => {
5155
5252
  this.currentAdaptationSetCriteria_ =
5156
5253
  new shaka.media.PreferenceBasedCriteria(
5157
5254
  language,
5158
5255
  role || '',
5159
- channelsCount,
5256
+ channelsCount || 0,
5160
5257
  /* hdrLevel= */ '',
5161
5258
  /* spatialAudio= */ false,
5162
5259
  /* videoLayout= */ '',
5163
5260
  /* audioLabel= */ '',
5164
5261
  /* videoLabel= */ '',
5165
5262
  this.config_.mediaSource.codecSwitchingStrategy,
5166
- this.config_.manifest.dash.enableAudioGroups,
5167
- codec);
5263
+ codec || '');
5168
5264
 
5169
5265
  const diff = (a, b) => {
5170
5266
  if (!a.video && !b.video) {
@@ -5198,19 +5294,32 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5198
5294
  }
5199
5295
  if (bestVariant) {
5200
5296
  const track = shaka.util.StreamUtils.variantToTrack(bestVariant);
5201
- this.selectVariantTrack(track, /* clearBuffer= */ true, safeMargin);
5297
+ this.selectVariantTrack(
5298
+ track, /* clearBuffer= */ true, safeMargin || 0);
5202
5299
  return;
5203
5300
  }
5204
5301
 
5205
5302
  // If we haven't switched yet, just use ABR to find a new track.
5206
5303
  this.chooseVariantAndSwitch_();
5207
- } else if (this.video_ && this.video_.audioTracks) {
5208
- const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
5209
- this.getVariantTracks(), language, role || '', false)[0];
5210
- if (track) {
5211
- this.selectVariantTrack(track);
5304
+ };
5305
+ const selectSrcEqualsMode = () => {
5306
+ if (this.video_ && this.video_.audioTracks) {
5307
+ const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
5308
+ this.getVariantTracks(), language, role || '', false)[0];
5309
+ if (track) {
5310
+ this.selectVariantTrack(track);
5311
+ }
5312
+ }
5313
+ };
5314
+ if (this.manifest_ && this.playhead_) {
5315
+ selectMediaSourceMode();
5316
+ // When using MSE + remote we need to set tracks for both MSE and native
5317
+ // apis so that synchronization is maintained.
5318
+ if (!this.isRemotePlayback()) {
5319
+ return;
5212
5320
  }
5213
5321
  }
5322
+ selectSrcEqualsMode();
5214
5323
  }
5215
5324
 
5216
5325
  /**
@@ -5224,10 +5333,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5224
5333
  * @export
5225
5334
  */
5226
5335
  selectTextLanguage(language, role, forced = false) {
5227
- if (this.manifest_ && this.playhead_ && !this.isRemotePlayback()) {
5336
+ const selectMediaSourceMode = () => {
5228
5337
  this.currentTextLanguage_ = language;
5229
5338
  this.currentTextRole_ = role || '';
5230
- this.currentTextForced_ = forced;
5339
+ this.currentTextForced_ = forced || false;
5231
5340
 
5232
5341
  const chosenText = this.chooseTextStream_();
5233
5342
  if (chosenText) {
@@ -5241,15 +5350,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5241
5350
  if (this.shouldStreamText_()) {
5242
5351
  this.streamingEngine_.switchTextStream(chosenText);
5243
5352
  this.onTextChanged_();
5353
+ this.setTextDisplayerLanguage_();
5244
5354
  }
5245
5355
  }
5246
- } else {
5356
+ };
5357
+ const selectSrcEqualsMode = () => {
5247
5358
  const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
5248
- this.getTextTracks(), language, role || '', forced)[0];
5359
+ this.getTextTracks(), language, role || '', forced || false)[0];
5249
5360
  if (track) {
5250
5361
  this.selectTextTrack(track);
5251
5362
  }
5363
+ };
5364
+ if (this.manifest_ && this.playhead_) {
5365
+ selectMediaSourceMode();
5366
+ // When using MSE + remote we need to set tracks for both MSE and native
5367
+ // apis so that synchronization is maintained.
5368
+ if (!this.isRemotePlayback()) {
5369
+ return;
5370
+ }
5252
5371
  }
5372
+ selectSrcEqualsMode();
5253
5373
  }
5254
5374
 
5255
5375
  /**
@@ -5267,7 +5387,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5267
5387
  * @export
5268
5388
  */
5269
5389
  selectVariantsByLabel(label, clearBuffer = true, safeMargin = 0) {
5270
- if (this.manifest_ && this.playhead_ && !this.isRemotePlayback()) {
5390
+ const selectMediaSourceMode = () => {
5271
5391
  let firstVariantWithLabel = null;
5272
5392
  for (const variant of this.manifest_.variants) {
5273
5393
  if (variant.audio.label == label) {
@@ -5297,24 +5417,35 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5297
5417
  label,
5298
5418
  /* videoLabel= */ '',
5299
5419
  this.config_.mediaSource.codecSwitchingStrategy,
5300
- this.config_.manifest.dash.enableAudioGroups,
5301
5420
  /* audioCodec= */ '');
5302
5421
 
5303
5422
  this.chooseVariantAndSwitch_(clearBuffer, safeMargin);
5304
- } else if (this.video_ && this.video_.audioTracks) {
5305
- const audioTracks = Array.from(this.video_.audioTracks);
5423
+ };
5424
+ const selectSrcEqualsMode = () => {
5425
+ if (this.video_ && this.video_.audioTracks) {
5426
+ const audioTracks = Array.from(this.video_.audioTracks);
5306
5427
 
5307
- let trackMatch = null;
5428
+ let trackMatch = null;
5308
5429
 
5309
- for (const audioTrack of audioTracks) {
5310
- if (audioTrack.label == label) {
5311
- trackMatch = audioTrack;
5430
+ for (const audioTrack of audioTracks) {
5431
+ if (audioTrack.label == label) {
5432
+ trackMatch = audioTrack;
5433
+ }
5434
+ }
5435
+ if (trackMatch) {
5436
+ this.switchHtml5Track_(trackMatch);
5312
5437
  }
5313
5438
  }
5314
- if (trackMatch) {
5315
- this.switchHtml5Track_(trackMatch);
5439
+ };
5440
+ if (this.manifest_ && this.playhead_) {
5441
+ selectMediaSourceMode();
5442
+ // When using MSE + remote we need to set tracks for both MSE and native
5443
+ // apis so that synchronization is maintained.
5444
+ if (!this.isRemotePlayback()) {
5445
+ return;
5316
5446
  }
5317
5447
  }
5448
+ selectSrcEqualsMode();
5318
5449
  }
5319
5450
 
5320
5451
  /**
@@ -5345,7 +5476,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5345
5476
  * @export
5346
5477
  */
5347
5478
  getChaptersTracks() {
5348
- if (this.video_ && this.video_.src && this.video_.textTracks) {
5479
+ if (this.video_ && this.video_.currentSrc && this.video_.textTracks) {
5349
5480
  const textTracks = this.getChaptersTracks_();
5350
5481
  const StreamUtils = shaka.util.StreamUtils;
5351
5482
  return textTracks.map((text) => StreamUtils.html5TextTrackToTrack(text));
@@ -5362,7 +5493,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5362
5493
  * @export
5363
5494
  */
5364
5495
  getChapters(language) {
5365
- if (!this.video_ || !this.video_.src || !this.video_.textTracks) {
5496
+ if (!this.video_ || !this.video_.currentSrc || !this.video_.textTracks) {
5366
5497
  return [];
5367
5498
  }
5368
5499
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -5484,6 +5615,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5484
5615
  if (streams.length > 0) {
5485
5616
  this.streamingEngine_.switchTextStream(streams[0]);
5486
5617
  this.onTextChanged_();
5618
+ this.setTextDisplayerLanguage_();
5487
5619
  }
5488
5620
  }
5489
5621
  } else {
@@ -5857,6 +5989,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5857
5989
  fastSwitching: false,
5858
5990
  fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
5859
5991
  mimeType || '', codec || '')]),
5992
+ isAudioMuxedInVideo: false,
5860
5993
  };
5861
5994
 
5862
5995
  const fullMimeType = shaka.util.MimeUtils.getFullType(
@@ -6022,6 +6155,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6022
6155
  fastSwitching: false,
6023
6156
  fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
6024
6157
  segmentMimeType || '', '')]),
6158
+ isAudioMuxedInVideo: false,
6025
6159
  };
6026
6160
 
6027
6161
  if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
@@ -6507,6 +6641,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6507
6641
  fastSwitching: false,
6508
6642
  fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
6509
6643
  mimeType, '')]),
6644
+ isAudioMuxedInVideo: false,
6510
6645
  };
6511
6646
  manifest.textStreams.push(textStream);
6512
6647
  closedCaptionsSet.add(id);
@@ -7415,6 +7550,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7415
7550
  shaka.util.FakeEvent.EventName.AbrStatusChanged, data));
7416
7551
  }
7417
7552
 
7553
+ /**
7554
+ * @private
7555
+ */
7556
+ setTextDisplayerLanguage_() {
7557
+ const activeTextTrack = this.getTextTracks().find((t) => t.active);
7558
+ if (activeTextTrack &&
7559
+ this.textDisplayer_ && this.textDisplayer_.setTextLanguage) {
7560
+ this.textDisplayer_.setTextLanguage(activeTextTrack.language);
7561
+ }
7562
+ }
7563
+
7418
7564
  /**
7419
7565
  * @param {boolean} updateAbrManager
7420
7566
  * @private
@@ -8123,7 +8269,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8123
8269
  * @export
8124
8270
  */
8125
8271
  // eslint-disable-next-line no-useless-concat, max-len
8126
- shaka.Player.version = 'v4.11.13' + '-uncompiled'; // x-release-please-version
8272
+ shaka.Player.version = 'v4.12.1' + '-uncompiled'; // x-release-please-version
8127
8273
 
8128
8274
  // Initialize the deprecation system using the version string we just set
8129
8275
  // on the player.
@@ -189,6 +189,13 @@ shaka.text.SimpleTextDisplayer = class {
189
189
  this.textTrack_.mode = on ? 'showing' : 'hidden';
190
190
  }
191
191
 
192
+ /**
193
+ * @override
194
+ * @export
195
+ */
196
+ setTextLanguage(language) {
197
+ }
198
+
192
199
  /**
193
200
  * Iterate over all the cues in a text track and remove all those for which
194
201
  * |predicate(cue)| returns true.
@@ -55,4 +55,11 @@ shaka.text.StubTextDisplayer = class {
55
55
  */
56
56
  setTextVisibility(on) {
57
57
  }
58
+
59
+ /**
60
+ * @override
61
+ * @export
62
+ */
63
+ setTextLanguage(language) {
64
+ }
58
65
  };
@@ -258,6 +258,18 @@ shaka.text.UITextDisplayer = class {
258
258
  this.updateCaptions_(/* forceUpdate= */ true);
259
259
  }
260
260
 
261
+ /**
262
+ * @override
263
+ * @export
264
+ */
265
+ setTextLanguage(language) {
266
+ if (language && language != 'und') {
267
+ this.textContainer_.setAttribute('lang', language);
268
+ } else {
269
+ this.textContainer_.setAttribute('lang', '');
270
+ }
271
+ }
272
+
261
273
  /**
262
274
  * @private
263
275
  */
@@ -42,6 +42,20 @@ shaka.util.Dom = class {
42
42
  }
43
43
 
44
44
 
45
+ /**
46
+ * @param {string} url
47
+ * @param {string=} mimeType
48
+ * @return {!HTMLSourceElement}
49
+ */
50
+ static createSourceElement(url, mimeType = '') {
51
+ const source =
52
+ /** @type {HTMLSourceElement} */ (document.createElement('source'));
53
+ source.src = url;
54
+ source.type = mimeType;
55
+ return source;
56
+ }
57
+
58
+
45
59
  /**
46
60
  * Cast a Node/Element to an HTMLElement
47
61
  *
package/lib/util/error.js CHANGED
@@ -784,6 +784,11 @@ shaka.util.Error.Code = {
784
784
  */
785
785
  'HLS_EMPTY_MEDIA_PLAYLIST': 4053,
786
786
 
787
+ /**
788
+ * We do not support playing encrypted content with MSE
789
+ * and legacy Apple MediaKeys API.
790
+ */
791
+ 'DASH_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED': 4054,
787
792
 
788
793
  // RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
789
794
  // RETIRED: 'INVALID_SEGMENT_INDEX': 5001,