shaka-player 4.0.3 → 4.0.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 (129) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/CONTRIBUTORS +2 -0
  3. package/README.md +20 -6
  4. package/demo/index.html +0 -2
  5. package/dist/controls.css +1 -1
  6. package/dist/deps.js +3 -3
  7. package/dist/locales.js +33 -33
  8. package/dist/shaka-player.compiled.d.ts +11 -0
  9. package/dist/shaka-player.compiled.debug.d.ts +11 -0
  10. package/dist/shaka-player.compiled.debug.externs.js +48 -37
  11. package/dist/shaka-player.compiled.debug.js +141 -137
  12. package/dist/shaka-player.compiled.debug.map +4 -4
  13. package/dist/shaka-player.compiled.externs.js +48 -37
  14. package/dist/shaka-player.compiled.js +774 -772
  15. package/dist/shaka-player.compiled.map +4 -4
  16. package/dist/shaka-player.ui.d.ts +11 -0
  17. package/dist/shaka-player.ui.debug.d.ts +11 -0
  18. package/dist/shaka-player.ui.debug.externs.js +48 -37
  19. package/dist/shaka-player.ui.debug.js +153 -149
  20. package/dist/shaka-player.ui.debug.map +4 -4
  21. package/dist/shaka-player.ui.externs.js +48 -37
  22. package/dist/shaka-player.ui.js +892 -890
  23. package/dist/shaka-player.ui.map +4 -4
  24. package/docs/tutorials/upgrade.md +1 -0
  25. package/docs/tutorials/welcome.md +6 -3
  26. package/lib/dash/dash_parser.js +5 -0
  27. package/lib/hls/hls_parser.js +6 -2
  28. package/lib/media/media_source_engine.js +57 -24
  29. package/lib/media/presentation_timeline.js +24 -1
  30. package/lib/media/streaming_engine.js +3 -4
  31. package/lib/media/time_ranges_utils.js +46 -0
  32. package/lib/player.js +1 -1
  33. package/lib/polyfill/media_capabilities.js +3 -0
  34. package/lib/polyfill/videoplaybackquality.js +3 -1
  35. package/lib/util/platform.js +19 -1
  36. package/lib/util/string_utils.js +56 -14
  37. package/maintained-branches.md +15 -0
  38. package/package.json +2 -4
  39. package/test/media/drm_engine_integration.js +24 -8
  40. package/test/media/media_source_engine_integration.js +24 -10
  41. package/test/media/media_source_engine_unit.js +53 -38
  42. package/test/media/streaming_engine_unit.js +28 -27
  43. package/test/test/util/fake_media_source_engine.js +10 -9
  44. package/app-engine/README.md +0 -9
  45. package/app-engine/demo-version-index/README.md +0 -14
  46. package/app-engine/demo-version-index/app.yaml +0 -28
  47. package/app-engine/demo-version-index/generate.py +0 -164
  48. package/app-engine/demo-version-index/main.py +0 -11
  49. package/app-engine/demo-version-index/requirements.txt +0 -5
  50. package/app-engine/demo-version-index/templates/index.html +0 -114
  51. package/app-engine/shaka-player-demo/README.md +0 -18
  52. package/app-engine/shaka-player-demo/app.yaml +0 -61
  53. package/app-engine/shaka-player-demo/main.py +0 -95
  54. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  55. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  56. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  57. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  58. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  59. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  60. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  61. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  62. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  63. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  64. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  65. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  66. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  67. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  68. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  69. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  70. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  71. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  72. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  73. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  74. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  75. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  76. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  77. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  78. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  79. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  80. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  81. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  82. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  83. package/app-engine/shaka-player-demo/posters.py +0 -110
  84. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  85. package/app-engine/shaka-player-demo/time.txt +0 -1
  86. package/build/README.md +0 -127
  87. package/build/__init__.py +0 -0
  88. package/build/all.py +0 -150
  89. package/build/apps.py +0 -186
  90. package/build/build.py +0 -392
  91. package/build/check.py +0 -354
  92. package/build/checkversion.py +0 -84
  93. package/build/compiler.py +0 -513
  94. package/build/conformance.textproto +0 -335
  95. package/build/docker/Dockerfile +0 -20
  96. package/build/docker/docker-compose.yml +0 -10
  97. package/build/docs.py +0 -54
  98. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  99. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  100. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  101. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  102. package/build/gendeps.py +0 -71
  103. package/build/generateExterns.js +0 -867
  104. package/build/generateLocalizations.py +0 -262
  105. package/build/generateTsDefs.py +0 -137
  106. package/build/install-linux-prereqs.sh +0 -136
  107. package/build/license-header +0 -5
  108. package/build/misspellings.txt +0 -39
  109. package/build/pixelsChanged.js +0 -35
  110. package/build/shaka-lab.yaml +0 -161
  111. package/build/shakaBuildHelpers.py +0 -356
  112. package/build/stats.py +0 -816
  113. package/build/subprocessWindowsPatch.py +0 -103
  114. package/build/test.py +0 -541
  115. package/build/types/ads +0 -8
  116. package/build/types/cast +0 -6
  117. package/build/types/cea +0 -13
  118. package/build/types/complete +0 -9
  119. package/build/types/core +0 -108
  120. package/build/types/dash +0 -8
  121. package/build/types/hls +0 -9
  122. package/build/types/manifests +0 -5
  123. package/build/types/networking +0 -6
  124. package/build/types/offline +0 -22
  125. package/build/types/polyfill +0 -18
  126. package/build/types/text +0 -11
  127. package/build/types/ui +0 -39
  128. package/build/updateScreenshots.py +0 -85
  129. package/build/wrapper.template.js +0 -52
