shaka-player 4.4.3 → 4.6.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 (307) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/workflows/release-please.yaml +2 -27
  3. package/AUTHORS +2 -0
  4. package/CHANGELOG.md +156 -39
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +54 -42
  8. package/demo/asset_card.js +18 -21
  9. package/demo/cast_receiver/index.html +0 -1
  10. package/demo/close_button.js +0 -3
  11. package/demo/common/asset.js +33 -22
  12. package/demo/common/assets.js +249 -60
  13. package/demo/config.js +219 -253
  14. package/demo/custom.js +88 -133
  15. package/demo/front.js +7 -10
  16. package/demo/index.html +19 -22
  17. package/demo/input.js +6 -11
  18. package/demo/input_container.js +8 -9
  19. package/demo/main.js +62 -126
  20. package/demo/search.js +29 -34
  21. package/demo/service_worker.js +0 -3
  22. package/demo/tooltip.js +3 -5
  23. package/demo/visualizer.js +3 -5
  24. package/demo/visualizer_button.js +1 -20
  25. package/dist/controls.css +1 -1
  26. package/dist/controls.css.map +1 -1
  27. package/dist/demo.compiled.debug.js +211 -228
  28. package/dist/demo.compiled.debug.map +4 -4
  29. package/dist/demo.compiled.js +211 -228
  30. package/dist/demo.compiled.map +4 -4
  31. package/dist/deps.js +42 -37
  32. package/dist/locales.js +34 -30
  33. package/dist/receiver.compiled.debug.js +64 -77
  34. package/dist/receiver.compiled.debug.map +4 -4
  35. package/dist/receiver.compiled.js +64 -77
  36. package/dist/receiver.compiled.map +4 -4
  37. package/dist/shaka-player.compiled.d.ts +343 -151
  38. package/dist/shaka-player.compiled.debug.d.ts +343 -151
  39. package/dist/shaka-player.compiled.debug.externs.js +356 -117
  40. package/dist/shaka-player.compiled.debug.js +952 -811
  41. package/dist/shaka-player.compiled.debug.map +4 -4
  42. package/dist/shaka-player.compiled.externs.js +356 -117
  43. package/dist/shaka-player.compiled.js +1165 -1078
  44. package/dist/shaka-player.compiled.map +4 -4
  45. package/dist/shaka-player.ui.d.ts +359 -153
  46. package/dist/shaka-player.ui.debug.d.ts +359 -153
  47. package/dist/shaka-player.ui.debug.externs.js +382 -117
  48. package/dist/shaka-player.ui.debug.js +1084 -918
  49. package/dist/shaka-player.ui.debug.map +4 -4
  50. package/dist/shaka-player.ui.externs.js +382 -117
  51. package/dist/shaka-player.ui.js +1377 -1274
  52. package/dist/shaka-player.ui.map +4 -4
  53. package/docs/design/lcevc-integration.md +14 -14
  54. package/docs/tutorials/ad_monetization.md +54 -0
  55. package/docs/tutorials/basic-usage.md +2 -1
  56. package/docs/tutorials/errors.md +2 -1
  57. package/docs/tutorials/faq.md +27 -55
  58. package/docs/tutorials/index.json +2 -1
  59. package/docs/tutorials/lcevc.md +118 -0
  60. package/docs/tutorials/offline.md +10 -8
  61. package/docs/tutorials/plugins.md +5 -0
  62. package/docs/tutorials/ui-customization.md +17 -0
  63. package/docs/tutorials/ui.md +2 -1
  64. package/docs/tutorials/upgrade.md +3 -0
  65. package/externs/css_style_declaration.js +19 -0
  66. package/externs/lcevc.js +37 -13
  67. package/externs/managedmediasource.js +24 -0
  68. package/externs/media_remote.js +68 -0
  69. package/externs/mediatailor.js +193 -0
  70. package/externs/pictureinpicture.js +11 -0
  71. package/externs/shaka/abr_manager.js +6 -2
  72. package/externs/shaka/ads.js +27 -0
  73. package/externs/shaka/cea.js +7 -0
  74. package/externs/shaka/codecs.js +5 -3
  75. package/externs/shaka/manifest.js +31 -10
  76. package/externs/shaka/manifest_parser.js +24 -1
  77. package/externs/shaka/net.js +2 -2
  78. package/externs/shaka/offline.js +13 -2
  79. package/externs/shaka/player.js +65 -8
  80. package/externs/shaka/text.js +3 -1
  81. package/externs/shaka/transmuxer.js +1 -1
  82. package/karma.conf.js +7 -3
  83. package/lib/abr/simple_abr_manager.js +66 -9
  84. package/lib/ads/ad_manager.js +104 -50
  85. package/lib/ads/media_tailor_ad.js +297 -0
  86. package/lib/ads/media_tailor_ad_manager.js +839 -0
  87. package/lib/cast/cast_receiver.js +1 -1
  88. package/lib/cea/cea_decoder.js +28 -0
  89. package/lib/cea/dummy_caption_decoder.js +5 -0
  90. package/lib/cea/ts_cea_parser.js +15 -2
  91. package/lib/config/codec_switching_strategy.js +24 -0
  92. package/lib/dash/content_protection.js +120 -2
  93. package/lib/dash/dash_parser.js +322 -93
  94. package/lib/dash/mpd_utils.js +62 -37
  95. package/lib/dash/segment_base.js +17 -10
  96. package/lib/dash/segment_list.js +18 -9
  97. package/lib/dash/segment_template.js +122 -107
  98. package/lib/dependencies/all.js +8 -7
  99. package/lib/hls/hls_classes.js +28 -43
  100. package/lib/hls/hls_parser.js +798 -658
  101. package/lib/hls/hls_utils.js +64 -5
  102. package/lib/hls/manifest_text_parser.js +7 -11
  103. package/lib/lcevc/lcevc_dec.js +220 -0
  104. package/lib/media/adaptation_set.js +32 -14
  105. package/lib/media/adaptation_set_criteria.js +86 -16
  106. package/lib/media/closed_caption_parser.js +13 -0
  107. package/lib/media/content_workarounds.js +6 -0
  108. package/lib/media/drm_engine.js +8 -13
  109. package/lib/media/manifest_parser.js +8 -113
  110. package/lib/media/media_source_capabilities.js +10 -3
  111. package/lib/media/media_source_engine.js +286 -44
  112. package/lib/media/play_rate_controller.js +8 -38
  113. package/lib/media/presentation_timeline.js +7 -1
  114. package/lib/media/quality_observer.js +1 -1
  115. package/lib/media/segment_index.js +6 -6
  116. package/lib/media/segment_prefetch.js +92 -24
  117. package/lib/media/segment_reference.js +73 -7
  118. package/lib/media/segment_utils.js +343 -0
  119. package/lib/media/stall_detector.js +0 -3
  120. package/lib/media/streaming_engine.js +73 -38
  121. package/lib/mss/mss_parser.js +19 -2
  122. package/lib/net/networking_engine.js +67 -12
  123. package/lib/net/networking_utils.js +95 -0
  124. package/lib/offline/indexeddb/v1_storage_cell.js +3 -0
  125. package/lib/offline/indexeddb/v2_storage_cell.js +3 -0
  126. package/lib/offline/manifest_converter.js +3 -0
  127. package/lib/offline/offline_manifest_parser.js +10 -0
  128. package/lib/offline/storage.js +14 -7
  129. package/lib/player.js +1098 -1537
  130. package/lib/polyfill/mediasource.js +22 -1
  131. package/lib/text/cue.js +15 -0
  132. package/lib/text/mp4_ttml_parser.js +2 -2
  133. package/lib/text/srt_text_parser.js +2 -2
  134. package/lib/text/stub_text_displayer.js +51 -0
  135. package/lib/text/text_engine.js +3 -2
  136. package/lib/text/ttml_text_parser.js +45 -5
  137. package/lib/text/ui_text_displayer.js +8 -0
  138. package/lib/text/vtt_text_parser.js +40 -22
  139. package/lib/transmuxer/aac_transmuxer.js +1 -1
  140. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  141. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  142. package/lib/transmuxer/h264.js +5 -4
  143. package/lib/transmuxer/h265.js +863 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  145. package/lib/transmuxer/mpeg_ts_transmuxer.js +4 -3
  146. package/lib/transmuxer/mss_transmuxer.js +1 -1
  147. package/lib/transmuxer/ts_transmuxer.js +187 -22
  148. package/lib/util/buffer_utils.js +26 -4
  149. package/lib/util/content_steering_manager.js +358 -0
  150. package/lib/util/error.js +30 -15
  151. package/lib/util/exp_golomb.js +30 -2
  152. package/lib/util/fairplay_utils.js +1 -1
  153. package/lib/util/fake_event.js +8 -1
  154. package/lib/util/id3_utils.js +50 -1
  155. package/lib/util/manifest_parser_utils.js +12 -8
  156. package/lib/util/mp4_generator.js +6 -8
  157. package/lib/util/mutex.js +59 -0
  158. package/lib/util/object_utils.js +1 -1
  159. package/lib/util/periods.js +156 -179
  160. package/lib/util/platform.js +44 -3
  161. package/lib/util/player_configuration.js +43 -6
  162. package/lib/util/state_history.js +6 -2
  163. package/lib/util/stream_utils.js +206 -283
  164. package/lib/util/ts_parser.js +335 -98
  165. package/lib/util/uint8array_utils.js +5 -1
  166. package/lib/util/xml_utils.js +5 -0
  167. package/maintained-branches.md +3 -2
  168. package/package.json +4 -6
  169. package/roadmap.md +25 -5
  170. package/shaka-player.uncompiled.js +2 -2
  171. package/support.html +0 -1
  172. package/test/abr/simple_abr_manager_unit.js +33 -33
  173. package/test/ads/ad_manager_unit.js +3 -2
  174. package/test/cast/cast_receiver_integration.js +50 -4
  175. package/test/cast/cast_utils_unit.js +2 -0
  176. package/test/cea/cea_decoder_unit.js +7 -0
  177. package/test/codec_switching/codec_switching_integration.js +182 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  179. package/test/dash/dash_parser_integration.js +74 -0
  180. package/test/dash/dash_parser_live_unit.js +2 -19
  181. package/test/dash/dash_parser_manifest_unit.js +536 -2
  182. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  183. package/test/dash/dash_parser_segment_list_unit.js +2 -0
  184. package/test/dash/dash_parser_segment_template_unit.js +2 -0
  185. package/test/dash/mpd_utils_unit.js +61 -30
  186. package/test/demo/demo_unit.js +0 -16
  187. package/test/hls/hls_live_unit.js +14 -3
  188. package/test/hls/hls_parser_unit.js +385 -33
  189. package/test/hls/manifest_text_parser_unit.js +32 -99
  190. package/test/media/adaptation_set_criteria_unit.js +284 -15
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/content_workarounds_unit.js +1 -1
  193. package/test/media/drm_engine_unit.js +3 -3
  194. package/test/media/media_source_engine_unit.js +166 -2
  195. package/test/media/play_rate_controller_unit.js +0 -48
  196. package/test/media/segment_index_unit.js +5 -4
  197. package/test/media/segment_prefetch_unit.js +1 -1
  198. package/test/media/streaming_engine_unit.js +17 -17
  199. package/test/mss/mss_parser_unit.js +2 -0
  200. package/test/mss/mss_player_integration.js +3 -2
  201. package/test/net/networking_engine_unit.js +3 -3
  202. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  203. package/test/offline/manifest_convert_unit.js +14 -0
  204. package/test/offline/offline_integration.js +2 -1
  205. package/test/offline/storage_integration.js +22 -13
  206. package/test/player_external.js +4 -2
  207. package/test/player_integration.js +15 -4
  208. package/test/player_load_graph_integration.js +47 -151
  209. package/test/player_src_equals_external.js +1 -0
  210. package/test/player_src_equals_integration.js +71 -4
  211. package/test/player_unit.js +182 -158
  212. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  213. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  214. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  215. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  216. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  217. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  218. package/test/test/assets/dash-aes-128/license +1 -0
  219. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  220. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  221. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  222. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  223. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  224. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  225. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  226. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  227. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  228. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  229. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  230. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  231. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  232. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  233. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  234. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  235. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  236. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  237. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  238. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  239. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  240. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  241. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  242. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  243. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  244. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  245. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  246. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  247. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  248. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  249. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  250. package/test/test/util/dash_parser_util.js +4 -0
  251. package/test/test/util/fake_ad_manager.js +20 -0
  252. package/test/test/util/fake_media_source_engine.js +13 -2
  253. package/test/test/util/fake_networking_engine.js +9 -1
  254. package/test/test/util/fake_segment_prefetch.js +3 -0
  255. package/test/test/util/manifest_generator.js +11 -3
  256. package/test/test/util/manifest_parser_util.js +14 -2
  257. package/test/test/util/mss_parser_util.js +4 -0
  258. package/test/test/util/offline_utils.js +2 -0
  259. package/test/test/util/simple_fakes.js +2 -2
  260. package/test/test/util/streaming_engine_util.js +10 -4
  261. package/test/test/util/test_scheme.js +6 -0
  262. package/test/test/util/ui_utils.js +4 -3
  263. package/test/text/mp4_ttml_parser_unit.js +4 -4
  264. package/test/text/srt_text_parser_unit.js +1 -1
  265. package/test/text/text_displayer_layout_unit.js +1 -1
  266. package/test/text/text_engine_unit.js +6 -0
  267. package/test/text/ttml_text_parser_unit.js +85 -2
  268. package/test/text/vtt_text_parser_unit.js +103 -31
  269. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  270. package/test/transmuxer/transmuxer_integration.js +150 -3
  271. package/test/ui/ad_ui_unit.js +2 -2
  272. package/test/ui/ui_customization_unit.js +17 -16
  273. package/test/ui/ui_integration.js +30 -20
  274. package/test/ui/ui_unit.js +28 -29
  275. package/test/util/buffer_utils_unit.js +1 -1
  276. package/test/util/content_steering_manager_unit.js +427 -0
  277. package/test/util/id3_utils_unit.js +52 -0
  278. package/test/util/stream_utils_unit.js +207 -169
  279. package/test/util/ts_parser_unit.js +3 -3
  280. package/third_party/closure-uri/uri.js +13 -1
  281. package/ui/audio_language_selection.js +9 -2
  282. package/ui/controls.js +125 -0
  283. package/ui/externs/ui.js +24 -2
  284. package/ui/language_utils.js +43 -8
  285. package/ui/less/range_elements.less +33 -0
  286. package/ui/locales/en.json +1 -0
  287. package/ui/locales/es-419.json +1 -0
  288. package/ui/locales/es.json +1 -0
  289. package/ui/locales/source.json +4 -0
  290. package/ui/localization.js +10 -0
  291. package/ui/pip_button.js +22 -120
  292. package/ui/remote_button.js +203 -0
  293. package/ui/resolution_selection.js +47 -10
  294. package/ui/seek_bar.js +296 -3
  295. package/ui/text_selection.js +2 -1
  296. package/ui/ui.js +11 -4
  297. package/demo/common/message_ids.js +0 -309
  298. package/demo/locales/en.json +0 -288
  299. package/demo/locales/source.json +0 -1146
  300. package/externs/mux.js +0 -69
  301. package/lib/lcevc/lcevc_dil.js +0 -152
  302. package/lib/routing/node.js +0 -23
  303. package/lib/routing/payload.js +0 -43
  304. package/lib/routing/walker.js +0 -587
  305. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  306. package/test/routing/walker_unit.js +0 -504
  307. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -31,110 +31,165 @@ shaka.util.StreamUtils = class {
31
31
  * @param {!shaka.extern.Manifest} manifest
32
32
  * @param {!Array.<string>} preferredVideoCodecs
33
33
  * @param {!Array.<string>} preferredAudioCodecs
34
- * @param {number} preferredAudioChannelCount
35
34
  * @param {!Array.<string>} preferredDecodingAttributes
36
35
  */
37
36
  static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
38
- preferredAudioCodecs, preferredAudioChannelCount,
39
- preferredDecodingAttributes) {
37
+ preferredAudioCodecs, preferredDecodingAttributes) {
40
38
  const StreamUtils = shaka.util.StreamUtils;
39
+ const MimeUtils = shaka.util.MimeUtils;
41
40
 
42
41
  let variants = manifest.variants;
43
-
44
42
  // To start, choose the codecs based on configured preferences if available.
45
43
  if (preferredVideoCodecs.length || preferredAudioCodecs.length) {
46
44
  variants = StreamUtils.choosePreferredCodecs(variants,
47
45
  preferredVideoCodecs, preferredAudioCodecs);
48
46
  }
49
47
 
50
- // Consider a subset of variants based on audio channel
51
- // preferences.
52
- // For some content (#1013), surround-sound variants will use a different
53
- // codec than stereo variants, so it is important to choose codecs **after**
54
- // considering the audio channel config.
55
- variants = StreamUtils.filterVariantsByAudioChannelCount(
56
- variants, preferredAudioChannelCount);
57
-
58
- // Now organize variants into buckets by codecs.
59
- /** @type {!shaka.util.MultiMap.<shaka.extern.Variant>} */
60
- let variantsByCodecs = StreamUtils.getVariantsByCodecs_(variants);
61
- variantsByCodecs = StreamUtils.filterVariantsByDensity_(variantsByCodecs);
62
-
63
- const bestCodecs = StreamUtils.chooseCodecsByDecodingAttributes_(
64
- variantsByCodecs, preferredDecodingAttributes);
48
+ if (preferredDecodingAttributes.length) {
49
+ // group variants by resolution and choose preferred variants only
50
+ /** @type {!shaka.util.MultiMap.<shaka.extern.Variant>} */
51
+ const variantsByResolutionMap = new shaka.util.MultiMap();
52
+ for (const variant of variants) {
53
+ variantsByResolutionMap
54
+ .push(String(variant.video.width || 0), variant);
55
+ }
56
+ const bestVariants = [];
57
+ variantsByResolutionMap.forEach((width, variantsByResolution) => {
58
+ let highestMatch = 0;
59
+ let matchingVariants = [];
60
+ for (const variant of variantsByResolution) {
61
+ const matchCount = preferredDecodingAttributes.filter(
62
+ (attribute) => variant.decodingInfos[0][attribute],
63
+ ).length;
64
+ if (matchCount > highestMatch) {
65
+ highestMatch = matchCount;
66
+ matchingVariants = [variant];
67
+ } else if (matchCount == highestMatch) {
68
+ matchingVariants.push(variant);
69
+ }
70
+ }
71
+ bestVariants.push(...matchingVariants);
72
+ });
73
+ variants = bestVariants;
74
+ }
65
75
 
66
- // Filter out any variants that don't match, forcing AbrManager to choose
67
- // from a single video codec and a single audio codec possible.
68
- manifest.variants = manifest.variants.filter((variant) => {
69
- const codecs = StreamUtils.getVariantCodecs_(variant);
70
- if (codecs == bestCodecs) {
71
- return true;
76
+ const audioStreamsSet = new Set();
77
+ const videoStreamsSet = new Set();
78
+ for (const variant of variants) {
79
+ if (variant.audio) {
80
+ audioStreamsSet.add(variant.audio);
81
+ }
82
+ if (variant.video) {
83
+ videoStreamsSet.add(variant.video);
72
84
  }
85
+ }
73
86
 
74
- shaka.log.debug('Dropping Variant (better codec available)', variant);
75
- return false;
87
+ const audioStreams = Array.from(audioStreamsSet).sort((v1, v2) => {
88
+ return v1.bandwidth - v2.bandwidth;
76
89
  });
77
- }
78
-
79
- /**
80
- * Get variants by codecs.
81
- *
82
- * @param {!Array<shaka.extern.Variant>} variants
83
- * @return {!shaka.util.MultiMap.<shaka.extern.Variant>}
84
- * @private
85
- */
86
- static getVariantsByCodecs_(variants) {
87
- const variantsByCodecs = new shaka.util.MultiMap();
88
- for (const variant of variants) {
89
- const variantCodecs = shaka.util.StreamUtils.getVariantCodecs_(variant);
90
- variantsByCodecs.push(variantCodecs, variant);
90
+ const validAudioIds = [];
91
+ const validAudioStreamsMap = new Map();
92
+ const getAudioId = (stream) => {
93
+ return stream.language + (stream.channelsCount || 0) +
94
+ (stream.audioSamplingRate || 0) + stream.roles.join(',') +
95
+ stream.label + stream.groupId + stream.fastSwitching;
96
+ };
97
+ for (const stream of audioStreams) {
98
+ const groupId = getAudioId(stream);
99
+ const validAudioStreams = validAudioStreamsMap.get(groupId) || [];
100
+ if (!validAudioStreams.length) {
101
+ validAudioStreams.push(stream);
102
+ validAudioIds.push(stream.id);
103
+ } else {
104
+ const previousStream = validAudioStreams[validAudioStreams.length - 1];
105
+ const previousCodec =
106
+ MimeUtils.getNormalizedCodec(previousStream.codecs);
107
+ const currentCodec =
108
+ MimeUtils.getNormalizedCodec(stream.codecs);
109
+ if (previousCodec == currentCodec) {
110
+ if (stream.bandwidth > previousStream.bandwidth) {
111
+ validAudioStreams.push(stream);
112
+ validAudioIds.push(stream.id);
113
+ }
114
+ }
115
+ }
116
+ validAudioStreamsMap.set(groupId, validAudioStreams);
91
117
  }
92
118
 
93
- return variantsByCodecs;
94
- }
119
+ const videoStreams = Array.from(videoStreamsSet)
120
+ .sort((v1, v2) => {
121
+ if (!v1.bandwidth || !v2.bandwidth) {
122
+ return v1.width - v2.width;
123
+ }
124
+ return v1.bandwidth - v2.bandwidth;
125
+ });
95
126
 
96
- /**
97
- * Filters variants by density.
98
- * Get variants by codecs map with the max density where all codecs are
99
- * present.
100
- *
101
- * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
102
- * @return {!shaka.util.MultiMap.<shaka.extern.Variant>}
103
- * @private
104
- */
105
- static filterVariantsByDensity_(variantsByCodecs) {
106
- let maxDensity = 0;
107
- const codecGroupsByDensity = new Map();
108
- const countCodecs = variantsByCodecs.size();
109
-
110
- variantsByCodecs.forEach((codecs, variants) => {
111
- for (const variant of variants) {
112
- const video = variant.video;
113
- if (!video || !video.width || !video.height) {
114
- continue;
115
- }
127
+ const isChangeTypeSupported =
128
+ shaka.media.Capabilities.isChangeTypeSupported();
116
129
 
117
- const density = video.width * video.height * (video.frameRate || 1);
118
- if (!codecGroupsByDensity.has(density)) {
119
- codecGroupsByDensity.set(density, new shaka.util.MultiMap());
130
+ const validVideoIds = [];
131
+ const validVideoStreamsMap = new Map();
132
+ const getVideoGroupId = (stream) => {
133
+ return Math.round(stream.frameRate || 0) + (stream.hdr || '') +
134
+ stream.fastSwitching;
135
+ };
136
+ for (const stream of videoStreams) {
137
+ const groupId = getVideoGroupId(stream);
138
+ const validVideoStreams = validVideoStreamsMap.get(groupId) || [];
139
+ if (!validVideoStreams.length) {
140
+ validVideoStreams.push(stream);
141
+ validVideoIds.push(stream.id);
142
+ } else {
143
+ const previousStream = validVideoStreams[validVideoStreams.length - 1];
144
+ if (!isChangeTypeSupported) {
145
+ const previousCodec =
146
+ MimeUtils.getNormalizedCodec(previousStream.codecs);
147
+ const currentCodec =
148
+ MimeUtils.getNormalizedCodec(stream.codecs);
149
+ if (previousCodec !== currentCodec) {
150
+ continue;
151
+ }
152
+ }
153
+ if (stream.width > previousStream.width ||
154
+ stream.height > previousStream.height) {
155
+ validVideoStreams.push(stream);
156
+ validVideoIds.push(stream.id);
157
+ } else if (stream.width == previousStream.width &&
158
+ stream.height == previousStream.height) {
159
+ const previousCodec =
160
+ MimeUtils.getNormalizedCodec(previousStream.codecs);
161
+ const currentCodec =
162
+ MimeUtils.getNormalizedCodec(stream.codecs);
163
+ if (previousCodec == currentCodec) {
164
+ if (stream.bandwidth < previousStream.bandwidth) {
165
+ validVideoStreams.push(stream);
166
+ validVideoIds.push(stream.id);
167
+ }
168
+ }
120
169
  }
170
+ }
171
+ validVideoStreamsMap.set(groupId, validVideoStreams);
172
+ }
121
173
 
122
- /** @type {!shaka.util.MultiMap.<shaka.extern.Variant>} */
123
- const group = codecGroupsByDensity.get(density);
124
- group.push(codecs, variant);
125
-
126
- // We want to look at the groups in which all codecs are present.
127
- // Take the max density from those groups where all codecs are present.
128
- // Later, we will compare bandwidth numbers only within this group.
129
- // Effectively, only the bandwidth differences in the highest-res and
130
- // highest-framerate content will matter in choosing a codec.
131
- if (group.size() === countCodecs) {
132
- maxDensity = Math.max(maxDensity, density);
174
+ // Filter out any variants that don't match, forcing AbrManager to choose
175
+ // from a single video codec and a single audio codec possible.
176
+ manifest.variants = manifest.variants.filter((variant) => {
177
+ const audio = variant.audio;
178
+ const video = variant.video;
179
+ if (audio) {
180
+ if (!validAudioIds.includes(audio.id)) {
181
+ shaka.log.debug('Dropping Variant (better codec available)', variant);
182
+ return false;
133
183
  }
134
184
  }
185
+ if (video) {
186
+ if (!validVideoIds.includes(video.id)) {
187
+ shaka.log.debug('Dropping Variant (better codec available)', variant);
188
+ return false;
189
+ }
190
+ }
191
+ return true;
135
192
  });
136
-
137
- return maxDensity ? codecGroupsByDensity.get(maxDensity) : variantsByCodecs;
138
193
  }
139
194
 
140
195
  /**
@@ -170,135 +225,6 @@ shaka.util.StreamUtils = class {
170
225
  return subset;
171
226
  }
172
227
 
173
- /**
174
- * Choose the codecs by configured preferred decoding attributes.
175
- *
176
- * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
177
- * @param {!Array.<string>} attributes
178
- * @return {string}
179
- * @private
180
- */
181
- static chooseCodecsByDecodingAttributes_(variantsByCodecs, attributes) {
182
- const StreamUtils = shaka.util.StreamUtils;
183
-
184
- for (const attribute of attributes) {
185
- if (attribute == StreamUtils.DecodingAttributes.SMOOTH ||
186
- attribute == StreamUtils.DecodingAttributes.POWER) {
187
- variantsByCodecs = StreamUtils.chooseCodecsByMediaCapabilitiesInfo_(
188
- variantsByCodecs, attribute);
189
- // If we only have one smooth or powerEfficient codecs, choose it as the
190
- // best codecs.
191
- if (variantsByCodecs.size() == 1) {
192
- return variantsByCodecs.keys()[0];
193
- }
194
- } else if (attribute == StreamUtils.DecodingAttributes.BANDWIDTH) {
195
- return StreamUtils.findCodecsByLowestBandwidth_(variantsByCodecs);
196
- }
197
- }
198
- // If there's no configured decoding preferences, or we have multiple codecs
199
- // that meets the configured decoding preferences, choose the one with
200
- // the lowest bandwidth.
201
- return StreamUtils.findCodecsByLowestBandwidth_(variantsByCodecs);
202
- }
203
-
204
- /**
205
- * Choose the best codecs by configured preferred MediaCapabilitiesInfo
206
- * attributes.
207
- *
208
- * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
209
- * @param {string} attribute
210
- * @return {!shaka.util.MultiMap.<shaka.extern.Variant>}
211
- * @private
212
- */
213
- static chooseCodecsByMediaCapabilitiesInfo_(variantsByCodecs, attribute) {
214
- let highestScore = 0;
215
- const bestVariantsByCodecs = new shaka.util.MultiMap();
216
- variantsByCodecs.forEach((codecs, variants) => {
217
- let sum = 0;
218
- let num = 0;
219
-
220
- for (const variant of variants) {
221
- if (variant.decodingInfos.length) {
222
- sum += variant.decodingInfos[0][attribute] ? 1 : 0;
223
- num++;
224
- }
225
- }
226
-
227
- const averageScore = sum / num;
228
- shaka.log.debug('codecs', codecs, 'avg', attribute, averageScore);
229
-
230
- if (averageScore > highestScore) {
231
- bestVariantsByCodecs.clear();
232
- bestVariantsByCodecs.push(codecs, variants);
233
- highestScore = averageScore;
234
- } else if (averageScore == highestScore) {
235
- bestVariantsByCodecs.push(codecs, variants);
236
- }
237
- });
238
- return bestVariantsByCodecs;
239
- }
240
-
241
- /**
242
- * Find the lowest-bandwidth (best) codecs.
243
- * Compute the average bandwidth for each group of variants.
244
- *
245
- * @param {!shaka.util.MultiMap.<shaka.extern.Variant>} variantsByCodecs
246
- * @return {string}
247
- * @private
248
- */
249
- static findCodecsByLowestBandwidth_(variantsByCodecs) {
250
- let bestCodecs = '';
251
- let lowestAverageBandwidth = Infinity;
252
-
253
- variantsByCodecs.forEach((codecs, variants) => {
254
- let sum = 0;
255
- let num = 0;
256
- for (const variant of variants) {
257
- sum += variant.bandwidth || 0;
258
- ++num;
259
- }
260
-
261
- const averageBandwidth = sum / num;
262
- shaka.log.debug('codecs', codecs, 'avg bandwidth', averageBandwidth);
263
-
264
- if (averageBandwidth < lowestAverageBandwidth) {
265
- bestCodecs = codecs;
266
- lowestAverageBandwidth = averageBandwidth;
267
- }
268
- });
269
-
270
- goog.asserts.assert(bestCodecs !== '', 'Should have chosen codecs!');
271
- goog.asserts.assert(!isNaN(lowestAverageBandwidth),
272
- 'Bandwidth should be a number!');
273
-
274
- return bestCodecs;
275
- }
276
-
277
- /**
278
- * Get a string representing all codecs used in a variant.
279
- *
280
- * @param {!shaka.extern.Variant} variant
281
- * @return {string}
282
- * @private
283
- */
284
- static getVariantCodecs_(variant) {
285
- // Only consider the base of the codec string. For example, these should
286
- // both be considered the same codec: avc1.42c01e, avc1.4d401f
287
- let baseVideoCodec = '';
288
- if (variant.video) {
289
- baseVideoCodec =
290
- shaka.util.MimeUtils.getNormalizedCodec(variant.video.codecs);
291
- }
292
-
293
- let baseAudioCodec = '';
294
- if (variant.audio) {
295
- baseAudioCodec =
296
- shaka.util.MimeUtils.getNormalizedCodec(variant.audio.codecs);
297
- }
298
-
299
- return baseVideoCodec + '-' + baseAudioCodec;
300
- }
301
-
302
228
  /**
303
229
  * Filter the variants in |manifest| to only include the variants that meet
304
230
  * the given restrictions.
@@ -402,14 +328,11 @@ shaka.util.StreamUtils = class {
402
328
  * Alters the given Manifest to filter out any unplayable streams.
403
329
  *
404
330
  * @param {shaka.media.DrmEngine} drmEngine
405
- * @param {?shaka.extern.Variant} currentVariant
406
331
  * @param {shaka.extern.Manifest} manifest
407
332
  */
408
- static async filterManifest(drmEngine, currentVariant, manifest) {
409
- await shaka.util.StreamUtils.filterManifestByMediaCapabilities(manifest,
410
- manifest.offlineSessionIds.length > 0);
411
- shaka.util.StreamUtils.filterManifestByCurrentVariant(
412
- currentVariant, manifest);
333
+ static async filterManifest(drmEngine, manifest) {
334
+ await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
335
+ drmEngine, manifest, manifest.offlineSessionIds.length > 0);
413
336
  shaka.util.StreamUtils.filterTextStreams_(manifest);
414
337
  await shaka.util.StreamUtils.filterImageStreams_(manifest);
415
338
  }
@@ -419,30 +342,53 @@ shaka.util.StreamUtils = class {
419
342
  * Alters the given Manifest to filter out any streams unsupported by the
420
343
  * platform via MediaCapabilities.decodingInfo() API.
421
344
  *
345
+ * @param {shaka.media.DrmEngine} drmEngine
422
346
  * @param {shaka.extern.Manifest} manifest
423
347
  * @param {boolean} usePersistentLicenses
424
348
  */
425
349
  static async filterManifestByMediaCapabilities(
426
- manifest, usePersistentLicenses) {
350
+ drmEngine, manifest, usePersistentLicenses) {
427
351
  goog.asserts.assert(navigator.mediaCapabilities,
428
352
  'MediaCapabilities should be valid.');
429
353
 
430
354
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
431
355
  manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
432
356
  /** preferredKeySystems= */ []);
357
+
358
+ let keySystem = null;
359
+ if (drmEngine) {
360
+ const drmInfo = drmEngine.getDrmInfo();
361
+ if (drmInfo) {
362
+ keySystem = drmInfo.keySystem;
363
+ }
364
+ }
365
+
366
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
367
+ const Capabilities = shaka.media.Capabilities;
368
+ const ManifestParserUtils = shaka.util.ManifestParserUtils;
369
+ const MimeUtils = shaka.util.MimeUtils;
370
+ const StreamUtils = shaka.util.StreamUtils;
371
+ const isXboxOne = shaka.util.Platform.isXboxOne();
372
+
433
373
  manifest.variants = manifest.variants.filter((variant) => {
434
374
  // See: https://github.com/shaka-project/shaka-player/issues/3860
435
375
  const video = variant.video;
376
+ const videoWidth = (video && video.width) || 0;
377
+ const videoHeight = (video && video.height) || 0;
436
378
 
437
- const ContentType = shaka.util.ManifestParserUtils.ContentType;
438
- const Capabilities = shaka.media.Capabilities;
439
- const ManifestParserUtils = shaka.util.ManifestParserUtils;
440
- const MimeUtils = shaka.util.MimeUtils;
441
- const StreamUtils = shaka.util.StreamUtils;
379
+ // See: https://github.com/shaka-project/shaka-player/issues/3380
380
+ // Note: it makes sense to drop early
381
+ if (isXboxOne && video &&
382
+ (videoWidth > 1920 || videoHeight > 1080) &&
383
+ (video.codecs.includes('avc1.') || video.codecs.includes('avc3.'))
384
+ ) {
385
+ shaka.log.debug('Dropping variant - not compatible with platform',
386
+ StreamUtils.getVariantSummaryString_(variant));
387
+ return false;
388
+ }
442
389
 
443
390
  if (video) {
444
391
  let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
445
-
446
392
  // For multiplexed streams. Here we must check the audio of the
447
393
  // stream to see if it is compatible.
448
394
  if (video.codecs.includes(',')) {
@@ -451,7 +397,6 @@ shaka.util.StreamUtils = class {
451
397
  videoCodecs = ManifestParserUtils.guessCodecs(
452
398
  ContentType.VIDEO, allCodecs);
453
399
  videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
454
-
455
400
  let audioCodecs = ManifestParserUtils.guessCodecs(
456
401
  ContentType.AUDIO, allCodecs);
457
402
  audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
@@ -493,19 +438,19 @@ shaka.util.StreamUtils = class {
493
438
  audio.codecs = codecs;
494
439
  }
495
440
 
496
- // See: https://github.com/shaka-project/shaka-player/issues/3380
497
- if (shaka.util.Platform.isXboxOne() && video &&
498
- ((video.width && video.width > 1920) ||
499
- (video.height && video.height > 1080)) &&
500
- (video.codecs.includes('avc1.') ||
501
- video.codecs.includes('avc3.'))) {
502
- shaka.log.debug('Dropping variant - not compatible with platform',
503
- StreamUtils.getVariantSummaryString_(variant));
504
- return false;
505
- }
506
-
507
441
  const supported = variant.decodingInfos.some((decodingInfo) => {
508
- return decodingInfo.supported;
442
+ if (!decodingInfo.supported) {
443
+ return false;
444
+ }
445
+ if (keySystem) {
446
+ const keySystemAccess = decodingInfo.keySystemAccess;
447
+ if (keySystemAccess) {
448
+ if (keySystemAccess.keySystem != keySystem) {
449
+ return false;
450
+ }
451
+ }
452
+ }
453
+ return true;
509
454
  });
510
455
  // Filter out all unsupported variants.
511
456
  if (!supported) {
@@ -1013,7 +958,10 @@ shaka.util.StreamUtils = class {
1013
958
  static async filterImageStreams_(manifest) {
1014
959
  const imageStreams = [];
1015
960
  for (const stream of manifest.imageStreams) {
1016
- const mimeType = stream.mimeType;
961
+ let mimeType = stream.mimeType;
962
+ if (mimeType == 'application/mp4' && stream.codecs == 'mjpg') {
963
+ mimeType = 'image/jpg';
964
+ }
1017
965
  if (!shaka.util.StreamUtils.supportedImageMimeTypes_.has(mimeType)) {
1018
966
  const minImage = shaka.util.StreamUtils.minImage_.get(mimeType);
1019
967
  if (minImage) {
@@ -1163,6 +1111,7 @@ shaka.util.StreamUtils = class {
1163
1111
  frameRate: null,
1164
1112
  pixelAspectRatio: null,
1165
1113
  hdr: null,
1114
+ videoLayout: null,
1166
1115
  mimeType: mimeType,
1167
1116
  audioMimeType: audioMimeType,
1168
1117
  videoMimeType: videoMimeType,
@@ -1198,6 +1147,7 @@ shaka.util.StreamUtils = class {
1198
1147
  track.pixelAspectRatio = video.pixelAspectRatio || null;
1199
1148
  track.videoBandwidth = video.bandwidth || null;
1200
1149
  track.hdr = video.hdr || null;
1150
+ track.videoLayout = video.videoLayout || null;
1201
1151
  }
1202
1152
 
1203
1153
  if (audio) {
@@ -1238,6 +1188,7 @@ shaka.util.StreamUtils = class {
1238
1188
  frameRate: null,
1239
1189
  pixelAspectRatio: null,
1240
1190
  hdr: null,
1191
+ videoLayout: null,
1241
1192
  mimeType: stream.mimeType,
1242
1193
  audioMimeType: null,
1243
1194
  videoMimeType: null,
@@ -1314,10 +1265,11 @@ shaka.util.StreamUtils = class {
1314
1265
  frameRate: null,
1315
1266
  pixelAspectRatio: null,
1316
1267
  hdr: null,
1268
+ videoLayout: null,
1317
1269
  mimeType: stream.mimeType,
1318
1270
  audioMimeType: null,
1319
1271
  videoMimeType: null,
1320
- codecs: null,
1272
+ codecs: stream.codecs || null,
1321
1273
  audioCodec: null,
1322
1274
  videoCodec: null,
1323
1275
  primary: false,
@@ -1441,6 +1393,7 @@ shaka.util.StreamUtils = class {
1441
1393
  frameRate: null,
1442
1394
  pixelAspectRatio: null,
1443
1395
  hdr: null,
1396
+ videoLayout: null,
1444
1397
  mimeType: null,
1445
1398
  audioMimeType: null,
1446
1399
  videoMimeType: null,
@@ -1495,52 +1448,6 @@ shaka.util.StreamUtils = class {
1495
1448
  }
1496
1449
 
1497
1450
 
1498
- /**
1499
- * Filters variants according to the given audio channel count config.
1500
- *
1501
- * @param {!Array.<shaka.extern.Variant>} variants
1502
- * @param {number} preferredAudioChannelCount
1503
- * @return {!Array.<!shaka.extern.Variant>}
1504
- */
1505
- static filterVariantsByAudioChannelCount(
1506
- variants, preferredAudioChannelCount) {
1507
- // Group variants by their audio channel counts.
1508
- const variantsWithChannelCounts =
1509
- variants.filter((v) => v.audio && v.audio.channelsCount);
1510
-
1511
- /** @type {!Map.<number, !Array.<shaka.extern.Variant>>} */
1512
- const variantsByChannelCount = new Map();
1513
- for (const variant of variantsWithChannelCounts) {
1514
- const count = variant.audio.channelsCount;
1515
- goog.asserts.assert(count != null, 'Must have count after filtering!');
1516
- if (!variantsByChannelCount.has(count)) {
1517
- variantsByChannelCount.set(count, []);
1518
- }
1519
- variantsByChannelCount.get(count).push(variant);
1520
- }
1521
-
1522
- /** @type {!Array.<number>} */
1523
- const channelCounts = Array.from(variantsByChannelCount.keys());
1524
-
1525
- // If no variant has audio channel count info, return the original variants.
1526
- if (channelCounts.length == 0) {
1527
- return variants;
1528
- }
1529
-
1530
- // Choose the variants with the largest number of audio channels less than
1531
- // or equal to the configured number of audio channels.
1532
- const countLessThanOrEqualtoConfig =
1533
- channelCounts.filter((count) => count <= preferredAudioChannelCount);
1534
- if (countLessThanOrEqualtoConfig.length) {
1535
- return variantsByChannelCount.get(
1536
- Math.max(...countLessThanOrEqualtoConfig));
1537
- }
1538
-
1539
- // If all variants have more audio channels than the config, choose the
1540
- // variants with the fewest audio channels.
1541
- return variantsByChannelCount.get(Math.min(...channelCounts));
1542
- }
1543
-
1544
1451
  /**
1545
1452
  * Chooses streams according to the given config.
1546
1453
  * Works both for Stream and Track types due to their similarities.
@@ -1691,6 +1598,23 @@ shaka.util.StreamUtils = class {
1691
1598
  }
1692
1599
 
1693
1600
 
1601
+ /**
1602
+ * Indicates if some of the variant's streams are fastSwitching.
1603
+ *
1604
+ * @param {shaka.extern.Variant} variant
1605
+ * @return {boolean}
1606
+ */
1607
+ static isFastSwitching(variant) {
1608
+ if (variant.audio && variant.audio.fastSwitching) {
1609
+ return true;
1610
+ }
1611
+ if (variant.video && variant.video.fastSwitching) {
1612
+ return true;
1613
+ }
1614
+ return false;
1615
+ }
1616
+
1617
+
1694
1618
  /**
1695
1619
  * Returns a string of a variant, with the attribute values of its audio
1696
1620
  * and/or video streams for log printing.
@@ -1762,7 +1686,6 @@ shaka.util.StreamUtils.nextTrackId_ = 0;
1762
1686
  shaka.util.StreamUtils.DecodingAttributes = {
1763
1687
  SMOOTH: 'smooth',
1764
1688
  POWER: 'powerEfficient',
1765
- BANDWIDTH: 'bandwidth',
1766
1689
  };
1767
1690
 
1768
1691
  /**