shaka-player 4.7.13 → 4.7.15

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 (59) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +2 -0
  3. package/dist/cast-boot.js +37 -0
  4. package/dist/deps.js +5 -4
  5. package/dist/locales.js +31 -31
  6. package/dist/shaka-player.compiled.d.ts +37 -14
  7. package/dist/shaka-player.compiled.debug.d.ts +37 -14
  8. package/dist/shaka-player.compiled.debug.externs.js +14 -0
  9. package/dist/shaka-player.compiled.debug.js +360 -350
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +14 -0
  12. package/dist/shaka-player.compiled.js +1107 -1105
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +37 -14
  15. package/dist/shaka-player.ui.debug.d.ts +37 -14
  16. package/dist/shaka-player.ui.debug.externs.js +14 -0
  17. package/dist/shaka-player.ui.debug.js +280 -269
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +14 -0
  20. package/dist/shaka-player.ui.js +1267 -1265
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/cast-namespace.js +14 -0
  23. package/externs/{chromecast.js → cast-sdk-v2.js} +0 -4
  24. package/externs/cast-sdk-v3.js +35 -0
  25. package/externs/ima.js +2 -0
  26. package/externs/mediatailor.js +0 -0
  27. package/externs/{cast.js → remote-playback.js} +2 -2
  28. package/externs/shaka/player.js +5 -2
  29. package/lib/ads/client_side_ad_manager.js +9 -5
  30. package/lib/cea/cea608_data_channel.js +9 -0
  31. package/lib/cea/cea_decoder.js +9 -2
  32. package/lib/dash/segment_template.js +30 -2
  33. package/lib/hls/hls_parser.js +19 -10
  34. package/lib/media/closed_caption_parser.js +16 -4
  35. package/lib/media/drm_engine.js +17 -3
  36. package/lib/media/gap_jumping_controller.js +9 -1
  37. package/lib/media/media_source_engine.js +20 -13
  38. package/lib/media/play_rate_controller.js +36 -8
  39. package/lib/media/playhead.js +3 -3
  40. package/lib/media/segment_index.js +10 -0
  41. package/lib/media/segment_reference.js +12 -0
  42. package/lib/media/stall_detector.js +3 -0
  43. package/lib/media/streaming_engine.js +8 -14
  44. package/lib/net/http_fetch_plugin.js +3 -1
  45. package/lib/net/http_xhr_plugin.js +6 -1
  46. package/lib/player.js +111 -53
  47. package/lib/polyfill/media_capabilities.js +7 -5
  48. package/lib/polyfill/patchedmediakeys_cert.js +62 -0
  49. package/lib/text/ui_text_displayer.js +10 -1
  50. package/lib/transmuxer/ts_transmuxer.js +6 -24
  51. package/lib/util/object_utils.js +1 -2
  52. package/lib/util/platform.js +100 -11
  53. package/lib/util/stream_utils.js +31 -14
  54. package/package.json +7 -2
  55. package/support.html +2 -0
  56. package/ui/audio_language_selection.js +4 -0
  57. package/ui/range_element.js +8 -2
  58. package/ui/resolution_selection.js +5 -0
  59. package/ui/text_selection.js +4 -0
@@ -1733,9 +1733,16 @@ shaka.media.StreamingEngine = class {
1733
1733
  otherState = this.mediaStates_.get(ContentType.VIDEO);
1734
1734
  }
1735
1735
  if (otherState) {
1736
+ // First, abort all operations in progress on the other stream.
1737
+ await this.abortOperations_(otherState).catch(() => {});
1738
+ // Then clear our cache of the last init segment, since MSE will be
1739
+ // reloaded and no init segment will be there post-reload.
1736
1740
  otherState.lastInitSegmentReference = null;
1741
+ // Now force the existing buffer to be cleared. It is not necessary
1742
+ // to perform the MSE clear operation, but this has the side-effect
1743
+ // that our state for that stream will then match MSE's post-reload
1744
+ // state.
1737
1745
  this.forceClearBuffer_(otherState);
1738
- this.abortOperations_(otherState).catch(() => {});
1739
1746
  }
