shaka-player 4.3.2 → 4.3.3

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 (38) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/deps.js +1 -1
  3. package/dist/locales.js +32 -32
  4. package/dist/shaka-player.compiled.d.ts +10 -0
  5. package/dist/shaka-player.compiled.debug.d.ts +10 -0
  6. package/dist/shaka-player.compiled.debug.externs.js +12 -0
  7. package/dist/shaka-player.compiled.debug.js +120 -117
  8. package/dist/shaka-player.compiled.debug.map +3 -3
  9. package/dist/shaka-player.compiled.externs.js +12 -0
  10. package/dist/shaka-player.compiled.js +622 -619
  11. package/dist/shaka-player.compiled.map +3 -3
  12. package/dist/shaka-player.ui.d.ts +10 -0
  13. package/dist/shaka-player.ui.debug.d.ts +10 -0
  14. package/dist/shaka-player.ui.debug.externs.js +12 -0
  15. package/dist/shaka-player.ui.debug.js +132 -130
  16. package/dist/shaka-player.ui.debug.map +3 -3
  17. package/dist/shaka-player.ui.externs.js +12 -0
  18. package/dist/shaka-player.ui.js +408 -406
  19. package/dist/shaka-player.ui.map +3 -3
  20. package/lib/hls/hls_parser.js +39 -15
  21. package/lib/media/media_source_engine.js +34 -4
  22. package/lib/media/segment_index.js +5 -10
  23. package/lib/media/segment_reference.js +39 -0
  24. package/lib/media/streaming_engine.js +15 -3
  25. package/lib/player.js +7 -1
  26. package/lib/text/ui_text_displayer.js +24 -4
  27. package/lib/text/web_vtt_generator.js +21 -9
  28. package/lib/util/dom_utils.js +3 -1
  29. package/lib/util/stream_utils.js +6 -0
  30. package/lib/util/timer.js +17 -0
  31. package/package.json +1 -1
  32. package/test/hls/hls_live_unit.js +8 -30
  33. package/test/hls/hls_parser_unit.js +30 -0
  34. package/test/media/segment_index_unit.js +28 -0
  35. package/test/media/streaming_engine_unit.js +91 -119
  36. package/test/player_integration.js +27 -0
  37. package/test/player_unit.js +4 -2
  38. package/test/text/ui_text_displayer_unit.js +70 -0
@@ -2728,9 +2728,67 @@ describe('StreamingEngine', () => {
2728
2728
  });
2729
2729
 
