shaka-player 4.12.3 → 4.12.5

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.
@@ -144,7 +144,7 @@ shaka.ads.InterstitialAd = class {
144
144
  * @export
145
145
  */
146
146
  canSkipNow() {
147
- return this.getTimeUntilSkippable() == 0;
147
+ return this.isSkippable_ && this.getTimeUntilSkippable() == 0;
148
148
  }
149
149
 
150
150
  /**
@@ -152,7 +152,9 @@ shaka.ads.InterstitialAd = class {
152
152
  * @export
153
153
  */
154
154
  skip() {
155
- this.onSkip_();
155
+ if (this.canSkipNow()) {
156
+ this.onSkip_();
157
+ }
156
158
  }
157
159
 
158
160
  /**
@@ -704,7 +704,6 @@ shaka.ads.InterstitialAdManager = class {
704
704
  this.onEvent_(new shaka.util.FakeEvent(
705
705
  shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
706
706
  }
707
- const eventsSent = new Set();
708
707
  this.adEventManager_.listenOnce(this.player_, 'error', error);
709
708
  this.adEventManager_.listen(this.video_, 'timeupdate', () => {
710
709
  const duration = this.video_.duration;
@@ -718,23 +717,21 @@ shaka.ads.InterstitialAdManager = class {
718
717
  new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
719
718
  }
720
719
  prevCanSkipNow = currentCanSkipNow;
721
- const currentPercent = 100 * this.video_.currentTime / duration;
722
- if (currentPercent >= 25 && !eventsSent.has('firstquartile')) {
723
- updateBaseVideoTime();
724
- this.onEvent_(
725
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
726
- eventsSent.add('firstquartile');
727
- } else if (currentPercent >= 50 && !eventsSent.has('midpoint')) {
728
- updateBaseVideoTime();
729
- this.onEvent_(
730
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
731
- eventsSent.add('midpoint');
732
- } else if (currentPercent >= 75 && !eventsSent.has('thirdquartile')) {
733
- updateBaseVideoTime();
734
- this.onEvent_(
735
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
736
- eventsSent.add('thirdquartile');
737
- }
720
+ });
721
+ this.adEventManager_.listenOnce(this.player_, 'firstquartile', () => {
722
+ updateBaseVideoTime();
723
+ this.onEvent_(
724
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
725
+ });
726
+ this.adEventManager_.listenOnce(this.player_, 'midpoint', () => {
727
+ updateBaseVideoTime();
728
+ this.onEvent_(
729
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
730
+ });
731
+ this.adEventManager_.listenOnce(this.player_, 'thirdquartile', () => {
732
+ updateBaseVideoTime();
733
+ this.onEvent_(
734
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
738
735
  });
739
736
  this.adEventManager_.listenOnce(this.player_, 'complete', complete);
740
737
  this.adEventManager_.listen(this.video_, 'play', () => {
@@ -1505,6 +1505,9 @@ shaka.dash.DashParser = class {
1505
1505
  let shouldDeleteIndex = true;
1506
1506
  for (const contextId of this.indexStreamMap_[pId]) {
1507
1507
  const stream = this.streamMap_[contextId];
1508
+ if (!stream) {
1509
+ continue;
1510
+ }
1508
1511
  if (stream.segmentIndex && !stream.segmentIndex.isEmpty()) {
1509
1512
  shouldDeleteIndex = false;
1510
1513
  continue;
@@ -9,12 +9,14 @@ goog.provide('shaka.media.DrmEngine');
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
11
  goog.require('shaka.net.NetworkingEngine');
12
+ goog.require('shaka.util.ArrayUtils');
12
13
  goog.require('shaka.util.BufferUtils');
13
14
  goog.require('shaka.util.Destroyer');
14
15
  goog.require('shaka.util.DrmUtils');
15
16
  goog.require('shaka.util.Error');
16
17
  goog.require('shaka.util.EventManager');
17
18
  goog.require('shaka.util.FakeEvent');
19
+ goog.require('shaka.util.Functional');
18
20
  goog.require('shaka.util.IDestroyable');
19
21
  goog.require('shaka.util.Iterables');
20
22
  goog.require('shaka.util.ManifestParserUtils');
@@ -66,12 +68,18 @@ shaka.media.DrmEngine = class {
66
68
  */
67
69
  this.activeSessions_ = new Map();
68
70
 
71
+ /** @private {!Array.<!shaka.net.NetworkingEngine.PendingRequest>} */
72
+ this.activeRequests_ = [];
73
+
69
74
  /**
70
75
  * @private {!Map<string,
71
76
  * {initData: ?Uint8Array, initDataType: ?string}>}
72
77
  */
73
78
  this.storedPersistentSessions_ = new Map();
74
79
 
80
+ /** @private {boolean} */
81
+ this.hasInitData_ = false;
82
+
75
83
  /** @private {!shaka.util.PublicPromise} */
76
84
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
77
85
 
@@ -705,6 +713,7 @@ shaka.media.DrmEngine = class {
705
713
 
706
714
  // Reset the promise for the next sessions to come if key needs aren't
707
715
  // satisfied with persistent sessions
716
+ this.hasInitData_ = false;
708
717
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
709
718
  this.allSessionsLoaded_.catch(() => {});
710
719
  }
@@ -766,11 +775,16 @@ shaka.media.DrmEngine = class {
766
775
  }
767
776
  }
