shaka-player 3.1.3 → 3.2.2

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 (182) hide show
  1. package/AUTHORS +3 -0
  2. package/CHANGELOG.md +175 -3
  3. package/CONTRIBUTORS +3 -0
  4. package/README.md +4 -4
  5. package/build/generateExterns.js +1 -1
  6. package/build/types/polyfill +1 -0
  7. package/demo/common/asset.js +9 -7
  8. package/demo/common/assets.js +11 -2
  9. package/demo/common/message_ids.js +2 -1
  10. package/demo/config.js +5 -2
  11. package/demo/index.html +1 -1
  12. package/demo/locales/en.json +2 -1
  13. package/demo/locales/source.json +8 -4
  14. package/demo/main.js +2 -1
  15. package/dist/controls.css +2 -2
  16. package/dist/controls.css.map +1 -1
  17. package/dist/demo.compiled.debug.js +15 -15
  18. package/dist/demo.compiled.debug.map +2 -2
  19. package/dist/demo.compiled.js +15 -15
  20. package/dist/demo.compiled.map +2 -2
  21. package/dist/deps.js +22 -21
  22. package/dist/locales.js +36 -36
  23. package/dist/receiver.compiled.debug.js +9 -9
  24. package/dist/receiver.compiled.debug.map +2 -2
  25. package/dist/receiver.compiled.js +9 -9
  26. package/dist/receiver.compiled.map +2 -2
  27. package/dist/shaka-player.compiled.d.ts +58 -10
  28. package/dist/shaka-player.compiled.debug.d.ts +58 -10
  29. package/dist/shaka-player.compiled.debug.externs.js +777 -717
  30. package/dist/shaka-player.compiled.debug.js +639 -607
  31. package/dist/shaka-player.compiled.debug.map +4 -4
  32. package/dist/shaka-player.compiled.externs.js +777 -717
  33. package/dist/shaka-player.compiled.js +826 -819
  34. package/dist/shaka-player.compiled.map +4 -4
  35. package/dist/shaka-player.ui.d.ts +58 -10
  36. package/dist/shaka-player.ui.debug.d.ts +58 -10
  37. package/dist/shaka-player.ui.debug.externs.js +529 -469
  38. package/dist/shaka-player.ui.debug.js +676 -645
  39. package/dist/shaka-player.ui.debug.map +4 -4
  40. package/dist/shaka-player.ui.externs.js +529 -469
  41. package/dist/shaka-player.ui.js +963 -955
  42. package/dist/shaka-player.ui.map +4 -4
  43. package/docs/tutorials/fairplay.md +1 -1
  44. package/docs/tutorials/ui.md +5 -5
  45. package/externs/aria.js +6 -0
  46. package/externs/shaka/ads.js +24 -1
  47. package/externs/shaka/manifest.js +6 -1
  48. package/externs/shaka/offline.js +5 -0
  49. package/externs/shaka/player.js +53 -9
  50. package/externs/shaka/text.js +10 -10
  51. package/externs/texttrack.js +3 -0
  52. package/karma.conf.js +1 -0
  53. package/lib/ads/ad_manager.js +16 -15
  54. package/lib/ads/client_side_ad_manager.js +52 -88
  55. package/lib/ads/server_side_ad_manager.js +26 -12
  56. package/lib/cast/cast_proxy.js +11 -2
  57. package/lib/cast/cast_receiver.js +2 -1
  58. package/lib/cast/cast_sender.js +2 -2
  59. package/lib/cast/cast_utils.js +6 -1
  60. package/lib/cea/cea608_data_channel.js +56 -56
  61. package/lib/cea/cea608_memory.js +33 -33
  62. package/lib/cea/cea708_service.js +43 -43
  63. package/lib/cea/cea708_window.js +38 -38
  64. package/lib/cea/cea_decoder.js +13 -14
  65. package/lib/cea/cea_utils.js +23 -23
  66. package/lib/cea/dtvcc_packet_builder.js +7 -7
  67. package/lib/cea/i_caption_decoder.js +2 -3
  68. package/lib/cea/i_cea_parser.js +2 -2
  69. package/lib/cea/mp4_cea_parser.js +6 -6
  70. package/lib/dash/dash_parser.js +98 -2
  71. package/lib/dash/segment_list.js +1 -1
  72. package/lib/dash/segment_template.js +1 -1
  73. package/lib/hls/hls_parser.js +159 -29
  74. package/lib/media/closed_caption_parser.js +1 -2
  75. package/lib/media/drm_engine.js +169 -192
  76. package/lib/media/gap_jumping_controller.js +4 -2
  77. package/lib/media/media_source_engine.js +1 -2
  78. package/lib/media/segment_index.js +3 -1
  79. package/lib/media/segment_reference.js +19 -2
  80. package/lib/media/streaming_engine.js +8 -7
  81. package/lib/net/networking_engine.js +3 -1
  82. package/lib/offline/download_manager.js +1 -2
  83. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  84. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  85. package/lib/offline/manifest_converter.js +3 -1
  86. package/lib/offline/storage.js +12 -14
  87. package/lib/player.js +437 -167
  88. package/lib/polyfill/aria.js +82 -0
  89. package/lib/polyfill/media_capabilities.js +7 -1
  90. package/lib/polyfill/mediasource.js +6 -0
  91. package/lib/polyfill/orientation.js +1 -1
  92. package/lib/polyfill/patchedmediakeys_apple.js +4 -4
  93. package/lib/polyfill/patchedmediakeys_ms.js +4 -4
  94. package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
  95. package/lib/routing/walker.js +2 -3
  96. package/lib/text/mp4_ttml_parser.js +3 -0
  97. package/lib/text/srt_text_parser.js +4 -4
  98. package/lib/text/text_engine.js +16 -4
  99. package/lib/text/ttml_text_parser.js +5 -0
  100. package/lib/text/ui_text_displayer.js +85 -37
  101. package/lib/text/vtt_text_parser.js +2 -2
  102. package/lib/text/web_vtt_generator.js +55 -12
  103. package/lib/util/error.js +6 -0
  104. package/lib/util/fake_event.js +53 -7
  105. package/lib/util/manifest_parser_utils.js +1 -0
  106. package/lib/util/mp4_box_parsers.js +20 -20
  107. package/lib/util/multi_map.js +8 -0
  108. package/lib/util/periods.js +4 -1
  109. package/lib/util/platform.js +13 -1
  110. package/lib/util/player_configuration.js +13 -1
  111. package/lib/util/stream_utils.js +153 -85
  112. package/lib/util/xml_utils.js +31 -12
  113. package/package.json +1 -1
  114. package/roadmap.md +24 -22
  115. package/shaka-player.uncompiled.js +1 -0
  116. package/test/cast/cast_proxy_unit.js +12 -6
  117. package/test/cast/cast_utils_unit.js +2 -1
  118. package/test/cea/cea608_memory_unit.js +1 -1
  119. package/test/cea/cea708_service_unit.js +8 -8
  120. package/test/cea/cea708_window_unit.js +6 -6
  121. package/test/cea/cea_decoder_unit.js +1 -1
  122. package/test/cea/dtvcc_packet_builder_unit.js +2 -2
  123. package/test/dash/dash_parser_manifest_unit.js +230 -2
  124. package/test/hls/hls_parser_unit.js +255 -1
  125. package/test/media/closed_caption_parser_unit.js +6 -1
  126. package/test/media/drm_engine_integration.js +86 -12
  127. package/test/media/drm_engine_unit.js +353 -450
  128. package/test/offline/manifest_convert_unit.js +2 -0
  129. package/test/offline/storage_integration.js +9 -19
  130. package/test/player_integration.js +90 -0
  131. package/test/player_src_equals_integration.js +52 -0
  132. package/test/player_unit.js +11 -33
  133. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  134. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  135. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  136. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  137. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  138. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  139. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  140. package/test/test/assets/3675/dash_0.mpd +30 -0
  141. package/test/test/assets/3675/dash_1.mpd +30 -0
  142. package/test/test/assets/3675/dash_2.mpd +30 -0
  143. package/test/test/assets/3675/dash_3.mpd +30 -0
  144. package/test/test/assets/3675/dash_4.mpd +30 -0
  145. package/test/test/assets/3675/dash_5.mpd +26 -0
  146. package/test/test/assets/3675/init-stream0.m4s +0 -0
  147. package/test/test/assets/chapters.srt +11 -0
  148. package/test/test/assets/chapters.vtt +10 -0
  149. package/test/test/util/cea_utils.js +12 -12
  150. package/test/test/util/fake_ad_manager.js +8 -1
  151. package/test/test/util/manifest_generator.js +2 -0
  152. package/test/test/util/manifest_parser_util.js +3 -1
  153. package/test/text/ttml_text_parser_unit.js +9 -0
  154. package/test/text/ui_text_displayer_unit.js +29 -17
  155. package/test/text/web_vtt_generator_unit.js +81 -7
  156. package/test/ui/localization_unit.js +15 -14
  157. package/test/ui/text_displayer_layout_unit.js +13 -1
  158. package/test/util/stream_utils_unit.js +153 -10
  159. package/ui/airplay_button.js +1 -1
  160. package/ui/audio_language_selection.js +2 -5
  161. package/ui/cast_button.js +6 -9
  162. package/ui/constants.js +0 -3
  163. package/ui/controls.js +4 -6
  164. package/ui/fast_forward_button.js +2 -3
  165. package/ui/fullscreen_button.js +1 -3
  166. package/ui/language_utils.js +1 -1
  167. package/ui/less/containers.less +3 -7
  168. package/ui/locales/ru.json +6 -6
  169. package/ui/localization.js +28 -51
  170. package/ui/loop_button.js +1 -3
  171. package/ui/mute_button.js +1 -3
  172. package/ui/overflow_menu.js +2 -3
  173. package/ui/pip_button.js +7 -10
  174. package/ui/playback_rate_selection.js +3 -6
  175. package/ui/presentation_time.js +1 -3
  176. package/ui/resolution_selection.js +4 -7
  177. package/ui/rewind_button.js +2 -3
  178. package/ui/seek_bar.js +2 -4
  179. package/ui/spacer.js +1 -1
  180. package/ui/text_selection.js +8 -11
  181. package/ui/ui_utils.js +1 -1
  182. package/ui/volume_bar.js +1 -3
