shaka-player 3.0.11 → 3.0.15

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 (142) hide show
  1. package/AUTHORS +5 -0
  2. package/CHANGELOG.md +132 -0
  3. package/CONTRIBUTORS +5 -0
  4. package/README.md +3 -0
  5. package/build/apps.py +2 -2
  6. package/build/check.py +2 -2
  7. package/build/conformance.textproto +0 -9
  8. package/build/generateExterns.js +1 -1
  9. package/build/shakaBuildHelpers.py +6 -5
  10. package/build/subprocessWindowsPatch.py +103 -0
  11. package/demo/common/assets.js +1 -1
  12. package/demo/common/message_ids.js +1 -0
  13. package/demo/config.js +2 -1
  14. package/demo/demo.less +4 -0
  15. package/demo/demo_utils.js +1 -0
  16. package/demo/input_container.js +2 -1
  17. package/demo/locales/en.json +1 -0
  18. package/demo/locales/source.json +4 -0
  19. package/dist/controls.css +2 -2
  20. package/dist/controls.css.map +1 -1
  21. package/dist/demo.compiled.debug.js +63 -60
  22. package/dist/demo.compiled.debug.map +3 -3
  23. package/dist/demo.compiled.js +63 -60
  24. package/dist/demo.compiled.map +3 -3
  25. package/dist/demo.css +1 -1
  26. package/dist/demo.css.map +1 -1
  27. package/dist/deps.js +9 -9
  28. package/dist/locales.js +35 -33
  29. package/dist/receiver.compiled.debug.js +59 -56
  30. package/dist/receiver.compiled.debug.map +3 -3
  31. package/dist/receiver.compiled.js +59 -56
  32. package/dist/receiver.compiled.map +3 -3
  33. package/dist/shaka-player.compiled.d.ts +131 -5
  34. package/dist/shaka-player.compiled.debug.d.ts +131 -5
  35. package/dist/shaka-player.compiled.debug.externs.js +142 -8
  36. package/dist/shaka-player.compiled.debug.js +330 -312
  37. package/dist/shaka-player.compiled.debug.map +4 -4
  38. package/dist/shaka-player.compiled.externs.js +142 -8
  39. package/dist/shaka-player.compiled.js +707 -702
  40. package/dist/shaka-player.compiled.map +4 -4
  41. package/dist/shaka-player.ui.d.ts +131 -5
  42. package/dist/shaka-player.ui.debug.d.ts +131 -5
  43. package/dist/shaka-player.ui.debug.externs.js +144 -10
  44. package/dist/shaka-player.ui.debug.js +354 -333
  45. package/dist/shaka-player.ui.debug.map +4 -4
  46. package/dist/shaka-player.ui.externs.js +144 -10
  47. package/dist/shaka-player.ui.js +860 -855
  48. package/dist/shaka-player.ui.map +4 -4
  49. package/docs/tutorials/fairplay.md +1 -1
  50. package/docs/tutorials/faq.md +25 -0
  51. package/externs/aria.js +18 -0
  52. package/externs/ima.js +4 -0
  53. package/externs/shaka/ads.js +2 -0
  54. package/karma.conf.js +1 -0
  55. package/lib/ads/ad_manager.js +29 -11
  56. package/lib/ads/client_side_ad_manager.js +76 -86
  57. package/lib/ads/server_side_ad_manager.js +26 -11
  58. package/lib/cast/cast_proxy.js +3 -2
  59. package/lib/cast/cast_receiver.js +6 -2
  60. package/lib/cast/cast_sender.js +2 -2
  61. package/lib/dash/dash_parser.js +63 -1
  62. package/lib/dash/segment_list.js +1 -1
  63. package/lib/dash/segment_template.js +41 -14
  64. package/lib/hls/hls_parser.js +41 -11
  65. package/lib/media/content_workarounds.js +18 -0
  66. package/lib/media/drm_engine.js +8 -0
  67. package/lib/media/gap_jumping_controller.js +8 -4
  68. package/lib/media/media_source_engine.js +7 -0
  69. package/lib/media/playhead.js +1 -1
  70. package/lib/media/presentation_timeline.js +3 -2
  71. package/lib/media/segment_index.js +41 -11
  72. package/lib/media/streaming_engine.js +34 -21
  73. package/lib/net/networking_engine.js +2 -1
  74. package/lib/offline/download_manager.js +1 -2
  75. package/lib/player.js +99 -44
  76. package/lib/polyfill/encryption_scheme.js +2 -0
  77. package/lib/polyfill/fullscreen.js +2 -0
  78. package/lib/polyfill/mathround.js +2 -0
  79. package/lib/polyfill/mediasource.js +2 -0
  80. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  81. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  82. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  83. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  84. package/lib/polyfill/pip_webkit.js +2 -0
  85. package/lib/polyfill/video_play_promise.js +2 -0
  86. package/lib/polyfill/videoplaybackquality.js +2 -0
  87. package/lib/polyfill/vttcue.js +2 -0
  88. package/lib/text/mp4_ttml_parser.js +3 -0
  89. package/lib/text/ttml_text_parser.js +5 -0
  90. package/lib/text/ui_text_displayer.js +37 -3
  91. package/lib/util/error.js +1 -4
  92. package/lib/util/fake_event.js +53 -7
  93. package/lib/util/mime_utils.js +33 -0
  94. package/lib/util/periods.js +25 -3
  95. package/lib/util/stream_utils.js +11 -0
  96. package/package.json +3 -3
  97. package/test/cast/cast_proxy_unit.js +12 -6
  98. package/test/cast/cast_receiver_integration.js +1 -1
  99. package/test/cast/cast_utils_unit.js +1 -1
  100. package/test/dash/dash_parser_manifest_unit.js +73 -0
  101. package/test/demo/demo_unit.js +0 -1
  102. package/test/hls/hls_parser_unit.js +45 -1
  103. package/test/media/drm_engine_unit.js +56 -0
  104. package/test/media/playhead_unit.js +27 -1
  105. package/test/media/presentation_timeline_unit.js +19 -0
  106. package/test/media/segment_index_unit.js +79 -6
  107. package/test/media/streaming_engine_unit.js +3 -3
  108. package/test/player_integration.js +38 -0
  109. package/test/player_unit.js +19 -1
  110. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  111. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  112. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  113. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  114. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  115. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  116. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  117. package/test/test/assets/3675/dash_0.mpd +30 -0
  118. package/test/test/assets/3675/dash_1.mpd +30 -0
  119. package/test/test/assets/3675/dash_2.mpd +30 -0
  120. package/test/test/assets/3675/dash_3.mpd +30 -0
  121. package/test/test/assets/3675/dash_4.mpd +30 -0
  122. package/test/test/assets/3675/dash_5.mpd +26 -0
  123. package/test/test/assets/3675/init-stream0.m4s +0 -0
  124. package/test/test/util/fake_ad_manager.js +4 -1
  125. package/test/test/util/fake_media_source_engine.js +4 -0
  126. package/test/test/util/simple_fakes.js +5 -3
  127. package/test/test/util/waiter.js +36 -2
  128. package/test/text/ttml_text_parser_unit.js +9 -0
  129. package/test/text/ui_text_displayer_unit.js +19 -3
  130. package/test/ui/localization_unit.js +15 -14
  131. package/test/ui/ui_unit.js +4 -0
  132. package/ui/big_play_button.js +10 -0
  133. package/ui/cast_button.js +2 -3
  134. package/ui/controls.js +9 -7
  135. package/ui/less/containers.less +3 -0
  136. package/ui/locales/en.json +1 -0
  137. package/ui/locales/ru.json +6 -6
  138. package/ui/locales/source.json +4 -0
  139. package/ui/localization.js +28 -51
  140. package/ui/pip_button.js +2 -3
  141. package/ui/play_button.js +10 -10
  142. package/ui/small_play_button.js +12 -0