@@ -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
@@ -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.
@@ -739,8 +739,14 @@ shaka.hls.HlsParser = class {
739
739
  for (const stream of streamsToNotify) {
740
740
  this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
741
741
  }
742
+
742
743
  this.notifySegments_();
743
744
 
745
+ // This is the first point where we have a meaningful presentation start
746
+ // time, and we need to tell PresentationTimeline that so that it can
747
+ // maintain consistency from here on.
748
+ this.presentationTimeline_.lockStartTime();
749
+
744
750
  // This asserts that the live edge is being calculated from segment times.
745
751
  // For VOD and event streams, this check should still pass.
746
752
  goog.asserts.assert(
@@ -1858,8 +1864,6 @@ shaka.hls.HlsParser = class {
1858
1864
  /* presentationStartTime= */ null, /* delay= */ 0);
1859
1865
  this.presentationTimeline_.setStatic(true);
1860
1866
  }
1861
-
1862
- this.notifySegments_();
1863
1867
  }
1864
1868
 
1865
1869
  /**
@@ -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
  *
@@ -1548,8 +1548,8 @@ shaka.media.StreamingEngine = class {
1548
1548
  const hasClosedCaptions = mediaState.stream.closedCaptions &&
1549
1549
  mediaState.stream.closedCaptions.size > 0;
1550
1550
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1551
- mediaState.type, initSegment, /* startTime= */ null,
1552
- /* endTime= */ null, hasClosedCaptions);
1551
+ mediaState.type, initSegment, /* reference= */ null,
1552
+ hasClosedCaptions);
1553
1553
  } catch (error) {
1554
1554
  mediaState.lastInitSegmentReference = null;
1555
1555
  throw error;
@@ -1602,8 +1602,7 @@ shaka.media.StreamingEngine = class {
1602
1602
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1603
1603
  mediaState.type,
1604
1604
  segment,
1605
- reference.startTime,
1606
- reference.endTime,
1605
+ reference,
1607
1606
  hasClosedCaptions,
1608
1607
  seeked);
1609
1608
  this.destroyer_.ensureNotDestroyed();
@@ -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
@@ -6450,7 +6450,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6450
6450
  * @define {string} A version number taken from git at compile time.
6451
6451
  * @export
6452
6452
  */
6453
- shaka.Player.version = 'v4.0.3-uncompiled';
6453
+ shaka.Player.version = 'v4.0.4-uncompiled';
6454
6454
 
6455
6455
  // Initialize the deprecation system using the version string we just set
6456
6456
  // on the player.
@@ -37,9 +37,12 @@ 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.
40
42
  let canUseNativeMCap = true;
41
43
  if (shaka.util.Platform.isApple() ||
42
44
  shaka.util.Platform.isPS5() ||
45
+ shaka.util.Platform.isPS4() ||
43
46
  shaka.util.Platform.isChromecast()) {
44
47
  canUseNativeMCap = false;
45
48
  }
@@ -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
  }