@@ -6,6 +6,8 @@
6
6
 
7
7
  goog.provide('shaka.util.FakeEvent');
8
8
 
9
+ goog.require('goog.asserts');
10
+
9
11
 
10
12
  /**
11
13
  * @summary Create an Event work-alike object based on the provided dictionary.
@@ -16,18 +18,62 @@ goog.provide('shaka.util.FakeEvent');
16
18
  */
17
19
  shaka.util.FakeEvent = class {
18
20
  /**
19
- * @param {string} type
20
- * @param {Object=} dict
21
+ * @param {!Event} event
22
+ * @return {!shaka.util.FakeEvent}
21
23
  */
22
- constructor(type, dict = {}) {
23
- // Take properties from dict if present.
24
- for (const key in dict) {
25
- Object.defineProperty(this, key, {
26
- value: dict[key],
24
+ static fromRealEvent(event) {
25
+ const fakeEvent = new shaka.util.FakeEvent(event.type);
26
+ for (const key in event) {
27
+ Object.defineProperty(fakeEvent, key, {
28
+ value: event[key],
27
29
  writable: true,
28
30
  enumerable: true,
29
31
  });
30
32
  }
33
+ return fakeEvent;
34
+ }
35
+
36
+ /**
37
+ * Allows us to tell the compiler that the dictionary "map" is actually a
38
+ * generic object, for backwards compatibility.
39
+ * @param {!Map.<string, Object>} dict
40
+ * @return {!Object}
41
+ * @suppress {invalidCasts}
42
+ * @private
43
+ */
44
+ static recastDictAsObject_(dict) {
45
+ goog.asserts.assert(!(dict instanceof Map), 'dict should not be a map');
46
+ return /** @type {!Object} */ (dict);
47
+ }
48
+
49
+ /**
50
+ * @param {string} type
51
+ * @param {Map.<string, Object>=} dict
52
+ */
53
+ constructor(type, dict) {
54
+ if (dict) {
55
+ if (dict instanceof Map) {
56
+ // Take properties from dict if present.
57
+ for (const key of dict.keys()) {
58
+ Object.defineProperty(this, key, {
59
+ value: dict.get(key),
60
+ writable: true,
61
+ enumerable: true,
62
+ });
63
+ }
64
+ } else {
65
+ // For backwards compatibility with external apps that may make use of
66
+ // this public constructor, this should still accept generic objects.
67
+ const obj = shaka.util.FakeEvent.recastDictAsObject_(dict);
68
+ for (const key in obj) {
69
+ Object.defineProperty(this, key, {
70
+ value: obj[key],
71
+ writable: true,
72
+ enumerable: true,
73
+ });
74
+ }
75
+ }
76
+ }
31
77
 
32
78
  // The properties below cannot be set by the dict. They are all provided
33
79
  // for compatibility with native events.
@@ -55,6 +55,7 @@ shaka.util.ManifestParserUtils = class {
55
55
  audioRobustness: '',
56
56
  videoRobustness: '',
57
57
  serverCertificate: null,
58
+ serverCertificateUri: '',
58
59
  sessionType: '',
59
60
  initData: initData || [],
60
61
  keyIds: new Set(),
@@ -14,7 +14,7 @@ shaka.util.Mp4BoxParsers = class {
14
14
  /**
15
15
  * Parses a TFHD Box.
16
16
  * @param {!shaka.util.DataViewReader} reader
17
- * @param {!number} flags
17
+ * @param {number} flags
18
18
  * @return {!shaka.util.ParsedTFHDBox}
19
19
  */
20
20
  static parseTFHD(reader, flags) {
@@ -53,7 +53,7 @@ shaka.util.Mp4BoxParsers = class {
53
53
  /**
54
54
  * Parses a TFDT Box.
55
55
  * @param {!shaka.util.DataViewReader} reader
56
- * @param {!number} version
56
+ * @param {number} version
57
57
  * @return {!shaka.util.ParsedTFDTBox}
58
58
  */
59
59
  static parseTFDT(reader, version) {
@@ -68,7 +68,7 @@ shaka.util.Mp4BoxParsers = class {
68
68
  /**
69
69
  * Parses a MDHD Box.
70
70
  * @param {!shaka.util.DataViewReader} reader
71
- * @param {!number} version
71
+ * @param {number} version
72
72
  * @return {!shaka.util.ParsedMDHDBox}
73
73
  */
74
74
  static parseMDHD(reader, version) {
@@ -107,8 +107,8 @@ shaka.util.Mp4BoxParsers = class {
107
107
  /**
108
108
  * Parses a TRUN Box.
109
109
  * @param {!shaka.util.DataViewReader} reader
110
- * @param {!number} version
111
- * @param {!number} flags
110
+ * @param {number} version
111
+ * @param {number} flags
112
112
  * @return {!shaka.util.ParsedTRUNBox}
113
113
  */
114
114
  static parseTRUN(reader, version, flags) {
@@ -168,7 +168,7 @@ shaka.util.Mp4BoxParsers = class {
168
168
  /**
169
169
  * Parses a TKHD Box.
170
170
  * @param {!shaka.util.DataViewReader} reader
171
- * @param {!number} version
171
+ * @param {number} version
172
172
  * @return {!shaka.util.ParsedTKHDBox}
173
173
  */
174
174
  static parseTKHD(reader, version) {
@@ -192,12 +192,12 @@ shaka.util.Mp4BoxParsers = class {
192
192
 
193
193
  /**
194
194
  * @typedef {{
195
- * trackId: !number,
195
+ * trackId: number,
196
196
  * defaultSampleDuration: ?number,
197
197
  * defaultSampleSize: ?number
198
198
  * }}
199
199
  *
200
- * @property {!number} trackId
200
+ * @property {number} trackId
201
201
  * As per the spec: an integer that uniquely identifies this
202
202
  * track over the entire life‐time of this presentation
203
203
  * @property {?number} defaultSampleDuration
@@ -213,10 +213,10 @@ shaka.util.ParsedTFHDBox;
213
213
 
214
214
  /**
215
215
  * @typedef {{
216
- * baseMediaDecodeTime: !number
216
+ * baseMediaDecodeTime: number
217
217
  * }}
218
218
  *
219
- * @property {!number} baseMediaDecodeTime
219
+ * @property {number} baseMediaDecodeTime
220
220
  * As per the spec: the absolute decode time, measured on the media
221
221
  * timeline, of the first sample in decode order in the track fragment
222
222
  *
@@ -226,10 +226,10 @@ shaka.util.ParsedTFDTBox;
226
226
 
227
227
  /**
228
228
  * @typedef {{
229
- * timescale: !number
229
+ * timescale: number
230
230
  * }}
231
231
  *
232
- * @property {!number} timescale
232
+ * @property {number} timescale
233
233
  * As per the spec: an integer that specifies the time‐scale for this media;
234
234
  * this is the number of time units that pass in one second
235
235
  *
@@ -239,13 +239,13 @@ shaka.util.ParsedMDHDBox;
239
239
 
240
240
  /**
241
241
  * @typedef {{
242
- * defaultSampleDuration: !number,
243
- * defaultSampleSize: !number
242
+ * defaultSampleDuration: number,
243
+ * defaultSampleSize: number
244
244
  * }}
245
245
  *
246
- * @property {!number} defaultSampleDuration
246
+ * @property {number} defaultSampleDuration
247
247
  * The default sample duration to be used in track fragments
248
- * @property {!number} defaultSampleSize
248
+ * @property {number} defaultSampleSize
249
249
  * The default sample size to be used in track fragments
250
250
  *
251
251
  * @exportDoc
@@ -254,11 +254,11 @@ shaka.util.ParsedTREXBox;
254
254
 
255
255
  /**
256
256
  * @typedef {{
257
- * sampleCount: !number,
257
+ * sampleCount: number,
258
258
  * sampleData: !Array.<shaka.util.ParsedTRUNSample>
259
259
  * }}
260
260
  *
261
- * @property {!number} sampleCount
261
+ * @property {number} sampleCount
262
262
  * As per the spec: the number of samples being added in this run;
263
263
  * @property {!Array.<shaka.util.ParsedTRUNSample>} sampleData
264
264
  * An array of size <sampleCount> containing data for each sample
@@ -290,10 +290,10 @@ shaka.util.ParsedTRUNSample;
290
290
 
291
291
  /**
292
292
  * @typedef {{
293
- * trackId: !number
293
+ * trackId: number
294
294
  * }}
295
295
  *
296
- * @property {!number} trackId
296
+ * @property {number} trackId
297
297
  * Unique ID indicative of this track
298
298
  *
299
299
  * @exportDoc
@@ -102,4 +102,12 @@ shaka.util.MultiMap = class {
102
102
  size() {
103
103
  return Object.keys(this.map_).length;
104
104
  }
105
+
106
+ /**
107
+ * Get a list of all the keys.
108
+ * @return {!Array.<string>}
109
+ */
110
+ keys() {
111
+ return Object.keys(this.map_);
112
+ }
105
113
  };
@@ -80,7 +80,10 @@ shaka.util.PeriodCombiner = class {
80
80
 
81
81
  /** @return {!Array.<shaka.extern.Stream>} */
82
82
  getTextStreams() {
83
- return this.textStreams_;
83
+ // Return a copy of the array because makeTextStreamsForClosedCaptions
84
+ // may make changes to the contents of the array. Those changes should not
85
+ // propagate back to the PeriodCombiner.
86
+ return this.textStreams_.slice();
84
87
  }
85
88
 
86
89
  /** @return {!Array.<shaka.extern.Stream>} */
@@ -163,7 +163,6 @@ shaka.util.Platform = class {
163
163
  !shaka.util.Platform.isEdge();
164
164
  }
165
165
 
166
-
167
166
  /**
168
167
  * Check if the current platform is from Apple.
169
168
  *
@@ -179,6 +178,19 @@ shaka.util.Platform = class {
179
178
  && !shaka.util.Platform.isTizen();
180
179
  }
181
180
 
181
+ /**
182
+ * Check if the current platform is Playstation 5.
183
+ *
184
+ * Returns true on Playstation 5 browsers.
185
+ *
186
+ * Returns false for Playstation 5 browsers
187
+ *
188
+ * @return {boolean}
189
+ */
190
+ static isPS5() {
191
+ return shaka.util.Platform.userAgentContains_('PlayStation 5');
192
+ }
193
+
182
194
  /**
183
195
  * Returns a major version number for Safari, or Safari-based iOS browsers.
184
196
  *
@@ -75,6 +75,7 @@ shaka.util.PlayerConfiguration = class {
75
75
  initDataTransform: shaka.media.DrmEngine.defaultInitDataTransform,
76
76
  logLicenseExchange: false,
77
77
  updateExpirationTime: 1,
78
+ preferredKeySystems: [],
78
79
  };
79
80
 
80
81
  const manifest = {
@@ -108,9 +109,16 @@ shaka.util.PlayerConfiguration = class {
108
109
  'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb':
109
110
  'com.adobe.primetime',
110
111
  },
112
+ // Need some operation in the callback or else closure may remove calls
113
+ // to the function as it would be a no-op. The operation can't just be
114
+ // a log message, because those are stripped in the compiled build.
115
+ manifestPreprocessor: (element) => {
116
+ return element;
117
+ },
111
118
  },
112
119
  hls: {
113
120
  ignoreTextStreamFailures: false,
121
+ ignoreImageStreamFailures: false,
114
122
  useFullSegmentsForStartTime: false,
115
123
  },
116
124
  };
@@ -154,6 +162,7 @@ shaka.util.PlayerConfiguration = class {
154
162
  autoLowLatencyMode: false,
155
163
  forceHTTPS: false,
156
164
  preferNativeHls: false,
165
+ updateIntervalSeconds: 1,
157
166
  };
158
167
 
159
168
  // Some browsers will stop earlier than others before a gap (e.g., Edge
@@ -241,7 +250,10 @@ shaka.util.PlayerConfiguration = class {
241
250
  preferredVariantRole: '',
242
251
  preferredTextRole: '',
243
252
  preferredAudioChannelCount: 2,
253
+ preferredVideoCodecs: [],
254
+ preferredAudioCodecs: [],
244
255
  preferForcedSubs: false,
256
+ preferredDecodingAttributes: [],
245
257
  restrictions: {
246
258
  minWidth: 0,
247
259
  maxWidth: Infinity,
@@ -256,7 +268,6 @@ shaka.util.PlayerConfiguration = class {
256
268
  },
257
269
  playRangeStart: 0,
258
270
  playRangeEnd: Infinity,
259
- useMediaCapabilities: false,
260
271
  textDisplayFactory: () => null,
261
272
  };
262
273
 
@@ -293,6 +304,7 @@ shaka.util.PlayerConfiguration = class {
293
304
  audioRobustness: '',
294
305
  sessionType: '',
295
306
  serverCertificate: new Uint8Array(0),
307
+ serverCertificateUri: '',
296
308
  individualizationServer: '',
297
309
  },
298
310
  };
@@ -8,7 +8,6 @@ goog.provide('shaka.util.StreamUtils');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
- goog.require('shaka.media.MediaSourceEngine');
12
11
  goog.require('shaka.text.TextEngine');
13
12
  goog.require('shaka.util.Functional');
14
13
  goog.require('shaka.util.LanguageUtils');
@@ -28,30 +27,43 @@ shaka.util.StreamUtils = class {
28
27
  * bandwidth and filter out the rest.
29
28
  * Also filters out variants that have too many audio channels.
30
29
  * @param {!shaka.extern.Manifest} manifest
30
+ * @param {!Array.<string>} preferredVideoCodecs
31
+ * @param {!Array.<string>} preferredAudioCodecs
31
32
  * @param {number} preferredAudioChannelCount
33
+ * @param {!Array.<string>} preferredDecodingAttributes
32
34
  */
33
- static chooseCodecsAndFilterManifest(manifest, preferredAudioChannelCount) {
35
+ static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
36
+ preferredAudioCodecs, preferredAudioChannelCount,
37
+ preferredDecodingAttributes) {
34
38
  const StreamUtils = shaka.util.StreamUtils;
35
39
 
36
- // To start, consider a subset of variants based on audio channel
40
+ let variants = manifest.variants;
41
+ // To start, choose the codecs based on configured preferences if available.
42
+ if (preferredVideoCodecs.length || preferredAudioCodecs.length) {
43
+ variants = StreamUtils.choosePreferredCodecs(variants,
44
+ preferredVideoCodecs, preferredAudioCodecs);
45
+ }
46
+
47
+ // Consider a subset of variants based on audio channel
37
48
  // preferences.
38
49
  // For some content (#1013), surround-sound variants will use a different
39
50
  // codec than stereo variants, so it is important to choose codecs **after**
40
51
  // considering the audio channel config.
41
- const variants = StreamUtils.filterVariantsByAudioChannelCount(
42
- manifest.variants, preferredAudioChannelCount);
52
+ variants = StreamUtils.filterVariantsByAudioChannelCount(
53
+ variants, preferredAudioChannelCount);
43
54
 
44
55
  // Now organize variants into buckets by codecs.
45
56
  /** @type {!shaka.util.MultiMap.<shaka.extern.Variant>} */
46
57
  let variantsByCodecs = StreamUtils.getVariantsByCodecs_(variants);
47
58
  variantsByCodecs = StreamUtils.filterVariantsByDensity_(variantsByCodecs);
48
59
 
49
- const bestCodecs = StreamUtils.findBestCodecs_(variantsByCodecs);
60
+ const bestCodecs = StreamUtils.chooseCodecsByDecodingAttributes_(
61
+ variantsByCodecs, preferredDecodingAttributes);
50
62
 
51
63
  // Filter out any variants that don't match, forcing AbrManager to choose
52
- // from the most efficient variants possible.
64
+ // from a single video codec and a single audio codec possible.
53
65
  manifest.variants = manifest.variants.filter((variant) => {
54
- const codecs = StreamUtils.getGroupVariantCodecs_(variant);
66
+ const codecs = StreamUtils.getVariantCodecs_(variant);
55
67
  if (codecs == bestCodecs) {
56
68
  return true;
57
69
  }
@@ -71,8 +83,8 @@ shaka.util.StreamUtils = class {
71
83
  static getVariantsByCodecs_(variants) {
72
84
  const variantsByCodecs = new shaka.util.MultiMap();
73
85
  for (const variant of variants) {
74
- const group = shaka.util.StreamUtils.getGroupVariantCodecs_(variant);
75
- variantsByCodecs.push(group, variant);
86
+ const variantCodecs = shaka.util.StreamUtils.getVariantCodecs_(variant);
87
+ variantsByCodecs.push(variantCodecs, variant);
76
88
  }
77
89
 
78
90
  return variantsByCodecs;
@@ -80,6 +92,8 @@ shaka.util.StreamUtils = class {
80
92
 
81
93
  /**
82
94
  * Filters variants by density.
95
+ * Get variants by codecs map with the max density where all codecs are
96
+ * present.
83
97
  *
84
98
  * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
85
99
  * @return {!shaka.util.MultiMap.<shaka.extern.Variant>}
@@ -120,6 +134,107 @@ shaka.util.StreamUtils = class {
120
134
  return maxDensity ? codecGroupsByDensity.get(maxDensity) : variantsByCodecs;
121
135
  }
122
136
 
137
+ /**
138
+ * Choose the codecs by configured preferred audio and video codecs.
139
+ *
140
+ * @param {!Array<shaka.extern.Variant>} variants
141
+ * @param {!Array.<string>} preferredVideoCodecs
142
+ * @param {!Array.<string>} preferredAudioCodecs
143
+ * @return {!Array<shaka.extern.Variant>}
144
+ */
145
+ static choosePreferredCodecs(variants, preferredVideoCodecs,
146
+ preferredAudioCodecs) {
147
+ let subset = variants;
148
+ for (const videoCodec of preferredVideoCodecs) {
149
+ const filtered = subset.filter((variant) => {
150
+ return variant.video && variant.video.codecs.startsWith(videoCodec);
151
+ });
152
+ if (filtered.length) {
153
+ subset = filtered;
154
+ break;
155
+ }
156
+ }
157
+
158
+ for (const audioCodec of preferredAudioCodecs) {
159
+ const filtered = subset.filter((variant) => {
160
+ return variant.audio && variant.audio.codecs.startsWith(audioCodec);
161
+ });
162
+ if (filtered.length) {
163
+ subset = filtered;
164
+ break;
165
+ }
166
+ }
167
+ return subset;
168
+ }
169
+
170
+ /**
171
+ * Choose the codecs by configured preferred decoding attributes.
172
+ *
173
+ * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
174
+ * @param {!Array.<string>} attributes
175
+ * @return {string}
176
+ * @private
177
+ */
178
+ static chooseCodecsByDecodingAttributes_(variantsByCodecs, attributes) {
179
+ const StreamUtils = shaka.util.StreamUtils;
180
+
181
+ for (const attribute of attributes) {
182
+ if (attribute == StreamUtils.DecodingAttributes.SMOOTH ||
183
+ attribute == StreamUtils.DecodingAttributes.POWER) {
184
+ variantsByCodecs = StreamUtils.chooseCodecsByMediaCapabilitiesInfo_(
185
+ variantsByCodecs, attribute);
186
+ // If we only have one smooth or powerEfficient codecs, choose it as the
187
+ // best codecs.
188
+ if (variantsByCodecs.size() == 1) {
189
+ return variantsByCodecs.keys()[0];
190
+ }
191
+ } else if (attribute == StreamUtils.DecodingAttributes.BANDWIDTH) {
192
+ return StreamUtils.findCodecsByLowestBandwidth_(variantsByCodecs);
193
+ }
194
+ }
195
+ // If there's no configured decoding preferences, or we have multiple codecs
196
+ // that meets the configured decoding preferences, choose the one with
197
+ // the lowest bandwidth.
198
+ return StreamUtils.findCodecsByLowestBandwidth_(variantsByCodecs);
199
+ }
200
+
201
+ /**
202
+ * Choose the best codecs by configured preferred MediaCapabilitiesInfo
203
+ * attributes.
204
+ *
205
+ * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
206
+ * @param {string} attribute
207
+ * @return {!shaka.util.MultiMap.<shaka.extern.Variant>}
208
+ * @private
209
+ */
210
+ static chooseCodecsByMediaCapabilitiesInfo_(variantsByCodecs, attribute) {
211
+ let highestScore = 0;
212
+ const bestVariantsByCodecs = new shaka.util.MultiMap();
213
+ variantsByCodecs.forEach((codecs, variants) => {
214
+ let sum = 0;
215
+ let num = 0;
216
+
217
+ for (const variant of variants) {
218
+ if (variant.decodingInfos.length) {
219
+ sum += variant.decodingInfos[0][attribute] ? 1 : 0;
220
+ num++;
221
+ }
222
+ }
223
+
224
+ const averageScore = sum / num;
225
+ shaka.log.debug('codecs', codecs, 'avg', attribute, averageScore);
226
+
227
+ if (averageScore > highestScore) {
228
+ bestVariantsByCodecs.clear();
229
+ bestVariantsByCodecs.push(codecs, variants);
230
+ highestScore = averageScore;
231
+ } else if (averageScore == highestScore) {
232
+ bestVariantsByCodecs.push(codecs, variants);
233
+ }
234
+ });
235
+ return bestVariantsByCodecs;
236
+ }
237
+
123
238
  /**
124
239
  * Find the lowest-bandwidth (best) codecs.
125
240
  * Compute the average bandwidth for each group of variants.
@@ -128,7 +243,7 @@ shaka.util.StreamUtils = class {
128
243
  * @return {string}
129
244
  * @private
130
245
  */
131
- static findBestCodecs_(variantsByCodecs) {
246
+ static findCodecsByLowestBandwidth_(variantsByCodecs) {
132
247
  let bestCodecs = '';
133
248
  let lowestAverageBandwidth = Infinity;
134
249
 
@@ -163,7 +278,7 @@ shaka.util.StreamUtils = class {
163
278
  * @return {string}
164
279
  * @private
165
280
  */
166
- static getGroupVariantCodecs_(variant) {
281
+ static getVariantCodecs_(variant) {
167
282
  // Only consider the base of the codec string. For example, these should
168
283
  // both be considered the same codec: avc1.42c01e, avc1.4d401f
169
284
  let baseVideoCodec = '';
@@ -183,7 +298,7 @@ shaka.util.StreamUtils = class {
183
298
  * Filter the variants in |manifest| to only include the variants that meet
184
299
  * the given restrictions.
185
300
  *
186
- * @param {shaka.extern.Manifest} manifest
301
+ * @param {!shaka.extern.Manifest} manifest
187
302
  * @param {shaka.extern.Restrictions} restrictions
188
303
  * @param {{width: number, height:number}} maxHwResolution
189
304
  */
@@ -283,17 +398,11 @@ shaka.util.StreamUtils = class {
283
398
  * @param {shaka.media.DrmEngine} drmEngine
284
399
  * @param {?shaka.extern.Variant} currentVariant
285
400
  * @param {shaka.extern.Manifest} manifest
286
- * @param {boolean=} useMediaCapabilities
287
401
  */
288
402
  static async filterManifest(
289
- drmEngine, currentVariant, manifest, useMediaCapabilities) {
290
- if (useMediaCapabilities) {
291
- await shaka.util.StreamUtils.filterManifestByMediaCapabilities(manifest,
292
- manifest.offlineSessionIds.length > 0);
293
- } else {
294
- shaka.util.StreamUtils.filterManifestByDrm(manifest, drmEngine);
295
- shaka.util.StreamUtils.filterManifestByMediaSource(manifest);
296
- }
403
+ drmEngine, currentVariant, manifest) {
404
+ await shaka.util.StreamUtils.filterManifestByMediaCapabilities(manifest,
405
+ manifest.offlineSessionIds.length > 0);
297
406
  shaka.util.StreamUtils.filterManifestByCurrentVariant(
298
407
  currentVariant, manifest);
299
408
  shaka.util.StreamUtils.filterTextStreams_(manifest);
@@ -301,29 +410,6 @@ shaka.util.StreamUtils = class {
301
410
  }
302
411
 
303
412
 
304
- /**
305
- * Filter the variants in |manifest| to only include those that are supported
306
- * by |drm|.
307
- * @param {shaka.extern.Manifest} manifest
308
- * @param {shaka.media.DrmEngine} drmEngine
309
- */
310
- static filterManifestByDrm(manifest, drmEngine) {
311
- manifest.variants = manifest.variants.filter((variant) => {
312
- if (drmEngine && drmEngine.initialized()) {
313
- if (!drmEngine.supportsVariant(variant)) {
314
- shaka.log.debug('Dropping variant - not compatible with key system',
315
- variant);
316
- return false;
317
- }
318
- return true;
319
- }
320
-
321
- shaka.log.debug('DrmEngine is not initialized yet.');
322
- return true;
323
- });
324
- }
325
-
326
-
327
413
  /**
328
414
  * Alters the given Manifest to filter out any streams unsupported by the
329
415
  * platform via MediaCapabilities.decodingInfo() API.
@@ -363,46 +449,6 @@ shaka.util.StreamUtils = class {
363
449
  }
364
450
 
365
451
 
366
- /**
367
- * Filter the variants in the |manifest| to only include those that are
368
- * supported by media source.
369
- * TODO: remove once MediaCap implementations are done.
370
- * @param {shaka.extern.Manifest} manifest
371
- */
372
- static filterManifestByMediaSource(manifest) {
373
- const StreamUtils = shaka.util.StreamUtils;
374
-
375
- manifest.variants = manifest.variants.filter((variant) => {
376
- const audio = variant.audio;
377
- const video = variant.video;
378
-
379
- if (audio && !shaka.media.MediaSourceEngine.isStreamSupported(audio)) {
380
- shaka.log.debug('Dropping variant - audio not compatible with platform',
381
- StreamUtils.getStreamSummaryString_(audio));
382
- return false;
383
- }
384
-
385
- if (video && !shaka.media.MediaSourceEngine.isStreamSupported(video)) {
386
- shaka.log.debug('Dropping variant - video not compatible with platform',
387
- StreamUtils.getStreamSummaryString_(video));
388
- return false;
389
- }
390
-
391
- // See: https://github.com/google/shaka-player/issues/3380
392
- if (shaka.util.Platform.isXboxOne() && video &&
393
- ((video.width && video.width > 1920) ||
394
- (video.height && video.height > 1080)) &&
395
- video.codecs.includes('avc1.')) {
396
- shaka.log.debug('Dropping variant - video not compatible with platform',
397
- StreamUtils.getStreamSummaryString_(video));
398
- return false;
399
- }
400
-
401
- return true;
402
- });
403
- }
404
-
405
-
406
452
  /**
407
453
  * Get the decodingInfo results of the variants via MediaCapabilities.
408
454
  * This should be called after the DrmEngine is created and configured, and
@@ -501,6 +547,19 @@ shaka.util.StreamUtils = class {
501
547
  // framerate must be greater than 0, otherwise the config is invalid.
502
548
  framerate: video.frameRate || 1,
503
549
  };
550
+ if (video.hdr) {
551
+ switch (video.hdr) {
552
+ case 'SDR':
553
+ mediaDecodingConfig.video.transferFunction = 'srgb';
554
+ break;
555
+ case 'PQ':
556
+ mediaDecodingConfig.video.transferFunction = 'pq';
557
+ break;
558
+ case 'HLG':
559
+ mediaDecodingConfig.video.transferFunction = 'hlg';
560
+ break;
561
+ }
562
+ }
504
563
  }
505
564
  if (audio) {
506
565
  // Some Tizen devices seem to misreport AC-3 support, but correctly
@@ -1358,3 +1417,12 @@ shaka.util.StreamUtils = class {
1358
1417
 
1359
1418
  /** @private {number} */
1360
1419
  shaka.util.StreamUtils.nextTrackId_ = 0;
1420
+
1421
+ /**
1422
+ * @enum {string}
1423
+ */
1424
+ shaka.util.StreamUtils.DecodingAttributes = {
1425
+ SMOOTH: 'smooth',
1426
+ POWER: 'powerEfficient',
1427
+ BANDWIDTH: 'bandwidth',
1428
+ };