shaka-player 3.2.2 → 3.3.0

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 (156) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/AUTHORS +6 -5
  5. package/CHANGELOG.md +247 -0
  6. package/CONTRIBUTORS +4 -2
  7. package/build/types/core +3 -0
  8. package/build/types/offline +1 -0
  9. package/build/types/polyfill +1 -0
  10. package/build/types/ui +2 -0
  11. package/demo/common/assets.js +18 -0
  12. package/demo/common/message_ids.js +13 -1
  13. package/demo/config.js +31 -1
  14. package/demo/index.html +1 -1
  15. package/demo/locales/en.json +12 -0
  16. package/demo/locales/source.json +48 -0
  17. package/dist/controls.css +26 -2
  18. package/dist/controls.css.map +1 -1
  19. package/dist/demo.compiled.debug.js +19 -16
  20. package/dist/demo.compiled.debug.map +3 -3
  21. package/dist/demo.compiled.js +19 -16
  22. package/dist/demo.compiled.map +3 -3
  23. package/dist/deps.js +18 -11
  24. package/dist/locales.js +28 -26
  25. package/dist/receiver.compiled.debug.js +11 -9
  26. package/dist/receiver.compiled.debug.map +3 -3
  27. package/dist/receiver.compiled.js +11 -9
  28. package/dist/receiver.compiled.map +3 -3
  29. package/dist/shaka-player.compiled.d.ts +205 -15
  30. package/dist/shaka-player.compiled.debug.d.ts +205 -15
  31. package/dist/shaka-player.compiled.debug.externs.js +256 -6
  32. package/dist/shaka-player.compiled.debug.js +406 -354
  33. package/dist/shaka-player.compiled.debug.map +4 -4
  34. package/dist/shaka-player.compiled.externs.js +256 -6
  35. package/dist/shaka-player.compiled.js +826 -800
  36. package/dist/shaka-player.compiled.map +4 -4
  37. package/dist/shaka-player.ui.d.ts +221 -16
  38. package/dist/shaka-player.ui.debug.d.ts +221 -16
  39. package/dist/shaka-player.ui.debug.externs.js +283 -6
  40. package/dist/shaka-player.ui.debug.js +547 -476
  41. package/dist/shaka-player.ui.debug.map +4 -4
  42. package/dist/shaka-player.ui.externs.js +283 -6
  43. package/dist/shaka-player.ui.js +1019 -980
  44. package/dist/shaka-player.ui.map +4 -4
  45. package/docs/design/bg-fetch-after.gv +29 -0
  46. package/docs/design/bg-fetch-after.gv.png +0 -0
  47. package/docs/design/bg-fetch-before.gv +18 -0
  48. package/docs/design/bg-fetch-before.gv.png +0 -0
  49. package/docs/design/bg-fetch.md +134 -0
  50. package/docs/tutorials/blob-url.md +15 -0
  51. package/docs/tutorials/index.json +1 -0
  52. package/docs/tutorials/ui-customization.md +66 -0
  53. package/docs/tutorials/ui.md +5 -5
  54. package/externs/ima.js +3 -0
  55. package/externs/shaka/ads.js +5 -0
  56. package/externs/shaka/manifest.js +12 -1
  57. package/externs/shaka/manifest_parser.js +10 -1
  58. package/externs/shaka/net.js +20 -4
  59. package/externs/shaka/offline.js +31 -2
  60. package/externs/shaka/player.js +130 -5
  61. package/externs/shaka/text.js +14 -0
  62. package/externs/webcrypto.js +18 -0
  63. package/lib/abr/ewma.js +14 -0
  64. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  65. package/lib/abr/simple_abr_manager.js +6 -0
  66. package/lib/ads/client_side_ad.js +8 -0
  67. package/lib/ads/client_side_ad_manager.js +8 -4
  68. package/lib/ads/server_side_ad.js +7 -0
  69. package/lib/cast/cast_utils.js +1 -0
  70. package/lib/dash/dash_parser.js +48 -14
  71. package/lib/dash/segment_base.js +25 -1
  72. package/lib/dash/segment_list.js +18 -9
  73. package/lib/dash/segment_template.js +23 -10
  74. package/lib/hls/hls_parser.js +86 -23
  75. package/lib/media/quality_observer.js +291 -0
  76. package/lib/media/segment_index.js +6 -0
  77. package/lib/media/segment_reference.js +18 -3
  78. package/lib/media/streaming_engine.js +42 -4
  79. package/lib/net/http_fetch_plugin.js +31 -11
  80. package/lib/net/http_plugin_utils.js +1 -0
  81. package/lib/net/http_xhr_plugin.js +40 -14
  82. package/lib/net/networking_engine.js +81 -18
  83. package/lib/offline/download_info.js +68 -0
  84. package/lib/offline/download_manager.js +13 -5
  85. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  86. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  87. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  88. package/lib/offline/storage.js +393 -284
  89. package/lib/offline/stored_content_utils.js +2 -0
  90. package/lib/player.js +206 -7
  91. package/lib/polyfill/random_uuid.js +52 -0
  92. package/lib/text/cue.js +12 -0
  93. package/lib/text/ttml_text_parser.js +25 -2
  94. package/lib/text/ui_text_displayer.js +2 -2
  95. package/lib/util/cmcd_manager.js +826 -0
  96. package/lib/util/error.js +6 -0
  97. package/lib/util/language_utils.js +12 -0
  98. package/lib/util/mutex.js +52 -0
  99. package/lib/util/periods.js +98 -64
  100. package/lib/util/player_configuration.js +18 -0
  101. package/package.json +1 -1
  102. package/shaka-player.uncompiled.js +3 -0
  103. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  104. package/test/dash/dash_parser_live_unit.js +101 -1
  105. package/test/dash/dash_parser_manifest_unit.js +35 -1
  106. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  107. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  108. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  109. package/test/hls/hls_live_unit.js +2 -0
  110. package/test/hls/hls_parser_unit.js +2 -0
  111. package/test/media/quality_observer_unit.js +139 -0
  112. package/test/media/segment_reference_unit.js +16 -1
  113. package/test/media/streaming_engine_integration.js +2 -1
  114. package/test/media/streaming_engine_unit.js +2 -0
  115. package/test/net/http_plugin_unit.js +20 -15
  116. package/test/offline/storage_integration.js +108 -4
  117. package/test/player_integration.js +99 -0
  118. package/test/test/util/dash_parser_util.js +4 -0
  119. package/test/test/util/fake_ad.js +7 -0
  120. package/test/test/util/fake_drm_engine.js +9 -2
  121. package/test/test/util/manifest_generator.js +3 -2
  122. package/test/test/util/test_scheme.js +14 -1
  123. package/test/text/ttml_text_parser_unit.js +47 -0
  124. package/test/ui/ui_unit.js +250 -0
  125. package/test/util/cmcd_manager_unit.js +277 -0
  126. package/ui/airplay_button.js +1 -0
  127. package/ui/audio_language_selection.js +7 -0
  128. package/ui/cast_button.js +1 -0
  129. package/ui/context_menu.js +109 -0
  130. package/ui/controls.js +41 -7
  131. package/ui/controls.less +1 -0
  132. package/ui/enums.js +4 -0
  133. package/ui/externs/ui.js +28 -1
  134. package/ui/fast_forward_button.js +14 -5
  135. package/ui/fullscreen_button.js +1 -0
  136. package/ui/less/buttons.less +0 -4
  137. package/ui/less/containers.less +74 -0
  138. package/ui/less/overflow_menu.less +0 -10
  139. package/ui/less/tooltip.less +97 -0
  140. package/ui/locales/en.json +1 -0
  141. package/ui/locales/source.json +4 -0
  142. package/ui/loop_button.js +5 -0
  143. package/ui/mute_button.js +2 -1
  144. package/ui/overflow_menu.js +5 -25
  145. package/ui/pip_button.js +5 -0
  146. package/ui/play_button.js +2 -2
  147. package/ui/playback_rate_selection.js +8 -9
  148. package/ui/resolution_selection.js +7 -0
  149. package/ui/rewind_button.js +15 -6
  150. package/ui/seek_bar.js +4 -2
  151. package/ui/settings_menu.js +36 -4
  152. package/ui/small_play_button.js +1 -0
  153. package/ui/statistics_button.js +253 -0
  154. package/ui/text_selection.js +11 -4
  155. package/ui/ui.js +30 -0
  156. package/ui/volume_bar.js +1 -1
package/lib/util/error.js CHANGED
@@ -986,6 +986,12 @@ shaka.util.Error.Code = {
986
986
  */
987
987
  'DOWNLOAD_SIZE_CALLBACK_ERROR': 9015,
988
988
 
989
+ /**
990
+ * The storage cell does not allow new operations that significantly change
991
+ * existing data.
992
+ */
993
+ 'MODIFY_OPERATION_NOT_SUPPORTED': 9016,
994
+
989
995
  /**
990
996
  * CS IMA SDK, required for ad insertion, has not been included on the page.
991
997
  */
@@ -13,6 +13,7 @@ goog.require('shaka.util.ManifestParserUtils');
13
13
  /**
14
14
  * @summary A set of language utility functions.
15
15
  * @final
16
+ * @export
16
17
  */
17
18
  shaka.util.LanguageUtils = class {
18
19
  /**
@@ -32,6 +33,7 @@ shaka.util.LanguageUtils = class {
32
33
  * @param {string} locale1
33
34
  * @param {string} locale2
34
35
  * @return {boolean}
36
+ * @export
35
37
  */
36
38
  static areLocaleCompatible(locale1, locale2) {
37
39
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -62,6 +64,7 @@ shaka.util.LanguageUtils = class {
62
64
  * @param {string} locale1
63
65
  * @param {string} locale2
64
66
  * @return {boolean}
67
+ * @export
65
68
  */
66
69
  static areLanguageCompatible(locale1, locale2) {
67
70
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -102,6 +105,7 @@ shaka.util.LanguageUtils = class {
102
105
  * @param {string} possibleParent
103
106
  * @param {string} possibleChild
104
107
  * @return {boolean}
108
+ * @export
105
109
  */
106
110
  static isParentOf(possibleParent, possibleChild) {
107
111
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -146,6 +150,7 @@ shaka.util.LanguageUtils = class {
146
150
  * @param {string} localeA
147
151
  * @param {string} localeB
148
152
  * @return {boolean}
153
+ * @export
149
154
  */
150
155
  static isSiblingOf(localeA, localeB) {
151
156
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -181,6 +186,7 @@ shaka.util.LanguageUtils = class {
181
186
  *
182
187
  * @param {string} locale
183
188
  * @return {string}
189
+ * @export
184
190
  */
185
191
  static normalize(locale) {
186
192
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -217,6 +223,7 @@ shaka.util.LanguageUtils = class {
217
223
  * @param {string} a
218
224
  * @param {string} b
219
225
  * @return {boolean}
226
+ * @export
220
227
  */
221
228
  static areSiblings(a, b) {
222
229
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -235,6 +242,7 @@ shaka.util.LanguageUtils = class {
235
242
  * @param {string} target
236
243
  * @param {string} candidate
237
244
  * @return {number}
245
+ * @export
238
246
  */
239
247
  static relatedness(target, candidate) {
240
248
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -271,6 +279,7 @@ shaka.util.LanguageUtils = class {
271
279
  *
272
280
  * @param {string} lang
273
281
  * @return {string}
282
+ * @export
274
283
  */
275
284
  static getBase(lang) {
276
285
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -300,6 +309,7 @@ shaka.util.LanguageUtils = class {
300
309
  *
301
310
  * @param {shaka.extern.Stream} stream
302
311
  * @return {string}
312
+ * @export
303
313
  */
304
314
  static getLocaleForText(stream) {
305
315
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -322,6 +332,7 @@ shaka.util.LanguageUtils = class {
322
332
  *
323
333
  * @param {shaka.extern.Variant} variant
324
334
  * @return {string}
335
+ * @export
325
336
  */
326
337
  static getLocaleForVariant(variant) {
327
338
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -356,6 +367,7 @@ shaka.util.LanguageUtils = class {
356
367
  * @param {string} target
357
368
  * @param {!Iterable.<string>} searchSpace
358
369
  * @return {?string}
370
+ * @export
359
371
  */
360
372
  static findClosestLocale(target, searchSpace) {
361
373
  const LanguageUtils = shaka.util.LanguageUtils;
@@ -0,0 +1,52 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.util.Mutex');
8
+
9
+
10
+ /**
11
+ * @summary A simple mutex.
12
+ */
13
+ shaka.util.Mutex = class {
14
+ /** Creates the mutex. */
15
+ constructor() {
16
+ /** @private {!Array.<function()>} */
17
+ this.waiting_ = [];
18
+
19
+ /** @private {number} */
20
+ this.nextMutexId_ = 0;
21
+
22
+ /** @private {number} */
23
+ this.acquiredMutexId_ = 0;
24
+ }
25
+
26
+ /** @return {!Promise.<number>} mutexId */
27
+ async acquire() {
28
+ const mutexId = ++this.nextMutexId_;
29
+ if (!this.acquiredMutexId_) {
30
+ this.acquiredMutexId_ = mutexId;
31
+ } else {
32
+ await (new Promise((resolve, reject) => {
33
+ this.waiting_.push(() => {
34
+ this.acquiredMutexId_ = mutexId;
35
+ resolve();
36
+ });
37
+ }));
38
+ }
39
+ return mutexId;
40
+ }
41
+
42
+ /** @param {number} mutexId */
43
+ release(mutexId) {
44
+ if (mutexId == this.acquiredMutexId_) {
45
+ this.acquiredMutexId_ = 0;
46
+ if (this.waiting_.length > 0) {
47
+ const callback = this.waiting_.shift();
48
+ callback();
49
+ }
50
+ }
51
+ }
52
+ };
@@ -547,8 +547,7 @@ shaka.util.PeriodCombiner = class {
547
547
  for (const stream of unusedStreams) {
548
548
  // Create a new output stream which includes this input stream.
549
549
  const outputStream =
550
- // eslint-disable-next-line no-await-in-loop
551
- await shaka.util.PeriodCombiner.createNewOutputStream_(
550
+ shaka.util.PeriodCombiner.createNewOutputStream_(
552
551
  stream, streamsPerPeriod, clone, concat,
553
552
  unusedStreamsPerPeriod);
554
553
  if (outputStream) {
@@ -619,52 +618,69 @@ shaka.util.PeriodCombiner = class {
619
618
  static async extendExistingOutputStream_(
620
619
  outputStream, streamsPerPeriod, firstNewPeriodIndex, concat,
621
620
  unusedStreamsPerPeriod) {
622
- const matches = shaka.util.PeriodCombiner.findMatchesInAllPeriods_(
623
- streamsPerPeriod, outputStream);
624
-
625
- if (!matches) {
626
- // We were unable to extend this output stream.
627
- shaka.log.error('No matches extending output stream!',
628
- outputStream, streamsPerPeriod);
629
- return false;
630
- }
621
+ shaka.util.PeriodCombiner.findMatchesInAllPeriods_(streamsPerPeriod,
622
+ outputStream);
631
623
 
632
624
  // This only exists where T == Stream, and this should only ever be called
633
625
  // on Stream types. StreamDB should not have pre-existing output streams.
634
626
  goog.asserts.assert(outputStream.createSegmentIndex,
635
627
  'outputStream should be a Stream type!');
636
628
 
629
+ if (!outputStream.matchedStreams) {
630
+ // We were unable to extend this output stream.
631
+ shaka.log.error('No matches extending output stream!',
632
+ outputStream, streamsPerPeriod);
633
+ return false;
634
+ }
637
635
  // We need to create all the per-period segment indexes and append them to
638
636
  // the output's MetaSegmentIndex.
639
- await shaka.util.PeriodCombiner.createSegmentIndexes_(matches);
640
-
641
- // Assure the compiler that matches didn't become null during the async
642
- // operation above.
643
- goog.asserts.assert(matches, 'Matches should be non-null');
637
+ if (outputStream.segmentIndex) {
638
+ await shaka.util.PeriodCombiner.extendOutputSegmentIndex_(outputStream,
639
+ firstNewPeriodIndex);
640
+ }
644
641
 
645
- shaka.util.PeriodCombiner.extendOutputStream_(
646
- outputStream, matches, firstNewPeriodIndex, concat,
647
- unusedStreamsPerPeriod);
642
+ shaka.util.PeriodCombiner.extendOutputStream_(outputStream,
643
+ firstNewPeriodIndex, concat, unusedStreamsPerPeriod);
648
644
  return true;
649
645
  }
650
646
 
651
647
  /**
652
- * Creates all segment indexes for an array of streams. Returns once every
653
- * segment index is created.
648
+ * Creates the segment indexes for an array of input streams, and append them
649
+ * to the output stream's segment index.
654
650
  *
655
- * @param {!Array.<!shaka.extern.Stream>} streams
656
- * @return {!Promise}
651
+ * @param {shaka.extern.Stream} outputStream
652
+ * @param {number} firstNewPeriodIndex An index into streamsPerPeriod which
653
+ * represents the first new period that hasn't been processed yet.
657
654
  * @private
658
655
  */
659
- static createSegmentIndexes_(streams) {
656
+ static async extendOutputSegmentIndex_(outputStream, firstNewPeriodIndex) {
660
657
  const operations = [];
658
+ const streams = outputStream.matchedStreams;
659
+ goog.asserts.assert(streams, 'matched streams should be valid');
660
+
661
661
  for (const stream of streams) {
662
662
  operations.push(stream.createSegmentIndex());
663
663
  if (stream.trickModeVideo && !stream.trickModeVideo.segmentIndex) {
664
664
  operations.push(stream.trickModeVideo.createSegmentIndex());
665
665
  }
666
666
  }
667
- return Promise.all(operations);
667
+ await Promise.all(operations);
668
+
669
+ // Concatenate the new matches onto the stream, starting at the first new
670
+ // period.
671
+ const Iterables = shaka.util.Iterables;
672
+ // Satisfy the compiler about the type.
673
+ // Also checks if the segmentIndex is still valid after the async
674
+ // operations, to make sure we stop if the active stream has changed.
675
+ if (outputStream.segmentIndex instanceof shaka.media.MetaSegmentIndex) {
676
+ for (const {i, item: match} of Iterables.enumerate(streams)) {
677
+ if (match.segmentIndex && i >= firstNewPeriodIndex) {
678
+ goog.asserts.assert(match.segmentIndex,
679
+ 'stream should have a segmentIndex.');
680
+ outputStream.segmentIndex.appendSegmentIndex(match.segmentIndex);
681
+ }
682
+ }
683
+ }
668
684
  }
669
685
 
670
686
  /**
@@ -682,7 +698,7 @@ shaka.util.PeriodCombiner = class {
682
698
  * @param {!Array.<!Set.<T>>} unusedStreamsPerPeriod An array of sets of
683
699
  * unused streams from each period.
684
700
  *
685
- * @return {!Promise.<?T>} A newly-created output Stream, or null if matches
701
+ * @return {?T} A newly-created output Stream, or null if matches
686
702
  * could not be found.`
687
703
  *
688
704
  * @template T
@@ -690,35 +706,36 @@ shaka.util.PeriodCombiner = class {
690
706
  *
691
707
  * @private
692
708
  */
693
- static async createNewOutputStream_(
709
+ static createNewOutputStream_(
694
710
  stream, streamsPerPeriod, clone, concat, unusedStreamsPerPeriod) {
695
711
  // Start by cloning the stream without segments, key IDs, etc.
696
712
  const outputStream = clone(stream);
697
713
 
698
714
  // Find best-matching streams in all periods.
699
- const matches = shaka.util.PeriodCombiner.findMatchesInAllPeriods_(
700
- streamsPerPeriod, outputStream);
701
-
702
- if (!matches) {
703
- // This is not a stream we can build output from, but it may become part
704
- // of another output based on another period's stream.
705
- return null;
706
- }
715
+ shaka.util.PeriodCombiner.findMatchesInAllPeriods_(streamsPerPeriod,
716
+ outputStream);
707
717
 
708
718
  // This only exists where T == Stream.
709
719
  if (outputStream.createSegmentIndex) {
720
+ // Override the createSegmentIndex function of the outputStream.
721
+ outputStream.createSegmentIndex = async () => {
722
+ if (!outputStream.segmentIndex) {
723
+ outputStream.segmentIndex = new shaka.media.MetaSegmentIndex();
724
+ await shaka.util.PeriodCombiner.extendOutputSegmentIndex_(
725
+ outputStream, /* firstNewPeriodIndex= */ 0);
726
+ }
727
+ };
710
728
  // For T == Stream, we need to create all the per-period segment indexes
711
729
  // in advance. concat() will add them to the output's MetaSegmentIndex.
712
- await shaka.util.PeriodCombiner.createSegmentIndexes_(matches);
713
730
  }
714
731
 
715
- // Assure the compiler that matches didn't become null during the async
716
- // operation above.
717
- goog.asserts.assert(matches, 'Matches should be non-null');
718
-
719
- shaka.util.PeriodCombiner.extendOutputStream_(
720
- outputStream, matches, /* firstNewPeriodIndex= */ 0, concat,
721
- unusedStreamsPerPeriod);
732
+ if (!outputStream.matchedStreams) {
733
+ // This is not a stream we can build output from, but it may become part
734
+ // of another output based on another period's stream.
735
+ return null;
736
+ }
737
+ shaka.util.PeriodCombiner.extendOutputStream_(outputStream,
738
+ /* firstNewPeriodIndex= */ 0, concat, unusedStreamsPerPeriod);
722
739
 
723
740
  return outputStream;
724
741
  }
@@ -726,7 +743,6 @@ shaka.util.PeriodCombiner = class {
726
743
  /**
727
744
  * @param {T} outputStream An existing output stream which needs to be
728
745
  * extended into new periods.
729
- * @param {!Array.<T>} matches A list of matching Streams from each period.
730
746
  * @param {number} firstNewPeriodIndex An index into streamsPerPeriod which
731
747
  * represents the first new period that hasn't been processed yet.
732
748
  * @param {function(T, T)} concat Concatenate the second stream onto the end
@@ -740,10 +756,15 @@ shaka.util.PeriodCombiner = class {
740
756
  * @private
741
757
  */
742
758
  static extendOutputStream_(
743
- outputStream, matches, firstNewPeriodIndex, concat,
744
- unusedStreamsPerPeriod) {
759
+ outputStream, firstNewPeriodIndex, concat, unusedStreamsPerPeriod) {
745
760
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
746
761
  const LanguageUtils = shaka.util.LanguageUtils;
762
+ const matches = outputStream.matchedStreams;
763
+
764
+ // Assure the compiler that matches didn't become null during the async
765
+ // operation before.
766
+ goog.asserts.assert(outputStream.matchedStreams,
767
+ 'matchedStreams should be non-null');
747
768
 
748
769
  // Concatenate the new matches onto the stream, starting at the first new
749
770
  // period.
@@ -787,7 +808,23 @@ shaka.util.PeriodCombiner = class {
787
808
  // streams that match this output.
788
809
  clone.originalId = null;
789
810
  clone.createSegmentIndex = () => Promise.resolve();
790
- clone.segmentIndex = new shaka.media.MetaSegmentIndex();
811
+ clone.closeSegmentIndex = () => {
812
+ if (clone.segmentIndex) {
813
+ clone.segmentIndex.release();
814
+ clone.segmentIndex = null;
815
+ }
816
+ // Close the segment index of the matched streams.
817
+ if (clone.matchedStreams) {
818
+ for (const match of clone.matchedStreams) {
819
+ if (match.segmentIndex) {
820
+ match.segmentIndex.release();
821
+ match.segmentIndex = null;
822
+ }
823
+ }
824
+ }
825
+ };
826
+
827
+ clone.segmentIndex = null;
791
828
  clone.emsgSchemeIdUris = [];
792
829
  clone.keyIds = new Set();
793
830
  clone.closedCaptions = null;
@@ -873,26 +910,24 @@ shaka.util.PeriodCombiner = class {
873
910
  }
874
911
  }
875
912
 
876
- // Satisfy the compiler about the type.
877
- goog.asserts.assert(
878
- output.segmentIndex instanceof shaka.media.MetaSegmentIndex,
879
- 'Output streams should have a MetaSegmentIndex!');
880
- // Satisfy the compiler that the input index has been created.
881
- goog.asserts.assert(
882
- input.segmentIndex,
883
- 'Input segment index should have been created by now!');
884
-
885
- output.segmentIndex.appendSegmentIndex(input.segmentIndex);
886
-
887
913
  // Combine trick-play video streams, if present.
888
914
  if (input.trickModeVideo) {
889
915
  if (!output.trickModeVideo) {
890
916
  // Create a fresh output stream for trick-mode playback.
891
917
  output.trickModeVideo = shaka.util.PeriodCombiner.cloneStream_(
892
918
  input.trickModeVideo);
893
- // Start it with whatever non-trick-mode Streams are in the output so
894
- // far.
895
- output.trickModeVideo.segmentIndex = output.segmentIndex.clone();
919
+ // TODO: fix the createSegmentIndex function for trickModeVideo.
920
+ // The trick-mode tracks in multi-period content should have trick-mode
921
+ // segment indexes whenever available, rather than only regular-mode
922
+ // segment indexes.
923
+ output.trickModeVideo.createSegmentIndex = () => {
924
+ // Satisfy the compiler about the type.
925
+ goog.asserts.assert(
926
+ output.segmentIndex instanceof shaka.media.MetaSegmentIndex,
927
+ 'The stream should have a MetaSegmentIndex.');
928
+ output.trickModeVideo.segmentIndex = output.segmentIndex.clone();
929
+ return Promise.resolve();
930
+ };
896
931
  }
897
932
 
898
933
  // Concatenate the trick mode input onto the trick mode output.
@@ -943,7 +978,6 @@ shaka.util.PeriodCombiner = class {
943
978
  *
944
979
  * @param {!Array.<!Array.<T>>} streamsPerPeriod
945
980
  * @param {T} outputStream
946
- * @return {Array.<T>}
947
981
  *
948
982
  * @template T
949
983
  * Accepts either a StreamDB or Stream type.
@@ -956,11 +990,11 @@ shaka.util.PeriodCombiner = class {
956
990
  const match = shaka.util.PeriodCombiner.findBestMatchInPeriod_(
957
991
  streams, outputStream);
958
992
  if (!match) {
959
- return null;
993
+ return;
960
994
  }
961
995
  matches.push(match);
962
996
  }
963
- return matches;
997
+ outputStream.matchedStreams = matches;
964
998
  }
965
999
 
966
1000
  /**
@@ -120,6 +120,8 @@ shaka.util.PlayerConfiguration = class {
120
120
  ignoreTextStreamFailures: false,
121
121
  ignoreImageStreamFailures: false,
122
122
  useFullSegmentsForStartTime: false,
123
+ defaultAudioCodec: 'mp4a.40.2',
124
+ defaultVideoCodec: 'avc1.42E01E',
123
125
  },
124
126
  };
125
127
 
@@ -163,6 +165,8 @@ shaka.util.PlayerConfiguration = class {
163
165
  forceHTTPS: false,
164
166
  preferNativeHls: false,
165
167
  updateIntervalSeconds: 1,
168
+ dispatchAllEmsgBoxes: false,
169
+ observeQualityChanges: false,
166
170
  };
167
171
 
168
172
  // Some browsers will stop earlier than others before a gap (e.g., Edge
@@ -235,6 +239,19 @@ shaka.util.PlayerConfiguration = class {
235
239
  minBandwidth: 0,
236
240
  maxBandwidth: Infinity,
237
241
  },
242
+ advanced: {
243
+ minTotalBytes: 128e3,
244
+ minBytes: 16e3,
245
+ fastHalfLife: 2,
246
+ slowHalfLife: 5,
247
+ },
248
+ };
249
+
250
+ const cmcd = {
251
+ enabled: false,
252
+ sessionId: '',
253
+ contentId: '',
254
+ useHeaders: false,
238
255
  };
239
256
 
240
257
  /** @type {shaka.extern.PlayerConfiguration} */
@@ -269,6 +286,7 @@ shaka.util.PlayerConfiguration = class {
269
286
  playRangeStart: 0,
270
287
  playRangeEnd: Infinity,
271
288
  textDisplayFactory: () => null,
289
+ cmcd: cmcd,
272
290
  };
273
291
 
274
292
  // Add this callback so that we can reference the preferred audio language
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": "3.2.2",
4
+ "version": "3.3.0",
5
5
  "homepage": "https://github.com/google/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -44,6 +44,7 @@ goog.require('shaka.polyfill.PatchedMediaKeysMs');
44
44
  goog.require('shaka.polyfill.PatchedMediaKeysNop');
45
45
  goog.require('shaka.polyfill.PatchedMediaKeysWebkit');
46
46
  goog.require('shaka.polyfill.PiPWebkit');
47
+ goog.require('shaka.polyfill.RandomUUID');
47
48
  goog.require('shaka.polyfill.VTTCue');
48
49
  goog.require('shaka.polyfill.VideoPlayPromise');
49
50
  goog.require('shaka.polyfill.VideoPlaybackQuality');
@@ -70,6 +71,7 @@ goog.require('shaka.ui.AdPosition');
70
71
  goog.require('shaka.ui.AirPlayButton');
71
72
  goog.require('shaka.ui.BigPlayButton');
72
73
  goog.require('shaka.ui.CastButton');
74
+ goog.require('shaka.ui.ContextMenu');
73
75
  goog.require('shaka.ui.Element');
74
76
  goog.require('shaka.ui.FastForwardButton');
75
77
  goog.require('shaka.ui.FullscreenButton');
@@ -85,6 +87,7 @@ goog.require('shaka.ui.RewindButton');
85
87
  goog.require('shaka.ui.SkipAdButton');
86
88
  goog.require('shaka.ui.SmallPlayButton');
87
89
  goog.require('shaka.ui.Spacer');
90
+ goog.require('shaka.ui.StatisticsButton');
88
91
  goog.require('shaka.ui.TextSelection');
89
92
  goog.require('shaka.ui.VolumeBar');
90
93
  goog.require('shaka.util.Dom');
@@ -42,6 +42,8 @@ describe('DashParser ContentProtection', () => {
42
42
 
43
43
  const playerInterface = {
44
44
  networkingEngine: netEngine,
45
+ modifyManifestRequest: (request, manifestInfo) => {},
46
+ modifySegmentRequest: (request, segmentInfo) => {},
45
47
  filter: (manifest) => Promise.resolve(),
46
48
  makeTextStreamsForClosedCaptions: (manifest) => {},
47
49
  onTimelineRegionAdded: fail, // Should not have any EventStream elements.
@@ -39,6 +39,8 @@ describe('DashParser Live', () => {
39
39
  parser.configure(shaka.util.PlayerConfiguration.createDefault().manifest);
40
40
  playerInterface = {
41
41
  networkingEngine: fakeNetEngine,
42
+ modifyManifestRequest: (request, manifestInfo) => {},
43
+ modifySegmentRequest: (request, segmentInfo) => {},
42
44
  filter: (manifest) => Promise.resolve(),
43
45
  makeTextStreamsForClosedCaptions: (manifest) => {},
44
46
  onTimelineRegionAdded: fail, // Should not have any EventStream elements.
@@ -364,7 +366,7 @@ describe('DashParser Live', () => {
364
366
  });
365
367
  }
366
368
 
367
- it('can add Periods', async () => {
369
+ it('can add Periods with SegmentTemplate', async () => {
368
370
  const template1 = [
369
371
  '<MPD type="dynamic" availabilityStartTime="1970-01-01T00:00:00Z"',
370
372
  ' suggestedPresentationDelay="PT5S"',
@@ -440,6 +442,104 @@ describe('DashParser Live', () => {
440
442
  // two segments (10-14s).
441
443
  expect(stream.segmentIndex.find(9)).not.toBe(null);
442
444
  expect(stream.segmentIndex.find(13)).not.toBe(null);
445
+
446
+ stream.closeSegmentIndex();
447
+ await stream.createSegmentIndex();
448
+
449
+ expect(stream.segmentIndex.find(9)).not.toBe(null);
450
+ expect(stream.segmentIndex.find(13)).not.toBe(null);
451
+ });
452
+
453
+ it('can add Periods with SegmentList', async () => {
454
+ const list1 = [
455
+ '<MPD type="dynamic" availabilityStartTime="1970-01-01T00:00:00Z"',
456
+ ' suggestedPresentationDelay="PT5S"',
457
+ ' minimumUpdatePeriod="PT%(updateTime)dS">',
458
+ ' <Period id="1">',
459
+ ' <AdaptationSet mimeType="video/mp4">',
460
+ ' <Representation id="1" bandwidth="500">',
461
+ ' <BaseURL>http://example.com</BaseURL>',
462
+ ' <SegmentList>',
463
+ ' <SegmentURL media="s1.mp4" />',
464
+ ' <SegmentURL media="s2.mp4" />',
465
+ ' <SegmentURL media="s3.mp4" />',
466
+ ' <SegmentTimeline>',
467
+ ' <S d="10" t="0" r="2"/>',
468
+ ' </SegmentTimeline>',
469
+ ' </SegmentList>',
470
+ ' </Representation>',
471
+ ' </AdaptationSet>',
472
+ ' </Period>',
473
+ '</MPD>',
474
+ ].join('\n');
475
+ const list2 = [
476
+ '<MPD type="dynamic" availabilityStartTime="1970-01-01T00:00:00Z"',
477
+ ' suggestedPresentationDelay="PT5S"',
478
+ ' minimumUpdatePeriod="PT%(updateTime)dS">',
479
+ ' <Period id="1" duration="PT40S">',
480
+ ' <AdaptationSet mimeType="video/mp4">',
481
+ ' <Representation id="1" bandwidth="500">',
482
+ ' <BaseURL>http://example.com</BaseURL>',
483
+ ' <SegmentList>',
484
+ ' <SegmentURL media="s1.mp4" />',
485
+ ' <SegmentURL media="s2.mp4" />',
486
+ ' <SegmentURL media="s3.mp4" />',
487
+ ' <SegmentURL media="s4.mp4" />',
488
+ ' <SegmentTimeline>',
489
+ ' <S d="10" t="0" r="3"/>',
490
+ ' </SegmentTimeline>',
491
+ ' </SegmentList>',
492
+ ' </Representation>',
493
+ ' </AdaptationSet>',
494
+ ' </Period>',
495
+ ' <Period id="2">',
496
+ ' <AdaptationSet mimeType="video/mp4">',
497
+ ' <Representation id="2" bandwidth="500">',
498
+ ' <BaseURL>http://example.com</BaseURL>',
499
+ ' <SegmentList>',
500
+ ' <SegmentURL media="s1.mp4" />',
501
+ ' <SegmentURL media="s2.mp4" />',
502
+ ' <SegmentTimeline>',
503
+ ' <S d="10" t="0" r="1"/>',
504
+ ' </SegmentTimeline>',
505
+ ' </SegmentList>',
506
+ ' </Representation>',
507
+ ' </AdaptationSet>',
508
+ ' </Period>',
509
+ '</MPD>',
510
+ ].join('\n');
511
+ const firstManifest = sprintf(list1, {updateTime: updateTime});
512
+ const secondManifest = sprintf(list2, {updateTime: updateTime});
513
+
514
+ fakeNetEngine.setResponseText('dummy://foo', firstManifest);
515
+ // First three segments should exist.
516
+ Date.now = () => 5;
517
+
518
+ const manifest = await parser.start('dummy://foo', playerInterface);
519
+ const variant = manifest.variants[0];
520
+ const stream = variant.video;
521
+ await stream.createSegmentIndex();
522
+
523
+ // First three segments exist, but not the fourth.
524
+ expect(stream.segmentIndex.find(25)).not.toBe(null);
525
+ expect(stream.segmentIndex.find(45)).toBe(null);
526
+
527
+ fakeNetEngine.setResponseText('dummy://foo', secondManifest);
528
+ Date.now = () => 25;
529
+
530
+ await updateManifest();
531
+
532
+ // The update should have affected the same variant object we captured
533
+ // before. Now the entire first period should exist (0-40s), plus the next
534
+ // two segments of the second period(40-60s).
535
+ expect(stream.segmentIndex.find(25)).not.toBe(null);
536
+ expect(stream.segmentIndex.find(45)).not.toBe(null);
537
+
538
+ stream.closeSegmentIndex();
539
+ await stream.createSegmentIndex();
540
+
541
+ expect(stream.segmentIndex.find(25)).not.toBe(null);
542
+ expect(stream.segmentIndex.find(45)).not.toBe(null);
443
543
  });
444
544
 
445
545
  it('uses redirect URL for manifest BaseURL and updates', async () => {