@@ -197,7 +197,9 @@ shaka.util.Platform = class {
197
197
  */
198
198
  static isApple() {
199
199
  return !!navigator.vendor && navigator.vendor.includes('Apple') &&
200
- !shaka.util.Platform.isTizen();
200
+ !shaka.util.Platform.isTizen() &&
201
+ !shaka.util.Platform.isEOS() &&
202
+ !shaka.util.Platform.isPS4();
201
203
  }
202
204
 
203
205
  /**
@@ -213,6 +215,13 @@ shaka.util.Platform = class {
213
215
  return shaka.util.Platform.userAgentContains_('PlayStation 5');
214
216
  }
215
217
 
218
+ /**
219
+ * Check if the current platform is Playstation 4.
220
+ */
221
+ static isPS4() {
222
+ return shaka.util.Platform.userAgentContains_('PlayStation 4');
223
+ }
224
+
216
225
  /**
217
226
  * Returns a major version number for Safari, or Safari-based iOS browsers.
218
227
  *
@@ -260,6 +269,15 @@ shaka.util.Platform = class {
260
269
  return !!shaka.util.Platform.safariVersion();
261
270
  }
262
271
 
272
+ /**
273
+ * Check if the current platform is an EOS set-top box.
274
+ *
275
+ * @return {boolean}
276
+ */
277
+ static isEOS() {
278
+ return shaka.util.Platform.userAgentContains_('PC=EOS');
279
+ }
280
+
263
281
  /**
264
282
  * Guesses if the platform is a mobile one (iOS or Android).
265
283
  *
@@ -11,6 +11,7 @@ goog.require('shaka.log');
11
11
  goog.require('shaka.util.BufferUtils');
12
12
  goog.require('shaka.util.Error');
13
13
  goog.require('shaka.util.Lazy');
14
+ goog.require('shaka.util.Platform');
14
15
 
15
16
 
16
17
  /**
@@ -36,19 +37,38 @@ shaka.util.StringUtils = class {
36
37
  if (uint8[0] == 0xef && uint8[1] == 0xbb && uint8[2] == 0xbf) {
37
38
  uint8 = uint8.subarray(3);
38
39
  }
39
-
40
- // Use the TextDecoder interface to decode the text. This has the advantage
41
- // compared to the previously-standard decodeUriComponent that it will
42
- // continue parsing even if it finds an invalid UTF8 character, rather than
43
- // stop and throw an error.
44
- const utf8decoder = new TextDecoder();
45
- const decoded = utf8decoder.decode(uint8);
46
- if (decoded.includes('\uFFFD')) {
47
- shaka.log.alwaysError('Decoded string contains an "unknown character" ' +
48
- 'codepoint. That probably means the UTF8 ' +
49
- 'encoding was incorrect!');
40
+ if (window.TextDecoder && !shaka.util.Platform.isPS4()) {
41
+ // Use the TextDecoder interface to decode the text. This has the
42
+ // advantage compared to the previously-standard decodeUriComponent that
43
+ // it will continue parsing even if it finds an invalid UTF8 character,
44
+ // rather than stop and throw an error.
45
+ const utf8decoder = new TextDecoder();
46
+ const decoded = utf8decoder.decode(uint8);
47
+ if (decoded.includes('\uFFFD')) {
48
+ shaka.log.alwaysError('Decoded string contains an "unknown character' +
49
+ '" codepoint. That probably means the UTF8 ' +
50
+ 'encoding was incorrect!');
51
+ }
52
+ return decoded;
53
+ } else {
54
+ // http://stackoverflow.com/a/13691499
55
+ const utf8 = shaka.util.StringUtils.fromCharCode(uint8);
56
+ // This converts each character in the string to an escape sequence. If
57
+ // the character is in the ASCII range, it is not converted; otherwise it
58
+ // is converted to a URI escape sequence.
59
+ // Example: '\x67\x35\xe3\x82\xac' -> 'g#%E3%82%AC'
60
+ const escaped = escape(utf8);
61
+ // Decode the escaped sequence. This will interpret UTF-8 sequences into
62
+ // the correct character.
63
+ // Example: 'g#%E3%82%AC' -> 'g#€'
64
+ try {
65
+ return decodeURIComponent(escaped);
66
+ } catch (e) {
67
+ throw new shaka.util.Error(
68
+ shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.TEXT,
69
+ shaka.util.Error.Code.BAD_ENCODING);
70
+ }
50
71
  }
51
- return decoded;
52
72
  }
53
73
 
54
74
 
@@ -141,8 +161,30 @@ shaka.util.StringUtils = class {
141
161
  * @export
142
162
  */
143
163
  static toUTF8(str) {
144
- const utf8Encoder = new TextEncoder();
145
- return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
164
+ if (window.TextEncoder && !shaka.util.Platform.isPS4()) {
165
+ const utf8Encoder = new TextEncoder();
166
+ return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
167
+ } else {
168
+ // http://stackoverflow.com/a/13691499
169
+ // Converts the given string to a URI encoded string. If a character
170
+ // falls in the ASCII range, it is not converted; otherwise it will be
171
+ // converted to a series of URI escape sequences according to UTF-8.
172
+ // Example: 'g#€' -> 'g#%E3%82%AC'
173
+ const encoded = encodeURIComponent(str);
174
+ // Convert each escape sequence individually into a character. Each
175
+ // escape sequence is interpreted as a code-point, so if an escape
176
+ // sequence happens to be part of a multi-byte sequence, each byte will
177
+ // be converted to a single character.
178
+ // Example: 'g#%E3%82%AC' -> '\x67\x35\xe3\x82\xac'
179
+ const utf8 = unescape(encoded);
180
+
181
+ const result = new Uint8Array(utf8.length);
182
+ for (let i = 0; i < utf8.length; i++) {
183
+ const item = utf8[i];
184
+ result[i] = item.charCodeAt(0);
185
+ }
186
+ return shaka.util.BufferUtils.toArrayBuffer(result);
187
+ }
146
188
  }
147
189
 
148
190
 
@@ -0,0 +1,15 @@
1
+ # Maintained Shaka Player Branches
2
+
3
+ We are currently maintaining the following branches of Shaka Player:
4
+
5
+ - v4.1 (latest)
6
+ - v4.0 (previous)
7
+ - v3.3 (LTS until April 30, 2023)
8
+ - v3.2 (in use by the Cast Application Framework)
9
+
10
+ Other branches are no longer receiving bug fixes, and we recommend you upgrade
11
+ to a maintained branch. For details on upgrading, please see our
12
+ [upgrade guide](https://shaka-player-demo.appspot.com/docs/api/tutorial-upgrade.html).
13
+
14
+ For more information on how these branches are chosen, please refer to our
15
+ [Branch Maintenance Policy](https://shaka-project.github.io/maintenance/shaka-player.html#branch-maintenance-policy).
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.0.3",
4
+ "version": "4.0.4",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -27,7 +27,6 @@
27
27
  "eslint-config-google": "^0.14.0",
28
28
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
29
29
  "esprima": "^4.0.1",
30
- "fastestsmallesttextencoderdecoder": "^1.0.22",
31
30
  "fontfaceonload": "^1.0.2",
32
31
  "google-closure-compiler-java": "^20220301.0.0",
33
32
  "google-closure-deps": "https://gitpkg.now.sh/google/closure-library/closure-deps?d7736da6",
@@ -50,7 +49,7 @@
50
49
  "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
51
50
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
52
51
  "material-design-lite": "^1.3.0",
53
- "mux.js": "^6.1.0",
52
+ "mux.js": "^6.2.0",
54
53
  "open-sans-fonts": "^1.6.2",
55
54
  "postcss-less": "^6.0.0",
56
55
  "pwacompat": "^2.0.17",
@@ -71,7 +70,6 @@
71
70
  "dialog-polyfill/dist/dialog-polyfill.js",
72
71
  "eme-encryption-scheme-polyfill/index.js",
73
72
  "es6-promise-polyfill/promise.min.js",
74
- "fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js",
75
73
  "google-closure-library/closure/goog/base.js",
76
74
  "less/dist/less.js",
77
75
  "material-design-lite/dist/material.indigo-blue.min.css",
@@ -209,10 +209,10 @@ describe('DrmEngine', () => {
209
209
  await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
210
210
  await drmEngine.attach(video);
211
211
  await mediaSourceEngine.appendBuffer(
212
- ContentType.VIDEO, videoInitSegment, null, null,
212
+ ContentType.VIDEO, videoInitSegment, null,
213
213
  /* hasClosedCaptions= */ false);
214
214
  await mediaSourceEngine.appendBuffer(
215
- ContentType.AUDIO, audioInitSegment, null, null,
215
+ ContentType.AUDIO, audioInitSegment, null,
216
216
  /* hasClosedCaptions= */ false);
217
217
  await encryptedEventSeen;
218
218
  // With PlayReady, a persistent license policy can cause a different
@@ -245,11 +245,13 @@ describe('DrmEngine', () => {
245
245
  }
246
246
  }
247
247
 
248
+ const reference = dummyReference(0, 10);
249
+
248
250
  await mediaSourceEngine.appendBuffer(
249
- ContentType.VIDEO, videoSegment, 0, 10,
251
+ ContentType.VIDEO, videoSegment, reference,
250
252
  /* hasClosedCaptions= */ false);
251
253
  await mediaSourceEngine.appendBuffer(
252
- ContentType.AUDIO, audioSegment, 0, 10,
254
+ ContentType.AUDIO, audioSegment, reference,
253
255
  /* hasClosedCaptions= */ false);
254
256
 
255
257
  expect(video.buffered.end(0)).toBeGreaterThan(0);
@@ -305,10 +307,10 @@ describe('DrmEngine', () => {
305
307
  await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
306
308
  await drmEngine.attach(video);
307
309
  await mediaSourceEngine.appendBuffer(
308
- ContentType.VIDEO, videoInitSegment, null, null,
310
+ ContentType.VIDEO, videoInitSegment, null,
309
311
  /* hasClosedCaptions= */ false);
310
312
  await mediaSourceEngine.appendBuffer(
311
- ContentType.AUDIO, audioInitSegment, null, null,
313
+ ContentType.AUDIO, audioInitSegment, null,
312
314
  /* hasClosedCaptions= */ false);
313
315
  await encryptedEventSeen;
314
316
 
@@ -326,11 +328,13 @@ describe('DrmEngine', () => {
326
328
  }
327
329
  }
328
330
 
331
+ const reference = dummyReference(0, 10);
332
+
329
333
  await mediaSourceEngine.appendBuffer(
330
- ContentType.VIDEO, videoSegment, 0, 10,
334
+ ContentType.VIDEO, videoSegment, reference,
331
335
  /* hasClosedCaptions= */ false);
332
336
  await mediaSourceEngine.appendBuffer(
333
- ContentType.AUDIO, audioSegment, 0, 10,
337
+ ContentType.AUDIO, audioSegment, reference,
334
338
  /* hasClosedCaptions= */ false);
335
339
 
336
340
  expect(video.buffered.end(0)).toBeGreaterThan(0);
@@ -345,4 +349,16 @@ describe('DrmEngine', () => {
345
349
  expect(video.currentTime).toBeGreaterThan(0);
346
350
  });
347
351
  }); // describe('ClearKey')
352
+
353
+ function dummyReference(startTime, endTime) {
354
+ return new shaka.media.SegmentReference(
355
+ startTime, endTime,
356
+ /* uris= */ () => ['foo://bar'],
357
+ /* startByte= */ 0,
358
+ /* endByte= */ null,
359
+ /* initSegmentReference= */ null,
360
+ /* timestampOffset= */ 0,
361
+ /* appendWindowStart= */ 0,
362
+ /* appendWindowEnd= */ Infinity);
363
+ }
348
364
  });