shaka-player 4.0.1 → 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 (157) hide show
  1. package/AUTHORS +1 -0
  2. package/CHANGELOG.md +37 -0
  3. package/CONTRIBUTORS +3 -0
  4. package/README.md +20 -6
  5. package/demo/common/assets.js +1 -1
  6. package/demo/demo.less +0 -1
  7. package/demo/index.html +0 -2
  8. package/demo/main.js +3 -3
  9. package/dist/controls.css +3 -3
  10. package/dist/controls.css.map +1 -1
  11. package/dist/demo.compiled.debug.js +3 -3
  12. package/dist/demo.compiled.debug.map +2 -2
  13. package/dist/demo.compiled.js +3 -3
  14. package/dist/demo.compiled.map +2 -2
  15. package/dist/demo.css +1 -1
  16. package/dist/demo.css.map +1 -1
  17. package/dist/deps.js +4 -4
  18. package/dist/locales.js +34 -34
  19. package/dist/receiver.compiled.debug.js +1 -1
  20. package/dist/receiver.compiled.debug.map +1 -1
  21. package/dist/receiver.compiled.js +1 -1
  22. package/dist/receiver.compiled.map +1 -1
  23. package/dist/shaka-player.compiled.d.ts +11 -0
  24. package/dist/shaka-player.compiled.debug.d.ts +11 -0
  25. package/dist/shaka-player.compiled.debug.externs.js +51 -39
  26. package/dist/shaka-player.compiled.debug.js +247 -237
  27. package/dist/shaka-player.compiled.debug.map +4 -4
  28. package/dist/shaka-player.compiled.externs.js +51 -39
  29. package/dist/shaka-player.compiled.js +799 -792
  30. package/dist/shaka-player.compiled.map +4 -4
  31. package/dist/shaka-player.ui.d.ts +11 -0
  32. package/dist/shaka-player.ui.debug.d.ts +11 -0
  33. package/dist/shaka-player.ui.debug.externs.js +51 -39
  34. package/dist/shaka-player.ui.debug.js +273 -262
  35. package/dist/shaka-player.ui.debug.map +4 -4
  36. package/dist/shaka-player.ui.externs.js +51 -39
  37. package/dist/shaka-player.ui.js +957 -952
  38. package/dist/shaka-player.ui.map +4 -4
  39. package/docs/tutorials/upgrade.md +1 -0
  40. package/docs/tutorials/welcome.md +6 -3
  41. package/externs/shaka/player.js +4 -2
  42. package/lib/abr/simple_abr_manager.js +2 -1
  43. package/lib/dash/dash_parser.js +5 -0
  44. package/lib/hls/hls_parser.js +197 -73
  45. package/lib/media/drm_engine.js +3 -0
  46. package/lib/media/media_source_engine.js +57 -24
  47. package/lib/media/presentation_timeline.js +24 -1
  48. package/lib/media/segment_index.js +26 -1
  49. package/lib/media/segment_reference.js +10 -3
  50. package/lib/media/streaming_engine.js +3 -4
  51. package/lib/media/time_ranges_utils.js +46 -0
  52. package/lib/player.js +1 -1
  53. package/lib/polyfill/media_capabilities.js +3 -0
  54. package/lib/polyfill/videoplaybackquality.js +3 -1
  55. package/lib/util/mime_utils.js +14 -5
  56. package/lib/util/platform.js +19 -1
  57. package/lib/util/stream_utils.js +1 -0
  58. package/lib/util/string_utils.js +56 -14
  59. package/maintained-branches.md +15 -0
  60. package/package.json +3 -5
  61. package/test/hls/hls_live_unit.js +57 -7
  62. package/test/hls/hls_parser_unit.js +102 -8
  63. package/test/media/drm_engine_integration.js +24 -8
  64. package/test/media/media_source_engine_integration.js +24 -10
  65. package/test/media/media_source_engine_unit.js +53 -38
  66. package/test/media/streaming_engine_unit.js +28 -27
  67. package/test/test/util/fake_media_source_engine.js +10 -9
  68. package/ui/less/containers.less +1 -1
  69. package/ui/less/general.less +1 -1
  70. package/ui/less/range_elements.less +1 -0
  71. package/.github/pull_request_template.md +0 -10
  72. package/app-engine/README.md +0 -9
  73. package/app-engine/demo-version-index/README.md +0 -14
  74. package/app-engine/demo-version-index/app.yaml +0 -28
  75. package/app-engine/demo-version-index/generate.py +0 -164
  76. package/app-engine/demo-version-index/main.py +0 -11
  77. package/app-engine/demo-version-index/requirements.txt +0 -5
  78. package/app-engine/demo-version-index/templates/index.html +0 -114
  79. package/app-engine/shaka-player-demo/README.md +0 -18
  80. package/app-engine/shaka-player-demo/app.yaml +0 -61
  81. package/app-engine/shaka-player-demo/main.py +0 -95
  82. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  83. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  84. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  85. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  86. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  87. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  88. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  89. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  90. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  91. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  92. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  93. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  94. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  95. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  96. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  97. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  98. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  99. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  100. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  101. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  102. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  103. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  104. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  105. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  106. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  107. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  108. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  109. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  110. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  111. package/app-engine/shaka-player-demo/posters.py +0 -110
  112. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  113. package/app-engine/shaka-player-demo/time.txt +0 -1
  114. package/build/README.md +0 -127
  115. package/build/__init__.py +0 -0
  116. package/build/all.py +0 -150
  117. package/build/apps.py +0 -186
  118. package/build/build.py +0 -392
  119. package/build/check.py +0 -354
  120. package/build/checkversion.py +0 -84
  121. package/build/compiler.py +0 -513
  122. package/build/conformance.textproto +0 -335
  123. package/build/docker/Dockerfile +0 -20
  124. package/build/docker/docker-compose.yml +0 -10
  125. package/build/docs.py +0 -54
  126. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  127. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  128. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  129. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  130. package/build/gendeps.py +0 -71
  131. package/build/generateExterns.js +0 -867
  132. package/build/generateLocalizations.py +0 -262
  133. package/build/generateTsDefs.py +0 -137
  134. package/build/install-linux-prereqs.sh +0 -136
  135. package/build/license-header +0 -5
  136. package/build/misspellings.txt +0 -39
  137. package/build/pixelsChanged.js +0 -35
  138. package/build/shaka-lab.yaml +0 -161
  139. package/build/shakaBuildHelpers.py +0 -356
  140. package/build/stats.py +0 -816
  141. package/build/subprocessWindowsPatch.py +0 -103
  142. package/build/test.py +0 -541
  143. package/build/types/ads +0 -8
  144. package/build/types/cast +0 -6
  145. package/build/types/cea +0 -13
  146. package/build/types/complete +0 -9
  147. package/build/types/core +0 -108
  148. package/build/types/dash +0 -8
  149. package/build/types/hls +0 -9
  150. package/build/types/manifests +0 -5
  151. package/build/types/networking +0 -6
  152. package/build/types/offline +0 -22
  153. package/build/types/polyfill +0 -18
  154. package/build/types/text +0 -11
  155. package/build/types/ui +0 -39
  156. package/build/updateScreenshots.py +0 -85
  157. package/build/wrapper.template.js +0 -52
