shaka-player 4.0.2 → 4.0.5

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 +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 +2 -0
  6. package/CHANGELOG.md +43 -0
  7. package/CONTRIBUTORS +4 -0
  8. package/README.md +20 -6
  9. package/demo/common/assets.js +1 -1
  10. package/demo/index.html +1 -3
  11. package/demo/main.js +3 -3
  12. package/dist/controls.css +1 -1
  13. package/dist/demo.compiled.debug.js +3 -3
  14. package/dist/demo.compiled.debug.map +2 -2
  15. package/dist/demo.compiled.js +3 -3
  16. package/dist/demo.compiled.map +2 -2
  17. package/dist/deps.js +3 -3
  18. package/dist/locales.js +29 -29
  19. package/dist/receiver.compiled.debug.js +1 -1
  20. package/dist/receiver.compiled.debug.map +1 -1
  21. package/dist/receiver.compiled.js +1 -1
  22. package/dist/receiver.compiled.map +1 -1
  23. package/dist/shaka-player.compiled.d.ts +12 -1
  24. package/dist/shaka-player.compiled.debug.d.ts +12 -1
  25. package/dist/shaka-player.compiled.debug.externs.js +52 -40
  26. package/dist/shaka-player.compiled.debug.js +269 -256
  27. package/dist/shaka-player.compiled.debug.map +4 -4
  28. package/dist/shaka-player.compiled.externs.js +52 -40
  29. package/dist/shaka-player.compiled.js +739 -730
  30. package/dist/shaka-player.compiled.map +4 -4
  31. package/dist/shaka-player.ui.d.ts +12 -1
  32. package/dist/shaka-player.ui.debug.d.ts +12 -1
  33. package/dist/shaka-player.ui.debug.externs.js +52 -40
  34. package/dist/shaka-player.ui.debug.js +296 -282
  35. package/dist/shaka-player.ui.debug.map +4 -4
  36. package/dist/shaka-player.ui.externs.js +52 -40
  37. package/dist/shaka-player.ui.js +885 -878
  38. package/dist/shaka-player.ui.map +4 -4
  39. package/docs/tutorials/ui.md +5 -5
  40. package/docs/tutorials/upgrade.md +1 -0
  41. package/docs/tutorials/welcome.md +6 -3
  42. package/externs/shaka/player.js +4 -2
  43. package/karma.conf.js +3 -11
  44. package/lib/ads/client_side_ad_manager.js +1 -1
  45. package/lib/dash/dash_parser.js +5 -0
  46. package/lib/hls/hls_parser.js +183 -70
  47. package/lib/media/drm_engine.js +1 -1
  48. package/lib/media/media_source_engine.js +57 -24
  49. package/lib/media/presentation_timeline.js +24 -1
  50. package/lib/media/segment_index.js +26 -1
  51. package/lib/media/segment_reference.js +10 -3
  52. package/lib/media/streaming_engine.js +4 -4
  53. package/lib/media/time_ranges_utils.js +46 -0
  54. package/lib/player.js +1 -6
  55. package/lib/polyfill/media_capabilities.js +9 -0
  56. package/lib/polyfill/videoplaybackquality.js +3 -1
  57. package/lib/text/cue.js +0 -5
  58. package/lib/text/simple_text_displayer.js +21 -9
  59. package/lib/text/ui_text_displayer.js +83 -19
  60. package/lib/util/buffer_utils.js +3 -0
  61. package/lib/util/object_utils.js +1 -0
  62. package/lib/util/platform.js +19 -1
  63. package/lib/util/stream_utils.js +24 -3
  64. package/lib/util/string_utils.js +102 -13
  65. package/maintained-branches.md +15 -0
  66. package/package.json +3 -5
  67. package/test/hls/hls_parser_unit.js +102 -8
  68. package/test/media/drm_engine_integration.js +24 -8
  69. package/test/media/media_source_engine_integration.js +24 -10
  70. package/test/media/media_source_engine_unit.js +53 -38
  71. package/test/media/streaming_engine_unit.js +28 -27
  72. package/test/test/util/fake_media_source_engine.js +10 -9
  73. package/test/text/simple_text_displayer_unit.js +27 -0
  74. package/test/text/ui_text_displayer_unit.js +65 -0
  75. package/test/util/string_utils_unit.js +46 -8
  76. package/ui/text_selection.js +2 -2
  77. package/app-engine/README.md +0 -9
  78. package/app-engine/demo-version-index/README.md +0 -14
  79. package/app-engine/demo-version-index/app.yaml +0 -28
  80. package/app-engine/demo-version-index/generate.py +0 -164
  81. package/app-engine/demo-version-index/main.py +0 -11
  82. package/app-engine/demo-version-index/requirements.txt +0 -5
  83. package/app-engine/demo-version-index/templates/index.html +0 -114
  84. package/app-engine/shaka-player-demo/README.md +0 -18
  85. package/app-engine/shaka-player-demo/app.yaml +0 -61
  86. package/app-engine/shaka-player-demo/main.py +0 -95
  87. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  88. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  89. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  90. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  91. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  92. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  93. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  94. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  95. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  96. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  97. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  98. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  99. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  100. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  101. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  102. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  103. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  104. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  105. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  106. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  107. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  108. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  109. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  110. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  111. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  112. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  113. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  114. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  115. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  116. package/app-engine/shaka-player-demo/posters.py +0 -110
  117. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  118. package/app-engine/shaka-player-demo/time.txt +0 -1
  119. package/build/README.md +0 -127
  120. package/build/__init__.py +0 -0
  121. package/build/all.py +0 -150
  122. package/build/apps.py +0 -186
  123. package/build/build.py +0 -392
  124. package/build/check.py +0 -354
  125. package/build/checkversion.py +0 -84
  126. package/build/compiler.py +0 -513
  127. package/build/conformance.textproto +0 -335
  128. package/build/docker/Dockerfile +0 -20
  129. package/build/docker/docker-compose.yml +0 -10
  130. package/build/docs.py +0 -54
  131. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  132. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  133. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  134. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  135. package/build/gendeps.py +0 -71
  136. package/build/generateExterns.js +0 -867
  137. package/build/generateLocalizations.py +0 -262
  138. package/build/generateTsDefs.py +0 -137
  139. package/build/install-linux-prereqs.sh +0 -136
  140. package/build/license-header +0 -5
  141. package/build/misspellings.txt +0 -39
  142. package/build/pixelsChanged.js +0 -35
  143. package/build/shaka-lab.yaml +0 -161
  144. package/build/shakaBuildHelpers.py +0 -356
  145. package/build/stats.py +0 -816
  146. package/build/subprocessWindowsPatch.py +0 -103
  147. package/build/test.py +0 -541
  148. package/build/types/ads +0 -8
  149. package/build/types/cast +0 -6
  150. package/build/types/cea +0 -13
  151. package/build/types/complete +0 -9
  152. package/build/types/core +0 -108
  153. package/build/types/dash +0 -8
  154. package/build/types/hls +0 -9
  155. package/build/types/manifests +0 -5
  156. package/build/types/networking +0 -6
  157. package/build/types/offline +0 -22
  158. package/build/types/polyfill +0 -18
  159. package/build/types/text +0 -11
  160. package/build/types/ui +0 -39
  161. package/build/updateScreenshots.py +0 -85
  162. 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
  });
