shaka-player 4.1.1 → 4.1.4

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 (155) hide show
  1. package/.github/workflows/build-and-test.yaml +67 -15
  2. package/.github/workflows/compute-incremental-coverage.py +251 -0
  3. package/.github/workflows/report-incremental-coverage.yaml +39 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +179 -31
  5. package/AUTHORS +1 -0
  6. package/CHANGELOG.md +40 -0
  7. package/CONTRIBUTORS +3 -0
  8. package/README.md +20 -6
  9. package/demo/index.html +1 -3
  10. package/dist/controls.css +2 -2
  11. package/dist/controls.css.map +1 -1
  12. package/dist/deps.js +3 -3
  13. package/dist/locales.js +33 -33
  14. package/dist/shaka-player.compiled.d.ts +12 -1
  15. package/dist/shaka-player.compiled.debug.d.ts +12 -1
  16. package/dist/shaka-player.compiled.debug.externs.js +49 -38
  17. package/dist/shaka-player.compiled.debug.js +173 -165
  18. package/dist/shaka-player.compiled.debug.map +4 -4
  19. package/dist/shaka-player.compiled.externs.js +49 -38
  20. package/dist/shaka-player.compiled.js +789 -785
  21. package/dist/shaka-player.compiled.map +4 -4
  22. package/dist/shaka-player.ui.d.ts +12 -1
  23. package/dist/shaka-player.ui.debug.d.ts +12 -1
  24. package/dist/shaka-player.ui.debug.externs.js +49 -38
  25. package/dist/shaka-player.ui.debug.js +187 -179
  26. package/dist/shaka-player.ui.debug.map +4 -4
  27. package/dist/shaka-player.ui.externs.js +49 -38
  28. package/dist/shaka-player.ui.js +916 -912
  29. package/dist/shaka-player.ui.map +4 -4
  30. package/docs/tutorials/ui.md +6 -6
  31. package/docs/tutorials/upgrade.md +1 -0
  32. package/docs/tutorials/welcome.md +6 -3
  33. package/karma.conf.js +3 -11
  34. package/lib/ads/client_side_ad_manager.js +1 -1
  35. package/lib/dash/dash_parser.js +5 -0
  36. package/lib/hls/hls_parser.js +6 -2
  37. package/lib/media/drm_engine.js +1 -1
  38. package/lib/media/media_source_engine.js +57 -24
  39. package/lib/media/presentation_timeline.js +24 -1
  40. package/lib/media/streaming_engine.js +4 -4
  41. package/lib/media/time_ranges_utils.js +46 -0
  42. package/lib/player.js +13 -6
  43. package/lib/polyfill/media_capabilities.js +9 -0
  44. package/lib/polyfill/videoplaybackquality.js +3 -1
  45. package/lib/text/cue.js +0 -5
  46. package/lib/text/simple_text_displayer.js +21 -9
  47. package/lib/text/ui_text_displayer.js +91 -19
  48. package/lib/util/buffer_utils.js +3 -0
  49. package/lib/util/object_utils.js +1 -0
  50. package/lib/util/platform.js +19 -1
  51. package/lib/util/player_configuration.js +1 -0
  52. package/lib/util/stream_utils.js +23 -3
  53. package/lib/util/string_utils.js +102 -13
  54. package/maintained-branches.md +15 -0
  55. package/package.json +3 -5
  56. package/test/media/drm_engine_integration.js +24 -8
  57. package/test/media/drm_engine_unit.js +29 -0
  58. package/test/media/media_source_engine_integration.js +24 -10
  59. package/test/media/media_source_engine_unit.js +53 -38
  60. package/test/media/streaming_engine_unit.js +28 -27
  61. package/test/player_src_equals_integration.js +2 -0
  62. package/test/player_unit.js +39 -1
  63. package/test/test/assets/small.mp4 +0 -0
  64. package/test/test/util/fake_media_source_engine.js +10 -9
  65. package/test/text/simple_text_displayer_unit.js +27 -0
  66. package/test/text/ui_text_displayer_unit.js +65 -0
  67. package/test/util/string_utils_unit.js +46 -8
  68. package/ui/less/ad_controls.less +4 -0
  69. package/ui/text_selection.js +2 -2
  70. package/app-engine/README.md +0 -9
  71. package/app-engine/demo-version-index/README.md +0 -14
  72. package/app-engine/demo-version-index/app.yaml +0 -28
  73. package/app-engine/demo-version-index/generate.py +0 -164
  74. package/app-engine/demo-version-index/main.py +0 -11
  75. package/app-engine/demo-version-index/requirements.txt +0 -5
  76. package/app-engine/demo-version-index/templates/index.html +0 -114
  77. package/app-engine/shaka-player-demo/README.md +0 -18
  78. package/app-engine/shaka-player-demo/app.yaml +0 -61
  79. package/app-engine/shaka-player-demo/main.py +0 -95
  80. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  81. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  82. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  83. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  84. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  85. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  86. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  87. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  88. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  89. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  90. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  91. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  92. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  93. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  94. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  95. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  96. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  97. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  98. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  99. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  100. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  101. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  102. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  103. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  104. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  105. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  106. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  107. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  108. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  109. package/app-engine/shaka-player-demo/posters.py +0 -110
  110. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  111. package/app-engine/shaka-player-demo/time.txt +0 -1
  112. package/build/README.md +0 -127
  113. package/build/__init__.py +0 -0
  114. package/build/all.py +0 -150
  115. package/build/apps.py +0 -186
  116. package/build/build.py +0 -392
  117. package/build/check.py +0 -354
  118. package/build/checkversion.py +0 -84
  119. package/build/compiler.py +0 -513
  120. package/build/conformance.textproto +0 -335
  121. package/build/docker/Dockerfile +0 -20
  122. package/build/docker/docker-compose.yml +0 -10
  123. package/build/docs.py +0 -54
  124. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  125. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  126. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  127. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  128. package/build/gendeps.py +0 -71
  129. package/build/generateExterns.js +0 -867
  130. package/build/generateLocalizations.py +0 -262
  131. package/build/generateTsDefs.py +0 -137
  132. package/build/install-linux-prereqs.sh +0 -136
  133. package/build/license-header +0 -5
  134. package/build/misspellings.txt +0 -39
  135. package/build/pixelsChanged.js +0 -35
  136. package/build/shaka-lab.yaml +0 -161
  137. package/build/shakaBuildHelpers.py +0 -356
  138. package/build/stats.py +0 -816
  139. package/build/subprocessWindowsPatch.py +0 -103
  140. package/build/test.py +0 -541
  141. package/build/types/ads +0 -8
  142. package/build/types/cast +0 -6
  143. package/build/types/cea +0 -13
  144. package/build/types/complete +0 -9
  145. package/build/types/core +0 -108
  146. package/build/types/dash +0 -8
  147. package/build/types/hls +0 -9
  148. package/build/types/manifests +0 -5
  149. package/build/types/networking +0 -6
  150. package/build/types/offline +0 -22
  151. package/build/types/polyfill +0 -18
  152. package/build/types/text +0 -11
  153. package/build/types/ui +0 -39
  154. package/build/updateScreenshots.py +0 -85
  155. package/build/wrapper.template.js +0 -52
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
30
30
  The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