1740
1747
  }
1741
1748
 
@@ -1766,19 +1773,6 @@ shaka.media.StreamingEngine = class {
1766
1773
  appendWindowEnd, ignoreTimestampOffset,
1767
1774
  reference.mimeType || mediaState.stream.mimeType,
1768
1775
  reference.codecs || mediaState.stream.codecs, streamsByType);
1769
- if (isResetMediaSourceNecessary) {
1770
- let otherState = null;
1771
- if (mediaState.type === ContentType.VIDEO) {
1772
- otherState = this.mediaStates_.get(ContentType.AUDIO);
1773
- } else if (mediaState.type === ContentType.AUDIO) {
1774
- otherState = this.mediaStates_.get(ContentType.VIDEO);
1775
- }
1776
- if (otherState) {
1777
- otherState.waitingToClearBuffer = false;
1778
- otherState.performingUpdate = false;
1779
- this.forceClearBuffer_(otherState);
1780
- }
1781
- }
1782
1776
  } catch (error) {
1783
1777
  mediaState.lastAppendWindowStart = null;
1784
1778
  mediaState.lastAppendWindowEnd = null;
@@ -167,8 +167,10 @@ shaka.net.HttpFetchPlugin = class {
167
167
  // is long enough, or if a whole segment is downloaded, call
168
168
  // progressUpdated().
169
169
  if (currentTime - lastTime > 100 || readObj.done) {
170
+ const numBytesRemaining =
171
+ readObj.done ? 0 : contentLength - loaded;
170
172
  progressUpdated(currentTime - lastTime, loaded - lastLoaded,
171
- contentLength - loaded);
173
+ numBytesRemaining);
172
174
  lastLoaded = loaded;
173
175
  lastTime = currentTime;
174
176
  }
@@ -67,6 +67,9 @@ shaka.net.HttpXHRPlugin = class {
67
67
  const xhrResponse = xhr.response;
68
68
 
69
69
  try {
70
+ const currentTime = Date.now();
71
+ progressUpdated(currentTime - lastTime, event.loaded - lastLoaded,
72
+ /* numBytesRemaining= */ 0);
70
73
  const response = shaka.net.HttpPluginUtils.makeResponse(headers,
71
74
  xhrResponse, xhr.status, uri, xhr.responseURL, requestType);
72
75
  resolve(response);
@@ -97,8 +100,10 @@ shaka.net.HttpXHRPlugin = class {
97
100
  // progressUpdated().
98
101
  if (currentTime - lastTime > 100 ||
99
102
  (event.lengthComputable && event.loaded == event.total)) {
103
+ const numBytesRemaining =
104
+ xhr.readyState == 4 ? 0 : event.total - event.loaded;
100
105
  progressUpdated(currentTime - lastTime, event.loaded - lastLoaded,
101
- event.total - event.loaded);
106
+ numBytesRemaining);
102
107
  lastLoaded = event.loaded;
103
108
  lastTime = currentTime;
104
109
  }
package/lib/player.js CHANGED
@@ -596,6 +596,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
596
596
  */
597
597
  this.playRateController_ = null;
598
598
 
599
+ // We use the buffering observer and timer to track when we move from having
600
+ // enough buffered content to not enough. They only exist when content has
601
+ // been loaded and are not re-used between loads.
602
+ /** @private {shaka.util.Timer} */
603
+ this.bufferPoller_ = null;
604
+
599
605
  /** @private {shaka.media.BufferingObserver} */
600
606
  this.bufferObserver_ = null;
601
607
 
@@ -1191,6 +1197,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1191
1197
  this.playheadObservers_ = null;
1192
1198
  }
1193
1199
 
1200
+ if (this.bufferPoller_) {
1201
+ this.bufferPoller_.stop();
1202
+ this.bufferPoller_ = null;
1203
+ }
1204
+
1194
1205
  // Stop the parser early. Since it is at the start of the pipeline, it
1195
1206
  // should be start early to avoid is pushing new data downstream.
1196
1207
  if (this.parser_) {
@@ -1977,31 +1988,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1977
1988
  this.config_.playRangeStart,
1978
1989
  this.config_.playRangeEnd);
1979
1990
 
1980
- this.playhead_ = this.createPlayhead(this.startTime_);
1981
- this.playheadObservers_ =
1982
- this.createPlayheadObserversForMSE_(startTimeOfLoad);
1983
1991
 
1984
- // We need to start the buffer management code near the end because it
1985
- // will set the initial buffering state and that depends on other
1986
- // components being initialized.
1987
- const rebufferThreshold = Math.max(
1988
- this.manifest_.minBufferTime, this.config_.streaming.rebufferingGoal);
1989
- this.startBufferManagement_(mediaElement, rebufferThreshold);
1992
+ const setupPlayhead = (startTime) => {
1993
+ this.playhead_ = this.createPlayhead(startTime);
1994
+ this.playheadObservers_ =
1995
+ this.createPlayheadObserversForMSE_(startTime);
1996
+
1997
+ // We need to start the buffer management code near the end because it
1998
+ // will set the initial buffering state and that depends on other
1999
+ // components being initialized.
2000
+ const rebufferThreshold = Math.max(
2001
+ this.manifest_.minBufferTime,
2002
+ this.config_.streaming.rebufferingGoal);
2003
+ this.startBufferManagement_(mediaElement, rebufferThreshold);
2004
+ };
2005
+
2006
+ if (!this.config_.streaming.startAtSegmentBoundary) {
2007
+ setupPlayhead(this.startTime_);
2008
+ }
1990
2009
 
1991
2010
  // Now we can switch to the initial variant.
1992
2011
  if (!activeVariant) {
1993
2012
  goog.asserts.assert(initialVariant,
1994
2013
  'Must have choosen an initial variant!');
1995
2014
 
1996
- this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
1997
- /* clearBuffer= */ false, /* safeMargin= */ 0);
1998
-
1999
2015
  // Now that we have initial streams, we may adjust the start time to
2000
2016
  // align to a segment boundary.
2001
2017
  if (this.config_.streaming.startAtSegmentBoundary) {
2002
- this.playhead_.setStartTime(await this.adjustStartTime_(
2003
- initialVariant, this.playhead_.getTime()));
2018
+ const timeline = this.manifest_.presentationTimeline;
2019
+ let initialTime = this.startTime_ || this.video_.currentTime;
2020
+ const seekRangeStart = timeline.getSeekRangeStart();
2021
+ const seekRangeEnd = timeline.getSeekRangeEnd();
2022
+ if (initialTime < seekRangeStart) {
2023
+ initialTime = seekRangeStart;
2024
+ } else if (initialTime > seekRangeEnd) {
2025
+ initialTime = seekRangeEnd;
2026
+ }
2027
+ const startTime = await this.adjustStartTime_(
2028
+ initialVariant, initialTime);
2029
+ setupPlayhead(startTime);
2004
2030
  }
2031
+
2032
+ this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
2033
+ /* clearBuffer= */ false, /* safeMargin= */ 0);
2005
2034
  }