@@ -1295,12 +1295,13 @@ describe('Playhead', () => {
1295
1295
  });
1296
1296
 
1297
1297
  // Regression test for https://github.com/google/shaka-player/issues/2987
1298
- it('does gap jump if paused at 0', () => {
1298
+ it('does gap jump if paused at 0 and has autoplay', () => {
1299
1299
  const buffered = [{start: 10, end: 20}];
1300
1300
  video.buffered = createFakeBuffered(buffered);
1301
1301
  video.currentTime = 0;
1302
1302
  video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
1303
1303
  video.paused = true;
1304
+ video.autoplay = true;
1304
1305
 
1305
1306
  config.jumpLargeGaps = true;
1306
1307
  playhead = new shaka.media.MediaSourcePlayhead(
@@ -1318,6 +1319,31 @@ describe('Playhead', () => {
1318
1319
  expect(video.currentTime).toBe(10);
1319
1320
  });
1320
1321
 
1322
+ // Regression test for https://github.com/google/shaka-player/issues/3451
1323
+ it('doesn\'t gap jump if paused at 0 and hasn\'t autoplay', () => {
1324
+ const buffered = [{start: 10, end: 20}];
1325
+ video.buffered = createFakeBuffered(buffered);
1326
+ video.currentTime = 0;
1327
+ video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
1328
+ video.paused = true;
1329
+ video.autoplay = false;
1330
+
1331
+ config.jumpLargeGaps = true;
1332
+ playhead = new shaka.media.MediaSourcePlayhead(
1333
+ video,
1334
+ manifest,
1335
+ config,
1336
+ /* startTime= */ 0,
1337
+ Util.spyFunc(onSeek),
1338
+ Util.spyFunc(onEvent));
1339
+
1340
+ playhead.notifyOfBufferingChange();
1341
+ jasmine.clock().tick(500);
1342
+
1343
+ // There should NOT have been a gap jump.
1344
+ expect(video.currentTime).toBe(0);
1345
+ });
1346
+
1321
1347
  /**
1322
1348
  * @param {string} name
1323
1349
  * @param {SeekTestInfo} data
@@ -290,6 +290,25 @@ describe('PresentationTimeline', () => {
290
290
  // last segment time (50)
291
291
  expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
292
292
  });
293
+
294
+ it('calculates time when there a transition of live to static', () => {
295
+ const timeline = makeLiveTimeline(/* availability= */ 20);
296
+
297
+ const ref1 = makeSegmentReference(0, 10);
298
+ const ref2 = makeSegmentReference(10, 20);
299
+ const ref3 = makeSegmentReference(20, 30);
300
+ const ref4 = makeSegmentReference(30, 40);
301
+ const ref5 = makeSegmentReference(40, 50);
302
+
303
+ setElapsed(50);
304
+ timeline.notifySegments([ref1, ref2, ref3, ref4, ref5]);
305
+
306
+ expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
307
+
308
+ timeline.setStatic(true);
309
+
310
+ expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
311
+ });
293
312
  });
