shaka-player 3.2.2 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/.github/workflows/build_and_test.yaml +73 -0
  5. package/.github/workflows/release.yaml +53 -0
  6. package/AUTHORS +6 -5
  7. package/CHANGELOG.md +314 -1
  8. package/CONTRIBUTORS +4 -2
  9. package/build/checkversion.py +1 -1
  10. package/build/conformance.textproto +183 -169
  11. package/build/test.py +6 -0
  12. package/build/types/core +3 -0
  13. package/build/types/offline +1 -0
  14. package/build/types/polyfill +1 -0
  15. package/build/types/ui +2 -0
  16. package/demo/common/assets.js +18 -0
  17. package/demo/common/message_ids.js +13 -1
  18. package/demo/config.js +31 -1
  19. package/demo/index.html +1 -1
  20. package/demo/locales/en.json +12 -0
  21. package/demo/locales/source.json +48 -0
  22. package/dist/controls.css +26 -2
  23. package/dist/controls.css.map +1 -1
  24. package/dist/demo.compiled.debug.js +19 -16
  25. package/dist/demo.compiled.debug.map +3 -3
  26. package/dist/demo.compiled.js +19 -16
  27. package/dist/demo.compiled.map +3 -3
  28. package/dist/deps.js +21 -14
  29. package/dist/locales.js +31 -29
  30. package/dist/receiver.compiled.debug.js +11 -9
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +11 -9
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +232 -18
  35. package/dist/shaka-player.compiled.debug.d.ts +232 -18
  36. package/dist/shaka-player.compiled.debug.externs.js +293 -17
  37. package/dist/shaka-player.compiled.debug.js +481 -422
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +293 -17
  40. package/dist/shaka-player.compiled.js +846 -817
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +250 -30
  43. package/dist/shaka-player.ui.debug.d.ts +250 -30
  44. package/dist/shaka-player.ui.debug.externs.js +322 -28
  45. package/dist/shaka-player.ui.debug.js +630 -553
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +322 -28
  48. package/dist/shaka-player.ui.js +1048 -1006
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/bg-fetch-after.gv +29 -0
  51. package/docs/design/bg-fetch-after.gv.png +0 -0
  52. package/docs/design/bg-fetch-before.gv +18 -0
  53. package/docs/design/bg-fetch-before.gv.png +0 -0
  54. package/docs/design/bg-fetch.md +134 -0
  55. package/docs/tutorials/blob-url.md +15 -0
  56. package/docs/tutorials/index.json +1 -0
  57. package/docs/tutorials/ui-customization.md +66 -0
  58. package/docs/tutorials/ui.md +5 -5
  59. package/externs/cmcd.js +186 -0
  60. package/externs/ima.js +3 -0
  61. package/externs/shaka/ads.js +5 -0
  62. package/externs/shaka/manifest.js +12 -1
  63. package/externs/shaka/manifest_parser.js +10 -1
  64. package/externs/shaka/net.js +20 -4
  65. package/externs/shaka/offline.js +31 -2
  66. package/externs/shaka/player.js +138 -5
  67. package/externs/shaka/text.js +24 -0
  68. package/externs/webcrypto.js +18 -0
  69. package/karma.conf.js +19 -15
  70. package/lib/abr/ewma.js +14 -0
  71. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  72. package/lib/abr/simple_abr_manager.js +6 -0
  73. package/lib/ads/ad_manager.js +1 -0
  74. package/lib/ads/client_side_ad.js +8 -0
  75. package/lib/ads/client_side_ad_manager.js +8 -4
  76. package/lib/ads/server_side_ad.js +7 -0
  77. package/lib/cast/cast_proxy.js +3 -0
  78. package/lib/cast/cast_receiver.js +3 -0
  79. package/lib/cast/cast_utils.js +1 -0
  80. package/lib/dash/dash_parser.js +79 -33
  81. package/lib/dash/segment_base.js +25 -1
  82. package/lib/dash/segment_list.js +18 -9
  83. package/lib/dash/segment_template.js +23 -10
  84. package/lib/hls/hls_parser.js +116 -23
  85. package/lib/hls/manifest_text_parser.js +1 -0
  86. package/lib/media/quality_observer.js +274 -0
  87. package/lib/media/region_observer.js +39 -33
  88. package/lib/media/region_timeline.js +17 -19
  89. package/lib/media/segment_index.js +8 -1
  90. package/lib/media/segment_reference.js +37 -4
  91. package/lib/media/streaming_engine.js +47 -6
  92. package/lib/net/http_fetch_plugin.js +31 -11
  93. package/lib/net/http_plugin_utils.js +1 -0
  94. package/lib/net/http_xhr_plugin.js +40 -14
  95. package/lib/net/networking_engine.js +85 -18
  96. package/lib/offline/download_info.js +68 -0
  97. package/lib/offline/download_manager.js +13 -5
  98. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  99. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  100. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  101. package/lib/offline/storage.js +401 -293
  102. package/lib/offline/stored_content_utils.js +2 -0
  103. package/lib/player.js +254 -27
  104. package/lib/polyfill/media_capabilities.js +30 -15
  105. package/lib/polyfill/random_uuid.js +52 -0
  106. package/lib/text/cue.js +18 -0
  107. package/lib/text/ttml_text_parser.js +109 -60
  108. package/lib/text/ui_text_displayer.js +19 -13
  109. package/lib/util/cmcd_manager.js +672 -0
  110. package/lib/util/error.js +6 -0
  111. package/lib/util/fake_event_target.js +21 -1
  112. package/lib/util/language_utils.js +12 -0
  113. package/lib/util/mutex.js +52 -0
  114. package/lib/util/periods.js +98 -64
  115. package/lib/util/player_configuration.js +18 -0
  116. package/lib/util/stream_utils.js +86 -10
  117. package/lib/util/xml_utils.js +19 -0
  118. package/package.json +3 -6
  119. package/shaka-player.uncompiled.js +3 -0
  120. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  121. package/test/dash/dash_parser_live_unit.js +144 -1
  122. package/test/dash/dash_parser_manifest_unit.js +37 -2
  123. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  124. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  125. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  126. package/test/hls/hls_live_unit.js +2 -0
  127. package/test/hls/hls_parser_unit.js +76 -0
  128. package/test/media/drm_engine_unit.js +3 -3
  129. package/test/media/quality_observer_unit.js +142 -0
  130. package/test/media/region_observer_unit.js +40 -5
  131. package/test/media/region_timeline_unit.js +40 -22
  132. package/test/media/segment_reference_unit.js +16 -1
  133. package/test/media/streaming_engine_integration.js +4 -2
  134. package/test/media/streaming_engine_unit.js +147 -0
  135. package/test/net/http_plugin_unit.js +20 -15
  136. package/test/offline/storage_integration.js +108 -4
  137. package/test/player_integration.js +140 -8
  138. package/test/player_src_equals_integration.js +41 -8
  139. package/test/player_unit.js +19 -9
  140. package/test/test/assets/chapters.vtt +2 -2
  141. package/test/test/assets/chapters2.vtt +10 -0
  142. package/test/test/util/dash_parser_util.js +4 -0
  143. package/test/test/util/fake_ad.js +7 -0
  144. package/test/test/util/fake_drm_engine.js +9 -2
  145. package/test/test/util/manifest_generator.js +3 -2
  146. package/test/test/util/test_scheme.js +14 -1
  147. package/test/test/util/ttml_utils.js +81 -0
  148. package/test/test/util/util.js +10 -7
  149. package/test/text/mp4_ttml_parser_unit.js +11 -18
  150. package/test/text/ttml_text_parser_unit.js +544 -228
  151. package/test/ui/text_displayer_layout_unit.js +8 -7
  152. package/test/ui/ui_unit.js +250 -0
  153. package/test/util/cmcd_manager_unit.js +291 -0
  154. package/test/util/stream_utils_unit.js +36 -30
  155. package/third_party/closure-uri/uri.js +21 -0
  156. package/ui/airplay_button.js +1 -0
  157. package/ui/audio_language_selection.js +7 -0
  158. package/ui/cast_button.js +1 -0
  159. package/ui/context_menu.js +109 -0
  160. package/ui/controls.js +44 -7
  161. package/ui/controls.less +1 -0
  162. package/ui/enums.js +4 -0
  163. package/ui/externs/ui.js +28 -1
  164. package/ui/fast_forward_button.js +14 -5
  165. package/ui/fullscreen_button.js +1 -0
  166. package/ui/less/buttons.less +0 -4
  167. package/ui/less/containers.less +74 -0
  168. package/ui/less/overflow_menu.less +0 -10
  169. package/ui/less/tooltip.less +97 -0
  170. package/ui/locales/en.json +1 -0
  171. package/ui/locales/source.json +4 -0
  172. package/ui/localization.js +12 -36
  173. package/ui/loop_button.js +5 -0
  174. package/ui/mute_button.js +2 -1
  175. package/ui/overflow_menu.js +5 -25
  176. package/ui/pip_button.js +5 -0
  177. package/ui/play_button.js +2 -2
  178. package/ui/playback_rate_selection.js +8 -9
  179. package/ui/resolution_selection.js +7 -0
  180. package/ui/rewind_button.js +15 -6
  181. package/ui/seek_bar.js +4 -2
  182. package/ui/settings_menu.js +36 -4
  183. package/ui/small_play_button.js +1 -0
  184. package/ui/statistics_button.js +253 -0
  185. package/ui/text_selection.js +12 -4
  186. package/ui/ui.js +30 -0
  187. package/ui/volume_bar.js +1 -1
  188. package/externs/media_capabilities.js +0 -16
