shaka-player 4.5.0 → 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 (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +98 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +35 -31
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +786 -736
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1154 -1121
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +877 -811
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1362 -1318
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +249 -83
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +83 -26
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +761 -649
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +41 -21
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +993 -1502
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +377 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -7,7 +7,6 @@
7
7
  goog.provide('shaka.util.StreamUtils');
8
8
 
9
9
  goog.require('goog.asserts');
10
- goog.require('shaka.config.CodecSwitchingStrategy');
11
10
  goog.require('shaka.log');
12
11
  goog.require('shaka.media.Capabilities');
13
12
  goog.require('shaka.text.TextEngine');
@@ -32,12 +31,10 @@ shaka.util.StreamUtils = class {
32
31
  * @param {!shaka.extern.Manifest} manifest
33
32
  * @param {!Array.<string>} preferredVideoCodecs
34
33
  * @param {!Array.<string>} preferredAudioCodecs
35
- * @param {number} preferredAudioChannelCount
36
34
  * @param {!Array.<string>} preferredDecodingAttributes
37
35
  */
38
36
  static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
39
- preferredAudioCodecs, preferredAudioChannelCount,
40
- preferredDecodingAttributes) {
37
+ preferredAudioCodecs, preferredDecodingAttributes) {
41
38
  const StreamUtils = shaka.util.StreamUtils;
42
39
  const MimeUtils = shaka.util.MimeUtils;
43
40
 
@@ -76,11 +73,6 @@ shaka.util.StreamUtils = class {
76
73
  variants = bestVariants;
77
74
  }
78
75
 
79
- if (preferredAudioChannelCount) {
80
- variants = StreamUtils.filterVariantsByAudioChannelCount(
81
- variants, preferredAudioChannelCount);
82
- }
83
-
84
76
  const audioStreamsSet = new Set();
85
77
  const videoStreamsSet = new Set();
86
78
  for (const variant of variants) {
@@ -95,23 +87,34 @@ shaka.util.StreamUtils = class {
95
87
  const audioStreams = Array.from(audioStreamsSet).sort((v1, v2) => {
96
88
  return v1.bandwidth - v2.bandwidth;
97
89
  });
98
- const audioStreamsMap = new Map();
90
+ const validAudioIds = [];
91
+ const validAudioStreamsMap = new Map();
99
92
  const getAudioId = (stream) => {
100
93
  return stream.language + (stream.channelsCount || 0) +
101
- stream.roles.join(',') + stream.label;
94
+ (stream.audioSamplingRate || 0) + stream.roles.join(',') +
95
+ stream.label + stream.groupId + stream.fastSwitching;
102
96
  };
103
97
  for (const stream of audioStreams) {
104
- const id = getAudioId(stream);
105
- if (!audioStreamsMap.has(id)) {
106
- audioStreamsMap.set(id, stream);
98
+ const groupId = getAudioId(stream);
99
+ const validAudioStreams = validAudioStreamsMap.get(groupId) || [];
100
+ if (!validAudioStreams.length) {
101
+ validAudioStreams.push(stream);
102
+ validAudioIds.push(stream.id);
107
103
  } else {
108
- const includedStream = audioStreamsMap.get(id);
109
- if (includedStream.bandwidth > stream.bandwidth) {
110
- audioStreamsMap.set(id, stream);
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
+ }
111
114
  }
112
115
  }
116
+ validAudioStreamsMap.set(groupId, validAudioStreams);
113
117
  }
114
- const validAudioStreams = [...audioStreamsMap.values()];
115
118
 
116
119
  const videoStreams = Array.from(videoStreamsSet)
117
120
  .sort((v1, v2) => {
@@ -124,10 +127,18 @@ shaka.util.StreamUtils = class {
124
127
  const isChangeTypeSupported =
125
128
  shaka.media.Capabilities.isChangeTypeSupported();
126
129
 
127
- const validVideoStreams = [];
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
+ };
128
136
  for (const stream of videoStreams) {
137
+ const groupId = getVideoGroupId(stream);
138
+ const validVideoStreams = validVideoStreamsMap.get(groupId) || [];
129
139
  if (!validVideoStreams.length) {
130
140
  validVideoStreams.push(stream);
141
+ validVideoIds.push(stream.id);
131
142
  } else {
132
143
  const previousStream = validVideoStreams[validVideoStreams.length - 1];
133
144
  if (!isChangeTypeSupported) {
@@ -142,6 +153,7 @@ shaka.util.StreamUtils = class {
142
153
  if (stream.width > previousStream.width ||
143
154
  stream.height > previousStream.height) {
144
155
  validVideoStreams.push(stream);
156
+ validVideoIds.push(stream.id);
145
157
  } else if (stream.width == previousStream.width &&
146
158
  stream.height == previousStream.height) {
147
159
  const previousCodec =
@@ -151,17 +163,14 @@ shaka.util.StreamUtils = class {
151
163
  if (previousCodec == currentCodec) {
152
164
  if (stream.bandwidth < previousStream.bandwidth) {
153
165
  validVideoStreams.push(stream);
166
+ validVideoIds.push(stream.id);
154
167
  }
155
168
  }
156
169
  }
157
170
  }
171
+ validVideoStreamsMap.set(groupId, validVideoStreams);
158
172
  }
159
173
 
160
- const validAudioIds =
161
- validAudioStreams.map((audioStream) => audioStream.id);
162
- const validVideoIds =
163
- validVideoStreams.map((videoStream) => videoStream.id);
164
-
165
174
  // Filter out any variants that don't match, forcing AbrManager to choose
166
175
  // from a single video codec and a single audio codec possible.
167
176
  manifest.variants = manifest.variants.filter((variant) => {
@@ -319,14 +328,11 @@ shaka.util.StreamUtils = class {
319
328
  * Alters the given Manifest to filter out any unplayable streams.
320
329
  *
321
330
  * @param {shaka.media.DrmEngine} drmEngine
322
- * @param {?shaka.extern.Variant} currentVariant
323
331
  * @param {shaka.extern.Manifest} manifest
324
- * @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
325
332
  */
326
- static async filterManifest(drmEngine, currentVariant, manifest,
327
- codecSwitchingStrategy=shaka.config.CodecSwitchingStrategy.RELOAD) {
328
- await shaka.util.StreamUtils.filterManifestByMediaCapabilities(manifest,
329
- manifest.offlineSessionIds.length > 0);
333
+ static async filterManifest(drmEngine, manifest) {
334
+ await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
335
+ drmEngine, manifest, manifest.offlineSessionIds.length > 0);
330
336
  shaka.util.StreamUtils.filterTextStreams_(manifest);
331
337
  await shaka.util.StreamUtils.filterImageStreams_(manifest);
332
338
  }
@@ -336,30 +342,53 @@ shaka.util.StreamUtils = class {
336
342
  * Alters the given Manifest to filter out any streams unsupported by the
337
343
  * platform via MediaCapabilities.decodingInfo() API.
338
344
  *
345
+ * @param {shaka.media.DrmEngine} drmEngine
339
346
  * @param {shaka.extern.Manifest} manifest
340
347
  * @param {boolean} usePersistentLicenses
341
348
  */
342
349
  static async filterManifestByMediaCapabilities(
343
- manifest, usePersistentLicenses) {
350
+ drmEngine, manifest, usePersistentLicenses) {
344
351
  goog.asserts.assert(navigator.mediaCapabilities,
345
352
  'MediaCapabilities should be valid.');
346
353
 
347
354
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
348
355
  manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
349
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
+
350
373
  manifest.variants = manifest.variants.filter((variant) => {
351
374
  // See: https://github.com/shaka-project/shaka-player/issues/3860
352
375
  const video = variant.video;
376
+ const videoWidth = (video && video.width) || 0;
377
+ const videoHeight = (video && video.height) || 0;
353
378
 
354
- const ContentType = shaka.util.ManifestParserUtils.ContentType;
355
- const Capabilities = shaka.media.Capabilities;
356
- const ManifestParserUtils = shaka.util.ManifestParserUtils;
357
- const MimeUtils = shaka.util.MimeUtils;
358
- 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
+ }
359
389
 
360
390
  if (video) {
361
391
  let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
362
-
363
392
  // For multiplexed streams. Here we must check the audio of the
364
393
  // stream to see if it is compatible.
365
394
  if (video.codecs.includes(',')) {
@@ -368,7 +397,6 @@ shaka.util.StreamUtils = class {
368
397
  videoCodecs = ManifestParserUtils.guessCodecs(
369
398
  ContentType.VIDEO, allCodecs);
370
399
  videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
371
-
372
400
  let audioCodecs = ManifestParserUtils.guessCodecs(
373
401
  ContentType.AUDIO, allCodecs);
374
402
  audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
@@ -410,19 +438,19 @@ shaka.util.StreamUtils = class {
410
438
  audio.codecs = codecs;
411
439
  }
412
440
 
413
- // See: https://github.com/shaka-project/shaka-player/issues/3380
414
- if (shaka.util.Platform.isXboxOne() && video &&
415
- ((video.width && video.width > 1920) ||
416
- (video.height && video.height > 1080)) &&
417
- (video.codecs.includes('avc1.') ||
418
- video.codecs.includes('avc3.'))) {
419
- shaka.log.debug('Dropping variant - not compatible with platform',
420
- StreamUtils.getVariantSummaryString_(variant));
421
- return false;
422
- }
423
-
424
441
  const supported = variant.decodingInfos.some((decodingInfo) => {
425
- 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;
426
454
  });
427
455
  // Filter out all unsupported variants.
428
456
  if (!supported) {
@@ -930,7 +958,10 @@ shaka.util.StreamUtils = class {
930
958
  static async filterImageStreams_(manifest) {
931
959
  const imageStreams = [];
932
960
  for (const stream of manifest.imageStreams) {
933
- const mimeType = stream.mimeType;
961
+ let mimeType = stream.mimeType;
962
+ if (mimeType == 'application/mp4' && stream.codecs == 'mjpg') {
963
+ mimeType = 'image/jpg';
964
+ }
934
965
  if (!shaka.util.StreamUtils.supportedImageMimeTypes_.has(mimeType)) {
935
966
  const minImage = shaka.util.StreamUtils.minImage_.get(mimeType);
936
967
  if (minImage) {
@@ -1080,6 +1111,7 @@ shaka.util.StreamUtils = class {
1080
1111
  frameRate: null,
1081
1112
  pixelAspectRatio: null,
1082
1113
  hdr: null,
1114
+ videoLayout: null,
1083
1115
  mimeType: mimeType,
1084
1116
  audioMimeType: audioMimeType,
1085
1117
  videoMimeType: videoMimeType,
@@ -1115,6 +1147,7 @@ shaka.util.StreamUtils = class {
1115
1147
  track.pixelAspectRatio = video.pixelAspectRatio || null;
1116
1148
  track.videoBandwidth = video.bandwidth || null;
1117
1149
  track.hdr = video.hdr || null;
1150
+ track.videoLayout = video.videoLayout || null;
1118
1151
  }
1119
1152
 
1120
1153
  if (audio) {
@@ -1155,6 +1188,7 @@ shaka.util.StreamUtils = class {
1155
1188
  frameRate: null,
1156
1189
  pixelAspectRatio: null,
1157
1190
  hdr: null,
1191
+ videoLayout: null,
1158
1192
  mimeType: stream.mimeType,
1159
1193
  audioMimeType: null,
1160
1194
  videoMimeType: null,
@@ -1231,10 +1265,11 @@ shaka.util.StreamUtils = class {
1231
1265
  frameRate: null,
1232
1266
  pixelAspectRatio: null,
1233
1267
  hdr: null,
1268
+ videoLayout: null,
1234
1269
  mimeType: stream.mimeType,
1235
1270
  audioMimeType: null,
1236
1271
  videoMimeType: null,
1237
- codecs: null,
1272
+ codecs: stream.codecs || null,
1238
1273
  audioCodec: null,
1239
1274
  videoCodec: null,
1240
1275
  primary: false,
@@ -1358,6 +1393,7 @@ shaka.util.StreamUtils = class {
1358
1393
  frameRate: null,
1359
1394
  pixelAspectRatio: null,
1360
1395
  hdr: null,
1396
+ videoLayout: null,
1361
1397
  mimeType: null,
1362
1398
  audioMimeType: null,
1363
1399
  videoMimeType: null,
@@ -1412,52 +1448,6 @@ shaka.util.StreamUtils = class {
1412
1448
  }
1413
1449
 
1414
1450
 
1415
- /**
1416
- * Filters variants according to the given audio channel count config.
1417
- *
1418
- * @param {!Array.<shaka.extern.Variant>} variants
1419
- * @param {number} preferredAudioChannelCount
1420
- * @return {!Array.<!shaka.extern.Variant>}
1421
- */
1422
- static filterVariantsByAudioChannelCount(
1423
- variants, preferredAudioChannelCount) {
1424
- // Group variants by their audio channel counts.
1425
- const variantsWithChannelCounts =
1426
- variants.filter((v) => v.audio && v.audio.channelsCount);
1427
-
1428
- /** @type {!Map.<number, !Array.<shaka.extern.Variant>>} */
1429
- const variantsByChannelCount = new Map();
1430
- const capableVariants = [];
1431
- for (const variant of variantsWithChannelCounts) {
1432
- const count = variant.audio.channelsCount;
1433
- goog.asserts.assert(count != null, 'Must have count after filtering!');
1434
- if (!variantsByChannelCount.has(count)) {
1435
- variantsByChannelCount.set(count, []);
1436
- }
1437
- variantsByChannelCount.get(count).push(variant);
1438
- if (count <= preferredAudioChannelCount) {
1439
- capableVariants.push(variant);
1440
- }
1441
- }
1442
-
1443
- // return all capable channelcounts
1444
- if (capableVariants.length) {
1445
- return capableVariants;
1446
- }
1447
-
1448
- /** @type {!Array.<number>} */
1449
- const channelCounts = Array.from(variantsByChannelCount.keys());
1450
-
1451
- // If no variant has audio channel count info, return the original variants.
1452
- if (channelCounts.length == 0) {
1453
- return variants;
1454
- }
1455
-
1456
- // If all variants have more audio channels than the config, choose the
1457
- // variants with the fewest audio channels.
1458
- return variantsByChannelCount.get(Math.min(...channelCounts));
1459
- }
1460
-
1461
1451
  /**
1462
1452
  * Chooses streams according to the given config.
1463
1453
  * Works both for Stream and Track types due to their similarities.
@@ -1608,6 +1598,23 @@ shaka.util.StreamUtils = class {
1608
1598
  }
1609
1599
 
1610
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
+
1611
1618
  /**
1612
1619
  * Returns a string of a variant, with the attribute values of its audio
1613
1620
  * and/or video streams for log printing.
@@ -35,6 +35,9 @@ shaka.util.TsParser = class {
35
35
  /** @private {!Array.<Uint8Array>} */
36
36
  this.videoData_ = [];
37
37
 
38
+ /** @private {!Array.<shaka.extern.MPEG_PES>} */
39
+ this.videoPes_ = [];
40
+
38
41
  /** @private {?number} */
39
42
  this.audioPid_ = null;
40
43
 
@@ -44,6 +47,9 @@ shaka.util.TsParser = class {
44
47
  /** @private {!Array.<Uint8Array>} */
45
48
  this.audioData_ = [];
46
49
 
50
+ /** @private {!Array.<shaka.extern.MPEG_PES>} */
51
+ this.audioPes_ = [];
52
+
47
53
  /** @private {?number} */
48
54
  this.id3Pid_ = null;
49
55
 
@@ -58,7 +64,9 @@ shaka.util.TsParser = class {
58
64
  */
59
65
  clearData() {
60
66
  this.videoData_ = [];
67
+ this.videoPes_ = [];
61
68
  this.audioData_ = [];
69
+ this.audioPes_ = [];
62
70
  this.id3Data_ = [];
63
71
  }
64
72
 
@@ -360,6 +368,7 @@ shaka.util.TsParser = class {
360
368
  packetLength: ((data[4] << 8) | data[5]),
361
369
  pts: null,
362
370
  dts: null,
371
+ nalus: [],
363
372
  };
364
373
 
365
374
  // if PES parsed length is not zero and greater than total received length,
@@ -430,7 +439,7 @@ shaka.util.TsParser = class {
430
439
  shaka.Deprecate.deprecateFeature(5,
431
440
  'TsParser',
432
441
  'Please use parseNalus function instead.');
433
- return this.parseNalus(pes, nextPes);
442
+ return this.parseNalus(pes);
434
443
  }
435
444
 
436
445
  /**
@@ -440,12 +449,11 @@ shaka.util.TsParser = class {
440
449
  * Credit to https://github.com/video-dev/hls.js/blob/master/src/demux/tsdemuxer.ts
441
450
  *
442
451
  * @param {shaka.extern.MPEG_PES} pes
443
- * @param {?shaka.extern.MPEG_PES=} nextPes
444
452
  * @param {?shaka.extern.VideoNalu=} lastNalu
445
453
  * @return {!Array.<shaka.extern.VideoNalu>}
446
454
  * @export
447
455
  */
448
- parseNalus(pes, nextPes, lastNalu) {
456
+ parseNalus(pes, lastNalu) {
449
457
  const timescale = shaka.util.TsParser.Timescale;
450
458
  const time = pes.pts ? pes.pts / timescale : null;
451
459
  const data = pes.data;
@@ -479,21 +487,21 @@ shaka.util.TsParser = class {
479
487
  const value = data[i];
480
488
  if (!value) {
481
489
  numZeros++;
482
- } else if (numZeros >= 2 && value == 1 && lastNalu) {
483
- // If we are scanning the next PES, we need append the data to the
484
- // previous Nalu and don't scan for more nalus.
485
- const startCodeSize = numZeros > 3 ? 3 : numZeros;
486
- const lastByteToKeep = i - startCodeSize;
487
- // Optimization
488
- if (lastByteToKeep == 0) {
489
- return [];
490
- }
491
- lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
492
- lastNalu.data, data.subarray(0, lastByteToKeep));
493
- lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
494
- lastNalu.fullData, data.subarray(0, lastByteToKeep));
495
- return [];
496
490
  } else if (numZeros >= 2 && value == 1) {
491
+ if (lastNalu && !nalus.length && lastNaluStart == -1) {
492
+ // If we are scanning the next PES, we need append the data to the
493
+ // previous Nalu and don't scan for more nalus.
494
+ const startCodeSize = numZeros > 3 ? 3 : numZeros;
495
+ const lastByteToKeep = i - startCodeSize;
496
+ // Optimization
497
+ if (lastByteToKeep != 0) {
498
+ const prevData = data.subarray(0, lastByteToKeep);
499
+ lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
500
+ lastNalu.data, prevData);
501
+ lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
502
+ lastNalu.fullData, prevData);
503
+ }
504
+ }
497
505
  // We just read a start code. Consume the NALU we passed, if any.
498
506
  if (lastNaluStart >= 0) {
499
507
  // Because the start position includes the header size.
@@ -513,11 +521,14 @@ shaka.util.TsParser = class {
513
521
  time: time,
514
522
  };
515
523
  nalus.push(nalu);
516
- } else if (lastNalu) {
524
+ } else if (lastNalu && !nalus.length) {
517
525
  const overflow = i - numZeros;
518
526
  if (overflow > 0) {
527
+ const prevData = data.subarray(0, overflow);
519
528
  lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
520
- lastNalu.data, data.subarray(0, overflow));
529
+ lastNalu.data, prevData);
530
+ lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
531
+ lastNalu.fullData, prevData);
521
532
  }
522
533
  }
523
534
 
@@ -553,9 +564,6 @@ shaka.util.TsParser = class {
553
564
  type: lastNaluType,
554
565
  time: time,
555
566
  };
556
- if (nextPes) {
557
- this.parseNalus(nextPes, /* nextPes= */ null, infoOfLastNalu);
558
- }
559
567
  }
560
568
  }
561
569
 
@@ -602,73 +610,122 @@ shaka.util.TsParser = class {
602
610
  * @export
603
611
  */
604
612
  getAudioData() {
605
- const audio = [];
606
- for (const audioData of this.audioData_) {
607
- const pes = this.parsePES_(audioData);
608
- if (pes) {
609
- audio.push(pes);
613
+ if (this.audioData_.length && !this.audioPes_.length) {
614
+ let sort = false;
615
+ for (const audioData of this.audioData_) {
616
+ const pes = this.parsePES_(audioData);
617
+ if (pes && pes.pts != null && pes.dts != null) {
618
+ if (this.audioPes_.length &&
619
+ pes.dts < (this.audioPes_[this.audioPes_.length - 1].dts || 0)) {
620
+ sort = true;
621
+ }
622
+ this.audioPes_.push(pes);
623
+ } else if (this.audioPes_.length) {
624
+ const data = pes ? pes.data : audioData;
625
+ if (!data) {
626
+ continue;
627
+ }
628
+ const previousPes = this.audioPes_.pop();
629
+ previousPes.data =
630
+ shaka.util.Uint8ArrayUtils.concat(previousPes.data, data);
631
+ this.audioPes_.push(previousPes);
632
+ }
633
+ }
634
+ if (sort) {
635
+ this.audioPes_ = this.audioPes_.sort((a, b) => {
636
+ const deltadts = (a.dts || 0) - (b.dts || 0);
637
+ const deltapts = (a.pts || 0) - (b.pts || 0);
638
+ return deltadts || deltapts;
639
+ });
610
640
  }
611
641
  }
612
- return audio;
642
+ return this.audioPes_;
613
643
  }
614
644
 
615
645
  /**
616
646
  * Return the audio data
617
647
  *
648
+ * @param {boolean=} naluProcessing
618
649
  * @return {!Array.<shaka.extern.MPEG_PES>}
619
650
  * @export
620
651
  */
621
- getVideoData() {
622
- const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
623
- const video = [];
624
- for (const videoData of this.videoData_) {
625
- const pes = this.parsePES_(videoData);
626
- if (pes && pes.pts != null && pes.dts != null) {
627
- video.push(pes);
628
- } else if (video.length) {
629
- const data = pes ? pes.data : videoData;
630
- if (!data) {
631
- continue;
652
+ getVideoData(naluProcessing = true) {
653
+ if (this.videoData_.length && !this.videoPes_.length) {
654
+ let sort = false;
655
+ for (const videoData of this.videoData_) {
656
+ const pes = this.parsePES_(videoData);
657
+ if (pes && pes.pts != null && pes.dts != null) {
658
+ if (this.videoPes_.length &&
659
+ pes.dts < (this.videoPes_[this.videoPes_.length - 1].dts || 0)) {
660
+ sort = true;
661
+ }
662
+ this.videoPes_.push(pes);
663
+ } else if (this.videoPes_.length) {
664
+ const data = pes ? pes.data : videoData;
665
+ if (!data) {
666
+ continue;
667
+ }
668
+ const previousPes = this.videoPes_.pop();
669
+ previousPes.data =
670
+ shaka.util.Uint8ArrayUtils.concat(previousPes.data, data);
671
+ this.videoPes_.push(previousPes);
632
672
  }
633
- const previousPes = video.pop();
634
- previousPes.data =
635
- Uint8ArrayUtils.concat(previousPes.data, data);
636
- video.push(previousPes);
637
673
  }
674
+ if (naluProcessing) {
675
+ let lastNalu;
676
+ for (const pes of this.videoPes_) {
677
+ pes.nalus = this.parseNalus(pes, lastNalu);
678
+ if (pes.nalus.length) {
679
+ lastNalu = pes.nalus[pes.nalus.length - 1];
680
+ }
681
+ }
682
+ this.videoPes_ = this.videoPes_.filter((pes) => {
683
+ return pes.nalus.length;
684
+ });
685
+ }
686
+ if (sort) {
687
+ this.videoPes_ = this.videoPes_.sort((a, b) => {
688
+ const deltadts = (a.dts || 0) - (b.dts || 0);
689
+ const deltapts = (a.pts || 0) - (b.pts || 0);
690
+ return deltadts || deltapts;
691
+ });
692
+ }
693
+ }
694
+ if (!naluProcessing) {
695
+ const prevVideoPes = this.videoPes_;
696
+ this.videoPes_ = [];
697
+ return prevVideoPes;
638
698
  }
639
- return video;
699
+ return this.videoPes_;
640
700
  }
641
701
 
642
702
  /**
643
703
  * Return the start time for the audio and video
644
704
  *
645
- * @return {{audio: ?number, video: ?number}}
705
+ * @param {string} contentType
706
+ * @return {?number}
646
707
  * @export
647
708
  */
648
- getStartTime() {
709
+ getStartTime(contentType) {
649
710
  const timescale = shaka.util.TsParser.Timescale;
650
- let audioStartTime = null;
651
- for (const pes of this.getAudioData()) {
652
- if (pes && pes.pts != null) {
653
- const startTime = Math.min(pes.dts, pes.pts) / timescale;
654
- if (audioStartTime == null || audioStartTime > startTime) {
655
- audioStartTime = startTime;
656
- }
711
+ if (contentType == 'audio') {
712
+ let audioStartTime = null;
713
+ const audioData = this.getAudioData();
714
+ if (audioData.length) {
715
+ const pes = audioData[0];
716
+ audioStartTime = Math.min(pes.dts, pes.pts) / timescale;
657
717
  }
658
- }
659
- let videoStartTime = null;
660
- for (const pes of this.getVideoData()) {
661
- if (pes && pes.pts != null) {
662
- const startTime = Math.min(pes.dts, pes.pts) / timescale;
663
- if (videoStartTime == null || videoStartTime > startTime) {
664
- videoStartTime = startTime;
665
- }
718
+ return audioStartTime;
719
+ } else if (contentType == 'video') {
720
+ let videoStartTime = null;
721
+ const videoData = this.getVideoData(/* naluProcessing= */ false);
722
+ if (videoData.length) {
723
+ const pes = videoData[0];
724
+ videoStartTime = Math.min(pes.dts, pes.pts) / timescale;
666
725
  }
726
+ return videoStartTime;
667
727
  }
668
- return {
669
- audio: audioStartTime,
670
- video: videoStartTime,
671
- };
728
+ return null;
672
729
  }
673
730
 
674
731
  /**
@@ -692,14 +749,8 @@ shaka.util.TsParser = class {
692
749
  */
693
750
  getVideoNalus() {
694
751
  const nalus = [];
695
- const videoData = this.getVideoData();
696
- for (let i = 0; i < videoData.length; i++) {
697
- const pes = videoData[i];
698
- let nextPes;
699
- if (i + 1 < videoData.length) {
700
- nextPes = videoData[i + 1];
701
- }
702
- nalus.push(...this.parseNalus(pes, nextPes));
752
+ for (const pes of this.getVideoData()) {
753
+ nalus.push(...pes.nalus);
703
754
  }
704
755
  return nalus;
705
756
  }
@@ -115,7 +115,11 @@ shaka.util.Uint8ArrayUtils = class {
115
115
  const result = new Uint8Array(totalLength);
116
116
  let offset = 0;
117
117
  for (const arr of varArgs) {
118
- result.set(shaka.util.BufferUtils.toUint8(arr), offset);
118
+ if (arr instanceof Uint8Array) {
119
+ result.set(arr, offset);
120
+ } else {
121
+ result.set(shaka.util.BufferUtils.toUint8(arr), offset);
122
+ }
119
123
  offset += arr.byteLength;
120
124
  }
121
125
  return result;