shaka-player 4.6.13 → 4.6.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 (47) hide show
  1. package/.github/workflows/shaka-bot-commands/lib.sh +37 -12
  2. package/.github/workflows/shaka-bot-commands/main.sh +4 -2
  3. package/.github/workflows/talk-to-shaka-bot.yaml +1 -0
  4. package/AUTHORS +1 -0
  5. package/CHANGELOG.md +26 -0
  6. package/CONTRIBUTORS +1 -0
  7. package/dist/controls.css +1 -1
  8. package/dist/controls.css.map +1 -1
  9. package/dist/deps.js +1 -1
  10. package/dist/locales.js +29 -29
  11. package/dist/shaka-player.compiled.debug.js +91 -89
  12. package/dist/shaka-player.compiled.debug.map +3 -3
  13. package/dist/shaka-player.compiled.js +348 -349
  14. package/dist/shaka-player.compiled.map +3 -3
  15. package/dist/shaka-player.ui.debug.js +105 -103
  16. package/dist/shaka-player.ui.debug.map +3 -3
  17. package/dist/shaka-player.ui.js +433 -434
  18. package/dist/shaka-player.ui.map +3 -3
  19. package/lib/ads/ad_manager.js +1 -1
  20. package/lib/dash/segment_template.js +18 -13
  21. package/lib/deprecate/deprecate.js +1 -1
  22. package/lib/hls/hls_parser.js +24 -5
  23. package/lib/media/manifest_parser.js +3 -2
  24. package/lib/media/segment_reference.js +4 -1
  25. package/lib/media/streaming_engine.js +2 -2
  26. package/lib/player.js +3 -3
  27. package/lib/polyfill/mediasource.js +5 -36
  28. package/lib/text/vtt_text_parser.js +2 -1
  29. package/lib/util/error.js +1 -0
  30. package/lib/util/periods.js +23 -1
  31. package/lib/util/stream_utils.js +1 -1
  32. package/lib/util/ts_parser.js +2 -2
  33. package/package.json +3 -3
  34. package/test/dash/dash_parser_segment_template_unit.js +1 -1
  35. package/test/hls/hls_parser_unit.js +28 -0
  36. package/test/media/buffering_observer_unit.js +13 -0
  37. package/test/player_integration.js +9 -1
  38. package/test/player_load_graph_integration.js +13 -10
  39. package/test/player_src_equals_integration.js +2 -2
  40. package/test/player_unit.js +9 -3
  41. package/test/test/assets/hls-ts-h265/index.m3u8 +2 -0
  42. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +5 -1
  43. package/test/test/util/waiter.js +5 -0
  44. package/test/text/vtt_text_parser_unit.js +31 -0
  45. package/test/transmuxer/transmuxer_integration.js +22 -53
  46. package/test/util/periods_unit.js +64 -0
  47. package/ui/less/containers.less +1 -0
@@ -656,7 +656,7 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
656
656
  */
657
657
  getServerSideCuePoints() {
658
658
  shaka.Deprecate.deprecateFeature(5,
659
- 'AdManager',
659
+ 'AdManager.getServerSideCuePoints',
660
660
  'Please use getCuePoints function.');
661
661
  return this.getCuePoints();
662
662
  }
@@ -755,16 +755,10 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
755
755
 
756
756
  this.evict(this.periodStart_);
757
757
 
758
- if (timeline.length === 0) {
759
- return;
760
- }
761
-
762
- if (this.periodEnd_ !== Infinity) {
763
- // Adjust the last timeline entry to match the period end
764
- const lastTimePeriod = timeline[timeline.length - 1];
765
- // NOTE: end should be relative to period start
766
- lastTimePeriod.end = this.periodEnd_ - this.periodStart_;
767
- }
758
+ // Do NOT adjust last range to match period end! With high precision
759
+ // timestamps several recalculations may give wrong results on less precise
760
+ // platforms. To mitigate that, we're using cached |periodEnd_| value in
761
+ // find/get() methods whenever possible.
768
762
  }