2006
2035
 
2007
2036
  this.playhead_.ready();
@@ -2065,7 +2094,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2065
2094
  this.manifest_.serviceDescription)) {
2066
2095
  const onTimeUpdate = () => this.onTimeUpdate_();
2067
2096
  this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2068
- } else if (!isLive) {
2097
+ }
2098
+ if (!isLive) {
2069
2099
  const onVideoProgress = () => this.onVideoProgress_();
2070
2100
  this.loadEventManager_.listen(
2071
2101
  mediaElement, 'timeupdate', onVideoProgress);
@@ -2147,42 +2177,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2147
2177
  disabledUntilTime: 0,
2148
2178
  primary: false,
2149
2179
  audio: null,
2150
- video: {
2151
- id: 0,
2152
- originalId: null,
2153
- groupId: null,
2154
- createSegmentIndex: () => Promise.resolve(),
2155
- segmentIndex: null,
2156
- mimeType: mimeType ? shaka.util.MimeUtils.getBasicType(mimeType) : '',
2157
- codecs: mimeType ? shaka.util.MimeUtils.getCodecs(mimeType) : '',
2158
- encrypted: true,
2159
- drmInfos: [], // Filled in by DrmEngine config.
2160
- keyIds: new Set(),
2161
- language: 'und',
2162
- originalLanguage: null,
2163
- label: null,
2164
- type: ContentType.VIDEO,
2165
- primary: false,
2166
- trickModeVideo: null,
2167
- emsgSchemeIdUris: null,
2168
- roles: [],
2169
- forced: false,
2170
- channelsCount: null,
2171
- audioSamplingRate: null,
2172
- spatialAudio: false,
2173
- closedCaptions: null,
2174
- accessibilityPurpose: null,
2175
- external: false,
2176
- fastSwitching: false,
2177
- fullMimeTypes: new Set(),
2178
- },
2180
+ video: null,
2179
2181
  bandwidth: 100,
2180
2182
  allowedByApplication: true,
2181
2183
  allowedByKeySystem: true,
2182
2184
  decodingInfos: [],
2183
2185
  };