@@ -87,6 +87,41 @@ describe('Player', () => {
87
87
  });
88
88
  }); // describe('attach')
89
89
 
90
+ describe('updateStartTime() in manifestparsed event handler', () => {
91
+ it('does not get segments prior to startTime', async () => {
92
+ player.addEventListener('manifestparsed', () => {
93
+ player.updateStartTime(24);
94
+ });
95
+ const results = {
96
+ requestedVideoSegment0: false,
97
+ requestedVideoSegment1: false,
98
+ requestedVideoSegment2: false,
99
+ };
100
+ const expected = {
101
+ requestedVideoSegment0: false,
102
+ requestedVideoSegment1: false,
103
+ requestedVideoSegment2: true,
104
+ };
105
+ player.getNetworkingEngine().registerRequestFilter(
106
+ (type, request) => {
107
+ if (request.uris[0] == 'test:sintel/video/0') {
108
+ results.requestedVideoSegment0 = true;
109
+ }
110
+ if (request.uris[0] == 'test:sintel/video/1') {
111
+ results.requestedVideoSegment1 = true;
112
+ }
113
+ if (request.uris[0] == 'test:sintel/video/2') {
114
+ results.requestedVideoSegment2 = true;
115
+ }
116
+ });
117
+ await player.load('test:sintel_compiled');
118
+ video.play();
119
+ await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 25, 30);
120
+ expect(results).toEqual(expected);
121
+ });
122
+ });
123
+
124
+
90
125
  describe('getStats', () => {
91
126
  it('gives stats about current stream', async () => {
92
127
  // This is tested more in player_unit.js. This is here to test the public
@@ -652,6 +687,70 @@ describe('Player', () => {
652
687
  });
653
688
  });
654
689
 
690
+ describe('mediaQualityChanges', () => {
691
+ /** @type {!jasmine.Spy} */
692
+ let onQualityChange;
693
+ /** @type {!shaka.test.Waiter} */
694
+ let waiter;
695
+
696
+ const qualityChange1 = jasmine.objectContaining({
697
+ mediaQuality: jasmine.objectContaining({
698
+ bandwidth: 1,
699
+ codecs: 'mp4a.40.2',
700
+ contentType: 'audio',
701
+ mimeType: 'audio/mp4',
702
+ }),
703
+ type: 'mediaqualitychanged',
704
+ position: jasmine.any(Number),
705
+ });
706
+ const qualityChange2 = jasmine.objectContaining({
707
+ mediaQuality: jasmine.objectContaining({
708
+ bandwidth: 1,
709
+ codecs: 'avc1.42c01e',
710
+ contentType: 'video',
711
+ mimeType: 'video/mp4',
712
+ }),
713
+ type: 'mediaqualitychanged',
714
+ position: jasmine.any(Number),
715
+ });
716
+
717
+ beforeEach(() => {
718
+ onQualityChange = jasmine.createSpy('onQualityChange');
719
+ // const spyFunc = Util.spyFunc(onQualityChange);
720
+ player.addEventListener('mediaqualitychanged',
721
+ Util.spyFunc(onQualityChange));
722
+ waiter = new shaka.test.Waiter(eventManager)
723
+ .timeoutAfter(10)
724
+ .failOnTimeout(true);
725
+ });
726
+
727
+ it('emits audio/video quality changes at start when enabled', async () => {
728
+ player.configure('streaming.observeQualityChanges', true);
729
+
730
+ await player.load('test:sintel_compiled');
731
+ video.play();
732
+
733
+ // Wait for the video to start playback. If it takes longer than 10
734
+ // seconds, fail the test.
735
+ await waiter.waitForMovementOrFailOnTimeout(video, 10);
736
+ expect(onQualityChange).toHaveBeenCalledTimes(2);
737
+ expect(onQualityChange).toHaveBeenCalledWith(qualityChange1);
738
+ expect(onQualityChange).toHaveBeenCalledWith(qualityChange2);
739
+ });
740
+
741
+ it('does not emit quality changes at start when disabled', async () => {
742
+ player.configure('streaming.observeQualityChanges', false);
743
+
744
+ await player.load('test:sintel_compiled');
745
+ video.play();
746
+
747
+ // Wait for the video to start playback. If it takes longer than 10
748
+ // seconds, fail the test.
749
+ await waiter.waitForMovementOrFailOnTimeout(video, 10);
750
+ expect(onQualityChange).not.toHaveBeenCalled();
751
+ });
752
+ });
753
+
655
754
  describe('seek range', () => {
656
755
  // Regression test for Issue #3675.
657
756
  it('has a reasonable seek range after going from live to VOD', async () => {
@@ -964,9 +1063,9 @@ describe('Player', () => {
964
1063
  it('add external chapters in vtt format', async () => {
965
1064
  await player.load('test:sintel_no_text_compiled');
966
1065
  const locationUri = new goog.Uri(location.href);
967
- const partialUri = new goog.Uri('/base/test/test/assets/chapters.vtt');
968
- const absoluteUri = locationUri.resolve(partialUri);
969
- await player.addChaptersTrack(absoluteUri.toString(), 'en');
1066
+ const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
1067
+ const absoluteUri1 = locationUri.resolve(partialUri1);
1068
+ await player.addChaptersTrack(absoluteUri1.toString(), 'en');
970
1069
 
971
1070
  await shaka.test.Util.delay(1.5);
972
1071
 
@@ -979,11 +1078,44 @@ describe('Player', () => {
979
1078
  const chapter2 = chapters[1];
980
1079
  expect(chapter2.title).toBe('Chapter 2');
981
1080
  expect(chapter2.startTime).toBe(5);
982
- expect(chapter2.endTime).toBe(30);
1081
+ expect(chapter2.endTime).toBe(10);
983
1082
  const chapter3 = chapters[2];
984
1083
  expect(chapter3.title).toBe('Chapter 3');
985
- expect(chapter3.startTime).toBe(30);
986
- expect(chapter3.endTime).toBe(61.349);
1084
+ expect(chapter3.startTime).toBe(10);
1085
+ expect(chapter3.endTime).toBe(20);
1086
+
1087
+ const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
1088
+ const absoluteUri2 = locationUri.resolve(partialUri2);
1089
+ await player.addChaptersTrack(absoluteUri2.toString(), 'en');
1090
+
1091
+ await shaka.test.Util.delay(1.5);
1092
+
1093
+ const chaptersUpdated = player.getChapters('en');
1094
+ expect(chaptersUpdated.length).toBe(6);
1095
+ const chapterUpdated1 = chaptersUpdated[0];
1096
+ expect(chapterUpdated1.title).toBe('Chapter 1');
1097
+ expect(chapterUpdated1.startTime).toBe(0);
1098
+ expect(chapterUpdated1.endTime).toBe(5);
1099
+ const chapterUpdated2 = chaptersUpdated[1];
1100
+ expect(chapterUpdated2.title).toBe('Chapter 2');
1101
+ expect(chapterUpdated2.startTime).toBe(5);
1102
+ expect(chapterUpdated2.endTime).toBe(10);
1103
+ const chapterUpdated3 = chaptersUpdated[2];
1104
+ expect(chapterUpdated3.title).toBe('Chapter 3');
1105
+ expect(chapterUpdated3.startTime).toBe(10);
1106
+ expect(chapterUpdated3.endTime).toBe(20);
1107
+ const chapterUpdated4 = chaptersUpdated[3];
1108
+ expect(chapterUpdated4.title).toBe('Chapter 4');
1109
+ expect(chapterUpdated4.startTime).toBe(20);
1110
+ expect(chapterUpdated4.endTime).toBe(30);
1111
+ const chapterUpdated5 = chaptersUpdated[4];
1112
+ expect(chapterUpdated5.title).toBe('Chapter 5');
1113
+ expect(chapterUpdated5.startTime).toBe(30);
1114
+ expect(chapterUpdated5.endTime).toBe(40);
1115
+ const chapterUpdated6 = chaptersUpdated[5];
1116
+ expect(chapterUpdated6.title).toBe('Chapter 6');
1117
+ expect(chapterUpdated6.startTime).toBe(40);
1118
+ expect(chapterUpdated6.endTime).toBe(61.349);
987
1119
  });
988
1120
 
989
1121
  it('add external chapters in srt format', async () => {
@@ -991,11 +1123,11 @@ describe('Player', () => {
991
1123
  const locationUri = new goog.Uri(location.href);
992
1124
  const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
993
1125
  const absoluteUri = locationUri.resolve(partialUri);
994
- await player.addChaptersTrack(absoluteUri.toString(), 'en');
1126
+ await player.addChaptersTrack(absoluteUri.toString(), 'es');
995
1127
 
996
1128
  await shaka.test.Util.delay(1.5);
997
1129
 
998
- const chapters = player.getChapters('en');
1130
+ const chapters = player.getChapters('es');
999
1131
  expect(chapters.length).toBe(3);
1000
1132
  const chapter1 = chapters[0];
1001
1133
  expect(chapter1.title).toBe('Chapter 1');
@@ -330,9 +330,9 @@ describe('Player Src Equals', () => {
330
330
  await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
331
331
 
332
332
  const locationUri = new goog.Uri(location.href);
333
- const partialUri = new goog.Uri('/base/test/test/assets/chapters.vtt');
334
- const absoluteUri = locationUri.resolve(partialUri);
335
- await player.addChaptersTrack(absoluteUri.toString(), 'en');
333
+ const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
334
+ const absoluteUri1 = locationUri.resolve(partialUri1);
335
+ await player.addChaptersTrack(absoluteUri1.toString(), 'en');
336
336
 
337
337
  await shaka.test.Util.delay(1.5);
338
338
 
@@ -345,11 +345,44 @@ describe('Player Src Equals', () => {
345
345
  const chapter2 = chapters[1];
346
346
  expect(chapter2.title).toBe('Chapter 2');
347
347
  expect(chapter2.startTime).toBe(5);
348
- expect(chapter2.endTime).toBe(30);
348
+ expect(chapter2.endTime).toBe(10);
349
349
  const chapter3 = chapters[2];
350
350
  expect(chapter3.title).toBe('Chapter 3');
351
- expect(chapter3.startTime).toBe(30);
352
- expect(chapter3.endTime).toBe(61.349);
351
+ expect(chapter3.startTime).toBe(10);
352
+ expect(chapter3.endTime).toBe(20);
353
+
354
+ const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
355
+ const absoluteUri2 = locationUri.resolve(partialUri2);
356
+ await player.addChaptersTrack(absoluteUri2.toString(), 'en');
357
+
358
+ await shaka.test.Util.delay(1.5);
359
+
360
+ const chaptersUpdated = player.getChapters('en');
361
+ expect(chaptersUpdated.length).toBe(6);
362
+ const chapterUpdated1 = chaptersUpdated[0];
363
+ expect(chapterUpdated1.title).toBe('Chapter 1');
364
+ expect(chapterUpdated1.startTime).toBe(0);
365
+ expect(chapterUpdated1.endTime).toBe(5);
366
+ const chapterUpdated2 = chaptersUpdated[1];
367
+ expect(chapterUpdated2.title).toBe('Chapter 2');
368
+ expect(chapterUpdated2.startTime).toBe(5);
369
+ expect(chapterUpdated2.endTime).toBe(10);
370
+ const chapterUpdated3 = chaptersUpdated[2];
371
+ expect(chapterUpdated3.title).toBe('Chapter 3');
372
+ expect(chapterUpdated3.startTime).toBe(10);
373
+ expect(chapterUpdated3.endTime).toBe(20);
374
+ const chapterUpdated4 = chaptersUpdated[3];
375
+ expect(chapterUpdated4.title).toBe('Chapter 4');
376
+ expect(chapterUpdated4.startTime).toBe(20);
377
+ expect(chapterUpdated4.endTime).toBe(30);
378
+ const chapterUpdated5 = chaptersUpdated[4];
379
+ expect(chapterUpdated5.title).toBe('Chapter 5');
380
+ expect(chapterUpdated5.startTime).toBe(30);
381
+ expect(chapterUpdated5.endTime).toBe(40);
382
+ const chapterUpdated6 = chaptersUpdated[5];
383
+ expect(chapterUpdated6.title).toBe('Chapter 6');
384
+ expect(chapterUpdated6.startTime).toBe(40);
385
+ expect(chapterUpdated6.endTime).toBe(61.349);
353
386
  });
354
387
 
355
388
  it('add external chapters in srt format', async () => {
@@ -358,11 +391,11 @@ describe('Player Src Equals', () => {
358
391
  const locationUri = new goog.Uri(location.href);
359
392
  const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
360
393
  const absoluteUri = locationUri.resolve(partialUri);
361
- await player.addChaptersTrack(absoluteUri.toString(), 'en');
394
+ await player.addChaptersTrack(absoluteUri.toString(), 'es');
362
395
 
363
396
  await shaka.test.Util.delay(1.5);
364
397
 
365
- const chapters = player.getChapters('en');
398
+ const chapters = player.getChapters('es');
366
399
  expect(chapters.length).toBe(3);
367
400
  const chapter1 = chapters[0];
368
401
  expect(chapter1.title).toBe('Chapter 1');
@@ -36,7 +36,7 @@ describe('Player', () => {
36
36
  const originalLogWarn = shaka.log.warning;
37
37
  const originalLogAlwaysWarn = shaka.log.alwaysWarn;
38
38
  const originalIsTypeSupported = window.MediaSource.isTypeSupported;
39
- const originalDecodingInfo = window.shakaMediaCapabilities.decodingInfo;
39
+ const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
40
40
 
41
41
  const fakeManifestUri = 'fake-manifest-uri';
42
42
  const fakeMimeType = 'application/test';
@@ -93,7 +93,7 @@ describe('Player', () => {
93
93
  // Since this is not an integration test, we don't want MediaCapabilities to
94
94
  // fail assertions based on browser support for types. Pretend that all
95
95
  // video and audio types are supported.
96
- window.shakaMediaCapabilities.decodingInfo = async (config) => {
96
+ navigator.mediaCapabilities.decodingInfo = async (config) => {
97
97
  await Promise.resolve();
98
98
  const videoType = config['video'] ?
99
99
  config['video'].contentType.split('/')[0] : null;
@@ -177,7 +177,7 @@ describe('Player', () => {
177
177
  shaka.log.alwaysWarn = originalLogAlwaysWarn;
178
178
  window.MediaSource.isTypeSupported = originalIsTypeSupported;
179
179
  shaka.media.ManifestParser.unregisterParserByMime(fakeMimeType);
180
- window.shakaMediaCapabilities.decodingInfo = originalDecodingInfo;
180
+ navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
181
181
  }
182
182
  });
183
183
 
@@ -1092,8 +1092,8 @@ describe('Player', () => {
1092
1092
  // Image tracks
1093
1093
  manifest.addImageStream(53, (stream) => {
1094
1094
  stream.originalId = 'thumbnail';
1095
- stream.width = 100;
1096
- stream.height = 200;
1095
+ stream.width = 200;
1096
+ stream.height = 400;
1097
1097
  stream.bandwidth = 10;
1098
1098
  stream.mimeType = 'image/jpeg';
1099
1099
  stream.tilesLayout = '1x1';
@@ -1503,8 +1503,8 @@ describe('Player', () => {
1503
1503
  audioBandwidth: null,
1504
1504
  videoBandwidth: null,
1505
1505
  bandwidth: 10,
1506
- width: 100,
1507
- height: 200,
1506
+ width: 200,
1507
+ height: 400,
1508
1508
  frameRate: null,
1509
1509
  pixelAspectRatio: null,
1510
1510
  hdr: null,
@@ -2463,7 +2463,7 @@ describe('Player', () => {
2463
2463
  it('throws CONTENT_UNSUPPORTED_BY_BROWSER', async () => {
2464
2464
  window.MediaSource.isTypeSupported = (mimeType) => false;
2465
2465
 
2466
- window.shakaMediaCapabilities.decodingInfo = async (config) => {
2466
+ navigator.mediaCapabilities.decodingInfo = async (config) => {
2467
2467
  await Promise.resolve();
2468
2468
  return {supported: false};
2469
2469
  };
@@ -2827,7 +2827,7 @@ describe('Player', () => {
2827
2827
  });
2828
2828
  });
2829
2829
 
2830
- window.shakaMediaCapabilities.decodingInfo = async (config) => {
2830
+ navigator.mediaCapabilities.decodingInfo = async (config) => {
2831
2831
  await Promise.resolve();
2832
2832
  const videoType = config['video'] ? config['video'].contentType : '';
2833
2833
  if (videoType.includes('video') &&
@@ -3537,29 +3537,39 @@ describe('Player', () => {
3537
3537
 
3538
3538
  await player.load(fakeManifestUri, 0, fakeMimeType);
3539
3539
 
3540
+ expect(player.getImageTracks()[0].width).toBe(100);
3541
+ expect(player.getImageTracks()[0].height).toBe(50);
3540
3542
  const thumbnail0 = await player.getThumbnails(5, 0);
3541
3543
  const thumbnail1 = await player.getThumbnails(5, 11);
3542
3544
  const thumbnail2 = await player.getThumbnails(5, 21);
3543
3545
  const thumbnail5 = await player.getThumbnails(5, 51);
3544
3546
  expect(thumbnail0).toEqual(jasmine.objectContaining({
3547
+ imageHeight: 150,
3548
+ imageWidth: 200,
3545
3549
  positionX: 0,
3546
3550
  positionY: 0,
3547
3551
  width: 100,
3548
3552
  height: 50,
3549
3553
  }));
3550
3554
  expect(thumbnail1).toEqual(jasmine.objectContaining({
3555
+ imageHeight: 150,
3556
+ imageWidth: 200,
3551
3557
  positionX: 100,
3552
3558
  positionY: 0,
3553
3559
  width: 100,
3554
3560
  height: 50,
3555
3561
  }));
3556
3562
  expect(thumbnail2).toEqual(jasmine.objectContaining({
3563
+ imageHeight: 150,
3564
+ imageWidth: 200,
3557
3565
  positionX: 0,
3558
3566
  positionY: 50,
3559
3567
  width: 100,
3560
3568
  height: 50,
3561
3569
  }));
3562
3570
  expect(thumbnail5).toEqual(jasmine.objectContaining({
3571
+ imageHeight: 150,
3572
+ imageWidth: 200,
3563
3573
  positionX: 100,
3564
3574
  positionY: 100,
3565
3575
  width: 100,
@@ -3,8 +3,8 @@ WEBVTT
3
3
  00:00.000 --> 00:05.000
4
4
  Chapter 1
5
5
 
6
- 00:05.000 --> 00:30.000
6
+ 00:05.000 --> 00:10.000
7
7
  Chapter 2
8
8
 
9
- 00:30.000 --> 01:01.349
9
+ 00:10.000 --> 00:20.000
10
10
  Chapter 3
@@ -0,0 +1,10 @@
1
+ WEBVTT
2
+
3
+ 00:20.000 --> 00:30.000
4
+ Chapter 4
5
+
6
+ 00:30.000 --> 00:40.000
7
+ Chapter 5
8
+
9
+ 00:40.000 --> 01:01.349
10
+ Chapter 6
@@ -46,6 +46,8 @@ shaka.test.Dash = class {
46
46
 
47
47
  const playerInterface = {
48
48
  networkingEngine: networkingEngine,
49
+ modifyManifestRequest: (request, manifestInfo) => {},
50
+ modifySegmentRequest: (request, segmentInfo) => {},
49
51
  filter: () => {},
50
52
  makeTextStreamsForClosedCaptions: (manifest) => {},
51
53
  onTimelineRegionAdded: fail, // Should not have any EventStream elements.
@@ -78,6 +80,8 @@ shaka.test.Dash = class {
78
80
 
79
81
  const playerInterface = {
80
82
  networkingEngine: networkingEngine,
83
+ modifyManifestRequest: (request, manifestInfo) => {},
84
+ modifySegmentRequest: (request, segmentInfo) => {},
81
85
  filter: () => {},
82
86
  makeTextStreamsForClosedCaptions: (manifest) => {},
83
87
  onTimelineRegionAdded: fail, // Should not have any EventStream elements.
@@ -154,6 +154,13 @@ shaka.test.FakeAd = class {
154
154
  }
155
155
 
156
156
 
157
+ /**
158
+ * @override
159
+ */
160
+ isLinear() {
161
+ return true;
162
+ }
163
+
157
164
  /**
158
165
  * @override
159
166
  */
@@ -17,7 +17,7 @@ goog.require('shaka.test.Util');
17
17
  */
18
18
  shaka.test.FakeDrmEngine = class {
19
19
  constructor() {
20
- /** @private {!Array.<number>} */
20
+ /** @private {!Array.<string>} */
21
21
  this.offlineSessions_ = [];
22
22
  /** @private {?shaka.extern.DrmInfo} */
23
23
  this.drmInfo_ = null;
@@ -45,6 +45,13 @@ shaka.test.FakeDrmEngine = class {
45
45
  // be returned.
46
46
  this.getDrmInfo.and.callFake(() => this.drmInfo_);
47
47
 
48
+ /** @type {!jasmine.Spy} */
49
+ this.newInitData = jasmine.createSpy('newInitData');
50
+ this.newInitData.and.callFake((initDataType, initData) => {
51
+ const num = 1 + this.offlineSessions_.length;
52
+ this.offlineSessions_.push('session-' + num);
53
+ });
54
+
48
55
  /** @type {!jasmine.Spy} */
49
56
  this.getExpiration = jasmine.createSpy('getExpiration');
50
57
  this.getExpiration.and.returnValue(Infinity);
@@ -90,7 +97,7 @@ shaka.test.FakeDrmEngine = class {
90
97
  }
91
98
 
92
99
  /**
93
- * @param {!Array.<number>} sessions
100
+ * @param {!Array.<string>} sessions
94
101
  */
95
102
  setSessionIds(sessions) {
96
103
  // Copy the values to break the reference to the input value.
@@ -653,15 +653,16 @@ shaka.test.ManifestGenerator.Stream = class {
653
653
  * @param {!Array.<string>} uris
654
654
  * @param {number} startByte
655
655
  * @param {?number} endByte
656
+ * @param {null|shaka.extern.MediaQualityInfo=} mediaQuality
656
657
  */
657
- setInitSegmentReference(uris, startByte, endByte) {
658
+ setInitSegmentReference(uris, startByte, endByte, mediaQuality) {
658
659
  goog.asserts.assert(this.manifest_,
659
660
  'A top-level generated Manifest is required to use this method!');
660
661
 
661
662
  const getUris = () => uris;
662
663
  this.initSegmentReference_ =
663
664
  new this.manifest_.shaka_.media.InitSegmentReference(
664
- getUris, startByte, endByte);
665
+ getUris, startByte, endByte, mediaQuality);
665
666
  }
666
667
 
667
668
  /**
@@ -206,10 +206,23 @@ shaka.test.TestScheme = class {
206
206
  * @param {string} name
207
207
  */
208
208
  function addStreamInfo(stream, variant, data, contentType, name) {
209
+ const mediaQualityInfo = {
210
+ bandwidth: 1,
211
+ codecs: data[contentType].codecs || 'unknown',
212
+ contentType: contentType,
213
+ mimeType: data[contentType].mimeType,
214
+ audioSamplingRate: null,
215
+ frameRate: null,
216
+ height: null,
217
+ channelsCount: null,
218
+ pixelAspectRatio: null,
219
+ width: null,
220
+ };
209
221
  stream.mimeType = data[contentType].mimeType;
210
222
  stream.codecs = data[contentType].codecs;
211
223
  stream.setInitSegmentReference(
212
- ['test:' + name + '/' + contentType + '/init'], 0, null);
224
+ ['test:' + name + '/' + contentType + '/init'], 0, null,
225
+ mediaQualityInfo);
213
226
  stream.useSegmentTemplate(
214
227
  'test:' + name + '/' + contentType + '/%d',
215
228
  data[contentType].segmentDuration);
@@ -0,0 +1,81 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.test.TtmlUtils');
8
+
9
+ shaka.test.TtmlUtils = class {
10
+ /**
11
+ * @param {!Array} expectedCues
12
+ * @param {!Array} actualCues
13
+ * @param {!Object} bodyProperties
14
+ * @param {Object=} divProperties
15
+ */
16
+ static verifyHelper(expectedCues, actualCues, bodyProperties, divProperties) {
17
+ const mapExpected = (cue) => {
18
+ if (cue.region) {
19
+ cue.region = jasmine.objectContaining(cue.region);
20
+ }
21
+
22
+ if (cue.nestedCues && (cue.nestedCues instanceof Array)) {
23
+ cue.nestedCues = cue.nestedCues.map(mapExpected);
24
+ }
25
+
26
+ if (cue.isContainer == undefined) {
27
+ // If not specified to be true, check for isContainer to be false.
28
+ cue.isContainer = false;
29
+ }
30
+
31
+ return jasmine.objectContaining(cue);
32
+ };
33
+
34
+ /**
35
+ * @param {!Object} properties
36
+ * @return {!shaka.extern.Cue}
37
+ */
38
+ const makeContainer = (properties) => {
39
+ const region = {
40
+ id: '',
41
+ viewportAnchorX: 0,
42
+ viewportAnchorY: 0,
43
+ regionAnchorX: 0,
44
+ regionAnchorY: 0,
45
+ width: 100,
46
+ height: 100,
47
+ widthUnits: shaka.text.CueRegion.units.PERCENTAGE,
48
+ heightUnits: shaka.text.CueRegion.units.PERCENTAGE,
49
+ viewportAnchorUnits: shaka.text.CueRegion.units.PERCENTAGE,
50
+ scroll: '',
51
+ };
52
+ const containerCue = /** @type {!shaka.extern.Cue} */ ({
53
+ region,
54
+ nestedCues: jasmine.any(Object),
55
+ payload: '',
56
+ startTime: 0,
57
+ endTime: Infinity,
58
+ isContainer: true,
59
+ });
60
+ Object.assign(containerCue, properties);
61
+ return mapExpected(containerCue);
62
+ };
63
+
64
+ if (expectedCues.length == 0 && !divProperties) {
65
+ expect(actualCues.length).toBe(0);
66
+ } else {
67
+ // Body.
68
+ expect(actualCues.length).toBe(1);
69
+ const body = actualCues[0];
70
+ expect(body).toEqual(makeContainer(bodyProperties));
71
+
72
+ // Div.
73
+ expect(body.nestedCues.length).toBe(1);
74
+ const div = body.nestedCues[0];
75
+ expect(div).toEqual(makeContainer(divProperties || bodyProperties));
76
+
77
+ // Cues.
78
+ expect(div.nestedCues).toEqual(expectedCues.map(mapExpected));
79
+ }
80
+ }
81
+ };
@@ -401,10 +401,10 @@ shaka.test.Util = class {
401
401
  * within the bounds of the viewport.
402
402
  * @param {string} name An identifier for the screenshot. Use alphanumeric
403
403
  * plus dash and underscore only.
404
- * @param {number} threshold A change threshold in pixels.
404
+ * @param {number} minSimilarity A minimum similarity score between 0 and 1.
405
405
  * @return {!Promise}
406
406
  */
407
- static async checkScreenshot(element, name, threshold=0) {
407
+ static async checkScreenshot(element, name, minSimilarity=1) {
408
408
  // Make sure the DOM is up-to-date and layout has settled before continuing.
409
409
  // Without this delay, or with a shorter delay, we sometimes get missing
410
410
  // elements in our UITextDisplayer tests on some platforms.
@@ -448,13 +448,16 @@ shaka.test.Util = class {
448
448
  const buffer = await shaka.test.Util.fetch(
449
449
  '/screenshot/diff' + parentUrlParams + paramsString);
450
450
  const json = shaka.util.StringUtils.fromUTF8(buffer);
451
- const pixelsChanged = /** @type {number} */(JSON.parse(json));
451
+ const similarity = /** @type {number} */(JSON.parse(json));
452
452
 
453
- // If the change threshold is exceeded, you can review the new screenshot
453
+ // If the minimum similarity is not met, you can review the new screenshot
454
454
  // and the diff image in the screenshots folder. Look for images that end
455
- // with "-new" and "-diff". If cropping doesn't work right, you can view
456
- // the full-page screenshot in the image that ends with "-full".
457
- expect(pixelsChanged).withContext(name).not.toBeGreaterThan(threshold);
455
+ // with "-new" and "-diff". (NOTE: The diff is a pixel-wise diff for human
456
+ // review, and is not produced with the same structural similarity
457
+ // algorithm used to detect changes in the test.) If cropping doesn't work
458
+ // right, you can view the full-page screenshot in the image that ends with
459
+ // "-full".
460
+ expect(similarity).withContext(name).not.toBeLessThan(minSimilarity);
458
461
  }
459
462
  };
460
463
 
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ goog.require('shaka.test.TtmlUtils');
7
8
  goog.require('shaka.test.Util');
8
9
  goog.require('shaka.text.Mp4TtmlParser');
9
10
  goog.require('shaka.util.BufferUtils');
@@ -48,7 +49,14 @@ describe('Mp4TtmlParser', () => {
48
49
  parser.parseInit(ttmlInitSegment);
49
50
  const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
50
51
  const ret = parser.parseMedia(ttmlSegmentMultipleMDAT, time);
51
- expect(ret.length).toBe(20);
52
+ // Bodies.
53
+ expect(ret.length).toBe(2);
54
+ // Divs.
55
+ expect(ret[0].nestedCues.length).toBe(1);
56
+ expect(ret[1].nestedCues.length).toBe(1);
57
+ // Cues.
58
+ expect(ret[0].nestedCues[0].nestedCues.length).toBe(10);
59
+ expect(ret[1].nestedCues[0].nestedCues.length).toBe(10);
52
60
  });
53
61
 
54
62
  it('accounts for offset', () => {
@@ -159,22 +167,7 @@ describe('Mp4TtmlParser', () => {
159
167
  parser.parseInit(ttmlInitSegment);
160
168
  const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
161
169
  const result = parser.parseMedia(ttmlSegment, time);
162
- verifyHelper(cues, result);
170
+ shaka.test.TtmlUtils.verifyHelper(
171
+ cues, result, {startTime: 23, endTime: 53.5});
163
172
  });
164
-
165
- function verifyHelper(/** !Array */ expected, /** !Array */ actual) {
166
- const mapExpected = (cue) => {
167
- if (cue.region) {
168
- cue.region = jasmine.objectContaining(cue.region);
169
- }
170
-
171
- if (cue.nestedCues) {
172
- cue.nestedCues = cue.nestedCues.map(mapExpected);
173
- }
174
-
175
- return jasmine.objectContaining(cue);
176
- };
177
-
178
- expect(actual).toEqual(expected.map(mapExpected));
179
- }
180
173
  });