shaka-player 4.1.1 → 4.2.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 (179) hide show
  1. package/.github/workflows/build-and-test.yaml +67 -15
  2. package/.github/workflows/compute-incremental-coverage.py +251 -0
  3. package/.github/workflows/report-incremental-coverage.yaml +39 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +44 -1
  5. package/AUTHORS +1 -0
  6. package/CHANGELOG.md +36 -8
  7. package/CONTRIBUTORS +3 -0
  8. package/README.md +20 -6
  9. package/demo/common/assets.js +25 -0
  10. package/demo/common/message_ids.js +2 -0
  11. package/demo/config.js +5 -1
  12. package/demo/index.html +1 -3
  13. package/demo/locales/en.json +2 -0
  14. package/demo/locales/source.json +8 -0
  15. package/dist/controls.css +1 -1
  16. package/dist/demo.compiled.debug.js +25 -23
  17. package/dist/demo.compiled.debug.map +3 -3
  18. package/dist/demo.compiled.js +25 -23
  19. package/dist/demo.compiled.map +3 -3
  20. package/dist/deps.js +6 -5
  21. package/dist/locales.js +30 -30
  22. package/dist/receiver.compiled.debug.js +24 -22
  23. package/dist/receiver.compiled.debug.map +3 -3
  24. package/dist/receiver.compiled.js +24 -22
  25. package/dist/receiver.compiled.map +3 -3
  26. package/dist/shaka-player.compiled.d.ts +69 -5
  27. package/dist/shaka-player.compiled.debug.d.ts +69 -5
  28. package/dist/shaka-player.compiled.debug.externs.js +111 -42
  29. package/dist/shaka-player.compiled.debug.js +352 -301
  30. package/dist/shaka-player.compiled.debug.map +4 -4
  31. package/dist/shaka-player.compiled.externs.js +111 -42
  32. package/dist/shaka-player.compiled.js +823 -802
  33. package/dist/shaka-player.compiled.map +4 -4
  34. package/dist/shaka-player.ui.d.ts +69 -5
  35. package/dist/shaka-player.ui.debug.d.ts +69 -5
  36. package/dist/shaka-player.ui.debug.externs.js +111 -42
  37. package/dist/shaka-player.ui.debug.js +360 -310
  38. package/dist/shaka-player.ui.debug.map +4 -4
  39. package/dist/shaka-player.ui.externs.js +111 -42
  40. package/dist/shaka-player.ui.js +980 -957
  41. package/dist/shaka-player.ui.map +4 -4
  42. package/docs/tutorials/fairplay.md +149 -0
  43. package/docs/tutorials/ui.md +6 -6
  44. package/docs/tutorials/upgrade.md +1 -0
  45. package/docs/tutorials/welcome.md +6 -3
  46. package/externs/shaka/abr_manager.js +8 -0
  47. package/externs/shaka/manifest.js +43 -0
  48. package/externs/shaka/player.js +13 -2
  49. package/karma.conf.js +3 -11
  50. package/lib/abr/simple_abr_manager.js +72 -4
  51. package/lib/ads/client_side_ad_manager.js +1 -1
  52. package/lib/dash/dash_parser.js +5 -0
  53. package/lib/hls/hls_parser.js +123 -27
  54. package/lib/media/drm_engine.js +1 -3
  55. package/lib/media/media_source_engine.js +57 -24
  56. package/lib/media/presentation_timeline.js +24 -1
  57. package/lib/media/segment_index.js +8 -0
  58. package/lib/media/segment_reference.js +7 -1
  59. package/lib/media/streaming_engine.js +42 -6
  60. package/lib/media/time_ranges_utils.js +46 -0
  61. package/lib/net/networking_engine.js +1 -0
  62. package/lib/player.js +8 -6
  63. package/lib/polyfill/media_capabilities.js +9 -0
  64. package/lib/polyfill/patchedmediakeys_apple.js +729 -0
  65. package/lib/polyfill/videoplaybackquality.js +3 -1
  66. package/lib/text/cue.js +0 -5
  67. package/lib/text/simple_text_displayer.js +21 -9
  68. package/lib/text/ui_text_displayer.js +83 -19
  69. package/lib/text/vtt_text_parser.js +61 -4
  70. package/lib/util/buffer_utils.js +3 -0
  71. package/lib/util/error.js +21 -1
  72. package/lib/util/fairplay_utils.js +100 -2
  73. package/lib/util/object_utils.js +1 -0
  74. package/lib/util/platform.js +30 -1
  75. package/lib/util/player_configuration.js +3 -10
  76. package/lib/util/stream_utils.js +23 -3
  77. package/lib/util/string_utils.js +102 -13
  78. package/maintained-branches.md +15 -0
  79. package/package.json +3 -5
  80. package/shaka-player.uncompiled.js +1 -0
  81. package/test/hls/hls_parser_unit.js +49 -33
  82. package/test/media/drm_engine_integration.js +24 -8
  83. package/test/media/media_source_engine_integration.js +24 -10
  84. package/test/media/media_source_engine_unit.js +53 -38
  85. package/test/media/streaming_engine_unit.js +107 -32
  86. package/test/test/util/fake_media_source_engine.js +10 -9
  87. package/test/test/util/simple_fakes.js +4 -0
  88. package/test/test/util/streaming_engine_util.js +10 -2
  89. package/test/text/simple_text_displayer_unit.js +27 -0
  90. package/test/text/ui_text_displayer_unit.js +65 -0
  91. package/test/text/vtt_text_parser_unit.js +26 -0
  92. package/test/util/string_utils_unit.js +46 -8
  93. package/ui/text_selection.js +2 -2
  94. package/app-engine/README.md +0 -9
  95. package/app-engine/demo-version-index/README.md +0 -14
  96. package/app-engine/demo-version-index/app.yaml +0 -28
  97. package/app-engine/demo-version-index/generate.py +0 -164
  98. package/app-engine/demo-version-index/main.py +0 -11
  99. package/app-engine/demo-version-index/requirements.txt +0 -5
  100. package/app-engine/demo-version-index/templates/index.html +0 -114
  101. package/app-engine/shaka-player-demo/README.md +0 -18
  102. package/app-engine/shaka-player-demo/app.yaml +0 -61
  103. package/app-engine/shaka-player-demo/main.py +0 -95
  104. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  105. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  106. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  107. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  108. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  109. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  110. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  111. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  112. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  113. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  114. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  115. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  116. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  117. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  118. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  119. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  120. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  121. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  122. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  123. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  124. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  125. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  126. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  127. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  128. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  129. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  130. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  131. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  132. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  133. package/app-engine/shaka-player-demo/posters.py +0 -110
  134. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  135. package/app-engine/shaka-player-demo/time.txt +0 -1
  136. package/build/README.md +0 -127
  137. package/build/__init__.py +0 -0
  138. package/build/all.py +0 -150
  139. package/build/apps.py +0 -186
  140. package/build/build.py +0 -392
  141. package/build/check.py +0 -354
  142. package/build/checkversion.py +0 -84
  143. package/build/compiler.py +0 -513
  144. package/build/conformance.textproto +0 -335
  145. package/build/docker/Dockerfile +0 -20
  146. package/build/docker/docker-compose.yml +0 -10
  147. package/build/docs.py +0 -54
  148. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  149. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  150. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  151. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  152. package/build/gendeps.py +0 -71
  153. package/build/generateExterns.js +0 -867
  154. package/build/generateLocalizations.py +0 -262
  155. package/build/generateTsDefs.py +0 -137
  156. package/build/install-linux-prereqs.sh +0 -136
  157. package/build/license-header +0 -5
  158. package/build/misspellings.txt +0 -39
  159. package/build/pixelsChanged.js +0 -35
  160. package/build/shaka-lab.yaml +0 -161
  161. package/build/shakaBuildHelpers.py +0 -356
  162. package/build/stats.py +0 -816
  163. package/build/subprocessWindowsPatch.py +0 -103
  164. package/build/test.py +0 -541
  165. package/build/types/ads +0 -8
  166. package/build/types/cast +0 -6
  167. package/build/types/cea +0 -13
  168. package/build/types/complete +0 -9
  169. package/build/types/core +0 -108
  170. package/build/types/dash +0 -8
  171. package/build/types/hls +0 -9
  172. package/build/types/manifests +0 -5
  173. package/build/types/networking +0 -6
  174. package/build/types/offline +0 -22
  175. package/build/types/polyfill +0 -18
  176. package/build/types/text +0 -11
  177. package/build/types/ui +0 -39
  178. package/build/updateScreenshots.py +0 -85
  179. package/build/wrapper.template.js +0 -52