769
763
 
770
764
  /**
@@ -784,7 +778,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
784
778
  const timeline = this.templateInfo_.timeline;
785
779
 
786
780
  // Early exit if the time isn't within this period
787
- if (time < this.periodStart_ || time > this.periodEnd_) {
781
+ if (time < this.periodStart_ || time >= this.periodEnd_) {
788
782
  return null;
789
783
  }
790
784
 
@@ -797,10 +791,14 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
797
791
  // the references by a fraction of a second. To account for this, we use
798
792
  // the start of the next segment as /end/, unless this is the last
799
793
  // reference, in which case we use the period end as the /end/
800
- let end = range.end + this.periodStart_;
794
+ let end;
801
795
 
802
796
  if (i < lastIndex) {
803
797
  end = timeline[i + 1].start + this.periodStart_;
798
+ } else if (this.periodEnd_ === Infinity) {
799
+ end = range.end + this.periodStart_;
800
+ } else {
801
+ end = this.periodEnd_;
804
802
  }
805
803
 
806
804
  if ((time >= start) && (time < end)) {
@@ -831,6 +829,12 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
831
829
  .unscaledPresentationTimeOffset + range.unscaledStart;
832
830
  const timestampOffset = this.periodStart_ -
833
831
  this.templateInfo_.scaledPresentationTimeOffset;
832
+ const trueSegmentEnd = this.periodStart_ + range.end;
833
+ let segmentEnd = trueSegmentEnd;
834
+ if (correctedPosition === this.getNumReferences() - 1 &&
835
+ this.periodEnd_ !== Infinity) {
836
+ segmentEnd = this.periodEnd_;
837
+ }
834
838
 
835
839
  const partialSegmentRefs = [];
836
840
 
@@ -901,7 +905,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
901
905
 
902
906
  ref = new shaka.media.SegmentReference(
903
907
  this.periodStart_ + range.start,
904
- this.periodStart_ + range.end,
908
+ segmentEnd,
905
909
  createUrisCb,
906
910
  /* startByte= */ 0,
907
911
  /* endByte= */ null,
@@ -916,6 +920,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
916
920
  shaka.media.SegmentReference.Status.AVAILABLE,
917
921
  this.aes128Key_,
918
922
  /* allPartialSegments= */ range.partialSegments > 0);
923
+ ref.trueEndTime = trueSegmentEnd;
919
924
  this.references[correctedPosition] = ref;
920
925
  }
921
926
 
@@ -107,7 +107,7 @@ shaka.Deprecate = class {
107
107
  libraryVersion,
108
108
  '. Additional information:',
109
109
  description,
110
- ].join('');
110
+ ].join(' ');
111
111
 
112
112
  shaka.log.alwaysError(errorMessage);
113
113
  goog.asserts.assert(false, errorMessage);
@@ -32,6 +32,7 @@ goog.require('shaka.util.FakeEvent');
32
32
  goog.require('shaka.util.LanguageUtils');
33
33
  goog.require('shaka.util.ManifestParserUtils');
34
34
  goog.require('shaka.util.MimeUtils');
35
+ goog.require('shaka.util.Networking');
35
36
  goog.require('shaka.util.OperationManager');
36
37
  goog.require('shaka.util.Pssh');
37
38
  goog.require('shaka.media.SegmentUtils');
@@ -908,8 +909,11 @@ shaka.hls.HlsParser = class {
908
909
  playlist, middleSegment.tags, getUris);
909
910
  this.mapTagToInitSegmentRefMap_.clear();