2730
2730
  describe('embedded emsg boxes', () => {
2731
- const emsgSegment = Uint8ArrayUtils.fromHex(
2731
+ // V0 box format
2732
+ const emsgSegmentV0 = Uint8ArrayUtils.fromHex(
2732
2733
  '0000003b656d736700000000666f6f3a6261723a637573746f6d646174617363' +
2733
2734
  '68656d6500310000000001000000080000ffff0000000174657374');
2735
+
2736
+ // V1 box format
2737
+ const emsgSegmentV1 = Uint8ArrayUtils.fromHex(
2738
+ '0000003f656d7367010000000000000100000000000000080000ffff00000001' +
2739
+ '666f6f3a6261723a637573746f6d64617461736368656d6500310074657374');
2740
+
2741
+ // V1 box format, non-zero start time
2742
+ const emsgSegmentV1NonZeroStart = Uint8ArrayUtils.fromHex(
2743
+ '0000003f656d7367010000000000000100000000000000120000ffff00000001' +
2744
+ '666f6f3a6261723a637573746f6d64617461736368656d6500310074657374');
2745
+
2746
+ const dummyBox = Uint8ArrayUtils.fromHex('0000000c6672656501020304');
2747
+
2748
+ const emsgSegmentV0Twice =
2749
+ Uint8ArrayUtils.concat(emsgSegmentV0, dummyBox, emsgSegmentV0);
2750
+
2751
+ // This is an 'emsg' box that contains a scheme of
2752
+ // urn:mpeg:dash:event:2012 to indicate a manifest update.
2753
+ const emsgSegmentV0ReloadManifest = Uint8ArrayUtils.fromHex(
2754
+ '0000003a656d73670000000075726e3a6d7065673a646173683a6576656e743a' +
2755
+ '3230313200000000003100000008000000ff0000000c74657374');
2756
+
2757
+ const reloadManifestSchemeUri = 'urn:mpeg:dash:event:2012';
2758
+
2759
+ // This is an 'emsg' box that contains a scheme of
2760
+ // https://aomedia.org/emsg/ID to indicate a ID3 metadata.
2761
+ const emsgSegmentV0ID3 = Uint8ArrayUtils.fromHex((
2762
+ // 105 bytes emsg box v0, flags 0
2763
+ '00 00 00 69 65 6d 73 67 00 00 00 00' +
2764
+
2765
+ // scheme id uri (13 bytes) 'https://aomedia.org/emsg/ID3'
2766
+ '68 74 74 70 73 3a 2f 2f 61 6f 6d 65 64 69 61 2e' +
2767
+ '6f 72 67 2f 65 6d 73 67 2f 49 44 33 00' +
2768
+
2769
+ // value (1 byte) ''
2770
+ '00' +
2771
+
2772
+ // timescale (4 bytes) 49
2773
+ '00 00 00 31' +
2774
+
2775
+ // presentation time delta (4 bytes) 8
2776
+ '00 00 00 08' +
2777
+
2778
+ // event duration (4 bytes) 255
2779
+ '00 00 00 ff' +
2780
+
2781
+ // id (4 bytes) 51
2782
+ '00 00 00 33' +
2783
+
2784
+ // message data (47 bytes)
2785
+ '49 44 33 03 00 40 00 00 00 1b 00 00 00 06 00 00' +
2786
+ '00 00 00 02 54 58 58 58 00 00 00 07 e0 00 03 00' +
2787
+ '53 68 61 6b 61 33 44 49 03 00 40 00 00 00 1b'
2788
+ ).replace(/\s/g, ''));
2789
+
2790
+ const id3SchemeUri = 'https://aomedia.org/emsg/ID3';
2791
+
2734
2792
  const emsgObj = {
2735
2793
  startTime: 8,
2736
2794
  endTime: 0xffff + 8,
@@ -2743,14 +2801,31 @@ describe('StreamingEngine', () => {
2743
2801
  messageData: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
2744
2802
  };
2745
2803
 
2804
+ const emsgObjWithOffset = {
2805
+ startTime: -2,
2806
+ endTime: 0xffff - 2,
2807
+ schemeIdUri: 'foo:bar:customdatascheme',
2808
+ value: '1',
2809
+ timescale: 1,
2810
+ presentationTimeDelta: -2,
2811
+ eventDuration: 0xffff,
2812
+ id: 1,
2813
+ messageData: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
2814
+ };
2815
+
2746
2816
  beforeEach(() => {
2747
- setupVod();
2817
+ // setup an offset for the timestamps.
2818
+ setupVod(false, 10);
2748
2819
  mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2749
2820
  createStreamingEngine();
2750
2821
  });
2751
2822
 
2823
+ function setSegment0(emsgBox) {
2824
+ segmentData[ContentType.VIDEO].segments[0] = emsgBox;
2825
+ }
2826
+
2752
2827
  it('raises an event for registered embedded emsg boxes', async () => {
2753
- segmentData[ContentType.VIDEO].segments[0] = emsgSegment;
2828
+ setSegment0(emsgSegmentV0);
2754
2829
  videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2755
2830
 
2756
2831
  // Here we go!
@@ -2767,12 +2842,8 @@ describe('StreamingEngine', () => {
2767
2842
  });
2768
2843
 
2769
2844
  it('raises an event for registered embedded v1 emsg boxes', async () => {
2770
- // same event but verison 1.
2771
- const v1EmsgSegment = Uint8ArrayUtils.fromHex(
2772
- '0000003f656d7367010000000000000100000000000000080000ffff00000001' +
2773
- '666f6f3a6261723a637573746f6d64617461736368656d6500310074657374');
2774
- segmentData[ContentType.VIDEO].segments[0] = v1EmsgSegment;
2775
- videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2845
+ setSegment0(emsgSegmentV1);
2846
+ videoStream.emsgSchemeIdUris = [emsgObjWithOffset.schemeIdUri];
2776
2847
 
2777
2848
  // Here we go!
2778
2849
  streamingEngine.switchVariant(variant);
@@ -2784,14 +2855,11 @@ describe('StreamingEngine', () => {
2784
2855
  expect(onEvent).toHaveBeenCalledTimes(1);
2785
2856
 
2786
2857
  const event = onEvent.calls.argsFor(0)[0];
2787
- expect(event.detail).toEqual(emsgObj);
2858
+ expect(event.detail).toEqual(emsgObjWithOffset);
2788
2859
  });
2789
2860
 
2790
2861
  it('raises multiple events', async () => {
2791
- const dummyBox =
2792
- shaka.util.Uint8ArrayUtils.fromHex('0000000c6672656501020304');
2793
- segmentData[ContentType.VIDEO].segments[0] =
2794
- shaka.util.Uint8ArrayUtils.concat(emsgSegment, dummyBox, emsgSegment);
2862
+ setSegment0(emsgSegmentV0Twice);
2795
2863
  videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2796
2864
 
2797
2865
  // Here we go!
@@ -2804,8 +2872,8 @@ describe('StreamingEngine', () => {
2804
2872
  expect(onEvent).toHaveBeenCalledTimes(2);
2805
2873
  });
2806
2874
 
2807
- it('won\'t raise an event without stream field set', async () => {
2808
- segmentData[ContentType.VIDEO].segments[0] = emsgSegment;
2875
+ it('won\'t raise an event for an unregistered emsg box', async () => {
2876
+ setSegment0(emsgSegmentV0);
2809
2877
 
2810
2878
  // Here we go!
2811
2879
  streamingEngine.switchVariant(variant);
@@ -2830,30 +2898,9 @@ describe('StreamingEngine', () => {
2830
2898
  expect(onEvent).not.toHaveBeenCalled();
2831
2899
  });
2832
2900
 
2833
- it('won\'t raise an event for an unregistered emsg box', async () => {
2834
- segmentData[ContentType.VIDEO].segments[0] = emsgSegment;
2835
-
2836
- // Here we go!
2837
- streamingEngine.switchVariant(variant);
2838
- streamingEngine.switchTextStream(textStream);
2839
- await streamingEngine.start();
2840
- playing = true;
2841
- await runTest();
2842
-
2843
- expect(onEvent).not.toHaveBeenCalled();
2844
- });
2845
-
2846
2901
  it('triggers manifest updates', async () => {
2847
- // This is an 'emsg' box that contains a scheme of
2848
- // urn:mpeg:dash:event:2012 to indicate a manifest update.
2849
- segmentData[ContentType.VIDEO].segments[0] =
2850
- Uint8ArrayUtils
2851
- .fromHex(
2852
- '0000003a656d73670000000075726e3a' +
2853
- '6d7065673a646173683a6576656e743a' +
2854
- '32303132000000000031000000080000' +
2855
- '00ff0000000c74657374');
2856
- videoStream.emsgSchemeIdUris = ['urn:mpeg:dash:event:2012'];
2902
+ setSegment0(emsgSegmentV0ReloadManifest);
2903
+ videoStream.emsgSchemeIdUris = [reloadManifestSchemeUri];
2857
2904
 
2858
2905
  // Here we go!
2859
2906
  streamingEngine.switchVariant(variant);
@@ -2867,39 +2914,8 @@ describe('StreamingEngine', () => {
2867
2914
  });
2868
2915
 
2869
2916
  it('triggers metadata event', async () => {
2870
- // This is an 'emsg' box that contains a scheme of
2871
- // https://aomedia.org/emsg/ID to indicate a ID3 metadata.
2872
- segmentData[ContentType.VIDEO].segments[0] =
2873
- Uint8ArrayUtils.fromHex((
2874
- // 105 bytes emsg box v0, flags 0
2875
- '00 00 00 69 65 6d 73 67 00 00 00 00' +
2876
-
2877
- // scheme id uri (13 bytes) 'https://aomedia.org/emsg/ID3'
2878
- '68 74 74 70 73 3a 2f 2f 61 6f 6d 65 64 69 61 2e' +
2879
- '6f 72 67 2f 65 6d 73 67 2f 49 44 33 00' +
2880
-
2881
- // value (1 byte) ''
2882
- '00' +
2883
-
2884
- // timescale (4 bytes) 49
2885
- '00 00 00 31' +
2886
-
2887
- // presentation time delta (4 bytes) 8
2888
- '00 00 00 08' +
2889
-
2890
- // event duration (4 bytes) 255
2891
- '00 00 00 ff' +
2892
-
2893
- // id (4 bytes) 51
2894
- '00 00 00 33' +
2895
-
2896
- // message data (47 bytes)
2897
- '49 44 33 03 00 40 00 00 00 1b 00 00 00 06 00 00' +
2898
- '00 00 00 02 54 58 58 58 00 00 00 07 e0 00 03 00' +
2899
- '53 68 61 6b 61 33 44 49 03 00 40 00 00 00 1b'
2900
- ).replace(/\s/g, ''));
2901
-
2902
- videoStream.emsgSchemeIdUris = ['https://aomedia.org/emsg/ID3'];
2917
+ setSegment0(emsgSegmentV0ID3);
2918
+ videoStream.emsgSchemeIdUris = [id3SchemeUri];
2903
2919
 
2904
2920
  // Here we go!
2905
2921
  streamingEngine.switchVariant(variant);
@@ -2911,54 +2927,10 @@ describe('StreamingEngine', () => {
2911
2927
  expect(onEvent).not.toHaveBeenCalled();
2912
2928
  expect(onMetadata).toHaveBeenCalled();
2913
2929
  });
2914
- });
2915
-
2916
- describe('embedded emsg boxes with non zero timestamps', () => {
2917
- const emsgSegment = Uint8ArrayUtils.fromHex(
2918
- '0000003b656d736700000000666f6f3a6261723a637573746f6d646174617363' +
2919
- '68656d6500310000000001000000080000ffff0000000174657374');
2920
- const emsgObj = {
2921
- startTime: 8,
2922
- endTime: 0xffff + 8,
2923
- schemeIdUri: 'foo:bar:customdatascheme',
2924
- value: '1',
2925
- timescale: 1,
2926
- presentationTimeDelta: 8,
2927
- eventDuration: 0xffff,
2928
- id: 1,
2929
- messageData: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
2930
- };
2931
-
2932
- beforeEach(() => {
2933
- // setup an offset for the timestamps.
2934
- setupVod(false, 10);
2935
- mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2936
- createStreamingEngine();
2937
- });
2938
-
2939
- it('event start matches presentation times for emsg boxes', async () => {
2940
- segmentData[ContentType.VIDEO].segments[0] = emsgSegment;
2941
- videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2942
-
2943
- // Here we go!
2944
- streamingEngine.switchVariant(variant);
2945
- streamingEngine.switchTextStream(textStream);
2946
- await streamingEngine.start();
2947
- playing = true;
2948
- await runTest();
2949
-
2950
- expect(onEvent).toHaveBeenCalledTimes(1);
2951
-
2952
- const event = onEvent.calls.argsFor(0)[0];
2953
- expect(event.detail).toEqual(emsgObj);
2954
- });
2955
2930
 
2956
- it('event start matches presentation time for v1 emsg boxes', async () => {
2957
- // same event but verison 1. start time is 18.
2958
- const v1EmsgSegment = Uint8ArrayUtils.fromHex(
2959
- '0000003f656d7367010000000000000100000000000000120000ffff00000001' +
2960
- '666f6f3a6261723a637573746f6d64617461736368656d6500310074657374');
2961
- segmentData[ContentType.VIDEO].segments[0] = v1EmsgSegment;
2931
+ it('event start matches presentation time', async () => {
2932
+ // This box has a non-zero event time, which doesn't matter.
2933
+ setSegment0(emsgSegmentV1NonZeroStart);
2962
2934
  videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2963
2935
 
2964
2936
  // Here we go!
@@ -72,6 +72,33 @@ describe('Player', () => {
72
72
  });
73
73
  }); // describe('attach')
74
74
 
75
+ describe('destroy', () => {
76
+ // Regression test for:
77
+ // https://github.com/shaka-project/shaka-player/issues/4850
78
+ it('does not leave any lingering timers', async () => {
79
+ shaka.util.Timer.activeTimers.clear();
80
+
81
+ // Unlike the other tests in this file, this uses an uncompiled build of
82
+ // Shaka, so that we don't need to expose shaka.util.Timer.activeTimers.
83
+ player = new shaka.Player(video);
84
+ waiter.setPlayer(player);
85
+
86
+ // Play the video for a little while.
87
+ await player.load('test:sintel');
88
+ video.play();
89
+ await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
90
+
91
+ // Destroy the player.
92
+ await player.destroy();
93
+
94
+ // Are there any timers left?
95
+ for (const timer of shaka.util.Timer.activeTimers.keys()) {
96
+ const stackTrace = shaka.util.Timer.activeTimers.get(timer);
97
+ fail('Lingering timer exists! Stack trace of creation: ' + stackTrace);
98
+ }
99
+ });
100
+ });
101
+
75
102
  describe('updateStartTime() in manifestparsed event handler', () => {
76
103
  it('does not get segments prior to startTime', async () => {
77
104
  player.addEventListener('manifestparsed', () => {
@@ -3393,7 +3393,9 @@ describe('Player', () => {
3393
3393
  variant.addAudio(0, (stream) => {
3394
3394
  stream.channelsCount = 6;
3395
3395
  stream.audioSamplingRate = 48000;
3396
- stream.codecs = 'ac-3';
3396
+ // ac-3 is rewritten as ec-3 on Tizen, so for the stability of this
3397
+ // test case, use ec-3.
3398
+ stream.codecs = 'ec-3';
3397
3399
  });
3398
3400
  });
3399
3401
 
@@ -3418,7 +3420,7 @@ describe('Player', () => {
3418
3420
  expect(abrManager.variants.length).toBe(1);
3419
3421
  // It should be the 6-channel variant, based on our preference.
3420
3422
  expect(abrManager.variants[0].audio.channelsCount).toBe(6);
3421
- expect(abrManager.variants[0].audio.codecs).toBe('ac-3');
3423
+ expect(abrManager.variants[0].audio.codecs).toBe('ec-3');
3422
3424
  });
3423
3425
  });
3424
3426
 
@@ -537,4 +537,74 @@ describe('UITextDisplayer', () => {
537
537
  '.shaka-text-region');
538
538
  expect(allRegionElements.length).toBe(1);
539
539
  });
540
+
541
+ it('creates separate regions when dimensions differ but id same', () => {
542
+ const identicalRegionId = 'regionId';
543
+
544
+ const cueRegion1 = new shaka.text.CueRegion();
545
+ const cueRegion2 = new shaka.text.CueRegion();
546
+ cueRegion1.id = identicalRegionId;
547
+ cueRegion2.id = identicalRegionId;
548
+
549
+ cueRegion1.height = 80;
550
+ cueRegion1.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
551
+ cueRegion1.width = 80;
552
+ cueRegion1.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
553
+
554
+ cueRegion2.height = 160; // the only difference!
555
+ cueRegion2.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
556
+ cueRegion2.width = 80;
557
+ cueRegion2.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
558
+
559
+ cueRegion1.viewportAnchorX = 10;
560
+ cueRegion1.viewportAnchorY = 10;
561
+ cueRegion1.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
562
+
563
+ cueRegion2.viewportAnchorX = 10;
564
+ cueRegion2.viewportAnchorY = 10;
565
+ cueRegion2.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
566
+
567
+ // These all attach to the same region, but only one region element should
568
+ // be created.
569
+ const firstBatchOfCues = [
570
+ new shaka.text.Cue(0, 100, ''),
571
+ new shaka.text.Cue(0, 100, ''),
572
+ new shaka.text.Cue(0, 100, ''),
573
+ ];
574
+ for (const cue of firstBatchOfCues) {
575
+ cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
576
+ cue.region = cueRegion1;
577
+ }
578
+
579
+ // Another batch for the other region
580
+ const secondBatchOfCues = [
581
+ new shaka.text.Cue(0, 100, ''),
582
+ new shaka.text.Cue(0, 100, ''),
583
+ new shaka.text.Cue(0, 100, ''),
584
+ ];
585
+ for (const cue of secondBatchOfCues) {
586
+ cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
587
+ cue.region = cueRegion2;
588
+ }
589
+
590
+ textDisplayer.setTextVisibility(true);
591
+ textDisplayer.append(firstBatchOfCues);
592
+ textDisplayer.append(secondBatchOfCues);
593
+ updateCaptions();
594
+
595
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
596
+ const allRegionElements = textContainer.querySelectorAll(
597
+ '.shaka-text-region');
598
+
599
+ // Verify that the nested cues are attached to respective region element.
600
+ expect(allRegionElements.length).toBe(2);
601
+
602
+ const childrenOfOne = Array.from(allRegionElements[0].childNodes).filter(
603
+ (e) => e.nodeType == Node.ELEMENT_NODE);
604
+ expect(childrenOfOne.length).toBe(3);
605
+
606
+ const childrenOfTwo = Array.from(allRegionElements[1].childNodes).filter(
607
+ (e) => e.nodeType == Node.ELEMENT_NODE);
608
+ expect(childrenOfTwo.length).toBe(3);
609
+ });
540
610
  });