@@ -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
  *
@@ -94,6 +94,25 @@ shaka.media.SegmentIndex = class {
94
94
  }
95
95
 
96
96
 
97
+ /**
98
+ * Return the earliest reference, or null if empty.
99
+ * @return {shaka.media.SegmentReference}
100
+ */
101
+ earliestReference() {
102
+ return this.references[0] || null;
103
+ }
104
+
105
+
106
+ /**
107
+ * Drop the first N references.
108
+ * Used in early HLS synchronization, and does not count as eviction.
109
+ * @param {number} n
110
+ */
111
+ dropFirstReferences(n) {
112
+ this.references.splice(0, n);
113
+ }
114
+
115
+
97
116
  /**
98
117
  * Finds the position of the segment for the given time, in seconds, relative
99
118
  * to the start of the presentation. Returns the position of the segment
@@ -166,7 +185,13 @@ shaka.media.SegmentIndex = class {
166
185
  for (const ref of this.references) {
167
186
  ref.startTime += offset;
168
187
  ref.endTime += offset;
169
- ref.timestampOffset += offset;
188
+ ref.trueEndTime += offset;
189
+
190
+ for (const partial of ref.partialReferences) {
191
+ partial.startTime += offset;
192
+ partial.endTime += offset;
193
+ partial.trueEndTime += offset;
194
+ }
170
195
  }
171
196
  }
172
197
  }
@@ -160,8 +160,9 @@ shaka.media.SegmentReference = class {
160
160
  * If not provided, the duration should be automatically calculated based on
161
161
  * the duration of the reference.
162
162
  * @param {?number=} syncTime
163
- * A time value, expressed in the same scale as the start and end time, which
164
- * is used to synchronize between streams.
163
+ * A time value, expressed in seconds since 1970, which is used to
164
+ * synchronize between streams. Both produced and consumed by the HLS
165
+ * parser. Other components should not need this value.
165
166
  * @param {shaka.media.SegmentReference.Status=} status
166
167
  * The segment status is used to indicate that a segment does not exist or is
167
168
  * not available.
@@ -221,7 +222,13 @@ shaka.media.SegmentReference = class {
221
222
  /** @type {?number} */