@@ -346,7 +346,7 @@ describe('MediaSourceEngine', () => {
346
346
 
347
347
  it('appends the given data', async () => {
348
348
  const p = mediaSourceEngine.appendBuffer(
349
- ContentType.AUDIO, buffer, null, null,
349
+ ContentType.AUDIO, buffer, null,
350
350
  /* hasClosedCaptions= */ false);
351
351
  expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
352
352
  audioSourceBuffer.updateend();
@@ -363,7 +363,7 @@ describe('MediaSourceEngine', () => {
363
363
  jasmine.objectContaining({message: 'fail!'})));
364
364
  await expectAsync(
365
365
  mediaSourceEngine.appendBuffer(
366
- ContentType.AUDIO, buffer, null, null,
366
+ ContentType.AUDIO, buffer, null,
367
367
  /* hasClosedCaptions= */ false))
368
368
  .toBeRejectedWith(expected);
369
369
  expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
@@ -382,7 +382,7 @@ describe('MediaSourceEngine', () => {
382
382
  ContentType.AUDIO));
383
383
  await expectAsync(
384
384
  mediaSourceEngine.appendBuffer(
385
- ContentType.AUDIO, buffer, null, null,
385
+ ContentType.AUDIO, buffer, null,
386
386
  /* hasClosedCaptions= */ false))
387
387
  .toBeRejectedWith(expected);
388
388
  expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
@@ -403,10 +403,10 @@ describe('MediaSourceEngine', () => {
403
403
  ContentType.AUDIO));
404
404
 
405
405
  const p1 = mediaSourceEngine.appendBuffer(
406
- ContentType.AUDIO, buffer, null, null,
406
+ ContentType.AUDIO, buffer, null,
407
407
  /* hasClosedCaptions= */ false);
408
408
  const p2 = mediaSourceEngine.appendBuffer(
409
- ContentType.AUDIO, buffer, null, null,
409
+ ContentType.AUDIO, buffer, null,
410
410
  /* hasClosedCaptions= */ false);
411
411
  audioSourceBuffer.updateend();
412
412
  await expectAsync(p1).toBeResolved();
@@ -416,7 +416,7 @@ describe('MediaSourceEngine', () => {
416
416
  it('rejects the promise if this operation fails async', async () => {
417
417
  mockVideo.error = {code: 5};
418
418
  const p = mediaSourceEngine.appendBuffer(
419
- ContentType.AUDIO, buffer, null, null,
419
+ ContentType.AUDIO, buffer, null,
420
420
  /* hasClosedCaptions= */ false);
421
421
  audioSourceBuffer.error();
422
422
  audioSourceBuffer.updateend();
@@ -433,11 +433,11 @@ describe('MediaSourceEngine', () => {
433
433
  it('queues operations on a single SourceBuffer', async () => {
434
434
  /** @type {!shaka.test.StatusPromise} */
435
435
  const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
436
- ContentType.AUDIO, buffer, null, null,
436
+ ContentType.AUDIO, buffer, null,
437
437
  /* hasClosedCaptions= */ false));
438
438
  /** @type {!shaka.test.StatusPromise} */
439
439
  const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
440
- ContentType.AUDIO, buffer2, null, null,
440
+ ContentType.AUDIO, buffer2, null,
441
441
  /* hasClosedCaptions= */ false));
442
442
 
443
443
  expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
@@ -456,15 +456,15 @@ describe('MediaSourceEngine', () => {
456
456
  it('queues operations independently for different types', async () => {
457
457
  /** @type {!shaka.test.StatusPromise} */
458
458
  const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
459
- ContentType.AUDIO, buffer, null, null,
459
+ ContentType.AUDIO, buffer, null,
460
460
  /* hasClosedCaptions= */ false));
461
461
  /** @type {!shaka.test.StatusPromise} */
462
462
  const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
463
- ContentType.AUDIO, buffer2, null, null,
463
+ ContentType.AUDIO, buffer2, null,
464
464
  /* hasClosedCaptions= */ false));
465
465
  /** @type {!shaka.test.StatusPromise} */
466
466
  const p3 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
467
- ContentType.VIDEO, buffer3, null, null,
467
+ ContentType.VIDEO, buffer3, null,
468
468
  /* hasClosedCaptions= */ false));
469
469
 
470
470
  expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
@@ -499,13 +499,13 @@ describe('MediaSourceEngine', () => {
499
499
  });
500
500
 
501
501
  const p1 = mediaSourceEngine.appendBuffer(
502
- ContentType.AUDIO, buffer, null, null,
502
+ ContentType.AUDIO, buffer, null,
503
503
  /* hasClosedCaptions= */ false);
504
504
  const p2 = mediaSourceEngine.appendBuffer(
505
- ContentType.AUDIO, buffer2, null, null,
505
+ ContentType.AUDIO, buffer2, null,
506
506
  /* hasClosedCaptions= */ false);
507
507
  const p3 = mediaSourceEngine.appendBuffer(
508
- ContentType.AUDIO, buffer3, null, null,
508
+ ContentType.AUDIO, buffer3, null,
509
509
  /* hasClosedCaptions= */ false);
510
510
 
511
511
  await expectAsync(p1).toBeResolved();
@@ -519,8 +519,9 @@ describe('MediaSourceEngine', () => {
519
519
  it('forwards to TextEngine', async () => {
520
520
  const data = new ArrayBuffer(0);
521
521
  expect(mockTextEngine.appendBuffer).not.toHaveBeenCalled();
522
+ const reference = dummyReference(0, 10);
522
523
  await mediaSourceEngine.appendBuffer(
523
- ContentType.TEXT, data, 0, 10, /* hasClosedCaptions= */ false);
524
+ ContentType.TEXT, data, reference, /* hasClosedCaptions= */ false);
524
525
  expect(mockTextEngine.appendBuffer).toHaveBeenCalledWith(
525
526
  data, 0, 10);
526
527
  });
@@ -538,7 +539,7 @@ describe('MediaSourceEngine', () => {
538
539
  const init = async () => {
539
540
  await mediaSourceEngine.init(initObject, false);
540
541
  await mediaSourceEngine.appendBuffer(
541
- ContentType.VIDEO, buffer, null, null,
542
+ ContentType.VIDEO, buffer, null,
542
543
  /* hasClosedCaptions= */ false);
543
544
  expect(mockTextEngine.storeAndAppendClosedCaptions).toHaveBeenCalled();
544
545
  expect(videoSourceBuffer.appendBuffer).toHaveBeenCalled();
@@ -568,7 +569,7 @@ describe('MediaSourceEngine', () => {
568
569
  const init = async () => {
569
570
  await mediaSourceEngine.init(initObject, false);
570
571
  await mediaSourceEngine.appendBuffer(
571
- ContentType.VIDEO, buffer, null, null,
572
+ ContentType.VIDEO, buffer, null,
572
573
  /* hasClosedCaptions= */ false);
573
574
  expect(mockTextEngine.storeAndAppendClosedCaptions)
574
575
  .not.toHaveBeenCalled();
@@ -599,7 +600,8 @@ describe('MediaSourceEngine', () => {
599
600
 
600
601
  // Initialize the closed caption parser.
601
602
  const appendInit = mediaSourceEngine.appendBuffer(
602
- ContentType.VIDEO, buffer, null, null, true);
603
+ ContentType.VIDEO, buffer, null,
604
+ /* hasClosedCaptions= */ true);
603
605
  // In MediaSourceEngine, appendBuffer() is async and Promise-based, but
604
606
  // at the browser level, it's event-based.
605
607
  // MediaSourceEngine waits for the 'updateend' event from the
@@ -612,8 +614,9 @@ describe('MediaSourceEngine', () => {
612
614
  expect(mockTextEngine.storeAndAppendClosedCaptions).not
613
615
  .toHaveBeenCalled();
614
616
  // Parse and append the closed captions embedded in video stream.
617
+ const reference = dummyReference(0, 1000);
615
618
  const appendVideo = mediaSourceEngine.appendBuffer(
616
- ContentType.VIDEO, buffer, 0, Infinity, true);
619
+ ContentType.VIDEO, buffer, reference, true);
617
620
  videoSourceBuffer.updateend();
618
621
  await appendVideo;
619
622
 
@@ -837,11 +840,11 @@ describe('MediaSourceEngine', () => {
837
840
  it('waits for all previous operations to complete', async () => {
838
841
  /** @type {!shaka.test.StatusPromise} */
839
842
  const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
840
- ContentType.AUDIO, buffer, null, null,
843
+ ContentType.AUDIO, buffer, null,
841
844
  /* hasClosedCaptions= */ false));
842
845
  /** @type {!shaka.test.StatusPromise} */
843
846
  const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
844
- ContentType.VIDEO, buffer, null, null,
847
+ ContentType.VIDEO, buffer, null,
845
848
  /* hasClosedCaptions= */ false));
846
849
  /** @type {!shaka.test.StatusPromise} */
847
850
  const p3 = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());
@@ -864,11 +867,11 @@ describe('MediaSourceEngine', () => {
864
867
  it('makes subsequent operations wait', async () => {
865
868
  /** @type {!Promise} */
866
869
  const p1 = mediaSourceEngine.endOfStream();
867
- mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null,
870
+ mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
868
871
  /* hasClosedCaptions= */ false);
869
- mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null,
872
+ mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
870
873
  /* hasClosedCaptions= */ false);
871
- mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null, null,
874
+ mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null,
872
875
  /* hasClosedCaptions= */ false);
873
876
 
874
877
  // endOfStream hasn't been called yet because blocking multiple queues
@@ -896,7 +899,7 @@ describe('MediaSourceEngine', () => {
896
899
  mockMediaSource.endOfStream.and.throwError(new Error());
897
900
  /** @type {!Promise} */
898
901
  const p1 = mediaSourceEngine.endOfStream();
899
- mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null,
902
+ mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
900
903
  /* hasClosedCaptions= */ false);
901
904
 
902
905
  expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
@@ -928,11 +931,11 @@ describe('MediaSourceEngine', () => {
928
931
  it('waits for all previous operations to complete', async () => {
929
932
  /** @type {!shaka.test.StatusPromise} */
930
933
  const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
931
- ContentType.AUDIO, buffer, null, null,
934
+ ContentType.AUDIO, buffer, null,
932
935
  /* hasClosedCaptions= */ false));
933
936
  /** @type {!shaka.test.StatusPromise} */
934
937
  const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
935
- ContentType.VIDEO, buffer, null, null,
938
+ ContentType.VIDEO, buffer, null,
936
939
  /* hasClosedCaptions= */ false));
937
940
  /** @type {!shaka.test.StatusPromise} */
938
941
  const p3 =
@@ -956,11 +959,11 @@ describe('MediaSourceEngine', () => {
956
959
  it('makes subsequent operations wait', async () => {
957
960
  /** @type {!Promise} */
958
961
  const p1 = mediaSourceEngine.setDuration(100);
959
- mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null,
962
+ mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
960
963
  /* hasClosedCaptions= */ false);
961
- mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null,
964
+ mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
962
965
  /* hasClosedCaptions= */ false);
963
- mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null, null,
966
+ mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null,
964
967
  /* hasClosedCaptions= */ false);
965
968
 
966
969
  // The setter hasn't been called yet because blocking multiple queues
@@ -989,7 +992,7 @@ describe('MediaSourceEngine', () => {
989
992
  mockMediaSource.durationSetter.and.throwError(new Error());
990
993
  /** @type {!Promise} */
991
994
  const p1 = mediaSourceEngine.setDuration(100);
992
- mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null,
995
+ mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
993
996
  /* hasClosedCaptions= */ false);
994
997
 
995
998
  expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
@@ -1013,9 +1016,9 @@ describe('MediaSourceEngine', () => {
1013
1016
  });
1014
1017
 
1015
1018
  it('waits for all operations to complete', async () => {
1016
- mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null,
1019
+ mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
1017
1020
  /* hasClosedCaptions= */ false);
1018
- mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null,
1021
+ mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
1019
1022
  /* hasClosedCaptions= */ false);
1020
1023
 
1021
1024
  /** @type {!shaka.test.StatusPromise} */
@@ -1033,7 +1036,7 @@ describe('MediaSourceEngine', () => {
1033
1036
 
1034
1037
  it('resolves even when a pending operation fails', async () => {
1035
1038
  const p = mediaSourceEngine.appendBuffer(
1036
- ContentType.AUDIO, buffer, null, null,
1039
+ ContentType.AUDIO, buffer, null,
1037
1040
  /* hasClosedCaptions= */ false);
1038
1041
  const d = mediaSourceEngine.destroy();
1039
1042
 
@@ -1058,10 +1061,10 @@ describe('MediaSourceEngine', () => {
1058
1061
 
1059
1062
  it('cancels operations that have not yet started', async () => {
1060
1063
  mediaSourceEngine.appendBuffer(
1061
- ContentType.AUDIO, buffer, null, null,
1064
+ ContentType.AUDIO, buffer, null,
1062
1065
  /* hasClosedCaptions= */ false);
1063
1066
  const rejected = mediaSourceEngine.appendBuffer(
1064
- ContentType.AUDIO, buffer2, null, null,
1067
+ ContentType.AUDIO, buffer2, null,
1065
1068
  /* hasClosedCaptions= */ false);
1066
1069
  // Create the expectation first so we don't get unhandled rejection errors
1067
1070
  const expected = expectAsync(rejected).toBeRejected();
@@ -1085,7 +1088,7 @@ describe('MediaSourceEngine', () => {
1085
1088
 
1086
1089
  it('cancels blocking operations that have not yet started', async () => {
1087
1090
  const p1 = mediaSourceEngine.appendBuffer(
1088
- ContentType.AUDIO, buffer, null, null,
1091
+ ContentType.AUDIO, buffer, null,
1089
1092
  /* hasClosedCaptions= */ false);
1090
1093
  const p2 = mediaSourceEngine.endOfStream();
1091
1094
  const d = mediaSourceEngine.destroy();
@@ -1100,7 +1103,7 @@ describe('MediaSourceEngine', () => {
1100
1103
  const d = mediaSourceEngine.destroy();
1101
1104
  await expectAsync(
1102
1105
  mediaSourceEngine.appendBuffer(
1103
- ContentType.AUDIO, buffer, null, null,
1106
+ ContentType.AUDIO, buffer, null,
1104
1107
  /* hasClosedCaptions= */ false))
1105
1108
  .toBeRejected();
1106
1109
  await d;
@@ -1198,4 +1201,16 @@ describe('MediaSourceEngine', () => {
1198
1201
  }
1199
1202
  });
1200
1203
  }
1204
+
1205
+ function dummyReference(startTime, endTime) {
1206
+ return new shaka.media.SegmentReference(
1207
+ startTime, endTime,
1208
+ /* uris= */ () => ['foo://bar'],
1209
+ /* startByte= */ 0,
1210
+ /* endByte= */ null,
1211
+ /* initSegmentReference= */ null,
1212
+ /* timestampOffset= */ 0,
1213
+ /* appendWindowStart= */ 0,
1214
+ /* appendWindowEnd= */ Infinity);
1215
+ }
1201
1216
  });
@@ -100,10 +100,13 @@ describe('StreamingEngine', () => {
100
100
  jasmine.clock().mockDate();
101
101
  });
102
102
 
103
- /** @param {boolean=} trickMode
103
+ /**
104
+ * @param {boolean=} trickMode
104
105
  * @param {number=} mediaOffset The offset from 0 for the segment start times
106
+ * @param {shaka.extern.HlsAes128Key=} hlsAes128Key The AES-128 key to put in
107
+ * the manifest, if one should exist
105
108
  */
106
- function setupVod(trickMode, mediaOffset) {
109
+ function setupVod(trickMode, mediaOffset, hlsAes128Key) {
107
110
  // For VOD, we fake a presentation that has 2 Periods of equal duration
108
111
  // (20 seconds), where each Period has 1 Variant and 1 text stream.
109
112
  //
@@ -209,7 +212,8 @@ describe('StreamingEngine', () => {
209
212
  setupManifest(
210
213
  /* firstPeriodStartTime= */ 0,
211
214
  /* secondPeriodStartTime= */ 20,
212
- /* presentationDuration= */ 40);
215
+ /* presentationDuration= */ 40,
216
+ hlsAes128Key);
213
217
  }
214
218
 
215
219
  function setupLive() {
@@ -356,8 +360,15 @@ describe('StreamingEngine', () => {
356
360
  /* delays= */ netEngineDelays);
357
361
  }
358
362
 
363
+ /**
364
+ * @param {number} firstPeriodStartTime
365
+ * @param {number} secondPeriodStartTime
366
+ * @param {number} presentationDuration
367
+ * @param {shaka.extern.HlsAes128Key=} hlsAes128Key
368
+ */
359
369
  function setupManifest(
360
- firstPeriodStartTime, secondPeriodStartTime, presentationDuration) {
370
+ firstPeriodStartTime, secondPeriodStartTime, presentationDuration,
371
+ hlsAes128Key) {
361
372
  const segmentDurations = {
362
373
  audio: segmentData[ContentType.AUDIO].segmentDuration,
363
374
  video: segmentData[ContentType.VIDEO].segmentDuration,
@@ -380,7 +391,7 @@ describe('StreamingEngine', () => {
380
391
  /** @type {!shaka.media.PresentationTimeline} */(timeline),
381
392
  [firstPeriodStartTime, secondPeriodStartTime],
382
393
  presentationDuration, segmentDurations, initSegmentRanges,
383
- timestampOffsets);
394
+ timestampOffsets, hlsAes128Key);
384
395
 
385
396
  audioStream = manifest.variants[0].audio;
386
397
  videoStream = manifest.variants[0].video;
@@ -868,9 +879,9 @@ describe('StreamingEngine', () => {
868
879
  netEngineDelays.audio = 1.0;
869
880
  netEngineDelays.video = 5.0; // Need init segment and media segment
870
881
 
871
- mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
882
+ mediaSourceEngine.appendBuffer.and.callFake((type, data, reference) => {
872
883
  // Call to the underlying implementation.
873
- const p = mediaSourceEngine.appendBufferImpl(type, data, start, end);
884
+ const p = mediaSourceEngine.appendBufferImpl(type, data, reference);
874
885
 
875
886
  // Validate that no one media type got ahead of any other.
876
887
  let minBuffered = Infinity;
@@ -1012,8 +1023,8 @@ describe('StreamingEngine', () => {
1012
1023
 
1013
1024
  const bufferEnd = {audio: 0, video: 0, text: 0};
1014
1025
  mediaSourceEngine.appendBuffer.and.callFake(
1015
- (type, data, start, end) => {
1016
- bufferEnd[type] = end;
1026
+ (type, data, reference) => {
1027
+ bufferEnd[type] = reference && reference.endTime;
1017
1028
  return Promise.resolve();
1018
1029
  });
1019
1030
  mediaSourceEngine.bufferEnd.and.callFake((type) => {
@@ -1080,9 +1091,9 @@ describe('StreamingEngine', () => {
1080
1091
  // Replace the whole spy since we want to call the original.
1081
1092
  mediaSourceEngine.appendBuffer =
1082
1093
  jasmine.createSpy('appendBuffer')
1083
- .and.callFake(async (type, data, start, end) => {
1094
+ .and.callFake(async (type, data, reference) => {
1084
1095
  await p;
1085
- return Util.invokeSpy(old, type, data, start, end);
1096
+ return Util.invokeSpy(old, type, data, reference);
1086
1097
  });
1087
1098
 
1088
1099
  await streamingEngine.start();
@@ -1107,9 +1118,9 @@ describe('StreamingEngine', () => {
1107
1118
  // Replace the whole spy since we want to call the original.
1108
1119
  mediaSourceEngine.appendBuffer =
1109
1120
  jasmine.createSpy('appendBuffer')
1110
- .and.callFake(async (type, data, start, end) => {
1121
+ .and.callFake(async (type, data, reference) => {
1111
1122
  await p;
1112
- return Util.invokeSpy(old, type, data, start, end);
1123
+ return Util.invokeSpy(old, type, data, reference);
1113
1124
  });
1114
1125
 
1115
1126
  await streamingEngine.start();
@@ -1750,9 +1761,9 @@ describe('StreamingEngine', () => {
1750
1761
  // eslint-disable-next-line no-restricted-syntax
1751
1762
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1752
1763
  mediaSourceEngine.appendBuffer.and.callFake(
1753
- (type, data, startTime, endTime) => {
1764
+ (type, data, reference) => {
1754
1765
  expect(presentationTimeInSeconds).toBe(125);
1755
- if (startTime >= 100) {
1766
+ if (reference && reference.startTime >= 100) {
1756
1767
  // Ignore a possible call for the first Period.
1757
1768
  expect(Util.invokeSpy(timeline.getSegmentAvailabilityStart))
1758
1769
  .toBe(100);
@@ -1765,7 +1776,7 @@ describe('StreamingEngine', () => {
1765
1776
 
1766
1777
  // eslint-disable-next-line no-restricted-syntax
1767
1778
  return originalAppendBuffer.call(
1768
- mediaSourceEngine, type, data, startTime, endTime);
1779
+ mediaSourceEngine, type, data, reference);
1769
1780
  });
1770
1781
 
1771
1782
  await runTest(slideSegmentAvailabilityWindow);
@@ -1862,14 +1873,14 @@ describe('StreamingEngine', () => {
1862
1873
  // eslint-disable-next-line no-restricted-syntax
1863
1874
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1864
1875
  mediaSourceEngine.appendBuffer.and.callFake(
1865
- (type, data, startTime, endTime) => {
1876
+ (type, data, reference) => {
1866
1877
  // Reject the first video init segment.
1867
1878
  if (data == segmentData[ContentType.VIDEO].initSegments[0]) {
1868
1879
  return Promise.reject(expectedError);
1869
1880
  } else {
1870
1881
  // eslint-disable-next-line no-restricted-syntax
1871
1882
  return originalAppendBuffer.call(
1872
- mediaSourceEngine, type, data, startTime, endTime);
1883
+ mediaSourceEngine, type, data, reference);
1873
1884
  }
1874
1885
  });
1875
1886
 
@@ -1896,14 +1907,14 @@ describe('StreamingEngine', () => {
1896
1907
  // eslint-disable-next-line no-restricted-syntax
1897
1908
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1898
1909
  mediaSourceEngine.appendBuffer.and.callFake(
1899
- (type, data, startTime, endTime) => {
1910
+ (type, data, reference) => {
1900
1911
  // Reject the first audio segment.
1901
1912
  if (data == segmentData[ContentType.AUDIO].segments[0]) {
1902
1913
  return Promise.reject(expectedError);
1903
1914
  } else {
1904
1915
  // eslint-disable-next-line no-restricted-syntax
1905
1916
  return originalAppendBuffer.call(
1906
- mediaSourceEngine, type, data, startTime, endTime);
1917
+ mediaSourceEngine, type, data, reference);
1907
1918
  }
1908
1919
  });
1909
1920
 
@@ -2187,7 +2198,7 @@ describe('StreamingEngine', () => {
2187
2198
  // Now that we're streaming, throw QuotaExceededError on every segment
2188
2199
  // to quickly trigger the quota error.
2189
2200
  const appendBufferSpy = jasmine.createSpy('appendBuffer');
2190
- appendBufferSpy.and.callFake((type, data, startTime, endTime) => {
2201
+ appendBufferSpy.and.callFake((type, data, reference) => {
2191
2202
  throw new shaka.util.Error(
2192
2203
  shaka.util.Error.Severity.CRITICAL,
2193
2204
  shaka.util.Error.Category.MEDIA,
@@ -2373,9 +2384,10 @@ describe('StreamingEngine', () => {
2373
2384
  // Throw two QuotaExceededErrors at different times.
2374
2385
  let numErrorsThrown = 0;
2375
2386
  appendBufferSpy.and.callFake(
2376
- (type, data, startTime, endTime) => {
2377
- const throwError = (numErrorsThrown == 0 && startTime == 10) ||
2378
- (numErrorsThrown == 1 && startTime == 20);
2387
+ (type, data, reference) => {
2388
+ const throwError = reference &&
2389
+ ((numErrorsThrown == 0 && reference.startTime == 10) ||
2390
+ (numErrorsThrown == 1 && reference.startTime == 20));
2379
2391
  if (throwError) {
2380
2392
  numErrorsThrown++;
2381
2393
  throw new shaka.util.Error(
@@ -2386,7 +2398,7 @@ describe('StreamingEngine', () => {
2386
2398
  } else {
2387
2399
  // eslint-disable-next-line no-restricted-syntax
2388
2400
  const p = originalAppendBuffer.call(
2389
- mediaSourceEngine, type, data, startTime, endTime);
2401
+ mediaSourceEngine, type, data, reference);
2390
2402
  return p;
2391
2403
  }
2392
2404
  });
@@ -2435,8 +2447,8 @@ describe('StreamingEngine', () => {
2435
2447
  // Throw QuotaExceededError multiple times after at least one segment of
2436
2448
  // each type has been appended.
2437
2449
  appendBufferSpy.and.callFake(
2438
- (type, data, startTime, endTime) => {
2439
- if (startTime >= 10) {
2450
+ (type, data, reference) => {
2451
+ if (reference && reference.startTime >= 10) {
2440
2452
  throw new shaka.util.Error(
2441
2453
  shaka.util.Error.Severity.CRITICAL,
2442
2454
  shaka.util.Error.Category.MEDIA,
@@ -2445,7 +2457,7 @@ describe('StreamingEngine', () => {
2445
2457
  } else {
2446
2458
  // eslint-disable-next-line no-restricted-syntax
2447
2459
  const p = originalAppendBuffer.call(
2448
- mediaSourceEngine, type, data, startTime, endTime);
2460
+ mediaSourceEngine, type, data, reference);
2449
2461
  return p;
2450
2462
  }
2451
2463
  });
@@ -2986,8 +2998,8 @@ describe('StreamingEngine', () => {
2986
2998
 
2987
2999
  // Naive buffered range tracking that only tracks the buffer end.
2988
3000
  const bufferEnd = {audio: 0, video: 0, text: 0};
2989
- mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
2990
- bufferEnd[type] = end;
3001
+ mediaSourceEngine.appendBuffer.and.callFake((type, data, reference) => {
3002
+ bufferEnd[type] = reference && reference.endTime;
2991
3003
  return Promise.resolve();
2992
3004
  });
2993
3005
  mediaSourceEngine.bufferEnd.and.callFake((type) => bufferEnd[type]);
@@ -3281,8 +3293,8 @@ describe('StreamingEngine', () => {
3281
3293
 
3282
3294
  const bufferEnd = {audio: 0, video: 0, text: 0};
3283
3295
  mediaSourceEngine.appendBuffer.and.callFake(
3284
- (type, data, start, end) => {
3285
- bufferEnd[type] = end;
3296
+ (type, data, reference) => {
3297
+ bufferEnd[type] = reference && reference.endTime;
3286
3298
  return Promise.resolve();
3287
3299
  });
3288
3300
  mediaSourceEngine.bufferEnd.and.callFake((type) => {
@@ -3429,6 +3441,69 @@ describe('StreamingEngine', () => {
3429
3441
  expect(alternateVideoStream.createSegmentIndex).not.toHaveBeenCalled();
3430
3442
  });
3431
3443
 
3444
+ describe('AES-128', () => {
3445
+ let key;
3446
+ /** @type {!shaka.extern.HlsAes128Key} */
3447
+ let hlsAes128Key;
3448
+
3449
+ beforeEach(async () => {
3450
+ // Get a key.
3451
+ const keyData = new ArrayBuffer(16);
3452
+ const keyDataView = new DataView(keyData);
3453
+ keyDataView.setInt16(0, 31710); // 0111 1011 1101 1110
3454
+ key = await window.crypto.subtle.importKey(
3455
+ 'raw', keyData, 'AES-CBC', true, ['decrypt']);
3456
+
3457
+ // Set up a manifest with AES-128 key info.
3458
+ // We don't actually provide the imported key OR the key fetching function
3459
+ // here, though, so that the individual tests can choose what the starting
3460
+ // state of the hlsAes128Key object is.
3461
+ hlsAes128Key = {method: 'AES-128', firstMediaSequenceNumber: 0};
3462
+
3463
+ setupVod(false, 0, hlsAes128Key);
3464
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
3465
+ presentationTimeInSeconds = 0;
3466
+ createStreamingEngine();
3467
+ streamingEngine.switchVariant(variant);
3468
+ });
3469
+
3470
+ afterEach(async () => {
3471
+ await streamingEngine.destroy();
3472
+ });
3473
+
3474
+ async function runTest() {
3475
+ spyOn(window.crypto.subtle, 'decrypt').and.callThrough();
3476
+
3477
+ await streamingEngine.start();
3478
+ playing = true;
3479
+ await Util.fakeEventLoop(10);
3480
+
3481
+ expect(mediaSourceEngine.appendBuffer).toHaveBeenCalledTimes(2);
3482
+ expect(window.crypto.subtle.decrypt).toHaveBeenCalledTimes(2);
3483
+ expect(window.crypto.subtle.decrypt).toHaveBeenCalledWith(
3484
+ {name: 'AES-CBC', iv: jasmine.any(Object)}, key, jasmine.any(Object));
3485
+ }
3486
+
3487
+ it('decrypts segments', async () => {
3488
+ hlsAes128Key.cryptoKey = key;
3489
+ await runTest();
3490
+ });
3491
+
3492
+ it('downloads key if not pre-filled', async () => {
3493
+ hlsAes128Key.fetchKey = () => {
3494
+ hlsAes128Key.cryptoKey = key;
3495
+ hlsAes128Key.fetchKey = undefined;
3496
+ return Promise.resolve();
3497
+ };
3498
+
3499
+ await runTest();
3500
+
3501
+ // The key should have been fetched.
3502
+ expect(hlsAes128Key.cryptoKey).not.toBeUndefined();
3503
+ expect(hlsAes128Key.fetchKey).toBeUndefined();
3504
+ });
3505
+ });
3506
+
3432
3507
  describe('destroy', () => {
3433
3508
  it('aborts pending network operations', async () => {
3434
3509
  setupVod();
@@ -78,8 +78,8 @@ shaka.test.FakeMediaSourceEngine = class {
78
78
 
79
79
  /** @type {!jasmine.Spy} */
80
80
  this.appendBuffer = jasmine.createSpy('appendBuffer')
81
- .and.callFake((type, data, start, end) =>
82
- this.appendBufferImpl(type, data, start, end));
81
+ .and.callFake((type, data, reference) =>
82
+ this.appendBufferImpl(type, data, reference));
83
83
 
84
84
  /** @type {!jasmine.Spy} */
85
85
  this.clear = jasmine.createSpy('clear')
@@ -229,11 +229,10 @@ shaka.test.FakeMediaSourceEngine = class {
229
229
  /**
230
230
  * @param {string} type
231
231
  * @param {!ArrayBuffer} data
232
- * @param {?number} startTime
233
- * @param {?number} endTime
232
+ * @param {?shaka.media.SegmentReference} reference
234
233
  * @return {!Promise}
235
234
  */
236
- appendBufferImpl(type, data, startTime, endTime) {
235
+ appendBufferImpl(type, data, reference) {
237
236
  if (!this.segments[type]) {
238
237
  throw new Error('unexpected type');
239
238
  }
@@ -257,8 +256,7 @@ shaka.test.FakeMediaSourceEngine = class {
257
256
  }
258
257
  if (i >= 0) {
259
258
  // Update the list of which init segment was appended last.
260
- expect(startTime).toBe(null);
261
- expect(endTime).toBe(null);
259
+ expect(reference).toBe(null);
262
260
  this.initSegments[type] =
263
261
  this.segmentData[type].initSegments.map((c) => false);
264
262
  this.initSegments[type][i] = true;
@@ -289,8 +287,11 @@ shaka.test.FakeMediaSourceEngine = class {
289
287
  const expectedStartTime = i * segmentData.segmentDuration;
290
288
  const expectedEndTime = expectedStartTime + segmentData.segmentDuration;
291
289
  expect(appendedTime).toBe(expectedStartTime);
292
- expect(startTime).toBe(expectedStartTime);
293
- expect(endTime).toBe(expectedEndTime);
290
+ expect(reference).not.toBe(null);
291
+ if (reference) {
292
+ expect(reference.startTime).toBe(expectedStartTime);
293
+ expect(reference.endTime).toBe(expectedEndTime);
294
+ }
294
295
 
295
296
  this.segments[type][i] = true;
296
297
  return Promise.resolve();
@@ -481,6 +481,10 @@ shaka.test.FakeSegmentIndex = class {
481
481
  return this.get(nextPosition - 1);
482
482
  },
483
483
 
484
+ currentPosition: () => {
485
+ return nextPosition;
486
+ },
487
+
484
488
  seek: (time) => {
485
489
  nextPosition = this.find(time);
486
490
  return this.get(nextPosition++);