2184
- variant.video.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2185
- variant.video.mimeType, variant.video.codecs));
2186
+ const stream = {
2187
+ id: 0,
2188
+ originalId: null,
2189
+ groupId: null,
2190
+ createSegmentIndex: () => Promise.resolve(),
2191
+ segmentIndex: null,
2192
+ mimeType: mimeType ? shaka.util.MimeUtils.getBasicType(mimeType) : '',
2193
+ codecs: mimeType ? shaka.util.MimeUtils.getCodecs(mimeType) : '',
2194
+ encrypted: true,
2195
+ drmInfos: [], // Filled in by DrmEngine config.
2196
+ keyIds: new Set(),
2197
+ language: 'und',
2198
+ originalLanguage: null,
2199
+ label: null,
2200
+ type: ContentType.VIDEO,
2201
+ primary: false,
2202
+ trickModeVideo: null,
2203
+ emsgSchemeIdUris: null,
2204
+ roles: [],
2205
+ forced: false,
2206
+ channelsCount: null,
2207
+ audioSamplingRate: null,
2208
+ spatialAudio: false,
2209
+ closedCaptions: null,
2210
+ accessibilityPurpose: null,
2211
+ external: false,
2212
+ fastSwitching: false,
2213
+ fullMimeTypes: new Set(),
2214
+ };
2215
+ stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2216
+ stream.mimeType, stream.codecs));
2217
+ if (mimeType.startsWith('audio/')) {
2218
+ stream.type = ContentType.AUDIO;
2219
+ variant.audio = stream;
2220
+ } else {
2221
+ variant.video = stream;
2222
+ }
2186
2223
 
2187
2224
  this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
2188
2225
  await this.drmEngine_.initForPlayback(
@@ -2316,6 +2353,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2316
2353
  mediaElement.load();
2317
2354
  }
2318
2355
 
2356
+ // In Safari using HLS won't load anything unless you call load()
2357
+ // explicitly, no matter the value of the preload attribute.
2358
+ // Note: this only happens when there are not autoplay.
2359
+ if (mediaElement.preload != 'none' && !mediaElement.autoplay &&
2360
+ shaka.util.MimeUtils.isHlsType(mimeType) &&
2361
+ shaka.util.Platform.safariVersion()) {
2362
+ mediaElement.load();
2363
+ }
2364
+
2319
2365
  // Set the load mode last so that we know that all our components are
2320
2366
  // initialized.
2321
2367
  this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS;