222
223
  this.tileDuration = tileDuration;
223
224
 
224
- /** @type {?number} */
225
+ /**
226
+ * A time value, expressed in seconds since 1970, which is used to
227
+ * synchronize between streams. Both produced and consumed by the HLS
228
+ * parser. Other components should not need this value.
229
+ *
230
+ * @type {?number}
231
+ */
225
232
  this.syncTime = syncTime;
226
233
 
227
234
  /** @type {shaka.media.SegmentReference.Status} */
@@ -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.syncTime == null ? reference.startTime : reference.syncTime,
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.1-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
  }
@@ -8,6 +8,7 @@ goog.provide('shaka.util.MimeUtils');
8
8
 
9
9
  goog.require('shaka.dependencies');
10
10
  goog.require('shaka.media.Transmuxer');
11
+ goog.require('shaka.util.ManifestParserUtils');
11
12
 
12
13
  /**
13
14
  * @summary A set of utility functions for dealing with MIME types.
@@ -34,6 +35,8 @@ shaka.util.MimeUtils = class {
34
35
  /**
35
36
  * Takes a MIME type and a codecs string and produces the full MIME
36
37
  * type. If it's a transport stream, convert its codecs to MP4 codecs.
38
+ * Otherwise for multiplexed content, convert the video MIME types to
39
+ * their audio equivalents if the content type is audio.
37
40
  *
38
41
  * @param {string} mimeType
39
42
  * @param {string} codecs
@@ -42,13 +45,19 @@ shaka.util.MimeUtils = class {
42
45
  */
43
46
  static getFullOrConvertedType(mimeType, codecs, contentType) {
44
47
  const fullMimeType = shaka.util.MimeUtils.getFullType(mimeType, codecs);
48
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
45
49
 
46
- if (!shaka.dependencies.muxjs() ||
47
- !shaka.media.Transmuxer.isTsContainer(fullMimeType)) {
48
- return fullMimeType;
50
+ if (shaka.media.Transmuxer.isTsContainer(fullMimeType)) {
51
+ if (shaka.dependencies.muxjs()) {
52
+ return shaka.media.Transmuxer.convertTsCodecs(
53
+ contentType, fullMimeType);
54
+ }
55
+ } else if (contentType == ContentType.AUDIO) {
56
+ // video/mp2t is the correct mime type for TS audio, so only replace the
57
+ // word "video" with "audio" for non-TS audio content.
58
+ return fullMimeType.replace('video', 'audio');
49
59
  }
50
-
51
- return shaka.media.Transmuxer.convertTsCodecs(contentType, fullMimeType);
60
+ return fullMimeType;
52
61
  }
53
62
 
54
63
 
@@ -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
  *
@@ -1008,6 +1008,7 @@ shaka.util.StreamUtils = class {
1008
1008
  track.channelsCount = audio.channelsCount;
1009
1009
  track.audioSamplingRate = audio.audioSamplingRate;
1010
1010
  track.audioBandwidth = audio.bandwidth || null;
1011
+ track.spatialAudio = audio.spatialAudio;
1011
1012
  track.label = audio.label;
1012
1013
  track.audioRoles = audio.roles;
1013
1014
  }
@@ -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.1",
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": "^5.14.1",
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",
@@ -96,7 +94,7 @@
96
94
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
97
95
  },
98
96
  "dependencies": {
99
- "eme-encryption-scheme-polyfill": "^2.0.3"
97
+ "eme-encryption-scheme-polyfill": "^2.0.5"
100
98
  },
101
99
  "engines": {
102
100
  "node": ">=14"