294
313
 
295
314
  describe('getDuration', () => {
@@ -329,12 +329,40 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
329
329
  makeReference(uri(20), 20, 30),
330
330
  ];
331
331
 
332
- // The first reference ends before the availabilityWindowStart, so it
333
- // should be discarded.
334
- index1.mergeAndEvict(references2, 19);
332
+ // The first two references end before the availabilityWindowStart, so
333
+ // they should be discarded.
334
+ index1.mergeAndEvict(references2, 21);
335
+ expect(index1.references.length).toBe(1);
336
+ expect(index1.references[0]).toEqual(references2[2]);
337
+ });
338
+
339
+ it('discards segments that end before the first old segment', () => {
340
+ /** @type {!Array.<!shaka.media.SegmentReference>} */
341
+ const references1 = [
342
+ // Assuming ref(0, 10) has been already evicted
343
+ makeReference(uri(10), 10, 20),
344
+ ];
345
+ const index1 = new shaka.media.SegmentIndex(references1);
346
+ const position1 = index1.find(10);
347
+
348
+ /** @type {!Array.<!shaka.media.SegmentReference>} */
349
+ const references2 = [
350
+ makeReference(uri(0), 0, 10),
351
+ makeReference(uri(10), 10, 20),
352
+ makeReference(uri(20), 20, 30),
353
+ ];
354
+
355
+ // The new first reference ends before the first old reference starts, so
356
+ // it should be discarded. We will never grow the list at the beginning.
357
+ index1.mergeAndEvict(references2, 0);
335
358
  expect(index1.references.length).toBe(2);
336
359
  expect(index1.references[0]).toEqual(references2[1]);
337
360
  expect(index1.references[1]).toEqual(references2[2]);
361
+
362
+ // The positions should be the same, as well.
363
+ expect(index1.find(10)).toBe(position1);
364
+ goog.asserts.assert(position1 != null, 'Position should not be null!');
365
+ expect(index1.get(position1)).toBe(references2[1]);
338
366
  });
