shaka-player 3.2.0 → 3.2.4

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 (162) hide show
  1. package/.github/workflows/build_and_test.yaml +73 -0
  2. package/.github/workflows/release.yaml +53 -0
  3. package/AUTHORS +3 -0
  4. package/CHANGELOG.md +182 -1
  5. package/CONTRIBUTORS +3 -0
  6. package/README.md +4 -4
  7. package/build/checkversion.py +1 -1
  8. package/build/generateExterns.js +1 -1
  9. package/build/misspellings.txt +1 -0
  10. package/build/shakaBuildHelpers.py +6 -5
  11. package/build/subprocessWindowsPatch.py +103 -0
  12. package/build/test.py +6 -0
  13. package/demo/common/assets.js +1 -1
  14. package/demo/index.html +1 -1
  15. package/dist/controls.css +2 -2
  16. package/dist/controls.css.map +1 -1
  17. package/dist/demo.compiled.debug.js +1 -1
  18. package/dist/demo.compiled.js +1 -1
  19. package/dist/deps.js +8 -8
  20. package/dist/locales.js +37 -37
  21. package/dist/receiver.compiled.debug.js +1 -1
  22. package/dist/receiver.compiled.js +1 -1
  23. package/dist/shaka-player.compiled.d.ts +195 -5
  24. package/dist/shaka-player.compiled.debug.d.ts +195 -5
  25. package/dist/shaka-player.compiled.debug.externs.js +226 -14
  26. package/dist/shaka-player.compiled.debug.js +424 -399
  27. package/dist/shaka-player.compiled.debug.map +4 -4
  28. package/dist/shaka-player.compiled.externs.js +226 -14
  29. package/dist/shaka-player.compiled.js +826 -812
  30. package/dist/shaka-player.compiled.map +4 -4
  31. package/dist/shaka-player.ui.d.ts +197 -16
  32. package/dist/shaka-player.ui.debug.d.ts +197 -16
  33. package/dist/shaka-player.ui.debug.externs.js +230 -27
  34. package/dist/shaka-player.ui.debug.js +466 -442
  35. package/dist/shaka-player.ui.debug.map +4 -4
  36. package/dist/shaka-player.ui.externs.js +230 -27
  37. package/dist/shaka-player.ui.js +946 -932
  38. package/dist/shaka-player.ui.map +4 -4
  39. package/docs/tutorials/fairplay.md +1 -1
  40. package/docs/tutorials/faq.md +14 -0
  41. package/docs/tutorials/ui.md +5 -5
  42. package/externs/ima.js +4 -0
  43. package/externs/shaka/ads.js +25 -0
  44. package/externs/shaka/player.js +8 -0
  45. package/externs/shaka/text.js +10 -0
  46. package/karma.conf.js +20 -15
  47. package/lib/ads/ad_manager.js +46 -26
  48. package/lib/ads/client_side_ad_manager.js +82 -88
  49. package/lib/ads/server_side_ad_manager.js +45 -13
  50. package/lib/cast/cast_proxy.js +6 -2
  51. package/lib/cast/cast_receiver.js +5 -1
  52. package/lib/cast/cast_sender.js +2 -2
  53. package/lib/cea/sei_processor.js +24 -17
  54. package/lib/dash/dash_parser.js +131 -24
  55. package/lib/dash/segment_list.js +1 -1
  56. package/lib/dash/segment_template.js +1 -1
  57. package/lib/hls/hls_parser.js +74 -17
  58. package/lib/hls/manifest_text_parser.js +1 -0
  59. package/lib/media/closed_caption_parser.js +3 -1
  60. package/lib/media/content_workarounds.js +12 -0
  61. package/lib/media/drm_engine.js +20 -1
  62. package/lib/media/gap_jumping_controller.js +8 -4
  63. package/lib/media/media_source_engine.js +1 -2
  64. package/lib/media/playhead.js +1 -1
  65. package/lib/media/region_observer.js +39 -33
  66. package/lib/media/region_timeline.js +17 -19
  67. package/lib/media/segment_index.js +2 -1
  68. package/lib/media/segment_reference.js +19 -1
  69. package/lib/media/streaming_engine.js +8 -4
  70. package/lib/net/networking_engine.js +6 -1
  71. package/lib/offline/download_manager.js +1 -2
  72. package/lib/offline/storage.js +8 -9
  73. package/lib/player.js +129 -59
  74. package/lib/polyfill/aria.js +12 -2
  75. package/lib/polyfill/encryption_scheme.js +2 -0
  76. package/lib/polyfill/fullscreen.js +2 -0
  77. package/lib/polyfill/mathround.js +2 -0
  78. package/lib/polyfill/media_capabilities.js +73 -21
  79. package/lib/polyfill/mediasource.js +8 -0
  80. package/lib/polyfill/orientation.js +3 -1
  81. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  82. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  83. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  84. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  85. package/lib/polyfill/pip_webkit.js +2 -0
  86. package/lib/polyfill/storage_estimate.js +2 -0
  87. package/lib/polyfill/video_play_promise.js +2 -0
  88. package/lib/polyfill/videoplaybackquality.js +2 -0
  89. package/lib/polyfill/vttcue.js +2 -0
  90. package/lib/text/cue.js +6 -0
  91. package/lib/text/mp4_ttml_parser.js +3 -0
  92. package/lib/text/text_engine.js +14 -2
  93. package/lib/text/ttml_text_parser.js +135 -87
  94. package/lib/text/ui_text_displayer.js +213 -80
  95. package/lib/text/vtt_text_parser.js +4 -2
  96. package/lib/text/web_vtt_generator.js +21 -12
  97. package/lib/util/fake_event.js +53 -7
  98. package/lib/util/fake_event_target.js +21 -1
  99. package/lib/util/mp4_box_parsers.js +1 -1
  100. package/lib/util/periods.js +4 -1
  101. package/lib/util/platform.js +13 -1
  102. package/lib/util/stream_utils.js +105 -13
  103. package/lib/util/xml_utils.js +50 -12
  104. package/package.json +3 -6
  105. package/roadmap.md +24 -22
  106. package/test/cast/cast_proxy_unit.js +12 -6
  107. package/test/cast/cast_receiver_integration.js +1 -1
  108. package/test/cast/cast_utils_unit.js +1 -1
  109. package/test/cea/mp4_cea_parser_unit.js +26 -0
  110. package/test/dash/dash_parser_live_unit.js +43 -0
  111. package/test/dash/dash_parser_manifest_unit.js +222 -3
  112. package/test/hls/hls_parser_unit.js +79 -1
  113. package/test/media/closed_caption_parser_unit.js +20 -0
  114. package/test/media/drm_engine_integration.js +86 -2
  115. package/test/media/drm_engine_unit.js +57 -0
  116. package/test/media/playhead_unit.js +27 -1
  117. package/test/media/region_observer_unit.js +40 -5
  118. package/test/media/region_timeline_unit.js +40 -22
  119. package/test/media/streaming_engine_integration.js +2 -1
  120. package/test/media/streaming_engine_unit.js +145 -0
  121. package/test/player_integration.js +79 -8
  122. package/test/player_src_equals_integration.js +41 -8
  123. package/test/player_unit.js +14 -4
  124. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  125. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  126. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  127. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  128. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  129. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  130. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  131. package/test/test/assets/3675/dash_0.mpd +30 -0
  132. package/test/test/assets/3675/dash_1.mpd +30 -0
  133. package/test/test/assets/3675/dash_2.mpd +30 -0
  134. package/test/test/assets/3675/dash_3.mpd +30 -0
  135. package/test/test/assets/3675/dash_4.mpd +30 -0
  136. package/test/test/assets/3675/dash_5.mpd +26 -0
  137. package/test/test/assets/3675/init-stream0.m4s +0 -0
  138. package/test/test/assets/chapters.vtt +2 -2
  139. package/test/test/assets/chapters2.vtt +10 -0
  140. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  141. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  142. package/test/test/util/fake_ad_manager.js +11 -1
  143. package/test/test/util/ttml_utils.js +81 -0
  144. package/test/test/util/util.js +10 -7
  145. package/test/text/mp4_ttml_parser_unit.js +11 -18
  146. package/test/text/ttml_text_parser_unit.js +542 -220
  147. package/test/text/ui_text_displayer_unit.js +93 -20
  148. package/test/text/vtt_text_parser_unit.js +17 -1
  149. package/test/text/web_vtt_generator_unit.js +58 -3
  150. package/test/ui/localization_unit.js +15 -14
  151. package/test/ui/text_displayer_layout_unit.js +21 -8
  152. package/test/util/mp4_box_parsers_unit.js +28 -0
  153. package/test/util/stream_utils_unit.js +60 -3
  154. package/ui/cast_button.js +2 -3
  155. package/ui/controls.js +11 -6
  156. package/ui/less/buttons.less +0 -4
  157. package/ui/less/containers.less +6 -7
  158. package/ui/locales/ru.json +6 -6
  159. package/ui/localization.js +40 -87
  160. package/ui/pip_button.js +2 -3
  161. package/ui/seek_bar.js +1 -2
  162. package/ui/text_selection.js +1 -0