@@ -2399,7 +2445,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2399
2445
  if (isLive && this.config_.streaming.liveSync) {
2400
2446
  const onTimeUpdate = () => this.onTimeUpdate_();
2401
2447
  this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2402
- } else if (!isLive) {
2448
+ }
2449
+ if (!isLive) {
2403
2450
  const onVideoProgress = () => this.onVideoProgress_();
2404
2451
  this.loadEventManager_.listen(
2405
2452
  mediaElement, 'timeupdate', onVideoProgress);
@@ -2717,16 +2764,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2717
2764
  * Create the observers for MSE playback. These observers are responsible for
2718
2765
  * notifying the app and player of specific events during MSE playback.
2719
2766
  *
2720
- * @param {number} startTimeOfLoad
2767
+ * @param {number} startTime
2721
2768
  * @return {!shaka.media.PlayheadObserverManager}
2722
2769
  * @private
2723
2770
  */
2724
- createPlayheadObserversForMSE_(startTimeOfLoad) {
2771
+ createPlayheadObserversForMSE_(startTime) {
2725
2772
  goog.asserts.assert(this.manifest_, 'Must have manifest');
2726
2773
  goog.asserts.assert(this.regionTimeline_, 'Must have region timeline');
2727
2774
  goog.asserts.assert(this.video_, 'Must have video element');
2728
2775
 
2729
- const startsPastZero = this.isLive() || startTimeOfLoad > 0;
2776
+ const startsPastZero = this.isLive() || startTime > 0;
2730
2777
 
2731
2778
  // Create the region observer. This will allow us to notify the app when we
2732
2779
  // move in and out of timeline regions.
@@ -2784,6 +2831,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2784
2831
  !this.bufferObserver_,
2785
2832
  'No buffering observer should exist before initialization.');
2786
2833
 
2834
+ goog.asserts.assert(
2835
+ !this.bufferPoller_,
2836
+ 'No buffer timer should exist before initialization.');
2837
+
2787
2838
  // Give dummy values, will be updated below.
2788
2839
  this.bufferObserver_ = new shaka.media.BufferingObserver(1, 2);
2789
2840
 
@@ -2793,6 +2844,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2793
2844
  this.updateBufferingSettings_(rebufferingGoal);
2794
2845
  this.updateBufferState_();
2795
2846
 
2847
+ this.bufferPoller_ = new shaka.util.Timer(() => {
2848
+ this.pollBufferState_();
2849
+ }).tickEvery(/* seconds= */ 0.25);
2796
2850
  this.loadEventManager_.listen(mediaElement, 'waiting',
2797
2851
  (e) => this.pollBufferState_());
2798
2852
  this.loadEventManager_.listen(mediaElement, 'stalled',
@@ -5556,6 +5610,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5556
5610
  const loaded = this.stats_ && this.bufferObserver_ && this.playhead_;
5557
5611
 
5558
5612
  if (loaded) {
5613
+ this.playRateController_.setBuffering(isBuffering);
5559
5614
  if (this.cmcdManager_) {
5560
5615
  this.cmcdManager_.setBuffering(isBuffering);
5561
5616
  }
@@ -6151,8 +6206,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6151
6206
  shaka.log.debug('switch_');
6152
6207
  goog.asserts.assert(this.config_.abr.enabled,
6153
6208
  'AbrManager should not call switch while disabled!');
6154
- goog.asserts.assert(this.manifest_, 'We need a manifest to switch ' +
6155
- 'variants.');
6209
+
6210
+ if (!this.manifest_) {
6211
+ // It could come from a preload manager operation.
6212
+ return;
6213
+ }
6156
6214
 
6157
6215
  if (!this.streamingEngine_) {
6158
6216
  // There's no way to change it.
@@ -6960,7 +7018,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6960
7018
  * @export
6961
7019
  */
6962
7020
  // eslint-disable-next-line no-useless-concat
6963
- shaka.Player.version = 'v4.7.13' + '-uncompiled'; // x-release-please-version
7021
+ shaka.Player.version = 'v4.7.15' + '-uncompiled'; // x-release-please-version
6964
7022
 
6965
7023
  // Initialize the deprecation system using the version string we just set
6966
7024
  // on the player.
@@ -25,10 +25,8 @@ shaka.polyfill.MediaCapabilities = class {
25
25
  * @export
26
26
  */
27
27
  static install() {
28
- // Since MediaCapabilities implementation is buggy on the Chromecast
29
- // platform (see https://github.com/shaka-project/shaka-player/issues/4569),
30
- // we should always install polyfills on all Chromecast models.
31
- // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
28
+ // We can enable MediaCapabilities in Android and Fuchsia devices, but not
29
+ // in Linux devices because the implementation is buggy.
32
30
  // Since MediaCapabilities implementation is buggy in Apple browsers, we
33
31
  // should always install polyfill for Apple browsers.
34
32
  // See: https://github.com/shaka-project/shaka-player/issues/3530
@@ -50,12 +48,16 @@ shaka.polyfill.MediaCapabilities = class {
50
48
  // Since MediaCapabilities implementation is buggy in Hisense browsers, we
51
49
  // should always install polyfill for Hisense browsers.
52
50
  let canUseNativeMCap = true;
51
+ if (shaka.util.Platform.isChromecast() &&
52
+ !shaka.util.Platform.isAndroidCastDevice() &&
53
+ !shaka.util.Platform.isFuchsiaCastDevice()) {
54
+ canUseNativeMCap = false;
55
+ }
53
56
  if (shaka.util.Platform.isApple() ||
54
57
  shaka.util.Platform.isPS5() ||
55
58
  shaka.util.Platform.isPS4() ||
56
59
  shaka.util.Platform.isWebOS() ||
57
60
  shaka.util.Platform.isTizen() ||
58
- shaka.util.Platform.isChromecast() ||
59
61
  shaka.util.Platform.isEOS() ||
60
62
  shaka.util.Platform.isHisense()) {
61
63
  canUseNativeMCap = false;
@@ -0,0 +1,62 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.polyfill.PatchedMediaKeysCert');
8
+
9
+ goog.require('shaka.log');
10
+ goog.require('shaka.polyfill');
11
+ goog.require('shaka.util.Platform');
12
+
13
+
14
+ /**
15
+ * @summary A polyfill to fix setServerCertificate implementation on
16
+ * older platforms which claim to support modern EME.
17
+ * @export
18
+ */
19
+ shaka.polyfill.PatchedMediaKeysCert = class {
20
+ /**
21
+ * Installs the polyfill if needed.
22
+ * @export
23
+ */
24
+ static install() {
25
+ if (!window.MediaKeys) {
26
+ // No MediaKeys available
27
+ return;
28
+ }
29
+ // eslint-disable-next-line no-restricted-syntax
30
+ if (MediaKeys.prototype.setServerCertificate &&
31
+ !shaka.polyfill.PatchedMediaKeysCert.hasInvalidImplementation_()) {
32
+ // setServerCertificate is there and userAgent seems to be valid.
33
+ return;
34
+ }
35
+
36
+ shaka.log.info('Patching MediaKeys.setServerCertificate');
37
+ // eslint-disable-next-line no-restricted-syntax
38
+ MediaKeys.prototype.setServerCertificate =
39
+ shaka.polyfill.PatchedMediaKeysCert.setServerCertificate_;
40
+ }
41
+
42
+ /**
43
+ * @param {!BufferSource} certificate
44
+ * @return {!Promise<boolean>}
45
+ * @private
46
+ */
47
+ static setServerCertificate_(certificate) {
48
+ shaka.log.debug('PatchedMediaKeysCert.setServerCertificate');
49
+ return Promise.resolve(false);
50
+ }
51
+
52
+ /**
53
+ * @return {boolean}
54
+ * @private
55
+ */
56
+ static hasInvalidImplementation_() {
57
+ return shaka.util.Platform.isTizen3() || shaka.util.Platform.isTizen4() ||
58
+ shaka.util.Platform.isTizen5_0() || shaka.util.Platform.isWebOS3();
59
+ }
60
+ };
61
+
62
+ shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysCert.install);
@@ -72,7 +72,9 @@ shaka.text.UITextDisplayer = class {
72
72
 
73
73
  /** @private {shaka.util.Timer} */
74
74
  this.captionsTimer_ = new shaka.util.Timer(() => {
75
- this.updateCaptions_();
75
+ if (!this.video_.paused) {
76
+ this.updateCaptions_();
77
+ }
76
78
  }).tickEvery(updatePeriod);
77
79
 
78
80
  /**
@@ -93,6 +95,10 @@ shaka.text.UITextDisplayer = class {
93
95
  this.updateCaptions_(/* forceUpdate= */ true);
94
96
  });
95
97
 
98
+ this.eventManager_.listen(this.video_, 'seeking', () => {
99
+ this.updateCaptions_(/* forceUpdate= */ true);
100
+ });
101
+
96
102
  // From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
97
103
  // Whenever the natural width or natural height of the video changes
98
104
  // (including, for example, because the selected video track was changed),
@@ -339,6 +345,9 @@ shaka.text.UITextDisplayer = class {
339
345
  const cueRegistry = this.currentCuesMap_.get(cue);
340
346
  goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
341
347
  if (cueRegistry.regionElement) {
348
+ if (cueRegistry.regionElement.contains(container)) {
349
+ cueRegistry.regionElement.removeChild(container);
350
+ }
342
351
  container.appendChild(cueRegistry.regionElement);
343
352
  cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
344
353
  } else {
@@ -21,6 +21,7 @@ goog.require('shaka.util.Id3Utils');
21
21
  goog.require('shaka.util.ManifestParserUtils');
22
22
  goog.require('shaka.util.MimeUtils');
23
23
  goog.require('shaka.util.Mp4Generator');
24
+ goog.require('shaka.util.StreamUtils');
24
25
  goog.require('shaka.util.TsParser');
25
26
  goog.require('shaka.util.Uint8ArrayUtils');
26
27
 
@@ -142,13 +143,17 @@ shaka.transmuxer.TsTransmuxer = class {
142
143
  convertCodecs(contentType, mimeType) {
143
144
  if (this.isTsContainer_(mimeType)) {
144
145
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
146
+ const StreamUtils = shaka.util.StreamUtils;
145
147
  // The replace it's necessary because Firefox(the only browser that
146
148
  // supports MP3 in MP4) only support the MP3 codec with the mp3 string.
147
149
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.34"') -> false
148
150
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') -> true
149
151
  const codecs = shaka.util.MimeUtils.getCodecs(mimeType)
150
152
  .replace('mp4a.40.34', 'mp3').split(',')
151
- .map(this.getCorrectVideoCodecs_).join(',');
153
+ .map((codecs) => {
154
+ return StreamUtils.getCorrectAudioCodecs(codecs, 'audio/mp4');
155
+ })
156
+ .map(StreamUtils.getCorrectVideoCodecs).join(',');
152
157
  if (contentType == ContentType.AUDIO) {
153
158
  return `audio/mp4; codecs="${codecs}"`;
154
159
  }
@@ -158,29 +163,6 @@ shaka.transmuxer.TsTransmuxer = class {
158
163
  }
159
164
 
160
165
 
161
- /**
162
- * @param {string} codec
163
- * @return {string}
164
- * @private
165
- */
166
- getCorrectVideoCodecs_(codec) {
167
- if (codec.includes('avc1')) {
168
- // Convert avc1 codec string from RFC-4281 to RFC-6381 for
169
- // MediaSource.isTypeSupported
170
- // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
171
- const avcdata = codec.split('.');
172
- if (avcdata.length == 3) {
173
- let result = avcdata.shift() + '.';
174
- result += parseInt(avcdata.shift(), 10).toString(16);
175
- result +=
176
- ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
177
- return result;
178
- }
179
- }
180
- return codec;
181
- }
182
-
183
-
184
166
  /**
185
167
  * @override
186
168
  * @export
@@ -41,8 +41,7 @@ shaka.util.ObjectUtils = class {
41
41
 
42
42
  // This covers Uint8Array and friends, even without a TypedArray
43
43
  // base-class constructor.
44
- const isTypedArray =
45
- val.buffer && val.buffer.constructor == ArrayBuffer;
44
+ const isTypedArray = val.buffer instanceof ArrayBuffer;
46
45
  if (isTypedArray) {
47
46
  return val;
48
47
  }