31
31
  the cast button will cast to; the value provided here is the sample cast receiver. -->
32
32
  <div data-shaka-player-container style="max-width:40em"
33
- data-shaka-player-cast-receiver-id="BBED8D28">
33
+ data-shaka-player-cast-receiver-id="06EDA6EE">
34
34
  <!-- The data-shaka-player tag will make the UI library use this video element.
35
35
  If no video is provided, the UI will automatically make one inside the container div. -->
36
36
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
111
111
  <!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
112
112
  the cast button will cast to; the value provided here is the sample cast receiver. -->
113
113
  <div data-shaka-player-container style="max-width:40em"
114
- data-shaka-player-cast-receiver-id="BBED8D28">
114
+ data-shaka-player-cast-receiver-id="06EDA6EE">
115
115
  </div>
116
116
  ```
117
117
 
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
154
154
 
155
155
  ```html
156
156
  <div data-shaka-player-container style="max-width:40em"
157
- data-shaka-player-cast-receiver-id="BBED8D28">
157
+ data-shaka-player-cast-receiver-id="06EDA6EE">
158
158
  <!-- The manifest url in the src attribute will be automatically loaded -->
159
159
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"
160
160
  src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
@@ -165,7 +165,7 @@ or
165
165
 
166
166
  ```html
167
167
  <div data-shaka-player-container style="max-width:40em"
168
- data-shaka-player-cast-receiver-id="BBED8D28">
168
+ data-shaka-player-cast-receiver-id="06EDA6EE">
169
169
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%">
170
170
  <!-- The manifest url in the src attribute will be auto loaded -->
171
171
  <source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
@@ -178,7 +178,7 @@ call to the first one fails.
178
178
 
179
179
  ```html