@@ -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
  *
@@ -423,9 +423,44 @@ shaka.util.StreamUtils = class {
423
423
  'MediaCapabilities should be valid.');
424
424
 
425
425
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
426
- manifest.variants, usePersistentLicenses);
426
+ manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
427
427
  manifest.variants = manifest.variants.filter((variant) => {
428
+ // See: https://github.com/google/shaka-player/issues/3860
428
429
  const video = variant.video;
430
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
431
+ if (video) {
432
+ let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
433
+ // For multiplexed streams. Here we must check the audio of the
434
+ // stream to see if it is compatible.
435
+ if (video.codecs.includes(',')) {
436
+ const allCodecs = video.codecs.split(',');
437
+ videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
438
+ ContentType.VIDEO, allCodecs);
439
+ const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
440
+ ContentType.AUDIO, allCodecs);
441
+ const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
442
+ video.mimeType, audioCodecs, ContentType.AUDIO);
443
+ if (!MediaSource.isTypeSupported(audioFullType)) {
444
+ return false;
445
+ }
446
+ }
447
+ const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
448
+ video.mimeType, videoCodecs, ContentType.VIDEO);
449
+ if (!MediaSource.isTypeSupported(fullType)) {
450
+ return false;
451
+ }
452
+ }
453
+ const audio = variant.audio;
454
+ if (audio) {
455
+ const codecs =
456
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
457
+ const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
458
+ audio.mimeType, codecs, ContentType.AUDIO);
459
+ if (!MediaSource.isTypeSupported(fullType)) {
460
+ return false;
461
+ }
462
+ }
463
+
429
464
  // See: https://github.com/google/shaka-player/issues/3380