768
777
 
778
+ // Mark that there is init data, so that the preloader will know to wait
779
+ // for sessions to be loaded.
780
+ this.hasInitData_ = true;
781
+
769
782
  // If there are pre-existing sessions that have all been loaded
770
783
  // then reset the allSessionsLoaded_ promise, which can now be
771
784
  // used to wait for new sesssions to be loaded
772
785
  if (this.activeSessions_.size > 0 && this.areAllSessionsLoaded_()) {
773
786
  this.allSessionsLoaded_.resolve();
787
+ this.hasInitData_ = false;
774
788
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
775
789
  this.allSessionsLoaded_.catch(() => {});
776
790
  }
@@ -914,14 +928,16 @@ shaka.media.DrmEngine = class {
914
928
  'Got MediaKeySystemAccess with configuration',
915
929
  realConfig);
916
930
 
931
+ const keySystem =
932
+ this.config_.keySystemsMapping[mediaKeySystemAccess.keySystem] ||
933
+ mediaKeySystemAccess.keySystem;
934
+
917
935
  if (variants.length) {
918
936
  this.currentDrmInfo_ = this.createDrmInfoByInfos_(
919
- mediaKeySystemAccess.keySystem,
920
- drmInfosByKeySystem.get(mediaKeySystemAccess.keySystem));
937
+ keySystem, drmInfosByKeySystem.get(keySystem));
921
938
  } else {
922
939
  this.currentDrmInfo_ = shaka.media.DrmEngine.createDrmInfoByConfigs_(
923
- mediaKeySystemAccess.keySystem,
924
- configsByKeySystem.get(mediaKeySystemAccess.keySystem));
940
+ keySystem, configsByKeySystem.get(keySystem));
925
941
  }