180
180
  <div data-shaka-player-container style="max-width:40em"
181
- data-shaka-player-cast-receiver-id="BBED8D28">
181
+ data-shaka-player-cast-receiver-id="06EDA6EE">
182
182
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%">
183
183
  <!-- Try this first -->
184
184
  <source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
@@ -222,7 +222,7 @@ const video = controls.getVideo();
222
222
  // Receiver Compatability.
223
223
  ui.configure({
224
224
  // Set the castReceiverAppId
225
- 'castReceiverAppId': 'BBED8D28',
225
+ 'castReceiverAppId': '06EDA6EE',
226
226
  // Enable casting to native Android Apps (e.g. Android TV Apps)
227
227
  'castAndroidReceiverCompatible': true,
228
228
  });
@@ -25,6 +25,7 @@ application:
25
25
  - TextDecoder/TextEncoder platform support or polyfill required (affects
26
26
  Xbox One, but not evergreen browsers); we suggest the polyfill
27
27
  [https://github.com/anonyco/FastestSmallestTextEncoderDecoder](fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js)
28
+ Fallback included by default in v4.2
28
29
 
29
30
  - Support removed:
30
31
  - IE11 support removed
@@ -98,11 +98,14 @@ You can find a full list of available browsers with `--browsers help`, and you
98
98
  can find a complete list of testing options with `--help`.
99
99
 
100
100
 
101
- #### Join the announcement list
101
+ #### Announcements
102
102
 
103
- If you want to receive release or survey announcements, you should join our
103
+ To subscribe to new releases on GitHub, you can follow
104
+ [instructions from this blog](https://www.jessesquires.com/blog/2020/07/30/github-tip-watching-releases/).
105
+
106
+ To receive infrequent announcements and surveys from us, you can join our
104
107
  [mailing list](https://groups.google.com/forum/#!forum/shaka-player-users).
105
- The list is very low volume.
108
+ The list is very low volume, and can only be written to by us.
106
109
 
107
110
 
108
111
  #### Continue the Tutorials
package/karma.conf.js CHANGED
@@ -160,10 +160,6 @@ module.exports = (config) => {
160
160
  'node_modules/es6-promise-polyfill/promise.js',
161
161
  // Babel polyfill, required for async/await
162
162
  'node_modules/@babel/polyfill/dist/polyfill.js',
163
- // TextDecoder polyfill, required for TextDecoder/TextEncoder on IE and
164
- // legacy Edge
165
- // eslint-disable-next-line max-len
166
- 'node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js',
167
163
 
168
164
  // muxjs module next
169
165
  'node_modules/mux.js/dist/mux.min.js',
@@ -306,13 +302,6 @@ module.exports = (config) => {
306
302
  // Force failure when running empty test-suites.
307
303
  failOnEmptyTestSuite: true,
308
304
 
309
- coverageReporter: {
310
- includeAllSources: true,
311
- reporters: [
312
- {type: 'text'},
313
- ],
314
- },
315
-
316
305
  specReporter: {
317
306
  suppressSkipped: true,
318
307
  showBrowser: true,
@@ -405,9 +394,12 @@ module.exports = (config) => {
405
394
 
406
395
  config.set({
407
396
  coverageReporter: {
397
+ includeAllSources: true,
408
398
  reporters: [
409
399
  {type: 'html', dir: 'coverage'},
410
400
  {type: 'cobertura', dir: 'coverage', file: 'coverage.xml'},
401
+ {type: 'json-summary', dir: 'coverage', file: 'coverage.json'},
402
+ {type: 'json', dir: 'coverage', file: 'coverage-details.json'},
411
403
  ],
412
404
  },
413
405
  });
@@ -427,7 +427,7 @@ shaka.ads.ClientSideAdManager = class {
427
427
  onAdComplete_(e) {
428
428
  this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
429
429
  (new Map()).set('originalEvent', e)));
430
- if (this.ad_.isLinear()) {
430
+ if (this.ad_ && this.ad_.isLinear()) {
431
431
  this.adContainer_.removeAttribute('ad-active');
432
432
  this.video_.play();
433
433
  }
@@ -513,6 +513,11 @@ shaka.dash.DashParser = class {
513
513
  }
514
514
  presentationTimeline.setClockOffset(offset);
515
515
  }
516
+
517
+ // This is the first point where we have a meaningful presentation start
518
+ // time, and we need to tell PresentationTimeline that so that it can
519
+ // maintain consistency from here on.
520
+ presentationTimeline.lockStartTime();
516
521
  } else {
517
522
  // Just update the variants and text streams, which may change as periods
518
523
  // are added or removed.
@@ -740,8 +740,14 @@ shaka.hls.HlsParser = class {
740
740
  for (const stream of streamsToNotify) {
741
741
  this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
742
742
  }
743
+
743
744
  this.notifySegments_();
744
745
 
746
+ // This is the first point where we have a meaningful presentation start
747
+ // time, and we need to tell PresentationTimeline that so that it can
748
+ // maintain consistency from here on.
749
+ this.presentationTimeline_.lockStartTime();
750
+
745
751
  // This asserts that the live edge is being calculated from segment times.
746
752
  // For VOD and event streams, this check should still pass.
747
753
  goog.asserts.assert(
@@ -1859,8 +1865,6 @@ shaka.hls.HlsParser = class {
1859
1865
  /* presentationStartTime= */ null, /* delay= */ 0);
1860
1866
  this.presentationTimeline_.setStatic(true);
1861
1867
  }
1862
-
1863
- this.notifySegments_();
1864
1868
  }
1865
1869
 
1866
1870
  /**
@@ -1548,7 +1548,7 @@ shaka.media.DrmEngine = class {
1548
1548
  if (shaka.media.DrmEngine.isPlayReadyKeySystem(
1549
1549
  this.currentDrmInfo_.keySystem) &&
1550
1550
  keyId.byteLength == 16 &&
1551
- shaka.util.Platform.isEdge()) {
1551
+ (shaka.util.Platform.isEdge() || shaka.util.Platform.isPS4())) {
1552
1552
  // Read out some fields in little-endian:
1553
1553
  const dataView = shaka.util.BufferUtils.toDataView(keyId);
1554
1554
  const part0 = dataView.getUint32(0, /* LE= */ true);
@@ -10,6 +10,7 @@ goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
11
  goog.require('shaka.media.ContentWorkarounds');
12
12
  goog.require('shaka.media.IClosedCaptionParser');
13
+ goog.require('shaka.media.SegmentReference');
13
14
  goog.require('shaka.media.TimeRangesUtils');
14
15
  goog.require('shaka.media.Transmuxer');
15
16
  goog.require('shaka.text.TextEngine');
@@ -466,13 +467,13 @@ shaka.media.MediaSourceEngine = class {
466
467
  */
467
468
  getBufferedInfo() {
468
469
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
469
- const TimeRangeUtils = shaka.media.TimeRangesUtils;
470
+ const TimeRangesUtils = shaka.media.TimeRangesUtils;
470
471
 
471
472
  const info = {
472
- total: TimeRangeUtils.getBufferedInfo(this.video_.buffered),
473
- audio: TimeRangeUtils.getBufferedInfo(
473
+ total: TimeRangesUtils.getBufferedInfo(this.video_.buffered),
474
+ audio: TimeRangesUtils.getBufferedInfo(
474
475
  this.getBuffered_(ContentType.AUDIO)),
475
- video: TimeRangeUtils.getBufferedInfo(
476
+ video: TimeRangesUtils.getBufferedInfo(
476
477
  this.getBuffered_(ContentType.VIDEO)),
477
478
  text: [],
478
479
  };
@@ -516,15 +517,14 @@ shaka.media.MediaSourceEngine = class {
516
517
  *
517
518
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
518
519
  * @param {!BufferSource} data
519
- * @param {?number} startTime relative to the start of the presentation
520
- * @param {?number} endTime relative to the start of the presentation
520
+ * @param {?shaka.media.SegmentReference} reference The segment reference
521
+ * we are appending, or null for init segments
521
522
  * @param {?boolean} hasClosedCaptions True if the buffer contains CEA closed
522
- * captions
523
+ * captions
523
524
  * @param {boolean=} seeked True if we just seeked
524
525
  * @return {!Promise}
525
526
  */
526
- async appendBuffer(
527
- contentType, data, startTime, endTime, hasClosedCaptions, seeked) {
527
+ async appendBuffer(contentType, data, reference, hasClosedCaptions, seeked) {
528
528
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
529
529
 
530
530
  if (contentType == ContentType.TEXT) {
@@ -533,7 +533,10 @@ shaka.media.MediaSourceEngine = class {
533
533
  const offset = await this.textSequenceModeOffset_;
534
534
  this.textEngine_.setTimestampOffset(offset);
535
535
  }
536
- await this.textEngine_.appendBuffer(data, startTime, endTime);
536
+ await this.textEngine_.appendBuffer(
537
+ data,
538
+ reference ? reference.startTime : null,
539
+ reference ? reference.endTime : null);
537
540
  return;
538
541
  }
539
542
 
@@ -549,7 +552,10 @@ shaka.media.MediaSourceEngine = class {
549
552
  if (transmuxedData.metadata) {
550
553
  const timestampOffset =
551
554
  this.sourceBuffers_[contentType].timestampOffset;
552
- this.onMetadata_(transmuxedData.metadata, timestampOffset, endTime);
555
+ this.onMetadata_(
556
+ transmuxedData.metadata,
557
+ timestampOffset,
558
+ reference ? reference.endTime : null);
553
559
  }
554
560
  // This doesn't work for native TS support (ex. Edge/Chromecast),
555
561
  // since no transmuxing is needed for native TS.
@@ -559,7 +565,10 @@ shaka.media.MediaSourceEngine = class {
559
565
  const closedCaptions = this.textEngine_
560
566
  .convertMuxjsCaptionsToShakaCaptions(transmuxedData.captions);
561
567
  this.textEngine_.storeAndAppendClosedCaptions(
562
- closedCaptions, startTime, endTime, videoOffset);
568
+ closedCaptions,
569
+ reference ? reference.startTime : null,
570
+ reference ? reference.endTime : null,
571
+ videoOffset);
563
572
  }
564
573
 
565
574
  data = transmuxedData.data;
@@ -569,7 +578,7 @@ shaka.media.MediaSourceEngine = class {
569
578
  }
570
579
  // If it is the init segment for closed captions, initialize the closed
571
580
  // caption parser.
572
- if (startTime == null && endTime == null) {
581
+ if (!reference) {
573
582
  this.captionParser_.init(data);
574
583
  } else {
575
584
  const closedCaptions = this.captionParser_.parseFrom(data);
@@ -577,18 +586,21 @@ shaka.media.MediaSourceEngine = class {
577
586
  const videoOffset =
578
587
  this.sourceBuffers_[ContentType.VIDEO].timestampOffset;
579
588
  this.textEngine_.storeAndAppendClosedCaptions(
580
- closedCaptions, startTime, endTime, videoOffset);
589
+ closedCaptions,
590
+ reference.startTime,
591
+ reference.endTime,
592
+ videoOffset);
581
593
  }
582
594
  }
583
595
  }
584
596
 
585
- data = this.workAroundBrokenPlatforms_(data, startTime, contentType);
597
+ data = this.workAroundBrokenPlatforms_(
598
+ data, reference ? reference.startTime : null, contentType);
586
599
 
587
600
  const sourceBuffer = this.sourceBuffers_[contentType];
588
601
  const SEQUENCE = shaka.media.MediaSourceEngine.SourceBufferMode_.SEQUENCE;
589
602
 
590
- if (this.sequenceMode_ && sourceBuffer.mode != SEQUENCE &&
591
- startTime != null) {
603
+ if (this.sequenceMode_ && sourceBuffer.mode != SEQUENCE && reference) {
592
604
  // This is the first media segment to be appended to a SourceBuffer in
593
605
  // sequence mode. We set the mode late so that we can trick MediaSource
594
606
  // into extracting a timestamp for us to align text segments in sequence
@@ -623,7 +635,7 @@ shaka.media.MediaSourceEngine = class {
623
635
  // segments.
624
636
  const mediaStartTime = shaka.media.TimeRangesUtils.bufferStart(
625
637
  this.getBuffered_(contentType));
626
- const textOffset = (startTime || 0) - (mediaStartTime || 0);
638
+ const textOffset = (reference.startTime || 0) - (mediaStartTime || 0);
627
639
  this.textSequenceModeOffset_.resolve(textOffset);
628
640
 
629
641
  // Finally, clear the buffer.
@@ -636,22 +648,43 @@ shaka.media.MediaSourceEngine = class {
636
648
  sourceBuffer.mode = SEQUENCE;
637
649
  }
638
650
 
639
- if (startTime != null && this.sequenceMode_ &&
640
- contentType != ContentType.TEXT) {
651
+ if (reference && this.sequenceMode_ && contentType != ContentType.TEXT) {
641
652
  // In sequence mode, for non-text streams, if we just cleared the buffer
642
653
  // and are performing an unbuffered seek, we need to set a new
643
654
  // timestampOffset on the sourceBuffer.
644
655
  if (seeked) {
645
- const timestampOffset = /** @type {number} */ (startTime);
656
+ const timestampOffset = reference.startTime;
646
657
  this.enqueueOperation_(
647
658
  contentType,
648
659
  () => this.setTimestampOffset_(contentType, timestampOffset));
649
660
  }
650
661
  }
651
662
 
652
- await this.enqueueOperation_(
653
- contentType,
654
- () => this.append_(contentType, data));
663
+ let bufferedBefore = null;
664
+
665
+ await this.enqueueOperation_(contentType, () => {
666
+ if (goog.DEBUG && reference) {
667
+ bufferedBefore = this.getBuffered_(contentType);
668
+ }
669
+ this.append_(contentType, data);
670
+ });
671
+
672
+ if (goog.DEBUG && reference) {
673
+ const bufferedAfter = this.getBuffered_(contentType);
674
+ const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
675
+ bufferedBefore, bufferedAfter);
676
+ if (newBuffered) {
677
+ const segmentDuration = reference.endTime - reference.startTime;
678
+ if (Math.abs(newBuffered.start - reference.startTime) >
679
+ segmentDuration / 2) {
680
+ shaka.log.error('Possible encoding problem detected!',
681
+ 'Unexpected buffered range for reference', reference,
682
+ 'from URIs', reference.getUris(),
683
+ 'should be', {start: reference.startTime, end: reference.endTime},
684
+ 'but got', newBuffered);
685
+ }
686
+ }
687
+ }
655
688
  }
656
689
 
657
690
  /**
@@ -93,6 +93,9 @@ shaka.media.PresentationTimeline = class {
93
93
  * @private {number}
94
94
  */
95
95
  this.availabilityTimeOffset_ = 0;
96
+
97
+ /** @private {boolean} */
98
+ this.startTimeLocked_ = false;
96
99
  }
97
100
 
98
101
 
@@ -229,7 +232,8 @@ shaka.media.PresentationTimeline = class {
229
232
  this.maxSegmentEndTime_ =
230
233
  Math.max(this.maxSegmentEndTime_, lastReferenceEndTime);
231
234
 
232
- if (this.presentationStartTime_ != null && this.autoCorrectDrift_) {
235
+ if (this.presentationStartTime_ != null && this.autoCorrectDrift_ &&
236
+ !this.startTimeLocked_) {
233
237
  // Since we have explicit segment end times, calculate a presentation
234
238
  // start based on them. This start time accounts for drift.
235
239
  // Date.now() is in milliseconds, from which we compute "now" in seconds.
@@ -243,6 +247,25 @@ shaka.media.PresentationTimeline = class {
243
247
  }
244
248
 
245
249
 
250
+ /**
251
+ * Lock the presentation timeline's start time. After this is called, no
252
+ * further adjustments to presentationStartTime_ will be permitted.
253
+ *
254
+ * This should be called after all Periods have been parsed, and all calls to
255
+ * notifySegments() from the initial manifest parse have been made.
256
+ *
257
+ * Without this, we can get assertion failures in SegmentIndex for certain
258
+ * DAI content. If DAI adds ad segments to the manifest faster than
259
+ * real-time, adjustments to presentationStartTime_ can cause availability
260
+ * windows to jump around on updates.
261
+ *
262
+ * @export
263
+ */
264
+ lockStartTime() {
265
+ this.startTimeLocked_ = true;
266
+ }
267
+
268
+
246
269
  /**
247
270
  * Gives PresentationTimeline a Stream's minimum segment start time.
248
271
  *
@@ -1555,8 +1555,8 @@ shaka.media.StreamingEngine = class {
1555
1555
  const hasClosedCaptions = mediaState.stream.closedCaptions &&
1556
1556
  mediaState.stream.closedCaptions.size > 0;
1557
1557
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1558
- mediaState.type, initSegment, /* startTime= */ null,
1559
- /* endTime= */ null, hasClosedCaptions);
1558
+ mediaState.type, initSegment, /* reference= */ null,
1559
+ hasClosedCaptions);
1560
1560
  } catch (error) {
1561
1561
  mediaState.lastInitSegmentReference = null;
1562
1562
  throw error;
@@ -1609,8 +1609,7 @@ shaka.media.StreamingEngine = class {
1609
1609
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1610
1610
  mediaState.type,
1611
1611
  segment,
1612
- reference.startTime,
1613
- reference.endTime,
1612
+ reference,
1614
1613
  hasClosedCaptions,
1615
1614
  seeked);
1616
1615
  this.destroyer_.ensureNotDestroyed();
@@ -1791,6 +1790,7 @@ shaka.media.StreamingEngine = class {
1791
1790
  *
1792
1791
  * @return {!Promise.<BufferSource>}
1793
1792
  * @private
1793
+ * @suppress {strictMissingProperties}
1794
1794
  */
1795
1795
  async fetch_(mediaState, reference, streamDataCallback) {
1796
1796
  const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
@@ -155,4 +155,50 @@ shaka.media.TimeRangesUtils = class {
155
155
  }
156
156
  return ret;
157
157
  }
158
+
159
+ /**
160
+ * This operation can be potentially EXPENSIVE and should only be done in
161
+ * debug builds for debugging purposes.
162
+ *
163
+ * @param {TimeRanges} oldRanges
164
+ * @param {TimeRanges} newRanges
165
+ * @return {?shaka.extern.BufferedRange} The last added range,
166
+ * chronologically by presentation time.
167
+ */
168
+ static computeAddedRange(oldRanges, newRanges) {
169
+ const TimeRangesUtils = shaka.media.TimeRangesUtils;
170
+
171
+ if (!oldRanges || !oldRanges.length) {
172
+ return null;
173
+ }
174
+ if (!newRanges || !newRanges.length) {
175
+ return TimeRangesUtils.getBufferedInfo(newRanges).pop();
176
+ }
177
+
178
+ const newRangesReversed =
179
+ TimeRangesUtils.getBufferedInfo(newRanges).reverse();
180
+ const oldRangesReversed =
181
+ TimeRangesUtils.getBufferedInfo(oldRanges).reverse();
182
+ for (const newRange of newRangesReversed) {
183
+ let foundOverlap = false;
184
+
185
+ for (const oldRange of oldRangesReversed) {
186
+ if (oldRange.end >= newRange.start && oldRange.end <= newRange.end) {
187
+ foundOverlap = true;
188
+
189
+ // If the new range goes beyond the corresponding old one, the
190
+ // difference is newly-added.
191
+ if (newRange.end > oldRange.end) {
192
+ return {start: oldRange.end, end: newRange.end};
193
+ }
194
+ }
195
+ }
196
+
197
+ if (!foundOverlap) {
198
+ return newRange;
199
+ }
200
+ }
201
+
202
+ return null;
203
+ }
158
204
  };
package/lib/player.js CHANGED
@@ -857,14 +857,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
857
857
  if (!window.Promise) {
858
858
  shaka.log.alwaysWarn('A Promise implementation or polyfill is required');
859
859
  }
860
- if (!window.TextDecoder || !window.TextEncoder) {
861
- shaka.log.alwaysWarn(
862
- 'A TextDecoder/TextEncoder implementation or polyfill is required');
863
- }
864
860
 
865
861
  // Basic features needed for the library to be usable.
866
862
  const basicSupport = !!window.Promise && !!window.Uint8Array &&
867
- !!window.TextDecoder && !!window.TextEncoder &&
868
863
  // eslint-disable-next-line no-restricted-syntax
869
864
  !!Array.prototype.forEach;
870
865
  if (!basicSupport) {
@@ -4457,6 +4452,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4457
4452
  this.stats_.setBandwidthEstimate(estimate);
4458
4453
  }
4459
4454
 
4455
+ if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
4456
+ this.stats_.setResolution(
4457
+ /* width= */ element.videoWidth || NaN,
4458
+ /* height= */ element.videoHeight || NaN);
4459
+ }
4460
+
4460
4461
  return this.stats_.getBlob();
4461
4462
  }
4462
4463
 
@@ -5623,6 +5624,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5623
5624
  return false;
5624
5625
  }
5625
5626
 
5627
+ if (!navigator.onLine) {
5628
+ // Don't restrict variants if we're completely offline, or else we end up
5629
+ // rapidly restricting all of them.
5630
+ return false;
5631
+ }
5632
+
5626
5633
  let maxDisabledTime = this.config_.streaming.maxDisabledTime;
5627
5634
  if (maxDisabledTime == 0) {
5628
5635
  if (error.code == shaka.util.Error.Code.SEGMENT_MISSING) {
@@ -6556,7 +6563,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6556
6563
  * @define {string} A version number taken from git at compile time.
6557
6564
  * @export
6558
6565
  */
6559
- shaka.Player.version = 'v4.1.1-uncompiled';
6566
+ shaka.Player.version = 'v4.1.4-uncompiled';
6560
6567
 
6561
6568
  // Initialize the deprecation system using the version string we just set
6562
6569
  // on the player.
@@ -37,9 +37,18 @@ shaka.polyfill.MediaCapabilities = class {
37
37
  // See: https://github.com/shaka-project/shaka-player/issues/3582
38
38
  // TODO: re-evaluate MediaCapabilities in the future versions of PS5
39
39
  // Browsers.
40
+ // Since MediaCapabilities implementation does not exist in PS4 browsers, we
41
+ // should always install polyfill.
42
+ // Since MediaCapabilities implementation is buggy in Tizen browsers, we
43
+ // should always install polyfill for Tizen browsers.
44
+ // Since MediaCapabilities implementation is buggy in WebOS browsers, we
45
+ // should always install polyfill for WebOS browsers.
40
46
  let canUseNativeMCap = true;
41
47
  if (shaka.util.Platform.isApple() ||
42
48
  shaka.util.Platform.isPS5() ||
49
+ shaka.util.Platform.isPS4() ||
50
+ shaka.util.Platform.isWebOS() ||
51
+ shaka.util.Platform.isTizen() ||
43
52
  shaka.util.Platform.isChromecast()) {
44
53
  canUseNativeMCap = false;
45
54
  }
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.polyfill.VideoPlaybackQuality');
8
8
 
9
9
  goog.require('shaka.polyfill');
10
+ goog.require('shaka.util.Platform');
10
11
 
11
12
 
12
13
  /**
@@ -33,7 +34,8 @@ shaka.polyfill.VideoPlaybackQuality = class {
33
34
  return;
34
35
  }
35
36
 
36
- if ('webkitDroppedFrameCount' in proto) {
37
+ if ('webkitDroppedFrameCount' in proto ||
38
+ shaka.util.Platform.isWebOS3()) {
37
39
  proto.getVideoPlaybackQuality =
38
40
  shaka.polyfill.VideoPlaybackQuality.webkit_;
39
41
  }
package/lib/text/cue.js CHANGED
@@ -41,14 +41,9 @@ shaka.text.Cue = class {
41
41
  */
42
42
  this.endTime = endTime;
43
43
 
44
- // The explicit type here is to work around an apparent compiler bug where
45
- // the compiler seems to get confused about the type. Since we are
46
- // overriding the interface type, it shouldn't be necessary to do this.
47
- // Compiler version 20200517.
48
44
  /**
49
45
  * @override
50
46
  * @exportInterface
51
- * @type {string}
52
47
  */
53
48
  this.payload = payload;
54
49
 
@@ -121,16 +121,28 @@ shaka.text.SimpleTextDisplayer = class {
121
121
  // We don't want to modify the array or objects passed in, since we don't
122
122
  // technically own them. So we build a new array and replace certain items
123
123
  // in it if they need to be flattened.
124
- const flattenedCues = cues.map((cue) => {
125
- if (cue.nestedCues.length) {
126
- const flatCue = cue.clone();
127
- flatCue.nestedCues = [];
128
- flatCue.payload = flattenPayload(cue);
129
- return flatCue;
130
- } else {
131
- return cue;
124
+ // We also don't want to flatten the text payloads starting at a container
125
+ // element; otherwise, for containers encapsulating multiple caption lines,
126
+ // the lines would merge into a single cue. This is undesirable when a
127
+ // subset of the captions are outside of the append time window. To fix
128
+ // this, we only call flattenPayload() starting at elements marked as
129
+ // isContainer = false.
130
+ const getCuesToFlatten = (cues, result) => {
131
+ for (const cue of cues) {
132
+ if (cue.isContainer) {
133
+ // Recurse to find the actual text payload cues.
134
+ getCuesToFlatten(cue.nestedCues, result);
135
+ } else {
136
+ // Flatten the payload.
137
+ const flatCue = cue.clone();
138
+ flatCue.nestedCues = [];
139
+ flatCue.payload = flattenPayload(cue);
140
+ result.push(flatCue);
141
+ }
132
142
  }
133
- });
143
+ return result;
144
+ };
145
+ const flattenedCues = getCuesToFlatten(cues, []);
134
146
 
135
147
  // Convert cues.
136
148
  const textTrackCues = [];