430
465
  if (shaka.util.Platform.isXboxOne() && video &&
431
466
  ((video.width && video.width > 1920) ||
@@ -456,9 +491,11 @@ shaka.util.StreamUtils = class {
456
491
  *
457
492
  * @param {!Array.<shaka.extern.Variant>} variants
458
493
  * @param {boolean} usePersistentLicenses
494
+ * @param {boolean} srcEquals
459
495
  * @exportDoc
460
496
  */
461
- static async getDecodingInfosForVariants(variants, usePersistentLicenses) {
497
+ static async getDecodingInfosForVariants(variants, usePersistentLicenses,
498
+ srcEquals) {
462
499
  const gotDecodingInfo = variants.some((variant) =>
463
500
  variant.decodingInfos.length);
464
501
  if (gotDecodingInfo) {
@@ -482,7 +519,7 @@ shaka.util.StreamUtils = class {
482
519
  for (const variant of variants) {
483
520
  /** @type {!Array.<!MediaDecodingConfiguration>} */
484
521
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
485
- variant, usePersistentLicenses);
522
+ variant, usePersistentLicenses, srcEquals);
486
523
 
487
524
  for (const config of decodingConfigs) {
488
525
  operations.push(getVariantDecodingInfos(variant, config));
@@ -497,17 +534,18 @@ shaka.util.StreamUtils = class {
497
534
  * results for each variant.
498
535
  * @param {!shaka.extern.Variant} variant
499
536
  * @param {boolean} usePersistentLicenses
537
+ * @param {boolean} srcEquals
500
538
  * @return {!Array.<!MediaDecodingConfiguration>}
501
539
  * @private
502
540
  */
503
- static getDecodingConfigs_(variant, usePersistentLicenses) {
541
+ static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
504
542
  const audio = variant.audio;
505
543
  const video = variant.video;
506
544
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
507
545
 
508
546
  /** @type {!MediaDecodingConfiguration} */
509
547
  const mediaDecodingConfig = {
510
- type: 'media-source',
548
+ type: srcEquals ? 'file' : 'media-source',
511
549
  };
512
550
 
513
551
  if (video) {
@@ -544,14 +582,23 @@ shaka.util.StreamUtils = class {
544
582
  // framerate must be greater than 0, otherwise the config is invalid.
545
583
  framerate: video.frameRate || 1,
546
584
  };
585
+ if (video.hdr) {
586
+ switch (video.hdr) {
587
+ case 'SDR':
588
+ mediaDecodingConfig.video.transferFunction = 'srgb';
589
+ break;
590
+ case 'PQ':
591
+ mediaDecodingConfig.video.transferFunction = 'pq';
592
+ break;
593
+ case 'HLG':
594
+ mediaDecodingConfig.video.transferFunction = 'hlg';
595
+ break;
596
+ }
597
+ }
547
598
  }
548
599
  if (audio) {
549
- // Some Tizen devices seem to misreport AC-3 support, but correctly
550
- // report EC-3 support. So query EC-3 as a fallback for AC-3.
551
- // See https://github.com/google/shaka-player/issues/2989 for details.
552
600
  const codecs =
553
- (audio.codecs.toLowerCase() == 'ac-3' &&
554
- shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
601
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
555
602
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
556
603
  audio.mimeType, codecs, ContentType.AUDIO);
557
604
 
@@ -662,6 +709,26 @@ shaka.util.StreamUtils = class {
662
709
  return configs;
663
710
  }
664
711
 
712
+
713
+ /**
714
+ * Generates the correct audio codec for MediaDecodingConfiguration and
715
+ * for MediaSource.isTypeSupported.
716
+ * @param {string} codecs
717
+ * @return {string}
718
+ * @private
719
+ */
720
+ static getCorrectAudioCodecs_(codecs) {
721
+ // Some Tizen devices seem to misreport AC-3 support, but correctly
722
+ // report EC-3 support. So query EC-3 as a fallback for AC-3.
723
+ // See https://github.com/google/shaka-player/issues/2989 for details.
724
+ if (shaka.util.Platform.isTizen()) {
725
+ return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
726
+ } else {
727
+ return codecs;
728
+ }
729
+ }
730
+
731
+
665
732
  /**
666
733
  * MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
667
734
  * codec strings into 'vp09...', to allow such content to play with
@@ -973,6 +1040,31 @@ shaka.util.StreamUtils = class {
973
1040
  static imageStreamToTrack(stream) {
974
1041
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
975
1042
 
1043
+ let width = stream.width || null;
1044
+ let height = stream.height || null;
1045
+
1046
+ // The stream width and height represent the size of the entire thumbnail
1047
+ // sheet, so divide by the layout.
1048
+ let reference = null;
1049
+ // Note: segmentIndex is built by default for HLS, but not for DASH, but
1050
+ // in DASH this information comes at the stream level and not at the
1051
+ // segment level.
1052
+ if (stream.segmentIndex) {
1053
+ reference = stream.segmentIndex.get(0);
1054
+ }
1055
+ let layout = stream.tilesLayout;
1056
+ if (reference) {
1057
+ layout = reference.getTilesLayout() || layout;
1058
+ }
1059
+ if (layout && width != null) {
1060
+ width /= Number(layout.split('x')[0]);
1061
+ }
1062
+ if (layout && height != null) {
1063
+ height /= Number(layout.split('x')[1]);
1064
+ }
1065
+ // TODO: What happens if there are multiple grids, with different
1066
+ // layout sizes, inside this image stream?
1067
+
976
1068
  /** @type {shaka.extern.Track} */
977
1069
  const track = {
978
1070
  id: stream.id,
@@ -982,8 +1074,8 @@ shaka.util.StreamUtils = class {
982
1074
  language: '',
983
1075
  label: null,
984
1076
  kind: null,
985
- width: stream.width || null,
986
- height: stream.height || null,
1077
+ width,
1078
+ height,
987
1079
  frameRate: null,
988
1080
  pixelAspectRatio: null,
989
1081
  hdr: null,
@@ -1000,7 +1092,7 @@ shaka.util.StreamUtils = class {
1000
1092
  channelsCount: null,
1001
1093
  audioSamplingRate: null,
1002
1094
  spatialAudio: false,
1003
- tilesLayout: stream.tilesLayout || null,
1095
+ tilesLayout: layout || null,
1004
1096
  audioBandwidth: null,
1005
1097
  videoBandwidth: null,
1006
1098
  originalVideoId: null,
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.util.XmlUtils');
8
8
 
9
+ goog.require('goog.asserts');
9
10
  goog.require('shaka.log');
10
11
  goog.require('shaka.util.StringUtils');
11
12
 
@@ -102,6 +103,25 @@ shaka.util.XmlUtils = class {
102
103
  }
103
104
 
104
105
 
106
+ /**
107
+ * Gets a namespace-qualified attribute.
108
+ * @param {!Element} elem The element to get from.
109
+ * @param {!Array.<string>} nsList The lis of namespace URIs.
110
+ * @param {string} name The local name of the attribute.
111
+ * @return {?string} The attribute's value, or null if not present.
112
+ */
113
+ static getAttributeNSList(elem, nsList, name) {
114
+ // Some browsers return the empty string when the attribute is missing,
115
+ // so check if it exists first. See: https://mzl.la/2L7F0UK
116
+ for (const ns of nsList) {
117
+ if (elem.hasAttributeNS(ns, name)) {
118
+ return elem.getAttributeNS(ns, name);
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+
124
+
105
125
  /**
106
126
  * Gets the text contents of a node.
107
127
  * @param {!Node} elem The XML element.
@@ -312,29 +332,46 @@ shaka.util.XmlUtils = class {
312
332
 
313
333
 
314
334
  /**
315
- * Parse a string and return the resulting root element if
316
- * it was valid XML.
335
+ * Parse a string and return the resulting root element if it was valid XML.
336
+ *
317
337
  * @param {string} xmlString
318
338
  * @param {string} expectedRootElemName
319
339
  * @return {Element}
320
340
  */
321
341
  static parseXmlString(xmlString, expectedRootElemName) {
322
342
  const parser = new DOMParser();
323
- let rootElem = null;
324
343
  let xml = null;
325
344
  try {
326
345
  xml = parser.parseFromString(xmlString, 'text/xml');
327
- } catch (exception) {}
328
- if (xml) {
329
- // The top-level element in the loaded xml should have the
330
- // same type as the element linking.
331
- if (xml.documentElement.tagName == expectedRootElemName) {
332
- rootElem = xml.documentElement;
333
- }
346
+ } catch (exception) {
347
+ shaka.log.error('XML parsing exception:', exception);
348
+ return null;
334
349
  }
335
- if (rootElem && rootElem.getElementsByTagName('parsererror').length > 0) {
350
+
351
+ // According to MDN, parseFromString never returns null.
352
+ goog.asserts.assert(xml, 'Parsed XML document cannot be null!');
353
+
354
+ // Check for empty documents.
355
+ const rootElem = xml.documentElement;
356
+ if (!rootElem) {
357
+ shaka.log.error('XML document was empty!');
336
358
  return null;
337
- } // It had a parser error in it.
359
+ }
360
+
361
+ // Check for parser errors.
362
+ const parserErrorElements = rootElem.getElementsByTagName('parsererror');
363
+ if (parserErrorElements.length) {
364
+ shaka.log.error('XML parser error found:', parserErrorElements[0]);
365
+ return null;
366
+ }
367
+
368
+ // The top-level element in the loaded XML should have the name we expect.
369
+ if (xml.documentElement.tagName != expectedRootElemName) {
370
+ shaka.log.error(
371
+ `XML tag name does not match expected "${expectedRootElemName}":`,
372
+ xml.documentElement.tagName);
373
+ return null;
374
+ }
338
375
 
339
376
  return rootElem;
340
377
  }
@@ -352,6 +389,7 @@ shaka.util.XmlUtils = class {
352
389
  const string = shaka.util.StringUtils.fromUTF8(data);
353
390
  return shaka.util.XmlUtils.parseXmlString(string, expectedRootElemName);
354
391
  } catch (exception) {
392
+ shaka.log.error('parseXmlString threw!', exception);
355
393
  return null;
356
394
  }
357
395
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "3.2.0",
4
+ "version": "3.2.4",
5
5
  "homepage": "https://github.com/google/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -11,7 +11,6 @@
11
11
  }
12
12
  ],
13
13
  "devDependencies": {
14
- "@chiragrupani/karma-chromium-edge-launcher": "^2.1.0",
15
14
  "@teppeis/clutz": "^1.0.29-4c95e12.v20190929",
16
15
  "awesomplete": "^1.1.4",
17
16
  "babel-core": "^6.26.3",
@@ -36,15 +35,12 @@
36
35
  "jsdoc": "github:joeyparrish/jsdoc#45c271fa",
37
36
  "karma": "^6.2.0",
38
37
  "karma-babel-preprocessor": "^7.0.0",
39
- "karma-chrome-launcher": "^2.2.0",
40
38
  "karma-coverage": "^1.1.2",
41
- "karma-edge-launcher": "^0.4.2",
42
- "karma-firefox-launcher": "^1.1.0",
43
39
  "karma-ie-launcher": "^1.0.0",
44
40
  "karma-jasmine": "^3.3.1",
45
41
  "karma-jasmine-ajax": "^0.1.13",
42
+ "karma-local-wd-launcher": "^1.4.1",
46
43
  "karma-opera-launcher": "^1.0.0",
47
- "karma-safari-launcher": "^1.0.0",
48
44
  "karma-sourcemap-loader": "^0.3.7",
49
45
  "karma-spec-reporter": "0.0.32",
50
46
  "karma-webdriver-launcher": "^1.0.8",
@@ -55,6 +51,7 @@
55
51
  "pwacompat": "^2.0.10",
56
52
  "rimraf": "^2.6.3",
57
53
  "sprintf-js": "^1.1.2",
54
+ "ssim.js": "^3.5.0",
58
55
  "stylelint": "^13.8.0",
59
56
  "stylelint-config-standard": "^20.0.0",
60
57
  "tippy.js": "^4.3.1",
package/roadmap.md CHANGED
@@ -8,49 +8,51 @@ CHANGELOG.md.
8
8
  The goals of future milestones are fluid until we begin that development cycle,
9
9
  so the exact milestone for future features is not pre-determined.
10
10
 
11
- Candidate features for future release cycles
12
- - HLS performance improvements
13
- https://github.com/google/shaka-player/issues/3363
14
- - HLS interstitial support
15
- https://github.com/google/shaka-player/issues/3364
16
- - Integration with Google Video ML/Cloud AI
11
+ Priority features up next:
17
12
  - Support containerless formats
18
13
  https://github.com/google/shaka-player/issues/2337
14
+ - Preload API
15
+ https://github.com/google/shaka-player/issues/880
16
+ - Codec-switching and order preference
17
+ https://github.com/google/shaka-player/issues/1528
18
+ https://github.com/google/shaka-player/issues/2179
19
+
20
+ Candidate features for future release cycles:
21
+ - HLS interstitial support
22
+ https://github.com/google/shaka-player/issues/3364
19
23
  - Support ID3 tags in mp4
20
24
  https://github.com/google/shaka-player/issues/3351
21
25
  - MSE support for FairPlay
22
26
  https://github.com/google/shaka-player/issues/3346
23
- - Codec-switching and order preference
24
- https://github.com/google/shaka-player/issues/1528
25
- https://github.com/google/shaka-player/issues/2179
26
- - Configurable key-system priority
27
- https://github.com/google/shaka-player/issues/3002
28
- - Preload API
29
- https://github.com/google/shaka-player/issues/880
30
- - Thumbnail tracks
31
- DASH: https://github.com/google/shaka-player/issues/559
32
- HLS: https://github.com/google/shaka-player/issues/2429
33
27
  - Smaller, more modular binary
34
28
  - Official TypeScript defs, generated from source
35
29
  https://github.com/google/shaka-player/issues/1030
30
+ - Shaka Analytics Component and an integration with BigQuery Analytics
31
+ https://github.com/google/shaka-player/issues/3359
32
+ - Integration with Google Video ML/Cloud AI
36
33
 
37
- v3.3 - 2021 Q3
34
+ v3.3 - 2021 Q3 or Q4
38
35
  - Background fetch for offline storage
39
36
  https://github.com/google/shaka-player/issues/879
40
- - Shaka Analytics Component and an integration with BigQuery Analytics
41
- https://github.com/google/shaka-player/issues/3359
37
+ - HLS performance improvements
38
+ https://github.com/google/shaka-player/issues/3363
42
39
 
43
- v3.2 - 2021 Q2
40
+ =====
41
+
42
+ v3.2 - 2021 Q3
44
43
  - MediaCapabilities to make performance-based decisions
45
44
  https://github.com/google/shaka-player/issues/1391
45
+ - Configurable key-system priority
46
+ https://github.com/google/shaka-player/issues/3002
46
47
 
47
48
  v3.1 - 2021 Q2
48
49
  - Low-latency live (LL-HLS, LL-DASH)
49
50
  https://github.com/google/shaka-player/issues/1525
50
51
  - Own CEA parser, use mux.js only for TS
51
52
  https://github.com/google/shaka-player/issues/2648
52
-
53
- =====
53
+ - Thumbnail tracks
54
+ DASH: https://github.com/google/shaka-player/issues/559
55
+ HLS: https://github.com/google/shaka-player/issues/2429
54
56
 
55
57
  v3.0 - 2020 Q2
56
58
  - Code health improvements
@@ -297,7 +297,8 @@ describe('CastProxy', () => {
297
297
  'timeupdate', Util.spyFunc(proxyListener));
298
298
 
299
299
  expect(proxyListener).not.toHaveBeenCalled();
300
- const fakeEvent = new FakeEvent('timeupdate', {detail: 8675309});
300
+ const fakeEvent = new FakeEvent(
301
+ 'timeupdate', (new Map()).set('detail', 8675309));
301
302
  mockVideo.on['timeupdate'](fakeEvent);
302
303
  expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
303
304
  type: 'timeupdate',
@@ -315,7 +316,8 @@ describe('CastProxy', () => {
315
316
  mockSender.hasRemoteProperties.and.returnValue(true);
316
317
 
317
318
  expect(proxyListener).not.toHaveBeenCalled();
318
- const fakeEvent = new FakeEvent('timeupdate', {detail: 8675309});
319
+ const fakeEvent = new FakeEvent(
320
+ 'timeupdate', (new Map()).set('detail', 8675309));
319
321
  mockVideo.on['timeupdate'](fakeEvent);
320
322
  expect(proxyListener).not.toHaveBeenCalled();
321
323
  });
@@ -332,7 +334,8 @@ describe('CastProxy', () => {
332
334
  mockSender.hasRemoteProperties.and.returnValue(true);
333
335
 
334
336
  expect(proxyListener).not.toHaveBeenCalled();
335
- const fakeEvent = new FakeEvent('timeupdate', {detail: 8675309});
337
+ const fakeEvent = new FakeEvent(
338
+ 'timeupdate', (new Map()).set('detail', 8675309));
336
339
  mockSender.onRemoteEvent('video', fakeEvent);
337
340
  expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
338
341
  type: 'timeupdate',
@@ -446,7 +449,8 @@ describe('CastProxy', () => {
446
449
  'buffering', Util.spyFunc(proxyListener));
447
450
 
448
451
  expect(proxyListener).not.toHaveBeenCalled();
449
- const fakeEvent = new FakeEvent('buffering', {detail: 8675309});
452
+ const fakeEvent = new FakeEvent(
453
+ 'buffering', (new Map()).set('detail', 8675309));
450
454
  mockPlayer.listeners['buffering'](fakeEvent);
451
455
  expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
452
456
  type: 'buffering',
@@ -464,7 +468,8 @@ describe('CastProxy', () => {
464
468
  mockSender.hasRemoteProperties.and.returnValue(true);
465
469
 
466
470
  expect(proxyListener).not.toHaveBeenCalled();
467
- const fakeEvent = new FakeEvent('buffering', {detail: 8675309});
471
+ const fakeEvent = new FakeEvent(
472
+ 'buffering', (new Map()).set('detail', 8675309));
468
473
  mockPlayer.listeners['buffering'](fakeEvent);
469
474
  expect(proxyListener).not.toHaveBeenCalled();
470
475
  });
@@ -481,7 +486,8 @@ describe('CastProxy', () => {
481
486
  mockSender.hasRemoteProperties.and.returnValue(true);
482
487
 
483
488
  expect(proxyListener).not.toHaveBeenCalled();
484
- const fakeEvent = new FakeEvent('buffering', {detail: 8675309});
489
+ const fakeEvent = new FakeEvent(
490
+ 'buffering', (new Map()).set('detail', 8675309));
485
491
  mockSender.onRemoteEvent('player', fakeEvent);
486
492
  expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
487
493
  type: 'buffering',
@@ -170,7 +170,7 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
170
170
  // Check that the update message is of a reasonable size. From previous
171
171
  // testing we found that the socket would silently reject data that got
172
172
  // too big. 6KB is safely below the limit.
173
- expect(message.length).toBeLessThan(6 * 1024);
173
+ expect(message.length).toBeLessThan(7 * 1024);
174
174
  });
175
175
 
176
176
  drmIt('has reasonable average message size', async () => {
@@ -123,7 +123,7 @@ describe('CastUtils', () => {
123
123
  expect(typeof deserialized).toBe('object');
124
124
 
125
125
  // The object can be used to construct a FakeEvent.
126
- const fakeEvent = new FakeEvent(deserialized['type'], deserialized);
126
+ const fakeEvent = FakeEvent.fromRealEvent(deserialized);
127
127
 
128
128
  // The fake event has the same type and properties as the original.
129
129
  const asObj = /** @type {!Object} */ (fakeEvent);
@@ -27,6 +27,32 @@ describe('Mp4CeaParser', () => {
27
27
  ceaSegment = responses[1];
28
28
  });
29
29
 
30
+ /**
31
+ * Test only the functionality of removing EPB
32
+ * Expect removeEmu() to return the NALU with correct length
33
+ *
34
+ * Chromium VDA has a strict standard on NALU length
35
+ * It will complain about conformance if the array is malformed
36
+ *
37
+ * If EPB is removed by shifting bytes, it will return the original NALU
38
+ * length, which will fail this test
39
+ *
40
+ * Note that the CEA-608 packet in this test is incomplete
41
+ */
42
+ it('parses CEA-608 SEI data from MP4 H.264 stream', () => {
43
+ const seiProcessor = new shaka.cea.SeiProcessor();
44
+
45
+ const cea608Packet = new Uint8Array([
46
+ 0x00, 0x00, 0x03, // Emulation prevention byte
47
+ ]);
48
+
49
+ const naluData = seiProcessor.removeEmu(cea608Packet);
50
+ expect(naluData).toBeDefined();
51
+
52
+ // EPB should be removed by returning new array, not by shifting bytes
53
+ expect(naluData.length).toBe(2);
54
+ });
55
+
30
56
  it('parses cea data from mp4 stream', () => {
31
57
  const cea708Parser = new shaka.cea.Mp4CeaParser();
32
58
 
@@ -1289,6 +1289,49 @@ describe('DashParser Live', () => {
1289
1289
  .toHaveBeenCalledWith(
1290
1290
  jasmine.objectContaining({id: '2', startTime: 10, endTime: 25}));
1291
1291
  });
1292
+
1293
+ it('will not add timeline regions outside the DVR window', async () => {
1294
+ const manifest = [
1295
+ '<MPD type="dynamic" minimumUpdatePeriod="PT' + updateTime + 'S"',
1296
+ ' availabilityStartTime="1970-01-01T00:00:00Z"',
1297
+ ' suggestedPresentationDelay="PT0S"',
1298
+ ' timeShiftBufferDepth="PT30S">',
1299
+ ' <Period id="1">',
1300
+ ' <EventStream schemeIdUri="http://example.com" timescale="1">',
1301
+ ' <Event id="0" presentationTime="0" duration="10"/>',
1302
+ ' <Event id="1" presentationTime="10" duration="10"/>',
1303
+ ' <Event id="2" presentationTime="20" duration="10"/>',
1304
+ ' <Event id="3" presentationTime="30" duration="10"/>',
1305
+ ' <Event id="4" presentationTime="40" duration="10"/>',
1306
+ ' </EventStream>',
1307
+ ' <AdaptationSet mimeType="video/mp4">',
1308
+ ' <Representation bandwidth="1">',
1309
+ ' <SegmentTemplate startNumber="1" media="s$Number$.mp4"',
1310
+ ' duration="2" />',
1311
+ ' </Representation>',
1312
+ ' </AdaptationSet>',
1313
+ ' </Period>',
1314
+ '</MPD>',
1315
+ ].join('\n');
1316
+
1317
+ // 45 seconds after the epoch, which is also 45 seconds after the
1318
+ // presentation started. Only the last 30 seconds are available, from
1319
+ // time 15 to 45.
1320
+ Date.now = () => 45 * 1000;
1321
+
1322
+ fakeNetEngine.setResponseText('dummy://foo', manifest);
1323
+ await parser.start('dummy://foo', playerInterface);
1324
+
1325
+ expect(onTimelineRegionAddedSpy).toHaveBeenCalledTimes(4);
1326
+ expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
1327
+ jasmine.objectContaining({id: '1'}));
1328
+ expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
1329
+ jasmine.objectContaining({id: '2'}));
1330
+ expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
1331
+ jasmine.objectContaining({id: '3'}));
1332
+ expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
1333
+ jasmine.objectContaining({id: '4'}));
1334
+ });
1292
1335
  });
1293
1336
 
1294
1337
  it('honors clockSyncUri for in-progress recordings', async () => {