926
942
  if (!this.currentDrmInfo_.licenseServerUri) {
927
943
  throw new shaka.util.Error(
@@ -1036,8 +1052,12 @@ shaka.media.DrmEngine = class {
1036
1052
  if (!decodingInfo.supported || !decodingInfo.keySystemAccess) {
1037
1053
  continue;
1038
1054
  }
1039
- const drmInfos =
1040
- drmInfosByKeySystem.get(decodingInfo.keySystemAccess.keySystem);
1055
+ const originalKeySystem = decodingInfo.keySystemAccess.keySystem;
1056
+ let drmInfos = drmInfosByKeySystem.get(originalKeySystem);
1057
+ if (!drmInfos && this.config_.keySystemsMapping[originalKeySystem]) {
1058
+ drmInfos = drmInfosByKeySystem.get(
1059
+ this.config_.keySystemsMapping[originalKeySystem]);
1060
+ }
1041
1061
  for (const info of drmInfos) {
1042
1062
  if (!!info.licenseServerUri == shouldHaveLicenseServer) {
1043
1063
  return decodingInfo.keySystemAccess;
@@ -1380,6 +1400,14 @@ shaka.media.DrmEngine = class {
1380
1400
  this.video_.paused && !this.initialRequestsSent_);
1381
1401
  }
1382
1402
 
1403
+ /** @return {!Promise} */
1404
+ async waitForActiveRequests() {
1405
+ if (this.hasInitData_) {
1406
+ await this.allSessionsLoaded_;
1407
+ await Promise.all(this.activeRequests_.map((req) => req.promise));
1408
+ }
1409
+ }
1410
+
1383
1411
  /**
1384
1412
  * Sends a license request.
1385
1413
  * @param {!MediaKeyMessageEvent} event
@@ -1439,7 +1467,9 @@ shaka.media.DrmEngine = class {
1439
1467
  try {
1440
1468
  const req = this.playerInterface_.netEngine.request(
1441
1469
  requestType, request, {isPreload: this.isPreload_()});
1470
+ this.activeRequests_.push(req);
1442
1471
  response = await req.promise;
1472
+ shaka.util.ArrayUtils.remove(this.activeRequests_, req);
1443
1473
  } catch (error) {
1444
1474
  if (this.destroyer_.destroyed()) {
1445
1475
  return;
@@ -1859,9 +1889,16 @@ shaka.media.DrmEngine = class {
1859
1889
  offlineConfig.sessionTypes = ['persistent-license'];
1860
1890
 
1861
1891
  const configs = [offlineConfig, basicConfig];
1862
-
1863
- const access = await navigator.requestMediaKeySystemAccess(
1864
- keySystem, configs);
1892
+ // On some (Android) WebView environments,
1893
+ // requestMediaKeySystemAccess will
1894
+ // not resolve or reject, at least if RESOURCE_PROTECTED_MEDIA_ID
1895
+ // is not set. This is a workaround for that issue.
1896
+ const TIMEOUT_FOR_CHECKACCESS_IN_SECONDS = 1;
1897
+ const access =
1898
+ await shaka.util.Functional.promiseWithTimeout(
1899
+ TIMEOUT_FOR_CHECKACCESS_IN_SECONDS,
1900
+ navigator.requestMediaKeySystemAccess(keySystem, configs),
1901
+ );
1865
1902
  await processMediaKeySystemAccess(keySystem, access);
1866
1903
  } catch (error) {} // Ignore errors.
1867
1904
  };
@@ -1896,13 +1933,22 @@ shaka.media.DrmEngine = class {
1896
1933
  },
1897
1934
  },
1898
1935
  };
1899
-
1936
+ // On some (Android) WebView environments, decodingInfo will
1937
+ // not resolve or reject, at least if RESOURCE_PROTECTED_MEDIA_ID
1938
+ // is not set. This is a workaround for that issue.
1939
+ const TIMEOUT_FOR_DECODING_INFO_IN_SECONDS = 1;
1900
1940
  const decodingInfo =
1901
- await navigator.mediaCapabilities.decodingInfo(decodingConfig);
1941
+ await shaka.util.Functional.promiseWithTimeout(
1942
+ TIMEOUT_FOR_DECODING_INFO_IN_SECONDS,
1943
+ navigator.mediaCapabilities.decodingInfo(decodingConfig),
1944
+ );
1902
1945
 
1903
1946
  const access = decodingInfo.keySystemAccess;
1904
1947
  await processMediaKeySystemAccess(keySystem, access);
1905
- } catch (error) {} // Ignore errors.
1948
+ } catch (error) {
1949
+ // Ignore errors.
1950
+ shaka.log.v2('Failed to probe support for', keySystem, error);
1951
+ }
1906
1952
  };
1907
1953
 
1908
1954
  // Initialize the support structure for each key system.
@@ -1961,18 +2007,10 @@ shaka.media.DrmEngine = class {
1961
2007
  * @private
1962
2008
  */
1963
2009
  async closeSession_(session) {
1964
- const DrmEngine = shaka.media.DrmEngine;
1965
-
1966
- const timeout = new Promise((resolve, reject) => {
1967
- const timer = new shaka.util.Timer(reject);
1968
- timer.tickAfter(DrmEngine.CLOSE_TIMEOUT_);
1969
- });
1970
-
1971
2010
  try {
1972
- await Promise.race([
1973
- Promise.all([session.close(), session.closed]),
1974
- timeout,
1975
- ]);
2011
+ await shaka.util.Functional.promiseWithTimeout(
2012
+ shaka.media.DrmEngine.CLOSE_TIMEOUT_,
2013
+ Promise.all([session.close(), session.closed]));
1976
2014
  } catch (e) {
1977
2015
  shaka.log.warning('Timeout waiting for session close');
1978
2016
  }
@@ -50,7 +50,8 @@ shaka.media.ManifestFilterer = class {
50
50
  async filterManifest(manifest) {
51
51
  goog.asserts.assert(manifest, 'Manifest should exist!');
52
52
  await shaka.util.StreamUtils.filterManifest(this.drmEngine_, manifest,
53
- this.config_.drm.preferredKeySystems);
53
+ this.config_.drm.preferredKeySystems,
54
+ this.config_.drm.keySystemsMapping);
54
55
  if (!this.config_.streaming.dontChooseCodecs) {
55
56
  shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
56
57
  manifest,
@@ -466,12 +466,12 @@ shaka.media.MediaSourceEngine = class {
466
466
  cleanup.push(this.textEngine_.destroy());
467
467
  }
468
468
 
469
+ await Promise.all(cleanup);
470
+
469
471
  for (const contentType in this.transmuxers_) {
470
- cleanup.push(this.transmuxers_[contentType].destroy());
472
+ this.transmuxers_[contentType].destroy();
471
473
  }
472
474
 
473
-
474
- await Promise.all(cleanup);
475
475
  if (this.eventManager_) {
476
476
  this.eventManager_.release();
477
477
  this.eventManager_ = null;
@@ -692,7 +692,8 @@ shaka.media.MediaSourceEngine = class {
692
692
  * @return {boolean}
693
693
  */
694
694
  isStreamingAllowed() {
695
- return this.streamingAllowed_ && !this.usingRemotePlayback_;
695
+ return this.streamingAllowed_ && !this.usingRemotePlayback_ &&
696
+ !this.reloadingMediaSource_;
696
697
  }
697
698
 
698
699
  /**
@@ -1859,11 +1860,13 @@ shaka.media.MediaSourceEngine = class {
1859
1860
 
1860
1861
  /** @type {!Array.<!shaka.util.PublicPromise>} */
1861
1862
  const allWaiters = [];
1863
+ /** @type {!Array.<!shaka.util.ManifestParserUtils.ContentType>} */
1864
+ const contentTypes = Object.keys(this.sourceBuffers_);
1862
1865
 
1863
1866
  // Enqueue a 'wait' operation onto each queue.
1864
1867
  // This operation signals its readiness when it starts.
1865
1868
  // When all wait operations are ready, the real operation takes place.
1866
- for (const contentType in this.sourceBuffers_) {
1869
+ for (const contentType of contentTypes) {
1867
1870
  const ready = new shaka.util.PublicPromise();
1868
1871
  const operation = {
1869
1872
  start: () => ready.resolve(),
@@ -1893,7 +1896,7 @@ shaka.media.MediaSourceEngine = class {
1893
1896
  // assert at the end of destroy passes. In compiled mode, the queues
1894
1897
  // are wiped in destroy.
1895
1898
  if (goog.DEBUG) {
1896
- for (const contentType in this.sourceBuffers_) {
1899
+ for (const contentType of contentTypes) {
1897
1900
  if (this.queues_[contentType].length) {
1898
1901
  goog.asserts.assert(
1899
1902
  this.queues_[contentType].length == 1,
@@ -1910,7 +1913,7 @@ shaka.media.MediaSourceEngine = class {
1910
1913
 
1911
1914
  if (goog.DEBUG) {
1912
1915
  // If we did it correctly, nothing is updating.
1913
- for (const contentType in this.sourceBuffers_) {
1916
+ for (const contentType of contentTypes) {
1914
1917
  goog.asserts.assert(
1915
1918
  this.sourceBuffers_[contentType].updating == false,
1916
1919
  'SourceBuffers should not be updating after a blocking op!');
@@ -1930,7 +1933,7 @@ shaka.media.MediaSourceEngine = class {
1930
1933
  null);
1931
1934
  } finally {
1932
1935
  // Unblock the queues.
1933
- for (const contentType in this.sourceBuffers_) {
1936
+ for (const contentType of contentTypes) {
1934
1937
  this.popFromQueue_(contentType);
1935
1938
  }
1936
1939
  }
@@ -1942,6 +1945,7 @@ shaka.media.MediaSourceEngine = class {
1942
1945
  * @private
1943
1946
  */
1944
1947
  popFromQueue_(contentType) {
1948
+ goog.asserts.assert(this.queues_[contentType], 'Queue should exist');
1945
1949
  // Remove the in-progress operation, which is now complete.
1946
1950
  this.queues_[contentType].shift();
1947
1951
  this.startOperation_(contentType);
@@ -2099,48 +2103,6 @@ shaka.media.MediaSourceEngine = class {
2099
2103
  null);
2100
2104
  }
2101
2105
 
2102
- /**
2103
- * Returns the source buffer parameters
2104
- * @param {shaka.util.ManifestParserUtils.ContentType} contentType
2105
- * @return {?shaka.media.MediaSourceEngine.SourceBufferParams}
2106
- * @private
2107
- */
2108
- getSourceBufferParams_(contentType) {
2109
- if (!this.sourceBuffers_[contentType]) {
2110
- return null;
2111
- }
2112
- return {
2113
- timestampOffset: this.sourceBuffers_[contentType].timestampOffset,
2114
- appendWindowStart: this.sourceBuffers_[contentType].appendWindowStart,
2115
- appendWindowEnd: this.sourceBuffers_[contentType].appendWindowEnd,
2116
- };
2117
- }
2118
-
2119
- /**
2120
- * Restore source buffer parameters
2121
- * @param {shaka.util.ManifestParserUtils.ContentType} contentType
2122
- * @param {?shaka.media.MediaSourceEngine.SourceBufferParams} params
2123
- * @private
2124
- */
2125
- restoreSourceBufferParams_(contentType, params) {
2126
- if (!params) {
2127
- return;
2128
- }
2129
-
2130
- if (!this.sourceBuffers_[contentType]) {
2131
- shaka.log.warning('Attempted to restore a non-existent source buffer');
2132
- return;
2133
- }
2134
-
2135
- this.sourceBuffers_[contentType].timestampOffset =
2136
- params.timestampOffset;
2137
- // `end` needs to be set before `start`
2138
- this.sourceBuffers_[contentType].appendWindowEnd =
2139
- params.appendWindowEnd;
2140
- this.sourceBuffers_[contentType].appendWindowStart =
2141
- params.appendWindowStart;
2142
- }
2143
-
2144
2106
  /**
2145
2107
  * Resets the MediaSource and re-adds source buffers due to codec mismatch
2146
2108
  *
@@ -2149,7 +2111,6 @@ shaka.media.MediaSourceEngine = class {
2149
2111
  * @private
2150
2112
  */
2151
2113
  async reset_(streamsByType) {
2152
- const Functional = shaka.util.Functional;
2153
2114
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
2154
2115
  this.reloadingMediaSource_ = true;
2155
2116
  this.needSplitMuxedContent_ = false;
@@ -2171,36 +2132,19 @@ shaka.media.MediaSourceEngine = class {
2171
2132
  try {
2172
2133
  this.eventManager_.removeAll();
2173
2134
 
2174
- const cleanup = [];
2175
2135
  for (const contentType in this.transmuxers_) {
2176
- cleanup.push(this.transmuxers_[contentType].destroy());
2177
- }
2178
- for (const contentType in this.queues_) {
2179
- // Make a local copy of the queue and the first item.
2180
- const q = this.queues_[contentType];
2181
- const inProgress = q[0];
2182
-
2183
- // Drop everything else out of the original queue.
2184
- this.queues_[contentType] = q.slice(0, 1);
2185
-
2186
- // We will wait for this item to complete/fail.
2187
- if (inProgress) {
2188
- cleanup.push(inProgress.p.catch(Functional.noop));
2189
- }
2190
-
2191
- // The rest will be rejected silently if possible.
2192
- for (const item of q.slice(1)) {
2193
- item.p.reject(shaka.util.Destroyer.destroyedError());
2194
- }
2136
+ this.transmuxers_[contentType].destroy();
2195
2137
  }
2196
2138
  for (const contentType in this.sourceBuffers_) {
2197
2139
  const sourceBuffer = this.sourceBuffers_[contentType];
2198
2140
  try {
2199
2141
  this.mediaSource_.removeSourceBuffer(sourceBuffer);
2200
- } catch (e) {}
2142
+ } catch (e) {
2143
+ shaka.log.debug('Exception on removeSourceBuffer', e);
2144
+ }
2201
2145
  }
2202
- await Promise.all(cleanup);
2203
2146
  this.transmuxers_ = {};
2147
+ this.sourceBuffers_ = {};
2204
2148
 
2205
2149
  const previousDuration = this.mediaSource_.duration;
2206
2150
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
@@ -2231,23 +2175,17 @@ shaka.media.MediaSourceEngine = class {
2231
2175
  onSourceBufferAdded);
2232
2176
 
2233
2177
  for (const contentType of streamsByType.keys()) {
2234
- const previousParams = this.getSourceBufferParams_(contentType);
2235
2178
  const stream = streamsByType.get(contentType);
2236
2179
  // eslint-disable-next-line no-await-in-loop
2237
2180
  await this.initSourceBuffer_(contentType, stream, stream.codecs);
2238
- if (this.needSplitMuxedContent_) {
2239
- this.queues_[ContentType.AUDIO] = [];
2240
- this.queues_[ContentType.VIDEO] = [];
2241
- } else {
2242
- this.queues_[contentType] = [];
2243
- }
2244
-
2245
- this.restoreSourceBufferParams_(contentType, previousParams);
2246
2181
  }
2247
2182
  const audio = streamsByType.get(ContentType.AUDIO);
2248
2183
  if (audio && audio.isAudioMuxedInVideo) {
2249
2184
  this.needSplitMuxedContent_ = true;
2250
2185
  }
2186
+ if (this.needSplitMuxedContent_ && !this.queues_[ContentType.AUDIO]) {
2187
+ this.queues_[ContentType.AUDIO] = [];
2188
+ }
2251
2189
 
2252
2190
  // Fake a seek to catchup the playhead.
2253
2191
  this.video_.currentTime = currentTime;
@@ -2393,13 +2331,12 @@ shaka.media.MediaSourceEngine = class {
2393
2331
  * Returns true if it's necessary codec switch to load the new stream.
2394
2332
  *
2395
2333
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
2396
- * @param {shaka.extern.Stream} stream
2397
2334
  * @param {string} refMimeType
2398
2335
  * @param {string} refCodecs
2399
2336
  * @return {boolean}
2400
2337
  * @private
2401
2338
  */
2402
- isCodecSwitchNecessary_(contentType, stream, refMimeType, refCodecs) {
2339
+ isCodecSwitchNecessary_(contentType, refMimeType, refCodecs) {
2403
2340
  if (contentType == shaka.util.ManifestParserUtils.ContentType.TEXT) {
2404
2341
  return false;
2405
2342
  }
@@ -2443,13 +2380,12 @@ shaka.media.MediaSourceEngine = class {
2443
2380
  * new stream.
2444
2381
  *
2445
2382
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
2446
- * @param {shaka.extern.Stream} stream
2447
2383
  * @param {string} mimeType
2448
2384
  * @param {string} codecs
2449
2385
  * @return {boolean}
2450
2386
  */
2451
- isResetMediaSourceNecessary(contentType, stream, mimeType, codecs) {
2452
- if (!this.isCodecSwitchNecessary_(contentType, stream, mimeType, codecs)) {
2387
+ isResetMediaSourceNecessary(contentType, mimeType, codecs) {
2388
+ if (!this.isCodecSwitchNecessary_(contentType, mimeType, codecs)) {
2453
2389
  return false;
2454
2390
  }
2455
2391
 
@@ -2540,12 +2476,3 @@ shaka.media.MediaSourceEngine.SourceBufferMode_ = {
2540
2476
  * Called when an embedded 'emsg' box should trigger a manifest update.
2541
2477
  */
2542
2478
  shaka.media.MediaSourceEngine.PlayerInterface;
2543
-
2544
- /**
2545
- * @typedef {{
2546
- * timestampOffset: number,
2547
- * appendWindowStart: number,
2548
- * appendWindowEnd: number
2549
- * }}
2550
- */
2551
- shaka.media.MediaSourceEngine.SourceBufferParams;
@@ -398,7 +398,13 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
398
398
  await this.initializeDrmInner_();
399
399
  this.throwIfDestroyed_();
400
400
 
401
- await this.chooseInitialVariantInner_();
401
+ await this.chooseInitialVariantAndPrefetchInner_();
402
+ this.throwIfDestroyed_();
403
+
404
+ // We don't need the drm keys to load completely for the initial variant
405
+ // to be chosen, but we won't mark the load as a success until it has
406
+ // been loaded. So wait for it here, not inside initializeDrmInner_.
407
+ await this.drmEngine_.waitForActiveRequests();
402
408
  this.throwIfDestroyed_();
403
409
 
404
410
  this.successPromise_.resolve();
@@ -610,12 +616,12 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
610
616
 
611
617
  /**
612
618
  * Performs a final filtering of the manifest, and chooses the initial
613
- * variant.
619
+ * variant. Also prefetches segments.
614
620
  *
615
621
  * @return {!Promise}
616
622
  * @private
617
623
  */
618
- async chooseInitialVariantInner_() {
624
+ async chooseInitialVariantAndPrefetchInner_() {
619
625
  goog.asserts.assert(
620
626
  this.manifest_, 'The manifest should already be parsed.');
621
627
 
@@ -2011,7 +2011,7 @@ shaka.media.StreamingEngine = class {
2011
2011
  const isResetMediaSourceNecessary =
2012
2012
  mediaState.lastCodecs && mediaState.lastMimeType &&
2013
2013
  this.playerInterface_.mediaSourceEngine.isResetMediaSourceNecessary(
2014
- mediaState.type, mediaState.stream, mimeType, fullCodecs);
2014
+ mediaState.type, mimeType, fullCodecs);
2015
2015
  if (isResetMediaSourceNecessary) {
2016
2016
  let otherState = null;
2017
2017
  if (mediaState.type === ContentType.VIDEO) {
@@ -2025,6 +2025,10 @@ shaka.media.StreamingEngine = class {
2025
2025
  // Then clear our cache of the last init segment, since MSE will be
2026
2026
  // reloaded and no init segment will be there post-reload.
2027
2027
  otherState.lastInitSegmentReference = null;
2028
+ // Clear cache of append window start and end, since they will need
2029
+ // to be reapplied post-reload by streaming engine.
2030
+ otherState.lastAppendWindowStart = null;
2031
+ otherState.lastAppendWindowEnd = null;
2028
2032
  // Now force the existing buffer to be cleared. It is not necessary
2029
2033
  // to perform the MSE clear operation, but this has the side-effect
2030
2034
  // that our state for that stream will then match MSE's post-reload
@@ -2729,6 +2733,8 @@ shaka.media.StreamingEngine = class {
2729
2733
  const audioMediaState = this.mediaStates_.get(ContentType.AUDIO);
2730
2734
  if (audioMediaState) {
2731
2735
  audioMediaState.lastInitSegmentReference = null;
2736
+ audioMediaState.lastAppendWindowStart = null;
2737
+ audioMediaState.lastAppendWindowEnd = null;
2732
2738
  if (clearBuffer) {
2733
2739
  this.forceClearBuffer_(audioMediaState);
2734
2740
  }
@@ -2740,6 +2746,8 @@ shaka.media.StreamingEngine = class {
2740
2746
  const videoMediaState = this.mediaStates_.get(ContentType.VIDEO);
2741
2747
  if (videoMediaState) {
2742
2748
  videoMediaState.lastInitSegmentReference = null;
2749
+ videoMediaState.lastAppendWindowStart = null;
2750
+ videoMediaState.lastAppendWindowEnd = null;
2743
2751
  if (clearBuffer) {
2744
2752
  this.forceClearBuffer_(videoMediaState);
2745
2753
  }
@@ -702,7 +702,7 @@ shaka.offline.Storage = class {
702
702
  // to play later (no point storing something we can't play).
703
703
  await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
704
704
  drmEngine, manifest, config.offline.usePersistentLicense,
705
- config.drm.preferredKeySystems);
705
+ config.drm.preferredKeySystems, config.drm.keySystemsMapping);
706
706
 
707
707
  // Gather all tracks.
708
708
  const allTracks = [];
package/lib/player.js CHANGED
@@ -1508,7 +1508,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1508
1508
  // after several playbacks, and they are not able anymore to properly
1509
1509
  // create MediaKeys objects. To prevent it, clear the cache after
1510
1510
  // each playback.
1511
- if (this.config_.streaming.clearDecodingCache) {
1511
+ if (this.config_ && this.config_.streaming.clearDecodingCache) {
1512
1512
  shaka.util.StreamUtils.clearDecodingConfigCache();
1513
1513
  shaka.util.DrmUtils.clearMediaKeySystemAccessMap();
1514
1514
  }
@@ -3136,15 +3136,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3136
3136
  fullyLoaded.reject(this.videoErrorToShakaError_());
3137
3137
  });
3138
3138
 
3139
- const timeout = new Promise((resolve, reject) => {
3140
- const timer = new shaka.util.Timer(reject);
3141
- timer.tickAfter(this.config_.streaming.loadTimeout);
3142
- });
3143
-
3144
- await Promise.race([
3145
- fullyLoaded,
3146
- timeout,
3147
- ]);
3139
+ await shaka.util.Functional.promiseWithTimeout(
3140
+ this.config_.streaming.loadTimeout, fullyLoaded);
3148
3141
 
3149
3142
  const isLive = this.isLive();
3150
3143
 
@@ -6984,7 +6977,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6984
6977
  's). Updating playbackRate to ' + minPlaybackRate);
6985
6978
  this.trickPlay(minPlaybackRate, /* useTrickPlayTrack= */ false);
6986
6979
  }
6987
- } else if (maxLatency && maxPlaybackRate &&
6980
+ } else if (maxLatency != undefined && maxPlaybackRate &&
6988
6981
  (latency - offset) > maxLatency) {
6989
6982
  if (playbackRate != maxPlaybackRate) {
6990
6983
  shaka.log.debug('Latency (' + latency + 's) is greater than ' +
@@ -6993,7 +6986,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6993
6986
  this.trickPlay(maxPlaybackRate, /* useTrickPlayTrack= */ false);
6994
6987
  }
6995
6988
  this.targetLatencyReached_ = null;
6996
- } else if (minLatency && minPlaybackRate &&
6989
+ } else if (minLatency != undefined && minPlaybackRate &&
6997
6990
  (latency - offset) < minLatency) {
6998
6991
  if (playbackRate != minPlaybackRate) {
6999
6992
  shaka.log.debug('Latency (' + latency + 's) is smaller than ' +
@@ -8275,7 +8268,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8275
8268
  * @export
8276
8269
  */
8277
8270
  // eslint-disable-next-line no-useless-concat, max-len
8278
- shaka.Player.version = 'v4.12.3' + '-uncompiled'; // x-release-please-version
8271
+ shaka.Player.version = 'v4.12.5' + '-uncompiled'; // x-release-please-version
8279
8272
 
8280
8273
  // Initialize the deprecation system using the version string we just set
8281
8274
  // on the player.