@@ -867,9 +867,9 @@ describe('StreamingEngine', () => {
867
867
  netEngineDelays.audio = 1.0;
868
868
  netEngineDelays.video = 5.0; // Need init segment and media segment
869
869
 
870
- mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
870
+ mediaSourceEngine.appendBuffer.and.callFake((type, data, reference) => {
871
871
  // Call to the underlying implementation.
872
- const p = mediaSourceEngine.appendBufferImpl(type, data, start, end);
872
+ const p = mediaSourceEngine.appendBufferImpl(type, data, reference);
873
873
 
874
874
  // Validate that no one media type got ahead of any other.
875
875
  let minBuffered = Infinity;
@@ -1011,8 +1011,8 @@ describe('StreamingEngine', () => {
1011
1011
 
1012
1012
  const bufferEnd = {audio: 0, video: 0, text: 0};
1013
1013
  mediaSourceEngine.appendBuffer.and.callFake(
1014
- (type, data, start, end) => {
1015
- bufferEnd[type] = end;
1014
+ (type, data, reference) => {
1015
+ bufferEnd[type] = reference && reference.endTime;
1016
1016
  return Promise.resolve();
1017
1017
  });
1018
1018
  mediaSourceEngine.bufferEnd.and.callFake((type) => {
@@ -1079,9 +1079,9 @@ describe('StreamingEngine', () => {
1079
1079
  // Replace the whole spy since we want to call the original.
1080
1080
  mediaSourceEngine.appendBuffer =
1081
1081
  jasmine.createSpy('appendBuffer')
1082
- .and.callFake(async (type, data, start, end) => {
1082
+ .and.callFake(async (type, data, reference) => {
1083
1083
  await p;
1084
- return Util.invokeSpy(old, type, data, start, end);
1084
+ return Util.invokeSpy(old, type, data, reference);
1085
1085
  });
1086
1086
 
1087
1087
  await streamingEngine.start();
@@ -1106,9 +1106,9 @@ describe('StreamingEngine', () => {
1106
1106
  // Replace the whole spy since we want to call the original.
1107
1107
  mediaSourceEngine.appendBuffer =
1108
1108
  jasmine.createSpy('appendBuffer')
1109
- .and.callFake(async (type, data, start, end) => {
1109
+ .and.callFake(async (type, data, reference) => {
1110
1110
  await p;
1111
- return Util.invokeSpy(old, type, data, start, end);
1111
+ return Util.invokeSpy(old, type, data, reference);
1112
1112
  });
1113
1113
 
1114
1114
  await streamingEngine.start();
@@ -1749,9 +1749,9 @@ describe('StreamingEngine', () => {
1749
1749
  // eslint-disable-next-line no-restricted-syntax
1750
1750
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1751
1751
  mediaSourceEngine.appendBuffer.and.callFake(
1752
- (type, data, startTime, endTime) => {
1752
+ (type, data, reference) => {
1753
1753
  expect(presentationTimeInSeconds).toBe(125);
1754
- if (startTime >= 100) {
1754
+ if (reference && reference.startTime >= 100) {
1755
1755
  // Ignore a possible call for the first Period.
1756
1756
  expect(Util.invokeSpy(timeline.getSegmentAvailabilityStart))
1757
1757
  .toBe(100);
@@ -1764,7 +1764,7 @@ describe('StreamingEngine', () => {
1764
1764
 
1765
1765
  // eslint-disable-next-line no-restricted-syntax
1766
1766
  return originalAppendBuffer.call(
1767
- mediaSourceEngine, type, data, startTime, endTime);
1767
+ mediaSourceEngine, type, data, reference);
1768
1768
  });
1769
1769
 
1770
1770
  await runTest(slideSegmentAvailabilityWindow);
@@ -1861,14 +1861,14 @@ describe('StreamingEngine', () => {
1861
1861
  // eslint-disable-next-line no-restricted-syntax
1862
1862
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1863
1863
  mediaSourceEngine.appendBuffer.and.callFake(
1864
- (type, data, startTime, endTime) => {
1864
+ (type, data, reference) => {
1865
1865
  // Reject the first video init segment.
1866
1866
  if (data == segmentData[ContentType.VIDEO].initSegments[0]) {
1867
1867
  return Promise.reject(expectedError);
1868
1868
  } else {
1869
1869
  // eslint-disable-next-line no-restricted-syntax
1870
1870
  return originalAppendBuffer.call(
1871
- mediaSourceEngine, type, data, startTime, endTime);
1871
+ mediaSourceEngine, type, data, reference);
1872
1872
  }
1873
1873
  });
1874
1874
 
@@ -1895,14 +1895,14 @@ describe('StreamingEngine', () => {
1895
1895
  // eslint-disable-next-line no-restricted-syntax
1896
1896
  shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl;
1897
1897
  mediaSourceEngine.appendBuffer.and.callFake(
1898
- (type, data, startTime, endTime) => {
1898
+ (type, data, reference) => {
1899
1899
  // Reject the first audio segment.
1900
1900
  if (data == segmentData[ContentType.AUDIO].segments[0]) {
1901
1901
  return Promise.reject(expectedError);
1902
1902
  } else {
1903
1903
  // eslint-disable-next-line no-restricted-syntax
1904
1904
  return originalAppendBuffer.call(
1905
- mediaSourceEngine, type, data, startTime, endTime);
1905
+ mediaSourceEngine, type, data, reference);
1906
1906
  }
1907
1907
  });
1908
1908
 
@@ -2186,7 +2186,7 @@ describe('StreamingEngine', () => {
2186
2186
  // Now that we're streaming, throw QuotaExceededError on every segment
2187
2187
  // to quickly trigger the quota error.
2188
2188
  const appendBufferSpy = jasmine.createSpy('appendBuffer');
2189
- appendBufferSpy.and.callFake((type, data, startTime, endTime) => {
2189
+ appendBufferSpy.and.callFake((type, data, reference) => {
2190
2190
  throw new shaka.util.Error(
2191
2191
  shaka.util.Error.Severity.CRITICAL,
2192
2192
  shaka.util.Error.Category.MEDIA,
@@ -2372,9 +2372,10 @@ describe('StreamingEngine', () => {
2372
2372
  // Throw two QuotaExceededErrors at different times.
2373
2373
  let numErrorsThrown = 0;
2374
2374
  appendBufferSpy.and.callFake(
2375
- (type, data, startTime, endTime) => {
2376
- const throwError = (numErrorsThrown == 0 && startTime == 10) ||
2377
- (numErrorsThrown == 1 && startTime == 20);
2375
+ (type, data, reference) => {
2376
+ const throwError = reference &&
2377
+ ((numErrorsThrown == 0 && reference.startTime == 10) ||
2378
+ (numErrorsThrown == 1 && reference.startTime == 20));
2378
2379
  if (throwError) {
2379
2380
  numErrorsThrown++;
2380
2381
  throw new shaka.util.Error(
@@ -2385,7 +2386,7 @@ describe('StreamingEngine', () => {
2385
2386
  } else {
2386
2387
  // eslint-disable-next-line no-restricted-syntax
2387
2388
  const p = originalAppendBuffer.call(
2388
- mediaSourceEngine, type, data, startTime, endTime);
2389
+ mediaSourceEngine, type, data, reference);
2389
2390
  return p;
2390
2391
  }
2391
2392
  });
@@ -2434,8 +2435,8 @@ describe('StreamingEngine', () => {
2434
2435
  // Throw QuotaExceededError multiple times after at least one segment of
2435
2436
  // each type has been appended.
2436
2437
  appendBufferSpy.and.callFake(
2437
- (type, data, startTime, endTime) => {
2438
- if (startTime >= 10) {
2438
+ (type, data, reference) => {
2439
+ if (reference && reference.startTime >= 10) {
2439
2440
  throw new shaka.util.Error(
2440
2441
  shaka.util.Error.Severity.CRITICAL,
2441
2442
  shaka.util.Error.Category.MEDIA,
@@ -2444,7 +2445,7 @@ describe('StreamingEngine', () => {
2444
2445
  } else {
2445
2446
  // eslint-disable-next-line no-restricted-syntax
2446
2447
  const p = originalAppendBuffer.call(
2447
- mediaSourceEngine, type, data, startTime, endTime);
2448
+ mediaSourceEngine, type, data, reference);
2448
2449
  return p;
2449
2450
  }
2450
2451
  });
@@ -2985,8 +2986,8 @@ describe('StreamingEngine', () => {
2985
2986
 
2986
2987
  // Naive buffered range tracking that only tracks the buffer end.
2987
2988
  const bufferEnd = {audio: 0, video: 0, text: 0};
2988
- mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
2989
- bufferEnd[type] = end;
2989
+ mediaSourceEngine.appendBuffer.and.callFake((type, data, reference) => {
2990
+ bufferEnd[type] = reference && reference.endTime;
2990
2991
  return Promise.resolve();
2991
2992
  });
2992
2993
  mediaSourceEngine.bufferEnd.and.callFake((type) => bufferEnd[type]);
@@ -3280,8 +3281,8 @@ describe('StreamingEngine', () => {
3280
3281
 
3281
3282
  const bufferEnd = {audio: 0, video: 0, text: 0};
3282
3283
  mediaSourceEngine.appendBuffer.and.callFake(
3283
- (type, data, start, end) => {
3284
- bufferEnd[type] = end;
3284
+ (type, data, reference) => {
3285
+ bufferEnd[type] = reference && reference.endTime;
3285
3286
  return Promise.resolve();
3286
3287
  });
3287
3288
  mediaSourceEngine.bufferEnd.and.callFake((type) => {
@@ -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();
@@ -127,6 +127,33 @@ describe('SimpleTextDisplayer', () => {
127
127
  [shakaCue]);
128
128
  });
129
129
 
130
+ it('flattens nested cue payloads correctly', () => {
131
+ const level0ContainerCue = new shaka.text.Cue(10, 30, '');
132
+ level0ContainerCue.isContainer = true;
133
+
134
+ const level1NonContainerCueA = new shaka.text.Cue(10, 20, '');
135
+ const level1NonContainerCueB = new shaka.text.Cue(20, 30, '');
136
+
137
+ // Add a trailing whitespace character to get a space-delimited expected
138
+ // result.
139
+ const cueANestedCue0 = new shaka.text.Cue(10, 20, 'Cue A Test0 ');
140
+ const cueANestedCue1 = new shaka.text.Cue(10, 20, 'Cue A Test1');
141
+ const cueBNestedCue0 = new shaka.text.Cue(20, 30, 'Cue B Test0 ');
142
+ const cueBNestedCue1 = new shaka.text.Cue(20, 30, 'Cue B Test1');
143
+
144
+ level1NonContainerCueA.nestedCues = [cueANestedCue0, cueANestedCue1];
145
+ level1NonContainerCueB.nestedCues = [cueBNestedCue0, cueBNestedCue1];
146
+ level0ContainerCue.nestedCues =
147
+ [level1NonContainerCueA, level1NonContainerCueB];
148
+
149
+ verifyHelper(
150
+ [
151
+ {startTime: 10, endTime: 20, text: 'Cue A Test0 Cue A Test1'},
152
+ {startTime: 20, endTime: 30, text: 'Cue B Test0 Cue B Test1'},
153
+ ],
154
+ [level0ContainerCue]);
155
+ });
156
+
130
157
  // Regression test for b/159050711
131
158
  it('maintains the styles of the parent cue', () => {
132
159
  const shakaCue = new shaka.text.Cue(10, 20, '');
@@ -50,6 +50,7 @@ describe('UITextDisplayer', () => {
50
50
  });
51
51
 
52
52
  beforeEach(() => {
53
+ video.currentTime = 0;
53
54
  textDisplayer = new shaka.text.UITextDisplayer(video, videoContainer);
54
55
  });
55
56
 
@@ -396,4 +397,68 @@ describe('UITextDisplayer', () => {
396
397
  expect(parentCueElements.length).toBe(1);
397
398
  expect(parentCueElements[0].textContent).toBe('');
398
399
  });
400
+
401
+ it('creates separate elements for cue regions', () => {
402
+ const cueRegion = new shaka.text.CueRegion();
403
+ cueRegion.id = 'regionId';
404
+ cueRegion.height = 80;
405
+ cueRegion.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
406
+ cueRegion.width = 80;
407
+ cueRegion.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
408
+ cueRegion.viewportAnchorX = 10;
409
+ cueRegion.viewportAnchorY = 10;
410
+ cueRegion.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
411
+
412
+ // These all attach to the same region, but only one region element should
413
+ // be created.
414
+ const cues = [
415
+ new shaka.text.Cue(0, 100, ''),
416
+ new shaka.text.Cue(0, 100, ''),
417
+ new shaka.text.Cue(0, 100, ''),
418
+ ];
419
+ for (const cue of cues) {
420
+ cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
421
+ cue.region = cueRegion;
422
+ }
423
+
424
+ textDisplayer.setTextVisibility(true);
425
+ textDisplayer.append(cues);
426
+ updateCaptions();
427
+
428
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
429
+ const allRegionElements = textContainer.querySelectorAll(
430
+ '.shaka-text-region');
431
+
432
+ // Verify that the nested cues are all attached to a single region element.
433
+ expect(allRegionElements.length).toBe(1);
434
+ const regionElement = allRegionElements[0];
435
+ const children = Array.from(regionElement.childNodes).filter(
436
+ (e) => e.nodeType == Node.ELEMENT_NODE);
437
+ expect(children.length).toBe(3);
438
+
439
+ // Verify styles applied to the region element.
440
+ const regionCssObj = parseCssText(regionElement.style.cssText);
441
+ const expectRegionCssObj = {
442
+ 'position': 'absolute',
443
+ 'height': '80%',
444
+ 'width': '80%',
445
+ 'top': '10%',
446
+ 'left': '10%',
447
+ 'display': 'flex',
448
+ 'flex-direction': 'column',
449
+ 'align-items': 'center',
450
+ 'justify-content': 'center',
451
+ };
452
+ expect(regionCssObj).toEqual(jasmine.objectContaining(expectRegionCssObj));
453
+
454
+ for (const caption of children) {
455
+ // Verify that styles applied to the nested cues _DO NOT_ include region
456
+ // placement.
457
+ const cueCssObj = parseCssText(caption.style.cssText);
458
+ expect(Object.keys(cueCssObj)).not.toContain('height');
459
+ expect(Object.keys(cueCssObj)).not.toContain('width');
460
+ expect(Object.keys(cueCssObj)).not.toContain('top');
461
+ expect(Object.keys(cueCssObj)).not.toContain('left');
462
+ }
463
+ });
399
464
  });