339
367
  });
340
368
 
@@ -407,9 +435,15 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
407
435
 
408
436
  describe('SegmentIterator', () => {
409
437
  const inputRefs = [
410
- makeReference(uri(0), 0, 10),
411
- makeReference(uri(1), 10, 20),
412
- makeReference(uri(2), 20, 30),
438
+ makeReference(uri(0.10), 0, 10),
439
+ makeReference(uri(10.20), 10, 20),
440
+ makeReference(uri(20.30), 20, 30),
441
+ ];
442
+
443
+ const additionalRefs = [
444
+ makeReference(uri(30.40), 30, 40),
445
+ makeReference(uri(40.50), 40, 50),
446
+ makeReference(uri(50.60), 50, 60),
413
447
  ];
414
448
 
415
449
  it('works with Array.from', () => {
@@ -433,6 +467,45 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
433
467
  expect(Array.from(index)).toEqual(inputRefs.slice(1));
434
468
  });
435
469
 
470
+ it('resumes iteration after new references are added', () => {
471
+ const refs = inputRefs.slice();
472
+ const index = new shaka.media.SegmentIndex(refs);
473
+
474
+ // This simulates the pattern of calls in StreamingEngine when we buffer
475
+ // to the edge of a live stream.
476
+ const iterator = index[Symbol.iterator]();
477
+ iterator.next();
478
+ expect(iterator.current()).toBe(inputRefs[0]);
479
+
480
+ iterator.next();
481
+ expect(iterator.current()).toBe(inputRefs[1]);
482
+
483
+ iterator.next();
484
+ expect(iterator.current()).toBe(inputRefs[2]);
485
+
486
+ iterator.next();
487
+ expect(iterator.current()).toBe(null);
488
+ // After we reach the end of the iteration, we should still get null back
489
+ // on subsequent requests.
490
+ expect(iterator.current()).toBe(null);
491
+ expect(iterator.current()).toBe(null);
492
+
493
+ // Although we have reached the end, we should still be able to add new
494
+ // references and iterate to them without starting over the iteration.
495
+ refs.push(...additionalRefs);
496
+
497
+ expect(iterator.current()).toBe(additionalRefs[0]);
498
+
499
+ iterator.next();
500
+ expect(iterator.current()).toBe(additionalRefs[1]);
501
+
502
+ iterator.next();
503
+ expect(iterator.current()).toBe(additionalRefs[2]);
504
+
505
+ iterator.next();
506
+ expect(iterator.current()).toBe(null);
507
+ });
508
+
436
509
  describe('seek', () => {
437
510
  it('returns the matching segment', () => {
438
511
  const index = new shaka.media.SegmentIndex(inputRefs);
@@ -788,7 +788,7 @@ describe('StreamingEngine', () => {
788
788
  // This would let some media types buffer faster than others if unchecked.
789
789
  netEngineDelays.text = 0.1;
790
790
  netEngineDelays.audio = 1.0;
791
- netEngineDelays.video = 10.0;
791
+ netEngineDelays.video = 5.0; // Need init segment and media segment
792
792
 
793
793
  mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
794
794
  // Call to the underlying implementation.
@@ -2123,13 +2123,13 @@ describe('StreamingEngine', () => {
2123
2123
  .bind(mediaSourceEngine);
2124
2124
 
2125
2125
  mediaSourceEngine.remove.and.callFake((type, start, end) => {
2126
- expect(presentationTimeInSeconds).toBe(20);
2126
+ expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(20);
2127
2127
  expect(start).toBe(0);
2128
2128
  expect(end).toBe(10);
2129
2129
 
2130
2130
  if (mediaSourceEngine.remove.calls.count() == 3) {
2131
2131
  mediaSourceEngine.remove.and.callFake((type, start, end) => {
2132
- expect(presentationTimeInSeconds).toBe(30);
2132
+ expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(30);
2133
2133
  expect(start).toBe(10);
2134
2134
  expect(end).toBe(20);
2135
2135
  return originalRemove(type, start, end);
@@ -649,6 +649,44 @@ describe('Player', () => {
649
649
  });
650
650
  });
651
651
 
652
+ describe('seek range', () => {
653
+ // Regression test for Issue #3675.
654
+ it('has a reasonable seek range after going from live to VOD', async () => {
655
+ const netEngine = player.getNetworkingEngine();
656
+ const startTime = Date.now();
657
+ netEngine.registerRequestFilter((type, request) => {
658
+ if (type != shaka.net.NetworkingEngine.RequestType.MANIFEST) {
659
+ return;
660
+ }
661
+ // Simulate a live stream by providing different manifests over time.
662
+ const time = (Date.now() - startTime) / 1000;
663
+ const manifestNumber = Math.min(5, Math.floor(0.5 + time / 2));
664
+ request.uris = [
665
+ '/base/test/test/assets/3675/dash_' + manifestNumber + '.mpd',
666
+ ];
667
+ console.log('getting manifest', request.uris);
668
+ });
669
+
670
+ // Play the stream.
671
+ await player.load('/base/test/test/assets/3675/dash_0.mpd');
672
+ video.play();
673
+
674
+ // Wait for the stream to be over.
675
+ eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
676
+ /** @type {shaka.test.Waiter} */
677
+ const waiter = new shaka.test.Waiter(eventManager)
678
+ .timeoutAfter(20).failOnTimeout(true);
679
+ await waiter.waitForEnd(video);
680
+
681
+ // The stream should have transitioned to VOD by now.
682
+ expect(player.isLive()).toBe(false);
683
+
684
+ // Check that the final seek range is as expected.
685
+ const seekRange = player.seekRange();
686
+ expect(seekRange.end).toBe(14);
687
+ });
688
+ });
689
+
652
690
  describe('buffering', () => {
653
691
  const startBuffering = jasmine.objectContaining({buffering: true});
654
692
  const endBuffering = jasmine.objectContaining({buffering: false});
@@ -1818,6 +1818,24 @@ describe('Player', () => {
1818
1818
  expect(variantChanged).not.toHaveBeenCalled();
1819
1819
  });
1820
1820
  });
1821
+
1822
+ it('chooses the configured text language and role at start', async () => {
1823
+ player.configure({
1824
+ preferredTextLanguage: 'en',
1825
+ preferredTextRole: 'commentary',
1826
+ });
1827
+
1828
+ await player.load(fakeManifestUri, 0, fakeMimeType);
1829
+
1830
+ // Text was turned on during startup.
1831
+ expect(player.isTextTrackVisible()).toBe(true);
1832
+
1833
+ expect(getActiveTextTrack()).toEqual(jasmine.objectContaining({
1834
+ id: 52,
1835
+ language: 'en',
1836
+ roles: ['commentary'],
1837
+ }));
1838
+ });
1821
1839
  }); // describe('tracks')
1822
1840
 
1823
1841
  describe('languages', () => {
@@ -2744,7 +2762,7 @@ describe('Player', () => {
2744
2762
 
2745
2763
  manifest.addVariant(1, (variant) => {
2746
2764
  variant.addVideo(2, (stream) => {
2747
- stream.size(200, 1500);
2765
+ stream.size(200, 1024);
2748
2766
  });
2749
2767
  });
2750
2768
 
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="dynamic"
8
+ minimumUpdatePeriod="PT2S"
9
+ suggestedPresentationDelay="PT2S"
10
+ availabilityStartTime="2021-10-28T07:36:27.755Z"
11
+ publishTime="2021-10-28T07:36:29.744Z"
12
+ timeShiftBufferDepth="PT10.0S"
13
+ maxSegmentDuration="PT2.0S"
14
+ minBufferTime="PT4.0S">
15
+ <ProgramInformation>
16
+ </ProgramInformation>
17
+ <ServiceDescription id="0">
18
+ </ServiceDescription>
19
+ <Period id="0" start="PT0.0S">
20
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
21
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
22
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
23
+ <SegmentTimeline>
24
+ <S t="0" d="30720" />
25
+ </SegmentTimeline>
26
+ </SegmentTemplate>
27
+ </Representation>
28
+ </AdaptationSet>
29
+ </Period>
30
+ </MPD>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="dynamic"
8
+ minimumUpdatePeriod="PT2S"
9
+ suggestedPresentationDelay="PT2S"
10
+ availabilityStartTime="2021-10-28T07:36:27.755Z"
11
+ publishTime="2021-10-28T07:36:31.750Z"
12
+ timeShiftBufferDepth="PT10.0S"
13
+ maxSegmentDuration="PT2.0S"
14
+ minBufferTime="PT4.0S">
15
+ <ProgramInformation>
16
+ </ProgramInformation>
17
+ <ServiceDescription id="0">
18
+ </ServiceDescription>
19
+ <Period id="0" start="PT0.0S">
20
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
21
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
22
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
23
+ <SegmentTimeline>
24
+ <S t="0" d="30720" r="1" />
25
+ </SegmentTimeline>
26
+ </SegmentTemplate>
27
+ </Representation>
28
+ </AdaptationSet>
29
+ </Period>
30
+ </MPD>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="dynamic"
8
+ minimumUpdatePeriod="PT2S"
9
+ suggestedPresentationDelay="PT2S"
10
+ availabilityStartTime="2021-10-28T07:36:27.755Z"
11
+ publishTime="2021-10-28T07:36:35.752Z"
12
+ timeShiftBufferDepth="PT10.0S"
13
+ maxSegmentDuration="PT2.0S"
14
+ minBufferTime="PT4.0S">
15
+ <ProgramInformation>
16
+ </ProgramInformation>
17
+ <ServiceDescription id="0">
18
+ </ServiceDescription>
19
+ <Period id="0" start="PT0.0S">
20
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
21
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
22
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
23
+ <SegmentTimeline>
24
+ <S t="0" d="30720" r="3" />
25
+ </SegmentTimeline>
26
+ </SegmentTemplate>
27
+ </Representation>
28
+ </AdaptationSet>
29
+ </Period>
30
+ </MPD>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="dynamic"
8
+ minimumUpdatePeriod="PT2S"
9
+ suggestedPresentationDelay="PT2S"
10
+ availabilityStartTime="2021-10-28T07:36:27.755Z"
11
+ publishTime="2021-10-28T07:36:37.752Z"
12
+ timeShiftBufferDepth="PT10.0S"
13
+ maxSegmentDuration="PT2.0S"
14
+ minBufferTime="PT4.0S">
15
+ <ProgramInformation>
16
+ </ProgramInformation>
17
+ <ServiceDescription id="0">
18
+ </ServiceDescription>
19
+ <Period id="0" start="PT0.0S">
20
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
21
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
22
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
23
+ <SegmentTimeline>
24
+ <S t="0" d="30720" r="4" />
25
+ </SegmentTimeline>
26
+ </SegmentTemplate>
27
+ </Representation>
28
+ </AdaptationSet>
29
+ </Period>
30
+ </MPD>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="dynamic"
8
+ minimumUpdatePeriod="PT2S"
9
+ suggestedPresentationDelay="PT2S"
10
+ availabilityStartTime="2021-10-28T07:36:27.755Z"
11
+ publishTime="2021-10-28T07:36:41.751Z"
12
+ timeShiftBufferDepth="PT10.0S"
13
+ maxSegmentDuration="PT2.0S"
14
+ minBufferTime="PT4.0S">
15
+ <ProgramInformation>
16
+ </ProgramInformation>
17
+ <ServiceDescription id="0">
18
+ </ServiceDescription>
19
+ <Period id="0" start="PT0.0S">
20
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
21
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
22
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="3">
23
+ <SegmentTimeline>
24
+ <S t="61440" d="30720" r="4" />
25
+ </SegmentTimeline>
26
+ </SegmentTemplate>
27
+ </Representation>
28
+ </AdaptationSet>
29
+ </Period>
30
+ </MPD>
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
4
+ xmlns:xlink="http://www.w3.org/1999/xlink"
5
+ xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
6
+ profiles="urn:mpeg:dash:profile:isoff-live:2011"
7
+ type="static"
8
+ mediaPresentationDuration="PT14.0S"
9
+ maxSegmentDuration="PT2.0S"
10
+ minBufferTime="PT4.0S">
11
+ <ProgramInformation>
12
+ </ProgramInformation>
13
+ <ServiceDescription id="0">
14
+ </ServiceDescription>
15
+ <Period id="0" start="PT0.0S">
16
+ <AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
17
+ <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
18
+ <SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="3">
19
+ <SegmentTimeline>
20
+ <S t="61440" d="30720" r="4" />
21
+ </SegmentTimeline>
22
+ </SegmentTemplate>
23
+ </Representation>
24
+ </AdaptationSet>
25
+ </Period>
26
+ </MPD>
@@ -25,6 +25,9 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
25
25
  this.stats_ = new shaka.ads.AdsStats();
26
26
  }
27
27
 
28
+ /** @override */
29
+ release() {}
30
+
28
31
  /** @override */
29
32
  setLocale(locale) {}
30
33
 
@@ -75,7 +78,7 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
75
78
  */
76
79
  startAd(ad) {
77
80
  const event = new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
78
- {'ad': ad});
81
+ (new Map()).set('ad', ad));
79
82
 
80
83
  this.dispatchEvent(event);
81
84
  }
@@ -124,6 +124,10 @@ shaka.test.FakeMediaSourceEngine = class {
124
124
  /** @type {!jasmine.Spy} */
125
125
  this.flush = jasmine.createSpy('flush').and.returnValue(Promise.resolve());
126
126
 
127
+ /** @type {!jasmine.Spy} */
128
+ this.clearSelectedClosedCaptionId =
129
+ jasmine.createSpy('clearSelectedClosedCaptionId');
130
+
127
131
  /** @type {!jasmine.Spy} */
128
132
  this.getTextDisplayer =
129
133
  jasmine.createSpy('getTextDisplayer')
@@ -90,13 +90,15 @@ shaka.test.FakeAbrManager = class {
90
90
  /** @extends {shaka.media.StreamingEngine} */
91
91
  shaka.test.FakeStreamingEngine = class {
92
92
  constructor() {
93
- const resolve = () => Promise.resolve();
94
-
95
93
  let activeVariant = null;
96
94
  let activeText = null;
97
95
 
98
96
  /** @type {!jasmine.Spy} */
99
- this.destroy = jasmine.createSpy('destroy').and.callFake(resolve);
97
+ this.destroy = jasmine.createSpy('destroy').and.callFake(() => {
98
+ activeVariant = null;
99
+ activeText = null;
100
+ return Promise.resolve();
101
+ });
100
102
 
101
103
  /** @type {!jasmine.Spy} */
102
104
  this.configure = jasmine.createSpy('configure');
@@ -132,10 +132,43 @@ shaka.test.Waiter = class {
132
132
  * @return {!Promise}
133
133
  */
134
134
  waitForEnd(mediaElement) {
135
- if (mediaElement.ended) {
135
+ // Sometimes, the ended flag is not set, or the event does not fire,
136
+ // (I'm looking at **you**, Safari), so also check if we've reached the
137
+ // duration.
138
+ if (mediaElement.ended ||
139
+ mediaElement.currentTime >= mediaElement.duration) {
136
140
  return Promise.resolve();
137
141
  }
138
- return this.waitForEvent(mediaElement, 'ended');
142
+
143
+ // The name of what we're waiting for.
144
+ const goalName = 'end of media';
145
+
146
+ // Cleanup on timeout.
147
+ const cleanup = () => {
148
+ this.eventManager_.unlisten(mediaElement, 'timeupdate');
149
+ this.eventManager_.unlisten(mediaElement, 'ended');
150
+ };
151
+
152
+ // The conditions for success. Don't rely on either time, or the ended
153
+ // flag, or the ended event, specifically. Any of these is sufficient.
154
+ // This flexibility cuts down on test flake on Safari (currently 14) in
155
+ // particular, where the flag might be set, but the ended event did not
156
+ // fire.
157
+ const p = new Promise((resolve) => {
158
+ this.eventManager_.listen(mediaElement, 'timeupdate', () => {
159
+ if (mediaElement.currentTime >= mediaElement.duration ||
160
+ mediaElement.ended) {
161
+ cleanup();
162
+ resolve();
163
+ }
164
+ });
165
+ this.eventManager_.listen(mediaElement, 'ended', () => {
166
+ cleanup();
167
+ resolve();
168
+ });
169
+ });
170
+
171
+ return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement);
139
172
  }
140
173
 
141
174
  /**
@@ -256,6 +289,7 @@ shaka.test.Waiter = class {
256
289
  'ready state', mediaElement.readyState,
257
290
  'playback rate', mediaElement.playbackRate,
258
291
  'paused', mediaElement.paused,
292
+ 'ended', mediaElement.ended,
259
293
  'buffered', buffered);
260
294
  }
261
295
  };
@@ -305,6 +305,15 @@ describe('TtmlTextParser', () => {
305
305
  {periodStart: 0, segmentStart: 0, segmentEnd: 0});
306
306
  });
307
307
 
308
+ it('supports comments in the body', () => {
309
+ verifyHelper(
310
+ [],
311
+ '<tt><body><div>' +
312
+ '<!-- text-based TTML -->' +
313
+ '</div></body></tt>',
314
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0});
315
+ });
316
+
308
317
  it('parses alignment from textAlign attribute of a region', () => {
309
318
  verifyHelper(
310
319
  [
@@ -4,7 +4,6 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- goog.require('shaka.test.UiUtils');
8
7
  goog.require('shaka.test.Util');
9
8
  goog.require('shaka.text.Cue');
10
9
  goog.require('shaka.text.UITextDisplayer');
@@ -51,8 +50,7 @@ describe('UITextDisplayer', () => {
51
50
  /** @type {!HTMLElement} */ (document.createElement('div'));
52
51
  videoContainer.style.height = `${videoContainerHeight}px`;
53
52
  document.body.appendChild(videoContainer);
54
- video = shaka.test.UiUtils.createVideoElement();
55
- videoContainer.appendChild(video);
53
+ video = new shaka.test.FakeVideo();
56
54
  });
57
55
 
58
56
  beforeEach(() => {
@@ -316,4 +314,22 @@ describe('UITextDisplayer', () => {
316
314
  // duplicates.
317
315
  expect(captions.length).toBe(3);
318
316
  });
317
+
318
+ it('hides currently displayed cue when removed', async () => {
319
+ const cue = new shaka.text.Cue(0, 50, 'One');
320
+ textDisplayer.setTextVisibility(true);
321
+ textDisplayer.append([cue]);
322
+ video.currentTime = 10;
323
+ await shaka.test.Util.delay(0.5);
324
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
325
+
326
+ let cueElements = textContainer.querySelectorAll('span');
327
+ expect(cueElements.length).toBe(1);
328
+ expect(cueElements[0].textContent).toBe('One');
329
+
330
+ textDisplayer.remove(0, 100);
331
+
332
+ cueElements = textContainer.querySelectorAll('span');
333
+ expect(cueElements.length).toBe(0);
334
+ });
319
335
  });