910
911
  if (initSegmentRef) {
911
- const initSegmentRequest = shaka.net.NetworkingEngine.makeRequest(
912
- initSegmentRef.getUris(), this.config_.retryParameters);
912
+ const initSegmentRequest = shaka.util.Networking.createSegmentRequest(
913
+ initSegmentRef.getUris(),
914
+ initSegmentRef.getStartByte(),
915
+ initSegmentRef.getEndByte(),
916
+ this.config_.retryParameters);
913
917
  const initType =
914
918
  shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
915
919
  const initResponse = await this.makeNetworkRequest_(
@@ -2831,12 +2835,12 @@ shaka.hls.HlsParser = class {
2831
2835
  // Don't download the key object until the segment is parsed, to avoid a
2832
2836
  // startup delay for long manifests with lots of keys.
2833
2837
  keyInfo.fetchKey = async () => {
2834
- const keyUri = shaka.hls.Utils.constructSegmentUris(
2838
+ const keyUris = shaka.hls.Utils.constructSegmentUris(
2835
2839
  getUris(), drmTag.getRequiredAttrValue('URI'), variables);
2836
2840
 
2837
2841
  const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
2838
2842
  const request = shaka.net.NetworkingEngine.makeRequest(
2839
- [keyUri], this.config_.retryParameters);
2843
+ keyUris, this.config_.retryParameters);
2840
2844
  const keyResponse = await this.makeNetworkRequest_(request, requestType);
2841
2845
 
2842
2846
  // keyResponse.status is undefined when URI is "data:text/plain;base64,"
@@ -4122,7 +4126,22 @@ shaka.hls.HlsParser = class {
4122
4126
  * @return {?shaka.extern.DrmInfo}
4123
4127
  * @private
4124
4128
  */
4125
- static identityDrmParser_(drmTag) {
4129
+ static identityDrmParser_(drmTag, mimeType) {
4130
+ if (mimeType == 'video/mp2t') {
4131
+ throw new shaka.util.Error(
4132
+ shaka.util.Error.Severity.CRITICAL,
4133
+ shaka.util.Error.Category.MANIFEST,
4134
+ shaka.util.Error.Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
4135
+ }
4136
+
4137
+ if (shaka.util.Platform.isMediaKeysPolyfilled()) {
4138
+ throw new shaka.util.Error(
4139
+ shaka.util.Error.Severity.CRITICAL,
4140
+ shaka.util.Error.Category.MANIFEST,
4141
+ shaka.util.Error.Code
4142
+ .HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED);
4143
+ }
4144
+
4126
4145
  const method = drmTag.getRequiredAttrValue('METHOD');
4127
4146
  const VALID_METHODS = ['SAMPLE-AES', 'SAMPLE-AES-CTR'];
4128
4147
  if (!VALID_METHODS.includes(method)) {
@@ -28,7 +28,7 @@ shaka.media.ManifestParser = class {
28
28
  */
29
29
  static registerParserByExtension(extension, parserFactory) {
30
30
  shaka.Deprecate.deprecateFeature(5,
31
- 'ManifestParser',
31
+ 'ManifestParser.registerParserByExtension',
32
32
  'Please use an ManifestParser with registerParserByMime function.');
33
33
  }
34
34
 
@@ -125,7 +125,8 @@ shaka.media.ManifestParser = class {
125
125
  shaka.util.Error.Severity.CRITICAL,
126
126
  shaka.util.Error.Category.MANIFEST,
127
127
  shaka.util.Error.Code.UNABLE_TO_GUESS_MANIFEST_TYPE,
128
- uri);
128
+ uri,
129
+ mimeType);
129
130
  }
130
131
 
131
132
 
@@ -123,7 +123,10 @@ shaka.media.InitSegmentReference = class {
123
123
  static equal(reference1, reference2) {
124
124
  const ArrayUtils = shaka.util.ArrayUtils;
125
125
  const BufferUtils = shaka.util.BufferUtils;
126
- if (!reference1 || !reference2) {
126
+
127
+ if (reference1 === reference2) {
128
+ return true;
129
+ } else if (!reference1 || !reference2) {
127
130
  return reference1 == reference2;
128
131
  } else {
129
132
  return reference1.getStartByte() == reference2.getStartByte() &&
@@ -2153,8 +2153,8 @@ shaka.media.StreamingEngine = class {
2153
2153
  const bufferedBehind = presentationTime - startTime;
2154
2154
 
2155
2155
  const overflow = bufferedBehind - bufferBehind;
2156
- // See: https://github.com/shaka-project/shaka-player/issues/2982
2157
- if (overflow <= 0.01) {
2156
+ // See: https://github.com/shaka-project/shaka-player/issues/6240
2157
+ if (overflow <= 1) {
2158
2158
  shaka.log.v2(logPrefix,
2159
2159
  'buffer behind okay:',
2160
2160
  'presentationTime=' + presentationTime,
package/lib/player.js CHANGED
@@ -734,7 +734,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
734
734
  // player having been initialized.
735
735
  if (mediaElement) {
736
736
  shaka.Deprecate.deprecateFeature(5,
737
- 'Player',
737
+ 'Player w/ mediaElement',
738
738
  'Please migrate from initializing Player with a mediaElement; ' +
739
739
  'use the attach method instead.');
740
740
  this.attach(mediaElement, /* initializeMediaSource= */ true);
@@ -1842,7 +1842,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1842
1842
  this.abrManager_ = abrFactory();
1843
1843
  if (typeof this.abrManager_.setMediaElement != 'function') {
1844
1844
  shaka.Deprecate.deprecateFeature(5,
1845
- 'AbrManager',
1845
+ 'AbrManager w/o setMediaElement',
1846
1846
  'Please use an AbrManager with setMediaElement function.');
1847
1847
  this.abrManager_.setMediaElement = () => {};
1848
1848
  }
@@ -6837,7 +6837,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6837
6837
  * @export
6838
6838
  */
6839
6839
  // eslint-disable-next-line no-useless-concat
6840
- shaka.Player.version = 'v4.6.13' + '-uncompiled'; // x-release-please-version
6840
+ shaka.Player.version = 'v4.6.15' + '-uncompiled'; // x-release-please-version
6841
6841
 
6842
6842
  // Initialize the deprecation system using the version string we just set
6843
6843
  // on the player.
@@ -32,11 +32,12 @@ shaka.polyfill.MediaSource = class {
32
32
 
33
33
  if (!window.MediaSource) {
34
34
  shaka.log.info('No MSE implementation available.');
35
- } else if (window.cast && cast.__platform__ &&
36
- cast.__platform__.canDisplayType) {
35
+ } else if (shaka.util.Platform.isChromecast()) {
37
36
  shaka.log.info('Patching Chromecast MSE bugs.');
38
- // Chromecast cannot make accurate determinations via isTypeSupported.
39
- shaka.polyfill.MediaSource.patchCastIsTypeSupported_();
37
+ // Chromecast fails on some TS content, even though it is supposed to
38
+ // support it. Better to transmux.
39
+ // See https://github.com/shaka-project/shaka-player/issues/5278
40
+ shaka.polyfill.MediaSource.rejectContainer_('mp2t');
40
41
  } else if (safariVersion) {
41
42
  // NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
42
43
  // Safari version.
@@ -182,38 +183,6 @@ shaka.polyfill.MediaSource = class {
182
183
  };
183
184
  }
184
185
 
185
- /**
186
- * Patch isTypeSupported() to chain to a private API on the Chromecast which
187
- * can query for support of detailed content parameters.
188
- *
189
- * @private
190
- */
191
- static patchCastIsTypeSupported_() {
192
- const originalIsTypeSupported = MediaSource.isTypeSupported;
193
-
194
- MediaSource.isTypeSupported = (mimeType) => {
195
- // Parse the basic MIME type from its parameters.
196
- const pieces = mimeType.split(/ *; */);
197
- pieces.shift(); // Remove basic MIME type from pieces.
198
-
199
- const hasCodecs = pieces.some((piece) => piece.startsWith('codecs='));
200
- if (!hasCodecs) {
201
- // Though the original reason for this special case was not documented,
202
- // it is presumed to be because the platform won't accept a MIME type
203
- // without codecs in canDisplayType. It is valid, however, in
204
- // isTypeSupported.
205
- return originalIsTypeSupported(mimeType);
206
- }
207
-
208
- // Only canDisplayType can check extended MIME type parameters on this
209
- // platform (such as frame rate, resolution, etc).
210
- // In previous versions of this polyfill, the MIME type parameters were
211
- // manipulated, filtered, or extended. This is no longer true, so we pass
212
- // the full MIME type to the platform as we received it.
213
- return cast.__platform__.canDisplayType(mimeType);
214
- };
215
- }
216
-
217
186
  /**
218
187
  * Patch isTypeSupported() to translate vp09 codec strings into vp9, to allow
219
188
  * such content to play on older smart TVs.
@@ -687,6 +687,8 @@ shaka.text.VttTextParser = class {
687
687
  refCue.fontFamily, cue.fontFamily);
688
688
  cue.fontSize = VttTextParser.getOrDefault_(
689
689
  refCue.fontSize, cue.fontSize);
690
+ cue.textShadow = VttTextParser.getOrDefault_(
691
+ refCue.textShadow, cue.textShadow);
690
692
 
691
693
  // Overwrite with new values as unable to determine
692
694
  // if new value is set or not
@@ -695,7 +697,6 @@ shaka.text.VttTextParser = class {
695
697
  cue.opacity = refCue.opacity;
696
698
  cue.rubyTag = refCue.rubyTag;
697
699
  cue.textCombineUpright = refCue.textCombineUpright;
698
- cue.textShadow = refCue.textShadow;
699
700
  cue.wrapLine = refCue.wrapLine;
700
701
  }
701
702
 
package/lib/util/error.js CHANGED
@@ -527,6 +527,7 @@ shaka.util.Error.Code = {
527
527
  * <li>Configure the server to accept a HEAD request for the manifest.
528
528
  * </ul>
529
529
  * <br> error.data[0] is the manifest URI.
530
+ * <br> error.data[1] is the detected or specified MIME type.
530
531
  */
531
532
  'UNABLE_TO_GUESS_MANIFEST_TYPE': 4000,
532
533
 
@@ -1180,7 +1180,18 @@ shaka.util.PeriodCombiner = class {
1180
1180
  return false;
1181
1181
  }
1182
1182
 
1183
- // If language-based differences haven't decided this, look at roles. If
1183
+ // If language-based differences haven't decided this, look at labels.
1184
+ // If available options differ, look does any matches with output stream.
1185
+ if (best.label !== candidate.label) {
1186
+ if (outputStream.label === best.label) {
1187
+ return false;
1188
+ }
1189
+ if (outputStream.label === candidate.label) {
1190
+ return true;
1191
+ }
1192
+ }
1193
+
1194
+ // If label-based differences haven't decided this, look at roles. If
1184
1195
  // the candidate has more roles in common with the output, upgrade to the
1185
1196
  // candidate.
1186
1197
  if (outputStream.roles.length) {
@@ -1383,6 +1394,17 @@ shaka.util.PeriodCombiner = class {
1383
1394
  return false;
1384
1395
  }
1385
1396
 
1397
+ // If language-based differences haven't decided this, look at labels.
1398
+ // If available options differ, look does any matches with output stream.
1399
+ if (best.label !== candidate.label) {
1400
+ if (outputStream.label === best.label) {
1401
+ return false;
1402
+ }
1403
+ if (outputStream.label === candidate.label) {
1404
+ return true;
1405
+ }
1406
+ }
1407
+
1386
1408
  // If the candidate has more roles in common with the output, upgrade to the
1387
1409
  // candidate.
1388
1410
  if (outputStream.roles.length) {
@@ -839,7 +839,7 @@ shaka.util.StreamUtils = class {
839
839
  // currently don't support 'fLaC', while 'flac' is supported by most
840
840
  // major browsers.
841
841
  // See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
842
- if (codecs === 'fLaC') {
842
+ if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
843
843
  return 'flac';
844
844
  }
845
845
 
@@ -433,7 +433,7 @@ shaka.util.TsParser = class {
433
433
  */
434
434
  parseAvcNalus(pes, nextPes) {
435
435
  shaka.Deprecate.deprecateFeature(5,
436
- 'TsParser',
436
+ 'TsParser.parseAvcNalus',
437
437
  'Please use parseNalus function instead.');
438
438
  return this.parseNalus(pes);
439
439
  }
@@ -769,7 +769,7 @@ shaka.util.TsParser = class {
769
769
  */
770
770
  getVideoResolution() {
771
771
  shaka.Deprecate.deprecateFeature(5,
772
- 'TsParser',
772
+ 'TsParser.getVideoResolution',
773
773
  'Please use getVideoInfo function instead.');
774
774
  const videoInfo = this.getVideoInfo();
775
775
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.6.13",
4
+ "version": "4.6.15",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -43,13 +43,13 @@
43
43
  "karma-coverage": "^2.2.0",
44
44
  "karma-jasmine": "^4.0.1",
45
45
  "karma-jasmine-ajax": "^0.1.13",
46
- "karma-local-wd-launcher": "^1.6.6",
46
+ "karma-local-wd-launcher": "^1.6.7",
47
47
  "karma-opera-launcher": "^1.0.0",
48
48
  "karma-sourcemap-loader": "^0.3.8",
49
49
  "karma-spec-reporter": "^0.0.34",
50
50
  "karma-webdriver-launcher": "^1.0.8",
51
51
  "lcevc_dec.js": "^1.0.1",
52
- "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
52
+ "less": "^4.2.0",
53
53
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
54
54
  "material-design-lite": "^1.3.0",
55
55
  "open-sans-fonts": "^1.6.2",
@@ -786,7 +786,7 @@ describe('DashParser SegmentTemplate', () => {
786
786
  const newTemplateInfo = makeTemplateInfo(newRanges);
787
787
 
788
788
  const newEnd = newRanges[newRanges.length - 1].end;
789
- index.appendTemplateInfo(newTemplateInfo, newEnd);
789
+ index.appendTemplateInfo(newTemplateInfo, /* periodStart= */ 0, newEnd);
790
790
  expect(index.find(newStart)).toBe(10);
791
791
  expect(index.find(newEnd - 1.0)).toBe(19);
792
792
  });
@@ -4074,6 +4074,34 @@ describe('HlsParser', () => {
4074
4074
  await verifyError(master, media, error, /* onCreateSegmentIndex= */ true);
4075
4075
  });
4076
4076
 
4077
+ it('if SAMPLE-AES encryption with MSE and mp2t content', async () => {
4078
+ const master = [
4079
+ '#EXTM3U\n',
4080
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
4081
+ 'RESOLUTION=960x540,FRAME-RATE=60\n',
4082
+ 'video\n',
4083
+ ].join('');
4084
+
4085
+ const media = [
4086
+ '#EXTM3U\n',
4087
+ '#EXT-X-TARGETDURATION:6\n',
4088
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
4089
+ '#EXT-X-KEY:METHOD=SAMPLE-AES,',
4090
+ 'URI="fake",\n',
4091
+ '#EXTINF:5,\n',
4092
+ '#EXT-X-BYTERANGE:121090@616\n',
4093
+ 'main.ts',
4094
+ ].join('');
4095
+
4096
+ const error = new shaka.util.Error(
4097
+ shaka.util.Error.Severity.CRITICAL,
4098
+ shaka.util.Error.Category.MANIFEST,
4099
+ Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
4100
+
4101
+ await verifyError(master, media, error, /* onCreateSegmentIndex= */ true);
4102
+ });
4103
+
4104
+
4077
4105
  describe('if required tags are missing', () => {
4078
4106
  /**
4079
4107
  * @param {string} master
@@ -11,15 +11,28 @@ describe('BufferingObserver', () => {
11
11
  const thresholdAfterStarving = 5;
12
12
  const thresholdAfterSatisfied = 2;
13
13
 
14
+ const originalDateNow = Date.now;
15
+
16
+ /** @type {!Date} */
17
+ let baseTime;
18
+
14
19
  /** @type {!shaka.media.BufferingObserver} */
15
20
  let controller;
16
21
 
17
22
  beforeEach(() => {
23
+ // Any test using Date needs to have Date mocked to avoid flake.
24
+ baseTime = new Date(2015, 11, 30);
25
+ Date.now = () => baseTime.getTime();
26
+
18
27
  controller = new BufferingObserver(
19
28
  thresholdAfterStarving,
20
29
  thresholdAfterSatisfied);
21
30
  });
22
31
 
32
+ afterEach(() => {
33
+ Date.now = originalDateNow;
34
+ });
35
+
23
36
  describe('when satisfied', () => {
24
37
  beforeEach(() => {
25
38
  controller.setState(State.SATISFIED);
@@ -1208,13 +1208,21 @@ describe('Player', () => {
1208
1208
  'com.widevine.alpha': bogusUrl,
1209
1209
  'com.microsoft.playready': bogusUrl,
1210
1210
  });
1211
- await player.load('test:sintel-enc_compiled');
1211
+
1212
+ // This load may be interrupted, so ignore errors and don't wait.
1213
+ const loadPromise =
1214
+ player.load('test:sintel-enc_compiled').catch(() => {});
1212
1215
 
1213
1216
  await errorPromise;
1214
1217
  expect(unloadPromise).not.toBeNull();
1218
+
1215
1219
  if (unloadPromise) {
1216
1220
  await unloadPromise;
1217
1221
  }
1222
+
1223
+ // This should be done, and errors ignored. But don't leave any Promise
1224
+ // unresolved.
1225
+ await loadPromise;
1218
1226
  });
1219
1227
  }); // describe('unloading')
1220
1228
 
@@ -373,11 +373,11 @@ describe('Player Load Graph', () => {
373
373
 
374
374
  // Make the two requests one-after-another so that we don't have any idle
375
375
  // time between them.
376
- const attachRequest = player.attach(video);
377
- const loadRequest = player.load('test:sintel');
376
+ const attach = player.attach(video);
377
+ const load = player.load('test:sintel');
378
378
 
379
- await attachRequest;
380
- await expectAsync(loadRequest).toBeRejected();
379
+ await attach;
380
+ await expectAsync(load).toBeRejected();
381
381
 
382
382
  // Wait a couple interrupter cycles to allow the player to enter idle
383
383
  // state.
@@ -420,22 +420,23 @@ describe('Player Load Graph', () => {
420
420
  // Normally the player would initialize media source after attaching to
421
421
  // the media element, however since we don't support media source, it
422
422
  // should stop at the attach state.
423
- player.attach(video, /* initMediaSource= */ true);
423
+ const attach = player.attach(video, /* initMediaSource= */ true);
424
424
 
425
425
  await shaka.test.Util.delay(/* seconds= */ 0.25);
426
426
  expect(lastStateChange).toBe('attach');
427
+
428
+ await attach;
427
429
  });
428
430
 
429
431
  it('loading ignores media source path', async () => {
430
432
  await player.attach(video, /* initMediaSource= */ false);
431
433
 
432
- // Normally the player would load content like this with the media source
433
- // path, but since we don't have media source support, it should use the
434
- // src= path.
435
- player.load(SMALL_MP4_CONTENT_URI);
434
+ const load = player.load(SMALL_MP4_CONTENT_URI);
436
435
 
437
436
  await shaka.test.Util.delay(/* seconds= */ 0.25);
438
437
  expect(lastStateChange).toBe('src-equals');
438
+
439
+ await load;
439
440
  });
440
441
 
441
442
  it('unloading ignores init media source flag', async () => {
@@ -445,10 +446,12 @@ describe('Player Load Graph', () => {
445
446
  // Normally the player would try to go to the media source state because
446
447
  // we are saying to initialize media source after unloading, but since we
447
448
  // don't have media source, it should stop at the attach state.
448
- player.unload(/* initMediaSource= */ true);
449
+ const unload = player.unload(/* initMediaSource= */ true);
449
450
 
450
451
  await shaka.test.Util.delay(/* seconds= */ 0.25);
451
452
  expect(lastStateChange).toBe('unload');
453
+
454
+ await unload;
452
455
  });
453
456
  });
454
457
 
@@ -115,8 +115,8 @@ describe('Player Src Equals', () => {
115
115
  expect(video.currentTime).toBeLessThan(2.0);
116
116
 
117
117
  // Trigger a seek and then wait for the seek to take effect.
118
- // This seek target is very close to the duration of the video.
119
- video.currentTime = 10;
118
+ // This seek target is very close to the duration of the video (10.01s).
119
+ video.currentTime = 9.5;
120
120
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
121
121
 
122
122
  // Make sure the playhead is roughly where we expect it to be after
@@ -667,15 +667,21 @@ describe('Player', () => {
667
667
  });
668
668
 
669
669
  describe('when config.streaming.preferNativeHls is set to true', () => {
670
- beforeEach(() => {
670
+ beforeAll(() => {
671
671
  shaka.media.ManifestParser.registerParserByMime(
672
672
  'application/x-mpegurl',
673
673
  () => new shaka.test.FakeManifestParser(manifest));
674
674
  });
675
675
 
676
+ afterAll(() => {
677
+ // IMPORTANT: restore the ORIGINAL parser. DO NOT just unregister the
678
+ // fake!
679
+ shaka.media.ManifestParser.registerParserByMime(
680
+ 'application/x-mpegurl',
681
+ () => new shaka.hls.HlsParser());
682
+ });
683
+
676
684
  afterEach(() => {
677
- shaka.media.ManifestParser.unregisterParserByMime(
678
- 'application/x-mpegurl');
679
685
  video.canPlayType.calls.reset();
680
686
  });
681
687
 
@@ -4,4 +4,6 @@
4
4
  #EXT-X-MEDIA-SEQUENCE:0
5
5
  #EXTINF:12.000000,
6
6
  0.ts
7
+ #EXTINF:12.000000,
8
+ 0.ts
7
9
  #EXT-X-ENDLIST
@@ -6,4 +6,8 @@
6
6
  0.ts
7
7
  #EXTINF:5.000000,
8
8
  1.ts
9
- #EXT-X-ENDLIST
9
+ #EXTINF:5.000000,
10
+ 0.ts
11
+ #EXTINF:5.000000,
12
+ 1.ts
13
+ #EXT-X-ENDLIST
@@ -184,6 +184,7 @@ shaka.test.Waiter = class {
184
184
  }
185
185
  this.eventManager_.unlisten(mediaElement, 'timeupdate');
186
186
  this.eventManager_.unlisten(mediaElement, 'ended');
187
+ this.eventManager_.unlisten(mediaElement, 'durationchange');
187
188
  };
188
189
 
189
190
  const p = new Promise((resolve) => {
@@ -202,6 +203,10 @@ shaka.test.Waiter = class {
202
203
  timer.tickEvery(/* seconds= */ 1);
203
204
  this.eventManager_.listen(mediaElement, 'timeupdate', check);
204
205
  this.eventManager_.listen(mediaElement, 'ended', check);
206
+ // Some tests on Safari expose a condition where the ended flag can be
207
+ // found in the desired state in a brief window during a durationchange
208
+ // event.
209
+ this.eventManager_.listen(mediaElement, 'durationchange', check);
205
210
  });
206
211
 
207
212
  return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement);