shaka-player 3.1.2 → 3.1.6

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 (145) hide show
  1. package/.github/workflows/build_and_test.yaml +73 -0
  2. package/.github/workflows/release.yaml +53 -0
  3. package/AUTHORS +3 -0
  4. package/CHANGELOG.md +170 -1
  5. package/CONTRIBUTORS +3 -0
  6. package/build/checkversion.py +1 -1
  7. package/build/generateExterns.js +1 -1
  8. package/build/misspellings.txt +1 -0
  9. package/build/shakaBuildHelpers.py +6 -5
  10. package/build/subprocessWindowsPatch.py +103 -0
  11. package/build/test.py +6 -0
  12. package/demo/common/assets.js +1 -1
  13. package/dist/controls.css +2 -2
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +1 -1
  16. package/dist/demo.compiled.js +1 -1
  17. package/dist/deps.js +7 -7
  18. package/dist/locales.js +35 -35
  19. package/dist/receiver.compiled.debug.js +1 -1
  20. package/dist/receiver.compiled.js +1 -1
  21. package/dist/shaka-player.compiled.d.ts +172 -3
  22. package/dist/shaka-player.compiled.debug.d.ts +172 -3
  23. package/dist/shaka-player.compiled.debug.externs.js +184 -1
  24. package/dist/shaka-player.compiled.debug.js +390 -370
  25. package/dist/shaka-player.compiled.debug.map +3 -3
  26. package/dist/shaka-player.compiled.externs.js +184 -1
  27. package/dist/shaka-player.compiled.js +809 -805
  28. package/dist/shaka-player.compiled.map +3 -3
  29. package/dist/shaka-player.ui.d.ts +174 -14
  30. package/dist/shaka-player.ui.debug.d.ts +174 -14
  31. package/dist/shaka-player.ui.debug.externs.js +200 -26
  32. package/dist/shaka-player.ui.debug.js +431 -412
  33. package/dist/shaka-player.ui.debug.map +4 -4
  34. package/dist/shaka-player.ui.externs.js +200 -26
  35. package/dist/shaka-player.ui.js +934 -929
  36. package/dist/shaka-player.ui.map +4 -4
  37. package/docs/tutorials/fairplay.md +1 -1
  38. package/docs/tutorials/faq.md +14 -0
  39. package/externs/ima.js +4 -0
  40. package/externs/shaka/ads.js +2 -0
  41. package/externs/shaka/text.js +10 -0
  42. package/karma.conf.js +20 -15
  43. package/lib/ads/ad_manager.js +30 -11
  44. package/lib/ads/client_side_ad_manager.js +76 -86
  45. package/lib/ads/server_side_ad_manager.js +26 -11
  46. package/lib/cast/cast_proxy.js +6 -2
  47. package/lib/cast/cast_receiver.js +5 -1
  48. package/lib/cast/cast_sender.js +2 -2
  49. package/lib/cea/sei_processor.js +24 -17
  50. package/lib/dash/dash_parser.js +131 -24
  51. package/lib/dash/segment_list.js +1 -1
  52. package/lib/dash/segment_template.js +1 -1
  53. package/lib/hls/hls_parser.js +44 -17
  54. package/lib/hls/manifest_text_parser.js +1 -0
  55. package/lib/media/closed_caption_parser.js +3 -1
  56. package/lib/media/content_workarounds.js +12 -0
  57. package/lib/media/drm_engine.js +20 -1
  58. package/lib/media/gap_jumping_controller.js +8 -4
  59. package/lib/media/media_source_engine.js +1 -2
  60. package/lib/media/playhead.js +1 -1
  61. package/lib/media/region_observer.js +39 -33
  62. package/lib/media/region_timeline.js +17 -19
  63. package/lib/media/streaming_engine.js +8 -4
  64. package/lib/net/networking_engine.js +6 -1
  65. package/lib/offline/storage.js +10 -11
  66. package/lib/player.js +66 -30
  67. package/lib/polyfill/encryption_scheme.js +2 -0
  68. package/lib/polyfill/fullscreen.js +2 -0
  69. package/lib/polyfill/mathround.js +2 -0
  70. package/lib/polyfill/media_capabilities.js +73 -21
  71. package/lib/polyfill/mediasource.js +8 -0
  72. package/lib/polyfill/orientation.js +3 -1
  73. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  74. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  75. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  76. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  77. package/lib/polyfill/pip_webkit.js +2 -0
  78. package/lib/polyfill/storage_estimate.js +2 -0
  79. package/lib/polyfill/video_play_promise.js +2 -0
  80. package/lib/polyfill/videoplaybackquality.js +2 -0
  81. package/lib/polyfill/vttcue.js +2 -0
  82. package/lib/text/cue.js +6 -0
  83. package/lib/text/mp4_ttml_parser.js +3 -0
  84. package/lib/text/text_engine.js +14 -2
  85. package/lib/text/ttml_text_parser.js +135 -87
  86. package/lib/text/ui_text_displayer.js +213 -80
  87. package/lib/text/vtt_text_parser.js +4 -2
  88. package/lib/util/fake_event.js +53 -7
  89. package/lib/util/fake_event_target.js +21 -1
  90. package/lib/util/mp4_box_parsers.js +1 -1
  91. package/lib/util/periods.js +4 -1
  92. package/lib/util/platform.js +13 -1
  93. package/lib/util/stream_utils.js +77 -10
  94. package/lib/util/xml_utils.js +19 -0
  95. package/package.json +3 -6
  96. package/test/cast/cast_proxy_unit.js +12 -6
  97. package/test/cast/cast_receiver_integration.js +1 -1
  98. package/test/cast/cast_utils_unit.js +1 -1
  99. package/test/cea/mp4_cea_parser_unit.js +26 -0
  100. package/test/dash/dash_parser_live_unit.js +43 -0
  101. package/test/dash/dash_parser_manifest_unit.js +222 -3
  102. package/test/hls/hls_parser_unit.js +79 -1
  103. package/test/media/closed_caption_parser_unit.js +20 -0
  104. package/test/media/drm_engine_unit.js +56 -0
  105. package/test/media/playhead_unit.js +27 -1
  106. package/test/media/region_observer_unit.js +40 -5
  107. package/test/media/region_timeline_unit.js +40 -22
  108. package/test/media/streaming_engine_integration.js +2 -1
  109. package/test/media/streaming_engine_unit.js +145 -0
  110. package/test/player_integration.js +38 -0
  111. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  112. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  113. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  114. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  115. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  116. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  117. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  118. package/test/test/assets/3675/dash_0.mpd +30 -0
  119. package/test/test/assets/3675/dash_1.mpd +30 -0
  120. package/test/test/assets/3675/dash_2.mpd +30 -0
  121. package/test/test/assets/3675/dash_3.mpd +30 -0
  122. package/test/test/assets/3675/dash_4.mpd +30 -0
  123. package/test/test/assets/3675/dash_5.mpd +26 -0
  124. package/test/test/assets/3675/init-stream0.m4s +0 -0
  125. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  126. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  127. package/test/test/util/fake_ad_manager.js +4 -1
  128. package/test/test/util/ttml_utils.js +81 -0
  129. package/test/test/util/util.js +10 -7
  130. package/test/text/mp4_ttml_parser_unit.js +11 -18
  131. package/test/text/ttml_text_parser_unit.js +542 -220
  132. package/test/text/ui_text_displayer_unit.js +93 -20
  133. package/test/text/vtt_text_parser_unit.js +17 -1
  134. package/test/ui/localization_unit.js +15 -14
  135. package/test/ui/text_displayer_layout_unit.js +21 -8
  136. package/test/util/mp4_box_parsers_unit.js +28 -0
  137. package/test/util/stream_utils_unit.js +59 -2
  138. package/ui/cast_button.js +2 -3
  139. package/ui/controls.js +11 -6
  140. package/ui/less/buttons.less +0 -4
  141. package/ui/less/containers.less +6 -7
  142. package/ui/locales/ru.json +6 -6
  143. package/ui/localization.js +40 -87
  144. package/ui/pip_button.js +2 -3
  145. package/ui/text_selection.js +1 -0
@@ -154,8 +154,10 @@ shaka.media.GapJumpingController = class {
154
154
  }
155
155
  // Don't gap jump while paused, so that you don't constantly jump ahead
156
156
  // while paused on a livestream. We make an exception for time 0, since we
157
- // may be _required_ to seek on startup before play can begin.
158
- if (this.video_.paused && this.video_.currentTime != 0) {
157
+ // may be _required_ to seek on startup before play can begin, but only if
158
+ // autoplay is enabled.
159
+ if (this.video_.paused && (this.video_.currentTime != 0 ||
160
+ (!this.video_.autoplay && this.video_.currentTime == 0))) {
159
161
  return;
160
162
  }
161
163
 
@@ -215,8 +217,10 @@ shaka.media.GapJumpingController = class {
215
217
  this.didFireLargeGap_ = true;
216
218
 
217
219
  // Event firing is synchronous.
218
- const event = new shaka.util.FakeEvent(
219
- 'largegap', {'currentTime': currentTime, 'gapSize': jumpSize});
220
+ const data = new Map()
221
+ .set('currentTime', currentTime)
222
+ .set('gapSize', jumpSize);
223
+ const event = new shaka.util.FakeEvent('largegap', data);
220
224
  event.cancelable = true;
221
225
  this.onEvent_(event);
222
226
 
@@ -48,8 +48,7 @@ shaka.media.MediaSourceEngine = class {
48
48
  * @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
49
49
  * onMetadata
50
50
  */
51
- constructor(video, closedCaptionParser, textDisplayer,
52
- onMetadata) {
51
+ constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
53
52
  /** @private {HTMLMediaElement} */
54
53
  this.video_ = video;
55
54
 
@@ -80,7 +80,7 @@ shaka.media.SrcEqualsPlayhead = class {
80
80
  // We listen for the loaded-data-event so that we know when we can
81
81
  // interact with |currentTime|.
82
82
  const onLoaded = () => {
83
- if (this.startTime_ == null) {
83
+ if (this.startTime_ == null || this.startTime_ == 0) {
84
84
  this.started_ = true;
85
85
  } else {
86
86
  // Startup is complete only when the video element acknowledges the
@@ -8,16 +8,19 @@ goog.provide('shaka.media.RegionObserver');
8
8
 
9
9
  goog.require('shaka.media.IPlayheadObserver');
10
10
  goog.require('shaka.media.RegionTimeline');
11
+ goog.require('shaka.util.EventManager');
12
+ goog.require('shaka.util.FakeEvent');
13
+ goog.require('shaka.util.FakeEventTarget');
11
14
 
12
15
 
13
16
  /**
14
17
  * The region observer watches a region timeline and playhead, and fires events
15
- * (onEnter, onExit, and onSkip) as the playhead moves.
18
+ * ('enter', 'exit', 'skip') as the playhead moves.
16
19
  *
17
20
  * @implements {shaka.media.IPlayheadObserver}
18
21
  * @final
19
22
  */
20
- shaka.media.RegionObserver = class {
23
+ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
21
24
  /**
22
25
  * Create a region observer for the given timeline. The observer does not
23
26
  * own the timeline, only uses it. This means that the observer should NOT
@@ -26,6 +29,8 @@ shaka.media.RegionObserver = class {
26
29
  * @param {!shaka.media.RegionTimeline} timeline
27
30
  */
28
31
  constructor(timeline) {
32
+ super();
33
+
29
34
  /** @private {shaka.media.RegionTimeline} */
30
35
  this.timeline_ = timeline;
31
36
 
@@ -39,13 +44,6 @@ shaka.media.RegionObserver = class {
39
44
  */
40
45
  this.oldPosition_ = new Map();
41
46
 
42
- /** @private {shaka.media.RegionObserver.EventListener} */
43
- this.onEnter_ = (region, seeking) => {};
44
- /** @private {shaka.media.RegionObserver.EventListener} */
45
- this.onExit_ = (region, seeking) => {};
46
- /** @private {shaka.media.RegionObserver.EventListener} */
47
- this.onSkip_ = (region, seeking) => {};
48
-
49
47
  // To make the rules easier to read, alias all the relative positions.
50
48
  const RelativePosition = shaka.media.RegionObserver.RelativePosition_;
51
49
  const BEFORE_THE_REGION = RelativePosition.BEFORE_THE_REGION;
@@ -62,39 +60,48 @@ shaka.media.RegionObserver = class {
62
60
  {
63
61
  weWere: null,
64
62
  weAre: IN_THE_REGION,
65
- invoke: (region, seeking) => this.onEnter_(region, seeking),
63
+ invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
66
64
  },
67
65
  {
68
66
  weWere: BEFORE_THE_REGION,
69
67
  weAre: IN_THE_REGION,
70
- invoke: (region, seeking) => this.onEnter_(region, seeking),
68
+ invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
71
69
  },
72
70
  {
73
71
  weWere: AFTER_THE_REGION,
74
72
  weAre: IN_THE_REGION,
75
- invoke: (region, seeking) => this.onEnter_(region, seeking),
73
+ invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
76
74
  },
77
75
  {
78
76
  weWere: IN_THE_REGION,
79
77
  weAre: BEFORE_THE_REGION,
80
- invoke: (region, seeking) => this.onExit_(region, seeking),
78
+ invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
81
79
  },
82
80
  {
83
81
  weWere: IN_THE_REGION,
84
82
  weAre: AFTER_THE_REGION,
85
- invoke: (region, seeking) => this.onExit_(region, seeking),
83
+ invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
86
84
  },
87
85
  {
88
86
  weWere: BEFORE_THE_REGION,
89
87
  weAre: AFTER_THE_REGION,
90
- invoke: (region, seeking) => this.onSkip_(region, seeking),
88
+ invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
91
89
  },
92
90
  {
93
91
  weWere: AFTER_THE_REGION,
94
92
  weAre: BEFORE_THE_REGION,
95
- invoke: (region, seeking) => this.onSkip_(region, seeking),
93
+ invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
96
94
  },
97
95
  ];
96
+
97
+ /** @private {shaka.util.EventManager} */
98
+ this.eventManager_ = new shaka.util.EventManager();
99
+
100
+ this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
101
+ /** @type {shaka.extern.TimelineRegionInfo} */
102
+ const region = event['region'];
103
+ this.oldPosition_.delete(region);
104
+ });
98
105
  }
99
106
 
100
107
  /** @override */
@@ -105,11 +112,10 @@ shaka.media.RegionObserver = class {
105
112
  // needed.
106
113
  this.oldPosition_.clear();
107
114
 
108
- // Clear the callbacks so that we don't hold onto any references external
109
- // to this class.
110
- this.onEnter_ = (region, seeking) => {};
111
- this.onExit_ = (region, seeking) => {};
112
- this.onSkip_ = (region, seeking) => {};
115
+ this.eventManager_.release();
116
+ this.eventManager_ = null;
117
+
118
+ super.release();
113
119
  }
114
120
 
115
121
  /** @override */
@@ -134,20 +140,20 @@ shaka.media.RegionObserver = class {
134
140
  }
135
141
 
136
142
  /**
137
- * Set all the listeners. This overrides any previous calls to |setListeners|.
143
+ * Dispatch events of the given type. All event types in this class have the
144
+ * same parameters: region and seeking.
138
145
  *
139
- * @param {shaka.media.RegionObserver.EventListener} onEnter
140
- * The callback for when we move from outside a region to inside a region.
141
- * @param {shaka.media.RegionObserver.EventListener} onExit
142
- * The callback for when we move from inside a region to outside a region.
143
- * @param {shaka.media.RegionObserver.EventListener} onSkip
144
- * The callback for when we move from before to after a region or from
145
- * after to before a region.
146
+ * @param {string} eventType
147
+ * @param {shaka.extern.TimelineRegionInfo} region
148
+ * @param {boolean} seeking
149
+ * @private
146
150
  */
147
- setListeners(onEnter, onExit, onSkip) {
148
- this.onEnter_ = onEnter;
149
- this.onExit_ = onExit;
150
- this.onSkip_ = onSkip;
151
+ onEvent_(eventType, region, seeking) {
152
+ const event = new shaka.util.FakeEvent(eventType, new Map([
153
+ ['region', region],
154
+ ['seeking', seeking],
155
+ ]));
156
+ this.dispatchEvent(event);
151
157
  }
152
158
 
153
159
  /**
@@ -6,26 +6,30 @@
6
6
 
7
7
  goog.provide('shaka.media.RegionTimeline');
8
8
 
9
+ goog.require('shaka.util.FakeEvent');
10
+ goog.require('shaka.util.FakeEventTarget');
9
11
  goog.require('shaka.util.IReleasable');
10
12
  goog.require('shaka.util.Timer');
11
13
 
12
14
 
13
15
  /**
14
16
  * The region timeline is a set of unique timeline region info entries. When
15
- * a new entry is added, the |onAddRegion| callback will be called.
17
+ * a new entry is added, the 'regionadd' event will be fired. When an entry is
18
+ * deleted, the 'regionremove' event will be fired.
16
19
  *
17
20
  * @implements {shaka.util.IReleasable}
18
21
  * @final
19
22
  */
20
- shaka.media.RegionTimeline = class {
23
+ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
21
24
  /**
22
25
  * @param {!function():{start: number, end: number}} getSeekRange
23
26
  */
24
27
  constructor(getSeekRange) {
25
- /** @private {function(shaka.extern.TimelineRegionInfo)} */
26
- this.onAddRegion_ = (region) => {};
28
+ super();
29
+
27
30
  /** @private {!Set.<shaka.extern.TimelineRegionInfo>} */
28
31
  this.regions_ = new Set();
32
+
29
33
  /** @private {!function():{start: number, end: number}} */
30
34
  this.getSeekRange_ = getSeekRange;
31
35
 
@@ -44,22 +48,9 @@ shaka.media.RegionTimeline = class {
44
48
 
45
49
  /** @override */
46
50
  release() {
47
- // Prevent us from holding onto any external references via the callback.
48
- this.onAddRegion_ = (region) => {};
49
51
  this.regions_.clear();
50
52
  this.filterTimer_.stop();
51
- }
52
-
53
- /**
54
- * Set the callbacks for events. This will override any previous calls to
55
- * |setListeners|.
56
- *
57
- * @param {function(shaka.extern.TimelineRegionInfo)} onAddRegion
58
- * Set the callback for when we add a new region. This callback will only
59
- * be called when a region is unique (we reject duplicate regions).
60
- */
61
- setListeners(onAddRegion) {
62
- this.onAddRegion_ = onAddRegion;
53
+ super.release();
63
54
  }
64
55
 
65
56
  /**
@@ -72,7 +63,10 @@ shaka.media.RegionTimeline = class {
72
63
  // instead of making the parser track it.
73
64
  if (similarRegion == null) {
74
65
  this.regions_.add(region);
75
- this.onAddRegion_(region);
66
+ const event = new shaka.util.FakeEvent('regionadd', new Map([
67
+ ['region', region],
68
+ ]));
69
+ this.dispatchEvent(event);
76
70
  }
77
71
  }
78
72
 
@@ -89,6 +83,10 @@ shaka.media.RegionTimeline = class {
89
83
  // reson to store or process them.
90
84
  if (region.endTime < seekRange.start) {
91
85
  this.regions_.delete(region);
86
+ const event = new shaka.util.FakeEvent('regionremove', new Map([
87
+ ['region', region],
88
+ ]));
89
+ this.dispatchEvent(event);
92
90
  }
93
91
  }
94
92
  }
@@ -623,7 +623,7 @@ shaka.media.StreamingEngine = class {
623
623
 
624
624
  // Don't clear the buffer unless something is buffered. This extra
625
625
  // check prevents extra, useless calls to clear the buffer.
626
- if (somethingBuffered) {
626
+ if (somethingBuffered || mediaState.performingUpdate) {
627
627
  this.forceClearBuffer_(mediaState);
628
628
  streamCleared = true;
629
629
  }
@@ -977,7 +977,10 @@ shaka.media.StreamingEngine = class {
977
977
  unscaledBufferingGoal * this.bufferingGoalScale_;
978
978
 
979
979
  // Check if we've buffered to the end of the presentation.
980
- if (timeNeeded >= this.manifest_.presentationTimeline.getDuration()) {
980
+ const timeUntilEnd =
981
+ this.manifest_.presentationTimeline.getDuration() - timeNeeded;
982
+ const oneMicrosecond = 1e-6;
983
+ if (timeUntilEnd < oneMicrosecond) {
981
984
  // We shouldn't rebuffer if the playhead is close to the end of the
982
985
  // presentation.
983
986
  shaka.log.debug(logPrefix, 'buffered to end of presentation');
@@ -1641,7 +1644,7 @@ shaka.media.StreamingEngine = class {
1641
1644
  // See DASH sec. 5.10.3.3.1
1642
1645
  // If a DASH client detects an event message box with a scheme that is not
1643
1646
  // defined in MPD, the client is expected to ignore it.
1644
- if (emsgSchemeIdUris.includes(schemeId)) {
1647
+ if (emsgSchemeIdUris && emsgSchemeIdUris.includes(schemeId)) {
1645
1648
  // See DASH sec. 5.10.4.1
1646
1649
  // A special scheme in DASH used to signal manifest updates.
1647
1650
  if (schemeId == 'urn:mpeg:dash:event:2012') {
@@ -1662,7 +1665,8 @@ shaka.media.StreamingEngine = class {
1662
1665
 
1663
1666
  // Dispatch an event to notify the application about the emsg box.
1664
1667
  const eventName = shaka.Player.EventName.Emsg;
1665
- const event = new shaka.util.FakeEvent(eventName, {'detail': emsg});
1668
+ const data = (new Map()).set('detail', emsg);
1669
+ const event = new shaka.util.FakeEvent(eventName, data);
1666
1670
  this.playerInterface_.onEvent(event);
1667
1671
  }
1668
1672
  }
@@ -222,6 +222,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
222
222
  this.destroyed_ = true;
223
223
  this.requestFilters_.clear();
224
224
  this.responseFilters_.clear();
225
+
226
+ // FakeEventTarget implements IReleasable
227
+ super.release();
228
+
225
229
  return this.operationManager_.destroy();
226
230
  }
227
231
 
@@ -544,7 +548,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
544
548
  }
545
549
 
546
550
  if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
547
- const event = new shaka.util.FakeEvent('retry', {'error': error});
551
+ const data = (new Map()).set('error', error);
552
+ const event = new shaka.util.FakeEvent('retry', data);
548
553
  this.dispatchEvent(event);
549
554
 
550
555
  // Move to the next URI.
@@ -477,23 +477,22 @@ shaka.offline.Storage = class {
477
477
  * @private
478
478
  */
479
479
  async filterManifest_(manifest, drmEngine, config) {
480
- const StreamUtils = shaka.util.StreamUtils;
481
-
482
480
  // Filter the manifest based on the restrictions given in the player
483
481
  // configuration.
484
482
  const maxHwRes = {width: Infinity, height: Infinity};
485
- StreamUtils.filterByRestrictions(manifest, config.restrictions, maxHwRes);
483
+ shaka.util.StreamUtils.filterByRestrictions(
484
+ manifest, config.restrictions, maxHwRes);
486
485
 
487
486
  // Filter the manifest based on what we know media source will be able to
488
487
  // play later (no point storing something we can't play).
489
488
  if (config.useMediaCapabilities) {
490
- StreamUtils.filterManifestByMediaCapabilities(
489
+ await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
491
490
  manifest, config.offline.usePersistentLicense);
492
491
  } else {
493
- StreamUtils.filterManifestByMediaSource(manifest);
492
+ shaka.util.StreamUtils.filterManifestByMediaSource(manifest);
494
493
  // Filter the manifest based on what we know our drm system will support
495
494
  // playing later.
496
- StreamUtils.filterManifestByDrm(manifest, drmEngine);
495
+ shaka.util.StreamUtils.filterManifestByDrm(manifest, drmEngine);
497
496
  }
498
497
 
499
498
  // Gather all tracks.
@@ -501,23 +500,23 @@ shaka.offline.Storage = class {
501
500
 
502
501
  // Choose the codec that has the lowest average bandwidth.
503
502
  const preferredAudioChannelCount = config.preferredAudioChannelCount;
504
- StreamUtils.chooseCodecsAndFilterManifest(
503
+ shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
505
504
  manifest, preferredAudioChannelCount);
506
505
 
507
506
  for (const variant of manifest.variants) {
508
507
  goog.asserts.assert(
509
- StreamUtils.isPlayable(variant),
508
+ shaka.util.StreamUtils.isPlayable(variant),
510
509
  'We should have already filtered by "is playable"');
511
510
 
512
- allTracks.push(StreamUtils.variantToTrack(variant));
511
+ allTracks.push(shaka.util.StreamUtils.variantToTrack(variant));
513
512
  }
514
513
 
515
514
  for (const text of manifest.textStreams) {
516
- allTracks.push(StreamUtils.textStreamToTrack(text));
515
+ allTracks.push(shaka.util.StreamUtils.textStreamToTrack(text));
517
516
  }
518
517
 
519
518
  for (const image of manifest.imageStreams) {
520
- allTracks.push(StreamUtils.imageStreamToTrack(image));
519
+ allTracks.push(shaka.util.StreamUtils.imageStreamToTrack(image));
521
520
  }
522
521
 
523
522
  // Let the application choose which tracks to store.
package/lib/player.js CHANGED
@@ -596,7 +596,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
596
596
  enterNode: (node, has, wants) => {
597
597
  this.dispatchEvent(this.makeEvent_(
598
598
  /* name= */ shaka.Player.EventName.OnStateChange,
599
- /* data= */ {'state': node.name}));
599
+ /* data= */ (new Map()).set('state', node.name)));
600
600
 
601
601
  const action = actions.get(node);
602
602
  return action(has, wants);
@@ -627,7 +627,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
627
627
  onIdle: (node) => {
628
628
  this.dispatchEvent(this.makeEvent_(
629
629
  /* name= */ shaka.Player.EventName.OnStateIdle,
630
- /* data= */ {'state': node.name}));
630
+ /* data= */ (new Map()).set('state', node.name)));
631
631
  },
632
632
  };
633
633
 
@@ -647,7 +647,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
647
647
 
648
648
  /**
649
649
  * @param {!shaka.Player.EventName} name
650
- * @param {Object=} data
650
+ * @param {Map.<string, Object>=} data
651
651
  * @return {!shaka.util.FakeEvent}
652
652
  * @private
653
653
  */
@@ -726,6 +726,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
726
726
  await this.networkingEngine_.destroy();
727
727
  this.networkingEngine_ = null;
728
728
  }
729
+
730
+ // FakeEventTarget implements IReleasable
731
+ super.release();
729
732
  }
730
733
 
731
734
  /**
@@ -1155,6 +1158,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1155
1158
  'mpg': 'video/mpeg',
1156
1159
  'mpeg': 'video/mpeg',
1157
1160
  'm3u8': 'application/x-mpegurl',
1161
+ 'mpd': 'application/dash+xml',
1158
1162
  'mp3': 'audio/mpeg',
1159
1163
  'aac': 'audio/aac',
1160
1164
  'flac': 'audio/flac',
@@ -1285,6 +1289,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1285
1289
  has.mediaElement = null;
1286
1290
  }
1287
1291
 
1292
+ if (this.adManager_) {
1293
+ // The ad manager is specific to the video, so detach it too.
1294
+ this.adManager_.release();
1295
+ }
1296
+
1288
1297
  // Clear our cached copy of the media element.
1289
1298
  this.video_ = null;
1290
1299
 
@@ -1621,8 +1630,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1621
1630
  // initialize it now even through it appears a little out-of-place.
1622
1631
  this.regionTimeline_ =
1623
1632
  new shaka.media.RegionTimeline(() => this.seekRange());
1624
- this.regionTimeline_.setListeners(/* onRegionAdded= */ (region) => {
1633
+ this.regionTimeline_.addEventListener('regionadd', (event) => {
1634
+ /** @type {shaka.extern.TimelineRegionInfo} */
1635
+ const region = event['region'];
1625
1636
  this.onRegionEvent_(shaka.Player.EventName.TimelineRegionAdded, region);
1637
+
1626
1638
  if (this.adManager_) {
1627
1639
  this.adManager_.onDashTimedMetadata(region);
1628
1640
  }
@@ -2064,6 +2076,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2064
2076
  decodingInfos: [],
2065
2077
  };
2066
2078
 
2079
+ this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
2067
2080
  await this.drmEngine_.initForPlayback(
2068
2081
  [variant], /* offlineSessionIds= */ []);
2069
2082
  await this.drmEngine_.attach(has.mediaElement);
@@ -2409,12 +2422,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2409
2422
  goog.asserts.assert(!endTime || startTime <= endTime,
2410
2423
  'Metadata start time should be less or equal to the end time!');
2411
2424
  const eventName = shaka.Player.EventName.Metadata;
2412
- const data = {
2413
- startTime: startTime,
2414
- endTime: endTime,
2415
- metadataType: metadataType,
2416
- payload: payload,
2417
- };
2425
+ const data = new Map()
2426
+ .set('startTime', startTime)
2427
+ .set('endTime', endTime)
2428
+ .set('metadataType', metadataType)
2429
+ .set('payload', payload);
2418
2430
  this.dispatchEvent(this.makeEvent_(eventName, data));
2419
2431
  }
2420
2432
 
@@ -2476,7 +2488,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2476
2488
  this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
2477
2489
  }
2478
2490
  };
2479
-
2480
2491
  return new shaka.net.NetworkingEngine(onProgressUpdated_);
2481
2492
  }
2482
2493
 
@@ -2514,21 +2525,31 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2514
2525
  // Create the region observer. This will allow us to notify the app when we
2515
2526
  // move in and out of timeline regions.
2516
2527
  const regionObserver = new shaka.media.RegionObserver(this.regionTimeline_);
2517
- const onEnterRegion = (region, seeking) => {
2528
+
2529
+ regionObserver.addEventListener('enter', (event) => {
2530
+ /** @type {shaka.extern.TimelineRegionInfo} */
2531
+ const region = event['region'];
2518
2532
  this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
2519
- };
2520
- const onExitRegion = (region, seeking) => {
2533
+ });
2534
+
2535
+ regionObserver.addEventListener('exit', (event) => {
2536
+ /** @type {shaka.extern.TimelineRegionInfo} */
2537
+ const region = event['region'];
2521
2538
  this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
2522
- };
2523
- const onSkipRegion = (region, seeking) => {
2539
+ });
2540
+
2541
+ regionObserver.addEventListener('skip', (event) => {
2542
+ /** @type {shaka.extern.TimelineRegionInfo} */
2543
+ const region = event['region'];
2544
+ /** @type {boolean} */
2545
+ const seeking = event['seeking'];
2524
2546
  // If we are seeking, we don't want to surface the enter/exit events since
2525
2547
  // they didn't play through them.
2526
2548
  if (!seeking) {
2527
2549
  this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
2528
2550
  this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
2529
2551
  }
2530
- };
2531
- regionObserver.setListeners(onEnterRegion, onExitRegion, onSkipRegion);
2552
+ });
2532
2553
 
2533
2554
  // Now that we have all our observers, create a manager for them.
2534
2555
  const manager = new shaka.media.PlayheadObserverManager(this.video_);
@@ -3235,6 +3256,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3235
3256
 
3236
3257
  const tracks = [];
3237
3258
 
3259
+ let activeTracks = 0;
3260
+
3238
3261
  // Convert each variant to a track.
3239
3262
  for (const variant of this.manifest_.variants) {
3240
3263
  if (!shaka.util.StreamUtils.isPlayable(variant)) {
@@ -3243,10 +3266,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3243
3266
 
3244
3267
  const track = shaka.util.StreamUtils.variantToTrack(variant);
3245
3268
  track.active = variant == currentVariant;
3269
+ if (!track.active && activeTracks != 1 && currentVariant != null &&
3270
+ variant.video == currentVariant.video &&
3271
+ variant.audio == currentVariant.audio) {
3272
+ track.active = true;
3273
+ }
3274
+
3275
+ if (track.active) {
3276
+ activeTracks++;
3277
+ }
3246
3278
 
3247
3279
  tracks.push(track);
3248
3280
  }
3249
3281
 
3282
+ goog.asserts.assert(activeTracks <= 1,
3283
+ 'It should only have one active track');
3284
+
3250
3285
  return tracks;
3251
3286
  } else if (this.video_ && this.video_.audioTracks) {
3252
3287
  // Safari's native HLS always shows a single element in videoTracks.
@@ -4796,7 +4831,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4796
4831
  // Surface the buffering event so that the app knows if/when we are
4797
4832
  // buffering.
4798
4833
  const eventName = shaka.Player.EventName.Buffering;
4799
- this.dispatchEvent(this.makeEvent_(eventName, {'buffering': isBuffering}));
4834
+ const data = (new Map()).set('buffering', isBuffering);
4835
+ this.dispatchEvent(this.makeEvent_(eventName, data));
4800
4836
  }
4801
4837
 
4802
4838
  /**
@@ -5178,10 +5214,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5178
5214
  onVariantChanged_(from, to) {
5179
5215
  // Delay the 'variantchanged' event so StreamingEngine has time to absorb
5180
5216
  // the changes before the user tries to query it.
5181
- const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, {
5182
- oldTrack: from,
5183
- newTrack: to,
5184
- });
5217
+ const data = new Map()
5218
+ .set('oldTrack', from)
5219
+ .set('newTrack', to);
5220
+ const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, data);
5185
5221
  this.delayDispatchEvent_(event);
5186
5222
  }
5187
5223
 
@@ -5204,10 +5240,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5204
5240
 
5205
5241
  /** @private */
5206
5242
  onAbrStatusChanged_() {
5207
- const event = this.makeEvent_(shaka.Player.EventName.AbrStatusChanged, {
5208
- newStatus: this.config_.abr.enabled,
5209
- });
5210
- this.delayDispatchEvent_(event);
5243
+ const data = (new Map()).set('newStatus', this.config_.abr.enabled);
5244
+ this.delayDispatchEvent_(this.makeEvent_(
5245
+ shaka.Player.EventName.AbrStatusChanged, data));
5211
5246
  }
5212
5247
 
5213
5248
  /**
@@ -5224,7 +5259,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5224
5259
  }
5225
5260
 
5226
5261
  const eventName = shaka.Player.EventName.Error;
5227
- const event = this.makeEvent_(eventName, {'detail': error});
5262
+ const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
5228
5263
  this.dispatchEvent(event);
5229
5264
  if (event.defaultPrevented) {
5230
5265
  error.handled = true;
@@ -5253,7 +5288,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5253
5288
  eventElement: region.eventElement,
5254
5289
  };
5255
5290
 
5256
- this.dispatchEvent(this.makeEvent_(eventName, {detail: clone}));
5291
+ const data = (new Map()).set('detail', clone);
5292
+ this.dispatchEvent(this.makeEvent_(eventName, data));
5257
5293
  }
5258
5294
 
5259
5295
  /**
@@ -6082,7 +6118,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6082
6118
  * @define {string} A version number taken from git at compile time.
6083
6119
  * @export
6084
6120
  */
6085
- shaka.Player.version = 'v3.1.2-uncompiled';
6121
+ shaka.Player.version = 'v3.1.6-uncompiled'; // x-release-please-version
6086
6122
 
6087
6123
  // Initialize the deprecation system using the version string we just set
6088
6124
  // on the player.
@@ -13,12 +13,14 @@ goog.require('shaka.polyfill');
13
13
  * @see https://wicg.github.io/encrypted-media-encryption-scheme/
14
14
  * @see https://github.com/w3c/encrypted-media/pull/457
15
15
  * @see https://github.com/google/eme-encryption-scheme-polyfill
16
+ * @export
16
17
  */
17
18
  shaka.polyfill.EncryptionScheme = class {
18
19
  /**
19
20
  * Install the polyfill if needed.
20
21
  *
21
22
  * @suppress {missingRequire}
23
+ * @export
22
24
  */
23
25
  static install() {
24
26
  EncryptionSchemePolyfills.install();
@@ -14,10 +14,12 @@ goog.require('shaka.polyfill');
14
14
  * Many browsers have prefixed fullscreen methods on Element and document.
15
15
  * See {@link https://mzl.la/2K0xcHo Using fullscreen mode} on MDN for more
16
16
  * information.
17
+ * @export
17
18
  */
18
19
  shaka.polyfill.Fullscreen = class {
19
20
  /**
20
21
  * Install the polyfill if needed.
22
+ * @export
21
23
  */
22
24
  static install() {
23
25
  if (!window.Document) {